From e3990966493cc13f96eb0b3788478b67afcd5fff Mon Sep 17 00:00:00 2001 From: lawmatsuyama Date: Wed, 11 Dec 2024 00:15:41 -0300 Subject: [PATCH] feat: initial version --- LICENSE | 222 +-------- go.mod | 50 ++ go.sum | 683 ++++++++++++++++++++++++++++ helpers/helpers.go | 53 +++ infra/consumer.go | 59 +++ infra/grpc.go | 70 +++ infra/httpcli.go | 18 + infra/rabbitmq.go | 80 ++++ infra/rabbitmq_test.go | 24 + main.go | 50 ++ models/consumer.go | 20 + models/grpc.go | 8 + service/consumer.go | 74 +++ service/custom_resolver.go | 31 ++ service/grpc.go | 50 ++ service/marshal_uuid.go | 116 +++++ service/protocompiler.go | 436 ++++++++++++++++++ service/unmarshal_uuid.go | 70 +++ view/consumerscreen/component.go | 52 +++ view/grpcscreen/gprc.go | 93 ++++ view/menu/menu.go | 30 ++ view/object/button.go | 83 ++++ view/object/canvas_interface.go | 7 + view/object/completion_container.go | 106 +++++ view/object/entry_container.go | 80 ++++ view/object/entry_widget.go | 67 +++ view/object/form_container.go | 42 ++ view/object/json_container.go | 102 +++++ view/object/main_container.go | 107 +++++ view/object/map_entries.go | 29 ++ view/object/protobuf_entry.go | 10 + view/object/protodialog.go | 33 ++ view/object/text_entry.go | 11 + view/protocscreen/component.go | 96 ++++ view/tabs/tabs.go | 88 ++++ view/theme/custom_theme.go | 43 ++ 36 files changed, 2992 insertions(+), 201 deletions(-) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 helpers/helpers.go create mode 100644 infra/consumer.go create mode 100644 infra/grpc.go create mode 100644 infra/httpcli.go create mode 100644 infra/rabbitmq.go create mode 100644 infra/rabbitmq_test.go create mode 100644 main.go create mode 100644 models/consumer.go create mode 100644 models/grpc.go create mode 100644 service/consumer.go create mode 100644 service/custom_resolver.go create mode 100644 service/grpc.go create mode 100644 service/marshal_uuid.go create mode 100644 service/protocompiler.go create mode 100644 service/unmarshal_uuid.go create mode 100644 view/consumerscreen/component.go create mode 100644 view/grpcscreen/gprc.go create mode 100644 view/menu/menu.go create mode 100644 view/object/button.go create mode 100644 view/object/canvas_interface.go create mode 100644 view/object/completion_container.go create mode 100644 view/object/entry_container.go create mode 100644 view/object/entry_widget.go create mode 100644 view/object/form_container.go create mode 100644 view/object/json_container.go create mode 100644 view/object/main_container.go create mode 100644 view/object/map_entries.go create mode 100644 view/object/protobuf_entry.go create mode 100644 view/object/protodialog.go create mode 100644 view/object/text_entry.go create mode 100644 view/protocscreen/component.go create mode 100644 view/tabs/tabs.go create mode 100644 view/theme/custom_theme.go diff --git a/LICENSE b/LICENSE index 261eeb9..15261f6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +MIT License + +Copyright (c) 2022 Gabriel Beletti + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2fc4384 --- /dev/null +++ b/go.mod @@ -0,0 +1,50 @@ +module github.com/lawmatsuyama/protogui + +go 1.23.2 + +require ( + fyne.io/fyne/v2 v2.5.2 + fyne.io/x/fyne v0.0.0-20240803204126-8b5b5bfe65ef + github.com/bufbuild/protocompile v0.14.1 + github.com/google/uuid v1.6.0 + github.com/rabbitmq/amqp091-go v1.10.0 + golang.design/x/clipboard v0.7.0 + google.golang.org/grpc v1.68.1 + google.golang.org/protobuf v1.35.2 +) + +require ( + fyne.io/systray v1.11.0 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fredbi/uri v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect + github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect + github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect + github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect + github.com/go-text/render v0.2.0 // indirect + github.com/go-text/typesetting v0.2.0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect + github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rymdport/portal v0.2.6 // indirect + github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect + github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/yuin/goldmark v1.7.1 // indirect + golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 // indirect + golang.org/x/image v0.18.0 // indirect + golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..aa66d13 --- /dev/null +++ b/go.sum @@ -0,0 +1,683 @@ +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.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.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +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= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +fyne.io/fyne/v2 v2.5.2 h1:eSyGTmSkv10yAdAeHpDet6u2KkKxOGFc14kQu81We7Q= +fyne.io/fyne/v2 v2.5.2/go.mod h1:26gqPDvtaxHeyct+C0BBjuGd2zwAJlPkUGSBrb+d7Ug= +fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= +fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= +fyne.io/x/fyne v0.0.0-20240803204126-8b5b5bfe65ef h1:5qFhIzsvwmIybR4GlmENHHjOkQB5XsRZ6ujO0ktnsl4= +fyne.io/x/fyne v0.0.0-20240803204126-8b5b5bfe65ef/go.mod h1:1pa3ZVIopRWNvfSG4ZrSkcZ3mJ8qoHPZv4PT8/zpn1o= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +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/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8= +github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4= +github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg= +github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a h1:ybgRdYvAHTn93HW79bLiBiJwVL4jVeyGQRZMgImoeWs= +github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= +github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk= +github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= +github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= +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/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= +github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= +github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho= +github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +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.3/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/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +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/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8= +github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk= +github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +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/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.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= +github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= +github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/rymdport/portal v0.2.6 h1:HWmU3gORu7vWcpr7VSwUS2Xx1HtJXVcUuTqEZcMEsIg= +github.com/rymdport/portal v0.2.6/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= +github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= +github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= +github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= +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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= +github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +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= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.design/x/clipboard v0.7.0 h1:4Je8M/ys9AJumVnl8m+rZnIvstSnYj1fvzqYrU3TXvo= +golang.design/x/clipboard v0.7.0/go.mod h1:PQIvqYO9GP29yINEfsEn5zSQKAz3UgXmZKzDA6dnq2E= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +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-20190820162420-60c769a6c586/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-20210711020723-a769d52b0f97/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-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +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 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= +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/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +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/lint v0.0.0-20210508222113-6edffad5e616/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/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= +golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg= +golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc= +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/mod v0.4.2/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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20201110031124-69a78807bb2b/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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +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/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/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/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-20191005200804-aed5e4c7ecf9/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-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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +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-20190328211700-ab21143f2384/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-20191112195655-aa38f8e97acc/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-20200619180055-7c47624df98f/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-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +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/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +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-20200513103714-09dca8ec2884/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-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +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.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +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/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +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= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/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= diff --git a/helpers/helpers.go b/helpers/helpers.go new file mode 100644 index 0000000..8260faf --- /dev/null +++ b/helpers/helpers.go @@ -0,0 +1,53 @@ +package helpers + +import ( + "os" + "strings" +) + +func DirectoryExists(path string) bool { + info, err := os.Stat(path) + if os.IsNotExist(err) { + return false + } + return info.IsDir() +} + +func DeepDirectory(path string, targetDir string) (string, bool) { + info, err := os.Stat(path + "/" + targetDir) + if os.IsNotExist(err) { + newPath, _, ok := CutLastDirectoryPath(path) + if !ok { + return "", false + } + return DeepDirectory(newPath, targetDir) + } + return path, info.IsDir() +} + +func WalkDeepDirectory(path string, targetDir string) ([]string, bool) { + info, err := os.Stat(path + "/" + targetDir) + if os.IsNotExist(err) { + newPath, _, ok := CutLastDirectoryPath(path) + if !ok { + return nil, false + } + + dirs, ok := WalkDeepDirectory(newPath, targetDir) + if !ok { + return []string{}, false + } + + return append(dirs, path), true + } + + return []string{path}, info.IsDir() +} + +func CutLastDirectoryPath(path string) (prefix string, cutted string, ok bool) { + if strings.LastIndex(path, "/") == -1 { + return "", "", false + } + + return path[:strings.LastIndex(path, "/")], path[strings.LastIndex(path, "/")+1:], true +} diff --git a/infra/consumer.go b/infra/consumer.go new file mode 100644 index 0000000..9960eb8 --- /dev/null +++ b/infra/consumer.go @@ -0,0 +1,59 @@ +package infra + +import ( + "context" + + "github.com/lawmatsuyama/protogui/models" + amqp "github.com/rabbitmq/amqp091-go" +) + +type ConsumerMQ struct { + rabbitCli *RabbitMQ +} + +type MessageRabbit struct { + Body []byte + Type string +} + +func NewConsumerMQ(rabbitCli *RabbitMQ) *ConsumerMQ { + return &ConsumerMQ{rabbitCli: rabbitCli} +} + +func (r *ConsumerMQ) Get(ctx context.Context, req *models.GetMessagesRequest) ([]models.GetMessagesResponse, error) { + _, ch := r.rabbitCli.GetRabbitmqAMQP() + + acks := make([]amqp.Delivery, 0) + defer func() { + for _, n := range acks { + if req.Mode == models.Ack { + n.Ack(true) + continue + } + n.Nack(true, true) + } + }() + + messages := make([]models.GetMessagesResponse, 0) + + var ( + msg amqp.Delivery + ok bool + err error + ) + + for range req.Quantity { + msg, ok, err = ch.Get(req.Queue, false) + if !ok || err != nil { + break + } + + acks = append(acks, msg) + messages = append(messages, models.GetMessagesResponse{ + Payload: msg.Body, + Type: msg.Type, + }) + } + + return messages, err +} diff --git a/infra/grpc.go b/infra/grpc.go new file mode 100644 index 0000000..76f0d30 --- /dev/null +++ b/infra/grpc.go @@ -0,0 +1,70 @@ +package infra + +import ( + "fmt" + "sync" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" +) + +type GRPC struct { + conns map[string]*grpc.ClientConn + mux *sync.Mutex +} + +func NewGRPC() *GRPC { + return &GRPC{conns: make(map[string]*grpc.ClientConn), mux: &sync.Mutex{}} +} + +func (g *GRPC) GetConn(address string) (*grpc.ClientConn, error) { + g.mux.Lock() + defer g.mux.Unlock() + + conn, ok := g.conns[address] + if ok { + return conn, nil + } + + conn, err := createCommonClientConn(address) + if err != nil { + return nil, fmt.Errorf("failed to create grpc client %v", err) + } + + g.conns[address] = conn + return conn, nil +} + +func (g *GRPC) Close() { + for _, conn := range g.conns { + conn.Close() + } +} + +func createCommonClientConn(serverAddr string) (*grpc.ClientConn, error) { + conn, err := grpc.NewClient( + serverAddr, + + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: time.Millisecond * 100, + Multiplier: 1.1, + Jitter: 0.3, + MaxDelay: time.Second * 120, + }, + MinConnectTimeout: time.Second * 2, + }), + + grpc.WithTransportCredentials(insecure.NewCredentials()), + + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: time.Second * 30, + Timeout: time.Second * 10, + PermitWithoutStream: true, + }), + ) + return conn, err +} diff --git a/infra/httpcli.go b/infra/httpcli.go new file mode 100644 index 0000000..f30c0f6 --- /dev/null +++ b/infra/httpcli.go @@ -0,0 +1,18 @@ +package infra + +import ( + "net/http" + "time" +) + +type HTTPCli struct { + *http.Client +} + +func NewHTTPCli() *HTTPCli { + httpCli := &http.Client{ + Timeout: time.Second * 5, + } + + return &HTTPCli{httpCli} +} diff --git a/infra/rabbitmq.go b/infra/rabbitmq.go new file mode 100644 index 0000000..052d07e --- /dev/null +++ b/infra/rabbitmq.go @@ -0,0 +1,80 @@ +package infra + +import ( + "fmt" + "sync" + "time" + + amqp "github.com/rabbitmq/amqp091-go" +) + +type RabbitMQ struct { + user string + password string + host string + vhost string + port int + connAMQP *amqp.Connection + ch *amqp.Channel + mux sync.Mutex +} + +func NewRabbitMQ(user, password, host, vhost string, port int) *RabbitMQ { + return &RabbitMQ{ + user: user, + password: password, + host: host, + vhost: vhost, + port: port, + } +} + +func (r *RabbitMQ) GetRabbitmqAMQP() (*amqp.Connection, *amqp.Channel) { + r.mux.Lock() + defer r.mux.Unlock() + + if r.connAMQP == nil { + conn, err := r.createConnection("rabbiter") + if err != nil { + panic(fmt.Sprintf("failed to connect in rabbitmq %v\n", err)) + } + r.connAMQP = conn + } + + if r.ch == nil || r.ch.IsClosed() { + fmt.Println("open amqp channel") + ch, err := r.connAMQP.Channel() + if err != nil { + panic(fmt.Sprintf("failed to connect in rabbitmq %v\n", err)) + } + r.ch = ch + } + + return r.connAMQP, r.ch +} + +func (r *RabbitMQ) createConnection(connName string) (*amqp.Connection, error) { + url := fmt.Sprintf("amqp://%s:%s@%s:%d/%s", r.user, r.password, r.host, r.port, r.vhost) + + props := make(map[string]interface{}) + if connName != "" { + props["connection_name"] = connName + } + + conn, err := amqp.DialConfig(url, amqp.Config{ + Heartbeat: time.Second * time.Duration(10), + Properties: amqp.Table(props), + }) + + return conn, err +} + +func (r *RabbitMQ) Close() { + if r.ch != nil && !r.ch.IsClosed() { + r.ch.Close() + } + + if r.connAMQP != nil { + r.connAMQP.Close() + } +} diff --git a/infra/rabbitmq_test.go b/infra/rabbitmq_test.go new file mode 100644 index 0000000..f979e11 --- /dev/null +++ b/infra/rabbitmq_test.go @@ -0,0 +1,24 @@ +package infra + +import ( + "context" + "fmt" + "testing" + + "github.com/lawmatsuyama/protogui/models" +) + +func TestRabbitmqGet(t *testing.T) { + rab := NewConsumerMQ(NewRabbitMQ("guest", "guest", "localhost", "", 5672)) + + msgs, err := rab.Get(context.Background(), &models.GetMessagesRequest{Queue: "ff.registration.foreign-finder.dlq", Mode: models.Nack, Quantity: 4}) + if err != nil { + t.Fatalf(err.Error()) + } + + for _, msg := range msgs { + fmt.Println(string(msg.Payload)) + fmt.Println(msg.Type) + } + +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..4075bf0 --- /dev/null +++ b/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "os" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + + "golang.design/x/clipboard" + + "github.com/lawmatsuyama/protogui/infra" + "github.com/lawmatsuyama/protogui/service" + "github.com/lawmatsuyama/protogui/view/consumerscreen" + "github.com/lawmatsuyama/protogui/view/grpcscreen" + "github.com/lawmatsuyama/protogui/view/protocscreen" + + "github.com/lawmatsuyama/protogui/view/tabs" + customTheme "github.com/lawmatsuyama/protogui/view/theme" +) + +func main() { + os.Setenv("FYNE_SCALE", "0.8") + _ = clipboard.Init() + + rabbitconn := infra.NewRabbitMQ("guest", "guest", "localhost", "", 5672) + defer rabbitconn.Close() + + grpcConn := infra.NewGRPC() + defer grpcConn.Close() + + protogui := app.NewWithID("protogui") + protogui.Settings().SetTheme(customTheme.CustomTheme{}) + window := protogui.NewWindow("Fyne Desktop Client") + protoC := service.NewProtoCompile() + + consumersvc := service.NewConsumer(infra.NewConsumerMQ(rabbitconn), protoC) + grpcSvc := service.NewGRPC(protoC, grpcConn) + + protocScreen := protocscreen.New(protoC, window) + + consumerScreen := consumerscreen.NewConsumer(consumersvc, window) + grpcScreen := grpcscreen.NewGRPC(grpcSvc, protoC, window) + + tabs := tabs.NewTabs(protocScreen, window, consumerScreen, grpcScreen) + + window.SetContent(tabs.MainContainer()) + window.Resize(fyne.NewSize(1200, 900)) + window.CenterOnScreen() + window.ShowAndRun() +} diff --git a/models/consumer.go b/models/consumer.go new file mode 100644 index 0000000..e460575 --- /dev/null +++ b/models/consumer.go @@ -0,0 +1,20 @@ +package models + +type Mode string + +var ( + Nack Mode = "nack" + Ack Mode = "ack" +) + +type GetMessagesRequest struct { + Path string + Queue string + Quantity int + Mode Mode +} + +type GetMessagesResponse struct { + Type string + Payload []byte +} diff --git a/models/grpc.go b/models/grpc.go new file mode 100644 index 0000000..bae1a89 --- /dev/null +++ b/models/grpc.go @@ -0,0 +1,8 @@ +package models + +type GRPCRequest struct { + Address string + Path string + Method string + RequestJsonMsg string +} diff --git a/service/consumer.go b/service/consumer.go new file mode 100644 index 0000000..01ab417 --- /dev/null +++ b/service/consumer.go @@ -0,0 +1,74 @@ +package service + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "strings" + + "github.com/lawmatsuyama/protogui/infra" + "github.com/lawmatsuyama/protogui/models" +) + +type Consumer interface { + GetMessages(ctx context.Context, req *models.GetMessagesRequest) (string, error) +} + +type consumer struct { + consumerMQ *infra.ConsumerMQ + protoCompiler ProtoCompiler +} + +func NewConsumer(consumerMQ *infra.ConsumerMQ, protoCompiler ProtoCompiler) *consumer { + return &consumer{consumerMQ: consumerMQ, protoCompiler: protoCompiler} +} + +func (c *consumer) GetMessages(ctx context.Context, req *models.GetMessagesRequest) (string, error) { + if req.Quantity > 50 { + return "", errors.New("too many messages to retrieve") + } + + err := c.protoCompiler.RegisterProto(req.Path) + if err != nil { + return "", err + } + + msgs, err := c.getMessages(ctx, req) + if err != nil { + return "", err + } + + // res := make([]string, 0) + var res string + for _, msg := range msgs { + m, err := c.protoCompiler.DecodeNoBase64("", msg.Type, msg.Payload) + if err != nil { + fmt.Printf("failed to decode message %v\n", err) + s := base64.StdEncoding.EncodeToString(msg.Payload) + m = string(s) + } + + formatedMessage := c.formatMessage(msg.Type, m) + if res == "" { + res = formatedMessage + continue + } + res = fmt.Sprintf("%s\n\n%s", res, formatedMessage) + } + + return res, nil +} + +func (c *consumer) formatMessage(typename string, msg string) string { + line1 := strings.Repeat("-", 10) + header := fmt.Sprintf("typename: %s", typename) + line2 := strings.Repeat("-", 10) + + return fmt.Sprintf("%s\n%s\n%s\n%s", line1, header, line2, msg) +} + +func (c *consumer) getMessages(ctx context.Context, req *models.GetMessagesRequest) ([]models.GetMessagesResponse, error) { + + return c.consumerMQ.Get(ctx, req) +} diff --git a/service/custom_resolver.go b/service/custom_resolver.go new file mode 100644 index 0000000..71bdf55 --- /dev/null +++ b/service/custom_resolver.go @@ -0,0 +1,31 @@ +package service + +import ( + "fmt" + + "github.com/bufbuild/protocompile" + "google.golang.org/protobuf/types/descriptorpb" +) + +type CustomResolver struct { + descriptors map[string]*descriptorpb.FileDescriptorProto +} + +func NewCustomResolver(fds *descriptorpb.FileDescriptorSet) *CustomResolver { + resolver := &CustomResolver{ + descriptors: make(map[string]*descriptorpb.FileDescriptorProto), + } + for _, fd := range fds.File { + resolver.descriptors[fd.GetName()] = fd + } + return resolver +} + +func (r *CustomResolver) FindFileByPath(path string) (protocompile.SearchResult, error) { + if fd, ok := r.descriptors[path]; ok { + return protocompile.SearchResult{ + Proto: fd, + }, nil + } + return protocompile.SearchResult{}, fmt.Errorf("file %s not found", path) +} diff --git a/service/grpc.go b/service/grpc.go new file mode 100644 index 0000000..7e9e4d9 --- /dev/null +++ b/service/grpc.go @@ -0,0 +1,50 @@ +package service + +import ( + "context" + + "github.com/lawmatsuyama/protogui/infra" + "github.com/lawmatsuyama/protogui/models" +) + +type GRPC interface { + Invoke(ctx context.Context, req models.GRPCRequest) (string, error) +} + +type grpcSvc struct { + protoCompiler ProtoCompiler + connGRPC *infra.GRPC +} + +func NewGRPC(protoCompiler ProtoCompiler, connGRPC *infra.GRPC) *grpcSvc { + return &grpcSvc{protoCompiler: protoCompiler, connGRPC: connGRPC} +} + +func (g *grpcSvc) Invoke(ctx context.Context, reqGRPC models.GRPCRequest) (string, error) { + conn, err := g.connGRPC.GetConn(reqGRPC.Address) + if err != nil { + return "", err + } + + reqTypename, resTypename, err := g.protoCompiler.GetRequestResponseFromMethod(reqGRPC.Path, reqGRPC.Method) + if err != nil { + return "", err + } + + req, err := g.protoCompiler.JSONToProto("", reqTypename, reqGRPC.RequestJsonMsg) + if err != nil { + return "", err + } + + res, err := g.protoCompiler.JSONToProto("", resTypename, "{}") + if err != nil { + return "", err + } + + err = conn.Invoke(ctx, reqGRPC.Method, req, res) + if err != nil { + return "", err + } + + return g.protoCompiler.ProtoToJSON(res) +} diff --git a/service/marshal_uuid.go b/service/marshal_uuid.go new file mode 100644 index 0000000..0509ac5 --- /dev/null +++ b/service/marshal_uuid.go @@ -0,0 +1,116 @@ +package service + +import ( + "encoding/base64" + "fmt" + + "github.com/google/uuid" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type UUIDMarshaller struct { + fieldsType map[string]protoreflect.Kind + protoMessage protoreflect.ProtoMessage +} + +func NewUUIDMarshaller(protoMessage protoreflect.ProtoMessage) *UUIDMarshaller { + return &UUIDMarshaller{ + fieldsType: make(map[string]protoreflect.Kind), + protoMessage: protoMessage, + } +} + +func (unmarshal *UUIDMarshaller) UUIDs(jsonMessage map[string]any) { + unmarshal.buildFieldsTypeFromDescriptors("", unmarshal.protoMessage.ProtoReflect().Descriptor().Fields()) + unmarshal.uuidsInMap("", jsonMessage) +} + +func (unmarshal *UUIDMarshaller) buildFieldsTypeFromDescriptors(root string, mm protoreflect.FieldDescriptors) { + for i := range mm.Len() { + field := mm.Get(i) + if root == "" { + unmarshal.fieldsType[string(field.JSONName())] = field.Kind() + } else { + fieldKey := fmt.Sprintf("%s.%s", root, field.JSONName()) + unmarshal.fieldsType[fieldKey] = field.Kind() + } + + if field.Kind() == protoreflect.MessageKind { + var newRoot string + if root != "" { + newRoot = fmt.Sprintf("%s.%s", root, field.JSONName()) + } else { + newRoot = string(field.JSONName()) + } + + unmarshal.buildFieldsTypeFromDescriptors(newRoot, field.Message().Fields()) + } + } +} + +func (unmarshal *UUIDMarshaller) uuidsInMap(key string, mapMsg map[string]any) { + for k, item := range mapMsg { + newKey := fmt.Sprintf("%s.%s", key, k) + if key == "" { + newKey = k + } + s, ok := unmarshal.nextUUID(newKey, item) + if ok { + mapMsg[k] = s + } + } +} + +func (unmarshal *UUIDMarshaller) uuidsInSlice(key string, items []any) { + for i, item := range items { + s, ok := unmarshal.nextUUID(key, item) + if ok { + items[i] = s + } + } +} + +func (unmarshal *UUIDMarshaller) uuid(key string, v any) (string, bool) { + s, ok := v.(string) + if !ok { + return "", false + } + + id, err := uuid.Parse(s) + if err != nil { + return "", false + } + + k, ok := unmarshal.fieldsType[key] + if !ok { + return "", false + } + + if k != protoreflect.BytesKind { + return "", false + } + + // MarshalBinary never returns error so it's safe to omit error + b, _ := id.MarshalBinary() + + bencoded := base64.StdEncoding.EncodeToString(b) + + return bencoded, true +} + +func (unmarshal *UUIDMarshaller) nextUUID(key string, v any) (any, bool) { + m, ok := v.(map[string]any) + if ok { + unmarshal.uuidsInMap(key, m) + return "", false + } + + sl, ok := v.([]any) + if ok { + unmarshal.uuidsInSlice(key, sl) + return "", false + } + + return unmarshal.uuid(key, v) + +} diff --git a/service/protocompiler.go b/service/protocompiler.go new file mode 100644 index 0000000..d4adb48 --- /dev/null +++ b/service/protocompiler.go @@ -0,0 +1,436 @@ +package service + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "os" + "strings" + "sync" + + "github.com/bufbuild/protocompile" + "github.com/lawmatsuyama/protogui/helpers" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" +) + +type ProtoCompiler interface { + DecodeNoBase64(path string, tp string, message []byte) (string, error) + Decode(path string, tp string, message string) (string, error) + Encode(path string, tp string, jsonMessageString string) (string, error) + RegisterProto(path string) error + JSONToProto(path string, tp string, jsonMessageString string) (protoreflect.ProtoMessage, error) + ProtoToJSON(msg protoreflect.ProtoMessage) (string, error) + GetMessageType(path string, typename string) (protoreflect.MessageType, error) + TemplateJSON(path string, typename string) (string, error) + TemplateJSONFromMethod(path string, method string) (string, error) + GetRegisteredTypes(path string) ([]string, error) + GetRequestResponseFromMethod(path string, method string) (requestTypename string, responseTypename string, err error) + GetRegisteredMethods(path string) ([]string, error) +} + +func NewProtoCompile() ProtoCompiler { + return &protoCompiler{ + mux: &sync.Mutex{}, + methods: make(map[string]protoreflect.MethodDescriptor), + } +} + +type protoCompiler struct { + mux *sync.Mutex + methods map[string]protoreflect.MethodDescriptor +} + +func (p *protoCompiler) Decode(path string, typename string, message string) (string, error) { + messageType, err := p.GetMessageType(path, typename) + if err != nil { + return "", fmt.Errorf("failed to register proto %w", err) + } + + if messageType == nil { + return "", fmt.Errorf("unknown message type %s", typename) + } + + msg := messageType.New().Interface() + + message = strings.Trim(strings.TrimSpace(message), "\n") + bb, err := base64.StdEncoding.DecodeString(message) + if err != nil { + fmt.Printf("maybe message is not base64 %v\n", err) + bb = []byte(message) + } + + err = proto.Unmarshal(bb, msg) + if err != nil { + return "", fmt.Errorf("failed to proto unmarshal %w", err) + } + + return p.ProtoToJSON(msg) +} + +func (p *protoCompiler) DecodeNoBase64(path string, typename string, message []byte) (string, error) { + messageType, err := p.GetMessageType(path, typename) + if err != nil { + return "", fmt.Errorf("failed to register proto %w", err) + } + + if messageType == nil { + return "", fmt.Errorf("unknown message type %s", typename) + } + msg := messageType.New().Interface() + err = proto.Unmarshal(message, msg) + if err != nil { + return "", fmt.Errorf("failed to proto unmarshal %w", err) + } + + return p.ProtoToJSON(msg) +} + +func (p *protoCompiler) Encode(filePath string, tp string, jsonMessageString string) (string, error) { + protoMessage, err := p.JSONToProto(filePath, tp, jsonMessageString) + if err != nil { + return "", err + } + + b, err := proto.Marshal(protoMessage) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(b), nil +} + +func (p *protoCompiler) GetMessageType(path string, typename string) (protoreflect.MessageType, error) { + if path != "" { + err := p.RegisterProto(path) + if err != nil { + return nil, err + } + } + + messageType, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(typename)) + if err != nil { + fmt.Printf("failed to find message type %v\n", err) + return nil, err + } + + return messageType, nil +} + +func (p *protoCompiler) RegisterProto(path string) error { + if path == "" { + return nil + } + + home := os.Getenv("HOME") + + importPaths := []string{ + path, + home + "/.local/include", + "/usr/include", + } + + dirs, ok := helpers.WalkDeepDirectory(path, ".github") + if ok { + importPaths = append(importPaths, dirs...) + } + + fsResolvers, err := p.fileDesriptorResolvers(path) + if err != nil { + return err + } + + resolvers := protocompile.CompositeResolver{ + &protocompile.SourceResolver{ + ImportPaths: importPaths, + }, + } + + resolvers = append(resolvers, fsResolvers...) + + compiler := protocompile.Compiler{ + Resolver: resolvers, + } + + entries, err := os.ReadDir(path) + if err != nil { + return fmt.Errorf("failed to read dir %w", err) + } + + files := make([]string, 0) + for _, entry := range entries { + if !entry.IsDir() && strings.Contains(entry.Name(), ".proto") { + files = append(files, entry.Name()) + } + } + + p.mux.Lock() + defer p.mux.Unlock() + protoregistry.GlobalTypes = new(protoregistry.Types) + p.methods = make(map[string]protoreflect.MethodDescriptor) + for _, file := range files { + f, err := compiler.Compile(context.Background(), file) + if err != nil { + fmt.Printf("failed to compile protobuf file %v", err) + return err + } + + ff := f.FindFileByPath(file) + for i := range ff.Messages().Len() { + msgType, err := f.AsResolver().FindMessageByName(ff.Messages().Get(i).FullName()) + if err != nil { + fmt.Printf("failed to find message by name %v", err) + return err + } + + err = protoregistry.GlobalTypes.RegisterMessage(msgType) + if err != nil { + fmt.Printf("failed to register message %v", err) + return err + } + } + + for i := range ff.Services().Len() { + for j := range ff.Services().Get(i).Methods().Len() { + methodDescript := ff.Services().Get(i).Methods().Get(j) + serviceName := ff.Services().Get(i).FullName() + methodName := methodDescript.FullName().Name() + + methodFullName := fmt.Sprintf("/%s/%s", serviceName, methodName) + + p.methods[methodFullName] = methodDescript + } + } + + } + + return err +} + +func (p *protoCompiler) GetRegisteredTypes(path string) ([]string, error) { + err := p.RegisterProto(path) + if err != nil { + return nil, fmt.Errorf("failed to register proto %v", err) + } + + types := make([]string, 0) + protoregistry.GlobalTypes.RangeMessages(func(mt protoreflect.MessageType) bool { + types = append(types, string(mt.Descriptor().FullName())) + return true + }) + + return types, nil +} + +func (p *protoCompiler) JSONToProto(filePath string, tp string, jsonMessageString string) (protoreflect.ProtoMessage, error) { + messageType, err := p.GetMessageType(filePath, tp) + if err != nil { + return nil, fmt.Errorf("failed to register proto %w", err) + } + + protoMessage := messageType.New().Interface() + + jsonMessage := map[string]any{} + err = json.Unmarshal([]byte(jsonMessageString), &jsonMessage) + if err != nil { + fmt.Println(err) + return nil, fmt.Errorf("failed to unmarshal json message to map %w", err) + } + + marshal := NewUUIDMarshaller(protoMessage) + marshal.UUIDs(jsonMessage) + + jsonMessageByte, err := json.Marshal(jsonMessage) + if err != nil { + return nil, fmt.Errorf("failed to marshal json message %w", err) + } + + err = protojson.Unmarshal(jsonMessageByte, protoMessage) + if err != nil { + fmt.Println(err) + return nil, fmt.Errorf("failed to unmarshal json message to proto %w", err) + } + + return protoMessage, nil +} + +func (p *protoCompiler) ProtoToJSON(msg protoreflect.ProtoMessage) (string, error) { + jb, err := protojson.Marshal(msg) + if err != nil { + return "", fmt.Errorf("failed to protojson marshal %w", err) + } + + mapMsg := make(map[string]any) + + err = json.Unmarshal(jb, &mapMsg) + if err != nil { + return "", fmt.Errorf("failed to json unmarshal %w", err) + } + + unmarshal := NewUUIDUnmarshaller() + unmarshal.UUIDs(mapMsg) + + jb2, err := json.MarshalIndent(mapMsg, "", "\t") + if err != nil { + return "", fmt.Errorf("failed to json marshal indent %w", err) + } + + return string(jb2), nil +} + +func (p *protoCompiler) TemplateJSON(path string, typename string) (string, error) { + msg, err := p.GetMessageType(path, typename) + if err != nil { + return "", err + } + + template := msg.New().Interface() + + setDefaultValuesToTemplate(template) + + marshaller := protojson.MarshalOptions{ + EmitUnpopulated: true, + } + + b, err := marshaller.Marshal(template) + if err != nil { + return "", err + } + + return string(b), nil + +} + +func (p *protoCompiler) TemplateJSONFromMethod(path string, method string) (string, error) { + err := p.RegisterProto(path) + if err != nil { + return "", err + } + + met, ok := p.getMethod(method) + if !ok { + return "", errors.New("method not found in template json") + } + + inputTypename := met.Input().FullName() + + return p.TemplateJSON("", string(inputTypename)) +} + +func setDefaultValuesToTemplate(m proto.Message) { + descriptor := m.ProtoReflect().Descriptor() + for i := range descriptor.Fields().Len() { + field := descriptor.Fields().Get(i) + + if field.IsMap() { + if field.MapValue().Kind() != protoreflect.MessageKind { + continue + } + + k := field.MapKey().Default().MapKey() + x := m.ProtoReflect().Mutable(field).Map().Mutable(protoreflect.MapKey(k)).Message() + + setDefaultValuesToTemplate(x.Interface()) + continue + } + + if !field.IsList() && field.Kind() == protoreflect.MessageKind { + nestedMessage := m.ProtoReflect().Mutable(field).Message() + setDefaultValuesToTemplate(nestedMessage.Interface()) + continue + } + + if field.IsList() && field.Kind() == protoreflect.MessageKind { + newMsg := m.ProtoReflect().Mutable(field).List().AppendMutable() + setDefaultValuesToTemplate(newMsg.Message().Interface()) + } + + } +} + +func (p *protoCompiler) getResolver(filePath string) (*CustomResolver, error) { + descriptorBytes, err := os.ReadFile(filePath) + if err != nil { + fmt.Printf("Failed to open descriptor set: %v\n", err) + return nil, err + } + + fds := &descriptorpb.FileDescriptorSet{} + err = proto.Unmarshal(descriptorBytes, fds) + if err != nil { + fmt.Printf("Failed to unmarshal descriptor set: %v\n", err) + return nil, err + } + + resolver := NewCustomResolver(fds) + return resolver, nil +} + +func (p *protoCompiler) fileDesriptorResolvers(path string) ([]protocompile.Resolver, error) { + entries, err := os.ReadDir(path) + if err != nil { + return nil, err + } + + resolvers := make([]protocompile.Resolver, 0) + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".def") { + continue + } + + resolver, err := p.getResolver(path + "/" + entry.Name()) + if err != nil { + return nil, err + } + + resolvers = append(resolvers, resolver) + + } + + return resolvers, nil +} + +func (p *protoCompiler) GetRequestResponseFromMethod(path string, method string) (requestTypename string, responseTypename string, err error) { + err = p.RegisterProto(path) + if err != nil { + return "", "", err + } + + met, ok := p.getMethod(method) + if !ok { + return "", "", errors.New("method not found") + } + + return string(met.Input().FullName()), string(met.Output().FullName()), nil +} + +func (p *protoCompiler) GetRegisteredMethods(path string) ([]string, error) { + err := p.RegisterProto(path) + if err != nil { + return nil, err + } + + methods := make([]string, len(p.methods)) + i := 0 + for k := range p.methods { + methods[i] = string(k) + i++ + } + + return methods, nil +} + +func (p *protoCompiler) getMethod(method string) (protoreflect.MethodDescriptor, bool) { + met, ok := p.methods[method] + return met, ok +} + +// func (p *protoCompiler) getMethodFullName(desc protoreflect.MethodDescriptor) string { + +// } diff --git a/service/unmarshal_uuid.go b/service/unmarshal_uuid.go new file mode 100644 index 0000000..cf94555 --- /dev/null +++ b/service/unmarshal_uuid.go @@ -0,0 +1,70 @@ +package service + +import ( + "encoding/base64" + + "github.com/google/uuid" +) + +type UUIDUnmarshaller struct{} + +func NewUUIDUnmarshaller() *UUIDUnmarshaller { + return &UUIDUnmarshaller{} +} + +func (u *UUIDUnmarshaller) UUIDs(mapMsg map[string]any) { + u.uuidsInMap(mapMsg) +} + +func (u *UUIDUnmarshaller) uuidsInMap(mapMsg map[string]any) { + for k, item := range mapMsg { + s, ok := u.nextUUID(item) + if ok { + mapMsg[k] = s + } + } +} + +func (u *UUIDUnmarshaller) uuidsInSlice(items []any) { + for i, item := range items { + s, ok := u.nextUUID(item) + if ok { + items[i] = s + } + } +} + +func (u *UUIDUnmarshaller) uuid(v any) (string, bool) { + s, ok := v.(string) + if !ok { + return "", false + } + + bbb, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return "", false + } + + uid, err := uuid.FromBytes(bbb) + if err != nil { + return "", false + } + + return uid.String(), true +} + +func (u *UUIDUnmarshaller) nextUUID(v any) (string, bool) { + m, ok := v.(map[string]any) + if ok { + u.uuidsInMap(m) + return "", false + } + + sl, ok := v.([]any) + if ok { + u.uuidsInSlice(sl) + return "", false + } + + return u.uuid(v) +} diff --git a/view/consumerscreen/component.go b/view/consumerscreen/component.go new file mode 100644 index 0000000..4d4bcdc --- /dev/null +++ b/view/consumerscreen/component.go @@ -0,0 +1,52 @@ +package consumerscreen + +import ( + "context" + + "github.com/lawmatsuyama/protogui/models" + "github.com/lawmatsuyama/protogui/service" + "github.com/lawmatsuyama/protogui/view/object" + + "fyne.io/fyne/v2" +) + +type ConsumerScreen struct { + service service.Consumer + window fyne.Window +} + +func NewConsumer(service service.Consumer, window fyne.Window) *ConsumerScreen { + return &ConsumerScreen{service: service, window: window} +} + +func (p *ConsumerScreen) NewCanvasObject() fyne.CanvasObject { + return object.NewMainContainer(p.window, 100.00, false). + AddProtobufEntry(). + AddEntry("queue", 400, object.String). + AddEntry("quantity", 100, object.Int). + AddButton("get", 100, 2, p.actionGetButton). + VSplitOffset(0.30). + Apply() +} + +func (p *ConsumerScreen) actionGetButton(mapEntries object.MapEntries) func() { + protobufEntry := mapEntries.GetProtobufEntry() + queueEntry := mapEntries.GetEntryByLabelName("queue") + quantityEntry := mapEntries.GetEntryByLabelName("quantity") + resultTextEntry := mapEntries.GetResultTextEntry() + + return func() { + m, err := p.service.GetMessages(context.Background(), &models.GetMessagesRequest{ + Path: protobufEntry.TextString(), + Queue: queueEntry.TextString(), + Quantity: quantityEntry.TextInt(), + Mode: models.Nack, + }) + if err != nil { + resultTextEntry.SetText(err.Error()) + return + } + + resultTextEntry.SetText(m) + } +} diff --git a/view/grpcscreen/gprc.go b/view/grpcscreen/gprc.go new file mode 100644 index 0000000..7a85ca2 --- /dev/null +++ b/view/grpcscreen/gprc.go @@ -0,0 +1,93 @@ +package grpcscreen + +import ( + "context" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/theme" + "github.com/lawmatsuyama/protogui/models" + "github.com/lawmatsuyama/protogui/service" + "github.com/lawmatsuyama/protogui/view/object" +) + +type GRPCScreen struct { + service service.GRPC + protoC service.ProtoCompiler + window fyne.Window +} + +func NewGRPC(service service.GRPC, protoC service.ProtoCompiler, window fyne.Window) *GRPCScreen { + return &GRPCScreen{ + service: service, + window: window, + protoC: protoC, + } +} + +func (p *GRPCScreen) NewCanvasObject() fyne.CanvasObject { + return object.NewMainContainer(p.window, 100, true). + AddProtobufEntry(). + AddCompletion("method", 500). + AddButtonIconSide("", theme.ViewRefreshIcon(), 37, p.actionLoad). + AddEntry("server address", 500, object.String). + AddButton("call", 100, 2, p.actionCallButton). + AddButtonSide("template", 100, p.actionTemplateButton). + VSplitOffset(0.27). + Apply() +} + +func (p *GRPCScreen) actionCallButton(m object.MapEntries) func() { + protobufEntry := m.GetProtobufEntry() + methodsCompletion := m.GetCompletionByLabelName("method") + addressEntry := m.GetEntryByLabelName("server address") + inputTextEntry := m.GetInputTextEntry() + resultTextEntry := m.GetResultTextEntry() + + return func() { + res, err := p.service.Invoke( + context.Background(), + models.GRPCRequest{ + Path: protobufEntry.TextString(), + Method: methodsCompletion.Completion().Text, + RequestJsonMsg: inputTextEntry.Text, + Address: addressEntry.Text, + }, + ) + + if err != nil { + resultTextEntry.SetText(err.Error()) + return + } + + resultTextEntry.SetText(res) + } +} + +func (p *GRPCScreen) actionTemplateButton(m object.MapEntries) func() { + protobufEntry := m.GetProtobufEntry() + methodsCompletion := m.GetCompletionByLabelName("method") + inputTextEntry := m.GetInputTextEntry() + + return func() { + template, err := p.protoC.TemplateJSONFromMethod(protobufEntry.TextString(), methodsCompletion.Completion().Text) + if err == nil { + inputTextEntry.SetText(template) + } + } +} + +func (p *GRPCScreen) actionLoad(m object.MapEntries) func() { + protobufEntry := m.GetProtobufEntry() + resultTextEntry := m.GetResultTextEntry() + methodsCompletion := m.GetCompletionByLabelName("method") + + return func() { + methods, err := p.protoC.GetRegisteredMethods(protobufEntry.TextString()) + if err != nil { + resultTextEntry.SetText(err.Error()) + return + } + + methodsCompletion.SetAllOptions(methods) + } +} diff --git a/view/menu/menu.go b/view/menu/menu.go new file mode 100644 index 0000000..540fb55 --- /dev/null +++ b/view/menu/menu.go @@ -0,0 +1,30 @@ +package menu + +// import ( +// fyne "fyne.io/fyne/v2" +// "github.com/lawmatsuyama/rabbitgui/view/tabs" +// ) + +// type Menu struct { +// Tabs *tabs.Tabs +// Menu *fyne.MainMenu +// } + +// func NewMenu(t *tabs.Tabs) *Menu { +// return &Menu{ +// Tabs: t, +// Menu: fyne.NewMainMenu(), +// } +// } + +// func (m *Menu) NewMainMenu() *fyne.MainMenu { +// m.Menu.Items = make([]*fyne.Menu, 1) + +// m.Menu.Items[0] = fyne.NewMenu("File", +// fyne.NewMenuItem("New protodec", func() { +// m.Tabs.NewButton("protoc",) +// }), +// ) + +// return m.Menu +// } diff --git a/view/object/button.go b/view/object/button.go new file mode 100644 index 0000000..eca4adb --- /dev/null +++ b/view/object/button.go @@ -0,0 +1,83 @@ +package object + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/widget" +) + +type Button struct { + container *fyne.Container + button *widget.Button + curPosX float32 + objs []fyne.CanvasObject +} + +func NewButton(labelName string, width float32, startPosition float32, action func(), objs ...fyne.CanvasObject) *Button { + buttonWidget := widget.NewButton(labelName, action) + buttonWidget.Move(fyne.NewPos(startPosition, 2)) + buttonWidget.Resize(fyne.NewSize(width, 40)) + + button := &Button{ + button: buttonWidget, + objs: objs, + } + + button.container = container.NewWithoutLayout(buttonWidget) + + for i := range objs { + button.AddCanvasObject(objs[i]) + } + + button.Resize(fyne.NewSize(button.curPosX+10, 40)) + + return button +} + +func NewButtonIcon(labelName string, icon fyne.Resource, size float32, startPosition float32, action func(), objs ...fyne.CanvasObject) *Button { + buttonWidget := widget.NewButtonWithIcon(labelName, icon, action) + buttonWidget.Move(fyne.NewPos(startPosition, 2)) + buttonWidget.Resize(fyne.NewSquareSize(size)) + + button := &Button{ + button: buttonWidget, + objs: objs, + } + + button.container = container.NewWithoutLayout(buttonWidget) + + for i := range objs { + button.AddCanvasObject(objs[i]) + } + + button.Resize(fyne.NewSize(button.curPosX+10, 40)) + + return button +} + +func (o *Button) Move(pos fyne.Position) { + o.container.Move(pos) +} + +func (o *Button) Resize(size fyne.Size) { + o.container.Resize(size) +} + +func (o *Button) AddCanvasObject(obj fyne.CanvasObject) { + if o.curPosX == 0 { + o.curPosX = o.button.Size().Width + o.button.Position().X + 10 + } + + obj.Move(fyne.NewPos(o.curPosX, 2)) + o.objs = append(o.objs, obj) + o.curPosX = o.curPosX + obj.Size().Width + 10 + o.container.Add(obj) +} + +func (o *Button) Container() *fyne.Container { + return o.container +} + +func (o *Button) Button() *widget.Button { + return o.button +} diff --git a/view/object/canvas_interface.go b/view/object/canvas_interface.go new file mode 100644 index 0000000..347c2b8 --- /dev/null +++ b/view/object/canvas_interface.go @@ -0,0 +1,7 @@ +package object + +import "fyne.io/fyne/v2" + +type CanvasAdder interface { + AddCanvasObject(obj fyne.CanvasObject) +} diff --git a/view/object/completion_container.go b/view/object/completion_container.go new file mode 100644 index 0000000..fe84333 --- /dev/null +++ b/view/object/completion_container.go @@ -0,0 +1,106 @@ +package object + +import ( + "strings" + "sync" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/widget" + widgetx "fyne.io/x/fyne/widget" +) + +type CompletionContainer struct { + completion *widgetx.CompletionEntry + label *widget.Label + objs []fyne.CanvasObject + container *fyne.Container + curPosX float32 + options []string + mux sync.Mutex + maxOptionsFound int +} + +func NewCompletion(labelName string, width float32, startPosition float32, objs ...fyne.CanvasObject) *CompletionContainer { + label := widget.NewLabelWithStyle(labelName, fyne.TextAlignTrailing, fyne.TextStyle{Bold: true}) + label.Resize(fyne.NewSize(20, 30)) + + completionEntry := widgetx.NewCompletionEntry([]string{}) + label.Move(fyne.NewPos(startPosition, 2)) + completionEntry.Move(fyne.NewPos(startPosition+label.Size().Width+10, 2)) + completionEntry.Resize(fyne.NewSize(width, 37)) + + completionContainer := &CompletionContainer{ + completion: completionEntry, + label: label, + objs: objs, + maxOptionsFound: 10, + } + + completionEntry.OnChanged = func(s string) { + if len(s) < 2 { + completionEntry.HideCompletion() + return + } + + completionContainer.mux.Lock() + defer completionContainer.mux.Unlock() + + result := make([]string, 0) + found := 0 + for _, opt := range completionContainer.options { + optLower := strings.ToLower(opt) + sLower := strings.ToLower(s) + if strings.Contains(optLower, sLower) { + result = append(result, opt) + found++ + } + + if found >= 10 { + break + } + } + + completionEntry.SetOptions(result) + if len(result) > 0 { + completionEntry.ShowCompletion() + } + } + + completionContainer.container = container.NewWithoutLayout(label, completionEntry) + + for i := range objs { + completionContainer.AddCanvasObject(objs[i]) + } + + completionContainer.Resize(fyne.NewSize(completionContainer.curPosX+10, 40)) + + return completionContainer +} + +func (o *CompletionContainer) Resize(size fyne.Size) { + o.container.Resize(size) +} + +func (o *CompletionContainer) AddCanvasObject(obj fyne.CanvasObject) { + if o.curPosX == 0 { + o.curPosX = o.completion.Size().Width + o.completion.Position().X + 10 + } + + obj.Move(fyne.NewPos(o.curPosX, 2)) + o.objs = append(o.objs, obj) + o.curPosX = o.curPosX + obj.Size().Width + 10 + o.container.Add(obj) +} + +func (o *CompletionContainer) SetAllOptions(opts []string) { + o.options = opts +} + +func (o *CompletionContainer) Container() *fyne.Container { + return o.container +} + +func (o *CompletionContainer) Completion() *widgetx.CompletionEntry { + return o.completion +} diff --git a/view/object/entry_container.go b/view/object/entry_container.go new file mode 100644 index 0000000..1e6b576 --- /dev/null +++ b/view/object/entry_container.go @@ -0,0 +1,80 @@ +package object + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/widget" +) + +type EntryContainer struct { + container *fyne.Container + entry *EntryWidget + label *widget.Label + objects []fyne.CanvasObject + curPosX float32 +} + +func NewEntry(labelName string, width float32, startPosition float32, typeObj TypeObject, objs ...fyne.CanvasObject) *EntryContainer { + label := widget.NewLabelWithStyle(labelName, fyne.TextAlignTrailing, fyne.TextStyle{Bold: true}) + label.Resize(fyne.NewSize(20, 30)) + + entryWidget := NewEntryWidget(typeObj) + entryWidget.Resize(fyne.NewSize(width, 37)) + + entryWidget.TypedKey(&fyne.KeyEvent{}) + + label.Move(fyne.NewPos(startPosition, 2)) + entryWidget.Move(fyne.NewPos(startPosition+label.Size().Width+10, 2)) + + entry := &EntryContainer{ + entry: entryWidget, + label: label, + objects: objs, + } + + entry.container = container.NewWithoutLayout(label, entryWidget) + + for i := range objs { + entry.AddCanvasObject(objs[i]) + } + + entry.Resize(fyne.NewSize(entry.curPosX+10, 40)) + + return entry +} + +func (o *EntryContainer) SetText(text string) { + o.entry.SetText(text) +} + +func (o *EntryContainer) Move(pos fyne.Position) { + o.container.Move(pos) +} + +func (o *EntryContainer) Resize(size fyne.Size) { + o.container.Resize(size) +} + +func (o *EntryContainer) AddCanvasObject(obj fyne.CanvasObject) { + if o.curPosX == 0 { + o.curPosX = o.entry.Size().Width + o.entry.Position().X + 10 + } + + obj.Move(fyne.NewPos(o.curPosX, 2)) + o.objects = append(o.objects, obj) + o.curPosX = o.curPosX + obj.Size().Width + 10 + o.container.Add(obj) +} + +func (o *EntryContainer) Container() *fyne.Container { + return o.container +} + +func (o *EntryContainer) EntryWidget() *EntryWidget { + return o.entry +} + +// func (o *Entry) TextInt() (int, error) { +// text := o.entry.Text +// return strconv.Atoi(text) +// } diff --git a/view/object/entry_widget.go b/view/object/entry_widget.go new file mode 100644 index 0000000..2997a57 --- /dev/null +++ b/view/object/entry_widget.go @@ -0,0 +1,67 @@ +package object + +import ( + "strconv" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/widget" +) + +type TypeObject string + +const ( + String TypeObject = "string" + Int TypeObject = "int" +) + +type EntryWidget struct { + tp TypeObject + widget.Entry +} + +func NewEntryWidget(tp TypeObject) *EntryWidget { + entry := &EntryWidget{tp: tp} + entry.ExtendBaseWidget(entry) + return entry +} + +func (e *EntryWidget) TypedRune(r rune) { + if e.tp == String { + e.Entry.TypedRune(r) + return + } + + if r >= '0' && r <= '9' { + e.Entry.TypedRune(r) + } +} + +func (e *EntryWidget) TypedShortcut(shortcut fyne.Shortcut) { + if e.tp == String { + e.Entry.TypedShortcut(shortcut) + } + + paste, ok := shortcut.(*fyne.ShortcutPaste) + if !ok { + e.Entry.TypedShortcut(shortcut) + return + } + + content := paste.Clipboard.Content() + if _, err := strconv.Atoi(content); err == nil { + e.Entry.TypedShortcut(shortcut) + } +} + +func (e *EntryWidget) TextInt() int { + if e.tp != Int { + return 0 + } + + n, _ := strconv.Atoi(e.Text) + return n +} + +func (e *EntryWidget) TextString() string { + return e.Text +} diff --git a/view/object/form_container.go b/view/object/form_container.go new file mode 100644 index 0000000..f7dea3b --- /dev/null +++ b/view/object/form_container.go @@ -0,0 +1,42 @@ +package object + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" +) + +type FormContainer struct { + containers []fyne.CanvasObject + mainContainer *fyne.Container + curPosY float32 +} + +func NewFormContainer(containers ...fyne.CanvasObject) *FormContainer { + form := &FormContainer{mainContainer: container.NewWithoutLayout()} + + if len(containers) == 0 { + return form + } + + for i := range containers { + form.AddContainer(containers[i]) + } + + return form +} + +func (c *FormContainer) AddContainer(container fyne.CanvasObject) { + if c.curPosY == 0 { + c.curPosY = 2 + } + + container.Move(fyne.NewPos(2, c.curPosY)) + + c.mainContainer.Add(container) + c.containers = append(c.containers, container) + c.curPosY = c.curPosY + container.Size().Height + 10 +} + +func (c *FormContainer) MainContainer() *fyne.Container { + return c.mainContainer +} diff --git a/view/object/json_container.go b/view/object/json_container.go new file mode 100644 index 0000000..ffd0a9d --- /dev/null +++ b/view/object/json_container.go @@ -0,0 +1,102 @@ +package object + +import ( + "bytes" + "encoding/json" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "golang.design/x/clipboard" +) + +// JSONEntry is a widget entry for json input and json result. +// TODO: ExtendBaseWidget is not working when try to extend MultiLineEntry so should think in another solution in the future. +// type JSONEntry struct { +// *widget.Entry +// } + +type JSONContainer struct { + input *widget.Entry + result *widget.Entry + clip *widget.Button + container *fyne.Container +} + +func NewJSONContainer(withInput bool) *JSONContainer { + inputJSON := newInputJSON() + resultJSON := newResultJSON() + + jsonContainer := &JSONContainer{ + input: inputJSON, + result: resultJSON, + } + + clipContainer := jsonContainer.addClip(resultJSON) + + var containerJSON fyne.CanvasObject + if withInput { + containerJSON = container.NewHSplit(inputJSON, resultJSON) + } else { + containerJSON = container.NewStack(resultJSON) + } + + jsonContainer.container = container.NewBorder(nil, clipContainer, nil, nil, containerJSON) + + return jsonContainer +} + +func newInputJSON() *widget.Entry { + entry := widget.NewMultiLineEntry() + entry.Wrapping = fyne.TextWrapBreak + entry.OnChanged = func(s string) { + ok := json.Valid([]byte(s)) + if !ok { + return + } + + var prettyJSON bytes.Buffer + err := json.Indent(&prettyJSON, []byte(s), "", "\t") + if err != nil { + return + } + entry.SetText(prettyJSON.String()) + } + return entry +} + +func newResultJSON() *widget.Entry { + entry := widget.NewMultiLineEntry() + entry.Wrapping = fyne.TextWrapBreak + entry.Disable() + return entry +} + +func (j *JSONContainer) addClip(result *widget.Entry) *fyne.Container { + clip := widget.NewButtonWithIcon("copy", theme.ContentCopyIcon(), func() { + _ = clipboard.Write(clipboard.FmtText, []byte(result.Text)) + }) + + clip.Resize(fyne.NewSize(100, 50)) + clip.Move(fyne.NewPos(900, 2)) + j.clip = clip + + return container.NewWithoutLayout(clip) +} + +func (i *JSONContainer) Input() *widget.Entry { + return i.input +} + +func (i *JSONContainer) Result() *widget.Entry { + return i.result +} + +func (i *JSONContainer) Clip() *widget.Button { + return i.clip +} + +func (i *JSONContainer) Container() fyne.CanvasObject { + return i.container +} diff --git a/view/object/main_container.go b/view/object/main_container.go new file mode 100644 index 0000000..b9c8dc5 --- /dev/null +++ b/view/object/main_container.go @@ -0,0 +1,107 @@ +package object + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" +) + +type MainContainer struct { + mainContainer fyne.CanvasObject + jsonContainer *JSONContainer + mapEntries MapEntries + formContainer *FormContainer + positionY float32 + window fyne.Window + lastObject CanvasAdder + vsplitOffset float64 +} + +func NewMainContainer(window fyne.Window, positionY float32, withInputJSON bool) *MainContainer { + if positionY <= 0 { + positionY = 2 + } + jsonContainer := NewJSONContainer(withInputJSON) + + mapEntries := make(MapEntries) + mapEntries[LabelName("input")] = jsonContainer.Input() + mapEntries[LabelName("result")] = jsonContainer.Result() + + return &MainContainer{ + jsonContainer: jsonContainer, + positionY: positionY, + window: window, + mapEntries: mapEntries, + formContainer: NewFormContainer(), + } +} + +func (c *MainContainer) SetPosY(y float32) *MainContainer { + c.positionY = y + return c +} + +func (c *MainContainer) AddContainer(container fyne.CanvasObject) *MainContainer { + c.formContainer.AddContainer(container) + return c +} + +func (c *MainContainer) AddEntry(labelName LabelName, width float32, typeObj TypeObject, objs ...fyne.CanvasObject) *MainContainer { + entry := NewEntry(string(labelName), width, c.positionY, typeObj, objs...) + c.formContainer.AddContainer(entry.Container()) + c.mapEntries[labelName] = entry.EntryWidget() + c.lastObject = entry + return c +} + +func (c *MainContainer) AddCompletion(labelName LabelName, width float32, objs ...fyne.CanvasObject) *MainContainer { + entry := NewCompletion(string(labelName), width, c.positionY, objs...) + c.formContainer.AddContainer(entry.Container()) + c.mapEntries[labelName] = entry + c.lastObject = entry + return c +} + +func (c *MainContainer) AddButton(labelName LabelName, width float32, position float32, action func(MapEntries) func(), objs ...fyne.CanvasObject) *MainContainer { + button := NewButton(string(labelName), width, position, action(c.mapEntries), objs...) + c.lastObject = button + c.formContainer.AddContainer(button.Container()) + return c +} + +func (c *MainContainer) AddButtonSide(labelName LabelName, width float32, action func(MapEntries) func(), objs ...fyne.CanvasObject) *MainContainer { + button := NewButton(string(labelName), width, 2, action(c.mapEntries), objs...) + c.lastObject.AddCanvasObject(button.Button()) + return c +} + +func (c *MainContainer) AddButtonIcon(labelName LabelName, icon fyne.Resource, size float32, position float32, action func(MapEntries) func(), objs ...fyne.CanvasObject) *MainContainer { + button := NewButtonIcon(string(labelName), icon, size, position, action(c.mapEntries), objs...) + c.lastObject = button + c.formContainer.AddContainer(button.Container()) + return c +} + +func (c *MainContainer) AddButtonIconSide(labelName LabelName, icon fyne.Resource, size float32, action func(MapEntries) func(), objs ...fyne.CanvasObject) *MainContainer { + button := NewButtonIcon(string(labelName), icon, size, 2, action(c.mapEntries), objs...) + c.lastObject.AddCanvasObject(button.Button()) + return c +} + +func (c *MainContainer) AddProtobufEntry() *MainContainer { + entry := NewProtobufEntry(c.window) + c.formContainer.AddContainer(entry.Container()) + c.mapEntries[LabelName("protobuf")] = entry.EntryWidget() + return c +} + +func (c *MainContainer) VSplitOffset(offset float64) *MainContainer { + c.vsplitOffset = offset + return c +} + +func (c *MainContainer) Apply() fyne.CanvasObject { + mainContainer := container.NewVSplit(c.formContainer.MainContainer(), c.jsonContainer.Container()) + mainContainer.SetOffset(c.vsplitOffset) + c.mainContainer = mainContainer + return mainContainer +} diff --git a/view/object/map_entries.go b/view/object/map_entries.go new file mode 100644 index 0000000..bcf907e --- /dev/null +++ b/view/object/map_entries.go @@ -0,0 +1,29 @@ +package object + +import ( + "fyne.io/fyne/v2/widget" +) + +type LabelName string + +type MapEntries map[LabelName]any + +func (m MapEntries) GetEntryByLabelName(label string) *EntryWidget { + return m[LabelName(label)].(*EntryWidget) +} + +func (m MapEntries) GetCompletionByLabelName(label string) *CompletionContainer { + return m[LabelName(label)].(*CompletionContainer) +} + +func (m MapEntries) GetInputTextEntry() *widget.Entry { + return m[LabelName("input")].(*widget.Entry) +} + +func (m MapEntries) GetResultTextEntry() *widget.Entry { + return m[LabelName("result")].(*widget.Entry) +} + +func (m MapEntries) GetProtobufEntry() *EntryWidget { + return m.GetEntryByLabelName("protobuf") +} diff --git a/view/object/protobuf_entry.go b/view/object/protobuf_entry.go new file mode 100644 index 0000000..ed94a40 --- /dev/null +++ b/view/object/protobuf_entry.go @@ -0,0 +1,10 @@ +package object + +import "fyne.io/fyne/v2" + +func NewProtobufEntry(window fyne.Window) *EntryContainer { + protobufEntry := NewEntry("protobuf", 700, 100, String) + dialog := NewProtoDialogButton(protobufEntry, window) + protobufEntry.AddCanvasObject(dialog.Button()) + return protobufEntry +} diff --git a/view/object/protodialog.go b/view/object/protodialog.go new file mode 100644 index 0000000..a249c33 --- /dev/null +++ b/view/object/protodialog.go @@ -0,0 +1,33 @@ +package object + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +type ProtoDialogButton struct { + button *widget.Button +} + +func NewProtoDialogButton(entry *EntryContainer, window fyne.Window) *ProtoDialogButton { + button := widget.NewButtonWithIcon("", theme.FolderOpenIcon(), func() { + fDial := dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) { + if lu != nil { + entry.SetText(lu.Path()) + } + }, window) + fDial.Resize(fyne.NewSize(800, 800)) + fDial.Show() + }) + button.Resize(fyne.NewSquareSize(37)) + + return &ProtoDialogButton{ + button: button, + } +} + +func (b *ProtoDialogButton) Button() *widget.Button { + return b.button +} diff --git a/view/object/text_entry.go b/view/object/text_entry.go new file mode 100644 index 0000000..7983eff --- /dev/null +++ b/view/object/text_entry.go @@ -0,0 +1,11 @@ +package object + +import "fyne.io/fyne/v2" + +type TextEntry interface { + TextInt() int + TypedShortcut(shortcut fyne.Shortcut) + TypedRune(r rune) + SetText(text string) + TextString() string +} diff --git a/view/protocscreen/component.go b/view/protocscreen/component.go new file mode 100644 index 0000000..ba70311 --- /dev/null +++ b/view/protocscreen/component.go @@ -0,0 +1,96 @@ +package protocscreen + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/theme" + "github.com/lawmatsuyama/protogui/service" + "github.com/lawmatsuyama/protogui/view/object" +) + +type ProtoCScreen struct { + protoC service.ProtoCompiler + window fyne.Window +} + +func New(protoC service.ProtoCompiler, window fyne.Window) *ProtoCScreen { + return &ProtoCScreen{ + protoC: protoC, + window: window, + } +} + +func (p *ProtoCScreen) String() string { + return "protoc" +} + +func (p *ProtoCScreen) NewCanvasObject() fyne.CanvasObject { + return object.NewMainContainer(p.window, 100.00, true). + AddProtobufEntry(). + AddCompletion("typename", 500). + AddButtonIconSide("", theme.ViewRefreshIcon(), 37, p.actionLoad). + AddButton("decode", 100, 2, p.actionDecodeButton). + AddButtonSide("encode", 100, p.actionEncodeButton). + AddButtonSide("template", 100, p.actionTemplateButton). + VSplitOffset(0.2). + Apply() +} + +func (p *ProtoCScreen) actionDecodeButton(m object.MapEntries) func() { + protobufEntry := m.GetProtobufEntry() + typenameEntry := m.GetCompletionByLabelName("typename") + inputTextEntry := m.GetInputTextEntry() + resultTextEntry := m.GetResultTextEntry() + return func() { + res, err := p.protoC.Decode(protobufEntry.TextString(), typenameEntry.Completion().Text, inputTextEntry.Text) + if err != nil { + resultTextEntry.SetText(err.Error()) + return + } + resultTextEntry.SetText(res) + } +} + +func (p *ProtoCScreen) actionEncodeButton(m object.MapEntries) func() { + protobufEntry := m.GetProtobufEntry() + typenameEntry := m.GetCompletionByLabelName("typename") + inputTextEntry := m.GetInputTextEntry() + resultTextEntry := m.GetResultTextEntry() + + return func() { + res, err := p.protoC.Encode(protobufEntry.TextString(), typenameEntry.Completion().Text, inputTextEntry.Text) + if err != nil { + resultTextEntry.SetText(err.Error()) + return + } + resultTextEntry.SetText(res) + } +} + +func (p *ProtoCScreen) actionTemplateButton(m object.MapEntries) func() { + protobufEntry := m.GetProtobufEntry() + typenameEntry := m.GetCompletionByLabelName("typename") + inputTextEntry := m.GetInputTextEntry() + + return func() { + template, err := p.protoC.TemplateJSON(protobufEntry.TextString(), typenameEntry.Completion().Text) + if err == nil { + inputTextEntry.SetText(template) + } + } +} + +func (p *ProtoCScreen) actionLoad(m object.MapEntries) func() { + protobufEntry := m.GetProtobufEntry() + resultTextEntry := m.GetResultTextEntry() + typenameCompletion := m.GetCompletionByLabelName("typename") + + return func() { + types, err := p.protoC.GetRegisteredTypes(protobufEntry.TextString()) + if err != nil { + resultTextEntry.SetText(err.Error()) + return + } + + typenameCompletion.SetAllOptions(types) + } +} diff --git a/view/tabs/tabs.go b/view/tabs/tabs.go new file mode 100644 index 0000000..cf5b6e2 --- /dev/null +++ b/view/tabs/tabs.go @@ -0,0 +1,88 @@ +package tabs + +import ( + "sync" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + + "fyne.io/fyne/v2/widget" + "github.com/lawmatsuyama/protogui/view/consumerscreen" + "github.com/lawmatsuyama/protogui/view/grpcscreen" + + "github.com/lawmatsuyama/protogui/view/protocscreen" +) + +type Screen interface { + NewCanvasObject() fyne.CanvasObject +} +type Tabs struct { + mux sync.Mutex + tabs *container.AppTabs + protocScreen *protocscreen.ProtoCScreen + window fyne.Window + closeButton *widget.Button + tabsContainer *fyne.Container + mainContainer *fyne.Container + consumerScreen *consumerscreen.ConsumerScreen + grpcScreen *grpcscreen.GRPCScreen + buttonPosX float32 +} + +func NewTabs( + protocScreen *protocscreen.ProtoCScreen, + window fyne.Window, + consumerScreen *consumerscreen.ConsumerScreen, + grpcsScreen *grpcscreen.GRPCScreen) *Tabs { + t := &Tabs{ + tabs: container.NewAppTabs(), + protocScreen: protocScreen, + window: window, + consumerScreen: consumerScreen, + grpcScreen: grpcsScreen, + buttonPosX: 2, + } + + t.setup() + return t +} + +func (t *Tabs) setup() { + close := widget.NewButton("close tab", func() { t.removeTab() }) + close.Resize(fyne.NewSize(100, 30)) + close.Move(fyne.NewPos(1050, 20)) + t.tabs.Move(fyne.NewPos(2, 20)) + t.tabs.Resize(fyne.NewSize(1000, 800)) + t.closeButton = close + t.tabsContainer = container.NewWithoutLayout(t.closeButton, t.tabs) + + protocButton := t.NewButton("protoc", t.protocScreen) + consumerButton := t.NewButton("consumer", t.consumerScreen) + grpcButton := t.NewButton("grpc", t.grpcScreen) + menu := container.NewWithoutLayout(protocButton, consumerButton, grpcButton) + t.mainContainer = container.NewVBox(menu, t.tabsContainer) +} + +func (t *Tabs) NewButton(label string, screen Screen) *widget.Button { + button := widget.NewButton(label, func() { + tab := container.NewTabItem(label, screen.NewCanvasObject()) + tab.Content.Resize(fyne.NewSize(20, 90)) + t.tabs.Append(tab) + t.tabs.Select(tab) + }) + + button.Resize(fyne.NewSize(100, 50)) + button.Move(fyne.NewPos(t.buttonPosX, 2)) + t.buttonPosX = t.buttonPosX + button.Size().Width + 10 + return button +} + +func (t *Tabs) MainContainer() *fyne.Container { + return t.mainContainer +} + +func (t *Tabs) removeTab() { + t.mux.Lock() + defer t.mux.Unlock() + t.tabs.RemoveIndex(t.tabs.SelectedIndex()) +} diff --git a/view/theme/custom_theme.go b/view/theme/custom_theme.go new file mode 100644 index 0000000..c022ba0 --- /dev/null +++ b/view/theme/custom_theme.go @@ -0,0 +1,43 @@ +package theme + +import ( + "image/color" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/theme" +) + +type CustomTheme struct{} + +func (c CustomTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color { + switch name { + case theme.ColorNameDisabled: + if variant == theme.VariantLight { + return color.Black + } + return color.White + // case theme.ColorNameForeground: + // return color.Black + // case theme.ColorNameInputBackground: + // return color.White + default: + return theme.DefaultTheme().Color(name, variant) + } +} + +func (c CustomTheme) Font(style fyne.TextStyle) fyne.Resource { + return theme.DefaultTheme().Font(style) +} + +func (c CustomTheme) Icon(name fyne.ThemeIconName) fyne.Resource { + return theme.DefaultTheme().Icon(name) +} + +func (c CustomTheme) Size(name fyne.ThemeSizeName) float32 { + switch name { + case theme.SizeNamePadding: + return 8 + } + + return theme.DefaultTheme().Size(name) +}