Skip to main content

interstice_cli/
node_utils.rs

1use crate::{
2    data_directory::nodes_dir,
3    node_client::{fetch_node_schema, handshake_with_node},
4    node_registry::{NodeRecord, NodeRegistry},
5    start::start,
6};
7use interstice_core::{IntersticeError, Node};
8use std::path::Path;
9
10/// Remove a node from the registry and clean up its local data directory if it's a local node
11pub fn remove_node_with_data(
12    registry: &mut NodeRegistry,
13    name_or_id: &str,
14) -> Result<(), IntersticeError> {
15    let removed = registry.remove(name_or_id)?;
16    if removed.local {
17        if let Some(node_id) = removed.node_id {
18            let node_path = nodes_dir().join(node_id);
19            if node_path.exists() {
20                std::fs::remove_dir_all(&node_path).map_err(|err| {
21                    IntersticeError::Internal(format!(
22                        "Failed to remove node data at {}. \
23                        Is another instance still running? Error: {err}",
24                        node_path.display()
25                    ))
26                })?;
27            }
28        }
29    }
30    Ok(())
31}
32
33pub async fn handle_node_command(args: &[String]) -> Result<(), IntersticeError> {
34    if args.len() < 3 {
35        print_node_help();
36        return Ok(());
37    }
38
39    let mut registry = NodeRegistry::load()?;
40    match args[2].as_str() {
41        "add" => {
42            if args.len() < 5 {
43                print_node_help();
44                return Ok(());
45            }
46            let name = args[3].clone();
47            let address = args[4].clone();
48            registry.add(NodeRecord {
49                name,
50                address,
51                node_id: None,
52                local: false,
53                last_seen: None,
54            })?;
55            println!("Node added.");
56        }
57        "create" => {
58            if args.len() < 5 {
59                print_node_help();
60                return Ok(());
61            }
62            let name = args[3].clone();
63            let port: u32 = args[4]
64                .trim()
65                .parse()
66                .map_err(|err| IntersticeError::Internal(format!("Failed to parse port: {err}")))?;
67            let address = format!("127.0.0.1:{}", port);
68            let node = Node::new(&nodes_dir(), port)?;
69            registry.add(NodeRecord {
70                name,
71                address,
72                node_id: Some(node.id.to_string()),
73                local: true,
74                last_seen: None,
75            })?;
76            println!("Local node created.");
77        }
78        "list" => {
79            for node in registry.list_sorted() {
80                let id = node.node_id.clone().unwrap_or_else(|| "-".into());
81                let last_seen = node
82                    .last_seen
83                    .map(|t| t.to_string())
84                    .unwrap_or_else(|| "-".into());
85                println!("{} | {} | {} | {}", node.name, node.address, id, last_seen);
86            }
87        }
88        "remove" => {
89            if args.len() < 4 {
90                print_node_help();
91                return Ok(());
92            }
93            remove_node_with_data(&mut registry, &args[3])?;
94            println!("Node removed.");
95        }
96        "rename" => {
97            if args.len() < 5 {
98                print_node_help();
99                return Ok(());
100            }
101            registry.rename(&args[3], &args[4])?;
102            println!("Node renamed.");
103        }
104        "show" => {
105            if args.len() < 4 {
106                print_node_help();
107                return Ok(());
108            }
109            let node = registry
110                .get(&args[3])
111                .ok_or_else(|| IntersticeError::Internal("Node not found".into()))?;
112            println!("name: {}", node.name);
113            println!("address: {}", node.address);
114            println!(
115                "node_id: {}",
116                node.node_id.clone().unwrap_or_else(|| "-".into())
117            );
118            println!("local: {}", node.local);
119            println!(
120                "last_seen: {}",
121                node.last_seen
122                    .map(|t| t.to_string())
123                    .unwrap_or_else(|| "-".into())
124            );
125        }
126        "start" => {
127            if args.len() < 4 {
128                print_node_help();
129                return Ok(());
130            }
131            let node = registry
132                .get(&args[3])
133                .ok_or_else(|| IntersticeError::Internal("Node not found".into()))?;
134            let port = node
135                .address
136                .split(':')
137                .next_back()
138                .ok_or_else(|| IntersticeError::Internal("Invalid address".into()))?
139                .parse()
140                .map_err(|_| IntersticeError::Internal("Invalid port".into()))?;
141
142            let node_id = node
143                .node_id
144                .clone()
145                .ok_or_else(|| IntersticeError::Internal("Missing node id".into()))?;
146            let parsed_node_id = node_id
147                .parse()
148                .map_err(|_| IntersticeError::Internal("Invalid node id".into()))?;
149            start(parsed_node_id, port).await?;
150        }
151        "ping" => {
152            if args.len() < 4 {
153                print_node_help();
154                return Ok(());
155            }
156            let address = registry
157                .resolve_address(&args[3])
158                .ok_or_else(|| IntersticeError::Internal("Unknown node".into()))?;
159            let (_stream, handshake) = handshake_with_node(&address).await?;
160            registry.set_last_seen(&args[3]);
161            registry.set_node_id(&args[3], handshake.node_id);
162            registry.save()?;
163            println!("Node reachable.");
164        }
165        "schema" => {
166            if args.len() < 4 {
167                print_node_help();
168                return Ok(());
169            }
170            let node_ref = &args[3];
171            let out_path = args
172                .get(4)
173                .map(Path::new)
174                .unwrap_or_else(|| Path::new("node_schema.toml"));
175            let address = registry
176                .resolve_address(node_ref)
177                .ok_or_else(|| IntersticeError::Internal("Unknown node".into()))?;
178            let node_name = registry
179                .get(node_ref)
180                .map(|node| node.name.clone())
181                .unwrap_or_else(|| node_ref.clone());
182            let (schema, handshake) = fetch_node_schema(&address, &node_name).await?;
183            registry.set_last_seen(node_ref);
184            registry.set_node_id(node_ref, handshake.node_id);
185            registry.save()?;
186            let contents = schema.to_toml_string().map_err(|err| {
187                IntersticeError::Internal(format!("Failed to serialize schema: {err}"))
188            })?;
189            std::fs::write(out_path, contents).map_err(|err| {
190                IntersticeError::Internal(format!("Failed to write schema: {err}"))
191            })?;
192            println!("Schema written to {}", out_path.display());
193        }
194        _ => print_node_help(),
195    }
196
197    Ok(())
198}
199
200fn print_node_help() {
201    println!("USAGE:");
202    println!("  interstice node add <name> <address>");
203    println!("  interstice node create <name> <port>");
204    println!("  interstice node list");
205    println!("  interstice node remove <name|id>");
206    println!("  interstice node rename <old> <new>");
207    println!("  interstice node show <name|id>");
208    println!("  interstice node start <name|id>");
209    println!("  interstice node ping <name|id>");
210    println!("  interstice node schema <name|id> [out]");
211}