memvid_cli/commands/
creation.rs1use std::fs;
4use std::path::PathBuf;
5
6use anyhow::Result;
7use clap::{ArgAction, Args, ValueEnum};
8use memvid_core::{Memvid, Ticket};
9
10use crate::config::CliConfig;
11use crate::utils::{format_bytes, open_read_only_mem, parse_size, yes_no};
12
13#[derive(Clone, Copy, Debug, ValueEnum)]
15pub enum TierArg {
16 Free,
17 Dev,
18 Enterprise,
19}
20
21impl From<TierArg> for memvid_core::Tier {
22 fn from(value: TierArg) -> Self {
23 match value {
24 TierArg::Free => memvid_core::Tier::Free,
25 TierArg::Dev => memvid_core::Tier::Dev,
26 TierArg::Enterprise => memvid_core::Tier::Enterprise,
27 }
28 }
29}
30
31#[derive(Args)]
33pub struct CreateArgs {
34 #[arg(value_name = "FILE", value_parser = clap::value_parser!(PathBuf))]
36 pub file: PathBuf,
37 #[arg(long, value_enum)]
39 pub tier: Option<TierArg>,
40 #[arg(long = "size", alias = "capacity", value_name = "SIZE", value_parser = parse_size)]
42 pub size: Option<u64>,
43 #[arg(long = "no-lex", action = ArgAction::SetTrue)]
45 pub no_lex: bool,
46 #[arg(long = "no-vector", aliases = ["no-vec"], action = ArgAction::SetTrue)]
48 pub no_vector: bool,
49}
50
51#[derive(Args)]
53pub struct OpenArgs {
54 #[arg(value_name = "FILE", value_parser = clap::value_parser!(PathBuf))]
56 pub file: PathBuf,
57 #[arg(long)]
59 pub json: bool,
60}
61
62pub fn handle_create(_config: &CliConfig, args: CreateArgs) -> Result<()> {
64 if let Some(parent) = args.file.parent() {
65 if !parent.exists() {
66 fs::create_dir_all(parent)?;
67 }
68 }
69
70 let tier = args
71 .tier
72 .map(memvid_core::Tier::from)
73 .unwrap_or(memvid_core::Tier::Free);
74 let capacity_bytes = args.size.unwrap_or_else(|| tier.capacity_bytes());
75 let lexical_enabled = !args.no_lex;
76 let vector_enabled = !args.no_vector;
77
78 let mut mem = Memvid::create(&args.file)?;
79 apply_capacity_override(&mut mem, capacity_bytes)?;
80 if lexical_enabled {
81 mem.enable_lex()?;
82 }
83
84 if vector_enabled {
85 mem.enable_vec()?;
86 }
87 mem.commit()?;
88
89 let stats = mem.stats()?;
90
91 let filename = args.file.display();
93 println!("✓ Created memory at {}", filename);
94 println!(
95 " Capacity: {} ({} bytes)",
96 format_bytes(stats.capacity_bytes),
97 stats.capacity_bytes
98 );
99 println!(" Size: {}", format_bytes(stats.size_bytes));
100 println!(
101 " Indexes: {} | {}",
102 if lexical_enabled { "lexical" } else { "no-lex" },
103 if vector_enabled { "vector" } else { "no-vec" }
104 );
105 println!();
106 println!("Next steps:");
107 println!(" memvid put {} --input <file> # Add content", filename);
108 println!(" memvid find {} --query <text> # Search", filename);
109 println!(" memvid stats {} # View stats", filename);
110 println!();
111 println!("Documentation: https://memvid.com/docs");
112 Ok(())
113}
114
115pub fn handle_open(_config: &CliConfig, args: OpenArgs) -> Result<()> {
117 let mem = open_read_only_mem(&args.file)?;
118 let stats = mem.stats()?;
119 if args.json {
120 println!("{}", serde_json::to_string_pretty(&stats)?);
121 } else {
122 println!("Memory: {}", args.file.display());
123 println!("Frames: {}", stats.frame_count);
124 println!("Size: {} bytes", stats.size_bytes);
125 println!("Tier: {:?}", stats.tier);
126 println!(
127 "Indices → lex: {}, vec: {}, time: {}",
128 yes_no(stats.has_lex_index),
129 yes_no(stats.has_vec_index),
130 yes_no(stats.has_time_index)
131 );
132 if let Some(seq) = stats.seq_no {
133 println!("Ticket sequence: {seq}");
134 }
135 }
136 Ok(())
137}
138
139pub fn apply_capacity_override(mem: &mut Memvid, capacity_bytes: u64) -> Result<()> {
142 let current = mem.current_ticket();
143 if current.capacity_bytes == capacity_bytes {
144 return Ok(());
145 }
146
147 let seq = current.seq_no.saturating_add(1).max(1);
148 let mut ticket = Ticket::new(current.issuer.clone(), seq).capacity_bytes(capacity_bytes);
149 if current.expires_in_secs != 0 {
150 ticket = ticket.expires_in_secs(current.expires_in_secs);
151 }
152 apply_ticket_with_warning(mem, ticket)?;
153 Ok(())
154}
155
156pub fn apply_ticket_with_warning(mem: &mut Memvid, ticket: Ticket) -> Result<()> {
157 let before = mem.stats()?.capacity_bytes;
158 mem.apply_ticket(ticket)?;
159 let after = mem.stats()?.capacity_bytes;
160 if after < before {
161 println!(
162 "Warning: capacity reduced from {} to {}",
163 format_bytes(before),
164 format_bytes(after)
165 );
166 }
167 Ok(())
168}