Skip to main content

fur_cli/commands/status/
core.rs

1use std::fs;
2use std::path::Path;
3use serde_json::Value;
4use std::collections::HashMap;
5
6pub fn load_index_and_conversation(fur_dir: &Path)
7    -> (Value, Value, String)
8{
9    let index_path = fur_dir.join("index.json");
10    let index: Value = read_json(&index_path);
11
12    let conversation_id = index["active_thread"].as_str().unwrap_or("");
13    let current = index["current_message"].as_str().unwrap_or("").to_string();
14
15    let convo_path = fur_dir.join("threads").join(format!("{}.json", conversation_id));
16    let conversation: Value = read_json(&convo_path);
17
18    (index, conversation, current)
19}
20
21
22/// Preload all reachable messages
23pub fn load_conversation_messages(
24    fur_dir: &Path,
25    conversation: &Value
26) -> HashMap<String, Value> {
27
28    let mut id_to_message = HashMap::new();
29
30    let mut stack: Vec<String> = conversation["messages"]
31        .as_array()
32        .unwrap_or(&vec![])
33        .iter()
34        .filter_map(|id| id.as_str().map(|s| s.to_string()))
35        .collect();
36
37    let messages_dir = fur_dir.join("messages");
38
39    while let Some(mid) = stack.pop() {
40
41        let msg_path = messages_dir.join(format!("{}.json", mid));
42
43        if let Some(content) = crate::security::io::read_text_file(&msg_path) {
44
45            if let Ok(mut obj) = serde_json::from_str::<Value>(&content) {
46
47                // ─────────────────────────────
48                // Lazy schema upgrade
49                // ─────────────────────────────
50                if crate::commands::jot::upgrade_message_schema(&mut obj) {
51
52                    // write upgraded message back to disk
53                    if let Ok(serialized) = serde_json::to_string_pretty(&obj) {
54                        let _ = fs::write(&msg_path, serialized);
55                    }
56                }
57
58                // ─────────────────────────────
59                // enqueue children
60                // ─────────────────────────────
61                if let Some(children) = obj["children"].as_array() {
62                    for c in children {
63                        if let Some(cid) = c.as_str() {
64                            stack.push(cid.to_string());
65                        }
66                    }
67                }
68
69                // ─────────────────────────────
70                // enqueue branch blocks
71                // ─────────────────────────────
72                if let Some(blocks) = obj["branches"].as_array() {
73                    for block in blocks {
74                        if let Some(arr) = block.as_array() {
75                            for c in arr {
76                                if let Some(cid) = c.as_str() {
77                                    stack.push(cid.to_string());
78                                }
79                            }
80                        }
81                    }
82                }
83
84                id_to_message.insert(mid.clone(), obj);
85            }
86        }
87    }
88
89    id_to_message
90}
91
92
93/// If current_message missing, use first in conversation.messages
94pub fn first_message_fallback(conversation: &Value) -> String {
95    conversation["messages"]
96        .as_array()
97        .and_then(|arr| arr.get(0))
98        .and_then(|v| v.as_str())
99        .unwrap_or("")
100        .to_string()
101}
102
103fn read_json(path: &Path) -> Value {
104
105    use crate::security::io::read_text_file;
106
107    serde_json::from_str(
108        &read_text_file(path)
109            .expect("❌ Project locked. Run `fur unlock`.")
110    ).unwrap()
111}