link_assistant_router/
server_command.rs1use std::io::Read as _;
4use std::process::ExitCode;
5
6use crate::cli::ServerOp;
7use crate::managed_server::{
8 PersistedServer, claim_managed, clear_persisted, configured_source, managed_status,
9 remove_managed, save_persisted, start_managed, stop_managed,
10};
11
12type AnyError = Box<dyn std::error::Error + Send + Sync>;
13
14#[must_use]
15pub fn run(op: &ServerOp) -> ExitCode {
16 let result = match op {
17 ServerOp::Use {
18 server,
19 token,
20 token_stdin,
21 clear,
22 run_max_requests,
23 } => configure(
24 server.as_deref(),
25 token.clone(),
26 *token_stdin,
27 *clear,
28 *run_max_requests,
29 ),
30 ServerOp::Status => status(),
31 ServerOp::Start => start_managed().map(|url| {
32 println!("managed router started at {url}");
33 }),
34 ServerOp::Claim => claim_managed().map(|token| {
35 eprintln!(
36 "The managed administrator is now claimed. Save this credential; future `with` runs require a token."
37 );
38 println!("{token}");
39 }),
40 ServerOp::Stop => stop_managed().map(|()| {
41 println!("managed router stopped; container and volume were preserved");
42 }),
43 ServerOp::Remove { yes } => remove_managed(*yes).map(|()| {
44 println!("removed the managed router container and credential volume");
45 }),
46 ServerOp::Reap { pid } => return crate::managed_server::reap(*pid),
47 };
48 match result {
49 Ok(()) => ExitCode::SUCCESS,
50 Err(error) => {
51 eprintln!("error: {error}");
52 ExitCode::from(1)
53 }
54 }
55}
56
57fn configure(
58 server: Option<&str>,
59 token: Option<String>,
60 token_stdin: bool,
61 clear: bool,
62 run_max_requests: Option<u64>,
63) -> Result<(), AnyError> {
64 if clear {
65 if server.is_some() || token.is_some() || token_stdin || run_max_requests.is_some() {
66 return Err("--clear cannot be combined with a server, token, or run budget".into());
67 }
68 let path = clear_persisted()?;
69 println!("cleared persisted server selection at {}", path.display());
70 return Ok(());
71 }
72 let server = server.ok_or("provide a server URL or use --clear")?;
73 let token = if token_stdin {
74 Some(read_token()?)
75 } else {
76 token
77 };
78 let path = save_persisted(&PersistedServer {
79 server: server.to_string(),
80 token,
81 run_max_requests,
82 })?;
83 println!(
84 "saved server selection in {} (token {})",
85 path.display(),
86 if crate::managed_server::load_persisted()?.is_some_and(|config| config.token.is_some()) {
87 "set"
88 } else {
89 "unset"
90 }
91 );
92 Ok(())
93}
94
95fn status() -> Result<(), AnyError> {
96 println!("effective server: {}", configured_source()?);
97 println!("managed server: {}", managed_status()?);
98 Ok(())
99}
100
101pub fn read_token() -> Result<String, AnyError> {
102 let mut input = String::new();
103 std::io::stdin().read_to_string(&mut input)?;
104 let token = input.lines().next().unwrap_or_default().trim();
105 if token.is_empty() {
106 Err("standard input did not contain a token".into())
107 } else {
108 Ok(token.to_string())
109 }
110}