memstead_cli/commands/
list.rs1use std::collections::HashMap;
2
3use clap::Parser;
4
5use memstead_base::ops::SearchScope;
6use memstead_base::render;
7
8use crate::CliError;
9use crate::output::ExitKind;
10use crate::output::{print_json, print_markdown};
11use crate::setup::{CliContext, CliEngine};
12
13#[derive(Parser, Debug)]
15#[command(after_long_help = super::FILTER_HELP)]
16pub struct Args {
17 #[arg(long)]
18 pub mem: Option<String>,
19
20 #[arg(long = "type")]
21 pub entity_type: Option<String>,
22
23 #[arg(long)]
24 pub level: Option<String>,
25
26 #[arg(long)]
27 pub status: Option<String>,
28
29 #[arg(long)]
30 pub edge_type: Option<String>,
31
32 #[arg(long = "filter", value_name = "KEY=VALUE")]
42 pub filter: Vec<String>,
43
44 #[arg(long)]
45 pub limit: Option<usize>,
46
47 #[arg(long)]
48 pub offset: Option<usize>,
49}
50
51pub fn run(ctx: &CliContext, args: Args) -> anyhow::Result<()> {
52 let mut filters = HashMap::new();
53 if let Some(level) = args.level {
54 filters.insert("level".to_string(), level);
55 }
56 if let Some(status) = args.status {
57 filters.insert("status".to_string(), status);
58 }
59 for raw in &args.filter {
60 let (key, value) = super::parse_filter_arg(raw)?;
61 filters.insert(key, value);
62 }
63
64 let scope = SearchScope {
65 mem: args.mem,
66 entity_type: args.entity_type,
67 limit: args.limit,
68 offset: args.offset,
69 edge_type: args.edge_type,
70 filters,
71 ..Default::default()
72 };
73
74 let result = match ctx.cli_engine()? {
75 #[cfg(feature = "mem-repo")]
76 CliEngine::MemRepo(engine) => {
77 if let Some(name) = scope.mem.as_deref()
81 && engine.mount(name).is_none()
82 {
83 return Err(unknown_mem_error(name, &engine).into());
84 }
85 engine.list(&scope)
86 }
87 CliEngine::Filesystem(engine) => {
88 if let Some(name) = scope.mem.as_deref()
89 && engine.mount(name).is_none()
90 {
91 return Err(unknown_mem_error(name, &engine).into());
92 }
93 engine.list(&scope)
94 }
95 };
96
97 if ctx.json {
98 let envelope = render::build_list_envelope(&result);
99 print_json(&envelope)?;
100 } else {
101 print_markdown(&render::render_list_markdown(&result));
102 }
103 Ok(())
104}
105
106pub(super) fn unknown_mem_error(name: &str, engine: &memstead_base::Engine) -> CliError {
111 let known: Vec<&str> = engine.mem_names();
112 let detail = if known.is_empty() {
113 "no mems loaded — run `memstead workspace dump` for the workspace state".to_string()
114 } else {
115 format!(
116 "known mems: [{}]. Run `memstead workspace dump` for the full snapshot.",
117 known.join(", ")
118 )
119 };
120 CliError {
121 code: "UNKNOWN_MEM",
122 kind: ExitKind::NotFound,
123 message: format!("unknown mem: {name} — {detail}"),
124 details: Some(serde_json::json!({ "mem": name })),
125 }
126}