Fix json support for client launch/connect and add json format support for client select (#118)

pull/137/head v0.17.1
Chip Senkbeil 2 years ago committed by GitHub
parent dde3cb275f
commit 4223c4e03f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.17.1] - 2022-08-16
### Added
- New `format` option available for `client select`
- Choices are provided via `{"type": "select", "choices": ["...", ...], "current": 0}`
- Selection is specified via `{"type": "selected", "choice": 0}`
### Fixed
- `distant client launch` using `--format json` now properly prints out id in
JSON format (`{"type": "launched", "id": "..."}`)
- `distant client connect` using `--format json` now properly prints out id in
JSON format (`{"type": "connected", "id": "..."}`)
## [0.17.0] - 2022-08-09
### Added

@ -3,7 +3,7 @@ name = "distant"
description = "Operate on a remote computer through file and process manipulation"
categories = ["command-line-utilities"]
keywords = ["cli"]
version = "0.17.0"
version = "0.17.1"
authors = ["Chip Senkbeil <chip@senkbeil.org>"]
edition = "2021"
homepage = "https://github.com/chipsenkbeil/distant"
@ -32,7 +32,7 @@ clap_complete = "3.2.3"
config = { version = "0.13.2", default-features = false, features = ["toml"] }
derive_more = { version = "0.99.17", default-features = false, features = ["display", "from", "error", "is_variant"] }
dialoguer = { version = "0.10.2", default-features = false }
distant-core = { version = "=0.17.0", path = "distant-core", features = ["clap", "schemars"] }
distant-core = { version = "=0.17.1", path = "distant-core", features = ["clap", "schemars"] }
directories = "4.0.1"
flexi_logger = "0.23.0"
indoc = "1.0.7"
@ -55,7 +55,7 @@ winsplit = "0.1.0"
whoami = "1.2.1"
# Optional native SSH functionality
distant-ssh2 = { version = "=0.17.0", path = "distant-ssh2", default-features = false, features = ["serde"], optional = true }
distant-ssh2 = { version = "=0.17.1", path = "distant-ssh2", default-features = false, features = ["serde"], optional = true }
[target.'cfg(unix)'.dependencies]
fork = "0.1.19"

@ -3,7 +3,7 @@ name = "distant-core"
description = "Core library for distant, enabling operation on a remote computer through file and process manipulation"
categories = ["network-programming"]
keywords = ["api", "async"]
version = "0.17.0"
version = "0.17.1"
authors = ["Chip Senkbeil <chip@senkbeil.org>"]
edition = "2021"
homepage = "https://github.com/chipsenkbeil/distant"
@ -19,11 +19,11 @@ async-trait = "0.1.57"
bitflags = "1.3.2"
bytes = "1.2.1"
derive_more = { version = "0.99.17", default-features = false, features = ["as_mut", "as_ref", "deref", "deref_mut", "display", "from", "error", "into", "into_iterator", "is_variant", "try_into"] }
distant-net = { version = "=0.17.0", path = "../distant-net" }
distant-net = { version = "=0.17.1", path = "../distant-net" }
futures = "0.3.21"
hex = "0.4.3"
log = "0.4.17"
notify = { version = "5.0.0-pre.15", features = ["serde"] }
notify = { version = "=5.0.0-pre.15", features = ["serde"] }
once_cell = "1.13.0"
portable-pty = "0.7.0"
rand = { version = "0.8.5", features = ["getrandom"] }

@ -3,7 +3,7 @@ name = "distant-net"
description = "Network library for distant, providing implementations to support client/server architecture"
categories = ["network-programming"]
keywords = ["api", "async"]
version = "0.17.0"
version = "0.17.1"
authors = ["Chip Senkbeil <chip@senkbeil.org>"]
edition = "2021"
homepage = "https://github.com/chipsenkbeil/distant"

