diff --git a/.gitignore b/.gitignore index 699bf92..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,120 +1 @@ -# Created by https://www.toptal.com/developers/gitignore/api/jetbrains+all,go -# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+all,go - -### Go ### -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - -### Go Patch ### -/vendor/ -/Godeps/ - -### JetBrains+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### JetBrains+all Patch ### -# Ignores the whole .idea folder and all .iml files -# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 - -.idea/ - -# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 - -*.iml -modules.xml -.idea/misc.xml -*.ipr - -# Sonarlint plugin -.idea/sonarlint - -# Builds -dist/ - -# Wixtoolset output -*.wixobj - -# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all,godist/ +/target diff --git a/.goreleaser.yml b/.goreleaser.yml deleted file mode 100644 index 14100be..0000000 --- a/.goreleaser.yml +++ /dev/null @@ -1,38 +0,0 @@ -project_name: vumm -before: - hooks: - - go mod tidy -builds: - - env: - - CGO_ENABLED=0 - goos: - - linux - - windows - - darwin - goarch: - - 386 - - amd64 - ldflags: - - -s -w -X github.com/vumm/cli/internal/common.version={{.Version}} -X github.com/vumm/cli/internal/common.commit={{.ShortCommit}} -X github.com/vumm/cli/internal/common.date={{ .CommitDate }} -archives: - - name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}' - format_overrides: - - goos: windows - format: zip - files: - - README.md - - LICENSE -checksum: - name_template: 'checksums.txt' -snapshot: - name_template: "{{ .Tag }}-next" -changelog: - sort: asc - filters: - exclude: - - '^docs:' - - '^test:' - - '^cicd:' - - '^refactor' - - Merge pull request - - Merge branch diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d946d68 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug 'vumm'", + "cargo": { + "args": [ + "build", + "--bin=vumm_cli", + "--package=vumm_cli" + ], + "filter": { + "name": "vumm_cli", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}/vumm_cli" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'rusty-vumm'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=rusty-vumm", + "--package=rusty-vumm" + ], + "filter": { + "name": "rusty-vumm", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0665f62 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "rust-analyzer.linkedProjects": ["./Cargo.toml"], + "workbench.colorCustomizations": { + "minimap.background": "#00000000", + "scrollbar.shadow": "#00000000" + } +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..da3f7d2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2466 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "byteorder", + "log", + "security-framework 2.11.1", + "security-framework 3.7.0", + "windows-sys 0.60.2", + "zeroize", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags 2.11.0", + "libc", + "redox_syscall 0.7.2", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 3.7.0", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "parse_duration" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7037e5e93e0172a5a96874380bf73bc6ecef022e26fa25f2be26864d6b3ba95d" +dependencies = [ + "lazy_static", + "num", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rpassword" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.59.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "self_update" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca4e4e6f29fddb78b3e7a6e5a395e8274d4aca2d36b2278a297fa49673a5b7c7" +dependencies = [ + "hyper", + "indicatif", + "log", + "quick-xml", + "regex", + "reqwest", + "semver", + "serde_json", + "tempfile", + "urlencoding", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vumm" +version = "0.1.0" +dependencies = [ + "tokio", + "vumm_api", + "vumm_cli", +] + +[[package]] +name = "vumm_api" +version = "0.1.0" +dependencies = [ + "flate2", + "futures", + "reqwest", + "semver", + "serde", + "serde_json", + "tar", + "thiserror", + "tokio", +] + +[[package]] +name = "vumm_cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "directories", + "flate2", + "keyring", + "parse_duration", + "regex", + "rpassword", + "self_update", + "semver", + "serde", + "serde_json", + "tar", + "tokio", + "tracing", + "tracing-subscriber", + "vumm_api", + "vumm_installer", +] + +[[package]] +name = "vumm_installer" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "semver", + "serde", + "serde_json", + "thiserror", + "tokio", + "vumm_api", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a89f4650b770e4521aa6573724e2aed4704372151bd0de9d16a3bbabb87441a" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0459fce --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,38 @@ +[package] +authors = ["IllustrisJack", "paulhobbel"] +name = "vumm" +version = "0.1.0" +readme = "README.md" +description = "Venice Unleashed Mod Manager is a simple, fast and easy to use mod manager for Venice Unleashed" +homepage = "https://github.com/BF3RM/vumm" +repository = "https://github.com/BF3RM/vumm" +keywords = [ + "venice-unleashed", + "mod-manager", + "mod", + "venice", + "unleashed", + "bf3", + "battlefield", + "battlefield-3", +] +edition = "2021" +rust-version = "1.74.0" + +[workspace] +members = ["vumm_api", "vumm_cli", "vumm_installer"] + +[dependencies] +vumm_api = { path = "vumm_api" } +vumm_cli = { path = "vumm_cli" } + +tokio = { version = "1.35", features = ["full"] } + +[[example]] +name = "client_get_mod" +path = "examples/client_get_mod.rs" + +[profile.release] +strip = true +opt-level = "z" +lto = true diff --git a/README.md b/README.md index 6fb4795..bdcf98b 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,21 @@ So the following command will install the latest version of [BlueprintManager](h ```bash vumm install blueprintmanager ``` -You can also specify a specific release tag or [semver constraint](https://docs.npmjs.com/about-semantic-versioning): +You can also specify a specific release tag, version, or [semver constraint](https://docs.npmjs.com/about-semantic-versioning): ```bash -vumm install blueprintmanager@^1.0.0 # install the latest minor version of 1.x -vumm install blueprintmanager@~1.0.0 # install the latest patch version of 1.0.x -vumm install blueprintmanager@dev # install the latest dev version of blueprintmanager +vumm install blueprintmanager@latest # install the latest version (default) +vumm install blueprintmanager@^1.0.0 # install the latest minor version of 1.x +vumm install blueprintmanager@~1.0.0 # install the latest patch version of 1.0.x +vumm install blueprintmanager@dev # install the latest dev version of blueprintmanager +vumm install blueprintmanager@1.0.0 # install specific version +``` + +The `@tag` syntax is supported across commands that take a mod name: +```bash +vumm info +vumm install +vumm uninstall +vumm unpublish ``` ### Authenticating @@ -64,15 +74,20 @@ The readonly type of authentication is mainly what you want on your server, wher vumm login [--type ] ``` -Registering has the exact same syntax. If you registered successfully it's not needed to login as the access token will already be stored. +Registering has the exact same syntax. If you registered successfully it's not needed to login as the access token will already be stored securely in your system's credential manager. ```bash vumm register [--type ] ``` +To logout and clear stored credentials: +```bash +vumm logout +``` + ### Publish a mod As a mod creator you can simply publish your mod by running the following command in your mod source folder ```bash -vumm publish [-t ] [--private] +vumm publish [-t ] ``` It will read the [mod.json](https://docs.veniceunleashed.net/modding/your-first-mod/#the-modjson-file) to resolve the version, and it's dependencies automatically.\ By default your mod will be published to the `latest` tag, when specifying the `-t` flag you can specify another tag like `dev` or `beta`.\ @@ -81,12 +96,13 @@ To use this command you have to be logged in with publish rights. #### Granting access If you decide to publish a private mod, or willing to add multiple contributors to your mod the `grant` and `revoke` commands can be used. ```bash -vumm grant # give someone publish or read rights over a mod -vumm revoke # revoke all permissions of user over a mod +vumm grant # give someone publish or read rights over a mod +vumm revoke # revoke all permissions of user over a mod ``` If your mod is private you can use the grant command to give people read rights over the mod like so: ```bash vumm grant realitymod paulhobbel readonly +vumm revoke realitymod paulhobbel ``` ## Updating VUMM diff --git a/cli.go b/cli.go deleted file mode 100644 index bfac743..0000000 --- a/cli.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "github.com/vumm/cli/cmd" -) - -func main() { - cmd.Execute() -} diff --git a/cmd/install.go b/cmd/install.go deleted file mode 100644 index c6717a4..0000000 --- a/cmd/install.go +++ /dev/null @@ -1,59 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/apex/log" - "github.com/caarlos0/ctrlc" - "github.com/spf13/cobra" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/internal/pipe/fetcher" - "github.com/vumm/cli/internal/pipe/installer" - "github.com/vumm/cli/internal/pipeline" - "time" -) - -type installCmd struct { - cmd *cobra.Command - timeout time.Duration -} - -func newInstallCmd() *installCmd { - root := &installCmd{} - root.cmd = &cobra.Command{ - Use: "install [@]", - Aliases: []string{"add", "i"}, - Short: "Install a mod", - Long: "This command installs a mod and any mods that it depends on.", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("install requires at least a name of the mod") - } - return nil - }, - - RunE: func(cmd *cobra.Command, args []string) error { - name := args[0] - - ctx, cancel := context.NewWithTimeout(client, root.timeout) - defer cancel() - - start := time.Now() - log.Info("installing...") - - err := ctrlc.Default.Run(ctx, func() error { - return pipeline.Run(ctx, fetcher.New(name), installer.Pipe{}) - }) - - if err != nil { - return fmt.Errorf("failed installing after %0.2fs: %w", time.Since(start).Seconds(), err) - } - - log.Infof("successfully installed after %0.2fs", time.Since(start).Seconds()) - return nil - }, - } - - root.cmd.Flags().DurationVar(&root.timeout, "timeout", 30*time.Minute, "Timeout for the install process") - - return root -} diff --git a/cmd/login.go b/cmd/login.go deleted file mode 100644 index ada76dc..0000000 --- a/cmd/login.go +++ /dev/null @@ -1,71 +0,0 @@ -package cmd - -import ( - "bufio" - "fmt" - "github.com/apex/log" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vumm/cli/pkg/api" - "golang.org/x/crypto/ssh/terminal" - "os" - "strings" -) - -type loginCmd struct { - tokenType string - cmd *cobra.Command -} - -func newLoginCmd() *loginCmd { - root := &loginCmd{} - root.cmd = &cobra.Command{ - Use: "login", - Short: "Login with username and password", - Long: "Login with a username and password so you can publish your mod", - RunE: func(cmd *cobra.Command, args []string) error { - tokenType, err := api.PermissionTypeFromString(root.tokenType) - if err != nil { - return err - } - - reader := bufio.NewReader(os.Stdin) - - fmt.Print("Username: ") - username, err := reader.ReadString('\n') - if err != nil { - return err - } - - fmt.Print("Password: ") - bytePassword, err := terminal.ReadPassword(int(os.Stdin.Fd())) - if err != nil { - return err - } - fmt.Println() - - log.Info("logging in...") - token, _, err := client.Auth.Login(cmd.Context(), strings.TrimSpace(username), strings.TrimSpace(string(bytePassword)), tokenType) - if err != nil { - return err - } - - viper.Set("token", token.Token) - if err = viper.WriteConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - return viper.SafeWriteConfig() - } else { - return err - } - } - - log.Info("logged in successfully") - - return nil - }, - } - - root.cmd.Flags().StringVar(&root.tokenType, "type", "publish", "Type of token to be generated (publish, readonly)") - - return root -} diff --git a/cmd/pack.go b/cmd/pack.go deleted file mode 100644 index efae4c9..0000000 --- a/cmd/pack.go +++ /dev/null @@ -1,50 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/apex/log" - "github.com/caarlos0/ctrlc" - "github.com/spf13/cobra" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/internal/pipe/archiver" - "github.com/vumm/cli/internal/pipe/project" - "github.com/vumm/cli/internal/pipeline" - "time" -) - -type packCmd struct { - cmd *cobra.Command - timeout time.Duration -} - -func newPackCmd() *packCmd { - root := &packCmd{} - root.cmd = &cobra.Command{ - Use: "pack", - Short: "Create a tarball for a mod", - Long: "Packs the current mod into a tarball like the publish command would do", - - RunE: func(cmd *cobra.Command, args []string) error { - log.Info("packing...") - start := time.Now() - - ctx, cancel := context.NewWithTimeout(client, root.timeout) - defer cancel() - - err := ctrlc.Default.Run(ctx, func() error { - return pipeline.Run(ctx, project.Pipe{}, archiver.Pipe{Store: true}) - }) - if err != nil { - return fmt.Errorf("failed packing after %0.2fs: %w", time.Since(start).Seconds(), err) - } - - log.Infof("pack success after %0.2fs", time.Since(start).Seconds()) - - return nil - }, - } - - root.cmd.Flags().DurationVar(&root.timeout, "timeout", 30*time.Minute, "Timeout for the pack process") - - return root -} diff --git a/cmd/permissions.go b/cmd/permissions.go deleted file mode 100644 index 03e93b0..0000000 --- a/cmd/permissions.go +++ /dev/null @@ -1,89 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/apex/log" - "github.com/spf13/cobra" - "github.com/vumm/cli/pkg/api" - "strings" -) - -type grantCmd struct { - cmd *cobra.Command -} - -func newGrantCmd() *grantCmd { - root := &grantCmd{} - root.cmd = &cobra.Command{ - Use: "grant ", - Short: "Grant mod permissions to a user", - Long: `Give people mod permissions. Either grant someone with publish permissions -or give someone access to a private mod by granting them the readonly permission`, - Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 3 { - return fmt.Errorf("accepts 3 arg(s), received %d", len(args)) - } - - if args[2] != "readonly" && args[2] != "publish" { - return fmt.Errorf("invalid permission type specified, only readonly and publish are supported") - } - - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - mod, tag := extractModNameAndTag(args[0]) - permission, err := api.PermissionTypeFromString(args[2]) - if err != nil { - return err - } - _, err = client.Mods.GrantModPermissions(cmd.Context(), mod, tag, args[1], permission) - - if err != nil { - return err - } - - log.Infof("successfully granted user %s %s permissions on mod %s", args[1], args[2], args[0]) - - return nil - }, - } - - return root -} - -type revokeCmd struct { - cmd *cobra.Command -} - -func newRevokeCmd() *revokeCmd { - root := &revokeCmd{} - root.cmd = &cobra.Command{ - Use: "revoke ", - Short: "Revoke mod permissions of a user", - Long: `Revoke all mod permissions of a user`, - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - mod, tag := extractModNameAndTag(args[0]) - - _, err := client.Mods.RevokeModPermissions(cmd.Context(), mod, tag, args[1]) - if err != nil { - return err - } - - log.Infof("successfully revoked user %s permissions on mod %s", args[1], args[0]) - - return nil - }, - } - - return root -} - -func extractModNameAndTag(mod string) (string, string) { - parts := strings.SplitN(mod, "@", 2) - if len(parts) > 1 { - return parts[0], parts[1] - } - - return parts[0], "" -} diff --git a/cmd/publish.go b/cmd/publish.go deleted file mode 100644 index 268614d..0000000 --- a/cmd/publish.go +++ /dev/null @@ -1,53 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/apex/log" - "github.com/caarlos0/ctrlc" - "github.com/spf13/cobra" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/internal/pipe/archiver" - "github.com/vumm/cli/internal/pipe/project" - "github.com/vumm/cli/internal/pipe/publish" - "github.com/vumm/cli/internal/pipeline" - "time" -) - -type publishCmd struct { - cmd *cobra.Command - tag string - timeout time.Duration -} - -func newPublishCmd() *publishCmd { - root := &publishCmd{} - root.cmd = &cobra.Command{ - Use: "publish", - Short: "Publish a mod", - Long: "Publishes a mod to the registry so that it can by installed by others.", - - RunE: func(cmd *cobra.Command, args []string) error { - log.Info("publishing...") - start := time.Now() - - ctx, cancel := context.NewWithTimeout(client, root.timeout) - defer cancel() - - err := ctrlc.Default.Run(ctx, func() error { - return pipeline.Run(ctx, project.Pipe{}, archiver.Pipe{}, publish.Pipe{Tag: root.tag}) - }) - if err != nil { - return fmt.Errorf("failed publishing after %0.2fs: %w", time.Since(start).Seconds(), err) - } - - log.Infof("publish success after %0.2fs", time.Since(start).Seconds()) - - return nil - }, - } - - root.cmd.Flags().StringVarP(&root.tag, "tag", "t", "latest", "The version tag") - root.cmd.Flags().DurationVar(&root.timeout, "timeout", 30*time.Minute, "Timeout for the publish process") - - return root -} diff --git a/cmd/register.go b/cmd/register.go deleted file mode 100644 index 289017e..0000000 --- a/cmd/register.go +++ /dev/null @@ -1,70 +0,0 @@ -package cmd - -import ( - "bufio" - "fmt" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vumm/cli/pkg/api" - "golang.org/x/crypto/ssh/terminal" - "os" - "strings" -) - -type registerCmd struct { - tokenType string - cmd *cobra.Command -} - -func newRegisterCmd() *registerCmd { - root := ®isterCmd{} - root.cmd = &cobra.Command{ - Use: "register", - Short: "Register with username and password", - Long: "Register with a username and password so you can publish your mod or access private mods", - RunE: func(cmd *cobra.Command, args []string) error { - tokenType, err := api.PermissionTypeFromString(root.tokenType) - if err != nil { - return err - } - - reader := bufio.NewReader(os.Stdin) - - fmt.Print("Username: ") - username, err := reader.ReadString('\n') - if err != nil { - return err - } - - fmt.Print("Password: ") - bytePassword, err := terminal.ReadPassword(int(os.Stdin.Fd())) - if err != nil { - return err - } - fmt.Println() - - fmt.Println("registering...") - token, _, err := client.Auth.Register(cmd.Context(), strings.TrimSpace(username), strings.TrimSpace(string(bytePassword)), tokenType) - if err != nil { - return err - } - - viper.Set("token", token.Token) - if err = viper.WriteConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - return viper.SafeWriteConfig() - } else { - return err - } - } - - fmt.Println("registered successfully, you are automatically logged in") - - return nil - }, - } - - root.cmd.Flags().StringVar(&root.tokenType, "type", "publish", "Type of token to be generated (publish, readonly)") - - return root -} diff --git a/cmd/root.go b/cmd/root.go deleted file mode 100644 index 091c18c..0000000 --- a/cmd/root.go +++ /dev/null @@ -1,114 +0,0 @@ -package cmd - -import ( - "github.com/apex/log" - "github.com/apex/log/handlers/cli" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/vumm/cli/internal/common" - "github.com/vumm/cli/internal/updater" - "github.com/vumm/cli/pkg/api" - "os" -) - -func Execute() { - log.SetHandler(cli.Default) - newRootCmd().Execute() -} - -var client *api.Client - -type rootCmd struct { - cmd *cobra.Command - verbose bool -} - -func (cmd *rootCmd) Execute() { - if err := cmd.cmd.Execute(); err != nil { - log.WithError(err).Error("command failed") - os.Exit(1) - } -} - -func newRootCmd() *rootCmd { - cobra.OnInitialize(initConfig) - - root := &rootCmd{} - - root.cmd = &cobra.Command{ - Use: "vumm", - Short: "A mod workspace for Venice Unleashed", - Long: "Install and manage your favourite Venice Unleashed mods.", - SilenceErrors: true, - SilenceUsage: true, - Version: common.GetFullVersion(), - PersistentPreRun: func(cmd *cobra.Command, args []string) { - var options []api.Option - registryUrl := viper.GetString("registry") - if registryUrl != "" { - options = append(options, api.BaseURL(registryUrl)) - } - - token := viper.GetString("token") - if token != "" { - options = append(options, api.RoundTrip(&api.TokenAuthTransport{Token: token})) - } - - if root.verbose { - log.SetLevel(log.DebugLevel) - } - - var err error - client, err = api.New(options...) - if err != nil { - log.WithError(err).Fatal("Failed to initialise api client") - } - }, - PersistentPostRun: func(cmd *cobra.Command, args []string) { - release, available, _ := updater.PeriodicCheckForUpdates() - if available { - log.WithField("version", release.Version()).Info("update available, run vumm update to install") - } - }, - } - - root.cmd.PersistentFlags().String("registry", "", "Custom registry url") - root.cmd.PersistentFlags().String("token", "", "A access token to access the registry") - root.cmd.PersistentFlags().BoolVarP(&root.verbose, "verbose", "v", false, "Enable verbose output") - - cobra.CheckErr(viper.BindPFlag("registry", root.cmd.PersistentFlags().Lookup("registry"))) - cobra.CheckErr(viper.BindPFlag("token", root.cmd.PersistentFlags().Lookup("token"))) - - root.cmd.AddCommand(newInstallCmd().cmd) - //root.cmd.AddCommand(uninstallCmd) - - root.cmd.AddCommand(newPublishCmd().cmd) - root.cmd.AddCommand(newUnpublishCmd().cmd) - root.cmd.AddCommand(newPackCmd().cmd) - - root.cmd.AddCommand(newLoginCmd().cmd) - root.cmd.AddCommand(newRegisterCmd().cmd) - root.cmd.AddCommand(newGrantCmd().cmd) - root.cmd.AddCommand(newRevokeCmd().cmd) - - root.cmd.AddCommand(newUpdateCmd().cmd) - - return root -} - -func initConfig() { - home, err := os.UserHomeDir() - cobra.CheckErr(err) - - viper.AddConfigPath(home) - viper.SetConfigType("json") - viper.SetConfigName(".vumm") - viper.SetEnvPrefix("vumm") - viper.AutomaticEnv() - - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - cobra.CheckErr(err) - } - } -} diff --git a/cmd/uninstall.go b/cmd/uninstall.go deleted file mode 100644 index c18a5b1..0000000 --- a/cmd/uninstall.go +++ /dev/null @@ -1,23 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/spf13/cobra" -) - -var uninstallCmd = &cobra.Command{ - Use: "uninstall ", - Aliases: []string{"remove", "u"}, - Short: "Remove a installed mod", - Long: "This command uninstalls a mods and any leftover dependencies", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("install requires at least a name of the mod") - } - return nil - }, - - Run: func(cmd *cobra.Command, args []string) { - - }, -} diff --git a/cmd/unpublish.go b/cmd/unpublish.go deleted file mode 100644 index b3ddcef..0000000 --- a/cmd/unpublish.go +++ /dev/null @@ -1,48 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/Masterminds/semver" - "github.com/apex/log" - "github.com/spf13/cobra" - "strings" - "time" -) - -type unpublishCmd struct { - cmd *cobra.Command -} - -func newUnpublishCmd() *unpublishCmd { - root := &unpublishCmd{} - root.cmd = &cobra.Command{ - Use: "unpublish @", - Short: "Remove a mod from the registry", - Long: "Removes a mod version from the registry.", - Args: cobra.ExactArgs(1), - - RunE: func(cmd *cobra.Command, args []string) error { - parts := strings.Split(args[0], "@") - if len(parts) != 2 { - return fmt.Errorf("invalid mod version specified") - } - mod := parts[0] - version, err := semver.NewVersion(parts[1]) - if err != nil { - return fmt.Errorf("invalid mod version specified") - } - - start := time.Now() - log.Info("unpublishing...") - - if _, err = client.Mods.UnpublishModVersion(cmd.Context(), mod, version); err != nil { - return fmt.Errorf("unpublish unsuccessful after %0.2fs: %v", time.Since(start).Seconds(), err) - } - - log.Infof("unpublish successful after %0.2fs", time.Since(start).Seconds()) - return nil - }, - } - - return root -} diff --git a/cmd/update.go b/cmd/update.go deleted file mode 100644 index 201c35f..0000000 --- a/cmd/update.go +++ /dev/null @@ -1,41 +0,0 @@ -package cmd - -import ( - "github.com/apex/log" - "github.com/spf13/cobra" - "github.com/vumm/cli/internal/updater" -) - -type updateCmd struct { - cmd *cobra.Command -} - -func newUpdateCmd() *updateCmd { - root := &updateCmd{} - root.cmd = &cobra.Command{ - Use: "update", - Short: "Check for updates", - Long: "Check for updates and try to self update if a newer version is available", - RunE: func(cmd *cobra.Command, args []string) error { - log.Infof("checking for updates") - release, updateAvailable, err := updater.CheckForUpdates() - if err != nil { - return err - } - - if !updateAvailable { - log.Info("already up to date") - return nil - } - - log.Infof("new version available, installing version %s", release.Version()) - if _, err := updater.SelfUpdate(); err != nil { - return err - } - - return nil - }, - } - - return root -} diff --git a/examples/client_get_mod.rs b/examples/client_get_mod.rs new file mode 100644 index 0000000..f837aab --- /dev/null +++ b/examples/client_get_mod.rs @@ -0,0 +1,25 @@ +use vumm_api::Error; + +#[tokio::main] +async fn main() { + let client = vumm_api::Client::new(); + + let mod_name = String::from("mapeditor"); + let mod_version = String::from("0.2.0"); + + let mod_response = client.mods().get_version(mod_name, mod_version).await; + + match mod_response { + Ok(mod_) => { + println!("Mod: {:?}", mod_); + } + Err(e) => match e { + Error::NotFound(response) => { + println!("Mod not found: {}", response.status()); + } + _ => { + println!("Error: {}", e); + } + }, + } +} diff --git a/go.mod b/go.mod deleted file mode 100644 index 0248441..0000000 --- a/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module github.com/vumm/cli - -go 1.15 - -require ( - github.com/Masterminds/semver v1.5.0 - github.com/Masterminds/semver/v3 v3.1.0 - github.com/apex/log v1.9.0 - github.com/caarlos0/ctrlc v1.0.0 - github.com/creativeprojects/go-selfupdate v0.6.1 - github.com/google/go-cmp v0.5.6 - github.com/spf13/cobra v1.3.0 - github.com/spf13/viper v1.10.1 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 9ce8441..0000000 --- a/go.sum +++ /dev/null @@ -1,853 +0,0 @@ -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 v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -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.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -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= -code.gitea.io/sdk/gitea v0.13.2 h1:wAnT/J7Z62q3fJXbgnecoaOBh8CM1Qq0/DakWxiv4yA= -code.gitea.io/sdk/gitea v0.13.2/go.mod h1:lee2y8LeV3kQb2iK+hHlMqoadL4bp27QOkOV/hawLKg= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= -github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= -github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= -github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= -github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= -github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= -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-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/caarlos0/ctrlc v1.0.0 h1:2DtF8GSIcajgffDFJzyG15vO+1PuBWOMUdFut7NnXhw= -github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -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/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -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.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creativeprojects/go-selfupdate v0.6.1 h1:H+wSRrXJ8FbXRcIuN3u1fy7rO2Q03XURhEEcrSXRnxU= -github.com/creativeprojects/go-selfupdate v0.6.1/go.mod h1:Zr5CB9GEee179eQpho5k6eNAVv1NJoz3DcbkAz/bpVE= -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/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -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/groupcache v0.0.0-20210331224755-41bb18bfe9da/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/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= -github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -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-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/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-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -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-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -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/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -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/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -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-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.1.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/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.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -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.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= -github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= -github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= -github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= -github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= -github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc= -github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= -github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= -github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= -github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -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= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -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.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -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.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -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-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -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-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/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/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/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -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-20180906233101-161cd47e91fd/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-20181114220301-adae6a3d119a/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -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-20190923162816-aa69164e4478/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-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20181116152217-5ac8a444bdc5/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-20190222072716-a9d3bda3a223/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-20190422165155-953cdadca894/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-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/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-20210303074136-134d130e1a04/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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= -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.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -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.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -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 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -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-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -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.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/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-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/common/semver.go b/internal/common/semver.go deleted file mode 100644 index 778b384..0000000 --- a/internal/common/semver.go +++ /dev/null @@ -1,47 +0,0 @@ -package common - -import ( - "encoding/json" - "github.com/Masterminds/semver/v3" -) - -type SemverConstraints struct { - original string - *semver.Constraints -} - -func NewSemverConstraints(c string) (*SemverConstraints, error) { - constraints, err := semver.NewConstraint(c) - if err != nil { - return nil, err - } - - return &SemverConstraints{ - original: c, - Constraints: constraints, - }, nil -} - -func (c *SemverConstraints) String() string { - return c.original -} - -// UnmarshalJSON implements JSON.Unmarshaler interface. -func (c *SemverConstraints) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - temp, err := semver.NewConstraint(s) - if err != nil { - return err - } - c.original = s - c.Constraints = temp - return nil -} - -// MarshalJSON implements JSON.Marshaler interface. -func (c *SemverConstraints) MarshalJSON() ([]byte, error) { - return json.Marshal(c.String()) -} diff --git a/internal/common/strings.go b/internal/common/strings.go deleted file mode 100644 index b658e67..0000000 --- a/internal/common/strings.go +++ /dev/null @@ -1,16 +0,0 @@ -package common - -import "fmt" - -func ByteCountToHuman(b int) string { - const unit = 1000 - if b < unit { - return fmt.Sprintf("%d B", b) - } - div, exp := int64(unit), 0 - for n := b / unit; n >= unit; n /= unit { - div *= unit - exp++ - } - return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp]) -} diff --git a/internal/common/version.go b/internal/common/version.go deleted file mode 100644 index a536d8e..0000000 --- a/internal/common/version.go +++ /dev/null @@ -1,25 +0,0 @@ -package common - -import "fmt" - -var ( - version = "dev" - commit = "local" - date = "unknown" -) - -func GetVersion() string { - return version -} - -func GetFullVersion() string { - return fmt.Sprintf("%s-%s", version, commit) -} - -func GetBuildDate() string { - return date -} - -func IsRelease() bool { - return version != "dev" -} diff --git a/internal/common/vu.go b/internal/common/vu.go deleted file mode 100644 index 11cc89f..0000000 --- a/internal/common/vu.go +++ /dev/null @@ -1,62 +0,0 @@ -package common - -import ( - "encoding/json" - "fmt" - "github.com/Masterminds/semver" - "os" - "strings" -) - -var InternalMods = []string{"veniceext"} - -func IsInternalMod(mod string) bool { - for _, internalMod := range InternalMods { - if strings.ToLower(mod) == internalMod { - return true - } - } - - return false -} - -// ModMetadata holds info of the mod.json -type ModMetadata struct { - Name string `json:"Name"` - Version *semver.Version `json:"Version"` - Dependencies map[string]*SemverConstraints `json:"Dependencies"` -} - -func (m ModMetadata) String() string { - return fmt.Sprintf("%s@%s", m.Name, m.Version) -} - -func LoadModMetadata(metadataFile string) (ModMetadata, error) { - file, err := os.Open(metadataFile) - if err != nil { - return ModMetadata{}, err - } - defer file.Close() - - var metadata ModMetadata - err = json.NewDecoder(file).Decode(&metadata) - if err != nil { - return ModMetadata{}, err - } - - // Normalize names (to lowercase) - metadata.Name = strings.ToLower(metadata.Name) - - for dep, constraints := range metadata.Dependencies { - normalizedDep := strings.ToLower(dep) - - if normalizedDep == dep { - continue - } - delete(metadata.Dependencies, dep) - - metadata.Dependencies[normalizedDep] = constraints - } - - return metadata, nil -} diff --git a/internal/config/config.go b/internal/config/config.go deleted file mode 100644 index 063048e..0000000 --- a/internal/config/config.go +++ /dev/null @@ -1,32 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path/filepath" -) - -var cachedPath string - -func init() { - path := GetPath() - if _, err := os.Stat(path); os.IsNotExist(err) { - err := os.MkdirAll(path, 0755) - if err != nil { - panic(fmt.Errorf("failed creating config path: %v", err)) - } - } -} - -func GetPath() string { - if cachedPath != "" { - return cachedPath - } - - homedir, err := os.UserHomeDir() - if err != nil { - panic(fmt.Errorf("failed resolving config path: %v", err)) - } - - return filepath.Join(homedir, ".vumm") -} diff --git a/internal/context/context.go b/internal/context/context.go deleted file mode 100644 index 8229ef9..0000000 --- a/internal/context/context.go +++ /dev/null @@ -1,66 +0,0 @@ -package context - -import ( - "context" - "github.com/vumm/cli/internal/project" - "github.com/vumm/cli/internal/workspace" - "github.com/vumm/cli/pkg/api" - "reflect" - "time" -) - -type Context struct { - context.Context - - Project *project.Project - WorkingDirectory string - ModList *workspace.ModList - Dependencies map[string]api.ModVersion - Client *api.Client - - values map[interface{}]interface{} -} - -func NewWithTimeout(client *api.Client, duration time.Duration) (*Context, context.CancelFunc) { - ctx, cancel := context.WithTimeout(context.Background(), duration) - - return &Context{ - Context: ctx, - Client: client, - values: map[interface{}]interface{}{}, - }, cancel -} - -// SetValue adds a value to the context for later usage -func (ctx *Context) SetValue(key, val interface{}) { - ctx.values[key] = val -} - -// Value tries to lookup a value within the context -func (ctx *Context) Value(key interface{}) interface{} { - return ctx.values[key] -} - -// ValueAs tries to lookup a value within the context and assign it to target -func (ctx *Context) ValueAs(key, target interface{}) bool { - val, ok := ctx.values[key] - if !ok { - return false - } - - if target == nil { - panic("errors: target cannot be nil") - } - targetVal := reflect.ValueOf(target) - targetTyp := targetVal.Type() - if targetTyp.Kind() != reflect.Ptr || targetVal.IsNil() { - panic("errors: target must be a non-nil pointer") - } - - if reflect.TypeOf(val).AssignableTo(targetTyp) { - targetVal.Elem().Set(reflect.ValueOf(val).Elem()) - return true - } - - return false -} diff --git a/internal/middleware/logger.go b/internal/middleware/logger.go deleted file mode 100644 index ce6ff66..0000000 --- a/internal/middleware/logger.go +++ /dev/null @@ -1,25 +0,0 @@ -package middleware - -import ( - "github.com/apex/log" - "github.com/apex/log/handlers/cli" - "github.com/vumm/cli/internal/context" -) - -const DefaultPadding = 3 - -// Logging is a middleware that automatically increases padding on the logger -func Logging(title string, next Next) Next { - return func(ctx *context.Context) error { - defer func() { - if cli.Default.Padding >= DefaultPadding*2 { - cli.Default.Padding /= 2 - } - }() - - log.Infof(title) - cli.Default.Padding *= 2 - - return next(ctx) - } -} diff --git a/internal/middleware/next.go b/internal/middleware/next.go deleted file mode 100644 index 76fc4a9..0000000 --- a/internal/middleware/next.go +++ /dev/null @@ -1,5 +0,0 @@ -package middleware - -import "github.com/vumm/cli/internal/context" - -type Next func(ctx *context.Context) error diff --git a/internal/pipe/archiver/archiver.go b/internal/pipe/archiver/archiver.go deleted file mode 100644 index 66f218f..0000000 --- a/internal/pipe/archiver/archiver.go +++ /dev/null @@ -1,53 +0,0 @@ -package archiver - -import ( - "bytes" - "fmt" - "github.com/apex/log" - "github.com/vumm/cli/internal/common" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/pkg/tar" - "os" - "path/filepath" -) - -type Pipe struct { - Store bool -} - -func (Pipe) String() string { - return "archiver" -} - -func (p Pipe) Run(ctx *context.Context) error { - log.Info("compressing files") - - packager := tar.NewPackager() - packager.SetFileFilter(func(filePath string) bool { - return !ctx.Project.Ignorer.Matches(filePath) - }) - - var buf bytes.Buffer - if err := packager.Compress(ctx.Project.Directory, &buf); err != nil { - return err - } - log.Infof("compressed files to archive of %s", common.ByteCountToHuman(buf.Len())) - - if p.Store { - fileName := fmt.Sprintf("%s-%s.tgz", ctx.Project.Metadata.Name, ctx.Project.Metadata.Version) - log.WithField("file", fileName).Infof("saving archive") - file, err := os.Create(filepath.Join(ctx.WorkingDirectory, fileName)) - if err != nil { - return err - } - defer file.Close() - n, err := file.Write(buf.Bytes()) - if err != nil { - return err - } - log.Debugf("wrote %s to %s", common.ByteCountToHuman(n), file.Name()) - } - - ctx.SetValue("archive", &buf) - return nil -} diff --git a/internal/pipe/fetcher/dependency.go b/internal/pipe/fetcher/dependency.go deleted file mode 100644 index 6e199a2..0000000 --- a/internal/pipe/fetcher/dependency.go +++ /dev/null @@ -1,43 +0,0 @@ -package fetcher - -import ( - "github.com/vumm/cli/internal/common" - "strings" -) - -type modDependency struct { - Name string - Tag string - VersionConstraints *common.SemverConstraints -} - -func resolveModDependency(name string, version string) modDependency { - var err error - var tag string - var constraints *common.SemverConstraints - - // First try to parse constraint - constraints, err = common.NewSemverConstraints(version) - - // Else set it as tag - if err != nil { - tag = version - } - - return modDependency{ - Name: name, - Tag: tag, - VersionConstraints: constraints, - } -} - -func resolveModDependencyFromString(mod string) modDependency { - parts := strings.SplitN(mod, "@", 2) - - version := "latest" - if len(parts) > 1 { - version = parts[1] - } - - return resolveModDependency(parts[0], version) -} diff --git a/internal/pipe/fetcher/dependency_test.go b/internal/pipe/fetcher/dependency_test.go deleted file mode 100644 index a68a054..0000000 --- a/internal/pipe/fetcher/dependency_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package fetcher - -import ( - "testing" -) - -type modDependencyTest struct { - name, version string - expectedTag string - hasConstraints bool -} - -type modDependencyStringTest struct { - nameAndVersion string - expectedTag string - hasConstraints bool -} - -func TestResolveModDependency(t *testing.T) { - tests := []modDependencyTest{ - {"realitymod", "latest", "latest", false}, - {"realitymod", "0.1.0", "", true}, - {"realitymod", ">=0.1.0", "", true}, - } - - for _, test := range tests { - dep := resolveModDependency(test.name, test.version) - if dep.Tag != test.expectedTag { - t.Errorf("Tag %s not equal to expected %s", dep.Tag, test.expectedTag) - } - hasConstraints := dep.VersionConstraints != nil - if hasConstraints != test.hasConstraints { - t.Errorf("VersionConstraints %v not equal to expected %v", hasConstraints, test.hasConstraints) - } - } -} - -func TestResolveModDependencyFromString(t *testing.T) { - tests := []modDependencyStringTest{ - {"realitymod@latest", "latest", false}, - {"realitymod@0.1.0", "", true}, - {"realitymod@>=0.1.0", "", true}, - } - - for _, test := range tests { - dep := resolveModDependencyFromString(test.nameAndVersion) - if dep.Tag != test.expectedTag { - t.Errorf("Tag %s not equal to expected %s", dep.Tag, test.expectedTag) - } - hasConstraints := dep.VersionConstraints != nil - if hasConstraints != test.hasConstraints { - t.Errorf("VersionConstraints %v not equal to expected %v", hasConstraints, test.hasConstraints) - } - } -} diff --git a/internal/pipe/fetcher/fetcher.go b/internal/pipe/fetcher/fetcher.go deleted file mode 100644 index 71061fd..0000000 --- a/internal/pipe/fetcher/fetcher.go +++ /dev/null @@ -1,99 +0,0 @@ -package fetcher - -import ( - "fmt" - "github.com/apex/log" - "github.com/vumm/cli/internal/common" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/pkg/api" -) - -// Pipe is a pipe that tries resolving the given mod and its dependencies -type Pipe struct { - mod string -} - -func New(mod string) Pipe { - return Pipe{mod: mod} -} - -func (Pipe) String() string { - return "gathering dependencies" -} - -func (p Pipe) Run(ctx *context.Context) error { - if ctx.Dependencies != nil { - return fmt.Errorf("dependencies where already resolved") - } - ctx.Dependencies = map[string]api.ModVersion{} - - // List with unresolved mod dependencies - unresolved := []modDependency{resolveModDependencyFromString(p.mod)} - - // TODO: Handle ctx.Done() - for len(unresolved) > 0 { - // Pop first unresolved - dep := unresolved[0] - unresolved = unresolved[1:] - - log.WithField("mod", dep.Name).Info("fetching metadata") - version, err := p.resolveModVersion(ctx, dep) - if err != nil { - return err - } - log.WithFields(log.Fields{ - "mod": version.Name, - "version": version.Version, - }).Debug("fetched metadata") - - if len(version.Dependencies) > 0 { - for name, version := range version.Dependencies { - if common.IsInternalMod(name) { - log.Debugf("skipped %s, is internal mod", name) - continue - } - - dep := resolveModDependency(name, version) - shouldAdd, err := p.checkCanAddDependency(ctx, dep) - if err != nil { - return err - } - - if shouldAdd { - unresolved = append(unresolved, dep) - } - } - } - - ctx.Dependencies[version.Name] = version - } - - return nil -} - -func (p Pipe) resolveModVersion(ctx *context.Context, dep modDependency) (api.ModVersion, error) { - mod, _, err := ctx.Client.Mods.GetMod(ctx, dep.Name) - if err != nil { - return api.ModVersion{}, err - } - if dep.Tag != "" { - return mod.GetVersionByTag(dep.Tag) - } - - return mod.GetLatestVersionByConstraints(dep.VersionConstraints) -} - -func (p Pipe) checkCanAddDependency(ctx *context.Context, dep modDependency) (bool, error) { - if version, ok := ctx.Dependencies[dep.Name]; ok { - // TODO: Handle edge case not compatible... - // Uhm what do we do if the dependency was found but not compatible??? - ok, errors := dep.VersionConstraints.Validate(version.Version) - if !ok { - return false, errors[0] - } - - return false, nil - } - - return true, nil -} diff --git a/internal/pipe/installer/installer.go b/internal/pipe/installer/installer.go deleted file mode 100644 index b666975..0000000 --- a/internal/pipe/installer/installer.go +++ /dev/null @@ -1,98 +0,0 @@ -package installer - -import ( - "fmt" - "github.com/apex/log" - "github.com/vumm/cli/internal/common" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/internal/middleware" - "github.com/vumm/cli/internal/workspace" - "github.com/vumm/cli/pkg/api" - "github.com/vumm/cli/pkg/tar" - "os" - "path/filepath" - "time" -) - -type Pipe struct { -} - -func (p Pipe) String() string { - return "installing dependencies" -} - -func (p Pipe) Run(ctx *context.Context) error { - if len(ctx.Dependencies) == 0 { - log.Warn("nothing to install") - return nil - } - - packager := tar.NewPackager() - - if err := p.loadModList(ctx); err != nil { - return err - } - - for _, version := range ctx.Dependencies { - if err := p.installModVersion(ctx, packager, version); err != nil { - return err - } - } - - return p.updateModList(ctx) -} - -func (p Pipe) installModVersion(ctx *context.Context, packager tar.Packager, version api.ModVersion) error { - return middleware.Logging(fmt.Sprintf("installing %s@%s", version.Name, version.Version), func(ctx *context.Context) error { - log.Info("fetching archive") - - archiveBuf, _, err := ctx.Client.Mods.DownloadModArchive(ctx, version.Name, version.Version) - - if err != nil { - return err - } - log.WithField("size", common.ByteCountToHuman(archiveBuf.Len())).Debugf("downloaded archive file") - - modFolder := filepath.Join(ctx.WorkingDirectory, "Mods", version.Name) - - // 1. Make sure the mods folder exists - if err := os.MkdirAll(modFolder, os.ModePerm); err != nil { - return err - } - - // 2. Make sure the ext folder is removed - if err := os.RemoveAll(filepath.Join(modFolder, "ext")); err != nil { - return err - } - - start := time.Now() - log.Infof("extracting archive") - err = packager.Decompress(archiveBuf, modFolder) - if err != nil { - return err - } - log.WithField("time", time.Since(start).Truncate(time.Millisecond)).Debugf("extracted archive") - - ctx.ModList.EnableMod(version.Name) - - return nil - })(ctx) -} - -func (p Pipe) loadModList(ctx *context.Context) error { - log.WithField("file", "ModList.txt").Infof("loading") - - modList, err := workspace.TryLoadModList(ctx.WorkingDirectory) - if err != nil { - return err - } - ctx.ModList = modList - - return nil -} - -func (p Pipe) updateModList(ctx *context.Context) error { - log.WithField("file", "ModList.txt").Infof("updating") - - return ctx.ModList.Save() -} diff --git a/internal/pipe/project/project.go b/internal/pipe/project/project.go deleted file mode 100644 index 5cf9691..0000000 --- a/internal/pipe/project/project.go +++ /dev/null @@ -1,28 +0,0 @@ -package project - -import ( - "fmt" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/internal/project" - "os" -) - -type Pipe struct { -} - -func (Pipe) String() string { - return "loading project" -} - -func (Pipe) Run(ctx *context.Context) error { - cwd, err := os.Getwd() - if err != nil { - return fmt.Errorf("failed receiving working directory: %v", err) - } - ctx.Project, err = project.Load(cwd) - if err != nil { - return fmt.Errorf("failed loading project: %v", err) - } - - return nil -} diff --git a/internal/pipe/publish/publisher.go b/internal/pipe/publish/publisher.go deleted file mode 100644 index df4b82a..0000000 --- a/internal/pipe/publish/publisher.go +++ /dev/null @@ -1,32 +0,0 @@ -package publish - -import ( - "bytes" - "fmt" - "github.com/apex/log" - "github.com/vumm/cli/internal/context" -) - -type Pipe struct { - Tag string -} - -func (Pipe) String() string { - return "publisher" -} - -func (p Pipe) Run(ctx *context.Context) error { - var archiveBuf bytes.Buffer - if !ctx.ValueAs("archive", &archiveBuf) { - return fmt.Errorf("missing archive buffer") - } - - log.Info("publishing to registry") - _, err := ctx.Client.Mods.PublishMod(ctx, ctx.Project.Metadata, p.Tag, &archiveBuf) - if err != nil { - return err - } - log.Infof("published %s successfully", ctx.Project.Metadata) - - return nil -} diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go deleted file mode 100644 index 380625e..0000000 --- a/internal/pipeline/pipeline.go +++ /dev/null @@ -1,22 +0,0 @@ -package pipeline - -import ( - "fmt" - "github.com/vumm/cli/internal/context" - "github.com/vumm/cli/internal/middleware" -) - -type Pipe interface { - fmt.Stringer - Run(ctx *context.Context) error -} - -func Run(ctx *context.Context, pipeline ...Pipe) error { - for _, pipe := range pipeline { - if err := middleware.Logging(pipe.String(), pipe.Run)(ctx); err != nil { - return err - } - } - - return nil -} diff --git a/internal/project/project.go b/internal/project/project.go deleted file mode 100644 index c48d485..0000000 --- a/internal/project/project.go +++ /dev/null @@ -1,68 +0,0 @@ -package project - -import ( - "github.com/apex/log" - "github.com/vumm/cli/internal/common" - "github.com/vumm/cli/pkg/ignorer" - "os" - "path/filepath" -) - -var defaultIgnoresLines = []string{ - // Git - ".git", - ".gitmodules", - ".gitignore", - ".github/", - - // Node - "node_modules/", - "package.json", - "package-lock.json", - "yarn.lock", - - // Editors - ".vscode/", - ".idea/", - ".editorconfig", - "*.iml", - - // VU (ui folders, these should be compiled to a ui.vuic anyways) - "^[Uu][Ii]/", - "^[Ww]eb[Uu][Ii]/", - ".vummignore", // yea lets also remove ourselves -} - -type Project struct { - Metadata common.ModMetadata - Directory string - Ignorer ignorer.Ignorer -} - -func Load(projectPath string) (*Project, error) { - log.WithField("file", "mod.json").Info("loading metadata") - metadata, err := common.LoadModMetadata(filepath.Join(projectPath, "mod.json")) - if err != nil { - return nil, err - } - log.WithField("file", "mod.json").Debug("loaded metadata") - - log.WithField("file", ".vummignore").Info("loading ignore file") - fileIgnorer, err := ignorer.CompileIgnorerFromFile(filepath.Join(projectPath, ".vummignore"), defaultIgnoresLines...) - if err != nil { - if !os.IsNotExist(err) { - return nil, err - } - - fileIgnorer = ignorer.CompileIgnorerFromLines(defaultIgnoresLines...) - log.Debug("no .vummignore found, using default") - } else { - log.WithField("file", ".vummignore").Debug("loaded ignore file") - } - - return &Project{ - Metadata: metadata, - Directory: projectPath, - Ignorer: fileIgnorer, - }, nil -} diff --git a/internal/updater/periodic.go b/internal/updater/periodic.go deleted file mode 100644 index 722655b..0000000 --- a/internal/updater/periodic.go +++ /dev/null @@ -1,47 +0,0 @@ -package updater - -import ( - "github.com/creativeprojects/go-selfupdate" - "github.com/vumm/cli/internal/common" - "github.com/vumm/cli/internal/config" - "os" - "path/filepath" - "time" -) - -const updateInterval = time.Hour * 24 * 7 // 1 week - -func PeriodicCheckForUpdates() (*selfupdate.Release, bool, error) { - if !shouldCheck() { - return nil, false, nil - } - - return CheckForUpdates() -} - -func shouldCheck() bool { - // Don't check for updates on development versions - if !common.IsRelease() { - return false - } - - checkFile := filepath.Join(config.GetPath(), "last-update-check") - - nextCheck := time.Now().Add(-updateInterval) - var lastChecked time.Time - - stat, err := os.Stat(checkFile) - if err != nil { - lastChecked = nextCheck.Add(-time.Millisecond) - } else { - lastChecked = stat.ModTime() - } - - if nextCheck.After(lastChecked) { - f, _ := os.Create(checkFile) - f.Close() - return true - } - - return false -} diff --git a/internal/updater/updater.go b/internal/updater/updater.go deleted file mode 100644 index fe9dab9..0000000 --- a/internal/updater/updater.go +++ /dev/null @@ -1,67 +0,0 @@ -package updater - -import ( - "errors" - "fmt" - "github.com/creativeprojects/go-selfupdate" - "github.com/vumm/cli/internal/common" - "log" - "os" - "runtime" -) - -var latestVersion *selfupdate.Release -var updater *selfupdate.Updater - -func init() { - var err error - updater, err = selfupdate.NewUpdater(selfupdate.Config{Validator: &selfupdate.ChecksumValidator{UniqueFilename: "checksums.txt"}}) - if err != nil { - panic(err) - } -} - -func CheckForUpdates() (*selfupdate.Release, bool, error) { - latest, found, err := updater.DetectLatest("BF3RM/vumm-cli") - if err != nil { - return nil, false, fmt.Errorf("failed to fetch latest version: %v", err) - } - - if !found { - return nil, false, fmt.Errorf("failed to find latest version for %s", runtime.GOOS) - } - - latestVersion = latest - return latest, IsUpdateAvailable(), nil -} - -func IsUpdateAvailable() bool { - if latestVersion == nil || !common.IsRelease() { - return false - } - - return latestVersion.GreaterThan(common.GetVersion()) -} - -func SelfUpdate() (bool, error) { - if latestVersion == nil { - _, _, err := CheckForUpdates() - if err != nil { - return false, err - } - } - - if !IsUpdateAvailable() { - return false, nil - } - - exe, err := os.Executable() - if err != nil { - return false, errors.New("could not locate executable path") - } - if err := updater.UpdateTo(latestVersion, exe); err != nil { - return false, fmt.Errorf("error occurred while updating binary: %v", err) - } - log.Printf("Successfully updated to version %s", latestVersion.Version()) - return false, nil -} diff --git a/internal/workspace/modlist.go b/internal/workspace/modlist.go deleted file mode 100644 index edc7c3a..0000000 --- a/internal/workspace/modlist.go +++ /dev/null @@ -1,123 +0,0 @@ -package workspace - -import ( - "bufio" - "fmt" - "github.com/apex/log" - "os" - "path/filepath" - "strings" -) - -type ModList struct { - dir string - lines []string -} - -func TryLoadModList(dir string) (*ModList, error) { - modList := &ModList{ - dir: dir, - lines: []string{}, - } - - filePath := filepath.Join(dir, "ModList.txt") - log.WithField("file", filePath).Debugf("loading mod list") - file, err := os.Open(filePath) - if err != nil { - if os.IsNotExist(err) { - log.Warn("mod list not found, creating") - return modList, nil - } - - return nil, err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - modList.lines = append(modList.lines, scanner.Text()) - } - if err = scanner.Err(); err != nil { - return nil, err - } - - return modList, nil -} - -func (m *ModList) EnableMod(mod string) bool { - mod = strings.ToLower(mod) - - enabled, idx := m.isModEnabled(mod) - - // not found, add new line - if idx == -1 { - m.lines = append(m.lines, mod) - return true - } - - m.lines[idx] = mod - - // disabled - return !enabled -} - -func (m *ModList) DisableMod(mod string) bool { - mod = strings.ToLower(mod) - - enabled, idx := m.isModEnabled(mod) - - // not found, nothing to do - if idx == -1 { - return false - } - - // enabled, lets disable - if enabled { - m.lines[idx] = "#" + mod - return true - } - - // Replace anyway to remove leftover spaces - m.lines[idx] = mod - return false -} - -func (m ModList) isModEnabled(mod string) (bool, int) { - for idx, line := range m.lines { - parsedLine := strings.TrimLeft(strings.ToLower(line), " ") - // already enabled - if parsedLine == mod { - return true, idx - } - - // disabled, lets enable - if strings.HasPrefix(parsedLine, "#") && strings.TrimLeft(parsedLine, "# ") == mod { - return false, idx - } - } - - return false, -1 -} - -func (m *ModList) Save() error { - log.WithField("file", "ModList.txt").Debugf("saving ModList.txt") - - file, err := os.OpenFile(filepath.Join(m.dir, "ModList.txt"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) - if err != nil { - return err - } - defer file.Close() - - writer := bufio.NewWriter(file) - - for _, line := range m.lines { - if _, err = writer.WriteString(fmt.Sprintf("%s\n", line)); err != nil { - return err - } - } - if err = writer.Flush(); err != nil { - return err - } - - return nil -} diff --git a/pkg/api/auth.go b/pkg/api/auth.go deleted file mode 100644 index 75751d2..0000000 --- a/pkg/api/auth.go +++ /dev/null @@ -1,49 +0,0 @@ -package api - -import ( - "context" - "fmt" - "net/http" - "time" -) - -type AuthResult struct { - Token string `json:"token"` - Type PermissionType `json:"type"` - CreatedAt time.Time `json:"createdAt"` -} - -type credentialsDto struct { - Username string `json:"username"` - Password string `json:"password"` - Type PermissionType `json:"type"` -} - -type AuthService commonService - -func (s AuthService) Login(ctx context.Context, username string, password string, permission PermissionType) (*AuthResult, *http.Response, error) { - return s.authRequest(ctx, "login", username, password, permission) -} - -func (s AuthService) Register(ctx context.Context, username string, password string, permission PermissionType) (*AuthResult, *http.Response, error) { - return s.authRequest(ctx, "register", username, password, permission) -} - -func (s AuthService) authRequest(ctx context.Context, endpoint, username, password string, permission PermissionType) (*AuthResult, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodPost, fmt.Sprintf("auth/%s", endpoint), &credentialsDto{ - Username: username, - Password: password, - Type: permission, - }) - if err != nil { - return nil, nil, err - } - - result := new(AuthResult) - res, err := s.client.Do(ctx, req, result) - if err != nil { - return nil, res, err - } - - return result, res, nil -} diff --git a/pkg/api/auth_test.go b/pkg/api/auth_test.go deleted file mode 100644 index 6c02d7d..0000000 --- a/pkg/api/auth_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package api - -import ( - "context" - "fmt" - "github.com/google/go-cmp/cmp" - "net/http" - "testing" - "time" -) - -func TestAuthService_Login(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - input := &credentialsDto{ - Username: "test", - Password: "test", - Type: PermissionTypePublish, - } - - mux.HandleFunc("/auth/login", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testBodyJSON(t, r, input) - - fmt.Fprint(w, `{"token": "1234", "type": "Publish", "createdAt": "2022-05-03T12:00:00.000Z"}`) - }) - - ctx := context.Background() - result, _, err := client.Auth.Login(ctx, "test", "test", PermissionTypePublish) - if err != nil { - t.Errorf("Auth.Login returned error: %v", err) - } - - expected := &AuthResult{ - Token: "1234", - Type: PermissionTypePublish, - CreatedAt: time.Date(2022, 5, 3, 12, 00, 00, 00, time.UTC), - } - if !cmp.Equal(result, expected) { - t.Errorf("Auth.Login returned %+v, expected: %+v", result, expected) - } -} - -func TestAuthService_Register(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - input := &credentialsDto{ - Username: "test", - Password: "test", - Type: PermissionTypePublish, - } - - mux.HandleFunc("/auth/register", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testBodyJSON(t, r, input) - - fmt.Fprint(w, `{"token": "1234", "type": "Publish", "createdAt": "2022-05-03T12:00:00.000Z"}`) - }) - - ctx := context.Background() - result, _, err := client.Auth.Register(ctx, "test", "test", PermissionTypePublish) - if err != nil { - t.Errorf("Auth.Register returned error: %v", err) - } - - expected := &AuthResult{ - Token: "1234", - Type: PermissionTypePublish, - CreatedAt: time.Date(2022, 5, 3, 12, 00, 00, 00, time.UTC), - } - if !cmp.Equal(result, expected) { - t.Errorf("Auth.Register returned %+v, expected: %+v", result, expected) - } -} diff --git a/pkg/api/client.go b/pkg/api/client.go deleted file mode 100644 index 99abe09..0000000 --- a/pkg/api/client.go +++ /dev/null @@ -1,151 +0,0 @@ -package api - -import ( - "bytes" - "context" - "encoding/json" - "io" - "io/ioutil" - "net/http" - "net/url" -) - -type Option func(c *Client) error - -func BaseURL(baseUrl string) Option { - return func(c *Client) error { - parsedUrl, err := url.Parse(baseUrl) - if err != nil { - return err - } - c.baseUrl = parsedUrl - return nil - } -} - -func RoundTrip(t http.RoundTripper) Option { - return func(c *Client) error { - c.client.Transport = t - return nil - } -} - -var defaultBaseUrl = "https://vumm.bf3reality.com/api/v1/" - -type Client struct { - baseUrl *url.URL - client *http.Client - - common commonService - - Auth *AuthService - Mods *ModsService -} - -func New(opts ...Option) (*Client, error) { - baseUrl, _ := url.Parse(defaultBaseUrl) - - client := &Client{ - baseUrl: baseUrl, - client: &http.Client{}, - } - - for _, option := range opts { - err := option(client) - if err != nil { - return nil, err - } - } - - client.common.client = client - client.Auth = (*AuthService)(&client.common) - client.Mods = (*ModsService)(&client.common) - - return client, nil -} - -func (c *Client) NewRequest(method, path string, body interface{}) (*http.Request, error) { - reqUrl, err := c.baseUrl.Parse(path) - if err != nil { - return nil, err - } - - var buf io.ReadWriter - if body != nil { - buf = &bytes.Buffer{} - err := json.NewEncoder(buf).Encode(body) - if err != nil { - return nil, err - } - } - - req, err := http.NewRequest(method, reqUrl.String(), buf) - if err != nil { - return nil, err - } - - if body != nil { - req.Header.Set("Content-Type", "application/json") - } - - return req, nil -} - -func (c *Client) DoRequest(ctx context.Context, req *http.Request) (*http.Response, error) { - req = req.WithContext(ctx) - - res, err := c.client.Do(req) - if err != nil { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - return nil, err - } - } - - err = c.checkResponse(res) - return res, err -} - -func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) { - res, err := c.DoRequest(ctx, req) - if err != nil { - return nil, err - } - defer res.Body.Close() - - switch v := v.(type) { - case nil: - case io.Writer: - _, err = io.Copy(v, res.Body) - default: - err = json.NewDecoder(res.Body).Decode(v) - } - - return res, err -} - -func (c *Client) checkResponse(res *http.Response) error { - // All statuses between 200 <-> 299 are ok - if c := res.StatusCode; 200 <= c && c <= 299 { - return nil - } - - resErr := &GenericError{Response: res} - data, err := ioutil.ReadAll(res.Body) - if err == nil && data != nil { - json.Unmarshal(data, resErr) - } - - switch res.StatusCode { - case http.StatusUnauthorized: - return (*UnauthorizedError)(resErr) - case http.StatusBadRequest: - return (*BadRequestError)(resErr) - case http.StatusConflict: - return (*ConflictError)(resErr) - default: - return resErr - } -} diff --git a/pkg/api/client_test.go b/pkg/api/client_test.go deleted file mode 100644 index 5fa15d5..0000000 --- a/pkg/api/client_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package api - -import ( - "encoding/json" - "github.com/google/go-cmp/cmp" - "net/http" - "net/http/httptest" - "reflect" - "testing" -) - -func setup() (client *Client, mux *http.ServeMux, teardown func()) { - mux = http.NewServeMux() - - server := httptest.NewServer(mux) - - client, _ = New(BaseURL(server.URL)) - - return client, mux, server.Close -} - -func testMethod(t *testing.T, r *http.Request, expectedMethod string) { - t.Helper() - if r.Method != expectedMethod { - t.Errorf("Request method: %v, expected %v", r.Method, expectedMethod) - } -} - -func testBodyJSON(t *testing.T, r *http.Request, expected interface{}) { - t.Helper() - - bodyType := reflect.TypeOf(expected) - bodyValue := reflect.New(bodyType.Elem()) - value := bodyValue.Interface() - - json.NewDecoder(r.Body).Decode(&value) - - if !cmp.Equal(value, expected) { - t.Errorf("Request body: %v, expected %v", value, expected) - } -} - -func TestBaseURL(t *testing.T) { - baseUrl := "https://vumm.com/api/v1/" - c, _ := New(BaseURL(baseUrl)) - - if c.baseUrl.String() != baseUrl { - t.Errorf("Base url: %v, expected: %v", c.baseUrl, baseUrl) - } -} - -func TestRoundTrip(t *testing.T) { - ts := &TokenAuthTransport{} - c, _ := New(RoundTrip(ts)) - - if !cmp.Equal(c.client.Transport, ts) { - t.Errorf("Round trip: %v, expected: %v", c.client.Transport, ts) - } -} diff --git a/pkg/api/client_transport.go b/pkg/api/client_transport.go deleted file mode 100644 index 50a4fab..0000000 --- a/pkg/api/client_transport.go +++ /dev/null @@ -1,21 +0,0 @@ -package api - -import "net/http" - -var _ http.RoundTripper = (*TokenAuthTransport)(nil) - -type TokenAuthTransport struct { - Token string -} - -func (t TokenAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { - if t.Token != "" { - req.Header.Set("Authorization", t.Token) - } - - return http.DefaultTransport.RoundTrip(req) -} - -type commonService struct { - client *Client -} diff --git a/pkg/api/client_transport_test.go b/pkg/api/client_transport_test.go deleted file mode 100644 index 46cb7c5..0000000 --- a/pkg/api/client_transport_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package api - -import ( - "net/http" - "testing" -) - -func TestTokenAuthTransport_RoundTrip_NoToken(t *testing.T) { - ts := TokenAuthTransport{Token: ""} - - res, _ := http.NewRequest(http.MethodGet, "/ok", nil) - ts.RoundTrip(res) - - if _, exists := res.Header["Authorization"]; exists { - t.Errorf("Request should not to contain a Authorization header") - } -} - -func TestTokenAuthTransport_RoundTrip_Token(t *testing.T) { - expected := "1234" - ts := TokenAuthTransport{Token: expected} - - res, _ := http.NewRequest(http.MethodGet, "/ok", nil) - ts.RoundTrip(res) - - if value := res.Header.Get("Authorization"); value != expected { - t.Errorf("Authorization header: %v, expected: %v", value, expected) - } -} diff --git a/pkg/api/errors.go b/pkg/api/errors.go deleted file mode 100644 index c6a5d9c..0000000 --- a/pkg/api/errors.go +++ /dev/null @@ -1,95 +0,0 @@ -package api - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "strings" -) - -var ( - ErrModVersionNotFound = errors.New("mod version was not found") -) - -type GenericError struct { - Response *http.Response - Message string `json:"message"` - Errors map[string][]string `json:"errors"` -} - -func (e GenericError) Error() string { - return fmt.Sprintf("%v %v: %d, %v %+v", e.Response.Request.Method, e.Response.Request.URL, e.Response.StatusCode, e.Message, e.Errors) -} - -type UnauthorizedError GenericError - -func (e *UnauthorizedError) Error() string { - return (*GenericError)(e).Error() -} - -type BadRequestError GenericError - -func (e *BadRequestError) Error() string { - return (*GenericError)(e).Error() -} - -type ConflictError GenericError - -func (e *ConflictError) Error() string { - return (*GenericError)(e).Error() -} - -type ValidationError struct { - GenericError - errors map[string][]string -} - -func (e ValidationError) GetKeyValidationErrors(key string) ([]string, bool) { - val, ok := e.errors[key] - return val, ok -} - -func (e ValidationError) GetValidationErrors() []string { - var errs []string - for _, valErrs := range e.errors { - errs = append(errs, valErrs...) - } - - return errs -} - -func (e *ValidationError) UnmarshalJSON(b []byte) error { - var raw map[string]interface{} - if err := json.Unmarshal(b, &raw); err != nil { - return err - } - - e.errors = map[string][]string{} - - if errors, ok := raw["errors"]; ok { - for key, val := range errors.(map[string]interface{}) { - var errs []string - for _, valErr := range val.([]interface{}) { - errs = append(errs, valErr.(string)) - } - e.errors[key] = errs - } - } - - return nil -} - -func (e ValidationError) Error() string { - if len(e.errors) == 0 { - return e.GenericError.Error() - } - - builder := strings.Builder{} - builder.WriteString(e.Message) - for _, err := range e.GetValidationErrors() { - builder.WriteString(fmt.Sprintf("\n\t- %s", err)) - } - - return builder.String() -} diff --git a/pkg/api/mods.go b/pkg/api/mods.go deleted file mode 100644 index 5f645e3..0000000 --- a/pkg/api/mods.go +++ /dev/null @@ -1,127 +0,0 @@ -package api - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "github.com/Masterminds/semver/v3" - "github.com/vumm/cli/internal/common" - "net/http" - "sort" -) - -type ModVersion struct { - Name string `json:"name"` - Description string `json:"description"` - Author string `json:"author"` - Version *semver.Version `json:"version"` - Dependencies map[string]string `json:"dependencies"` -} - -func (m ModVersion) String() string { - str, err := json.Marshal(m) - if err != nil { - panic(err) - } - - return string(str) -} - -// ModVersions is a collection of ModVersion instances and implements the sort -// interface based on the semver version inside it. -type ModVersions []ModVersion - -// Len returns the length of a collection. -func (c ModVersions) Len() int { - return len(c) -} - -// Less checks if one is greater (reverse) than the other based on Semver. -func (c ModVersions) Less(i, j int) bool { - return c[i].Version.GreaterThan(c[j].Version) -} - -func (c ModVersions) Swap(i, j int) { - c[i], c[j] = c[j], c[i] -} - -type Mod struct { - Name string `json:"name"` - Description string `json:"description"` - Author string `json:"author"` - Tags map[string]string `json:"tags"` - Versions map[string]ModVersion `json:"versions"` - - // sorted list of mod versions - versions ModVersions -} - -// GetVersionByTag tries to find the version by tag name -func (m Mod) GetVersionByTag(tag string) (ModVersion, error) { - tagVersion, ok := m.Tags[tag] - if !ok { - return ModVersion{}, ErrModVersionNotFound - } - - version, ok := m.Versions[tagVersion] - if !ok { - return ModVersion{}, ErrModVersionNotFound - } - return version, nil -} - -// GetLatestVersionByConstraints tries to find the latest version satisfying the constraints -func (m *Mod) GetLatestVersionByConstraints(constraints *common.SemverConstraints) (ModVersion, error) { - // If versions are not sorted yet, do that now - if m.versions == nil { - m.versions = make(ModVersions, 0, len(m.Versions)) - for _, version := range m.Versions { - m.versions = append(m.versions, version) - } - sort.Sort(m.versions) - } - - for _, modVersion := range m.versions { - if constraints.Check(modVersion.Version) { - return modVersion, nil - } - } - - return ModVersion{}, ErrModVersionNotFound -} - -type ModsService commonService - -// GetMod fetches a mod from the registry -func (s ModsService) GetMod(ctx context.Context, modName string) (*Mod, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodGet, fmt.Sprintf("mods/%s", modName), nil) - if err != nil { - return nil, nil, err - } - - mod := new(Mod) - res, err := s.client.Do(ctx, req, &mod) - - if err != nil { - return nil, res, err - } - - return mod, res, nil -} - -// DownloadModArchive fetches a mods archive from the registry -func (s ModsService) DownloadModArchive(ctx context.Context, modName string, modVersion *semver.Version) (*bytes.Buffer, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodGet, fmt.Sprintf("mods/%s/%s/download", modName, modVersion), nil) - if err != nil { - return nil, nil, err - } - - buf := new(bytes.Buffer) - res, err := s.client.Do(ctx, req, buf) - if err != nil { - return nil, res, err - } - - return buf, res, nil -} diff --git a/pkg/api/mods_permissions.go b/pkg/api/mods_permissions.go deleted file mode 100644 index 8ff918c..0000000 --- a/pkg/api/mods_permissions.go +++ /dev/null @@ -1,62 +0,0 @@ -package api - -import ( - "context" - "fmt" - "net/http" -) - -type PermissionType string - -const ( - PermissionTypeReadonly PermissionType = "Readonly" - PermissionTypePublish = "Publish" -) - -type grantPermissionDto struct { - Username string `json:"username"` - Permission PermissionType `json:"permission,omitempty"` - Tag string `json:"tag"` -} - -func PermissionTypeFromString(in string) (permission PermissionType, err error) { - if in == "" { - return PermissionTypeReadonly, nil - } - - switch in { - case "readonly": - permission = PermissionTypeReadonly - case "publish": - permission = PermissionTypePublish - default: - err = fmt.Errorf("%s is not a valid permission type", in) - } - - return -} - -func (s ModsService) GrantModPermissions(ctx context.Context, modName string, modTag string, username string, permission PermissionType) (*http.Response, error) { - req, err := s.client.NewRequest(http.MethodPost, fmt.Sprintf("mods/%s/grant", modName), &grantPermissionDto{ - Tag: modTag, - Username: username, - Permission: permission, - }) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -func (s ModsService) RevokeModPermissions(ctx context.Context, modName string, modTag string, username string) (*http.Response, error) { - req, err := s.client.NewRequest(http.MethodPost, fmt.Sprintf("mods/%s/revoke", modName), &grantPermissionDto{ - Tag: modTag, - Username: username, - }) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/pkg/api/mods_permissions_test.go b/pkg/api/mods_permissions_test.go deleted file mode 100644 index 28c25e8..0000000 --- a/pkg/api/mods_permissions_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package api - -import ( - "context" - "fmt" - "net/http" - "testing" -) - -func TestModsService_GrantModPermissions(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - input := &grantPermissionDto{ - Username: "test", - Permission: PermissionTypeReadonly, - Tag: "qa", - } - - mux.HandleFunc("/mods/realitymod/grant", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testBodyJSON(t, r, input) - - fmt.Fprint(w, `{"message": "OK"}`) - }) - - ctx := context.Background() - _, err := client.Mods.GrantModPermissions(ctx, "realitymod", "qa", "test", PermissionTypeReadonly) - if err != nil { - t.Errorf("Mods.GrantModPermissions returned error: %v", err) - } -} - -func TestModsService_RevokeModPermissions(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - input := &grantPermissionDto{ - Username: "test", - Tag: "qa", - } - - mux.HandleFunc("/mods/realitymod/revoke", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testBodyJSON(t, r, input) - - fmt.Fprint(w, `{"message": "OK"}`) - }) - - ctx := context.Background() - _, err := client.Mods.RevokeModPermissions(ctx, "realitymod", "qa", "test") - if err != nil { - t.Errorf("Mods.RevokeModPermissions returned error: %v", err) - } -} diff --git a/pkg/api/mods_publish.go b/pkg/api/mods_publish.go deleted file mode 100644 index 1be3ff1..0000000 --- a/pkg/api/mods_publish.go +++ /dev/null @@ -1,70 +0,0 @@ -package api - -import ( - "context" - "encoding/base64" - "fmt" - "github.com/Masterminds/semver" - "github.com/vumm/cli/internal/common" - "io" - "io/ioutil" - "net/http" -) - -type modArchiveDto struct { - Data string `json:"data"` - Length int `json:"length"` - ContentType string `json:"content_type"` -} - -type publishModDto struct { - common.ModMetadata - Tag string `json:"tag"` - Archive modArchiveDto `json:"archive"` -} - -func (s ModsService) PublishMod(ctx context.Context, metadata common.ModMetadata, tag string, reader io.Reader) (*http.Response, error) { - archive, err := s.encodeModArchive(reader) - if err != nil { - return nil, fmt.Errorf("failed to encode archive: %v", err) - } - - publishUrl := fmt.Sprintf("mods/%s/%s", metadata.Name, metadata.Version) - - req, err := s.client.NewRequest(http.MethodPut, publishUrl, &publishModDto{ - ModMetadata: metadata, - Tag: tag, - Archive: archive, - }) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -func (s ModsService) UnpublishModVersion(ctx context.Context, modName string, modVersion *semver.Version) (*http.Response, error) { - req, err := s.client.NewRequest(http.MethodDelete, fmt.Sprintf("mods/%s/%s", modName, modVersion), nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -func (s ModsService) encodeModArchive(reader io.Reader) (modArchiveDto, error) { - buf, err := ioutil.ReadAll(reader) - if err != nil { - return modArchiveDto{}, err - } - - length := len(buf) - contentType := http.DetectContentType(buf) - data := base64.StdEncoding.EncodeToString(buf) - - return modArchiveDto{ - Data: data, - Length: length, - ContentType: contentType, - }, nil -} diff --git a/pkg/api/mods_publish_test.go b/pkg/api/mods_publish_test.go deleted file mode 100644 index bf7bdf7..0000000 --- a/pkg/api/mods_publish_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package api - -import ( - "bytes" - "context" - "fmt" - "github.com/Masterminds/semver" - "github.com/vumm/cli/internal/common" - "io" - "net/http" - "os" - "testing" -) - -func TestModsService_encodeModArchive(t *testing.T) { - service := ModsService{} - - file, err := os.Open("testdata/0.1.0.tgz") - if err != nil { - t.Errorf("Failed to open test file") - } - defer file.Close() - - archive, err := service.encodeModArchive(file) - if err != nil { - t.Errorf("Mods.encodeModArchive returned error: %v", err) - } - - if archive.Data == "" { - t.Errorf("Archive data is empty") - } - - if archive.ContentType != "application/x-gzip" { - t.Errorf("Archive content type: %v, expected application/x-gzip", archive.ContentType) - } -} - -func TestModsService_PublishMod(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - metadata := common.ModMetadata{ - Name: "realitymod", - Version: semver.MustParse("0.1.0"), - Dependencies: nil, - } - - file, err := os.Open("testdata/0.1.0.tgz") - if err != nil { - t.Errorf("Failed to open test file") - } - defer file.Close() - var buf bytes.Buffer - archive, _ := client.Mods.encodeModArchive(io.TeeReader(file, &buf)) - - input := &publishModDto{ - ModMetadata: metadata, - Tag: "qa", - Archive: archive, - } - - mux.HandleFunc("/mods/realitymod/0.1.0", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") - testBodyJSON(t, r, input) - - fmt.Fprint(w, `{"message": "OK"}`) - }) - - ctx := context.Background() - _, err = client.Mods.PublishMod(ctx, metadata, "qa", &buf) - if err != nil { - t.Errorf("Mods.PublishMod returned error: %v", err) - } -} - -func TestModsService_UnpublishModVersion(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - mux.HandleFunc("/mods/realitymod/0.1.0", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - - fmt.Fprint(w, `{"message": "OK"}`) - }) - - ctx := context.Background() - _, err := client.Mods.UnpublishModVersion(ctx, "realitymod", semver.MustParse("0.1.0")) - if err != nil { - t.Errorf("Mods.UnpublishModVersion returned error: %v", err) - } -} diff --git a/pkg/api/mods_test.go b/pkg/api/mods_test.go deleted file mode 100644 index cceeb0b..0000000 --- a/pkg/api/mods_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package api - -import ( - "bytes" - "context" - "fmt" - "github.com/Masterminds/semver/v3" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "net/http" - "testing" -) - -func TestModsService_GetMod(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - mux.HandleFunc("/mods/realitymod", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - - fmt.Fprint(w, ` -{ - "name": "realitymod", - "description": "Da best mod", - "author": "author", - "tags": { - "latest": "0.1.0" - }, - "versions": { - "0.1.0": { - "name": "realitymod", - "description": "Da best mod", - "author": "author", - "version": "0.1.0", - "dependencies": { - "vemanager": ">=0.1.0" - } - } - } -}`) - }) - - ctx := context.Background() - result, _, err := client.Mods.GetMod(ctx, "realitymod") - if err != nil { - t.Errorf("Mods.GetMod returned error: %v", err) - } - - expected := &Mod{ - Name: "realitymod", - Description: "Da best mod", - Author: "author", - Tags: map[string]string{"latest": "0.1.0"}, - Versions: map[string]ModVersion{"0.1.0": { - Name: "realitymod", - Description: "Da best mod", - Author: "author", - Version: semver.MustParse("0.1.0"), - Dependencies: map[string]string{"vemanager": ">=0.1.0"}, - }}, - } - if !cmp.Equal(result, expected, cmpopts.IgnoreUnexported(Mod{})) { - t.Errorf("Mods.GetMod returned %+v, expected: %+v", result, expected) - } -} - -func TestModsService_DownloadModArchive(t *testing.T) { - client, mux, teardown := setup() - defer teardown() - - mux.HandleFunc("/mods/realitymod/0.1.0/download", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - - fmt.Fprint(w, "test") - }) - - ctx := context.Background() - result, _, err := client.Mods.DownloadModArchive(ctx, "realitymod", semver.MustParse("0.1.0")) - if err != nil { - t.Errorf("Mods.DownloadModArchive returned error: %v", err) - } - - expected := bytes.NewBufferString("test") - if !cmp.Equal(result.String(), expected.String()) { - t.Errorf("Mods.DownloadModArchive returned %+v, expected: %+v", result.String(), expected.String()) - } -} diff --git a/pkg/api/testdata/0.1.0.tgz b/pkg/api/testdata/0.1.0.tgz deleted file mode 100644 index eaccb38..0000000 Binary files a/pkg/api/testdata/0.1.0.tgz and /dev/null differ diff --git a/pkg/ignorer/ignore.go b/pkg/ignorer/ignore.go deleted file mode 100644 index cbb2380..0000000 --- a/pkg/ignorer/ignore.go +++ /dev/null @@ -1,145 +0,0 @@ -package ignorer - -import ( - "io/ioutil" - "os" - "regexp" - "strings" -) - -type Ignorer interface { - Matches(filePath string) bool -} - -type noopIgnorer struct { -} - -func (noopIgnorer) Matches(filePath string) bool { - return false -} - -func NOOP() Ignorer { - return &noopIgnorer{} -} - -type ignorePattern struct { - regex *regexp.Regexp - negate bool -} - -type patternIgnorer struct { - patterns []*ignorePattern -} - -func parseRegexFromLine(line string) (*regexp.Regexp, bool) { - line = strings.TrimRight(line, "\r") - - // Strip comments [Rule 2] - if strings.HasPrefix(line, "#") { - return nil, false - } - - // Trim string [Rule 3] - line = strings.Trim(line, " ") - - if line == "" { - return nil, false - } - - // Handle [Rule 4] which negates the match for patterns leading with "!" - negatePattern := false - if line[0] == '!' { - negatePattern = true - line = line[1:] - } - - // Handle [Rule 2, 4], when # or ! is escaped with a \ - // Handle [Rule 4] once we tag negatePattern, strip the leading ! char - if regexp.MustCompile(`^(\#|\!)`).MatchString(line) { - line = line[1:] - } - - // Prepend with a / - if regexp.MustCompile(`([^\/+]/.*\*\.)`).MatchString(line) && line[0] != '/' { - line = "/" + line - } - - // Escape the "." char - line = regexp.MustCompile(`\.`).ReplaceAllString(line, `\.`) - - magicStar := "#$~" - - // Handle "/**/" usage - if strings.HasPrefix(line, "/**/") { - line = line[1:] - } - line = regexp.MustCompile(`/\*\*/`).ReplaceAllString(line, `(/|/.+/)`) - line = regexp.MustCompile(`\*\*/`).ReplaceAllString(line, `(|.`+magicStar+`/)`) - line = regexp.MustCompile(`/\*\*`).ReplaceAllString(line, `(|/.`+magicStar+`)`) - - // Handle escaping the "*" char - line = regexp.MustCompile(`\\\*`).ReplaceAllString(line, `\`+magicStar) - line = regexp.MustCompile(`\*`).ReplaceAllString(line, `([^/]*)`) - - // Handle escaping the "?" char - line = strings.Replace(line, "?", `\?`, -1) - - line = strings.Replace(line, magicStar, "*", -1) - - // Temporary regex - var expr = "" - if strings.HasSuffix(line, "/") { - expr = line + "(|.*)$" - } else { - expr = line + "(|/.*)$" - } - if strings.HasPrefix(expr, "/") { - expr = "^(|/)" + expr[1:] - } else { - expr = "^(|.*/)" + expr - } - pattern, _ := regexp.Compile(expr) - - return pattern, negatePattern -} - -func CompileIgnorerFromLines(lines ...string) Ignorer { - ignorer := &patternIgnorer{} - - for _, line := range lines { - regex, negate := parseRegexFromLine(line) - if regex != nil { - pattern := &ignorePattern{regex, negate} - ignorer.patterns = append(ignorer.patterns, pattern) - } - } - - return ignorer -} - -func CompileIgnorerFromFile(filePath string, additionalLines ...string) (Ignorer, error) { - bs, err := ioutil.ReadFile(filePath) - if err != nil { - return nil, err - } - - lines := append(strings.Split(string(bs), "\n"), additionalLines...) - return CompileIgnorerFromLines(lines...), nil -} - -func (ignorer patternIgnorer) Matches(filePath string) bool { - filePath = strings.ReplaceAll(filePath, string(os.PathSeparator), "/") - - matchesPath := false - for _, pattern := range ignorer.patterns { - if pattern.regex.MatchString(filePath) { - if !pattern.negate { - matchesPath = true - } else if matchesPath { - matchesPath = false - } - } - } - - return matchesPath -} diff --git a/pkg/tar/packager.go b/pkg/tar/packager.go deleted file mode 100644 index b89d562..0000000 --- a/pkg/tar/packager.go +++ /dev/null @@ -1,48 +0,0 @@ -package tar - -import ( - "archive/tar" - "compress/gzip" - "fmt" - "io" -) - -type Packager interface { - SetFileFilter(filter FileFilter) - Compress(src string, writer io.Writer) error - Decompress(reader io.Reader, dest string) error -} - -func NewPackager() Packager { - return &tarGzPackager{} -} - -type tarGzPackager struct { - filter FileFilter -} - -func (p *tarGzPackager) SetFileFilter(filter FileFilter) { - p.filter = filter -} - -func (p tarGzPackager) Compress(src string, writer io.Writer) error { - gzWriter := gzip.NewWriter(writer) - defer gzWriter.Close() - - tarWriter := tar.NewWriter(gzWriter) - defer tarWriter.Close() - - return tarballToWriter(src, tarWriter, p.filter) -} - -func (p tarGzPackager) Decompress(reader io.Reader, dest string) error { - gzReader, err := gzip.NewReader(reader) - if err != nil { - return fmt.Errorf("failed to decompress archive: %v", err) - } - defer gzReader.Close() - - tarReader := tar.NewReader(gzReader) - - return untarFromReader(tarReader, dest) -} diff --git a/pkg/tar/tar.go b/pkg/tar/tar.go deleted file mode 100644 index b3645c9..0000000 --- a/pkg/tar/tar.go +++ /dev/null @@ -1,151 +0,0 @@ -package tar - -import ( - "archive/tar" - "fmt" - "io" - "os" - "path/filepath" - "runtime" - "strings" -) - -type FileFilter func(filePath string) bool - -func tarballToWriter(src string, writer *tar.Writer, filter FileFilter) error { - //srcInfo, err := os.Stat(src) - //if err != nil { - // return fmt.Errorf("%s: stat: %v", src, err) - //} - - //var baseDir string - //if srcInfo.IsDir() { - // baseDir = filepath.Base(src) - //} - - return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { - if err != nil { - return fmt.Errorf("error walking to %s: %v", path, err) - } - - // Ignore root directory - if path == src { - return nil - } - - header, err := tar.FileInfoHeader(info, path) - if err != nil { - return fmt.Errorf("%s: making header: %v", path, err) - } - - // Rewrite baseDir - //if baseDir != "" { - header.Name = strings.TrimPrefix(path, src+string(filepath.Separator)) - //} - - if info.IsDir() { - header.Name += "/" - } - - if filter != nil && !filter(header.Name) { - return nil - } - - err = writer.WriteHeader(header) - if err != nil { - return fmt.Errorf("%s: writing header: %v", path, err) - } - - // We done, nothing left to do for directories - if info.IsDir() { - return nil - } - - if header.Typeflag == tar.TypeReg { - file, err := os.Open(path) - if err != nil { - return fmt.Errorf("%s: open: %v", path, err) - } - defer file.Close() - - _, err = io.CopyN(writer, file, info.Size()) - if err != nil && err != io.EOF { - return fmt.Errorf("%s: copying contents: %v", path, err) - } - } - return nil - }) -} - -func untarFromReader(reader *tar.Reader, dest string) error { - for { - header, err := reader.Next() - if err == io.EOF { - break - } else if err != nil { - return err - } - - switch header.Typeflag { - case tar.TypeDir: - err = mkdir(filepath.Join(dest, header.Name)) - case tar.TypeReg: - err = writeNewFile(filepath.Join(dest, header.Name), reader, header.FileInfo().Mode()) - case tar.TypeSymlink: - err = writeNewSymbolicLink(filepath.Join(dest, header.Name), header.Linkname) - default: - return fmt.Errorf("%s: unknown type flag: %c", header.Name, header.Typeflag) - } - - if err != nil { - return err - } - } - return nil -} - -func writeNewFile(filePath string, in io.Reader, fm os.FileMode) error { - err := os.MkdirAll(filepath.Dir(filePath), 0755) - if err != nil { - return fmt.Errorf("%s: making directory for file: %v", filePath, err) - } - - out, err := os.Create(filePath) - if err != nil { - return fmt.Errorf("%s: creating new file: %v", filePath, err) - } - defer out.Close() - - err = out.Chmod(fm) - if err != nil && runtime.GOOS != "windows" { - return fmt.Errorf("%s: changing file mode: %v", filePath, err) - } - - _, err = io.Copy(out, in) - if err != nil { - return fmt.Errorf("%s: writing file: %v", filePath, err) - } - return nil -} - -func writeNewSymbolicLink(filePath string, target string) error { - err := os.MkdirAll(filepath.Dir(filePath), 0755) - if err != nil { - return fmt.Errorf("%s: making directory for file: %v", filePath, err) - } - - err = os.Symlink(target, filePath) - if err != nil { - return fmt.Errorf("%s: making symbolic link for: %v", filePath, err) - } - - return nil -} - -func mkdir(dirPath string) error { - err := os.MkdirAll(dirPath, 0755) - if err != nil { - return fmt.Errorf("%s: making directory: %v", dirPath, err) - } - return nil -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0c28156 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +use vumm_cli; + +fn main() { + if let Err(e) = vumm_cli::run_cli() { + eprintln!("Error: {}", e); + std::process::exit(1); + } +} diff --git a/vumm_api/Cargo.toml b/vumm_api/Cargo.toml new file mode 100644 index 0000000..c06d49f --- /dev/null +++ b/vumm_api/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "vumm_api" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +flate2 = "1.0" +futures = "0.3" +reqwest = { version = "0.11", features = ["json", "stream"] } +semver = { version = "1.0", features = ["serde"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tar = "0.4" +thiserror = "1.0" +tokio = "1.35" diff --git a/vumm_api/src/auth.rs b/vumm_api/src/auth.rs new file mode 100644 index 0000000..852a210 --- /dev/null +++ b/vumm_api/src/auth.rs @@ -0,0 +1,249 @@ +use serde::{Deserialize, Serialize}; + +use crate::{Client, ClientResult}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum PermissionType { + #[serde(rename = "Readonly")] + Readonly, + #[serde(rename = "Publish")] + Publish, +} + +impl PermissionType { + pub fn from_str(s: &str) -> Self { + match s.to_lowercase().as_str() { + "publish" => PermissionType::Publish, + _ => PermissionType::Readonly, + } + } +} + +pub fn deserialize_permission<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + Ok(PermissionType::from_str(&s)) +} + +impl Default for PermissionType { + fn default() -> Self { + Self::Readonly + } +} + +impl std::str::FromStr for PermissionType { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(PermissionType::from_str(s)) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AuthResult { + pub token: String, + #[serde(rename = "type", deserialize_with = "deserialize_permission")] + pub permission_type: PermissionType, +} + +#[derive(Debug, Serialize)] +struct CredentialsDto { + username: String, + password: String, + #[serde(rename = "type")] + permission_type: PermissionType, +} + +pub struct AuthEndpoint<'a> { + pub client: &'a Client, +} + +impl AuthEndpoint<'_> { + pub async fn login( + &self, + username: String, + password: String, + permission_type: PermissionType, + ) -> ClientResult { + self.auth_request("login", username, password, permission_type) + .await + } + + pub async fn register( + &self, + username: String, + password: String, + permission_type: PermissionType, + ) -> ClientResult { + self.auth_request("register", username, password, permission_type) + .await + } + + async fn auth_request( + &self, + endpoint: &str, + username: String, + password: String, + permission_type: PermissionType, + ) -> ClientResult { + let path = format!("/auth/{}", endpoint); + let body = serde_json::json!(CredentialsDto { + username, + password, + permission_type, + }); + + self.client + .parse_json_response::(self.client.post(path, &body).await?) + .await + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ModMetadata { + pub name: String, + pub version: String, + pub description: Option, + pub author: Option, +} + +#[derive(Debug, Serialize)] +struct PublishModDto { + #[serde(flatten)] + metadata: ModMetadata, + tag: String, + archive: ModArchiveDto, +} + +#[derive(Debug, Serialize)] +struct ModArchiveDto { + data: String, + length: usize, + content_type: String, +} + +#[derive(Debug, Serialize)] +struct GrantPermissionDto { + #[serde(rename = "username")] + username: String, + #[serde(rename = "permission")] + permission_type: Option, + #[serde(rename = "tag")] + tag: String, +} + +pub struct ModsPublishEndpoint<'a> { + pub client: &'a Client, +} + +impl ModsPublishEndpoint<'_> { + pub async fn publish( + &self, + metadata: ModMetadata, + tag: &str, + mut archive: R, + ) -> ClientResult { + use tokio::io::AsyncReadExt; + + let mut buffer = Vec::new(); + archive.read_to_end(&mut buffer).await?; + + let length = buffer.len(); + let content_type = detect_content_type(&buffer); + let data = base64_encode(&buffer); + + let path = format!("/mods/{}/{}", metadata.name, metadata.version); + let body = serde_json::json!(PublishModDto { + metadata, + tag: tag.to_string(), + archive: ModArchiveDto { + data, + length, + content_type, + }, + }); + + self.client.put(path, &body).await + } + + pub async fn unpublish(&self, mod_name: &str, mod_version: &str) -> ClientResult { + let path = format!("/mods/{}/{}", mod_name, mod_version); + self.client.delete(path, &serde_json::Value::Null).await + } + + pub async fn grant( + &self, + mod_name: &str, + mod_tag: &str, + username: &str, + permission_type: Option, + ) -> ClientResult { + let path = format!("/mods/{}/grant", mod_name); + let body = serde_json::json!(GrantPermissionDto { + username: username.to_string(), + permission_type, + tag: mod_tag.to_string(), + }); + + self.client.post(path, &body).await + } + + pub async fn revoke( + &self, + mod_name: &str, + mod_tag: &str, + username: &str, + ) -> ClientResult { + let path = format!("/mods/{}/revoke", mod_name); + let body = serde_json::json!(GrantPermissionDto { + username: username.to_string(), + permission_type: None, + tag: mod_tag.to_string(), + }); + + self.client.post(path, &body).await + } +} + +fn detect_content_type(data: &[u8]) -> String { + if data.starts_with(&[0x1f, 0x8b]) { + return "application/gzip".to_string(); + } + if data.starts_with(b"PK") { + return "application/zip".to_string(); + } + "application/octet-stream".to_string() +} + +fn base64_encode(data: &[u8]) -> String { + const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let mut result = String::new(); + let mut i = 0; + + while i < data.len() { + let b0 = data[i] as usize; + let b1 = if i + 1 < data.len() { data[i + 1] as usize } else { 0 }; + let b2 = if i + 2 < data.len() { data[i + 2] as usize } else { 0 }; + + result.push(CHARS[b0 >> 2] as char); + result.push(CHARS[((b0 & 0x03) << 4) | (b1 >> 4)] as char); + + if i + 1 < data.len() { + result.push(CHARS[((b1 & 0x0f) << 2) | (b2 >> 6)] as char); + } else { + result.push('='); + } + + if i + 2 < data.len() { + result.push(CHARS[b2 & 0x3f] as char); + } else { + result.push('='); + } + + i += 3; + } + + result +} diff --git a/vumm_api/src/lib.rs b/vumm_api/src/lib.rs new file mode 100644 index 0000000..7434f73 --- /dev/null +++ b/vumm_api/src/lib.rs @@ -0,0 +1,140 @@ +pub mod auth; +pub mod mods; + +use serde::de::DeserializeOwned; +use serde_json::Value; + +use crate::auth::{AuthEndpoint, ModsPublishEndpoint}; +use crate::mods::ModsEndpoint; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("bad request")] + BadRequest(reqwest::Response), + + #[error("unauthorized")] + Unauthorized(reqwest::Response), + + #[error("forbidden")] + Forbidden(reqwest::Response), + + #[error("not found")] + NotFound(reqwest::Response), + + #[error("status code {}", reqwest::Response::status(.0))] + Unknown(reqwest::Response), + + #[error("failed to process request: {0}")] + Internal(#[from] reqwest::Error), + + #[error("io error: {0}")] + Io(#[from] std::io::Error), +} + +pub type ClientResult = Result; + +pub struct Client { + http_client: reqwest::Client, + base_url: String, + bearer_token: Option, +} + +impl Clone for Client { + fn clone(&self) -> Self { + Self { + http_client: self.http_client.clone(), + base_url: self.base_url.clone(), + bearer_token: self.bearer_token.clone(), + } + } +} + +impl Client { + pub fn new() -> Self { + Self { + http_client: reqwest::Client::new(), + base_url: String::from("https://vumm.bf3reality.com/api/v1"), + bearer_token: None, + } + } + + pub fn mods(&self) -> ModsEndpoint<'_> { + ModsEndpoint { client: self } + } + + pub fn auth(&self) -> AuthEndpoint<'_> { + AuthEndpoint { client: self } + } + + pub fn mods_publish(&self) -> ModsPublishEndpoint<'_> { + ModsPublishEndpoint { client: self } + } + + pub fn set_bearer_token(&mut self, token: String) { + self.bearer_token = Some(token); + } + + pub fn set_base_url(&mut self, url: String) { + self.base_url = url; + } + + pub async fn get(&self, path: String) -> ClientResult { + self.request(reqwest::Method::GET, path, |req| req).await + } + + pub async fn post(&self, path: String, body: &Value) -> ClientResult { + self.request(reqwest::Method::POST, path, |req| req.json(body)) + .await + } + + pub async fn put(&self, path: String, body: &Value) -> ClientResult { + self.request(reqwest::Method::PUT, path, |req| req.json(body)) + .await + } + + pub async fn delete(&self, path: String, body: &Value) -> ClientResult { + self.request(reqwest::Method::DELETE, path, |req| req.json(body)) + .await + } + + async fn request( + &self, + method: reqwest::Method, + path: String, + request_builder: B, + ) -> ClientResult + where + B: FnOnce(reqwest::RequestBuilder) -> reqwest::RequestBuilder, + { + let url = format!("{}{}", self.base_url, path); + let mut request = self.http_client.request(method.clone(), url); + + if let Some(token) = &self.bearer_token { + request = request.header("Authorization", token); + } + + request = request_builder(request); + + let response = request.send().await?; + + if response.status().is_success() { + Ok(response) + } else { + Err(match response.status() { + // TODO: handle validation errors + reqwest::StatusCode::BAD_REQUEST => Error::BadRequest(response), + reqwest::StatusCode::UNAUTHORIZED => Error::Unauthorized(response), + reqwest::StatusCode::FORBIDDEN => Error::Forbidden(response), + reqwest::StatusCode::NOT_FOUND => Error::NotFound(response), + _ => Error::Unknown(response), + }) + } + } + + async fn parse_json_response( + &self, + response: reqwest::Response, + ) -> ClientResult { + response.json::().await.map_err(Into::into) + } +} \ No newline at end of file diff --git a/vumm_api/src/mods.rs b/vumm_api/src/mods.rs new file mode 100644 index 0000000..4112745 --- /dev/null +++ b/vumm_api/src/mods.rs @@ -0,0 +1,276 @@ +use std::{collections::HashMap, fmt, io::Cursor}; + +use flate2::read::GzDecoder; +use semver::{Version, VersionReq}; +use serde::Deserialize; +use tar::Archive; + +use crate::{Client, ClientResult}; + +#[derive(Deserialize, Debug, Clone)] +pub struct Mod { + pub name: String, + pub description: Option, + pub author: Option, + pub tags: HashMap, + pub versions: HashMap, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct ModVersion { + pub name: String, + pub description: Option, + pub author: Option, + pub version: Version, + pub dependencies: Option>, +} + +impl Mod { + pub fn get_version_by_tag(&self, tag: &str) -> Option { + let tag_version = self.tags.get(tag)?; + + self.versions.get(tag_version).cloned() + } + + pub fn get_version_by_constraint(&self, constraint: &VersionReq) -> Option { + let mut versions = self.versions.values().collect::>(); + + versions.sort_by(|a, b| b.version.cmp(&a.version)); + + for version in versions { + if constraint.matches(&version.version) { + return Some(version.clone()); + } + } + + return None; + } + + pub fn get_last_versions(&self, max_count: usize) -> Mod { + let mut versions: Vec<(&String, &ModVersion)> = self.versions.iter().collect(); + versions.sort_by(|a, b| b.1.version.cmp(&a.1.version)); + versions.truncate(max_count); + + let new_versions: HashMap = versions + .into_iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + let mut mod_clone = self.clone(); + mod_clone.versions = new_versions; + + mod_clone + } +} + +struct UnderlinedText(String); + +impl fmt::Display for UnderlinedText { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let start_underline = "\x1B[4m"; + let reset_formatting = "\x1B[0m"; + + write!(f, "{}{}{}", start_underline, self.0, reset_formatting) + } +} + +impl fmt::Display for ModVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.version.pre.len() > 0 { + true => { + write!( + f, + "{}\n", + UnderlinedText(format!( + "{}.{}.{}-{}", + self.version.major, + self.version.minor, + self.version.patch, + self.version.pre + )) + )?; + } + false => { + write!( + f, + "{}\n", + UnderlinedText(format!( + "{}.{}.{}", + self.version.major, self.version.minor, self.version.patch + )) + )?; + } + } + + match &self.dependencies { + Some(dependencies) if !dependencies.is_empty() => { + write!(f, "Mod Dependencies:\n")?; + for (dep, version_req) in dependencies { + // Adjust the format as per the structure of VersionReq + write!(f, "- {}: {}\n", dep, version_req)?; + } + } + _ => write!(f, "No dependencies.")?, + } + + Ok(()) + } +} + +impl fmt::Display for Mod { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Mod Name: {}", self.name)?; + if let Some(description) = &self.description { + writeln!(f, "Description: {}", description)?; + } + if let Some(author) = &self.author { + writeln!(f, "Author: {}", author)?; + } + writeln!(f, "Tags:")?; + for (tag, version) in &self.tags { + writeln!(f, "- {}: {}", tag, version)?; + } + writeln!(f, "Latest Versions:")?; + for (_version_number, mod_version) in &self.versions { + writeln!(f, "{}", mod_version)?; + } + Ok(()) + } +} + +pub struct ModsEndpoint<'a> { + pub client: &'a Client, +} + +impl ModsEndpoint<'_> { + /// Get a mod by it's name + /// # Example + /// + /// ```no_run + /// use vumm_api::Client; + /// + /// let client = Client::new(); + /// let mod_name = String::from("mapeditor"); + /// + /// let mods = client.mods(); + /// let mod_response = mods.get(mod_name); + /// ``` + /// + /// # Arguments + /// + /// `mod_name` - The name of the mod to get + pub async fn get(&self, mod_name: String) -> ClientResult { + let path = format!("/mods/{}", mod_name); + + return self + .client + .parse_json_response::(self.client.get(path).await?) + .await; + } + + pub async fn get_version( + &self, + mod_name: String, + mod_version: String, + ) -> ClientResult { + let path = format!("/mods/{}/{}", mod_name, mod_version); + + return self + .client + .parse_json_response::(self.client.get(path).await?) + .await; + } + + pub async fn download_version( + &self, + mod_name: String, + mod_version: String, + ) -> ClientResult>>>> { + let path = format!("/mods/{}/{}/download", mod_name, mod_version); + + let res = self.client.get(path).await?; + + let bytes = res.bytes().await?.to_vec(); + + // Create a Cursor to read the downloaded bytes + let cursor = Cursor::new(bytes); + + // Open the gzipped tar archive + let decoder = GzDecoder::new(cursor); + let archive = Archive::new(decoder); + + return Ok(archive); + } +} + +#[cfg(test)] + +mod tests { + use super::*; + + #[test] + fn test_mod_get_version_by_tag() { + let mod_json = r#" + { + "name": "mapeditor", + "description": "A map editor for Voxel Universe", + "author": "Voxel Universe", + "tags": { + "latest": "0.1.0" + }, + "versions": { + "0.1.0": { + "name": "mapeditor", + "description": "A map editor for Voxel Universe", + "author": "Voxel Universe", + "version": "0.1.0", + "dependencies": {} + } + } + } + "#; + + let mod_obj: Mod = serde_json::from_str(mod_json).unwrap(); + + let version = mod_obj.get_version_by_tag("latest").unwrap(); + + assert_eq!(version.version, Version::parse("0.1.0").unwrap()); + } + + #[test] + fn test_mod_get_version_by_constraint() { + let mod_json = r#" + { + "name": "mapeditor", + "description": "A map editor for Voxel Universe", + "author": "Voxel Universe", + "tags": { + "latest": "0.1.0" + }, + "versions": { + "0.1.0": { + "name": "mapeditor", + "description": "A map editor for Voxel Universe", + "author": "Voxel Universe", + "version": "0.1.0", + "dependencies": {} + }, + "0.2.0": { + "name": "mapeditor", + "description": "A map editor for Voxel Universe", + "author": "Voxel Universe", + "version": "0.2.0", + "dependencies": {} + } + } + } + "#; + + let mod_obj: Mod = serde_json::from_str(mod_json).unwrap(); + + let version = mod_obj + .get_version_by_constraint(&VersionReq::parse(">= 0.2.0").unwrap()) + .unwrap(); + + assert_eq!(version.version, Version::parse("0.2.0").unwrap()); + } +} diff --git a/vumm_cli/Cargo.toml b/vumm_cli/Cargo.toml new file mode 100644 index 0000000..4524605 --- /dev/null +++ b/vumm_cli/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "vumm_cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1.35", features = ["full"] } +clap = { version = "4.4", features = ["derive"] } +semver = "1.0" +regex = "1.10" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +directories = "5" +anyhow = "1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +rpassword = "7" +parse_duration = "2" +flate2 = "1.0" +tar = "0.4" +self_update = "0.36" +keyring = { version = "3", features = ["windows-native", "apple-native"] } + +vumm_api = { path = "../vumm_api" } +vumm_installer = { path = "../vumm_installer" } diff --git a/vumm_cli/src/commands/auth.rs b/vumm_cli/src/commands/auth.rs new file mode 100644 index 0000000..cfb42ec --- /dev/null +++ b/vumm_cli/src/commands/auth.rs @@ -0,0 +1,119 @@ +use std::io::{self, Write}; + +use anyhow::Result; +use clap::Args; +use vumm_api::auth::PermissionType; + +use crate::config::Config; + +#[derive(Args)] +#[command(about = "Login with username and password", long_about = None)] +pub struct Login { + #[arg(long, default_value = "publish")] + token_type: String, +} + +impl Login { + pub async fn run(&self, config: &Config) -> Result<()> { + let permission_type = PermissionType::from_str(&self.token_type); + + let mut client = vumm_api::Client::new(); + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + + print!("Username: "); + io::stdout().flush()?; + let mut username = String::new(); + io::stdin().read_line(&mut username)?; + let username = username.trim().to_string(); + + print!("Password: "); + io::stdout().flush()?; + let password = rpassword::read_password()?; + let password = password.trim().to_string(); + + print!("Logging in... "); + io::stdout().flush()?; + + let result = client + .auth() + .login(username, password, permission_type) + .await?; + + config.set_token(&result.token)?; + println!("OK"); + + Ok(()) + } +} + +#[derive(Args)] +#[command(about = "Register a new account", long_about = None)] +pub struct Register { + #[arg(long, default_value = "publish")] + token_type: String, +} + +impl Register { + pub async fn run(&self, config: &Config) -> Result<()> { + let permission_type = PermissionType::from_str(&self.token_type); + + let mut client = vumm_api::Client::new(); + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + + print!("Username: "); + io::stdout().flush()?; + let mut username = String::new(); + io::stdin().read_line(&mut username)?; + let username = username.trim().to_string(); + + print!("Password: "); + io::stdout().flush()?; + let password = rpassword::read_password()?; + let password = password.trim().to_string(); + + print!("Confirm Password: "); + io::stdout().flush()?; + let password_confirm = rpassword::read_password()?; + let password_confirm = password_confirm.trim().to_string(); + + if password != password_confirm { + anyhow::bail!("Passwords do not match"); + } + + print!("Registering... "); + io::stdout().flush()?; + + let result = client + .auth() + .register(username, password, permission_type) + .await?; + + config.set_token(&result.token)?; + println!("OK"); + + Ok(()) + } +} + +#[derive(Args)] +#[command(about = "Logout and delete stored credentials", long_about = None)] +pub struct Logout; + +impl Logout { + pub async fn run(&self, config: &Config) -> Result<()> { + if !config.has_token() { + println!("No active session (not logged in)"); + return Ok(()); + } + + match config.clear_token() { + Ok(()) => println!("Logged out successfully"), + Err(e) => eprintln!("Error clearing token: {}", e), + } + Ok(()) + } +} diff --git a/vumm_cli/src/commands/get_info.rs b/vumm_cli/src/commands/get_info.rs new file mode 100644 index 0000000..9643fb2 --- /dev/null +++ b/vumm_cli/src/commands/get_info.rs @@ -0,0 +1,84 @@ +use clap::Args; +use semver::VersionReq; +use vumm_api::{Client, Error}; + +use crate::config::Config; + +const SHOW_LAST_VERSION_COUNT: usize = 3; + +fn parse_mod_string(input: &str) -> (String, Option) { + if let Some((name, version)) = input.split_once('@') { + (name.to_string(), Some(version.to_string())) + } else { + (input.to_string(), None) + } +} + +#[derive(Args)] +#[command( + about = "Get information about a specific mod", + arg_required_else_help = true +)] +pub struct ModInfo { + #[arg(help = "Name of the mod (can include tag like mod@latest or mod@1.0.0)", required = true)] + mod_name: String, + #[arg(help = "Version/Tag of the mod OR Version comparator [e.g. '<0.5'] (optional)")] + mod_version: Option, +} + +impl ModInfo { + pub async fn run(&mut self, config: &Config) -> anyhow::Result<()> { + let mut client = Client::new(); + + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + if let Some(token) = config.get_token() { + client.set_bearer_token(token.clone()); + } + + let (mod_name, tag_or_version) = parse_mod_string(&self.mod_name); + self.mod_name = mod_name; + if let Some(v) = tag_or_version { + self.mod_version = Some(v); + } + + let mod_ = match client.mods().get(self.mod_name.clone()).await { + Ok(returned_mod) => returned_mod, + Err(Error::NotFound(response)) => { + eprintln!("Mod not found: {}", response.status()); + return Ok(()); + } + Err(e) => { + eprintln!("Error retrieving mod: {}", e); + return Ok(()); + } + }; + + if let Some(mod_version) = &self.mod_version { + match VersionReq::parse(mod_version) { + Ok(version_req) => { + if let Some(mod_version) = mod_.get_version_by_constraint(&version_req) { + println!("Found matching version: {}", mod_version); + } else { + println!("No version found matching the provided constraint."); + } + } + Err(_) => { + if let Some(mod_version) = mod_.get_version_by_tag(mod_version) { + println!("Found version by tag: {}", mod_version); + } else { + println!("No version found with the provided tag."); + } + } + } + } else { + println!( + "Mod found: {}", + mod_.get_last_versions(SHOW_LAST_VERSION_COUNT) + ); + } + + Ok(()) + } +} diff --git a/vumm_cli/src/commands/install.rs b/vumm_cli/src/commands/install.rs new file mode 100644 index 0000000..4ce1fb5 --- /dev/null +++ b/vumm_cli/src/commands/install.rs @@ -0,0 +1,187 @@ +use anyhow::Result; +use clap::Args; +use vumm_api::Client; +use std::fs; +use std::path::Path; + +use crate::config::Config; +use vumm_installer::DependencyResolver; + +fn copy_dir_all(src: &Path, dst: &Path) -> Result<()> { + fs::create_dir_all(dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(&entry.path(), &dst.join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.join(entry.file_name()))?; + } + } + Ok(()) +} + +#[derive(Args)] +#[command(about = "Install a mod", long_about = None)] +pub struct Install { + #[arg(help = "Name of the mod OR name@version of mod", required = true)] + mod_name: String, + + #[arg(long, default_value = "30m")] + timeout: String, + + #[arg(long, short = 'd', help = "Target directory for mod symlinks")] + target_dir: Option, +} + +impl Install { + pub async fn run(&self, config: &Config) -> Result<()> { + let _timeout = parse_duration::parse(&self.timeout) + .map_err(|e| anyhow::anyhow!("Invalid timeout: {}", e))?; + + let mut client = Client::new(); + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + if let Some(token) = config.get_token() { + client.set_bearer_token(token.clone()); + } + + let (mod_name, mod_version) = parse_mod_string(&self.mod_name); + + println!("Installing {}...", self.mod_name); + + let resolver = DependencyResolver::new(client.clone()); + + let lock_file = resolver + .resolve_dependencies(mod_name.clone(), mod_version.clone()) + .await?; + + let target_dir = match &self.target_dir { + Some(d) => std::path::PathBuf::from(d), + None => std::env::current_dir()?.join("mods"), + }; + + let cache_dir = get_cache_directory()?; + + print!("Installing to: {} ... ", target_dir.display()); + std::fs::create_dir_all(&target_dir)?; + println!("OK"); + + std::fs::create_dir_all(&cache_dir)?; + + println!("Installing {} mods...", lock_file.mods.len()); + + for (name, locked_mod) in &lock_file.mods { + print!(" {} v{} ... ", name, locked_mod.version); + + let mod_cache_dir = cache_dir.join(name).join(locked_mod.version.to_string()); + + if !mod_cache_dir.exists() { + let archive = client.mods() + .download_version(name.clone(), locked_mod.version.to_string()) + .await; + + match archive { + Ok(mut archive) => { + let temp_dir = cache_dir.join(".tmp").join(name).join(locked_mod.version.to_string()); + std::fs::create_dir_all(&temp_dir)?; + + for entry in archive.entries()? { + let mut entry = entry?; + let path = entry.path()?.into_owned(); + let full_path = temp_dir.join(&path); + + if let Some(parent) = full_path.parent() { + std::fs::create_dir_all(parent)?; + } + + entry.unpack(&full_path)?; + } + + if let Err(e) = std::fs::rename(&temp_dir, &mod_cache_dir) { + let _ = std::fs::remove_dir_all(&temp_dir); + return Err(e).map_err(|e| anyhow::anyhow!("Failed to move temp dir: {}", e)); + } + } + Err(e) => { + println!("FAILED"); + eprintln!(" Download error: {}", e); + continue; + } + } + } + + let target_link = target_dir.join(name); + + #[cfg(windows)] + { + if target_link.exists() { + if target_link.is_dir() { + let _ = std::fs::remove_dir_all(&target_link); + } else { + let _ = std::fs::remove_file(&target_link); + } + } + copy_dir_all(&mod_cache_dir, &target_link)?; + } + + #[cfg(not(windows))] + { + if target_link.exists() { + std::fs::remove_file(&target_link).ok(); + } + std::os::unix::fs::symlink(&mod_cache_dir, &target_link)?; + } + + println!("OK"); + } + + let lock_file_path = target_dir.join("vumm.lock"); + lock_file.save(lock_file_path.to_str().unwrap())?; + + println!("\nDone! Installed {} mods.", lock_file.mods.len()); + + Ok(()) + } +} + +fn get_cache_directory() -> Result { + if let Some(proj_dirs) = directories::ProjectDirs::from("com", "bf3rm", "vumm") { + Ok(proj_dirs.cache_dir().to_path_buf()) + } else { + anyhow::bail!("Could not determine cache directory") + } +} + +fn parse_mod_string(input: &str) -> (String, String) { + if let Some((name, version)) = input.split_once('@') { + (name.to_string(), version.to_string()) + } else { + (input.to_string(), "latest".to_string()) + } +} + +#[derive(Args)] +#[command(about = "Uninstall a mod", long_about = None)] +pub struct Uninstall { + #[arg(help = "Name of the mod to uninstall (can include tag like mod@latest)", required = true)] + mod_name: String, +} + +impl Uninstall { + pub async fn run(&self, _config: &Config) -> Result<()> { + let (mod_name, _tag) = parse_mod_string(&self.mod_name); + let target_dir = std::env::current_dir()?.join("mods"); + let target_link = target_dir.join(&mod_name); + + if target_link.exists() { + std::fs::remove_dir_all(&target_link)?; + println!("Uninstalled {}", mod_name); + } else { + println!("{} is not installed", mod_name); + } + + Ok(()) + } +} diff --git a/vumm_cli/src/commands/mod.rs b/vumm_cli/src/commands/mod.rs new file mode 100644 index 0000000..b714885 --- /dev/null +++ b/vumm_cli/src/commands/mod.rs @@ -0,0 +1,6 @@ +pub mod auth; +pub mod get_info; +pub mod install; +pub mod permissions; +pub mod publish; +pub mod update; \ No newline at end of file diff --git a/vumm_cli/src/commands/permissions.rs b/vumm_cli/src/commands/permissions.rs new file mode 100644 index 0000000..684cc6c --- /dev/null +++ b/vumm_cli/src/commands/permissions.rs @@ -0,0 +1,86 @@ +use anyhow::{Context, Result}; +use clap::Args; +use vumm_api::auth::PermissionType; + +use crate::config::Config; + +#[derive(Args)] +#[command(about = "Grant permissions to a user for a mod", long_about = None)] +pub struct Grant { + #[arg(help = "Mod name", required = true)] + mod_name: String, + + #[arg(help = "Username to grant permission", required = true)] + username: String, + + #[arg(help = "Permission type: publish or readonly", required = true)] + permission: String, +} + +impl Grant { + pub async fn run(&self, config: &Config) -> Result<()> { + let token = config.get_token() + .context("Not logged in. Run 'vumm login' first")?; + + let permission_type = PermissionType::from_str(&self.permission); + + let mut client = vumm_api::Client::new(); + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + client.set_bearer_token(token.clone()); + + println!("Granting {} permission to {} for {}", + self.permission, self.username, self.mod_name); + + let response = client.mods_publish() + .grant(&self.mod_name, "", &self.username, Some(permission_type)) + .await?; + + if response.status().is_success() { + println!("Successfully granted permission"); + } else { + eprintln!("Failed to grant permission: {}", response.status()); + } + + Ok(()) + } +} + +#[derive(Args)] +#[command(about = "Revoke permissions from a user for a mod", long_about = None)] +pub struct Revoke { + #[arg(help = "Mod name", required = true)] + mod_name: String, + + #[arg(help = "Username to revoke permission", required = true)] + username: String, +} + +impl Revoke { + pub async fn run(&self, config: &Config) -> Result<()> { + let token = config.get_token() + .context("Not logged in. Run 'vumm login' first")?; + + let mut client = vumm_api::Client::new(); + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + client.set_bearer_token(token.clone()); + + println!("Revoking permission from {} for {}", + self.username, self.mod_name); + + let response = client.mods_publish() + .revoke(&self.mod_name, "", &self.username) + .await?; + + if response.status().is_success() { + println!("Successfully revoked permission"); + } else { + eprintln!("Failed to revoke permission: {}", response.status()); + } + + Ok(()) + } +} diff --git a/vumm_cli/src/commands/publish.rs b/vumm_cli/src/commands/publish.rs new file mode 100644 index 0000000..196610a --- /dev/null +++ b/vumm_cli/src/commands/publish.rs @@ -0,0 +1,278 @@ +use std::collections::HashMap; +use std::fs::{self, File}; +use std::io::Read; +use std::path::Path; + +use anyhow::{Context, Result}; +use clap::Args; +use flate2::write::GzEncoder; +use flate2::Compression; +use serde::{Deserialize, Serialize}; +use tar::Builder; +use vumm_api::auth::ModMetadata; + +use crate::config::Config; + +const MOD_JSON_FILENAME: &str = "mod.json"; +const DEFAULT_IGNORES: &[&str] = &[ + ".git", + ".gitignore", + ".github/", + + "node_modules/", + "package.json", + "package-lock.json", + "yarn.lock", + ".vscode/", + ".idea/", + "*.iml", + ".vummignore", +]; + +fn parse_mod_string(input: &str) -> (String, String) { + if let Some((name, version)) = input.split_once('@') { + (name.to_string(), version.to_string()) + } else { + (input.to_string(), String::new()) + } +} + +#[derive(Serialize, Deserialize)] +pub struct ModJson { + pub name: String, + pub version: String, + #[serde(default)] + pub dependencies: HashMap, + #[serde(default)] + pub description: Option, + #[serde(default)] + pub author: Option, +} + +impl From for ModMetadata { + fn from(m: ModJson) -> Self { + ModMetadata { + name: m.name.to_lowercase(), + version: m.version, + description: m.description, + author: m.author, + } + } +} + +fn load_mod_json(project_path: &Path) -> Result { + let mod_json_path = project_path.join(MOD_JSON_FILENAME); + let contents = fs::read_to_string(&mod_json_path) + .with_context(|| format!("Failed to read {}", MOD_JSON_FILENAME))?; + let contents = contents.trim_start_matches('\u{feff}'); + let mod_json: ModJson = serde_json::from_str(contents) + .with_context(|| format!("Failed to parse {}", MOD_JSON_FILENAME))?; + Ok(mod_json) +} + +fn should_ignore(path: &Path, ignores: &[String]) -> bool { + let path_str = path.to_string_lossy(); + for ignore in ignores { + if ignore.starts_with('^') { + let prefix = &ignore[1..]; + if path_str.starts_with(prefix) { + return true; + } + } else if path_str.contains(ignore) { + return true; + } + } + false +} + +fn create_tarball(project_path: &Path, output_path: &Path) -> Result<()> { + let _mod_json = load_mod_json(project_path)?; + + let ignores = load_ignores(project_path)?; + + let file = File::create(output_path)?; + let encoder = GzEncoder::new(file, Compression::default()); + let mut builder = Builder::new(encoder); + + let mut entries = Vec::new(); + for entry in fs::read_dir(project_path)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() || path.is_dir() { + entries.push(path); + } + } + + for path in entries { + let relative_path = path.strip_prefix(project_path).unwrap(); + + if should_ignore(relative_path, &ignores) { + continue; + } + + let mut file = File::open(&path)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + + let mut header = tar::Header::new_old(); + header.set_path(relative_path)?; + if path.is_dir() { + header.set_entry_type(tar::EntryType::Directory); + header.set_mode(0o755); + } else { + header.set_size(buffer.len() as u64); + header.set_entry_type(tar::EntryType::Regular); + header.set_mode(0o644); + } + + builder.append(&header, buffer.as_slice())?; + } + + builder.finish()?; + + Ok(()) +} + +fn load_ignores(project_path: &Path) -> Result> { + let mut ignores: Vec = DEFAULT_IGNORES.iter().map(|s| s.to_string()).collect(); + + let vummignore_path = project_path.join(".vummignore"); + if vummignore_path.exists() { + let contents = fs::read_to_string(&vummignore_path)?; + for line in contents.lines() { + let line = line.trim(); + if !line.is_empty() && !line.starts_with('#') { + ignores.push(line.to_string()); + } + } + } + + Ok(ignores) +} + +#[derive(Args)] +#[command(about = "Pack current directory into a tarball", long_about = None)] +pub struct Pack { + #[arg(long, default_value = "30m")] + timeout: String, +} + +impl Pack { + pub async fn run(&self, _config: &Config) -> Result<()> { + let project_path = std::env::current_dir() + .context("Failed to get current directory")?; + + println!("Packing mod in {}", project_path.display()); + + let mod_json = load_mod_json(&project_path)?; + + println!("Mod: {} v{}", mod_json.name, mod_json.version); + + let output_filename = format!("{}-{}.tar.gz", mod_json.name, mod_json.version); + let output_path = project_path.join(&output_filename); + + create_tarball(&project_path, &output_path)?; + + println!("Packed to {}", output_filename); + + Ok(()) + } +} + +#[derive(Args)] +#[command(about = "Publish a mod to the registry", long_about = None)] +pub struct Publish { + #[arg(long, default_value = "latest")] + tag: String, + + #[arg(long, default_value = "30m")] + timeout: String, +} + +impl Publish { + pub async fn run(&self, config: &Config) -> Result<()> { + let token = config.get_token() + .context("Not logged in. Run 'vumm login' first")?; + + let project_path = std::env::current_dir() + .context("Failed to get current directory")?; + + println!("Publishing mod from {}", project_path.display()); + + let mod_json = load_mod_json(&project_path)?; + + println!("Publishing: {} v{}", mod_json.name, mod_json.version); + + let output_filename = format!("{}-{}.tar.gz", mod_json.name, mod_json.version); + let output_path = project_path.join(&output_filename); + + create_tarball(&project_path, &output_path)?; + + let mut file = File::open(&output_path)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + + let mut client = vumm_api::Client::new(); + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + client.set_bearer_token(token.clone()); + + let metadata: ModMetadata = mod_json.into(); + let metadata_name = metadata.name.clone(); + + let response = client.mods_publish() + .publish(metadata, &self.tag, buffer.as_slice()) + .await?; + + if response.status().is_success() { + println!("Successfully published {} as {}", metadata_name, self.tag); + } else { + eprintln!("Failed to publish: {}", response.status()); + } + + fs::remove_file(&output_path)?; + + Ok(()) + } +} + +#[derive(Args)] +#[command(about = "Unpublish a mod version from the registry", long_about = None)] +pub struct Unpublish { + #[arg(help = "Mod name (can include version like mod@1.0.0)", required = true)] + mod_name: String, +} + +impl Unpublish { + pub async fn run(&self, config: &Config) -> Result<()> { + let token = config.get_token() + .context("Not logged in. Run 'vumm login' first")?; + + let (mod_name, version) = parse_mod_string(&self.mod_name); + + if version.is_empty() { + anyhow::bail!("Version is required for unpublish"); + } + + let mut client = vumm_api::Client::new(); + if let Some(registry) = &config.registry { + client.set_base_url(registry.clone()); + } + client.set_bearer_token(token.clone()); + + println!("Unpublishing {}@{}", mod_name, version); + + let response = client.mods_publish() + .unpublish(&mod_name, &version) + .await?; + + if response.status().is_success() { + println!("Successfully unpublished {}@{}", mod_name, version); + } else { + eprintln!("Failed to unpublish: {}", response.status()); + } + + Ok(()) + } +} diff --git a/vumm_cli/src/commands/update.rs b/vumm_cli/src/commands/update.rs new file mode 100644 index 0000000..34a3589 --- /dev/null +++ b/vumm_cli/src/commands/update.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use clap::Args; + +use crate::config::Config; + +#[derive(Args)] +#[command(about = "Check for updates", long_about = None)] +pub struct Update; + +impl Update { + pub async fn run(&self, _config: &Config) -> Result<()> { + let current_version = env!("CARGO_PKG_VERSION"); + + println!("Current version: v{}", current_version); + println!("Please check GitHub releases for updates:"); + println!("https://github.com/BF3RM/vumm/releases"); + + Ok(()) + } +} diff --git a/vumm_cli/src/config.rs b/vumm_cli/src/config.rs new file mode 100644 index 0000000..30f88a8 --- /dev/null +++ b/vumm_cli/src/config.rs @@ -0,0 +1,82 @@ +use anyhow::Result; +use directories::ProjectDirs; +use keyring::Entry; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +const SERVICE_NAME: &str = "vumm"; +const TOKEN_KEY: &str = "token"; + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct Config { + pub registry: Option, +} + +impl Config { + pub fn load() -> Result { + let config_path = Self::config_path()?; + + if !config_path.exists() { + return Ok(Self::default()); + } + + let contents = fs::read_to_string(&config_path)?; + let config: Config = serde_json::from_str(&contents)?; + Ok(config) + } + + #[allow(dead_code)] + pub fn save(&self) -> Result<()> { + let config_path = Self::config_path()?; + + if let Some(parent) = config_path.parent() { + fs::create_dir_all(parent)?; + } + + let contents = serde_json::to_string_pretty(self)?; + fs::write(&config_path, contents)?; + Ok(()) + } + + pub fn get_token(&self) -> Option { + let entry = Entry::new(SERVICE_NAME, TOKEN_KEY).ok()?; + entry.get_password().ok() + } + + pub fn has_token(&self) -> bool { + self.get_token().is_some() + } + + pub fn set_token(&self, token: &str) -> Result<()> { + let entry = Entry::new(SERVICE_NAME, TOKEN_KEY)?; + entry.set_password(token)?; + Ok(()) + } + + pub fn clear_token(&self) -> Result<()> { + let entry = Entry::new(SERVICE_NAME, TOKEN_KEY)?; + match entry.delete_credential() { + Ok(()) => Ok(()), + Err(e) + if e.to_string().contains("not found") + || e.to_string().contains("Element not found") => + { + Err(anyhow::anyhow!("not found")) + } + Err(e) => Err(anyhow::anyhow!(e)), + } + } + + fn config_path() -> Result { + if let Some(proj_dirs) = ProjectDirs::from("com", "bf3rm", "vumm") { + return Ok(proj_dirs.config_dir().join("config.json")); + } + let local_app_data = std::env::var("LOCALAPPDATA") + .map_err(|_| anyhow::anyhow!("Could not determine config directory"))?; + Ok(PathBuf::from(local_app_data) + .join("bf3rm") + .join("vumm") + .join("config.json")) + } +} diff --git a/vumm_cli/src/context.rs b/vumm_cli/src/context.rs new file mode 100644 index 0000000..a8f951b --- /dev/null +++ b/vumm_cli/src/context.rs @@ -0,0 +1,25 @@ +use std::time::Duration; +use tokio::time::timeout; + +#[allow(dead_code)] +pub struct CommandContext { + pub timeout: Duration, +} + +#[allow(dead_code)] +impl CommandContext { + pub fn new(timeout: Duration) -> Self { + Self { timeout } + } + + pub async fn run_with_timeout(self, f: F) -> Result + where + F: FnOnce() -> Fut, + Fut: std::future::Future, + { + match timeout(self.timeout, f()).await { + Ok(result) => Ok(result), + Err(_) => Err(format!("Operation timed out after {:?}", self.timeout)), + } + } +} diff --git a/vumm_cli/src/lib.rs b/vumm_cli/src/lib.rs new file mode 100644 index 0000000..3394fd1 --- /dev/null +++ b/vumm_cli/src/lib.rs @@ -0,0 +1,73 @@ +use clap::{Parser, Subcommand}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +mod commands; +mod config; +mod context; + +use config::Config; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +struct Cli { + #[command(subcommand)] + command: Commands, + + #[arg(short, long, global = true)] + verbose: bool, + + #[arg(long, global = true)] + registry: Option, +} + +#[derive(Subcommand)] +#[clap(version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"), about = env!("CARGO_PKG_DESCRIPTION"))] +enum Commands { + Info(commands::get_info::ModInfo), + Login(commands::auth::Login), + Register(commands::auth::Register), + Logout(commands::auth::Logout), + Install(commands::install::Install), + Uninstall(commands::install::Uninstall), + Pack(commands::publish::Pack), + Publish(commands::publish::Publish), + Unpublish(commands::publish::Unpublish), + Grant(commands::permissions::Grant), + Revoke(commands::permissions::Revoke), + Update(commands::update::Update), +} + +#[tokio::main] +pub async fn run_cli() -> anyhow::Result<()> { + let cli: Cli = Cli::parse(); + + if cli.verbose { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "debug".into()), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); + } + + let config = Config::load().unwrap_or_default(); + + match cli.command { + Commands::Info(mut cmd) => cmd.run(&config).await?, + Commands::Login(cmd) => cmd.run(&config).await?, + Commands::Register(cmd) => cmd.run(&config).await?, + Commands::Logout(cmd) => cmd.run(&config).await?, + Commands::Install(cmd) => cmd.run(&config).await?, + Commands::Uninstall(cmd) => cmd.run(&config).await?, + Commands::Pack(cmd) => cmd.run(&config).await?, + Commands::Publish(cmd) => cmd.run(&config).await?, + Commands::Unpublish(cmd) => cmd.run(&config).await?, + Commands::Grant(cmd) => cmd.run(&config).await?, + Commands::Revoke(cmd) => cmd.run(&config).await?, + Commands::Update(cmd) => cmd.run(&config).await?, + } + + Ok(()) +} diff --git a/vumm_installer/Cargo.toml b/vumm_installer/Cargo.toml new file mode 100644 index 0000000..542e81b --- /dev/null +++ b/vumm_installer/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "vumm_installer" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0" +async-trait = "0.1" +semver = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1.35", features = ["full"] } +vumm_api = { path = "../vumm_api" } diff --git a/vumm_installer/src/dependency_resolver.rs b/vumm_installer/src/dependency_resolver.rs new file mode 100644 index 0000000..1f632a6 --- /dev/null +++ b/vumm_installer/src/dependency_resolver.rs @@ -0,0 +1,124 @@ +use std::collections::{HashMap, VecDeque}; + +use vumm_api::{mods::ModVersion, Client}; + +use super::lock::{LockFile, LockedMod}; +use super::ModVersionConstraint; + +#[derive(thiserror::Error, Debug)] +pub enum DependencyResolverError { + #[error("Mod not found: {0}")] + ModNotFound(String), + + #[error("Insufficient permissions to access mod: {0}")] + ModNoAccess(String), + + #[error("Mod version not found: {0}@{1}")] + ModVersionNotFound(String, String), + + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +pub struct DependencyResolver { + api_client: Client, +} + +impl DependencyResolver { + pub fn new(api_client: Client) -> DependencyResolver { + DependencyResolver { + api_client: api_client, + } + } + + pub async fn resolve_dependencies( + &self, + mod_name: String, + mod_version: String, + ) -> Result { + let mut unresolved = VecDeque::new(); + let mut lock_file = LockFile { + mods: HashMap::new(), + }; + + unresolved.push_back(ModVersionConstraint::new(mod_name, mod_version)); + + while let Some(constraint) = unresolved.pop_front() { + let resolved_version = self.resolve_dependency_version(&constraint).await?; + + lock_file.mods.insert( + constraint.name.to_string(), + LockedMod { + name: constraint.name.to_string(), + version: resolved_version.version.clone(), + dependencies: resolved_version.dependencies.clone().unwrap_or_default(), + }, + ); + + if let Some(dependencies) = resolved_version.dependencies { + for (dep_name, dep_version) in dependencies.iter() { + // Skip internal veniceext mod + if dep_name == "veniceext" { + continue; + } + + // TODO: Check if dependency is already part of the lock file + + // TODO: Check if not already resolving this dependency and whether the version matches + + unresolved.push_back(ModVersionConstraint { + name: dep_name.clone(), + tag: None, + version: Some(dep_version.clone()), + }); + } + } + } + + return Ok(lock_file); + } + + pub async fn resolve_dependency_version( + &self, + mod_constraint: &ModVersionConstraint, + ) -> Result { + let mod_response = self + .api_client + .mods() + .get(mod_constraint.name.to_string()) + .await; + + match mod_response { + Ok(mod_info) => { + if let Some(tag) = &mod_constraint.tag { + mod_info.get_version_by_tag(tag).ok_or( + DependencyResolverError::ModVersionNotFound( + mod_constraint.name.to_string(), + tag.to_string(), + ), + ) + } else if let Some(version) = &mod_constraint.version { + mod_info.get_version_by_constraint(version).ok_or( + DependencyResolverError::ModVersionNotFound( + mod_constraint.name.to_string(), + version.to_string(), + ), + ) + } else { + Err(DependencyResolverError::Other(anyhow::anyhow!( + "Invalid mod version constraint" + ))) + } + } + Err(err) => Err(match err { + vumm_api::Error::NotFound(_) => { + DependencyResolverError::ModNotFound(mod_constraint.name.to_string()) + } + vumm_api::Error::Forbidden(_) => { + DependencyResolverError::ModNoAccess(mod_constraint.name.to_string()) + } + _ => DependencyResolverError::Other(err.into()), + }), + } + } +} diff --git a/vumm_installer/src/lib.rs b/vumm_installer/src/lib.rs new file mode 100644 index 0000000..b7916da --- /dev/null +++ b/vumm_installer/src/lib.rs @@ -0,0 +1,109 @@ +mod dependency_resolver; +mod lock; + +use core::fmt; +use semver::VersionReq; + +pub use dependency_resolver::{DependencyResolver, DependencyResolverError}; +pub use lock::{LockFile, LockedMod}; + +#[derive(Clone, Debug)] +pub struct ModVersionConstraint { + name: String, + tag: Option, + version: Option, +} + +impl ModVersionConstraint { + pub fn new(mod_name: String, mod_version: String) -> Self { + let req = VersionReq::parse(mod_version.as_str()); + + match req { + Ok(version_req) => ModVersionConstraint { + name: mod_name, + tag: None, + version: Some(version_req), + }, + Err(_) => ModVersionConstraint { + name: mod_name, + tag: Some(mod_version), + version: None, + }, + } + } + + pub fn get_name(&self) -> &String { + &self.name + } + + pub fn get_tag(&self) -> &Option { + &self.tag + } + + pub fn get_version(&self) -> &Option { + &self.version + } +} + +impl fmt::Display for ModVersionConstraint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(version) = &self.version { + write!(f, "{}@{}", self.name, version) + } else { + write!(f, "{}@{}", self.name, self.tag.as_ref().unwrap()) + } + } +} + +#[cfg(test)] +mod tests { + use vumm_api::Client; + + use super::*; + + // #[test] + // fn test_resolve_version_constraint() { + // let mod_name = String::from("test"); + // let mod_version = String::from("1.0.0"); + + // let constraint = resolve_mod_version_constraint(mod_name, mod_version); + + // assert_eq!(constraint.name, mod_name); + // assert_eq!(constraint.tag, None); + // assert_eq!( + // constraint.version, + // Some(VersionReq::parse(mod_version.as_str()).unwrap()) + // ); + // } + + // #[test] + // fn test_resolve_version_constraint_tag() { + // let mod_name = String::from("test"); + // let mod_version = String::from("latest"); + + // let constraint = resolve_mod_version_constraint(mod_name, mod_version); + + // assert_eq!(constraint.name, mod_name); + // assert_eq!(constraint.tag, Some(mod_version)); + // assert_eq!(constraint.version, None); + // } + + #[tokio::test] + async fn test_resolve_dependencies() { + let api_client = Client::new(); + let resolver = DependencyResolver::new(api_client); + + let lock_file = resolver + .resolve_dependencies("mapeditor".to_string(), "preview".to_string()) + .await; + + match lock_file { + Ok(lock_file) => { + println!("Lock file: {:#?}", lock_file); + } + Err(err) => { + println!("Error: {}", err); + } + } + } +} diff --git a/vumm_installer/src/lock.rs b/vumm_installer/src/lock.rs new file mode 100644 index 0000000..fa00539 --- /dev/null +++ b/vumm_installer/src/lock.rs @@ -0,0 +1,34 @@ +use std::{collections::HashMap, fs}; + +use semver::{Version, VersionReq}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct LockedMod { + pub name: String, + pub version: Version, + pub dependencies: HashMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct LockFile { + pub mods: HashMap, +} + +impl LockFile { + pub fn new() -> Self { + LockFile { + mods: HashMap::new(), + } + } + + pub fn load(path: &str) -> Result { + let json = fs::read_to_string(path)?; + serde_json::from_str(json.as_str()).map_err(|e| e.into()) + } + + pub fn save(&self, path: &str) -> Result<(), anyhow::Error> { + let json = serde_json::to_string_pretty(self)?; + fs::write(path, json.as_bytes()).map_err(|e| e.into()) + } +}