1use std::fs;
2use std::path::Path;
3use clap::Args;
4use serde_json::Value;
5
6#[derive(Args, Debug)]
8pub struct JumpArgs {
9 #[arg(short, long)]
10 pub past: Option<usize>,
11
12 #[arg(short, long)]
13 pub child: Option<usize>,
14
15 #[arg(short, long)]
16 pub id: Option<String>,
17}
18
19pub fn run_jump(args: JumpArgs) -> Result<(), Box<dyn std::error::Error>> {
20 let index_path = Path::new(".fur/index.json");
21 let index_data = fs::read_to_string(index_path).expect("❌ Couldn't read index.json");
22 let mut index: Value = serde_json::from_str(&index_data).unwrap();
23
24 let conversation_id = index["active_thread"].as_str().unwrap();
25 let convo_path = Path::new(".fur/threads").join(format!("{}.json", conversation_id));
26 let conversation_data = fs::read_to_string(convo_path).expect("❌ Couldn't read conversation file");
27 let conversation: Value = serde_json::from_str(&conversation_data).unwrap();
28
29 let current_id = index["current_message"].as_str().unwrap_or_default();
30
31 let messages = conversation["messages"].as_array().unwrap();
32
33 let current_msg_id = current_id;
35 let current_msg = messages
36 .iter()
37 .find_map(|id| {
38 if id.as_str()? == current_msg_id {
39 let msg_path = Path::new(".fur/messages").join(format!("{}.json", current_msg_id));
40 let msg_data = fs::read_to_string(msg_path).ok()?;
41 serde_json::from_str::<Value>(&msg_data).ok()
42 } else {
43 None
44 }
45 });
46
47 if current_msg.is_none() {
48 eprintln!("❌ Current message not found in conversation.");
49 return Ok(());
50 }
51 let current = current_msg.unwrap();
52
53 if let Some(n) = args.past {
55 let mut current_id = current["id"].as_str().unwrap_or_default().to_string();
56 let mut jumped = 0;
57
58 while jumped < n {
59 let msg_path = Path::new(".fur/messages").join(format!("{}.json", current_id));
62 let msg_data = fs::read_to_string(&msg_path);
63 if msg_data.is_err() {
64 eprintln!("❌ Failed to load message: {}", current_id);
65 return Ok(());
66 }
67
68 let msg_json: Value = serde_json::from_str(&msg_data.unwrap()).unwrap();
69
70 match msg_json["parent"].as_str() {
74 Some(pid) if !pid.is_empty() => {
75 let in_conversation = conversation["messages"]
76 .as_array()
77 .unwrap_or(&vec![])
78 .iter()
79 .any(|val| val.as_str() == Some(pid));
80
81 if !in_conversation {
82 println!("\x1b[91m📜 You've reached the origin of this conversation. No earlier messages exist.\x1b[0m");
83 println!("\x1b[93m🌱 To start a new conversation, run:\n fur new \"Title of your new conversation\"\x1b[0m");
84 return Ok(());
85 }
86
87 current_id = pid.to_string();
89 jumped += 1;
90 }
91 _ => {
92 println!("\x1b[91m📜 You've reached the origin of this conversation. No earlier messages exist.\x1b[0m");
93 println!("\x1b[93m🌱 To start a new conversation, run:\n fur new \"Title of your new conversation\"\x1b[0m");
94 return Ok(());
95 }
96 }
97 }
98
99 index["current_message"] = Value::String(current_id.clone());
100 fs::write(index_path, serde_json::to_string_pretty(&index).unwrap()).unwrap();
101 println!(
102 "⏪ Jumped back {} message{} to {}",
103 jumped,
104 if jumped == 1 { "" } else { "s" },
105 current_id
106 );
107 return Ok(());
108 }
109
110
111 if let Some(n) = args.child {
113 let current_id = current["id"].as_str().unwrap_or_default();
114
115 let children: Vec<String> = messages.iter().filter_map(|msg_id| {
116 let msg_path = Path::new(".fur/messages").join(format!("{}.json", msg_id.as_str()?));
117 let msg_data = fs::read_to_string(msg_path).ok()?;
118 let msg_json: Value = serde_json::from_str(&msg_data).ok()?;
119 if msg_json["parent"].as_str()? == current_id {
120 Some(msg_json["id"].as_str()?.to_string())
121 } else {
122 None
123 }
124 }).collect();
125
126 if let Some(child_id) = children.get(n) {
127 index["current_message"] = Value::String(child_id.to_string());
128 fs::write(index_path, serde_json::to_string_pretty(&index).unwrap()).unwrap();
129 println!("⏩ Jumped to child [{}]: {}", n, child_id);
130 return Ok(());
131 } else {
132 eprintln!("❌ No such child at index {}", n);
133 return Ok(());
134 }
135
136 }
137
138 if let Some(ref target_id) = args.id {
140 if messages.iter().any(|m| m["id"].as_str() == Some(&target_id)) {
141 index["current_message"] = Value::String(target_id.to_string());
142 fs::write(index_path, serde_json::to_string_pretty(&index).unwrap()).unwrap();
143 println!("🎯 Jumped directly to message ID {}", target_id);
144 return Ok(());
145 } else {
146 eprintln!("❌ Message ID not found: {}", target_id);
147 return Ok(());
148 }
149 }
150
151 eprintln!("❗ No jump argument provided. Use --past, --child, or --id.");
152 Ok(())
153
154
155}