Skip to main content

fur_cli/helpers/
insertion.rs

1use serde_json::json;
2use std::fs;
3use std::path::Path;
4
5use crate::commands::jot::{self, JotArgs};
6use crate::commands::chat;
7use crate::commands::message::{MsgArgs, detect_id, resolve_target_message};
8
9/// Main entry point: orchestrates insertion
10pub fn run_insert(args: &MsgArgs, insert_before: bool) {
11    let Some(target_pfx) = &args.id_prefix else {
12        eprintln!("❌ Must supply a target message ID prefix.");
13        return;
14    };
15
16    // 1. Resolve prefix → full ID
17    let target_id = detect_id(&Some(target_pfx.clone()))
18        .unwrap_or_else(|| resolve_target_message(Some(target_pfx.clone())));
19
20    // 2. Create new message via jot/chat passthrough
21    let new_id = match create_message_from_passthrough(args) {
22        Ok(id) => id,
23        Err(e) => {
24            eprintln!("❌ {}", e);
25            return;
26        }
27    };
28
29    // 3. Insert relative to target
30    insert_message_relative_to(&target_id, &new_id, insert_before);
31
32    println!(
33        "➕ Inserted {} {} {}",
34        &new_id[..8],
35        if insert_before { "before" } else { "after" },
36        &target_id[..8]
37    );
38}
39
40/// Create new message by delegating to jot/chat
41fn create_message_from_passthrough(args: &MsgArgs) -> Result<String, String> {
42    if args.rest.is_empty() {
43        return Err("No jot/chat command provided.".into());
44    }
45
46    let cmd = args.rest[0].as_str();
47    let rest = &args.rest[1..];
48
49    match cmd {
50        "jot" => {
51            // jot <avatar?> <text?>
52            let avatar = rest.get(0).cloned();
53            let text = rest.get(1).cloned();
54
55            let jargs = JotArgs {
56                avatar,
57                positional_text: text,
58                text: None,
59                markdown: None,
60                img: None,
61                parent: None,
62            };
63
64            jot::run_jot(jargs);
65            Ok(get_current_message_id())
66        }
67
68        "chat" => {
69            // chat <avatar?>
70            let avatar = rest.get(0).cloned();
71            chat::run_chat(avatar);
72            Ok(get_current_message_id())
73        }
74
75        other => Err(format!("Unknown passthrough subcommand: {}", other)),
76    }
77}
78
79/// Insert a message ID into threads/<thread>.json
80fn insert_message_relative_to(target_id: &str, new_id: &str, insert_before: bool) {
81    let fur_dir = Path::new(".fur");
82
83    // Load index
84    let index_path = fur_dir.join("index.json");
85    let index: serde_json::Value =
86        serde_json::from_str(&fs::read_to_string(&index_path).unwrap()).unwrap();
87    let convo_id = index["active_thread"].as_str().unwrap();
88
89    // Load conversation
90    let convo_path = fur_dir.join("threads").join(format!("{}.json", convo_id));
91    let mut convo: serde_json::Value =
92        serde_json::from_str(&fs::read_to_string(&convo_path).unwrap()).unwrap();
93
94    let arr = convo["messages"].as_array_mut().unwrap();
95
96    // Remove message from natural append location (jot/chat always append to the end)
97    arr.retain(|v| v.as_str() != Some(new_id));
98
99    // Find target index
100    let idx = arr.iter().position(|v| v.as_str() == Some(target_id));
101
102    if let Some(i) = idx {
103        if insert_before {
104            arr.insert(i, json!(new_id));
105        } else {
106            arr.insert(i + 1, json!(new_id));
107        }
108    } else {
109        eprintln!("❌ Could not find target message {}.", target_id);
110        return;
111    }
112
113    // Save updated conversation
114    fs::write(&convo_path, serde_json::to_string_pretty(&convo).unwrap()).unwrap();
115}
116
117/// Retrieve the last message ID created
118pub fn get_current_message_id() -> String {
119    let idx_path = Path::new(".fur/index.json");
120    let index: serde_json::Value =
121        serde_json::from_str(&fs::read_to_string(idx_path).unwrap()).unwrap();
122
123    index["current_message"]
124        .as_str()
125        .expect("No current_message after jot/chat")
126        .to_string()
127}