use std::path::Path;
pub struct ProjectInfo {
pub language: String,
#[allow(dead_code)]
pub version: Option<String>,
}
impl ProjectInfo {
pub fn cli_name(&self) -> &str {
match self.language.as_str() {
"python" => "tina4python",
"php" => "tina4php",
"ruby" => "tina4ruby",
"nodejs" => "tina4nodejs",
"tina4js" => "vite",
_ => "tina4",
}
}
pub fn default_port(&self) -> u16 {
match self.language.as_str() {
"python" => 7146,
"php" => 7145,
"ruby" => 7147,
"nodejs" => 7148,
"tina4js" => 5173,
_ => 7145,
}
}
}
pub fn detect_language() -> Option<ProjectInfo> {
if Path::new("pyproject.toml").exists() {
if let Ok(content) = std::fs::read_to_string("pyproject.toml") {
if content.contains("tina4") {
return Some(ProjectInfo {
language: "python".into(),
version: extract_version_toml(&content),
});
}
}
}
if Path::new("requirements.txt").exists() {
if let Ok(content) = std::fs::read_to_string("requirements.txt") {
if content.to_lowercase().contains("tina4") {
return Some(ProjectInfo {
language: "python".into(),
version: None,
});
}
}
}
if Path::new("app.py").exists() {
return Some(ProjectInfo {
language: "python".into(),
version: None,
});
}
if Path::new("composer.json").exists() {
if let Ok(content) = std::fs::read_to_string("composer.json") {
if content.contains("tina4") {
return Some(ProjectInfo {
language: "php".into(),
version: extract_version_json(&content, "version"),
});
}
}
}
if Path::new("Gemfile").exists() {
if let Ok(content) = std::fs::read_to_string("Gemfile") {
if content.contains("tina4") {
return Some(ProjectInfo {
language: "ruby".into(),
version: None,
});
}
}
}
if Path::new("tina4ruby.gemspec").exists() {
return Some(ProjectInfo {
language: "ruby".into(),
version: None,
});
}
if Path::new("package.json").exists() {
if let Ok(content) = std::fs::read_to_string("package.json") {
if content.contains("\"tina4js\"") && !content.contains("tina4-nodejs") && !content.contains("tina4nodejs") {
return Some(ProjectInfo {
language: "tina4js".into(),
version: extract_version_json(&content, "version"),
});
}
if content.contains("@tina4") || content.contains("tina4-nodejs") || content.contains("tina4nodejs") {
return Some(ProjectInfo {
language: "nodejs".into(),
version: extract_version_json(&content, "version"),
});
}
}
}
if Path::new("app.ts").exists() {
return Some(ProjectInfo {
language: "nodejs".into(),
version: None,
});
}
if Path::new("app.rb").exists() {
return Some(ProjectInfo {
language: "ruby".into(),
version: None,
});
}
if Path::new("index.php").exists() {
return Some(ProjectInfo {
language: "php".into(),
version: None,
});
}
None
}
fn extract_version_toml(content: &str) -> Option<String> {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("version") && trimmed.contains('=') {
let val = trimmed.split('=').nth(1)?.trim();
return Some(val.trim_matches('"').to_string());
}
}
None
}
fn extract_version_json(content: &str, key: &str) -> Option<String> {
let pattern = format!("\"{}\"", key);
let pos = content.find(&pattern)?;
let rest = &content[pos + pattern.len()..];
let colon = rest.find(':')?;
let after_colon = rest[colon + 1..].trim_start();
if let Some(stripped) = after_colon.strip_prefix('"') {
let end = stripped.find('"')?;
return Some(stripped[..end].to_string());
}
None
}