Started Work on v3
This commit is contained in:
parent
7f10a81cec
commit
7cc06eeddc
674
LICENSE
674
LICENSE
@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
12
Makefile
Normal file
12
Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
boltbrowser:
|
||||
go build -o build/boltbrowser
|
||||
|
||||
run: ./build/boltbrowser
|
||||
cd build && ./boltbrowser
|
||||
|
||||
install:
|
||||
go install
|
||||
|
||||
clean:
|
||||
rm build/boltbrowser
|
54
cmd/root.go
Normal file
54
cmd/root.go
Normal file
@ -0,0 +1,54 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"git.bullercodeworks.com/brian/boltbrowser/ui"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
AppVersion = 3.0
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "boltbrowser",
|
||||
Short: "A browser and editor for bolt databases",
|
||||
Long: ``,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("No database given.")
|
||||
}
|
||||
viper.Set("dbs", args)
|
||||
if err := ui.NewUi().Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.Flags().BoolP("readonly", "ro", false, "Open database in read only mode")
|
||||
viper.BindPFlag("readonly", rootCmd.Flags().Lookup("readonly"))
|
||||
rootCmd.Flags().Bool("no-value", false, "Do not output values in the left pane")
|
||||
viper.BindPFlag("no-value", rootCmd.Flags().Lookup("no-value"))
|
||||
rootCmd.Flags().String("timeout", "1s", "Amount of time to wait for the DB to open, otherwise error. Given in 'duration' format (e.g. 1s for one second, 1m for one minute)")
|
||||
viper.BindPFlag("timeout", rootCmd.Flags().Lookup("timeout"))
|
||||
viper.Set("version", AppVersion)
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
|
||||
}
|
37
go.mod
37
go.mod
@ -1,11 +1,34 @@
|
||||
module github.com/br0xen/boltbrowser
|
||||
module git.bullercodeworks.com/brian/boltbrowser
|
||||
|
||||
require (
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/br0xen/termbox-util v0.0.0-20170904143325-de1d4c83380e
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/nsf/termbox-go v0.0.0-20180819125858-b66b20ab708e
|
||||
golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed // indirect
|
||||
git.bullercodeworks.com/brian/wandle v1.0.1
|
||||
git.bullercodeworks.com/brian/widdles v1.1.0
|
||||
github.com/nsf/termbox-go v1.1.1
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/viper v1.11.0
|
||||
)
|
||||
|
||||
go 1.13
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
go 1.18
|
||||
|
504
go.sum
504
go.sum
@ -1,10 +1,494 @@
|
||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/br0xen/termbox-util v0.0.0-20170904143325-de1d4c83380e h1:PF4gYXcZfTbAoAk5DPZcvjmq8gyg4gpcmWdT8W+0X1c=
|
||||
github.com/br0xen/termbox-util v0.0.0-20170904143325-de1d4c83380e/go.mod h1:x9wJlgOj74OFTOBwXOuO8pBguW37EgYNx51Dbjkfzo4=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/nsf/termbox-go v0.0.0-20180819125858-b66b20ab708e h1:fvw0uluMptljaRKSU8459cJ4bmi3qUYyMs5kzpic2fY=
|
||||
github.com/nsf/termbox-go v0.0.0-20180819125858-b66b20ab708e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
|
||||
golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed h1:5TJcLJn2a55mJjzYk0yOoqN8X1OdvBDUnaZaKKyQtkY=
|
||||
golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.bullercodeworks.com/brian/wandle v1.0.1 h1:qzqlTgWulIztyTtnP7SKKoO0RYSrDy4oymCYOhVcL34=
|
||||
git.bullercodeworks.com/brian/wandle v1.0.1/go.mod h1:jyY6L/ubxvPPE6ykBca/b9clJIBUv1fhlqC8MJthKuA=
|
||||
git.bullercodeworks.com/brian/widdles v1.1.0 h1:H1fCPdf3Vu8/6Ky8+TsPeb9wJ2U2p7ecmlcXERBPfF0=
|
||||
git.bullercodeworks.com/brian/widdles v1.1.0/go.mod h1:gHNRaUPauffZXWouF2kVcHeZ/qKjRAGFK0u79wnAlOY=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY=
|
||||
github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0=
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
|
||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44=
|
||||
github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
|
||||
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
143
main.go
143
main.go
@ -1,144 +1,13 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/nsf/termbox-go"
|
||||
"git.bullercodeworks.com/brian/boltbrowser/cmd"
|
||||
)
|
||||
|
||||
var ProgramName = "boltbrowser"
|
||||
var VersionNum = 2.0
|
||||
|
||||
var databaseFiles []string
|
||||
var db *bolt.DB
|
||||
var memBolt *BoltDB
|
||||
|
||||
var currentFilename string
|
||||
|
||||
const DefaultDBOpenTimeout = time.Second
|
||||
|
||||
var AppArgs struct {
|
||||
DBOpenTimeout time.Duration
|
||||
ReadOnly bool
|
||||
NoValue bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
AppArgs.DBOpenTimeout = DefaultDBOpenTimeout
|
||||
AppArgs.ReadOnly = false
|
||||
}
|
||||
|
||||
func parseArgs() {
|
||||
var err error
|
||||
if len(os.Args) == 1 {
|
||||
printUsage(nil)
|
||||
}
|
||||
parms := os.Args[1:]
|
||||
for i := range parms {
|
||||
// All 'option' arguments start with "-"
|
||||
if !strings.HasPrefix(parms[i], "-") {
|
||||
databaseFiles = append(databaseFiles, parms[i])
|
||||
continue
|
||||
}
|
||||
if strings.Contains(parms[i], "=") {
|
||||
// Key/Value pair Arguments
|
||||
pts := strings.Split(parms[i], "=")
|
||||
key, val := pts[0], pts[1]
|
||||
switch key {
|
||||
case "-timeout":
|
||||
AppArgs.DBOpenTimeout, err = time.ParseDuration(val)
|
||||
if err != nil {
|
||||
// See if we can successfully parse by adding a 's'
|
||||
AppArgs.DBOpenTimeout, err = time.ParseDuration(val + "s")
|
||||
}
|
||||
// If err is still not nil, print usage
|
||||
if err != nil {
|
||||
printUsage(err)
|
||||
}
|
||||
case "-readonly", "-ro":
|
||||
if val == "true" {
|
||||
AppArgs.ReadOnly = true
|
||||
}
|
||||
case "-no-value":
|
||||
if val == "true" {
|
||||
AppArgs.NoValue = true
|
||||
}
|
||||
case "-help":
|
||||
printUsage(nil)
|
||||
default:
|
||||
printUsage(errors.New("Invalid option"))
|
||||
}
|
||||
} else {
|
||||
// Single-word arguments
|
||||
switch parms[i] {
|
||||
case "-readonly", "-ro":
|
||||
AppArgs.ReadOnly = true
|
||||
case "-no-value":
|
||||
AppArgs.NoValue = true
|
||||
case "-help":
|
||||
printUsage(nil)
|
||||
default:
|
||||
printUsage(errors.New("Invalid option"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage(err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <filename(s)>\nOptions:\n", ProgramName)
|
||||
fmt.Fprintf(os.Stderr, " -timeout=duration\n DB file open timeout (default 1s)\n")
|
||||
fmt.Fprintf(os.Stderr, " -ro, -readonly \n Open the DB in read-only mode\n")
|
||||
fmt.Fprintf(os.Stderr, " -no-value \n Do not display a value in left pane\n")
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
parseArgs()
|
||||
|
||||
err = termbox.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer termbox.Close()
|
||||
style := defaultStyle()
|
||||
termbox.SetOutputMode(termbox.Output256)
|
||||
|
||||
for _, databaseFile := range databaseFiles {
|
||||
currentFilename = databaseFile
|
||||
db, err = bolt.Open(databaseFile, 0600, &bolt.Options{Timeout: AppArgs.DBOpenTimeout})
|
||||
if err == bolt.ErrTimeout {
|
||||
termbox.Close()
|
||||
fmt.Printf("File %s is locked. Make sure it's not used by another app and try again\n", databaseFile)
|
||||
os.Exit(1)
|
||||
} else if err != nil {
|
||||
if len(databaseFiles) > 1 {
|
||||
mainLoop(nil, style)
|
||||
continue
|
||||
} else {
|
||||
termbox.Close()
|
||||
fmt.Printf("Error reading file: %q\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// First things first, load the database into memory
|
||||
memBolt.refreshDatabase()
|
||||
if AppArgs.ReadOnly {
|
||||
// If we're opening it in readonly mode, close it now
|
||||
db.Close()
|
||||
}
|
||||
|
||||
// Kick off the UI loop
|
||||
mainLoop(memBolt, style)
|
||||
defer db.Close()
|
||||
}
|
||||
cmd.Execute()
|
||||
}
|
||||
|
37
mainloop.go
37
mainloop.go
@ -1,37 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
func mainLoop(memBolt *BoltDB, style Style) {
|
||||
screens := defaultScreensForData(memBolt)
|
||||
displayScreen := screens[BrowserScreenIndex]
|
||||
layoutAndDrawScreen(displayScreen, style)
|
||||
for {
|
||||
event := termbox.PollEvent()
|
||||
if event.Type == termbox.EventKey {
|
||||
if event.Key == termbox.KeyCtrlZ {
|
||||
process, _ := os.FindProcess(os.Getpid())
|
||||
termbox.Close()
|
||||
process.Signal(syscall.SIGSTOP)
|
||||
termbox.Init()
|
||||
}
|
||||
newScreenIndex := displayScreen.handleKeyEvent(event)
|
||||
if newScreenIndex < len(screens) {
|
||||
displayScreen = screens[newScreenIndex]
|
||||
layoutAndDrawScreen(displayScreen, style)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if event.Type == termbox.EventResize {
|
||||
layoutAndDrawScreen(displayScreen, style)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// +build windows
|
||||
package main
|
||||
|
||||
// Windows doesn't support process backgrounding like *nix.
|
||||
// So we have a separate loop for it.
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
func mainLoop(memBolt *BoltDB, style Style) {
|
||||
screens := defaultScreensForData(memBolt)
|
||||
displayScreen := screens[BrowserScreenIndex]
|
||||
layoutAndDrawScreen(displayScreen, style)
|
||||
for {
|
||||
event := termbox.PollEvent()
|
||||
if event.Type == termbox.EventKey {
|
||||
newScreenIndex := displayScreen.handleKeyEvent(event)
|
||||
if newScreenIndex < len(screens) {
|
||||
displayScreen = screens[newScreenIndex]
|
||||
layoutAndDrawScreen(displayScreen, style)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if event.Type == termbox.EventResize {
|
||||
layoutAndDrawScreen(displayScreen, style)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -6,46 +6,55 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"git.bullercodeworks.com/brian/boltbrowser/util"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
/*
|
||||
BoltDB A Database, basically a collection of buckets
|
||||
*/
|
||||
type BoltDB struct {
|
||||
db *bolt.DB
|
||||
buckets []BoltBucket
|
||||
}
|
||||
|
||||
/*
|
||||
BoltBucket is just a struct representation of a Bucket in the Bolt DB
|
||||
*/
|
||||
type BoltBucket struct {
|
||||
name string
|
||||
pairs []BoltPair
|
||||
buckets []BoltBucket
|
||||
parent *BoltBucket
|
||||
expanded bool
|
||||
errorFlag bool
|
||||
isRoot bool
|
||||
func (bd *BoltDB) RefreshDatabase() {
|
||||
// Reload the database from the file
|
||||
bd.buckets = []BoltBucket{}
|
||||
bd.db.View(func(tx *bolt.Tx) error {
|
||||
err := tx.ForEach(func(nm []byte, b *bolt.Bucket) error {
|
||||
bb, err := bd.ReadBucket(b)
|
||||
if err == nil {
|
||||
bb.name = string(nm)
|
||||
bb.expanded = false
|
||||
bd.buckets = append(bd.buckets, *bb)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
// Check if there are key/values directly in the root
|
||||
bb, err := bd.ReadBucket(tx.Cursor().Bucket())
|
||||
if err == nil {
|
||||
bb.isRoot = true
|
||||
bb.expanded = true
|
||||
bd.buckets = append(bd.buckets, *bb)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
BoltPair is just a struct representation of a Pair in the Bolt DB
|
||||
*/
|
||||
type BoltPair struct {
|
||||
parent *BoltBucket
|
||||
key string
|
||||
val string
|
||||
}
|
||||
|
||||
func (bd *BoltDB) getGenericFromPath(path []string) (*BoltBucket, *BoltPair, error) {
|
||||
func (bd *BoltDB) GetGenericFromPath(path []string) (*BoltBucket, *BoltPair, error) {
|
||||
// Check if 'path' leads to a pair
|
||||
p, err := bd.getPairFromPath(path)
|
||||
p, err := bd.GetPairFromPath(path)
|
||||
if err == nil {
|
||||
return nil, p, nil
|
||||
}
|
||||
// Nope, check if it leads to a bucket
|
||||
b, err := bd.getBucketFromPath(path)
|
||||
b, err := bd.GetBucketFromPath(path)
|
||||
if err == nil {
|
||||
return b, nil, nil
|
||||
}
|
||||
@ -53,19 +62,19 @@ func (bd *BoltDB) getGenericFromPath(path []string) (*BoltBucket, *BoltPair, err
|
||||
return nil, nil, errors.New("Invalid Path")
|
||||
}
|
||||
|
||||
func (bd *BoltDB) getBucketFromPath(path []string) (*BoltBucket, error) {
|
||||
func (bd *BoltDB) GetBucketFromPath(path []string) (*BoltBucket, error) {
|
||||
if len(path) > 0 {
|
||||
// Find the BoltBucket with a path == path
|
||||
var b *BoltBucket
|
||||
var err error
|
||||
// Find the root bucket
|
||||
b, err = memBolt.getBucket(path[0])
|
||||
b, err = bd.GetBucket(path[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(path) > 1 {
|
||||
for p := 1; p < len(path); p++ {
|
||||
b, err = b.getBucket(path[p])
|
||||
b, err = b.GetBucket(path[p])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -76,32 +85,32 @@ func (bd *BoltDB) getBucketFromPath(path []string) (*BoltBucket, error) {
|
||||
return nil, errors.New("Invalid Path")
|
||||
}
|
||||
|
||||
func (bd *BoltDB) getPairFromPath(path []string) (*BoltPair, error) {
|
||||
func (bd *BoltDB) GetPairFromPath(path []string) (*BoltPair, error) {
|
||||
if len(path) <= 0 {
|
||||
return nil, errors.New("No Path")
|
||||
}
|
||||
b, err := bd.getBucketFromPath(path[:len(path)-1])
|
||||
b, err := bd.GetBucketFromPath(path[:len(path)-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Found the bucket, pull out the pair
|
||||
p, err := b.getPair(path[len(path)-1])
|
||||
p, err := b.GetPair(path[len(path)-1])
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
|
||||
func (bd *BoltDB) GetVisibleItemCount(path []string) (int, error) {
|
||||
vis := 0
|
||||
var retErr error
|
||||
if len(path) == 0 {
|
||||
for i := range bd.buckets {
|
||||
n, err := bd.getVisibleItemCount(bd.buckets[i].GetPath())
|
||||
n, err := bd.GetVisibleItemCount(bd.buckets[i].GetPath())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vis += n
|
||||
}
|
||||
} else {
|
||||
b, err := bd.getBucketFromPath(path)
|
||||
b, err := bd.GetBucketFromPath(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -113,7 +122,7 @@ func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
|
||||
vis += len(b.pairs)
|
||||
// * recurse for buckets
|
||||
for i := range b.buckets {
|
||||
n, err := bd.getVisibleItemCount(b.buckets[i].GetPath())
|
||||
n, err := bd.GetVisibleItemCount(b.buckets[i].GetPath())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -124,12 +133,12 @@ func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
|
||||
return vis, retErr
|
||||
}
|
||||
|
||||
func (bd *BoltDB) buildVisiblePathSlice(filter string) ([][]string, error) {
|
||||
func (bd *BoltDB) BuildVisiblePathSlice(filter string) ([][]string, error) {
|
||||
var retSlice [][]string
|
||||
var retErr error
|
||||
// The root path, recurse for root buckets
|
||||
for i := range bd.buckets {
|
||||
bktS, bktErr := bd.buckets[i].buildVisiblePathSlice([]string{}, filter)
|
||||
bktS, bktErr := bd.buckets[i].BuildVisiblePathSlice([]string{}, filter)
|
||||
if bktErr == nil {
|
||||
retSlice = append(retSlice, bktS...)
|
||||
} else {
|
||||
@ -140,8 +149,8 @@ func (bd *BoltDB) buildVisiblePathSlice(filter string) ([][]string, error) {
|
||||
return retSlice, retErr
|
||||
}
|
||||
|
||||
func (bd *BoltDB) isVisiblePath(path []string, filter string) bool {
|
||||
visPaths, err := bd.buildVisiblePathSlice(filter)
|
||||
func (bd *BoltDB) IsVisiblePath(path []string, filter string) bool {
|
||||
visPaths, err := bd.BuildVisiblePathSlice(filter)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@ -162,8 +171,8 @@ func (bd *BoltDB) isVisiblePath(path []string, filter string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (bd *BoltDB) getPrevVisiblePath(path []string, filter string) []string {
|
||||
visPaths, err := bd.buildVisiblePathSlice(filter)
|
||||
func (bd *BoltDB) GetPrevVisiblePath(path []string, filter string) []string {
|
||||
visPaths, err := bd.BuildVisiblePathSlice(filter)
|
||||
if path == nil {
|
||||
if len(visPaths) > 0 {
|
||||
return visPaths[len(visPaths)-1]
|
||||
@ -186,8 +195,8 @@ func (bd *BoltDB) getPrevVisiblePath(path []string, filter string) []string {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (bd *BoltDB) getNextVisiblePath(path []string, filter string) []string {
|
||||
visPaths, err := bd.buildVisiblePathSlice(filter)
|
||||
func (bd *BoltDB) GetNextVisiblePath(path []string, filter string) []string {
|
||||
visPaths, err := bd.BuildVisiblePathSlice(filter)
|
||||
if path == nil {
|
||||
if len(visPaths) > 0 {
|
||||
return visPaths[0]
|
||||
@ -211,34 +220,34 @@ func (bd *BoltDB) getNextVisiblePath(path []string, filter string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bd *BoltDB) toggleOpenBucket(path []string) error {
|
||||
func (bd *BoltDB) ToggleOpenBucket(path []string) error {
|
||||
// Find the BoltBucket with a path == path
|
||||
b, err := bd.getBucketFromPath(path)
|
||||
b, err := bd.GetBucketFromPath(path)
|
||||
if err == nil {
|
||||
b.expanded = !b.expanded
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (bd *BoltDB) closeBucket(path []string) error {
|
||||
func (bd *BoltDB) CloseBucket(path []string) error {
|
||||
// Find the BoltBucket with a path == path
|
||||
b, err := bd.getBucketFromPath(path)
|
||||
b, err := bd.GetBucketFromPath(path)
|
||||
if err == nil {
|
||||
b.expanded = false
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (bd *BoltDB) openBucket(path []string) error {
|
||||
func (bd *BoltDB) OpenBucket(path []string) error {
|
||||
// Find the BoltBucket with a path == path
|
||||
b, err := bd.getBucketFromPath(path)
|
||||
b, err := bd.GetBucketFromPath(path)
|
||||
if err == nil {
|
||||
b.expanded = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (bd *BoltDB) getBucket(k string) (*BoltBucket, error) {
|
||||
func (bd *BoltDB) GetBucket(k string) (*BoltBucket, error) {
|
||||
for i := range bd.buckets {
|
||||
if bd.buckets[i].name == k {
|
||||
return &bd.buckets[i], nil
|
||||
@ -247,166 +256,26 @@ func (bd *BoltDB) getBucket(k string) (*BoltBucket, error) {
|
||||
return nil, errors.New("Bucket Not Found")
|
||||
}
|
||||
|
||||
func (bd *BoltDB) openAllBuckets() {
|
||||
func (bd *BoltDB) OpenAllBuckets() {
|
||||
for i := range bd.buckets {
|
||||
bd.buckets[i].openAllBuckets()
|
||||
bd.buckets[i].OpenAllBuckets()
|
||||
bd.buckets[i].expanded = true
|
||||
}
|
||||
}
|
||||
|
||||
func (bd *BoltDB) syncOpenBuckets(shadow *BoltDB) {
|
||||
func (bd *BoltDB) SyncOpenBuckets(shadow *BoltDB) {
|
||||
// First test this bucket
|
||||
for i := range bd.buckets {
|
||||
for j := range shadow.buckets {
|
||||
if bd.buckets[i].name == shadow.buckets[j].name {
|
||||
bd.buckets[i].syncOpenBuckets(&shadow.buckets[j])
|
||||
bd.buckets[i].SyncOpenBuckets(&shadow.buckets[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bd *BoltDB) refreshDatabase() *BoltDB {
|
||||
// Reload the database into memBolt
|
||||
memBolt = new(BoltDB)
|
||||
db.View(func(tx *bolt.Tx) error {
|
||||
err := tx.ForEach(func(nm []byte, b *bolt.Bucket) error {
|
||||
bb, err := readBucket(b)
|
||||
if err == nil {
|
||||
bb.name = string(nm)
|
||||
bb.expanded = false
|
||||
memBolt.buckets = append(memBolt.buckets, *bb)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
memBolt = new(BoltDB)
|
||||
// Check if there are key/values directly in the root
|
||||
bb, err := readBucket(tx.Cursor().Bucket())
|
||||
if err == nil {
|
||||
bb.isRoot = true
|
||||
bb.expanded = true
|
||||
memBolt.buckets = append(memBolt.buckets, *bb)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
})
|
||||
return memBolt
|
||||
}
|
||||
|
||||
/*
|
||||
GetPath returns the database path leading to this BoltBucket
|
||||
*/
|
||||
func (b *BoltBucket) GetPath() []string {
|
||||
if b.parent != nil {
|
||||
return append(b.parent.GetPath(), b.name)
|
||||
}
|
||||
return []string{b.name}
|
||||
}
|
||||
|
||||
/*
|
||||
buildVisiblePathSlice builds a slice of string slices containing all visible paths in this bucket
|
||||
The passed prefix is the path leading to the current bucket
|
||||
*/
|
||||
func (b *BoltBucket) buildVisiblePathSlice(prefix []string, filter string) ([][]string, error) {
|
||||
var retSlice [][]string
|
||||
var retErr error
|
||||
retSlice = append(retSlice, append(prefix, b.name))
|
||||
if b.expanded {
|
||||
// Add subbuckets
|
||||
for i := range b.buckets {
|
||||
bktS, bktErr := b.buckets[i].buildVisiblePathSlice(append(prefix, b.name), filter)
|
||||
if bktErr != nil {
|
||||
return retSlice, bktErr
|
||||
}
|
||||
retSlice = append(retSlice, bktS...)
|
||||
}
|
||||
// Add pairs
|
||||
for i := range b.pairs {
|
||||
if filter != "" && !strings.Contains(b.pairs[i].key, filter) {
|
||||
continue
|
||||
}
|
||||
retSlice = append(retSlice, append(append(prefix, b.name), b.pairs[i].key))
|
||||
}
|
||||
}
|
||||
return retSlice, retErr
|
||||
}
|
||||
|
||||
func (b *BoltBucket) syncOpenBuckets(shadow *BoltBucket) {
|
||||
// First test this bucket
|
||||
b.expanded = shadow.expanded
|
||||
for i := range b.buckets {
|
||||
for j := range shadow.buckets {
|
||||
if b.buckets[i].name == shadow.buckets[j].name {
|
||||
b.buckets[i].syncOpenBuckets(&shadow.buckets[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BoltBucket) openAllBuckets() {
|
||||
for i := range b.buckets {
|
||||
b.buckets[i].openAllBuckets()
|
||||
b.buckets[i].expanded = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BoltBucket) getBucket(k string) (*BoltBucket, error) {
|
||||
for i := range b.buckets {
|
||||
if b.buckets[i].name == k {
|
||||
return &b.buckets[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Bucket Not Found")
|
||||
}
|
||||
|
||||
func (b *BoltBucket) getPair(k string) (*BoltPair, error) {
|
||||
for i := range b.pairs {
|
||||
if b.pairs[i].key == k {
|
||||
return &b.pairs[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Pair Not Found")
|
||||
}
|
||||
|
||||
/*
|
||||
GetPath Returns the path of the BoltPair
|
||||
*/
|
||||
func (p *BoltPair) GetPath() []string {
|
||||
return append(p.parent.GetPath(), p.key)
|
||||
}
|
||||
|
||||
/* This is a go-between function (between the boltbrowser structs
|
||||
* above, and the bolt convenience functions below)
|
||||
* for taking a boltbrowser bucket and recursively adding it
|
||||
* and all of it's children into the database.
|
||||
* Mainly used for moving a bucket from one path to another
|
||||
* as in the 'renameBucket' function below.
|
||||
*/
|
||||
func addBucketFromBoltBucket(path []string, bb *BoltBucket) error {
|
||||
if err := insertBucket(path, bb.name); err == nil {
|
||||
bucketPath := append(path, bb.name)
|
||||
for i := range bb.pairs {
|
||||
if err = insertPair(bucketPath, bb.pairs[i].key, bb.pairs[i].val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range bb.buckets {
|
||||
if err = addBucketFromBoltBucket(bucketPath, &bb.buckets[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteKey(path []string) error {
|
||||
if AppArgs.ReadOnly {
|
||||
return errors.New("DB is in Read-Only Mode")
|
||||
}
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
func (bd *BoltDB) DeleteKey(path []string) error {
|
||||
err := bd.db.Update(func(tx *bolt.Tx) error {
|
||||
// len(b.path)-1 is the key we need to delete,
|
||||
// the rest are buckets leading to that key
|
||||
if len(path) == 1 {
|
||||
@ -423,7 +292,7 @@ func deleteKey(path []string) error {
|
||||
for i := range path[1 : len(path)-1] {
|
||||
b = b.Bucket([]byte(path[i+1]))
|
||||
if b == nil {
|
||||
return errors.New("deleteKey: Invalid Path")
|
||||
return errors.New("DeleteKey: Invalid Path")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -437,19 +306,19 @@ func deleteKey(path []string) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
return errors.New("deleteKey: Invalid Path")
|
||||
return errors.New("DeleteKey: Invalid Path")
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func readBucket(b *bolt.Bucket) (*BoltBucket, error) {
|
||||
func (bd *BoltDB) ReadBucket(b *bolt.Bucket) (*BoltBucket, error) {
|
||||
bb := new(BoltBucket)
|
||||
if b == nil {
|
||||
return nil, errors.New("No bucket passed")
|
||||
}
|
||||
b.ForEach(func(k, v []byte) error {
|
||||
if v == nil {
|
||||
tb, err := readBucket(b.Bucket(k))
|
||||
tb, err := bd.ReadBucket(b.Bucket(k))
|
||||
tb.parent = bb
|
||||
if err == nil {
|
||||
tb.name = string(k)
|
||||
@ -465,13 +334,13 @@ func readBucket(b *bolt.Bucket) (*BoltBucket, error) {
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
func renameBucket(path []string, name string) error {
|
||||
func (bd *BoltDB) RenameBucket(path []string, name string) error {
|
||||
if name == path[len(path)-1] {
|
||||
// No change requested
|
||||
return nil
|
||||
}
|
||||
var bb *BoltBucket // For caching the current bucket
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
err := bd.db.View(func(tx *bolt.Tx) error {
|
||||
// len(b.path)-1 is the key we need to delete,
|
||||
// the rest are buckets leading to that key
|
||||
b := tx.Bucket([]byte(path[0]))
|
||||
@ -484,18 +353,18 @@ func renameBucket(path []string, name string) error {
|
||||
for i := range path[1:] {
|
||||
b = b.Bucket([]byte(path[i+1]))
|
||||
if b == nil {
|
||||
return errors.New("renameBucket: Invalid Path")
|
||||
return errors.New("RenameBucket: Invalid Path")
|
||||
}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
// Ok, cache b
|
||||
bb, err = readBucket(b)
|
||||
bb, err = bd.ReadBucket(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("renameBucket: Invalid Bucket")
|
||||
return errors.New("RenameBucket: Invalid Bucket")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -503,11 +372,11 @@ func renameBucket(path []string, name string) error {
|
||||
return err
|
||||
}
|
||||
if bb == nil {
|
||||
return errors.New("renameBucket: Couldn't find Bucket")
|
||||
return errors.New("RenameBucket: Couldn't find Bucket")
|
||||
}
|
||||
|
||||
// Ok, we have the bucket cached, now delete the current instance
|
||||
if err = deleteKey(path); err != nil {
|
||||
if err = bd.DeleteKey(path); err != nil {
|
||||
return err
|
||||
}
|
||||
// Rechristen our cached bucket
|
||||
@ -515,17 +384,14 @@ func renameBucket(path []string, name string) error {
|
||||
// And re-add it
|
||||
|
||||
parentPath := path[:len(path)-1]
|
||||
if err = addBucketFromBoltBucket(parentPath, bb); err != nil {
|
||||
if err = bd.AddBucketFromBoltBucket(parentPath, bb); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updatePairKey(path []string, k string) error {
|
||||
if AppArgs.ReadOnly {
|
||||
return errors.New("DB is in Read-Only Mode")
|
||||
}
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
func (bd *BoltDB) UpdatePairKey(path []string, k string) error {
|
||||
err := bd.db.Update(func(tx *bolt.Tx) error {
|
||||
// len(b.path)-1 is the key for the pair we're updating,
|
||||
// the rest are buckets leading to that key
|
||||
b := tx.Bucket([]byte(path[0]))
|
||||
@ -538,7 +404,7 @@ func updatePairKey(path []string, k string) error {
|
||||
for i := range path[1 : len(path)-1] {
|
||||
b = b.Bucket([]byte(path[i+1]))
|
||||
if b == nil {
|
||||
return errors.New("updatePairValue: Invalid Path")
|
||||
return errors.New("UpdatePairKey: Invalid Path")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -552,16 +418,13 @@ func updatePairKey(path []string, k string) error {
|
||||
// Now update the last key in the path
|
||||
return err
|
||||
}
|
||||
return errors.New("updatePairValue: Invalid Path")
|
||||
return errors.New("UpdatePairKey: Invalid Path")
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func updatePairValue(path []string, v string) error {
|
||||
if AppArgs.ReadOnly {
|
||||
return errors.New("DB is in Read-Only Mode")
|
||||
}
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
func (bd *BoltDB) UpdatePairValue(path []string, v string) error {
|
||||
err := bd.db.Update(func(tx *bolt.Tx) error {
|
||||
// len(b.GetPath())-1 is the key for the pair we're updating,
|
||||
// the rest are buckets leading to that key
|
||||
b := tx.Bucket([]byte(path[0]))
|
||||
@ -574,7 +437,7 @@ func updatePairValue(path []string, v string) error {
|
||||
for i := range path[1 : len(path)-1] {
|
||||
b = b.Bucket([]byte(path[i+1]))
|
||||
if b == nil {
|
||||
return errors.New("updatePairValue: Invalid Path")
|
||||
return errors.New("UpdatePairValue: Invalid Path")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -582,22 +445,19 @@ func updatePairValue(path []string, v string) error {
|
||||
err := b.Put([]byte(path[len(path)-1]), []byte(v))
|
||||
return err
|
||||
}
|
||||
return errors.New("updatePairValue: Invalid Path")
|
||||
return errors.New("UpdatePairValue: Invalid Path")
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func insertBucket(path []string, n string) error {
|
||||
if AppArgs.ReadOnly {
|
||||
return errors.New("DB is in Read-Only Mode")
|
||||
}
|
||||
func (bd *BoltDB) InsertBucket(path []string, n string) error {
|
||||
// Inserts a new bucket named 'n' at 'path'
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
err := bd.db.Update(func(tx *bolt.Tx) error {
|
||||
if len(path) == 0 || path[0] == "" {
|
||||
// insert at root
|
||||
_, err := tx.CreateBucket([]byte(n))
|
||||
if err != nil {
|
||||
return fmt.Errorf("insertBucket: %s", err)
|
||||
return fmt.Errorf("InsertBucket: %s", err)
|
||||
}
|
||||
} else {
|
||||
rootBucket, path := path[0], path[1:]
|
||||
@ -611,7 +471,7 @@ func insertBucket(path []string, n string) error {
|
||||
// Not a bucket, if we're out of path, just move on
|
||||
if len(path) != 0 {
|
||||
// Out of path, error
|
||||
return errors.New("insertBucket: Invalid Path 1")
|
||||
return errors.New("InsertBucket: Invalid Path 1")
|
||||
}
|
||||
} else {
|
||||
b = nB
|
||||
@ -627,15 +487,12 @@ func insertBucket(path []string, n string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func insertPair(path []string, k string, v string) error {
|
||||
if AppArgs.ReadOnly {
|
||||
return errors.New("DB is in Read-Only Mode")
|
||||
}
|
||||
func (bd *BoltDB) InsertPair(path []string, k string, v string) error {
|
||||
// Insert a new pair k => v at path
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
err := bd.db.Update(func(tx *bolt.Tx) error {
|
||||
if len(path) == 0 {
|
||||
// We cannot insert a pair at root
|
||||
return errors.New("insertPair: Cannot insert pair at root")
|
||||
return errors.New("InsertPair: Cannot insert pair at root")
|
||||
}
|
||||
var err error
|
||||
b := tx.Bucket([]byte(path[0]))
|
||||
@ -648,13 +505,13 @@ func insertPair(path []string, k string, v string) error {
|
||||
for i := 1; i < len(path); i++ {
|
||||
b = b.Bucket([]byte(path[i]))
|
||||
if b == nil {
|
||||
return fmt.Errorf("insertPair: %s", err)
|
||||
return fmt.Errorf("InsertPair: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
err := b.Put([]byte(k), []byte(v))
|
||||
if err != nil {
|
||||
return fmt.Errorf("insertPair: %s", err)
|
||||
return fmt.Errorf("InsertPair: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -662,8 +519,8 @@ func insertPair(path []string, k string, v string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func exportValue(path []string, fName string) error {
|
||||
return db.View(func(tx *bolt.Tx) error {
|
||||
func (bd *BoltDB) exportValue(path []string, fName string) error {
|
||||
return bd.db.View(func(tx *bolt.Tx) error {
|
||||
// len(b.path)-1 is the key whose value we want to export
|
||||
// the rest are buckets leading to that key
|
||||
b := tx.Bucket([]byte(path[0]))
|
||||
@ -676,20 +533,20 @@ func exportValue(path []string, fName string) error {
|
||||
for i := range path[1 : len(path)-1] {
|
||||
b = b.Bucket([]byte(path[i+1]))
|
||||
if b == nil {
|
||||
return errors.New("exportValue: Invalid Path: " + strings.Join(path, "/"))
|
||||
return errors.New("ExportValue: Invalid Path: " + strings.Join(path, "/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
bk := []byte(path[len(path)-1])
|
||||
v := b.Get(bk)
|
||||
return writeToFile(fName, string(v)+"\n", os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
return util.WriteToFile(fName, string(v)+"\n", os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
}
|
||||
return errors.New("exportValue: Invalid Bucket")
|
||||
return errors.New("ExportValue: Invalid Bucket")
|
||||
})
|
||||
}
|
||||
|
||||
func exportJSON(path []string, fName string) error {
|
||||
return db.View(func(tx *bolt.Tx) error {
|
||||
func (bd *BoltDB) ExportJSON(path []string, fName string) error {
|
||||
return bd.db.View(func(tx *bolt.Tx) error {
|
||||
// len(b.path)-1 is the key whose value we want to export
|
||||
// the rest are buckets leading to that key
|
||||
b := tx.Bucket([]byte(path[0]))
|
||||
@ -702,57 +559,36 @@ func exportJSON(path []string, fName string) error {
|
||||
for i := range path[1 : len(path)-1] {
|
||||
b = b.Bucket([]byte(path[i+1]))
|
||||
if b == nil {
|
||||
return errors.New("exportValue: Invalid Path: " + strings.Join(path, "/"))
|
||||
return errors.New("ExportJSON: Invalid Path: " + strings.Join(path, "/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
bk := []byte(path[len(path)-1])
|
||||
if v := b.Get(bk); v != nil {
|
||||
return writeToFile(fName, "{\""+string(bk)+"\":\""+string(v)+"\"}", os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
return util.WriteToFile(fName, "{\""+string(bk)+"\":\""+string(v)+"\"}", os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
}
|
||||
if b.Bucket(bk) != nil {
|
||||
return writeToFile(fName, genJSONString(b.Bucket(bk)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
return util.WriteToFile(fName, util.ToJsonString(b.Bucket(bk)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
}
|
||||
return writeToFile(fName, genJSONString(b), os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
return util.WriteToFile(fName, util.ToJsonString(b), os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
}
|
||||
return errors.New("exportValue: Invalid Bucket")
|
||||
return errors.New("ExportJSON: Invalid Bucket")
|
||||
})
|
||||
}
|
||||
|
||||
func genJSONString(b *bolt.Bucket) string {
|
||||
ret := "{"
|
||||
b.ForEach(func(k, v []byte) error {
|
||||
ret = fmt.Sprintf("%s\"%s\":", ret, string(k))
|
||||
if v == nil {
|
||||
ret = fmt.Sprintf("%s%s,", ret, genJSONString(b.Bucket(k)))
|
||||
} else {
|
||||
ret = fmt.Sprintf("%s\"%s\",", ret, string(v))
|
||||
func (bd *BoltDB) AddBucketFromBoltBucket(path []string, bb *BoltBucket) error {
|
||||
if err := bd.InsertBucket(path, bb.name); err == nil {
|
||||
bucketPath := append(path, bb.name)
|
||||
for i := range bb.pairs {
|
||||
if err = bd.InsertPair(bucketPath, bb.pairs[i].key, bb.pairs[i].val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range bb.buckets {
|
||||
if err = bd.AddBucketFromBoltBucket(bucketPath, &bb.buckets[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
ret = fmt.Sprintf("%s}", ret[:len(ret)-1])
|
||||
return ret
|
||||
}
|
||||
|
||||
func logToFile(s string) error {
|
||||
return writeToFile("bolt-log", s+"\n", os.O_RDWR|os.O_APPEND)
|
||||
}
|
||||
|
||||
func writeToFile(fn, s string, mode int) error {
|
||||
var f *os.File
|
||||
var err error
|
||||
if f == nil {
|
||||
f, err = os.OpenFile(fn, mode, 0660)
|
||||
}
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = f.WriteString(s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
94
models/bucket.go
Normal file
94
models/bucket.go
Normal file
@ -0,0 +1,94 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
BoltBucket is just a struct representation of a Bucket in the Bolt DB
|
||||
*/
|
||||
type BoltBucket struct {
|
||||
name string
|
||||
pairs []BoltPair
|
||||
buckets []BoltBucket
|
||||
parent *BoltBucket
|
||||
expanded bool
|
||||
errorFlag bool
|
||||
isRoot bool
|
||||
}
|
||||
|
||||
/*
|
||||
GetPath returns the database path leading to this BoltBucket
|
||||
*/
|
||||
func (b *BoltBucket) GetPath() []string {
|
||||
if b.parent != nil {
|
||||
return append(b.parent.GetPath(), b.name)
|
||||
}
|
||||
return []string{b.name}
|
||||
}
|
||||
|
||||
/*
|
||||
buildVisiblePathSlice builds a slice of string slices containing all visible paths in this bucket
|
||||
The passed prefix is the path leading to the current bucket
|
||||
*/
|
||||
func (b *BoltBucket) BuildVisiblePathSlice(prefix []string, filter string) ([][]string, error) {
|
||||
var retSlice [][]string
|
||||
var retErr error
|
||||
retSlice = append(retSlice, append(prefix, b.name))
|
||||
if b.expanded {
|
||||
// Add subbuckets
|
||||
for i := range b.buckets {
|
||||
bktS, bktErr := b.buckets[i].BuildVisiblePathSlice(append(prefix, b.name), filter)
|
||||
if bktErr != nil {
|
||||
return retSlice, bktErr
|
||||
}
|
||||
retSlice = append(retSlice, bktS...)
|
||||
}
|
||||
// Add pairs
|
||||
for i := range b.pairs {
|
||||
if filter != "" && !strings.Contains(b.pairs[i].key, filter) {
|
||||
continue
|
||||
}
|
||||
retSlice = append(retSlice, append(append(prefix, b.name), b.pairs[i].key))
|
||||
}
|
||||
}
|
||||
return retSlice, retErr
|
||||
}
|
||||
|
||||
func (b *BoltBucket) SyncOpenBuckets(shadow *BoltBucket) {
|
||||
// First test this bucket
|
||||
b.expanded = shadow.expanded
|
||||
for i := range b.buckets {
|
||||
for j := range shadow.buckets {
|
||||
if b.buckets[i].name == shadow.buckets[j].name {
|
||||
b.buckets[i].SyncOpenBuckets(&shadow.buckets[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BoltBucket) OpenAllBuckets() {
|
||||
for i := range b.buckets {
|
||||
b.buckets[i].OpenAllBuckets()
|
||||
b.buckets[i].expanded = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BoltBucket) GetBucket(k string) (*BoltBucket, error) {
|
||||
for i := range b.buckets {
|
||||
if b.buckets[i].name == k {
|
||||
return &b.buckets[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Bucket Not Found")
|
||||
}
|
||||
|
||||
func (b *BoltBucket) GetPair(k string) (*BoltPair, error) {
|
||||
for i := range b.pairs {
|
||||
if b.pairs[i].key == k {
|
||||
return &b.pairs[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Pair Not Found")
|
||||
}
|
17
models/pair.go
Normal file
17
models/pair.go
Normal file
@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
/*
|
||||
BoltPair is just a struct representation of a Pair in the Bolt DB
|
||||
*/
|
||||
type BoltPair struct {
|
||||
parent *BoltBucket
|
||||
key string
|
||||
val string
|
||||
}
|
||||
|
||||
/*
|
||||
GetPath Returns the path of the BoltPair
|
||||
*/
|
||||
func (p *BoltPair) GetPath() []string {
|
||||
return append(p.parent.GetPath(), p.key)
|
||||
}
|
47
screen.go
47
screen.go
@ -1,47 +0,0 @@
|
||||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
// Screen is a basic structure for all of the applications screens
|
||||
type Screen interface {
|
||||
handleKeyEvent(event termbox.Event) int
|
||||
performLayout()
|
||||
drawScreen(style Style)
|
||||
}
|
||||
|
||||
const (
|
||||
// BrowserScreenIndex is the index
|
||||
BrowserScreenIndex = iota
|
||||
// AboutScreenIndex The idx number for the 'About' Screen
|
||||
AboutScreenIndex
|
||||
// ExitScreenIndex The idx number for Exiting
|
||||
ExitScreenIndex
|
||||
)
|
||||
|
||||
func defaultScreensForData(db *BoltDB) []Screen {
|
||||
browserScreen := BrowserScreen{db: db, rightViewPort: ViewPort{}, leftViewPort: ViewPort{}}
|
||||
aboutScreen := AboutScreen(0)
|
||||
screens := [...]Screen{
|
||||
&browserScreen,
|
||||
&aboutScreen,
|
||||
}
|
||||
|
||||
return screens[:]
|
||||
}
|
||||
|
||||
func drawBackground(bg termbox.Attribute) {
|
||||
termbox.Clear(0, bg)
|
||||
}
|
||||
|
||||
func layoutAndDrawScreen(screen Screen, style Style) {
|
||||
screen.performLayout()
|
||||
drawBackground(style.defaultBg)
|
||||
screen.drawScreen(style)
|
||||
termbox.Flush()
|
||||
}
|
||||
|
||||
type Line struct {
|
||||
Text string
|
||||
Fg termbox.Attribute
|
||||
Bg termbox.Attribute
|
||||
}
|
@ -1,967 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
termboxUtil "github.com/br0xen/termbox-util"
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
/*
|
||||
ViewPort helps keep track of what's being displayed on the screen
|
||||
*/
|
||||
type ViewPort struct {
|
||||
bytesPerRow int
|
||||
numberOfRows int
|
||||
firstRow int
|
||||
scrollRow int
|
||||
}
|
||||
|
||||
/*
|
||||
BrowserScreen holds all that's going on :D
|
||||
*/
|
||||
type BrowserScreen struct {
|
||||
db *BoltDB
|
||||
leftViewPort ViewPort
|
||||
rightViewPort ViewPort
|
||||
queuedCommand string
|
||||
currentPath []string
|
||||
currentType int
|
||||
message string
|
||||
filter string
|
||||
mode BrowserMode
|
||||
inputModal *termboxUtil.InputModal
|
||||
confirmModal *termboxUtil.ConfirmModal
|
||||
messageTimeout time.Duration
|
||||
messageTime time.Time
|
||||
|
||||
leftPaneBuffer []Line
|
||||
rightPaneBuffer []Line
|
||||
}
|
||||
|
||||
/*
|
||||
BrowserMode is just for designating the mode that we're in
|
||||
*/
|
||||
type BrowserMode int
|
||||
|
||||
const (
|
||||
modeBrowse = 16 // 0000 0001 0000
|
||||
modeChange = 32 // 0000 0010 0000
|
||||
modeChangeKey = 33 // 0000 0010 0001
|
||||
modeChangeVal = 34 // 0000 0010 0010
|
||||
modeFilter = 35 // 0100 0010 0011
|
||||
modeInsert = 64 // 0000 0100 0000
|
||||
modeInsertBucket = 65 // 0000 0100 0001
|
||||
modeInsertPair = 68 // 0000 0100 0100
|
||||
modeInsertPairKey = 69 // 0000 0100 0101
|
||||
modeInsertPairVal = 70 // 0000 0100 0110
|
||||
modeDelete = 256 // 0001 0000 0000
|
||||
modeModToParent = 8 // 0000 0000 1000
|
||||
modeExport = 512 // 0010 0000 0000
|
||||
modeExportValue = 513 // 0010 0000 0001
|
||||
modeExportJSON = 514 // 0010 0000 0010
|
||||
)
|
||||
|
||||
/*
|
||||
BoltType is just for tracking what type of db item we're looking at
|
||||
*/
|
||||
type BoltType int
|
||||
|
||||
const (
|
||||
typeBucket = iota
|
||||
typePair
|
||||
)
|
||||
|
||||
func (screen *BrowserScreen) handleKeyEvent(event termbox.Event) int {
|
||||
if screen.mode == 0 {
|
||||
screen.mode = modeBrowse
|
||||
}
|
||||
if screen.mode == modeBrowse {
|
||||
return screen.handleBrowseKeyEvent(event)
|
||||
} else if screen.mode&modeChange == modeChange {
|
||||
return screen.handleInputKeyEvent(event)
|
||||
} else if screen.mode&modeInsert == modeInsert {
|
||||
return screen.handleInsertKeyEvent(event)
|
||||
} else if screen.mode == modeDelete {
|
||||
return screen.handleDeleteKeyEvent(event)
|
||||
} else if screen.mode&modeExport == modeExport {
|
||||
return screen.handleExportKeyEvent(event)
|
||||
}
|
||||
return BrowserScreenIndex
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int {
|
||||
if event.Ch == '?' {
|
||||
// About
|
||||
return AboutScreenIndex
|
||||
|
||||
} else if event.Ch == 'q' || event.Key == termbox.KeyEsc || event.Key == termbox.KeyCtrlC {
|
||||
// Quit
|
||||
return ExitScreenIndex
|
||||
|
||||
} else if event.Ch == 'g' {
|
||||
// Jump to Beginning
|
||||
screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
|
||||
|
||||
} else if event.Ch == 'G' {
|
||||
// Jump to End
|
||||
screen.currentPath = screen.db.getPrevVisiblePath(nil, screen.filter)
|
||||
|
||||
} else if event.Key == termbox.KeyCtrlR {
|
||||
screen.refreshDatabase()
|
||||
|
||||
} else if event.Key == termbox.KeyCtrlF {
|
||||
// Jump forward half a screen
|
||||
_, h := termbox.Size()
|
||||
half := h / 2
|
||||
screen.jumpCursorDown(half)
|
||||
|
||||
} else if event.Key == termbox.KeyCtrlB {
|
||||
_, h := termbox.Size()
|
||||
half := h / 2
|
||||
screen.jumpCursorUp(half)
|
||||
|
||||
} else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown {
|
||||
screen.moveCursorDown()
|
||||
|
||||
} else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp {
|
||||
screen.moveCursorUp()
|
||||
|
||||
} else if event.Ch == 'J' {
|
||||
screen.moveRightPaneDown()
|
||||
|
||||
} else if event.Ch == 'K' {
|
||||
screen.moveRightPaneUp()
|
||||
|
||||
} else if event.Ch == 'p' {
|
||||
// p creates a new pair at the current level
|
||||
screen.startInsertItem(typePair)
|
||||
} else if event.Ch == 'P' {
|
||||
// P creates a new pair at the parent level
|
||||
screen.startInsertItemAtParent(typePair)
|
||||
|
||||
} else if event.Ch == 'b' {
|
||||
// b creates a new bucket at the current level
|
||||
screen.startInsertItem(typeBucket)
|
||||
} else if event.Ch == 'B' {
|
||||
// B creates a new bucket at the parent level
|
||||
screen.startInsertItemAtParent(typeBucket)
|
||||
|
||||
} else if event.Ch == 'e' {
|
||||
b, p, _ := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if b != nil {
|
||||
screen.setMessage("Cannot edit a bucket, did you mean to (r)ename?")
|
||||
} else if p != nil {
|
||||
screen.startEditItem()
|
||||
}
|
||||
} else if event.Ch == '/' {
|
||||
screen.startFilter()
|
||||
|
||||
} else if event.Ch == 'r' {
|
||||
screen.startRenameItem()
|
||||
|
||||
} else if event.Key == termbox.KeyEnter {
|
||||
b, p, _ := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if b != nil {
|
||||
screen.db.toggleOpenBucket(screen.currentPath)
|
||||
} else if p != nil {
|
||||
screen.startEditItem()
|
||||
}
|
||||
|
||||
} else if event.Ch == 'l' || event.Key == termbox.KeyArrowRight {
|
||||
b, p, _ := screen.db.getGenericFromPath(screen.currentPath)
|
||||
// Select the current item
|
||||
if b != nil {
|
||||
screen.db.toggleOpenBucket(screen.currentPath)
|
||||
} else if p != nil {
|
||||
screen.startEditItem()
|
||||
} else {
|
||||
screen.setMessage("Not sure what to do here...")
|
||||
}
|
||||
|
||||
} else if event.Ch == 'h' || event.Key == termbox.KeyArrowLeft {
|
||||
// If we are _on_ a bucket that's open, close it
|
||||
b, _, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil && b != nil && b.expanded {
|
||||
screen.db.closeBucket(screen.currentPath)
|
||||
} else {
|
||||
if len(screen.currentPath) > 1 {
|
||||
parentBucket, err := screen.db.getBucketFromPath(screen.currentPath[:len(screen.currentPath)-1])
|
||||
if err == nil {
|
||||
screen.db.closeBucket(parentBucket.GetPath())
|
||||
// Figure out how far up we need to move the cursor
|
||||
screen.currentPath = parentBucket.GetPath()
|
||||
}
|
||||
} else {
|
||||
screen.db.closeBucket(screen.currentPath)
|
||||
}
|
||||
}
|
||||
|
||||
} else if event.Ch == 'D' {
|
||||
screen.startDeleteItem()
|
||||
} else if event.Ch == 'x' {
|
||||
// Export Value
|
||||
screen.startExportValue()
|
||||
} else if event.Ch == 'X' {
|
||||
// Export Key/Value (or Bucket) as JSON
|
||||
screen.startExportJSON()
|
||||
}
|
||||
return BrowserScreenIndex
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int {
|
||||
if event.Key == termbox.KeyEsc {
|
||||
screen.mode = modeBrowse
|
||||
screen.inputModal.Clear()
|
||||
} else {
|
||||
screen.inputModal.HandleEvent(event)
|
||||
if screen.inputModal.IsDone() {
|
||||
b, p, _ := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if screen.mode == modeFilter {
|
||||
screen.filter = screen.inputModal.GetValue()
|
||||
if !screen.db.isVisiblePath(screen.currentPath, screen.filter) {
|
||||
screen.currentPath = screen.currentPath[:len(screen.currentPath)-1]
|
||||
}
|
||||
}
|
||||
if b != nil {
|
||||
if screen.mode == modeChangeKey {
|
||||
newName := screen.inputModal.GetValue()
|
||||
if renameBucket(screen.currentPath, newName) != nil {
|
||||
screen.setMessage("Error renaming bucket.")
|
||||
} else {
|
||||
b.name = newName
|
||||
screen.currentPath[len(screen.currentPath)-1] = b.name
|
||||
screen.setMessage("Bucket Renamed!")
|
||||
screen.refreshDatabase()
|
||||
}
|
||||
}
|
||||
} else if p != nil {
|
||||
if screen.mode == modeChangeKey {
|
||||
newKey := screen.inputModal.GetValue()
|
||||
if err := updatePairKey(screen.currentPath, newKey); err != nil {
|
||||
screen.setMessage("Error occurred updating Pair.")
|
||||
} else {
|
||||
p.key = newKey
|
||||
screen.currentPath[len(screen.currentPath)-1] = p.key
|
||||
screen.setMessage("Pair updated!")
|
||||
screen.refreshDatabase()
|
||||
}
|
||||
} else if screen.mode == modeChangeVal {
|
||||
newVal := screen.inputModal.GetValue()
|
||||
if err := updatePairValue(screen.currentPath, newVal); err != nil {
|
||||
screen.setMessage("Error occurred updating Pair.")
|
||||
} else {
|
||||
p.val = newVal
|
||||
screen.setMessage("Pair updated!")
|
||||
screen.refreshDatabase()
|
||||
}
|
||||
}
|
||||
}
|
||||
screen.mode = modeBrowse
|
||||
screen.inputModal.Clear()
|
||||
}
|
||||
}
|
||||
return BrowserScreenIndex
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) handleDeleteKeyEvent(event termbox.Event) int {
|
||||
screen.confirmModal.HandleEvent(event)
|
||||
if screen.confirmModal.IsDone() {
|
||||
if screen.confirmModal.IsAccepted() {
|
||||
holdNextPath := screen.db.getNextVisiblePath(screen.currentPath, screen.filter)
|
||||
holdPrevPath := screen.db.getPrevVisiblePath(screen.currentPath, screen.filter)
|
||||
if deleteKey(screen.currentPath) == nil {
|
||||
screen.refreshDatabase()
|
||||
// Move the current path endpoint appropriately
|
||||
//found_new_path := false
|
||||
if holdNextPath != nil {
|
||||
if len(holdNextPath) > 2 {
|
||||
if holdNextPath[len(holdNextPath)-2] == screen.currentPath[len(screen.currentPath)-2] {
|
||||
screen.currentPath = holdNextPath
|
||||
} else if holdPrevPath != nil {
|
||||
screen.currentPath = holdPrevPath
|
||||
} else {
|
||||
// Otherwise, go to the parent
|
||||
screen.currentPath = screen.currentPath[:(len(holdNextPath) - 2)]
|
||||
}
|
||||
} else {
|
||||
// Root bucket deleted, set to next
|
||||
screen.currentPath = holdNextPath
|
||||
}
|
||||
} else if holdPrevPath != nil {
|
||||
screen.currentPath = holdPrevPath
|
||||
} else {
|
||||
screen.currentPath = screen.currentPath[:0]
|
||||
}
|
||||
}
|
||||
}
|
||||
screen.mode = modeBrowse
|
||||
screen.confirmModal.Clear()
|
||||
}
|
||||
return BrowserScreenIndex
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int {
|
||||
if event.Key == termbox.KeyEsc {
|
||||
if len(screen.db.buckets) == 0 {
|
||||
return ExitScreenIndex
|
||||
}
|
||||
screen.mode = modeBrowse
|
||||
screen.inputModal.Clear()
|
||||
} else {
|
||||
screen.inputModal.HandleEvent(event)
|
||||
if screen.inputModal.IsDone() {
|
||||
newVal := screen.inputModal.GetValue()
|
||||
screen.inputModal.Clear()
|
||||
var insertPath []string
|
||||
if len(screen.currentPath) > 0 {
|
||||
_, p, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e != nil {
|
||||
screen.setMessage("Error Inserting new item. Invalid Path.")
|
||||
}
|
||||
insertPath = screen.currentPath
|
||||
// where are we inserting?
|
||||
if p != nil {
|
||||
// If we're sitting on a pair, we have to go to it's parent
|
||||
screen.mode = screen.mode | modeModToParent
|
||||
}
|
||||
if screen.mode&modeModToParent == modeModToParent {
|
||||
if len(screen.currentPath) > 1 {
|
||||
insertPath = screen.currentPath[:len(screen.currentPath)-1]
|
||||
} else {
|
||||
insertPath = make([]string, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parentB, _, _ := screen.db.getGenericFromPath(insertPath)
|
||||
if screen.mode&modeInsertBucket == modeInsertBucket {
|
||||
err := insertBucket(insertPath, newVal)
|
||||
if err != nil {
|
||||
screen.setMessage(fmt.Sprintf("%s => %s", err, insertPath))
|
||||
} else {
|
||||
if parentB != nil {
|
||||
parentB.expanded = true
|
||||
}
|
||||
}
|
||||
screen.currentPath = append(insertPath, newVal)
|
||||
|
||||
screen.refreshDatabase()
|
||||
screen.mode = modeBrowse
|
||||
screen.inputModal.Clear()
|
||||
} else if screen.mode&modeInsertPair == modeInsertPair {
|
||||
err := insertPair(insertPath, newVal, "")
|
||||
if err != nil {
|
||||
screen.setMessage(fmt.Sprintf("%s => %s", err, insertPath))
|
||||
screen.refreshDatabase()
|
||||
screen.mode = modeBrowse
|
||||
screen.inputModal.Clear()
|
||||
} else {
|
||||
if parentB != nil {
|
||||
parentB.expanded = true
|
||||
}
|
||||
screen.currentPath = append(insertPath, newVal)
|
||||
screen.refreshDatabase()
|
||||
screen.startEditItem()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return BrowserScreenIndex
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) handleExportKeyEvent(event termbox.Event) int {
|
||||
if event.Key == termbox.KeyEsc {
|
||||
screen.mode = modeBrowse
|
||||
screen.inputModal.Clear()
|
||||
} else {
|
||||
screen.inputModal.HandleEvent(event)
|
||||
if screen.inputModal.IsDone() {
|
||||
b, p, _ := screen.db.getGenericFromPath(screen.currentPath)
|
||||
fileName := screen.inputModal.GetValue()
|
||||
if screen.mode&modeExportValue == modeExportValue {
|
||||
// Exporting the value
|
||||
if p != nil {
|
||||
if err := exportValue(screen.currentPath, fileName); err != nil {
|
||||
//screen.setMessage("Error Exporting to file " + fileName + ".")
|
||||
screen.setMessage(err.Error())
|
||||
} else {
|
||||
screen.setMessage("Value exported to file: " + fileName)
|
||||
}
|
||||
}
|
||||
} else if screen.mode&modeExportJSON == modeExportJSON {
|
||||
if b != nil || p != nil {
|
||||
if exportJSON(screen.currentPath, fileName) != nil {
|
||||
screen.setMessage("Error Exporting to file " + fileName + ".")
|
||||
} else {
|
||||
screen.setMessage("Value exported to file: " + fileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
screen.mode = modeBrowse
|
||||
screen.inputModal.Clear()
|
||||
}
|
||||
}
|
||||
return BrowserScreenIndex
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) jumpCursorUp(distance int) bool {
|
||||
// Jump up 'distance' lines
|
||||
visPaths, err := screen.db.buildVisiblePathSlice(screen.filter)
|
||||
if err == nil {
|
||||
findPath := screen.currentPath
|
||||
for idx, pth := range visPaths {
|
||||
startJump := true
|
||||
for i := range pth {
|
||||
if len(screen.currentPath) > i && pth[i] != screen.currentPath[i] {
|
||||
startJump = false
|
||||
}
|
||||
}
|
||||
if startJump {
|
||||
distance--
|
||||
if distance == 0 {
|
||||
screen.currentPath = visPaths[len(visPaths)-1-idx]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
isCurPath := true
|
||||
for i := range screen.currentPath {
|
||||
if screen.currentPath[i] != findPath[i] {
|
||||
isCurPath = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isCurPath {
|
||||
screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (screen *BrowserScreen) jumpCursorDown(distance int) bool {
|
||||
visPaths, err := screen.db.buildVisiblePathSlice(screen.filter)
|
||||
if err == nil {
|
||||
findPath := screen.currentPath
|
||||
for idx, pth := range visPaths {
|
||||
startJump := true
|
||||
|
||||
for i := range pth {
|
||||
if len(screen.currentPath) > i && pth[i] != screen.currentPath[i] {
|
||||
startJump = false
|
||||
}
|
||||
}
|
||||
if startJump {
|
||||
distance--
|
||||
if distance == 0 {
|
||||
screen.currentPath = visPaths[idx]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
isCurPath := true
|
||||
for i := range screen.currentPath {
|
||||
if screen.currentPath[i] != findPath[i] {
|
||||
isCurPath = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isCurPath {
|
||||
screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) moveCursorUp() bool {
|
||||
newPath := screen.db.getPrevVisiblePath(screen.currentPath, screen.filter)
|
||||
if newPath != nil {
|
||||
screen.currentPath = newPath
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (screen *BrowserScreen) moveCursorDown() bool {
|
||||
newPath := screen.db.getNextVisiblePath(screen.currentPath, screen.filter)
|
||||
if newPath != nil {
|
||||
screen.currentPath = newPath
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (screen *BrowserScreen) moveRightPaneUp() bool {
|
||||
if screen.rightViewPort.scrollRow > 0 {
|
||||
screen.rightViewPort.scrollRow--
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (screen *BrowserScreen) moveRightPaneDown() bool {
|
||||
screen.rightViewPort.scrollRow++
|
||||
return true
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) performLayout() {}
|
||||
|
||||
func (screen *BrowserScreen) drawScreen(style Style) {
|
||||
if screen.db == nil {
|
||||
screen.drawHeader(style)
|
||||
screen.setMessage("Invalid DB. Press 'q' to quit, '?' for help")
|
||||
screen.drawFooter(style)
|
||||
return
|
||||
}
|
||||
if len(screen.db.buckets) == 0 && screen.mode&modeInsertBucket != modeInsertBucket {
|
||||
// Force a bucket insert
|
||||
screen.startInsertItemAtParent(typeBucket)
|
||||
}
|
||||
if screen.message == "" {
|
||||
screen.setMessageWithTimeout("Press '?' for help", -1)
|
||||
}
|
||||
screen.drawLeftPane(style)
|
||||
screen.drawRightPane(style)
|
||||
screen.drawHeader(style)
|
||||
screen.drawFooter(style)
|
||||
|
||||
if screen.inputModal != nil {
|
||||
screen.inputModal.Draw()
|
||||
}
|
||||
if screen.mode == modeDelete {
|
||||
screen.confirmModal.Draw()
|
||||
}
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) drawHeader(style Style) {
|
||||
width, _ := termbox.Size()
|
||||
headerStringLen := func(fileName string) int {
|
||||
return len(ProgramName) + len(fileName) + 1
|
||||
}
|
||||
headerFileName := currentFilename
|
||||
if headerStringLen(headerFileName) > width {
|
||||
headerFileName = filepath.Base(headerFileName)
|
||||
}
|
||||
headerString := ProgramName + ": " + headerFileName
|
||||
count := ((width - len(headerString)) / 2) + 1
|
||||
if count < 0 {
|
||||
count = 0
|
||||
}
|
||||
spaces := strings.Repeat(" ", count)
|
||||
termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, headerString, spaces), 0, 0, style.titleFg, style.titleBg)
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) drawFooter(style Style) {
|
||||
if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout {
|
||||
screen.clearMessage()
|
||||
}
|
||||
_, height := termbox.Size()
|
||||
termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, style.defaultFg, style.defaultBg)
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) buildLeftPane(style Style) {
|
||||
screen.leftPaneBuffer = nil
|
||||
if len(screen.currentPath) == 0 {
|
||||
screen.currentPath = screen.db.getNextVisiblePath(nil, screen.filter)
|
||||
}
|
||||
for i := range screen.db.buckets {
|
||||
screen.leftPaneBuffer = append(screen.leftPaneBuffer, screen.bucketToLines(&screen.db.buckets[i], style)...)
|
||||
}
|
||||
// Find the cursor in the leftPane
|
||||
for k, v := range screen.leftPaneBuffer {
|
||||
if v.Fg == style.cursorFg {
|
||||
screen.leftViewPort.scrollRow = k
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) drawLeftPane(style Style) {
|
||||
screen.buildLeftPane(style)
|
||||
w, h := termbox.Size()
|
||||
if w > 80 {
|
||||
w = w / 2
|
||||
}
|
||||
screen.leftViewPort.bytesPerRow = w
|
||||
screen.leftViewPort.numberOfRows = h - 2
|
||||
termboxUtil.FillWithChar('=', 0, 1, w, 1, style.defaultFg, style.defaultBg)
|
||||
startX, startY := 0, 3
|
||||
screen.leftViewPort.firstRow = startY
|
||||
treeOffset := 0
|
||||
maxCursor := screen.leftViewPort.numberOfRows * 2 / 3
|
||||
|
||||
if screen.leftViewPort.scrollRow > maxCursor {
|
||||
treeOffset = screen.leftViewPort.scrollRow - maxCursor
|
||||
}
|
||||
if len(screen.leftPaneBuffer) > 0 {
|
||||
for k, v := range screen.leftPaneBuffer[treeOffset:] {
|
||||
termboxUtil.DrawStringAtPoint(v.Text, startX, (startY + k - 1), v.Fg, v.Bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) buildRightPane(style Style) {
|
||||
screen.rightPaneBuffer = nil
|
||||
b, p, err := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if err == nil {
|
||||
if b != nil {
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(b.GetPath()), " → ")), style.defaultFg, style.defaultBg})
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{fmt.Sprintf("Buckets: %d", len(b.buckets)), style.defaultFg, style.defaultBg})
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{fmt.Sprintf("Pairs: %d", len(b.pairs)), style.defaultFg, style.defaultBg})
|
||||
} else if p != nil {
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(p.GetPath()), " → ")), style.defaultFg, style.defaultBg})
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{fmt.Sprintf("Key: %s", stringify([]byte(p.key))), style.defaultFg, style.defaultBg})
|
||||
|
||||
value := strings.Split(string(formatValue([]byte(p.val))), "\n")
|
||||
if len(value) == 1 {
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{fmt.Sprintf("Value: %s", value[0]), style.defaultFg, style.defaultBg})
|
||||
} else {
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{"Value:", style.defaultFg, style.defaultBg})
|
||||
for _, v := range value {
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{v, style.defaultFg, style.defaultBg})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{fmt.Sprintf("Path: %s", strings.Join(stringifyPath(screen.currentPath), " → ")), style.defaultFg, style.defaultBg})
|
||||
screen.rightPaneBuffer = append(screen.rightPaneBuffer,
|
||||
Line{err.Error(), termbox.ColorRed, termbox.ColorBlack})
|
||||
}
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) drawRightPane(style Style) {
|
||||
screen.buildRightPane(style)
|
||||
w, h := termbox.Size()
|
||||
if w > 80 {
|
||||
screen.rightViewPort.bytesPerRow = w / 2
|
||||
screen.rightViewPort.numberOfRows = h - 2
|
||||
// Screen is wide enough, split it
|
||||
termboxUtil.FillWithChar('=', 0, 1, w, 1, style.defaultFg, style.defaultBg)
|
||||
termboxUtil.FillWithChar('|', (w / 2), screen.rightViewPort.firstRow-1, (w / 2), h, style.defaultFg, style.defaultBg)
|
||||
// Clear the right pane
|
||||
termboxUtil.FillWithChar(' ', (w/2)+1, screen.rightViewPort.firstRow+2, w, h, style.defaultFg, style.defaultBg)
|
||||
|
||||
startX := (w / 2) + 2
|
||||
startY := 3
|
||||
maxScroll := len(screen.rightPaneBuffer) - screen.rightViewPort.numberOfRows
|
||||
if maxScroll < 0 {
|
||||
maxScroll = 0
|
||||
}
|
||||
if screen.rightViewPort.scrollRow > maxScroll {
|
||||
screen.rightViewPort.scrollRow = maxScroll
|
||||
}
|
||||
if len(screen.rightPaneBuffer) > 0 {
|
||||
for k, v := range screen.rightPaneBuffer[screen.rightViewPort.scrollRow:] {
|
||||
termboxUtil.DrawStringAtPoint(v.Text, startX, (startY + k - 1), v.Fg, v.Bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatValue(val []byte) []byte {
|
||||
// Attempt JSON parsing and formatting
|
||||
out, err := formatValueJSON(val)
|
||||
if err == nil {
|
||||
return out
|
||||
}
|
||||
return []byte(stringify([]byte(val)))
|
||||
}
|
||||
|
||||
func formatValueJSON(val []byte) ([]byte, error) {
|
||||
var jsonOut interface{}
|
||||
err := json.Unmarshal(val, &jsonOut)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
out, err := json.MarshalIndent(jsonOut, "", " ")
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) bucketToLines(bkt *BoltBucket, style Style) []Line {
|
||||
var ret []Line
|
||||
bfg, bbg := style.defaultFg, style.defaultBg
|
||||
if comparePaths(screen.currentPath, bkt.GetPath()) {
|
||||
bfg, bbg = style.cursorFg, style.cursorBg
|
||||
}
|
||||
bktPrefix := strings.Repeat(" ", len(bkt.GetPath())*2)
|
||||
if bkt.expanded {
|
||||
ret = append(ret, Line{bktPrefix + "- " + stringify([]byte(bkt.name)), bfg, bbg})
|
||||
for i := range bkt.buckets {
|
||||
ret = append(ret, screen.bucketToLines(&bkt.buckets[i], style)...)
|
||||
}
|
||||
for _, bp := range bkt.pairs {
|
||||
if screen.filter != "" && !strings.Contains(bp.key, screen.filter) {
|
||||
continue
|
||||
}
|
||||
pfg, pbg := style.defaultFg, style.defaultBg
|
||||
if comparePaths(screen.currentPath, bp.GetPath()) {
|
||||
pfg, pbg = style.cursorFg, style.cursorBg
|
||||
}
|
||||
prPrefix := strings.Repeat(" ", len(bp.GetPath())*2)
|
||||
var pairString string
|
||||
if AppArgs.NoValue {
|
||||
pairString = fmt.Sprintf("%s%s", prPrefix, stringify([]byte(bp.key)))
|
||||
} else {
|
||||
pairString = fmt.Sprintf("%s%s: %s", prPrefix, stringify([]byte(bp.key)), stringify([]byte(bp.val)))
|
||||
}
|
||||
ret = append(ret, Line{pairString, pfg, pbg})
|
||||
}
|
||||
} else {
|
||||
ret = append(ret, Line{bktPrefix + "+ " + stringify([]byte(bkt.name)), bfg, bbg})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startDeleteItem() bool {
|
||||
b, p, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := (w / 2), 6
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateConfirmModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
if b != nil {
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Delete Bucket '%s'?", b.name), inpW-1, termboxUtil.AlignCenter))
|
||||
} else if p != nil {
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Delete Pair '%s'?", p.key), inpW-1, termboxUtil.AlignCenter))
|
||||
}
|
||||
mod.Show()
|
||||
mod.SetText(termboxUtil.AlignText("This cannot be undone!", inpW-1, termboxUtil.AlignCenter))
|
||||
screen.confirmModal = mod
|
||||
screen.mode = modeDelete
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startFilter() bool {
|
||||
_, _, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := (w / 2), 6
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
mod.SetTitle(termboxUtil.AlignText("Filter", inpW, termboxUtil.AlignCenter))
|
||||
mod.SetValue(screen.filter)
|
||||
mod.Show()
|
||||
screen.inputModal = mod
|
||||
screen.mode = modeFilter
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startEditItem() bool {
|
||||
_, p, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := (w / 2), 6
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
if p != nil {
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Input new value for '%s'", p.key), inpW, termboxUtil.AlignCenter))
|
||||
mod.SetValue(p.val)
|
||||
}
|
||||
mod.Show()
|
||||
screen.inputModal = mod
|
||||
screen.mode = modeChangeVal
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startRenameItem() bool {
|
||||
b, p, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := (w / 2), 6
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
if b != nil {
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Rename Bucket '%s' to:", b.name), inpW, termboxUtil.AlignCenter))
|
||||
mod.SetValue(b.name)
|
||||
} else if p != nil {
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Rename Key '%s' to:", p.key), inpW, termboxUtil.AlignCenter))
|
||||
mod.SetValue(p.key)
|
||||
}
|
||||
mod.Show()
|
||||
screen.inputModal = mod
|
||||
screen.mode = modeChangeKey
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startInsertItemAtParent(tp BoltType) bool {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := w-1, 7
|
||||
if w > 80 {
|
||||
inpW, inpH = (w / 2), 7
|
||||
}
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
screen.inputModal = mod
|
||||
if len(screen.currentPath) <= 0 {
|
||||
// in the root directory
|
||||
if tp == typeBucket {
|
||||
mod.SetTitle(termboxUtil.AlignText("Create Root Bucket", inpW, termboxUtil.AlignCenter))
|
||||
screen.mode = modeInsertBucket | modeModToParent
|
||||
mod.Show()
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
var insPath string
|
||||
_, p, e := screen.db.getGenericFromPath(screen.currentPath[:len(screen.currentPath)-1])
|
||||
if e == nil && p != nil {
|
||||
insPath = strings.Join(screen.currentPath[:len(screen.currentPath)-2], " → ") + " → "
|
||||
} else {
|
||||
insPath = strings.Join(screen.currentPath[:len(screen.currentPath)-1], " → ") + " → "
|
||||
}
|
||||
titlePrfx := ""
|
||||
if tp == typeBucket {
|
||||
titlePrfx = "New Bucket: "
|
||||
} else if tp == typePair {
|
||||
titlePrfx = "New Pair: "
|
||||
}
|
||||
titleText := titlePrfx + insPath
|
||||
if len(titleText) > inpW {
|
||||
truncW := len(titleText) - inpW
|
||||
titleText = titlePrfx + "..." + insPath[truncW+3:]
|
||||
}
|
||||
if tp == typeBucket {
|
||||
mod.SetTitle(termboxUtil.AlignText(titleText, inpW, termboxUtil.AlignCenter))
|
||||
screen.mode = modeInsertBucket | modeModToParent
|
||||
mod.Show()
|
||||
return true
|
||||
} else if tp == typePair {
|
||||
mod.SetTitle(termboxUtil.AlignText(titleText, inpW, termboxUtil.AlignCenter))
|
||||
mod.Show()
|
||||
screen.mode = modeInsertPair | modeModToParent
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startInsertItem(tp BoltType) bool {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := w-1, 7
|
||||
if w > 80 {
|
||||
inpW, inpH = (w / 2), 7
|
||||
}
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
//mod.SetInputWrap(true)
|
||||
screen.inputModal = mod
|
||||
var insPath string
|
||||
_, p, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil && p != nil {
|
||||
insPath = strings.Join(screen.currentPath[:len(screen.currentPath)-1], " → ") + " → "
|
||||
} else {
|
||||
insPath = strings.Join(screen.currentPath, " → ") + " → "
|
||||
}
|
||||
titlePrfx := ""
|
||||
if tp == typeBucket {
|
||||
titlePrfx = "New Bucket: "
|
||||
} else if tp == typePair {
|
||||
titlePrfx = "New Pair: "
|
||||
}
|
||||
titleText := titlePrfx + insPath
|
||||
if len(titleText) > inpW {
|
||||
truncW := len(titleText) - inpW
|
||||
titleText = titlePrfx + "..." + insPath[truncW+3:]
|
||||
}
|
||||
if tp == typeBucket {
|
||||
mod.SetTitle(termboxUtil.AlignText(titleText, inpW, termboxUtil.AlignCenter))
|
||||
screen.mode = modeInsertBucket
|
||||
mod.Show()
|
||||
return true
|
||||
} else if tp == typePair {
|
||||
mod.SetTitle(termboxUtil.AlignText(titleText, inpW, termboxUtil.AlignCenter))
|
||||
mod.Show()
|
||||
screen.mode = modeInsertPair
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startExportValue() bool {
|
||||
_, p, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil && p != nil {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := (w / 2), 6
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Export value of '%s' to:", p.key), inpW, termboxUtil.AlignCenter))
|
||||
mod.SetValue("")
|
||||
mod.Show()
|
||||
screen.inputModal = mod
|
||||
screen.mode = modeExportValue
|
||||
return true
|
||||
}
|
||||
screen.setMessage("Couldn't do string export on " + screen.currentPath[len(screen.currentPath)-1] + "(did you mean 'X'?)")
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) startExportJSON() bool {
|
||||
b, p, e := screen.db.getGenericFromPath(screen.currentPath)
|
||||
if e == nil {
|
||||
w, h := termbox.Size()
|
||||
inpW, inpH := (w / 2), 6
|
||||
inpX, inpY := ((w / 2) - (inpW / 2)), ((h / 2) - inpH)
|
||||
mod := termboxUtil.CreateInputModal("", inpX, inpY, inpW, inpH, termbox.ColorWhite, termbox.ColorBlack)
|
||||
if b != nil {
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Export JSON of '%s' to:", b.name), inpW, termboxUtil.AlignCenter))
|
||||
mod.SetValue("")
|
||||
} else if p != nil {
|
||||
mod.SetTitle(termboxUtil.AlignText(fmt.Sprintf("Export JSON of '%s' to:", p.key), inpW, termboxUtil.AlignCenter))
|
||||
mod.SetValue("")
|
||||
}
|
||||
mod.Show()
|
||||
screen.inputModal = mod
|
||||
screen.mode = modeExportJSON
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) setMessage(msg string) {
|
||||
screen.message = msg
|
||||
screen.messageTime = time.Now()
|
||||
screen.messageTimeout = time.Second * 2
|
||||
}
|
||||
|
||||
/* setMessageWithTimeout lets you specify the timeout for the message
|
||||
* setting it to -1 means it won't timeout
|
||||
*/
|
||||
func (screen *BrowserScreen) setMessageWithTimeout(msg string, timeout time.Duration) {
|
||||
screen.message = msg
|
||||
screen.messageTime = time.Now()
|
||||
screen.messageTimeout = timeout
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) clearMessage() {
|
||||
screen.message = ""
|
||||
screen.messageTimeout = -1
|
||||
}
|
||||
|
||||
func (screen *BrowserScreen) refreshDatabase() {
|
||||
shadowDB := screen.db
|
||||
screen.db = screen.db.refreshDatabase()
|
||||
screen.db.syncOpenBuckets(shadowDB)
|
||||
}
|
||||
|
||||
func comparePaths(p1, p2 []string) bool {
|
||||
return strings.Join(p1, " → ") == strings.Join(p2, " → ")
|
||||
}
|
39
stringify.go
39
stringify.go
@ -1,39 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// stringify ensures that we can print only valid characters.
|
||||
// It's wrong to assume that everything is a string, since BoltDB is typeless.
|
||||
func stringify(v []byte) string {
|
||||
if utf8.Valid(v) {
|
||||
ok := true
|
||||
for _, r := range string(v) {
|
||||
if r < 0x20 {
|
||||
ok = false
|
||||
break
|
||||
} else if r >= 0x7f && r <= 0x9f {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
return string(v)
|
||||
}
|
||||
}
|
||||
if len(v) == 8 {
|
||||
return fmt.Sprintf("%v", binary.BigEndian.Uint64(v))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", v)
|
||||
}
|
||||
|
||||
func stringifyPath(path []string) []string {
|
||||
for k, v := range path {
|
||||
path[k] = stringify([]byte(v))
|
||||
}
|
||||
return path
|
||||
}
|
27
style.go
27
style.go
@ -1,27 +0,0 @@
|
||||
package main
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
/*
|
||||
Style Defines the colors for the terminal display, basically
|
||||
*/
|
||||
type Style struct {
|
||||
defaultBg termbox.Attribute
|
||||
defaultFg termbox.Attribute
|
||||
titleFg termbox.Attribute
|
||||
titleBg termbox.Attribute
|
||||
cursorFg termbox.Attribute
|
||||
cursorBg termbox.Attribute
|
||||
}
|
||||
|
||||
func defaultStyle() Style {
|
||||
var style Style
|
||||
style.defaultBg = termbox.ColorBlack
|
||||
style.defaultFg = termbox.ColorWhite
|
||||
style.titleFg = termbox.ColorBlack
|
||||
style.titleBg = termbox.ColorGreen
|
||||
style.cursorFg = termbox.ColorBlack
|
||||
style.cursorBg = termbox.ColorGreen
|
||||
|
||||
return style
|
||||
}
|
@ -1,39 +1,93 @@
|
||||
package main
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/br0xen/termbox-util"
|
||||
"git.bullercodeworks.com/brian/wandle"
|
||||
"github.com/nsf/termbox-go"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
/*
|
||||
Command is a struct for associating descriptions to keys
|
||||
*/
|
||||
const (
|
||||
ABT_CmdGotoBrowse = AboutId | iota
|
||||
)
|
||||
|
||||
type AboutMsg struct {
|
||||
source int
|
||||
data interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
type aboutScreen struct {
|
||||
ui *Ui
|
||||
|
||||
commands1 []Command
|
||||
commands2 []Command
|
||||
}
|
||||
|
||||
type Command struct {
|
||||
key string
|
||||
description string
|
||||
}
|
||||
|
||||
/*
|
||||
AboutScreen is just a basic 'int' type that we can extend to make this screen
|
||||
*/
|
||||
type AboutScreen int
|
||||
func NewAboutScreen(u *Ui) *aboutScreen { return &aboutScreen{ui: u} }
|
||||
|
||||
func drawCommandAtPoint(cmd Command, xPos int, yPos int, style Style) {
|
||||
termboxUtil.DrawStringAtPoint(fmt.Sprintf("%6s", cmd.key), xPos, yPos, style.defaultFg, style.defaultBg)
|
||||
termboxUtil.DrawStringAtPoint(cmd.description, xPos+8, yPos, style.defaultFg, style.defaultBg)
|
||||
func (s *aboutScreen) Init() wandle.Cmd {
|
||||
s.commands1 = []Command{
|
||||
{"h,←", "close parent"},
|
||||
{"j,↓", "down"},
|
||||
{"k,↑", "up"},
|
||||
{"l,→", "open item"},
|
||||
{"J", "scroll right pane down"},
|
||||
{"K", "scroll right pane up"},
|
||||
{"", ""},
|
||||
{"g", "goto top"},
|
||||
{"G", "goto bottom"},
|
||||
{"", ""},
|
||||
{"ctrl+f", "jump down"},
|
||||
{"ctrl+b", "jump up"},
|
||||
}
|
||||
|
||||
s.commands2 = []Command{
|
||||
{"p,P", "create pair/at parent"},
|
||||
{"b,B", "create bucket/at parent"},
|
||||
{"e", "edit value of pair"},
|
||||
{"r", "rename pair/bucket"},
|
||||
{"", ""},
|
||||
{"", ""},
|
||||
{"D", "delete item"},
|
||||
{"x,X", "export as string/json to file"},
|
||||
{"", ""},
|
||||
{"?", "this screen"},
|
||||
{"q", "quit program"},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (screen *AboutScreen) handleKeyEvent(event termbox.Event) int {
|
||||
return BrowserScreenIndex
|
||||
func (s *aboutScreen) Update(msg wandle.Msg) wandle.Cmd {
|
||||
switch msg := msg.(type) {
|
||||
case AboutMsg:
|
||||
return s.handleAboutMsg(msg)
|
||||
case termbox.Event:
|
||||
return s.handleTermboxEvent(msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (screen *AboutScreen) performLayout() {}
|
||||
func (s *aboutScreen) handleAboutMsg(msg AboutMsg) wandle.Cmd {
|
||||
switch msg.source {
|
||||
case ABT_CmdGotoBrowse:
|
||||
return wandle.SwitchScreenCmd(s.ui.browseScreen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (screen *AboutScreen) drawScreen(style Style) {
|
||||
defaultFg := style.defaultFg
|
||||
defaultBg := style.defaultBg
|
||||
func (s *aboutScreen) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *aboutScreen) View(style wandle.Style) {
|
||||
width, height := termbox.Size()
|
||||
template := [...]string{
|
||||
" _______ _______ ___ _______ _______ ______ _______ _ _ _______ _______ ______ ",
|
||||
@ -65,7 +119,7 @@ func (screen *AboutScreen) drawScreen(style Style) {
|
||||
title := "BoltBrowser"
|
||||
startY = 0
|
||||
yPos = 0
|
||||
termboxUtil.DrawStringAtPoint(title, (width-len(title))/2, startY, style.titleFg, style.titleBg)
|
||||
wandle.Print((width-len(title))/2, startY, style, title)
|
||||
} else {
|
||||
if height < 25 {
|
||||
startY = 0
|
||||
@ -74,12 +128,10 @@ func (screen *AboutScreen) drawScreen(style Style) {
|
||||
for _, line := range template {
|
||||
xPos = startX
|
||||
for _, runeValue := range line {
|
||||
bg := defaultBg
|
||||
displayRune := ' '
|
||||
if runeValue != ' ' {
|
||||
//bg = termbox.Attribute(125)
|
||||
displayRune = runeValue
|
||||
termbox.SetCell(xPos, yPos, displayRune, defaultFg, bg)
|
||||
termbox.SetCell(xPos, yPos, displayRune, style.Foreground, style.Background)
|
||||
}
|
||||
xPos++
|
||||
}
|
||||
@ -87,47 +139,18 @@ func (screen *AboutScreen) drawScreen(style Style) {
|
||||
}
|
||||
}
|
||||
yPos++
|
||||
versionString := fmt.Sprintf("Version: %0.1f", VersionNum)
|
||||
termboxUtil.DrawStringAtPoint(versionString, (width-len(versionString))/2, yPos, style.defaultFg, style.defaultBg)
|
||||
|
||||
commands1 := []Command{
|
||||
{"h,←", "close parent"},
|
||||
{"j,↓", "down"},
|
||||
{"k,↑", "up"},
|
||||
{"l,→", "open item"},
|
||||
{"J", "scroll right pane down"},
|
||||
{"K", "scroll right pane up"},
|
||||
{"", ""},
|
||||
{"g", "goto top"},
|
||||
{"G", "goto bottom"},
|
||||
{"", ""},
|
||||
{"ctrl+f", "jump down"},
|
||||
{"ctrl+b", "jump up"},
|
||||
}
|
||||
|
||||
commands2 := []Command{
|
||||
{"p,P", "create pair/at parent"},
|
||||
{"b,B", "create bucket/at parent"},
|
||||
{"e", "edit value of pair"},
|
||||
{"r", "rename pair/bucket"},
|
||||
{"", ""},
|
||||
{"", ""},
|
||||
{"D", "delete item"},
|
||||
{"x,X", "export as string/json to file"},
|
||||
{"", ""},
|
||||
{"?", "this screen"},
|
||||
{"q", "quit program"},
|
||||
}
|
||||
versionString := fmt.Sprintf("Version: %0.1f", viper.GetFloat64("version"))
|
||||
wandle.Print((width-len(versionString))/2, yPos, style, versionString)
|
||||
var maxCmd1 int
|
||||
for k := range commands1 {
|
||||
tst := len(commands1[k].key) + 1 + len(commands1[k].description)
|
||||
for k := range s.commands1 {
|
||||
tst := len(s.commands1[k].key) + 1 + len(s.commands1[k].description)
|
||||
if tst > maxCmd1 {
|
||||
maxCmd1 = tst
|
||||
}
|
||||
}
|
||||
var maxCmd2 int
|
||||
for k := range commands2 {
|
||||
tst := len(commands2[k].key) + 1 + len(commands2[k].description)
|
||||
for k := range s.commands2 {
|
||||
tst := len(s.commands2[k].key) + 1 + len(s.commands2[k].description)
|
||||
if tst > maxCmd2 {
|
||||
maxCmd2 = tst
|
||||
}
|
||||
@ -135,12 +158,13 @@ func (screen *AboutScreen) drawScreen(style Style) {
|
||||
xPos = (width / 2) - ((maxCmd1 + maxCmd2) / 2)
|
||||
yPos++
|
||||
|
||||
for k := range commands1 {
|
||||
drawCommandAtPoint(commands1[k], xPos, yPos+1+k, style)
|
||||
for k, cmd := range s.commands1 {
|
||||
wandle.Print(xPos, yPos+1+k, style, fmt.Sprintf("%6s %s", cmd.key, cmd.description))
|
||||
}
|
||||
for k := range commands2 {
|
||||
drawCommandAtPoint(commands2[k], xPos+40, yPos+1+k, style)
|
||||
for k, cmd := range s.commands2 {
|
||||
wandle.Print(xPos+40, yPos+1+k, style, fmt.Sprintf("%6s %s", cmd.key, cmd.description))
|
||||
}
|
||||
exitTxt := "Press any key to return to browser"
|
||||
termboxUtil.DrawStringAtPoint(exitTxt, (width-len(exitTxt))/2, height-1, style.titleFg, style.titleBg)
|
||||
st := style.Bold(true)
|
||||
wandle.Print((width-len(exitTxt))/2, height-1, st, exitTxt)
|
||||
}
|
75
ui/screen_browse.go
Normal file
75
ui/screen_browse.go
Normal file
@ -0,0 +1,75 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.bullercodeworks.com/brian/wandle"
|
||||
"git.bullercodeworks.com/brian/widdles"
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
const (
|
||||
//BS_CmdGotoAbout = BrowseId | iota
|
||||
)
|
||||
|
||||
type BrowseMsg struct {
|
||||
source int
|
||||
data interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
type browseScreen struct {
|
||||
ui *Ui
|
||||
dbPath string
|
||||
|
||||
menu *widdles.TopMenu
|
||||
status *widdles.Text
|
||||
statusTimeout time.Duration
|
||||
statusTime time.Time
|
||||
}
|
||||
|
||||
func NewBrowseScreen(u *Ui) *browseScreen {
|
||||
w, h := termbox.Size()
|
||||
return &browseScreen{
|
||||
ui: u,
|
||||
menu: widdles.NewTopMenu(0, 0, w),
|
||||
status: widdles.NewText("", 0, h),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *browseScreen) Init() wandle.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *browseScreen) Update(msg wandle.Msg) wandle.Cmd {
|
||||
switch msg := msg.(type) {
|
||||
case BrowseMsg:
|
||||
return s.handleBrowseMsg(msg)
|
||||
case termbox.Event:
|
||||
return s.handleTermboxEvent(msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *browseScreen) handleBrowseMsg(msg BrowseMsg) wandle.Cmd {
|
||||
switch msg.source {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *browseScreen) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
|
||||
if (msg.Type == termbox.EventKey && msg.Key == termbox.KeyEsc) || s.menu.IsActive() {
|
||||
return s.menu.Update(msg)
|
||||
}
|
||||
switch msg.Type {
|
||||
case termbox.EventKey:
|
||||
if msg.Ch == '?' {
|
||||
return wandle.SwitchScreenCmd(NewAboutScreen(s.ui))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *browseScreen) View(style wandle.Style) {
|
||||
}
|
31
ui/ui.go
Normal file
31
ui/ui.go
Normal file
@ -0,0 +1,31 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.bullercodeworks.com/brian/wandle"
|
||||
)
|
||||
|
||||
const (
|
||||
BrowseId = iota << 5
|
||||
AboutId
|
||||
)
|
||||
|
||||
type Ui struct {
|
||||
config Config
|
||||
wandle *wandle.Program
|
||||
|
||||
browseScreen *browseScreen
|
||||
}
|
||||
|
||||
func NewUi() *Ui {
|
||||
u := new(Ui)
|
||||
u.browseScreen = NewBrowseScreen(u)
|
||||
u.config = Config{
|
||||
sep: string(os.PathSeparator),
|
||||
}
|
||||
u.wandle = wandle.NewProgram(u.browseScreen)
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *Ui) Start() error { return u.wandle.Start() }
|
46
util/bolt_helpers.go
Normal file
46
util/bolt_helpers.go
Normal file
@ -0,0 +1,46 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func ToJsonString(b *bolt.Bucket) string {
|
||||
ret := "{"
|
||||
b.ForEach(func(k, v []byte) error {
|
||||
ret = fmt.Sprintf("%s\"%s\":", ret, string(k))
|
||||
if v == nil {
|
||||
ret = fmt.Sprintf("%s%s,", ret, ToJsonString(b.Bucket(k)))
|
||||
} else {
|
||||
ret = fmt.Sprintf("%s\"%s\",", ret, string(v))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
ret = fmt.Sprintf("%s}", ret[:len(ret)-1])
|
||||
return ret
|
||||
}
|
||||
|
||||
func LogToFile(s string) error {
|
||||
return WriteToFile("bolt-log", s+"\n", os.O_RDWR|os.O_APPEND)
|
||||
}
|
||||
|
||||
func WriteToFile(fn, s string, mode int) error {
|
||||
var f *os.File
|
||||
var err error
|
||||
if f == nil {
|
||||
f, err = os.OpenFile(fn, mode, 0660)
|
||||
}
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = f.WriteString(s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user