Skip to content

Commit 61d24cd

Browse files
committed
Merge branch 'master'
2 parents aa5483c + b5252b2 commit 61d24cd

File tree

12 files changed

+1131
-693
lines changed

12 files changed

+1131
-693
lines changed

daemon/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2024"
66
[dependencies]
77
config = "0.15.8"
88
daemonize = "0.5.0"
9+
serde = { version = "1.0.219", features = ["derive"] }
910
serde_json = "1.0.140" # Aligned with models and lib
1011
uuid = { version = "1.3.1", features = ["serde", "v4", "v1", "rng"] } # Aligned with models and security
1112
prost = "0.13.0" # Latest stable based on crates.io search on 2025-07-01
@@ -23,6 +24,9 @@ prctl = "1.0.0"
2324
proctitle = "0.1.1"
2425
users = "0.11.0"
2526
bytemuck = "1.21.0"
27+
thiserror = "2.0.12" # Consistent
28+
lazy_static = "1.5.0"
29+
regex = "1.11.1"
2630

2731
[lib]
2832
name = "daemon"

daemon_api/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@ daemonize = "0.5.0" # Consistent
1212
daemon = { path = "../daemon" }
1313
config = "0.15.8" # Consistent
1414
lazy_static = "1.5.0" # Aligned with lib
15+
clap = { version = "4.5.29", features = ["derive"] } # Added clap
16+
anyhow = "1.0.98" # Added anyhow for FromStr error type
17+
reqwest = { version = "0.12.22", features = ["json"] } # Added with json feature
18+
thiserror = "2.0.12" # Consistent
1519
tokio = { version = "1.44.2", features = ["net", "time"] } # Updated to latest stable, retaining specific features
20+
lib = { path = "../lib" }
21+
22+

