feat(sdk/rust): add tinyplace CLI binary (#56)#189
Conversation
Port a first slice of the TypeScript SDK CLI to a Rust `[[bin]]` built on the existing `tinyplace` crate. Read-only command surface for this cut: whoami, version, debug/doctor, lookup, groups, and pricing (assets/pairs/networks/ quote/gas). JSON output by default, `--format md` for Markdown; config from `~/.tinyplace/config.json` + `$TINYPLACE_*` env overrides. - clap is optional behind a `cli` feature with a `required-features` bin, so library consumers don't pull it in; the Rust CI job builds/lints/tests with `--features cli`. - Add `Serialize` to a few response types that were `Deserialize`-only (PriceAsset(s), TradePairs, SupportedNetworks, GroupListResponse) so the CLI can render them — consistent with siblings (GroupMetadata, PriceQuote). - Tests: pure unit tests for config/env resolution and output formatting, plus wiremock-backed command-dispatch tests. Encrypted messaging and on-chain flows stay TypeScript-only (per PORTING.md). Refs tinyhumansai#56
|
@martian56 is attempting to deploy a commit to the Vezures Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughAdds an optional ChangesRust SDK CLI Binary
Sequence Diagram(s)sequenceDiagram
actor User
participant main as main.rs
participant commands as commands::run
participant config as config::resolve
participant context as context::build
participant sdk as TinyPlaceClient
participant output as output::render
User->>main: tinyplace [flags] <subcommand>
main->>commands: run(cli)
alt version / whoami / debug
commands-->>commands: handle locally
else API command (lookup / groups / pricing)
commands->>config: resolve(cli_endpoint)
config-->>commands: Resolved { endpoint, seed, config_path }
commands->>context: build(&resolved)
context-->>commands: (TinyPlaceClient, Option<Signer>)
commands->>sdk: API call (lookup / groups / pricing/*)
sdk-->>commands: deserialized response
end
commands->>output: render(value, format)
output-->>commands: formatted String
commands-->>main: Ok(String) or Err(CliError)
alt success
main->>User: stdout + ExitCode::SUCCESS
else error
main->>User: stderr JSON + ExitCode::FAILURE
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
sdk/rust/src/cli/args.rs (1)
21-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDisallow conflicting shorthand format flags.
--jsonand--mdcan currently be set together;output_format()silently prefers markdown. Add clap conflicts so invalid combinations fail fast.Suggested change
/// Shorthand for `--format json`. - #[arg(long, global = true)] + #[arg(long, global = true, conflicts_with_all = ["md", "format"])] pub json: bool, /// Shorthand for `--format md`. - #[arg(long, global = true)] + #[arg(long, global = true, conflicts_with_all = ["json", "format"])] pub md: bool,Also applies to: 37-46
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/rust/src/cli/args.rs` around lines 21 - 27, The `json` and `md` fields in the args struct allow conflicting format flags to be set simultaneously, which should be prevented. Add a `conflicts_with` attribute to the clap `#[arg(...)]` macro on both the `json` field and the `md` field to declare that they conflict with each other. This will make clap reject the command if both flags are provided together, rather than silently preferring one over the other in the `output_format()` method. Apply the same conflict constraint to any other format-related flags mentioned around lines 37-46.sdk/rust/src/cli/commands.rs (1)
195-197: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWiremock matchers are too broad in two tests.
Line 195 and Line 215 match any
GET, so path regressions can still pass. Add explicit path matchers to validate dispatch targets.🔧 Proposed fix
#[tokio::test] async fn pricing_networks_renders_array() { let server = MockServer::start().await; Mock::given(method("GET")) + .and(path("/pricing/networks")) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "networks": [] }))) .mount(&server) .await; @@ #[tokio::test] async fn sdk_http_error_maps_to_sdk_error_code() { let server = MockServer::start().await; Mock::given(method("GET")) + .and(path("/registry/names/ghost")) .respond_with(ResponseTemplate::new(404).set_body_json(json!({ "error": "nope" }))) .mount(&server) .await;Also applies to: 215-217
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sdk/rust/src/cli/commands.rs` around lines 195 - 197, The Mock setup using only Mock::given(method("GET")) without path validation allows tests to pass even when calling incorrect endpoints. For both mock definitions (around line 195 and line 215), add explicit path matchers alongside the method matcher. Use Mock::given with both method("GET") and an appropriate path matcher (such as path matching the specific endpoint being tested) to ensure the mocks validate the correct dispatch targets are being called, preventing path regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 181-189: The CI job currently only validates the `cli` feature
path in the Rust SDK, which leaves the default feature path untested and allows
non-CLI regressions to slip through. Add separate steps to run cargo clippy,
cargo build, and cargo test without the `--features cli` flag (in addition to
the existing steps) to ensure the default feature path is also covered. These
additional steps should run in the same `sdk/rust` working directory and
validate the SDK without any feature flags.
In `@sdk/rust/src/cli/config.rs`:
- Around line 93-97: The seed variable assignment does not filter out empty or
whitespace values from the TINYPLACE_SECRET_KEY environment variable, causing an
empty env var to block the fallback to file.secret_key. Modify the chain after
std::env::var("TINYPLACE_SECRET_KEY").ok() to check if the string is empty or
contains only whitespace, and only use the env var value if it's valid; if it's
empty or whitespace, allow the .or(file.secret_key) fallback to be used instead.
In `@sdk/rust/src/cli/output.rs`:
- Around line 31-35: The redaction logic in the condition starting with
is_sensitive_key check only fully redacts sensitive keys when the value is a
string, but when a sensitive key maps to an object or array, nested secrets
within those structures are not properly redacted and can leak. Fix this by
ensuring that whenever is_sensitive_key returns true for a key, the entire value
is redacted regardless of its type: if it is a string redact to "[redacted]",
and if it is an object or array, recursively redact all nested values within the
structure before returning it.
---
Nitpick comments:
In `@sdk/rust/src/cli/args.rs`:
- Around line 21-27: The `json` and `md` fields in the args struct allow
conflicting format flags to be set simultaneously, which should be prevented.
Add a `conflicts_with` attribute to the clap `#[arg(...)]` macro on both the
`json` field and the `md` field to declare that they conflict with each other.
This will make clap reject the command if both flags are provided together,
rather than silently preferring one over the other in the `output_format()`
method. Apply the same conflict constraint to any other format-related flags
mentioned around lines 37-46.
In `@sdk/rust/src/cli/commands.rs`:
- Around line 195-197: The Mock setup using only Mock::given(method("GET"))
without path validation allows tests to pass even when calling incorrect
endpoints. For both mock definitions (around line 195 and line 215), add
explicit path matchers alongside the method matcher. Use Mock::given with both
method("GET") and an appropriate path matcher (such as path matching the
specific endpoint being tested) to ensure the mocks validate the correct
dispatch targets are being called, preventing path regressions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b1adc0f0-b894-410a-bd29-2f23b3d840bb
⛔ Files ignored due to path filters (1)
sdk/rust/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
.github/workflows/ci.ymlsdk/rust/Cargo.tomlsdk/rust/README.mdsdk/rust/src/api/groups.rssdk/rust/src/api/pricing.rssdk/rust/src/cli/args.rssdk/rust/src/cli/commands.rssdk/rust/src/cli/config.rssdk/rust/src/cli/context.rssdk/rust/src/cli/main.rssdk/rust/src/cli/output.rs
…dow the config key
|
thanks for the contribution @martian56 ! merging shortly. |
Summary
Adds a
tinyplaceCLI binary to the Rust SDK (a first slice of #56), built on theexisting
tinyplacecrate —clapfor args, the SDK for everything else.This cut ships a read-only command surface:
whoamiversiondebug/doctorlookup <handle>registry.get)groups [--q --tag --limit]groups.list)pricing {assets,pairs,networks,quote,gas}Output is JSON by default (
--format mdfor Markdown), errors are JSON on stderrwith a machine-readable
code+ exit 1, and config resolves from~/.tinyplace/config.jsonwith$TINYPLACE_ENDPOINT/$TINYPLACE_API_URL,$TINYPLACE_SECRET_KEY, and$TINYPLACE_CONFIGoverrides — matching theTypeScript CLI's contract.
Design notes
clapis optional, behind aclifeature with arequired-featuresbin, solibrary consumers don't pull it in. The Rust CI job now runs
--features clisothe binary is still fully built / linted / tested.
Serializeto four response types that wereDeserialize-only (PriceAsset/PriceAssets,TradePairs,SupportedNetworks,GroupListResponse) so the CLI can render them. This is consistent with theirsiblings (
GroupMetadata,PriceQuote,GasEstimatealready derive both).sdk/rust/PORTING.md, the Rust SDK omits Signal E2Eencryption and on-chain Solana execution, so the TS CLI's encrypted-messaging and
on-chain commands are intentionally not in this slice. Mutating/auth'd and
workflow commands can follow incrementally.
Testing
secret redaction).
wiremock-backed command-dispatch tests (success, list response, and404 → sdk_errormapping) — the dispatch is split intorun_api_command(client, …)so it runs against a mock with no live network.
cargo fmt --check,cargo clippy --all-targets --features cli -- -D warnings,cargo build --all-targets --features cli,cargo test --features cli(all green), plus a manual smoke test of the binary against thelive API.
Refs #56
Summary by CodeRabbit
tinyplace) to the Rust SDK with commands for identity, lookup, groups, and pricing.~/.tinyplace/config.jsonand environment variables (with CLI overrides).