pub mod account;
pub mod agent;
pub mod app;
pub mod bulk;
pub(crate) async fn prompt_version_if_interactive(
binary: std::path::PathBuf,
) -> anyhow::Result<Option<String>> {
use anyhow::Context;
tokio::task::spawn_blocking(move || -> anyhow::Result<Option<String>> {
use std::io::{IsTerminal, Write};
if !std::io::stdin().is_terminal() {
return Ok(None);
}
eprint!("VERSIONINFO not found in {binary:?}. Enter version label: ");
std::io::stderr().flush().ok();
let mut buf = String::new();
std::io::stdin()
.read_line(&mut buf)
.context("read version label from stdin")?;
let entered = buf.trim().to_string();
if entered.is_empty() {
anyhow::bail!("no version entered — aborting");
}
Ok(Some(entered))
})
.await
.context("version prompt task")?
}
pub(crate) fn validate_segment(label: &str, value: &str) -> anyhow::Result<()> {
if value.is_empty() {
anyhow::bail!("{label} must be non-empty");
}
if value.contains('/') {
anyhow::bail!("{label} must not contain '/'");
}
for c in value.chars() {
if !c.is_ascii() {
anyhow::bail!("{label} must be ASCII-printable (rejected non-ASCII {c:?})");
}
if c.is_ascii_control() {
anyhow::bail!("{label} must not contain control characters");
}
if c == '"' || c == '\\' {
anyhow::bail!("{label} must not contain '\"' or '\\\\'");
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn prompt_version_non_interactive_returns_none() {
let got = prompt_version_if_interactive(std::path::PathBuf::from("vendor.msi"))
.await
.unwrap();
assert_eq!(got, None);
}
#[test]
fn validate_segment_accepts_plain_labels() {
assert!(validate_segment("version", "1.2.3").is_ok());
assert!(validate_segment("name", "kanade-client").is_ok());
}
#[test]
fn validate_segment_rejects_bad_labels() {
assert!(validate_segment("version", "").is_err(), "empty");
assert!(validate_segment("version", "bad/version").is_err(), "slash");
assert!(validate_segment("version", "v1\n").is_err(), "control");
assert!(validate_segment("version", "café").is_err(), "non-ascii");
assert!(validate_segment("version", "a\"b").is_err(), "quote");
}
}
pub mod config;
pub mod exec;
pub mod freeze;
pub mod group;
pub mod jetstream;
pub mod job;
pub mod kill;
pub mod login;
pub mod ping;
pub mod provenance;
pub mod publish_verify;
pub mod revoke;
pub mod run;
pub mod schedule;
pub mod script;
pub mod self_update;