fur_cli/commands/
thread.rs1use std::fs;
2use std::path::Path;
3use serde_json::{Value, json};
4use clap::Parser;
5use crate::renderer::list::render_list;
6
7#[derive(Parser)]
9pub struct ThreadArgs {
10 pub id: Option<String>,
12
13 #[arg(long)]
15 pub view: bool,
16}
17
18pub fn run_thread(args: ThreadArgs) {
20 let fur_dir = Path::new(".fur");
21 let index_path = fur_dir.join("index.json");
22
23 if !index_path.exists() {
24 eprintln!("🚨 .fur/ not found. Run `fur new` first.");
25 return;
26 }
27
28 let mut index: Value =
29 serde_json::from_str(&fs::read_to_string(&index_path).unwrap()).unwrap();
30
31 if args.view || args.id.is_none() {
35 let empty_vec: Vec<Value> = Vec::new();
36 let threads = index["threads"].as_array().unwrap_or(&empty_vec);
37 let active = index["active_thread"].as_str().unwrap_or("");
38
39 let mut rows = Vec::new();
40 let mut active_idx = None;
41
42 for (i, tid) in threads.iter().enumerate() {
43 if let Some(tid_str) = tid.as_str() {
44 let thread_path = fur_dir.join("threads").join(format!("{}.json", tid_str));
45 if let Ok(content) = fs::read_to_string(thread_path) {
46 if let Ok(thread_json) = serde_json::from_str::<Value>(&content) {
47 let title = thread_json["title"].as_str().unwrap_or("Untitled");
48 let short_id = &tid_str[..8];
49 rows.push(vec![short_id.to_string(), title.to_string()]);
50 if tid_str == active {
51 active_idx = Some(i);
52 }
53 }
54 }
55 }
56 }
57
58 render_list("Threads", &["ID", "Title"], rows, active_idx);
59 return;
60 }
61
62 if let Some(tid) = args.id {
66 let empty_vec: Vec<Value> = Vec::new();
67 let threads: Vec<String> = index["threads"]
68 .as_array()
69 .unwrap_or(&empty_vec)
70 .iter()
71 .filter_map(|t| t.as_str().map(|s| s.to_string()))
72 .collect();
73
74 let mut found = threads.iter().find(|&s| s == &tid);
76
77 if found.is_none() {
79 let matches: Vec<&String> = threads
80 .iter()
81 .filter(|s| s.starts_with(&tid))
82 .collect();
83
84 if matches.len() == 1 {
85 found = Some(matches[0]);
86 } else if matches.len() > 1 {
87 eprintln!("❌ Ambiguous prefix '{}'. Matches: {:?}", tid, matches);
88 return;
89 }
90 }
91
92 let tid_full = match found {
93 Some(s) => s,
94 None => {
95 eprintln!("❌ Thread not found: {}", tid);
96 return;
97 }
98 };
99
100 index["active_thread"] = json!(tid_full);
102 index["current_message"] = serde_json::Value::Null;
103 fs::write(&index_path, serde_json::to_string_pretty(&index).unwrap()).unwrap();
104
105 let thread_path = fur_dir.join("threads").join(format!("{}.json", tid_full));
106 let content = fs::read_to_string(thread_path).unwrap();
107 let thread_json: Value = serde_json::from_str(&content).unwrap();
108 let title = thread_json["title"].as_str().unwrap_or("Untitled");
109
110 println!("✔️ Switched active thread to {} \"{}\"", &tid_full[..8], title);
111 }
112}