pulseengine_mcp_cli/
utils.rs1use crate::CliError;
4use std::path::Path;
5
6#[cfg(feature = "cli")]
8pub fn parse_cargo_toml<P: AsRef<Path>>(path: P) -> Result<CargoToml, CliError> {
9 use std::fs;
10
11 let content = fs::read_to_string(path)
12 .map_err(|e| CliError::configuration(format!("Failed to read Cargo.toml: {e}")))?;
13
14 let cargo_toml: CargoToml = toml::from_str(&content)
15 .map_err(|e| CliError::configuration(format!("Failed to parse Cargo.toml: {e}")))?;
16
17 Ok(cargo_toml)
18}
19
20pub fn find_cargo_toml() -> Result<std::path::PathBuf, CliError> {
22 let mut current_dir = std::env::current_dir()
23 .map_err(|e| CliError::configuration(format!("Failed to get current directory: {e}")))?;
24
25 loop {
26 let cargo_toml = current_dir.join("Cargo.toml");
27 if cargo_toml.exists() {
28 return Ok(cargo_toml);
29 }
30
31 if !current_dir.pop() {
32 break;
33 }
34 }
35
36 Err(CliError::configuration(
37 "Cargo.toml not found in current directory or parents",
38 ))
39}
40
41#[cfg(feature = "cli")]
43#[derive(Debug, serde::Deserialize)]
44pub struct CargoToml {
45 pub package: Option<Package>,
46}
47
48#[cfg(feature = "cli")]
49#[derive(Debug, serde::Deserialize)]
50pub struct Package {
51 pub name: Option<String>,
52 pub version: Option<String>,
53 pub description: Option<String>,
54 pub authors: Option<Vec<String>>,
55}
56
57#[cfg(feature = "cli")]
58impl CargoToml {
59 pub fn get_name(&self) -> Option<&str> {
60 self.package.as_ref()?.name.as_deref()
61 }
62
63 pub fn get_version(&self) -> Option<&str> {
64 self.package.as_ref()?.version.as_deref()
65 }
66
67 pub fn get_description(&self) -> Option<&str> {
68 self.package.as_ref()?.description.as_deref()
69 }
70}
71
72pub mod validation {
74 use crate::CliError;
75
76 pub fn validate_port(port: u16) -> Result<(), CliError> {
78 if port == 0 {
79 return Err(CliError::configuration("Port cannot be 0"));
80 }
81 if port < 1024 {
82 tracing::warn!(
83 "Using privileged port {}, this may require elevated permissions",
84 port
85 );
86 }
87 Ok(())
88 }
89
90 pub fn validate_url(url: &str) -> Result<(), CliError> {
92 url::Url::parse(url)
93 .map_err(|e| CliError::configuration(format!("Invalid URL '{url}': {e}")))?;
94 Ok(())
95 }
96
97 pub fn validate_file_exists(path: &str) -> Result<(), CliError> {
99 if !std::path::Path::new(path).exists() {
100 return Err(CliError::configuration(format!(
101 "File does not exist: {path}"
102 )));
103 }
104 Ok(())
105 }
106
107 pub fn validate_dir_exists(path: &str) -> Result<(), CliError> {
109 let path = std::path::Path::new(path);
110 if !path.exists() {
111 return Err(CliError::configuration(format!(
112 "Directory does not exist: {}",
113 path.display()
114 )));
115 }
116 if !path.is_dir() {
117 return Err(CliError::configuration(format!(
118 "Path is not a directory: {}",
119 path.display()
120 )));
121 }
122 Ok(())
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_validate_port() {
132 use validation::*;
133
134 assert!(validate_port(8080).is_ok());
135 assert!(validate_port(80).is_ok()); assert!(validate_port(0).is_err());
137 }
138
139 #[test]
140 fn test_validate_url() {
141 use validation::*;
142
143 assert!(validate_url("https://example.com").is_ok());
144 assert!(validate_url("http://localhost:8080").is_ok());
145 assert!(validate_url("invalid-url").is_err());
146 }
147}