daemon_api/src/cli_schema.rs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// daemon_api/src/cli_schema.rs
2+
3+
use clap::{Parser, Subcommand, Args};
4+
use std::path::PathBuf;
5+
use lib::storage_engine::config::StorageEngineType;
6+
7+
#[derive(Parser, Debug)]
8+
#[clap(author, version, about = "GraphDB CLI for managing graph data and daemons", long_about = None)]
9+
pub struct CliArgs {
10+
#[clap(long)]
11+
pub cli: bool,
12+
#[clap(long, conflicts_with = "cli")]
13+
pub query: Option<String>,
14+
#[clap(subcommand)]
15+
pub command: Option<GraphDbCommands>,
16+
}
17+
18+
#[derive(Subcommand, Debug)]
19+
pub enum GraphDbCommands {
20+
Start(StartArgs),
21+
Stop(StopArgs),
22+
Status(StatusArgs),
23+
Daemon(DaemonCommandWrapper),
24+
Rest(RestCommandWrapper),
25+
Storage(StorageActionWrapper),
26+
#[clap(arg_required_else_help = true)]
27+
ViewGraph {
28+
#[clap(long)]
29+
graph_id: Option<String>,
30+
},
31+
#[clap(arg_required_else_help = true)]
32+
ViewGraphHistory {
33+
#[clap(long)]
34+
graph_id: Option<String>,
35+
#[clap(long)]
36+
start_date: Option<String>,
37+
#[clap(long)]
38+
end_date: Option<String>,
39+
},
40+
#[clap(arg_required_else_help = true)]
41+
IndexNode {
42+
#[clap(long)]
43+
node_id: Option<String>,
44+
},
45+
#[clap(arg_required_else_help = true)]
46+
CacheNodeState {
47+
#[clap(long)]
48+
node_id: Option<String>,
49+
},
50+
Help(HelpArgs),
51+
}
52+
53+
#[derive(Args, Debug)]
54+
pub struct StartArgs {
55+
#[clap(long, conflicts_with = "cluster")]
56+
pub port: Option<u16>,
57+
#[clap(long)]
58+
pub cluster: Option<String>,
59+
#[clap(long)]
60+
pub listen_port: Option<u16>,
61+
#[clap(long)]
62+
pub storage_port: Option<u16>,
63+
#[clap(long)]
64+
pub storage_config_file: Option<PathBuf>,
65+
#[clap(subcommand)]
66+
pub component: Option<StartComponent>,
67+
}
68+
69+
#[derive(Subcommand, Debug)]
70+
pub enum StartComponent {
71+
Rest(StartRestArgs),
72+
Storage(StartStorageArgs),
73+
}
74+
75+
#[derive(Args, Debug)]
76+
pub struct StartRestArgs {
77+
#[clap(long)]
78+
pub port: Option<u16>,
79+
}
80+
81+
#[derive(Args, Debug)]
82+
pub struct StartStorageArgs {
83+
#[clap(long)]
84+
pub port: Option<u16>,
85+
#[clap(long)]
86+
pub config_file: Option<PathBuf>,
87+
#[clap(long, value_enum)]
88+
pub engine_type: Option<StorageEngineType>,
89+
}
90+
91+
#[derive(Args, Debug)]
92+
pub struct StopArgs {
93+
#[clap(subcommand)]
94+
pub action: Option<StopAction>,
95+
}
96+
97+
#[derive(Subcommand, Debug)]
98+
pub enum StopAction {
99+
Rest,
100+
Daemon {
101+
#[clap(long)]
102+
port: Option<u16>,
103+
},
104+
Storage {
105+
#[clap(long)]
106+
port: Option<u16>,
107+
},
108+
}
109+
110+
#[derive(Args, Debug)]
111+
pub struct StatusArgs {
112+
#[clap(subcommand)]
113+
pub action: Option<StatusAction>,
114+
}
115+
116+
#[derive(Subcommand, Debug)]
117+
pub enum StatusAction {
118+
Rest,
119+
Daemon {
120+
#[clap(long)]
121+
port: Option<u16>,
122+
},
123+
Storage {
124+
#[clap(long)]
125+
port: Option<u16>,
126+
},
127+
}
128+
129+
#[derive(Args, Debug)]
130+
pub struct DaemonCommandWrapper {
131+
#[clap(subcommand)]
132+
pub command: DaemonCliCommand,
133+
}
134+
135+
#[derive(Subcommand, Debug)]
136+
pub enum DaemonCliCommand {
137+
Start {
138+
#[clap(long)]
139+
port: Option<u16>,
140+
#[clap(long)]
141+
cluster: Option<String>,
142+
},
143+
Stop {
144+
#[clap(long)]
145+
port: Option<u16>,
146+
},
147+
Status {
148+
#[clap(long)]
149+
port: Option<u16>,
150+
},
151+
List,
152+
ClearAll,
153+
}
154+
155+
#[derive(Args, Debug)]
156+
pub struct RestCommandWrapper {
157+
#[clap(subcommand)]
158+
pub command: RestCliCommand,
159+
}
160+
161+
#[derive(Subcommand, Debug)]
162+
pub enum RestCliCommand {
163+
Start {
164+
#[clap(long)]
165+
port: Option<u16>,
166+
},
167+
Stop,
168+
Status,
169+
Health,
170+
Version,
171+
RegisterUser {
172+
#[clap(long)]
173+
username: String,
174+
#[clap(long)]
175+
password: String,
176+
},
177+
Authenticate {
178+
#[clap(long)]
179+
username: String,
180+
#[clap(long)]
181+
password: String,
182+
},
183+
GraphQuery {
184+
query_string: String,
185+
#[clap(long)]
186+
persist: Option<bool>,
187+
},
188+
StorageQuery,
189+
}
190+
191+
#[derive(Args, Debug)]
192+
pub struct StorageActionWrapper {
193+
#[clap(subcommand)]
194+
pub action: StorageAction,
195+
}
196+
197+
#[derive(Subcommand, Debug)]
198+
pub enum StorageAction {
199+
Start {
200+
#[clap(long)]
201+
port: Option<u16>,
202+
#[clap(long)]
203+
config_file: Option<PathBuf>,
204+
#[clap(long, value_enum)]
205+
engine_type: Option<StorageEngineType>,
206+
},
207+
Stop {
208+
#[clap(long)]
209+
port: Option<u16>,
210+
},
211+
Status {
212+
#[clap(long)]
213+
port: Option<u16>,
214+
},
215+
}
216+
217+
#[derive(Args, Debug)]
218+
pub struct HelpArgs {
219+
pub command_path: Vec<String>,
220+
}
221+
222+
#[derive(Debug)]
223+
pub struct KVPair {
224+
pub key: String,
225+
pub value: String,
226+
}
227+
228+
#[derive(Debug)]
229+
pub struct PidStore {
230+
pub pid: u32,
231+
pub port: u16,
232+
pub component: String,
233+
}

