use anyhow::{Context, Result};
use colored::Colorize;
use std::fs;
use std::path::{Path, PathBuf};
pub mod cargo;
pub mod format;
pub mod git;
pub fn validate_service_name(name: &str) -> Result<()> {
if name.is_empty() {
anyhow::bail!("Service name cannot be empty");
}
if !name
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
{
anyhow::bail!(
"Service name must be kebab-case (lowercase letters, numbers, hyphens only)\n\n\
Valid examples:\n\
• my-service\n\
• user-api\n\
• auth-service-v2"
);
}
if name.starts_with('-') || name.ends_with('-') {
anyhow::bail!("Service name cannot start or end with a hyphen");
}
if name.len() > 64 {
anyhow::bail!("Service name must be 64 characters or less");
}
Ok(())
}
pub fn create_dir_all(path: &Path) -> Result<()> {
fs::create_dir_all(path)
.with_context(|| format!("Failed to create directory: {}", path.display()))
}
pub fn write_file(path: &Path, content: &str) -> Result<()> {
if let Some(parent) = path.parent() {
create_dir_all(parent)?;
}
fs::write(path, content).with_context(|| format!("Failed to write file: {}", path.display()))
}
#[allow(dead_code)]
pub fn is_dir_empty(path: &Path) -> Result<bool> {
if !path.exists() {
return Ok(true);
}
if !path.is_dir() {
anyhow::bail!("Path exists but is not a directory: {}", path.display());
}
let entries = fs::read_dir(path)
.with_context(|| format!("Failed to read directory: {}", path.display()))?;
Ok(entries.count() == 0)
}
pub fn find_project_root() -> Result<PathBuf> {
let current_dir = std::env::current_dir().context("Failed to get current directory")?;
let mut dir = current_dir.as_path();
loop {
let cargo_toml = dir.join("Cargo.toml");
if cargo_toml.exists() {
return Ok(dir.to_path_buf());
}
match dir.parent() {
Some(parent) => dir = parent,
None => anyhow::bail!(
"Could not find Cargo.toml in current directory or any parent directory"
),
}
}
}
pub fn success(message: &str) {
println!("{} {}", "✓".green().bold(), message);
}
#[allow(dead_code)]
pub fn info(message: &str) {
println!("{} {}", "→".blue().bold(), message);
}
pub fn warning(message: &str) {
println!("{} {}", "⚠".yellow().bold(), message);
}
#[allow(dead_code)]
pub fn error(message: &str) {
eprintln!("{} {}", "✗".red().bold(), message);
}
#[allow(dead_code)]
pub fn section(title: &str) {
println!("\n{}", title.bold().underline());
}