Skip to main content

mail4agent_messenger_shell/cli/
session_arg.rs

1//! Shared by the owner-facing commands (`wake`, `element-open`): which session is meant, and where
2//! its files live. A session is named by `--session <id>` or by `--as <nick>` (looked up in the
3//! records of `M4A_SESSIONS_DIR`). The store root is `M4A_STORE_ROOT`.
4
5use std::path::PathBuf;
6
7use crate::machine::{load_session_records, routine_name_for, HostSession, SESSIONS_DIR_ENV};
8use crate::{session_store_dir, STORE_ROOT_ENV};
9
10/// Tests of the owner commands set process-wide environment; they run one at a time.
11#[cfg(test)]
12pub(crate) static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
13
14pub struct Target {
15    pub session_id: String,
16    pub nick: Option<String>,
17    pub store_root: PathBuf,
18    /// The record, when the session was named by `--as` or the record directory is set.
19    pub record: Option<HostSession>,
20}
21
22impl Target {
23    pub fn store_dir(&self) -> PathBuf {
24        session_store_dir(&self.store_root, &self.session_id)
25    }
26}
27
28/// The value of `--flag value` and the arguments left over.
29pub fn take_flag(args: &mut Vec<String>, flag: &str) -> Result<Option<String>, String> {
30    let Some(i) = args.iter().position(|a| a == flag) else { return Ok(None) };
31    if i + 1 >= args.len() {
32        return Err(format!("{flag} needs a value"));
33    }
34    let v = args.remove(i + 1);
35    args.remove(i);
36    Ok(Some(v))
37}
38
39pub fn take_switch(args: &mut Vec<String>, flag: &str) -> bool {
40    match args.iter().position(|a| a == flag) {
41        Some(i) => {
42            args.remove(i);
43            true
44        }
45        None => false,
46    }
47}
48
49/// Resolves `--session` / `--as` (removing them from `args`).
50pub fn resolve(args: &mut Vec<String>) -> Result<Target, String> {
51    let session = take_flag(args, "--session")?;
52    let nick = take_flag(args, "--as")?;
53    let store_root = std::env::var(STORE_ROOT_ENV).ok().filter(|v| !v.is_empty()).map(PathBuf::from).ok_or_else(|| format!("{STORE_ROOT_ENV} is not set"))?;
54    let records = std::env::var(SESSIONS_DIR_ENV).ok().filter(|v| !v.is_empty()).and_then(|d| load_session_records(std::path::Path::new(&d)).ok()).unwrap_or_default();
55    match (session, nick) {
56        (Some(_), Some(_)) => Err("give --session or --as, not both".into()),
57        (None, None) => Err("name the session: --as <nick> or --session <id>".into()),
58        (Some(id), None) => {
59            let record = records.into_iter().find(|r| r.session_id == id);
60            let nick = record.as_ref().and_then(routine_name_for);
61            Ok(Target { session_id: id, nick, store_root, record })
62        }
63        (None, Some(n)) => {
64            let want = crate::nick::lookup_nick(&n).map_err(|e| e.to_string())?;
65            let record = records.into_iter().find(|r| routine_name_for(r).is_some_and(|x| x.eq_ignore_ascii_case(&want))).ok_or_else(|| format!("no session record for {want} in {SESSIONS_DIR_ENV}"))?;
66            Ok(Target { session_id: record.session_id.clone(), nick: Some(want), store_root, record: Some(record) })
67        }
68    }
69}