mi6_cli/commands/
session.rs1use anyhow::{Context, Result};
2use serde_json::{Map, Value};
3use std::collections::HashSet;
4
5use mi6_core::{Session, Storage};
6
7pub fn run_session<S: Storage>(
9 storage: &S,
10 session_or_pid: String,
11 machine_id: Option<String>,
12 json: bool,
13 fields: Option<Vec<String>>,
14) -> Result<()> {
15 let session = if let Ok(pid) = session_or_pid.parse::<i32>() {
17 storage
19 .get_session_by_pid(pid)
20 .context("failed to query session by PID")?
21 .ok_or_else(|| anyhow::anyhow!("no session found for PID: {}", pid))?
22 } else if let Some(ref machine) = machine_id {
23 storage
25 .get_session_by_key(machine, &session_or_pid)
26 .context("failed to get session by key")?
27 .ok_or_else(|| {
28 anyhow::anyhow!(
29 "no session found: {} on machine {}",
30 session_or_pid,
31 machine
32 )
33 })?
34 } else {
35 storage
37 .get_session(&session_or_pid)
38 .context("failed to get session")?
39 .ok_or_else(|| anyhow::anyhow!("no session found: {}", session_or_pid))?
40 };
41
42 let session_json = session_to_json(&session)?;
44
45 let output = if let Some(ref field_list) = fields {
47 filter_fields(&session_json, field_list)
48 } else {
49 session_json
50 };
51
52 if json {
53 print_json(&output);
54 } else {
55 print_list(&output);
56 }
57
58 Ok(())
59}
60
61fn session_to_json(session: &Session) -> Result<Value> {
63 let mut map: Map<String, Value> = serde_json::from_value(serde_json::to_value(session)?)?;
65
66 map.insert(
68 "total_tokens".to_string(),
69 Value::Number(session.total_tokens().into()),
70 );
71 map.insert(
72 "duration_ms".to_string(),
73 Value::Number(session.duration().num_milliseconds().into()),
74 );
75 map.insert("is_active".to_string(), Value::Bool(session.is_active()));
76 map.insert(
77 "is_process_running".to_string(),
78 Value::Bool(session.is_process_running()),
79 );
80 map.insert(
81 "metrics_source".to_string(),
82 Value::String(session.metrics_source().to_string()),
83 );
84
85 Ok(Value::Object(map))
86}
87
88fn filter_fields(value: &Value, fields: &[String]) -> Value {
90 let Some(obj) = value.as_object() else {
91 return value.clone();
92 };
93
94 let field_set: HashSet<&str> = fields.iter().map(|s| s.as_str()).collect();
95
96 let filtered: Map<String, Value> = obj
97 .iter()
98 .filter(|(k, _)| field_set.contains(k.as_str()))
99 .map(|(k, v)| (k.clone(), v.clone()))
100 .collect();
101
102 Value::Object(filtered)
103}
104
105fn print_json(value: &Value) {
107 if let Ok(json) = serde_json::to_string_pretty(value) {
108 println!("{}", json);
109 }
110}
111
112fn print_list(value: &Value) {
114 let Some(obj) = value.as_object() else {
115 return;
116 };
117
118 let mut keys: Vec<&String> = obj.keys().collect();
120 keys.sort();
121
122 for key in keys {
123 if let Some(val) = obj.get(key) {
124 println!("- {}: {}", key, format_value(val));
125 }
126 }
127}
128
129fn format_value(value: &Value) -> String {
131 match value {
132 Value::Null => "null".to_string(),
133 Value::Bool(b) => b.to_string(),
134 Value::Number(n) => n.to_string(),
135 Value::String(s) => s.clone(),
136 Value::Array(arr) => {
137 let items: Vec<String> = arr.iter().map(format_value).collect();
138 format!("[{}]", items.join(", "))
139 }
140 Value::Object(_) => "[object]".to_string(),
141 }
142}