refactor: Use a custom ResultFromCli to enable Cancel and Back variants and lift CLI args on Ok/Err (#4)

This commit is contained in:
FroVolod
2023-04-02 22:24:06 +03:00
committed by GitHub
parent 356b33943b
commit ec88899e54
22 changed files with 748 additions and 1261 deletions
Vendored
BIN
View File
Binary file not shown.
+1
View File
@@ -1,2 +1,3 @@
/target
.DS_Store
Cargo.lock
Generated
-744
View File
@@ -1,744 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "color-eyre"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]]
name = "crossterm"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
dependencies = [
"winapi",
]
[[package]]
name = "dyn-clone"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
[[package]]
name = "eyre"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc225d8f637923fe585089fcf03e705c222131232d2c1fb622e84ecf725d0eb8"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "inquire"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6055ce38cac9b10ac819ed4a509d92ccbc60808152c19ff9121c98198964272"
dependencies = [
"bitflags",
"crossterm",
"dyn-clone",
"lazy_static",
"newline-converter",
"thiserror",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "interactive-clap"
version = "0.1.0"
dependencies = [
"clap",
"color-eyre",
"inquire",
"interactive-clap-derive",
"shell-words",
"strum",
"strum_macros",
]
[[package]]
name = "interactive-clap-derive"
version = "0.1.0"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "mio"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "newline-converter"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "object"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "os_str_bytes"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]]
name = "owo-colors"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustversion"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "shell-words"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
[[package]]
name = "signal-hook"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "tracing"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23"
dependencies = [
"lazy_static",
"valuable",
]
[[package]]
name = "tracing-error"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-segmentation"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+2 -2
View File
@@ -5,7 +5,7 @@ members = [
[package]
name = "interactive-clap"
version = "0.1.0"
version = "0.2.0"
authors = ["FroVolod <frol_off@meta.ua>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -15,7 +15,7 @@ description = "Interactive mode extension crate to Command Line Arguments Parser
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
interactive-clap-derive = { path = "interactive-clap-derive", version = "0.1.0" }
interactive-clap-derive = { path = "interactive-clap-derive", version = "0.2.0" }
strum = { version = "0.24", features = ["derive"] }
strum_macros = "0.24"
+71
View File
@@ -0,0 +1,71 @@
//This example shows how to parse data from the command line to an enum using the "interactive-clap" macro.
// 1) build an example: cargo build --example advanced_enum
// 2) go to the `examples` folder: cd target/debug/examples
// 3) run an example: ./advanced_enum (without parameters) => entered interactive mode
// ./advanced_enum network => mode: Ok(Network)
// ./advanced_enum offline => mode: Ok(Offline)
// To learn more about the parameters, use "help" flag: ./advanced_enum --help
use interactive_clap::{ResultFromCli, ToCliArgs};
use strum::{EnumDiscriminants, EnumIter, EnumMessage};
#[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)]
#[strum_discriminants(derive(EnumMessage, EnumIter))]
///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed.
///Do you want to derive some information required for transaction construction automatically querying it online?
pub enum Mode {
/// Prepare and, optionally, submit a new transaction with online mode
#[strum_discriminants(strum(message = "Yes, I keep it simple"))]
Network(Args),
/// Prepare and, optionally, submit a new transaction with offline mode
#[strum_discriminants(strum(
message = "No, I want to work in no-network (air-gapped) environment"
))]
Offline,
}
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
pub struct Args {
age: u64,
first_name: String,
second_name: String,
}
fn main() -> color_eyre::Result<()> {
let cli_mode = Mode::try_parse().ok();
let context = (); // default: input_context = ()
let mode = loop {
let mode = <Mode as interactive_clap::FromCli>::from_cli(cli_mode.clone(), context);
match mode {
ResultFromCli::Ok(cli_mode) => break cli_mode,
ResultFromCli::Cancel(Some(cli_mode)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_mode.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {}
ResultFromCli::Err(optional_cli_mode, err) => {
if let Some(cli_mode) = optional_cli_mode {
println!(
"Your console command: {}",
shell_words::join(&cli_mode.to_cli_args())
);
}
return Err(err);
}
}
};
println!("cli_mode: {:?}", mode);
println!(
"Your console command: {}",
shell_words::join(&mode.to_cli_args())
);
Ok(())
}
+90 -56
View File
@@ -7,15 +7,14 @@
// => args: Ok(Args { age: 30, first_name: "QWE", second_name: "QWERTY" })
// To learn more about the parameters, use "help" flag: ./advanced_struct --help
use inquire::{CustomType, Text};
use interactive_clap::{ResultFromCli, ToCliArgs};
#[derive(Debug, interactive_clap_derive::InteractiveClap)]
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(skip_default_from_cli)]
struct Args {
#[interactive_clap(long = "age-full-years")] // hgfashdgfajdfsadajsdfh
#[interactive_clap(skip_default_from_cli_arg)] // указать для чего этот атрибут нужен
#[interactive_clap(long = "age-full-years")]
#[interactive_clap(skip_default_input_arg)]
age: u64,
age: Option<u64>,
#[interactive_clap(long)]
///What is your first name?
first_name: String,
@@ -24,62 +23,97 @@ struct Args {
second_name: String,
}
impl interactive_clap::FromCli for Args {
type FromCliContext = ();
type FromCliError = color_eyre::eyre::Error;
fn from_cli(
optional_clap_variant: Option<<Self as interactive_clap::ToCli>::CliVariant>,
context: Self::FromCliContext,
) -> ResultFromCli<<Self as interactive_clap::ToCli>::CliVariant, Self::FromCliError>
where
Self: Sized + interactive_clap::ToCli,
{
let mut clap_variant = optional_clap_variant.unwrap_or_default();
if clap_variant.age.is_none() {
clap_variant.age = match Self::input_age(&context) {
Ok(optional_age) => optional_age,
Err(err) => return ResultFromCli::Err(Some(clap_variant), err),
};
}
let age = clap_variant.age;
if clap_variant.first_name.is_none() {
clap_variant.first_name = match Self::input_first_name(&context) {
Ok(Some(first_name)) => Some(first_name),
Ok(None) => return ResultFromCli::Cancel(Some(clap_variant)),
Err(err) => return ResultFromCli::Err(Some(clap_variant), err),
};
}
let first_name = clap_variant.first_name.clone().expect("Unexpected error");
if clap_variant.second_name.is_none() {
clap_variant.second_name = match Self::input_second_name(&context) {
Ok(Some(second_name)) => Some(second_name),
Ok(None) => return ResultFromCli::Cancel(Some(clap_variant)),
Err(err) => return ResultFromCli::Err(Some(clap_variant), err),
};
}
let second_name = clap_variant.second_name.clone().expect("Unexpected error");
ResultFromCli::Ok(clap_variant)
}
}
impl Args {
pub fn from_cli(
optional_clap_variant: Option<CliArgs>,
context: (),
) -> color_eyre::eyre::Result<Option<Self>> {
let age = Self::from_cli_age(
optional_clap_variant
.clone()
.and_then(|clap_variant| clap_variant.age),
&context,
)?;
let first_name = Self::from_cli_first_name(
optional_clap_variant
.clone()
.and_then(|clap_variant| clap_variant.first_name),
&context,
)?;
let second_name = Self::from_cli_second_name(
optional_clap_variant
.clone()
.and_then(|clap_variant| clap_variant.second_name),
&context,
)?;
let new_context_scope = InteractiveClapContextScopeForArgs {
age,
first_name,
second_name,
};
Ok(Some(Self {
age: new_context_scope.age,
first_name: new_context_scope.first_name,
second_name: new_context_scope.second_name,
}))
fn input_age(_context: &()) -> color_eyre::eyre::Result<Option<u64>> {
match inquire::CustomType::new("Input age full years".to_string().as_str()).prompt() {
Ok(value) => Ok(Some(value)),
Err(
inquire::error::InquireError::OperationCanceled
| inquire::error::InquireError::OperationInterrupted,
) => Ok(None),
Err(err) => Err(err.into()),
}
}
fn input_age(_context: &()) -> color_eyre::eyre::Result<u64> {
Ok(CustomType::new("How old are you?").prompt()?)
}
fn input_second_name(_context: &()) -> color_eyre::eyre::Result<String> {
Ok(Text::new("What is your last name?").prompt()?)
}
fn from_cli_age(
optional_cli_age: Option<u64>,
context: &(), // default: input_context = ()
) -> color_eyre::eyre::Result<u64> {
match optional_cli_age {
Some(age) => Ok(age),
None => Self::input_age(&context),
fn input_second_name(_context: &()) -> color_eyre::eyre::Result<Option<String>> {
match inquire::Text::new("Input second name".to_string().as_str()).prompt() {
Ok(value) => Ok(Some(value)),
Err(
inquire::error::InquireError::OperationCanceled
| inquire::error::InquireError::OperationInterrupted,
) => Ok(None),
Err(err) => Err(err.into()),
}
}
}
fn main() {
let cli_args = Args::parse();
let args = Args::from_cli(Some(cli_args), ());
println!("args: {:?}", args)
fn main() -> color_eyre::Result<()> {
let mut cli_args = Args::parse();
let context = (); // default: input_context = ()
loop {
let args = <Args as interactive_clap::FromCli>::from_cli(Some(cli_args), context);
match args {
ResultFromCli::Ok(cli_args) | ResultFromCli::Cancel(Some(cli_args)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_args.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {
cli_args = Default::default();
}
ResultFromCli::Err(cli_args, err) => {
if let Some(cli_args) = cli_args {
println!(
"Your console command: {}",
shell_words::join(&cli_args.to_cli_args())
);
}
return Err(err);
}
}
}
}
+29 -4
View File
@@ -7,9 +7,10 @@
// ./simple_enum offline => mode: Ok(Offline)
// To learn more about the parameters, use "help" flag: ./simple_enum --help
use interactive_clap::{ResultFromCli, ToCliArgs};
use strum::{EnumDiscriminants, EnumIter, EnumMessage};
#[derive(Debug, Clone, EnumDiscriminants, interactive_clap_derive::InteractiveClap)]
#[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)]
#[strum_discriminants(derive(EnumMessage, EnumIter))]
///To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed.
///Do you want to derive some information required for transaction construction automatically querying it online?
@@ -24,9 +25,33 @@ pub enum Mode {
Offline,
}
fn main() {
fn main() -> color_eyre::Result<()> {
let cli_mode = Mode::try_parse().ok();
let context = (); // default: input_context = ()
let mode = <Mode as interactive_clap::FromCli>::from_cli(cli_mode, context);
println!("mode: {:?}", mode)
loop {
let mode = <Mode as interactive_clap::FromCli>::from_cli(cli_mode.clone(), context);
match mode {
ResultFromCli::Ok(cli_mode) | ResultFromCli::Cancel(Some(cli_mode)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_mode.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {}
ResultFromCli::Err(optional_cli_mode, err) => {
if let Some(cli_mode) = optional_cli_mode {
println!(
"Your console command: {}",
shell_words::join(&cli_mode.to_cli_args())
);
}
return Err(err);
}
}
}
}
+34 -6
View File
@@ -6,16 +6,44 @@
// ./simple_struct 30 QWE QWERTY => args: Ok(Args { age: 30, first_name: "QWE", second_name: "QWERTY" })
// To learn more about the parameters, use "help" flag: ./simple_struct --help
#[derive(Debug, interactive_clap::InteractiveClap)]
struct Args {
use interactive_clap::{ResultFromCli, ToCliArgs};
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
pub struct Args {
age: u64,
first_name: String,
second_name: String,
}
fn main() {
let cli_args = Args::parse();
fn main() -> color_eyre::Result<()> {
let mut cli_args = Args::parse();
let context = (); // default: input_context = ()
let args = <Args as interactive_clap::FromCli>::from_cli(Some(cli_args), context);
println!("args: {:?}", args)
loop {
let args = <Args as interactive_clap::FromCli>::from_cli(Some(cli_args), context);
match args {
ResultFromCli::Ok(cli_args) | ResultFromCli::Cancel(Some(cli_args)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_args.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {
cli_args = Default::default();
}
ResultFromCli::Err(cli_args, err) => {
if let Some(cli_args) = cli_args {
println!(
"Your console command: {}",
shell_words::join(&cli_args.to_cli_args())
);
}
return Err(err);
}
}
}
}
+68 -14
View File
@@ -6,17 +6,18 @@
// ./struct_with_context account QWERTY => offline_args: Ok(OfflineArgs { account: Sender { sender_account_id: "QWERTY" } })
// To learn more about the parameters, use "help" flag: ./struct_with_context --help
use inquire::Text;
use interactive_clap::{ResultFromCli, ToCliArgs};
mod common;
mod simple_enum;
#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)]
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(input_context = ())]
#[interactive_clap(output_context = NetworkContext)]
#[interactive_clap(output_context = OfflineArgsContext)]
pub struct OfflineArgs {
#[interactive_clap(named_arg)]
///Specify a sender
account: Sender,
sender: Sender,
}
#[derive(Debug)]
@@ -27,7 +28,7 @@ pub struct OfflineArgsContext {
impl OfflineArgsContext {
fn from_previous_context(
_previous_context: (),
scope: &<OfflineArgs as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
_scope: &<OfflineArgs as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
Ok(Self {
some_context_field: 42,
@@ -43,29 +44,82 @@ impl From<OfflineArgsContext> for NetworkContext {
}
}
impl From<()> for NetworkContext {
fn from(_: ()) -> Self {
Self {
connection_config: None,
}
}
}
impl From<NetworkContext> for () {
fn from(_: NetworkContext) -> Self {
()
}
}
#[derive(Debug)]
pub struct NetworkContext {
pub connection_config: Option<common::ConnectionConfig>,
}
#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)]
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(context = NetworkContext)]
pub struct Sender {
#[interactive_clap(skip_default_input_arg)]
pub sender_account_id: String,
sender_account_id: String,
#[interactive_clap(subcommand)]
network: simple_enum::Mode,
}
impl Sender {
fn input_sender_account_id(context: &NetworkContext) -> color_eyre::eyre::Result<String> {
fn input_sender_account_id(
context: &NetworkContext,
) -> color_eyre::eyre::Result<Option<String>> {
println!("Let's use context: {:?}", context);
Ok(Text::new("What is the account ID?").prompt()?)
match inquire::CustomType::new("What is the account ID?").prompt() {
Ok(value) => Ok(Some(value)),
Err(
inquire::error::InquireError::OperationCanceled
| inquire::error::InquireError::OperationInterrupted,
) => Ok(None),
Err(err) => Err(err.into()),
}
}
}
fn main() {
let cli_offline_args = OfflineArgs::parse();
fn main() -> color_eyre::Result<()> {
let mut cli_offline_args = OfflineArgs::parse();
let context = (); // #[interactive_clap(input_context = ())]
let offline_args =
<OfflineArgs as interactive_clap::FromCli>::from_cli(Some(cli_offline_args), context);
println!("offline_args: {:?}", offline_args)
loop {
let offline_args = <OfflineArgs as interactive_clap::FromCli>::from_cli(
Some(cli_offline_args.clone()),
context,
);
match offline_args {
ResultFromCli::Ok(cli_offline_args) | ResultFromCli::Cancel(Some(cli_offline_args)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_offline_args.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {
cli_offline_args = Default::default();
}
ResultFromCli::Err(cli_offline_args, err) => {
if let Some(cli_offline_args) = cli_offline_args {
println!(
"Your console command: {}",
shell_words::join(&cli_offline_args.to_cli_args())
);
}
return Err(err);
}
}
}
}
+35 -7
View File
@@ -7,22 +7,50 @@
// ./struct_with_named_arg account QWERTY => account: Ok(Account { account: Sender { sender_account_id: "QWERTY" } })
// To learn more about the parameters, use "help" flag: ./struct_with_named_arg --help
#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)]
use interactive_clap::{ResultFromCli, ToCliArgs};
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
struct Account {
#[interactive_clap(named_arg)]
///Specify a sender
account: Sender,
}
#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)]
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
pub struct Sender {
///What is the account ID?
///What is the sender account ID?
pub sender_account_id: String,
}
fn main() {
let cli_account = Account::parse();
fn main() -> color_eyre::Result<()> {
let mut cli_account = Account::parse();
let context = (); // default: input_context = ()
let account = <Account as interactive_clap::FromCli>::from_cli(Some(cli_account), context);
println!("account: {:?}", account)
loop {
let account = <Account as interactive_clap::FromCli>::from_cli(Some(cli_account), context);
match account {
ResultFromCli::Ok(cli_account) | ResultFromCli::Cancel(Some(cli_account)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_account.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {
cli_account = Default::default();
}
ResultFromCli::Err(cli_account, err) => {
if let Some(cli_account) = cli_account {
println!(
"Your console command: {}",
shell_words::join(&cli_account.to_cli_args())
);
}
return Err(err);
}
}
}
}
+37 -6
View File
@@ -7,18 +7,49 @@
// ./struct_with_subcommand offline => operation_mode: Ok(OperationMode { mode: Offline })
// To learn more about the parameters, use "help" flag: ./struct_with_subcommand --help
use interactive_clap::{ResultFromCli, ToCliArgs};
mod simple_enum;
#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)]
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
pub struct OperationMode {
#[interactive_clap(subcommand)]
pub mode: simple_enum::Mode,
}
fn main() {
let cli_operation_mode = OperationMode::parse();
fn main() -> color_eyre::Result<()> {
let mut cli_operation_mode = OperationMode::parse();
let context = (); // default: input_context = ()
let operation_mode =
<OperationMode as interactive_clap::FromCli>::from_cli(Some(cli_operation_mode), context);
println!("operation_mode: {:?}", &operation_mode);
loop {
let operation_mode = <OperationMode as interactive_clap::FromCli>::from_cli(
Some(cli_operation_mode),
context,
);
match operation_mode {
ResultFromCli::Ok(cli_operation_mode)
| ResultFromCli::Cancel(Some(cli_operation_mode)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_operation_mode.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {
cli_operation_mode = Default::default();
}
ResultFromCli::Err(cli_operation_mode, err) => {
if let Some(cli_operation_mode) = cli_operation_mode {
println!(
"Your console command: {}",
shell_words::join(&cli_operation_mode.to_cli_args())
);
}
return Err(err);
}
}
}
}
+135 -32
View File
@@ -8,54 +8,105 @@
// To learn more about the parameters, use "help" flag: ./to_cli_args --help
use inquire::Select;
use interactive_clap::{ResultFromCli, SelectVariantOrBack, ToCliArgs};
use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator};
use interactive_clap::{SelectVariantOrBack, ToCliArgs};
mod common;
#[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)]
#[derive(Debug, Clone, interactive_clap::InteractiveClap)]
#[interactive_clap(context = common::ConnectionConfig)]
struct OnlineArgs {
/// What is the name of the network
#[interactive_clap(skip_default_input_arg)]
network_name: String,
#[interactive_clap(subcommand)]
submit: Submit,
}
#[derive(Debug, EnumDiscriminants, Clone, clap::Parser, interactive_clap_derive::ToCliArgs)]
impl OnlineArgs {
fn input_network_name(
_context: &common::ConnectionConfig,
) -> color_eyre::eyre::Result<Option<String>> {
match inquire::Text::new("Input network name").prompt() {
Ok(value) => Ok(Some(value)),
Err(
inquire::error::InquireError::OperationCanceled
| inquire::error::InquireError::OperationInterrupted,
) => Ok(None),
Err(err) => Err(err.into()),
}
}
}
#[derive(Debug, EnumDiscriminants, Clone, clap::Parser)]
#[strum_discriminants(derive(EnumMessage, EnumIter))]
pub enum Submit {
#[strum_discriminants(strum(message = "I want to send the transaction to the network"))]
Send,
Send(Args),
#[strum_discriminants(strum(
message = "I only want to print base64-encoded transaction for JSON RPC input and exit"
))]
Display,
}
#[derive(Debug, EnumDiscriminants, Clone, clap::Parser)]
pub enum CliSubmit {
Send(CliArgs),
Display,
}
impl From<Submit> for CliSubmit {
fn from(command: Submit) -> Self {
match command {
Submit::Send(args) => Self::Send(args.into()),
Submit::Display => Self::Display,
}
}
}
impl interactive_clap::FromCli for Submit {
type FromCliContext = common::ConnectionConfig;
type FromCliError = color_eyre::eyre::Error;
fn from_cli(
optional_clap_variant: Option<<Self as interactive_clap::ToCli>::CliVariant>,
_context: Self::FromCliContext,
) -> Result<Option<Self>, Self::FromCliError>
context: Self::FromCliContext,
) -> ResultFromCli<<Self as interactive_clap::ToCli>::CliVariant, Self::FromCliError>
where
Self: Sized + interactive_clap::ToCli,
{
let submit: Option<Submit> = optional_clap_variant.clone();
match submit {
Some(submit) => Ok(Some(submit)),
None => Ok(Some(Submit::Display)),
match optional_clap_variant {
Some(submit) => ResultFromCli::Ok(submit),
None => Self::choose_variant(context),
}
}
}
impl interactive_clap::ToCliArgs for CliSubmit {
fn to_cli_args(&self) -> std::collections::VecDeque<String> {
match self {
Self::Send(cli_args) => {
let mut args = cli_args.to_cli_args();
args.push_front("send".to_owned());
args
}
Self::Display => {
let mut args = std::collections::VecDeque::new();
args.push_front("display".to_owned());
args
}
}
}
}
impl Submit {
fn choose_variant(
_context: common::ConnectionConfig,
) -> color_eyre::eyre::Result<Option<Self>> {
let selected_variant = Select::new(
context: common::ConnectionConfig,
) -> ResultFromCli<
<Self as interactive_clap::ToCli>::CliVariant,
<Self as interactive_clap::FromCli>::FromCliError,
> {
match Select::new(
"How would you like to proceed",
SubmitDiscriminants::iter()
.map(SelectVariantOrBack::Variant)
@@ -63,17 +114,40 @@ impl Submit {
.collect(),
)
.prompt()
.unwrap();
match selected_variant {
SelectVariantOrBack::Variant(SubmitDiscriminants::Send) => Ok(Some(Submit::Send)),
SelectVariantOrBack::Variant(SubmitDiscriminants::Display) => Ok(Some(Submit::Display)),
SelectVariantOrBack::Back => Ok(None),
{
Ok(SelectVariantOrBack::Variant(variant)) => ResultFromCli::Ok(match variant {
SubmitDiscriminants::Send => {
let cli_args =
match <Args as interactive_clap::FromCli>::from_cli(None, context) {
ResultFromCli::Ok(cli_args) => cli_args,
ResultFromCli::Cancel(optional_cli_args) => {
return ResultFromCli::Cancel(Some(CliSubmit::Send(
optional_cli_args.unwrap_or_default(),
)));
}
ResultFromCli::Back => return ResultFromCli::Back,
ResultFromCli::Err(optional_cli_args, err) => {
return ResultFromCli::Err(
Some(CliSubmit::Send(optional_cli_args.unwrap_or_default())),
err,
);
}
};
CliSubmit::Send(cli_args)
}
SubmitDiscriminants::Display => CliSubmit::Display,
}),
Ok(SelectVariantOrBack::Back) => ResultFromCli::Back,
Err(
inquire::error::InquireError::OperationCanceled
| inquire::error::InquireError::OperationInterrupted,
) => ResultFromCli::Cancel(None),
Err(err) => ResultFromCli::Err(None, err.into()),
}
}
}
impl interactive_clap::ToCli for Submit {
type CliVariant = Submit;
type CliVariant = CliSubmit;
}
impl std::fmt::Display for SubmitDiscriminants {
@@ -85,23 +159,52 @@ impl std::fmt::Display for SubmitDiscriminants {
}
}
fn main() {
#[derive(Debug, Clone, interactive_clap::InteractiveClap, clap::Args)]
#[interactive_clap(context = common::ConnectionConfig)]
pub struct Args {
age: u64,
first_name: String,
second_name: String,
}
fn main() -> color_eyre::Result<()> {
let mut cli_online_args = OnlineArgs::parse();
let context = common::ConnectionConfig::Testnet; //#[interactive_clap(context = common::ConnectionConfig)]
let online_args = loop {
if let Some(args) = <OnlineArgs as interactive_clap::FromCli>::from_cli(
Some(cli_online_args.clone()),
let cli_args = loop {
match <OnlineArgs as interactive_clap::FromCli>::from_cli(
Some(cli_online_args),
context.clone(),
)
.unwrap()
{
break args;
) {
ResultFromCli::Ok(cli_args) => break cli_args,
ResultFromCli::Cancel(Some(cli_args)) => {
println!(
"Your console command: {}",
shell_words::join(&cli_args.to_cli_args())
);
return Ok(());
}
ResultFromCli::Cancel(None) => {
println!("Goodbye!");
return Ok(());
}
ResultFromCli::Back => {
cli_online_args = Default::default();
}
ResultFromCli::Err(cli_args, err) => {
if let Some(cli_args) = cli_args {
println!(
"Your console command: {}",
shell_words::join(&cli_args.to_cli_args())
);
}
return Err(err);
}
}
};
cli_online_args = online_args.into();
let completed_cli = cli_online_args.to_cli_args();
println!("cli_args: {:?}", cli_args);
println!(
"Your console command: {}",
shell_words::join(&completed_cli)
shell_words::join(&cli_args.to_cli_args())
);
Ok(())
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "interactive-clap-derive"
version = "0.1.0"
version = "0.2.0"
authors = ["FroVolod <frol_off@meta.ua>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -12,13 +12,10 @@ pub fn fn_choose_variant(
let name = &ast.ident;
let interactive_clap_attrs_context =
super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(ast);
let command_discriminants =
syn::Ident::new(&format!("{}Discriminants", name), Span::call_site());
let cli_command = syn::Ident::new(&format!("Cli{}", name), Span::call_site());
let command_discriminants = syn::Ident::new(&format!("{name}Discriminants"), Span::call_site());
let cli_command = syn::Ident::new(&format!("Cli{name}"), Span::call_site());
let variant_ident = &variants[0].ident;
let mut cli_variant = quote!();
let mut actions_push_back = quote! {.chain([SelectVariantOrBack::Back])};
let mut ast_attrs: Vec<&str> = std::vec::Vec::new();
if !ast.attrs.is_empty() {
@@ -26,13 +23,7 @@ pub fn fn_choose_variant(
if attr.path.is_ident("interactive_clap") {
for attr_token in attr.tokens.clone() {
if let proc_macro2::TokenTree::Group(group) = attr_token {
if group
.stream()
.to_string()
.contains("disable_strum_discriminants")
{
ast_attrs.push("disable_strum_discriminants");
} else if group.stream().to_string().contains("disable_back") {
if group.stream().to_string().contains("disable_back") {
ast_attrs.push("disable_back");
};
}
@@ -48,106 +39,91 @@ pub fn fn_choose_variant(
}
};
}
if ast_attrs.contains(&"disable_strum_discriminants") {
match &variants[0].fields {
syn::Fields::Unnamed(_) => {
cli_variant = quote! {
let cli_variant = #cli_command::#variant_ident(Default::default());
};
}
syn::Fields::Unit => {
cli_variant = quote! {
let cli_variant = #cli_command::#variant_ident;
};
}
_ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed"),
}
} else {
if ast_attrs.contains(&"disable_back") {
actions_push_back = quote!();
}
if ast_attrs.contains(&"strum_discriminants") {
let doc_attrs = ast
.attrs
.iter()
.filter(|attr| attr.path.is_ident("doc"))
.map(|attr| {
let mut literal_string = String::new();
for attr_token in attr.tokens.clone() {
if let proc_macro2::TokenTree::Literal(literal) = attr_token {
literal_string = literal.to_string();
}
if ast_attrs.contains(&"strum_discriminants") {
let doc_attrs = ast
.attrs
.iter()
.filter(|attr| attr.path.is_ident("doc"))
.map(|attr| {
let mut literal_string = String::new();
for attr_token in attr.tokens.clone() {
if let proc_macro2::TokenTree::Literal(literal) = attr_token {
literal_string = literal.to_string();
}
literal_string
})
.collect::<Vec<_>>();
let literal_vec = doc_attrs
.iter()
.map(|s| s.replace('\"', ""))
.collect::<Vec<_>>();
let literal = proc_macro2::Literal::string(literal_vec.join("\n ").as_str());
let enum_variants = variants.iter().map(|variant| {
let variant_ident = &variant.ident;
match &variant.fields {
syn::Fields::Unnamed(_) => {
quote! {
#command_discriminants::#variant_ident => #cli_command::#variant_ident(Default::default())
}
},
syn::Fields::Unit => {
quote! {
#command_discriminants::#variant_ident => #cli_command::#variant_ident
}
},
_ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed")
}
});
cli_variant = quote! {
use interactive_clap::SelectVariantOrBack;
use inquire::Select;
use strum::{EnumMessage, IntoEnumIterator};
fn prompt_variant<T>(prompt: &str) -> color_eyre::eyre::Result<Option<T>>
where
T: IntoEnumIterator + EnumMessage,
T: Copy + Clone,
{
let selected_variant = Select::new(
prompt,
T::iter()
.map(SelectVariantOrBack::Variant)
#actions_push_back
.collect(),
)
.prompt()?;
match selected_variant {
SelectVariantOrBack::Variant(variant) => Ok(Some(variant)),
SelectVariantOrBack::Back => Ok(None),
literal_string
})
.collect::<Vec<_>>();
let literal_vec = doc_attrs
.iter()
.map(|s| s.replace('\"', ""))
.collect::<Vec<_>>();
let literal = proc_macro2::Literal::string(literal_vec.join("\n ").as_str());
let enum_variants = variants.iter().map(|variant| {
let variant_ident = &variant.ident;
match &variant.fields {
syn::Fields::Unnamed(_) => {
quote! {
#command_discriminants::#variant_ident => {
#cli_command::#variant_ident(Default::default())
}
}
};
let variant = if let Some(variant) = prompt_variant(#literal.to_string().as_str())? {
variant
} else {
return Ok(None);
};
let cli_variant = match variant {
#( #enum_variants, )*
};
};
}
}
syn::Fields::Unit => {
quote! {
#command_discriminants::#variant_ident => #cli_command::#variant_ident
}
}
_ => abort_call_site!(
"Only option `Fields::Unnamed` or `Fields::Unit` is needed"
),
}
});
let actions_push_back = if ast_attrs.contains(&"disable_back") {
quote!()
} else {
quote! {.chain([SelectVariantOrBack::Back])}
};
cli_variant = quote! {
use interactive_clap::SelectVariantOrBack;
use inquire::Select;
use strum::{EnumMessage, IntoEnumIterator};
let selected_variant = Select::new(
#literal,
#command_discriminants::iter()
.map(SelectVariantOrBack::Variant)
#actions_push_back
.collect(),
)
.prompt();
match selected_variant {
Ok(SelectVariantOrBack::Variant(variant)) => {
let cli_args = match variant {
#( #enum_variants, )*
};
return interactive_clap::ResultFromCli::Ok(cli_args);
},
Ok(SelectVariantOrBack::Back) => return interactive_clap::ResultFromCli::Back,
Err(
inquire::error::InquireError::OperationCanceled
| inquire::error::InquireError::OperationInterrupted,
) => return interactive_clap::ResultFromCli::Cancel(None),
Err(err) => return interactive_clap::ResultFromCli::Err(None, err.into()),
}
};
}
};
let input_context = interactive_clap_attrs_context.get_input_context_dir();
let context = interactive_clap_attrs_context.get_input_context_dir();
quote! {
pub fn choose_variant(context: #input_context) -> color_eyre::eyre::Result<Option<Self>> {
loop {
#cli_variant
if let Some(variant) = <Self as interactive_clap::FromCli>::from_cli(Some(cli_variant), context.clone())? {
return Ok(Some(variant));
}
}
}
pub fn choose_variant(context: #context) -> interactive_clap::ResultFromCli<
<Self as interactive_clap::ToCli>::CliVariant,
<Self as interactive_clap::FromCli>::FromCliError,
> {
#cli_variant
}
}
}
@@ -1,21 +0,0 @@
extern crate proc_macro;
use syn;
pub fn is_field_without_skip_default_from_cli_arg(field: &syn::Field) -> bool {
if field.attrs.is_empty() {
return true;
}
!field
.attrs
.iter()
.filter(|attr| attr.path.is_ident("interactive_clap"))
.flat_map(|attr| attr.tokens.clone())
.any(|attr_token| match attr_token {
proc_macro2::TokenTree::Group(group) => group
.stream()
.to_string()
.contains("skip_default_from_cli_arg"),
_ => false, // abort_call_site!("Only option `TokenTree::Group` is needed")
})
}
@@ -10,7 +10,7 @@ pub fn from_cli_for_enum(
variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
) -> proc_macro2::TokenStream {
let name = &ast.ident;
let cli_name = syn::Ident::new(&format!("Cli{}", name), Span::call_site());
let cli_name = syn::Ident::new(&format!("Cli{name}"), Span::call_site());
let interactive_clap_attrs_context =
super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(ast);
@@ -20,42 +20,74 @@ pub fn from_cli_for_enum(
let from_cli_variants = variants.iter().map(|variant| {
let variant_ident = &variant.ident;
let output_context = match &interactive_clap_attrs_context.output_context_dir {
Some(output_context_dir) => {
quote! {
type Alias = <#name as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope;
let new_context_scope = Alias::#variant_ident;
let output_context = match #output_context_dir::from_previous_context(context.clone(), &new_context_scope) {
Ok(new_context) => new_context,
Err(err) => return interactive_clap::ResultFromCli::Err(Some(#cli_name::#variant_ident(inner_cli_args)), err),
};
}
}
None => {
quote! {
let output_context = context.clone();
}
}
};
match &variant.fields {
syn::Fields::Unnamed(fields) => {
let ty = &fields.unnamed[0].ty;
let context_name = syn::Ident::new(&format!("{}Context", &name), Span::call_site());
match &interactive_clap_attrs_context.output_context_dir {
Some(output_context_dir) => quote! {
Some(#cli_name::#variant_ident(inner_cli_args)) => {
type Alias = <#name as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope;
let new_context_scope = Alias::#variant_ident;
let new_context = #context_name::from_previous_context(context.clone(), &new_context_scope)?;
let output_context = #output_context_dir::from(new_context);
let optional_inner_args = <#ty as interactive_clap::FromCli>::from_cli(Some(inner_cli_args), output_context)?;
if let Some(inner_args) = optional_inner_args {
Ok(Some(Self::#variant_ident(inner_args,)))
} else {
Self::choose_variant(context.clone())
quote! {
Some(#cli_name::#variant_ident(inner_cli_args)) => {
#output_context
let cli_inner_args = <#ty as interactive_clap::FromCli>::from_cli(Some(inner_cli_args), output_context.into());
match cli_inner_args {
interactive_clap::ResultFromCli::Ok(cli_args) => {
interactive_clap::ResultFromCli::Ok(#cli_name::#variant_ident(cli_args))
}
}
},
None => quote! {
Some(#cli_name::#variant_ident(inner_cli_args)) => {
let optional_inner_args = <#ty as interactive_clap::FromCli>::from_cli(Some(inner_cli_args), context.clone().into())?;
if let Some(inner_args) = optional_inner_args {
Ok(Some(Self::#variant_ident(inner_args,)))
} else {
Self::choose_variant(context.clone())
interactive_clap::ResultFromCli::Back => {
optional_clap_variant = None;
continue;
},
interactive_clap::ResultFromCli::Cancel(Some(cli_args)) => {
interactive_clap::ResultFromCli::Cancel(Some(#cli_name::#variant_ident(cli_args)))
}
interactive_clap::ResultFromCli::Cancel(None) => {
interactive_clap::ResultFromCli::Cancel(None)
}
interactive_clap::ResultFromCli::Err(Some(cli_args), err) => {
interactive_clap::ResultFromCli::Err(Some(#cli_name::#variant_ident(cli_args)), err)
}
interactive_clap::ResultFromCli::Err(None, err) => {
interactive_clap::ResultFromCli::Err(None, err)
}
}
}
}
},
syn::Fields::Unit => {
quote! {
Some(#cli_name::#variant_ident) => Ok(Some(Self::#variant_ident)),
match &interactive_clap_attrs_context.output_context_dir {
Some(output_context_dir) => quote! {
Some(#cli_name::#variant_ident) => {
type Alias = <#name as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope;
let new_context_scope = Alias::#variant_ident;
let output_context = match #output_context_dir::from_previous_context(context.clone(), &new_context_scope) {
Ok(new_context) => new_context,
Err(err) => return interactive_clap::ResultFromCli::Err(Some(#cli_name::#variant_ident), err),
};
interactive_clap::ResultFromCli::Ok(#cli_name::#variant_ident)
}
},
None => quote! {
Some(#cli_name::#variant_ident) => {
interactive_clap::ResultFromCli::Ok(#cli_name::#variant_ident)
},
}
}
},
_ => abort_call_site!("Only option `Fields::Unnamed` or `Fields::Unit` is needed")
@@ -71,12 +103,20 @@ pub fn from_cli_for_enum(
type FromCliContext = #input_context_dir;
type FromCliError = color_eyre::eyre::Error;
fn from_cli(
optional_clap_variant: Option<<Self as interactive_clap::ToCli>::CliVariant>,
mut optional_clap_variant: Option<<Self as interactive_clap::ToCli>::CliVariant>,
context: Self::FromCliContext,
) -> Result<Option<Self>, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
match optional_clap_variant {
#(#from_cli_variants)*
None => Self::choose_variant(context.clone()),
) -> interactive_clap::ResultFromCli<<Self as interactive_clap::ToCli>::CliVariant, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
loop {
return match optional_clap_variant {
#(#from_cli_variants)*
None => match Self::choose_variant(context.clone()) {
interactive_clap::ResultFromCli::Ok(cli_args) => {
optional_clap_variant = Some(cli_args);
continue;
},
result => return result,
},
}
}
}
}
@@ -33,13 +33,7 @@ pub fn from_cli_for_struct(
let field_value_named_arg = if let Some(token_stream) = fields
.iter()
.map(|field| {
field_value_named_arg(
name,
field,
&interactive_clap_attrs_context.output_context_dir,
)
})
.map(|field| field_value_named_arg(name, field))
.find(|token_stream| !token_stream.is_empty())
{
token_stream
@@ -49,13 +43,7 @@ pub fn from_cli_for_struct(
let field_value_subcommand = if let Some(token_stream) = fields
.iter()
.map(|field| {
field_value_subcommand(
name,
field,
&interactive_clap_attrs_context.output_context_dir,
)
})
.map(field_value_subcommand)
.find(|token_stream| !token_stream.is_empty())
{
token_stream
@@ -63,11 +51,9 @@ pub fn from_cli_for_struct(
quote!()
};
let struct_fields = fields
.iter()
.map(|field| struct_field(field, &fields_without_subcommand));
let input_context_dir = interactive_clap_attrs_context.get_input_context_dir();
let input_context_dir = interactive_clap_attrs_context
.clone()
.get_input_context_dir();
let interactive_clap_context_scope_for_struct = syn::Ident::new(
&format!("InteractiveClapContextScopeFor{}", &name),
@@ -77,6 +63,19 @@ pub fn from_cli_for_struct(
let new_context_scope = #interactive_clap_context_scope_for_struct { #(#fields_without_subcommand,)* };
};
let output_context = match &interactive_clap_attrs_context.output_context_dir {
Some(output_context_dir) => {
quote! {
let output_context = match #output_context_dir::from_previous_context(context.clone(), &new_context_scope) {
Ok(new_context) => new_context,
Err(err) => return interactive_clap::ResultFromCli::Err(Some(clap_variant), err),
};
let context = output_context;
}
}
None => quote!(),
};
quote! {
impl interactive_clap::FromCli for #name {
type FromCliContext = #input_context_dir;
@@ -84,12 +83,14 @@ pub fn from_cli_for_struct(
fn from_cli(
optional_clap_variant: Option<<Self as interactive_clap::ToCli>::CliVariant>,
context: Self::FromCliContext,
) -> Result<Option<Self>, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
) -> interactive_clap::ResultFromCli<<Self as interactive_clap::ToCli>::CliVariant, Self::FromCliError> where Self: Sized + interactive_clap::ToCli {
let mut clap_variant = optional_clap_variant.unwrap_or_default();
#(#fields_value)*
#new_context_scope
#output_context
#field_value_named_arg
#field_value_subcommand;
Ok(Some(Self{ #(#struct_fields,)* }))
interactive_clap::ResultFromCli::Ok(clap_variant)
}
}
}
@@ -97,26 +98,25 @@ pub fn from_cli_for_struct(
fn fields_value(field: &syn::Field) -> proc_macro2::TokenStream {
let ident_field = &field.clone().ident.expect("this field does not exist");
let fn_from_cli_arg = syn::Ident::new(&format!("from_cli_{}", &ident_field), Span::call_site());
let fn_input_arg = syn::Ident::new(&format!("input_{}", &ident_field), Span::call_site());
if super::fields_without_subcommand::is_field_without_subcommand(field) {
quote! {
let #ident_field = Self::#fn_from_cli_arg(
optional_clap_variant
.clone()
.and_then(|clap_variant| clap_variant.#ident_field),
&context,
)?;
if clap_variant.#ident_field.is_none() {
clap_variant
.#ident_field = match Self::#fn_input_arg(&context) {
Ok(Some(#ident_field)) => Some(#ident_field),
Ok(None) => return interactive_clap::ResultFromCli::Cancel(Some(clap_variant)),
Err(err) => return interactive_clap::ResultFromCli::Err(Some(clap_variant), err),
};
};
let #ident_field = clap_variant.#ident_field.clone().expect("Unexpected error");
}
} else {
quote!()
}
}
fn field_value_named_arg(
name: &syn::Ident,
field: &syn::Field,
output_context_dir: &Option<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
fn field_value_named_arg(name: &syn::Ident, field: &syn::Field) -> proc_macro2::TokenStream {
let ident_field = &field.clone().ident.expect("this field does not exist");
let ty = &field.ty;
if field.attrs.is_empty() {
@@ -144,38 +144,26 @@ fn field_value_named_arg(
let enum_for_clap_named_arg = syn::Ident::new(&format!("ClapNamedArg{}For{}", &type_string, &name), Span::call_site());
let variant_name_string = crate::helpers::snake_case_to_camel_case::snake_case_to_camel_case(ident_field.to_string());
let variant_name = &syn::Ident::new(&variant_name_string, Span::call_site());
match output_context_dir {
Some(output_context_dir) => {
let context_for_struct = syn::Ident::new(&format!("{}Context", &name), Span::call_site());
quote! {
let new_context = #context_for_struct::from_previous_context(context, &new_context_scope)?;
let output_context = #output_context_dir::from(new_context);
let #ident_field = <#ty as interactive_clap::FromCli>::from_cli(
optional_clap_variant.and_then(|clap_variant| match clap_variant.#ident_field {
Some(#enum_for_clap_named_arg::#variant_name(cli_arg)) => Some(cli_arg),
None => None,
}),
output_context,
)?;
let #ident_field = if let Some(value) = #ident_field {
value
} else {
return Ok(None);
}
quote! {
let optional_field = match clap_variant.#ident_field.take() {
Some(#enum_for_clap_named_arg::#variant_name(cli_arg)) => Some(cli_arg),
None => None,
};
match <#ty as interactive_clap::FromCli>::from_cli(
optional_field,
context.into(),
) {
interactive_clap::ResultFromCli::Ok(cli_field) => {
clap_variant.#ident_field = Some(#enum_for_clap_named_arg::#variant_name(cli_field));
}
},
None => quote! {
let #ident_field = <#ty as interactive_clap::FromCli>::from_cli(
optional_clap_variant.and_then(|clap_variant| match clap_variant.#ident_field {
Some(#enum_for_clap_named_arg::#variant_name(cli_sender)) => Some(cli_sender),
None => None,
}),
context.into(),
)?;
let #ident_field = if let Some(value) = #ident_field {
value
} else {
return Ok(None);
interactive_clap::ResultFromCli::Cancel(optional_cli_field) => {
clap_variant.#ident_field = optional_cli_field.map(#enum_for_clap_named_arg::#variant_name);
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
}
interactive_clap::ResultFromCli::Back => return interactive_clap::ResultFromCli::Back,
interactive_clap::ResultFromCli::Err(optional_cli_field, err) => {
clap_variant.#ident_field = optional_cli_field.map(#enum_for_clap_named_arg::#variant_name);
return interactive_clap::ResultFromCli::Err(Some(clap_variant), err);
}
}
}
@@ -187,11 +175,7 @@ fn field_value_named_arg(
}
}
fn field_value_subcommand(
name: &syn::Ident,
field: &syn::Field,
output_context_dir: &Option<proc_macro2::TokenStream>,
) -> proc_macro2::TokenStream {
fn field_value_subcommand(field: &syn::Field) -> proc_macro2::TokenStream {
let ident_field = &field.clone().ident.expect("this field does not exist");
let ty = &field.ty;
if field.attrs.is_empty() {
@@ -207,32 +191,23 @@ fn field_value_subcommand(
}
})
.map(|_| {
match output_context_dir {
Some(output_context_dir) => {
let context_for_struct = syn::Ident::new(&format!("{}Context", &name), Span::call_site());
quote! {
let new_context = #context_for_struct::from_previous_context(context, &new_context_scope)?;
let output_context = #output_context_dir::from(new_context);
let #ident_field = match optional_clap_variant.and_then(|clap_variant| clap_variant.#ident_field) {
Some(cli_arg) => <#ty as interactive_clap::FromCli>::from_cli(Some(cli_arg), output_context)?,
None => #ty::choose_variant(output_context)?,
};
let #ident_field = if let Some(value) = #ident_field {
value
} else {
return Ok(None);
}
quote! {
match <#ty as interactive_clap::FromCli>::from_cli(clap_variant.#ident_field.take(), context.into()) {
interactive_clap::ResultFromCli::Ok(cli_field) => {
clap_variant.#ident_field = Some(cli_field);
}
},
None => quote! {
let #ident_field = match optional_clap_variant.and_then(|clap_variant| clap_variant.#ident_field) {
Some(cli_arg) => <#ty as interactive_clap::FromCli>::from_cli(Some(cli_arg), context)?,
None => #ty::choose_variant(context.into())?,
};
let #ident_field = if let Some(value) = #ident_field {
value
} else {
return Ok(None);
interactive_clap::ResultFromCli::Cancel(option_cli_field) => {
clap_variant.#ident_field = option_cli_field;
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
}
interactive_clap::ResultFromCli::Cancel(option_cli_field) => {
clap_variant.#ident_field = option_cli_field;
return interactive_clap::ResultFromCli::Cancel(Some(clap_variant));
}
interactive_clap::ResultFromCli::Back => return interactive_clap::ResultFromCli::Back,
interactive_clap::ResultFromCli::Err(option_cli_field, err) => {
clap_variant.#ident_field = option_cli_field;
return interactive_clap::ResultFromCli::Err(Some(clap_variant), err);
}
}
}
@@ -243,23 +218,3 @@ fn field_value_subcommand(
}
}
}
fn struct_field(
field: &syn::Field,
fields_without_subcommand: &[proc_macro2::TokenStream],
) -> proc_macro2::TokenStream {
let ident_field = &field.clone().ident.expect("this field does not exist");
if fields_without_subcommand
.iter()
.map(|token_stream| token_stream.to_string())
.any(|x| *ident_field == x)
{
quote! {
#ident_field: new_context_scope.#ident_field
}
} else {
quote! {
#ident_field
}
}
}
@@ -1,104 +0,0 @@
extern crate proc_macro;
use proc_macro2::Span;
use quote::quote;
use syn;
pub fn from_cli_arg(ast: &syn::DeriveInput, fields: &syn::Fields) -> Vec<proc_macro2::TokenStream> {
let interactive_clap_attrs_context =
super::interactive_clap_attrs_context::InteractiveClapAttrsContext::new(ast);
let fields_without_subcommand = fields
.iter()
.filter(|field| super::fields_without_subcommand::is_field_without_subcommand(field))
.map(|field| {
let ident_field = &field.clone().ident.expect("this field does not exist");
quote! {#ident_field}
})
.collect::<Vec<_>>();
let fields_without_skip_default_from_cli_arg = fields
.iter()
.filter(|field| {
super::fields_without_skip_default_from_cli_arg::is_field_without_skip_default_from_cli_arg(
field,
)
})
.map(|field| {
let ident_field = &field.clone().ident.expect("this field does not exist");
quote! {#ident_field}
})
.collect::<Vec<_>>();
let get_arg_for_fields = fields
.iter()
.map(|field| {
let ident_field = &field.clone().ident.expect("this field does not exist");
let ty = &field.ty;
let fields_without_subcommand_to_string = fields_without_subcommand
.iter()
.map(|token_stream| token_stream.to_string())
.collect::<Vec<_>>();
let fields_without_skip_default_from_cli_arg_to_string =
fields_without_skip_default_from_cli_arg
.iter()
.map(|token_stream| token_stream.to_string())
.collect::<Vec<_>>();
if fields_without_subcommand_to_string.contains(&ident_field.to_string())
& fields_without_skip_default_from_cli_arg_to_string
.iter()
.map(|token_stream| token_stream.to_string())
.any(|x| *ident_field == x)
{
let fn_from_cli_arg =
syn::Ident::new(&format!("from_cli_{}", &ident_field), Span::call_site());
let optional_cli_field_name =
syn::Ident::new(&format!("optional_cli_{}", ident_field), Span::call_site());
let input_context_dir = interactive_clap_attrs_context
.clone()
.get_input_context_dir();
let cli_field_type = super::cli_field_type::cli_field_type(ty);
let fn_input_arg =
syn::Ident::new(&format!("input_{}", &ident_field), Span::call_site());
let type_string = match &ty {
syn::Type::Path(type_path) => match type_path.path.segments.last() {
Some(path_segment) => path_segment.ident.to_string(),
_ => String::new(),
},
_ => String::new(),
};
if let "Option" = type_string.as_str() {
quote! {
fn #fn_from_cli_arg(
#optional_cli_field_name: #cli_field_type,
context: &#input_context_dir,
) -> color_eyre::eyre::Result<#ty> {
match #optional_cli_field_name {
Some(#ident_field) => Ok(Some(#ident_field)),
None => Self::#fn_input_arg(&context),
}
}
}
} else {
quote! {
fn #fn_from_cli_arg(
#optional_cli_field_name: #cli_field_type,
context: &#input_context_dir,
) -> color_eyre::eyre::Result<#ty> {
match #optional_cli_field_name {
Some(#ident_field) => Ok(#ident_field),
None => Self::#fn_input_arg(&context),
}
}
}
}
} else {
quote!()
}
})
.filter(|token_stream| !token_stream.is_empty())
.collect::<Vec<proc_macro2::TokenStream>>();
get_arg_for_fields
}
@@ -34,8 +34,12 @@ pub fn vec_fn_input_arg(
return quote! {
fn #fn_input_arg(
_context: &#input_context_dir,
) -> color_eyre::eyre::Result<#ty> {
Ok(inquire::CustomType::new(#promt).prompt()?)
) -> color_eyre::eyre::Result<Option<#ty>> {
match inquire::CustomType::new(#promt).prompt() {
Ok(value) => Ok(Some(value)),
Err(inquire::error::InquireError::OperationCanceled | inquire::error::InquireError::OperationInterrupted) => Ok(None),
Err(err) => Err(err.into()),
}
}
};
}
@@ -63,8 +67,12 @@ pub fn vec_fn_input_arg(
quote! {
fn #fn_input_arg(
_context: &#input_context_dir,
) -> color_eyre::eyre::Result<#ty> {
Ok(inquire::CustomType::new(#literal.to_string().as_str()).prompt()?)
) -> color_eyre::eyre::Result<Option<#ty>> {
match inquire::CustomType::new(#literal).prompt() {
Ok(value) => Ok(Some(value)),
Err(inquire::error::InquireError::OperationCanceled | inquire::error::InquireError::OperationInterrupted) => Ok(None),
Err(err) => Err(err.into()),
}
}
}
})
@@ -1,10 +1,8 @@
pub mod choose_variant;
pub mod cli_field_type;
pub mod fields_without_skip_default_from_cli_arg;
pub mod fields_without_skip_default_input_arg;
pub mod fields_without_subcommand;
pub mod from_cli_for_enum;
pub mod from_cli_for_struct;
pub mod get_arg_from_cli_for_struct;
pub mod input_arg;
pub mod interactive_clap_attrs_context;
@@ -112,8 +112,6 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream {
let fn_from_cli_for_struct =
self::methods::from_cli_for_struct::from_cli_for_struct(ast, &fields);
let fn_get_arg = self::methods::get_arg_from_cli_for_struct::from_cli_arg(ast, &fields);
let vec_fn_input_arg = self::methods::input_arg::vec_fn_input_arg(ast, &fields);
let context_scope_fields = fields
@@ -175,7 +173,7 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream {
};
let gen = quote! {
#[derive(Debug, Default, Clone, clap::Parser, interactive_clap_derive::ToCliArgs)]
#[derive(Debug, Default, Clone, clap::Parser, interactive_clap::ToCliArgs)]
#[clap(author, version, about, long_about = None)]
pub struct #cli_name {
#( #cli_fields, )*
@@ -190,7 +188,6 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream {
#fn_from_cli_for_struct
impl #name {
#(#fn_get_arg)*
#(#vec_fn_input_arg)*
fn try_parse() -> Result<#cli_name, clap::Error> {
@@ -305,7 +302,7 @@ pub fn impl_interactive_clap(ast: &syn::DeriveInput) -> TokenStream {
self::methods::from_cli_for_enum::from_cli_for_enum(ast, variants);
let gen = quote! {
#[derive(Debug, Clone, clap::Parser, interactive_clap_derive::ToCliArgs)]
#[derive(Debug, Clone, clap::Parser, interactive_clap::ToCliArgs)]
pub enum #cli_name {
#( #enum_variants, )*
}
+8 -1
View File
@@ -30,13 +30,20 @@ pub trait ToCliArgs {
fn to_cli_args(&self) -> std::collections::VecDeque<String>;
}
pub enum ResultFromCli<T, E> {
Ok(T),
Cancel(Option<T>),
Back,
Err(Option<T>, E),
}
pub trait FromCli {
type FromCliContext;
type FromCliError;
fn from_cli(
optional_clap_variant: Option<<Self as ToCli>::CliVariant>,
context: Self::FromCliContext,
) -> Result<Option<Self>, Self::FromCliError>
) -> ResultFromCli<<Self as ToCli>::CliVariant, Self::FromCliError>
where
Self: Sized + ToCli;
}