@ -2,7 +2,7 @@
name = "distant-ssh2"
description = "Library to enable native ssh-2 protocol for use with distant sessions"
categories = ["network-programming"]
version = "0.17.0"
version = "0.17.1"
authors = ["Chip Senkbeil <chip@senkbeil.org>"]
edition = "2021"
homepage = "https://github.com/chipsenkbeil/distant"
@ -20,7 +20,7 @@ async-compat = "0.2.1"
async-once-cell = "0.4.2"
async-trait = "0.1.57"
derive_more = { version = "0.99.17", default-features = false, features = ["display", "error"] }
distant-core = { version = "=0.17.0", path = "../distant-core" }
distant-core = { version = "=0.17.1", path = "../distant-core" }
futures = "0.3.21"
hex = "0.4.3"
log = "0.4.17"

@ -17,6 +17,7 @@ use distant_core::{
DistantResponseData, Extra, RemoteCommand, Watcher,
};
use log::*;
use serde_json::{json, Value};
use std::{
io,
path::{Path, PathBuf},
@ -178,6 +179,9 @@ pub enum ClientSubcommand {
/// Connection to use, otherwise will prompt to select
connection: Option<ConnectionId>,
#[clap(short, long, default_value_t, value_enum)]
format: Format,
#[clap(flatten)]
network: NetworkConfig,
},
@ -401,7 +405,17 @@ impl ClientSubcommand {
*cache.data.selected = id;
cache.write_to_disk().await?;
println!("{}", id);
match format {
Format::Shell => println!("{}", id),
Format::Json => println!(
"{}",
serde_json::to_string(&json!({
"type": "connected",
"id": id,
}))
.unwrap()
),
}
}
Self::Launch {
config: launcher_config,
@ -465,7 +479,17 @@ impl ClientSubcommand {
*cache.data.selected = id;
cache.write_to_disk().await?;
println!("{}", id);
match format {
Format::Shell => println!("{}", id),
Format::Json => println!(
"{}",
serde_json::to_string(&json!({
"type": "launched",
"id": id,
}))
.unwrap()
),
}
}
Self::Lsp {
connection,
@ -583,6 +607,7 @@ impl ClientSubcommand {
}
Self::Select {
connection,
format,
network,
..
} => match connection {
@ -608,8 +633,8 @@ impl ClientSubcommand {
)));
}
trace!("Building selection prompt of {} choices", list.len());
let selected = list
// Figure out the current selection
let current = list
.iter()
.enumerate()
.find_map(|(i, (id, _))| {
@ -621,6 +646,7 @@ impl ClientSubcommand {
})
.unwrap_or_default();
trace!("Building selection prompt of {} choices", list.len());
let items: Vec<String> = list
.iter()
.map(|(_, destination)| {
@ -639,12 +665,51 @@ impl ClientSubcommand {
})
.collect();
trace!("Rendering prompt");
let selected = Select::with_theme(&ColorfulTheme::default())
.items(&items)
.default(selected)
.interact_on_opt(&Term::stderr())
.context("Failed to render prompt")?;
// Prompt for a selection, with None meaning no change
let selected = match format {
Format::Shell => {
trace!("Rendering prompt");
Select::with_theme(&ColorfulTheme::default())
.items(&items)
.default(current)
.interact_on_opt(&Term::stderr())
.context("Failed to render prompt")?
}
Format::Json => {
// Print out choices
MsgSender::from_stdout()
.send_blocking(&json!({
"type": "select",
"choices": items,
"current": current,
}))
.context("Failed to send JSON choices")?;
// Wait for a response
let msg = MsgReceiver::from_stdin()
.recv_blocking::<Value>()
.context("Failed to receive JSON selection")?;
// Verify the response type is "selected"
match msg.get("type") {
Some(value) if value == "selected" => msg
.get("choice")
.and_then(|value| value.as_u64())
.map(|choice| choice as usize),
Some(value) => {
return Err(CliError::Error(anyhow::anyhow!(
"Unexpected 'type' field value: {value}"
)))
}
None => {
return Err(CliError::Error(anyhow::anyhow!(
"Missing 'type' field"
)))
}
}
}
};
match selected {
Some(index) => {

Loading…
Cancel
Save