daemon_api/src/help_generator.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// daemon_api/src/help_generator.rs
2+
3+
// This module provides functions for generating CLI help messages programmatically,
4+
// leveraging clap's capabilities.
5+
6+
use clap::CommandFactory;
7+
use crate::cli_schema::CliArgs;
8+
9+
/// Generates the full, auto-generated help message for the CLI.
10+
pub fn generate_full_help() -> String {
11+
let mut cmd = CliArgs::command(); // FIX: Use CliArgs::command() directly
12+
let mut buffer = Vec::new();
13+
cmd.write_help(&mut buffer).expect("Failed to write help to buffer");
14+
String::from_utf8(buffer).expect("Failed to convert help to UTF-8")
15+
}
16+
17+
/// Generates a filtered help message based on a command path.
18+
///
19+
/// This function attempts to get the help for a specific subcommand path.
20+
/// If the path is empty, it returns the full help.
21+
/// If the path is invalid or no specific help is found, it activates the error case.
22+
pub fn generate_help_for_path(command_path: &[String]) -> String {
23+
if command_path.is_empty() {
24+
return generate_full_help();
25+
}
26+
27+
let mut cmd = CliArgs::command(); // FIX: Use CliArgs::command() directly
28+
let mut current_subcommand = Some(&mut cmd);
29+
let mut full_path_str = String::new();
30+
31+
// Traverse the command structure to find the specific subcommand
32+
for (i, segment) in command_path.iter().enumerate() {
33+
if i > 0 {
34+
full_path_str.push(' ');
35+
}
36+
full_path_str.push_str(segment);
37+
38+
if let Some(sub) = current_subcommand.take().and_then(|c| c.find_subcommand_mut(segment)) {
39+
current_subcommand = Some(sub);
40+
} else {
41+
// If a segment is not found as a subcommand, return an error message
42+
return format!(
43+
"Error: Command path '{}' not found. Displaying general help.\n\n{}",
44+
full_path_str,
45+
generate_full_help()
46+
);
47+
}
48+
}
49+
50+
if let Some(final_cmd) = current_subcommand {
51+
let mut buffer = Vec::new();
52+
final_cmd.write_help(&mut buffer).expect("Failed to write filtered help to buffer");
53+
String::from_utf8(buffer).expect("Failed to convert filtered help to UTF-8")
54+
} else {
55+
// This case should ideally not be reached if the loop logic is correct,
56+
// but it's a fallback.
57+
format!(
58+
"Error: Could not retrieve help for '{}'. Displaying general help.\n\n{}",
59+
full_path_str,
60+
generate_full_help()
61+
)
62+
}
63+
}
64+
65+
/// Generates status information for a given command path.
66+
/// This is a mock/placeholder for now, as actual status checks involve network calls.
67+
/// In a real scenario, this would likely interact with daemon_api or other health endpoints.
68+
pub async fn generate_status_for_path(command_path: &[String]) -> String {
69+
let mut response_lines = vec![];
70+
response_lines.push(format!("--- Status for: {} ---", command_path.join(" ")));
71+
72+
// Corrected matching on &[String] directly
73+
match command_path {
74+
[] => {
75+
// Full status summary
76+
response_lines.push("Comprehensive system status:".to_string());
77+
response_lines.push(" GraphDB Daemon: Checking...".to_string());
78+
response_lines.push(" REST API: Checking...".to_string());
79+
response_lines.push(" Storage Daemon: Checking...".to_string());
80+
response_lines.push("\nNote: For detailed status, use CLI 'status <component>' or REST '/api/v1/status/<component>'".to_string());
81+
}
82+
// FIX: Removed 'ref' binding modifier
83+
[rest_str] if rest_str == "rest" => {
84+
response_lines.push("Checking REST API status...".to_string());
85+
// In a real implementation, make an HTTP call to the REST API's /health endpoint
86+
// For now, a placeholder:
87+
response_lines.push(" REST API: Mock Status - Running".to_string());
88+
}
89+
// FIX: Removed 'ref' binding modifier
90+
[daemon_str] if daemon_str == "daemon" => {
91+
response_lines.push("Checking GraphDB Daemon status (all common ports)...".to_string());
92+
// Placeholder for checking multiple daemon ports
93+
response_lines.push(" Daemon (8080): Mock Status - Running".to_string());
94+
response_lines.push(" Daemon (9001): Mock Status - Down".to_string());
95+
}
96+
// FIX: Removed 'ref' binding modifier
97+
[daemon_str, port_str] if daemon_str == "daemon" => {
98+
if let Ok(port) = port_str.parse::<u16>() {
99+
response_lines.push(format!("Checking GraphDB Daemon status on port {}...", port));
100+
// Placeholder for checking a specific daemon port
101+
response_lines.push(format!(" Daemon ({}): Mock Status - Running", port));
102+
} else {
103+
response_lines.push(format!("Invalid port specified: {}", port_str));
104+
}
105+
}
106+
// FIX: Removed 'ref' binding modifier
107+
[storage_str] if storage_str == "storage" => {
108+
response_lines.push("Checking Storage Daemon status...".to_string());
109+
// Placeholder for checking storage daemon status
110+
response_lines.push(" Storage Daemon: Mock Status - Running (Sled)".to_string());
111+
}
112+
// FIX: Removed 'ref' binding modifier
113+
[storage_str, port_str] if storage_str == "storage" => {
114+
if let Ok(port) = port_str.parse::<u16>() {
115+
response_lines.push(format!("Checking Storage Daemon status on port {}...", port));
116+
// Placeholder for checking a specific storage daemon port
117+
response_lines.push(format!(" Storage Daemon ({}): Mock Status - Running (RocksDB)", port));
118+
} else {
119+
response_lines.push(format!("Invalid port specified: {}", port_str));
120+
}
121+
}
122+
_ => {
123+
response_lines.push(format!("Unknown status request: {:?}. Displaying general status summary.", command_path));
124+
response_lines.push(" GraphDB Daemon: Checking...".to_string());
125+
response_lines.push(" REST API: Checking...".to_string());
126+
response_lines.push(" Storage Daemon: Checking...".to_string());
127+
}
128+
}
129+
response_lines.join("\n")
130+
}
131+

0 commit comments

Comments
 (0)