oxidite-cli 2.0.2

CLI tool for the Oxidite v2 web framework
Documentation
use std::fs;
use std::path::Path;

pub fn make_model(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    // Create models directory if it doesn't exist
    let models_dir = Path::new("src/models");
    if !models_dir.exists() {
        fs::create_dir_all(models_dir)?;
    }
    
    let model_template = format!(r#"use serde::{{Deserialize, Serialize}};
use oxidite_db::{{Model, Connection, Result}};

#[derive(Debug, Serialize, Deserialize)]
pub struct {} {{
    pub id: i64,
    // Add your fields here
}}

#[async_trait::async_trait]
impl Model for {} {{
    fn table_name() -> &'static str {{
        "{}"
    }}
    
    async fn find(id: i64, conn: &dyn Connection) -> Result<Option<Self>> {{
        // TODO: Implement find
        todo!()
    }}
    
    async fn create(self, conn: &dyn Connection) -> Result<Self> {{
        // TODO: Implement create
        todo!()
    }}
    
    async fn update(&self, conn: &dyn Connection) -> Result<()> {{
        // TODO: Implement update
        todo!()
    }}
    
    async fn delete(&self, conn: &dyn Connection) -> Result<()> {{
        // TODO: Implement delete
        todo!()
    }}
}}
"#, name, name, name.to_lowercase());
    
    let filename = format!("src/models/{}.rs", name.to_lowercase());
    fs::write(&filename, model_template)?;
    
    println!("✅ Model created: {}", filename);
    Ok(())
}

pub fn make_controller(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    // Create controllers directory if it doesn't exist
    let controllers_dir = Path::new("src/controllers");
    if !controllers_dir.exists() {
        fs::create_dir_all(controllers_dir)?;
    }
    
    let controller_template = format!(r#"use oxidite_core::{{Request, Response, Result, Error}};
use serde_json::json;

pub struct {} {{}}

impl {} {{
    pub async fn index(_req: Request) -> Result<Response, Error> {{
        Ok(Response::json(json!({{
            "message": "List endpoint"
        }})))
    }}
    
    pub async fn show(_req: Request) -> Result<Response, Error> {{
        // TODO: Implement show
        Ok(Response::json(json!({{
            "message": "Show endpoint"
        }})))
    }}
    
    pub async fn create(_req: Request) -> Result<Response, Error> {{
        // TODO: Implement create
        Ok(Response::json(json!({{
            "message": "Create endpoint"
        }})))
    }}
    
    pub async fn update(_req: Request) -> Result<Response, Error> {{
        // TODO: Implement update
        Ok(Response::json(json!({{
            "message": "Update endpoint"
        }})))
    }}
    
    pub async fn destroy(_req: Request) -> Result<Response, Error> {{
        // TODO: Implement destroy
        Ok(Response::json(json!({{
            "message": "Destroy endpoint"
        }})))
    }}
}}
"#, name, name);
    
    let filename = format!("src/controllers/{}.rs", name.to_lowercase());
    fs::write(&filename, controller_template)?;
    
    println!("✅ Controller created: {}", filename);
    Ok(())
}

pub fn make_middleware(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    // Create middleware directory if it doesn't exist
    let middleware_dir = Path::new("src/middleware");
    if !middleware_dir.exists() {
        fs::create_dir_all(middleware_dir)?;
    }
    
    let middleware_template = format!(r#"use oxidite_core::{{Request, Response, Error}};
use tower::{{Service, Layer}};
use std::task::{{Context, Poll}};
use std::future::Future;
use std::pin::Pin;

#[derive(Clone)]
pub struct {}<S> {{
    inner: S,
}}

impl<S> {}<S> {{
    pub fn new(inner: S) -> Self {{
        Self {{ inner }}
    }}
}}

impl<S> Service<Request> for {}<S>
where
    S: Service<Request, Response = Response, Error = Error> + Clone + Send + 'static,
    S::Future: Send + 'static,
{{
    type Response = S::Response;
    type Error = S::Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {{
        self.inner.poll_ready(cx)
    }}

    fn call(&mut self, req: Request) -> Self::Future {{
        let mut inner = self.inner.clone();
        
        Box::pin(async move {{
            // Pre-processing
            println!("Before request");
            
            // Call inner service
            let response = inner.call(req).await?;
            
            // Post-processing
            println!("After request");
            
            Ok(response)
        }})
    }}
}}

pub struct {}Layer {{}}

impl {}Layer {{
    pub fn new() -> Self {{
        Self {{}}
    }}
}}

impl<S> Layer<S> for {}Layer {{
    type Service = {}<S>;

    fn layer(&self, inner: S) -> Self::Service {{
        {}<S>::new(inner)
    }}
}}
"#, name, name, name, name, name, name, name, name);
    
    let filename = format!("src/middleware/{}.rs", name.to_lowercase());
    fs::write(&filename, middleware_template)?;
    
    println!("✅ Middleware created: {}", filename);
    Ok(())
}

pub fn make_service(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    // Create services directory if it doesn't exist
    let services_dir = Path::new("src/services");
    if !services_dir.exists() {
        fs::create_dir_all(services_dir)?;
    }
    
    let service_template = format!(r#"use std::sync::Arc;
use oxidite_db::Connection;
use serde::{{Deserialize, Serialize}};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {}Input {{
    // Add your input fields here
}}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {}Output {{
    // Add your output fields here
}}

#[derive(Clone)]
pub struct {}Service {{
    // Add dependencies like database connection pool
    // db: Arc<dyn Connection>,
}}

impl {}Service {{
    pub fn new(/* db: Arc<dyn Connection> */) -> Self {{
        Self {{
            // db,
        }}
    }}
    
    pub async fn execute(&self, input: {}Input) -> Result<{}Output, Box<dyn std::error::Error>> {{
        // TODO: Implement business logic
        todo!()
    }}
}}
"#, name, name, name, name, name, name);
    
    let filename = format!("src/services/{}.rs", name.to_lowercase());
    fs::write(&filename, service_template)?;
    
    println!("✅ Service created: {}", filename);
    Ok(())
}

pub fn make_validator(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    // Create validators directory if it doesn't exist
    let validators_dir = Path::new("src/validators");
    if !validators_dir.exists() {
        fs::create_dir_all(validators_dir)?;
    }
    
    let validator_template = format!(r#"use serde_json::Value;
use std::collections::HashMap;

pub struct {}Validator;

impl {}Validator {{
    pub fn validate(data: &Value) -> Result<(), ValidationError> {{
        let mut errors = HashMap::new();
        
        // Add validation rules here
        // Example:
        // if let Some(field) = data.get("field_name") {{
        //     if !Self::is_valid_field(field) {{
        //         errors.insert("field_name".to_string(), "Field is invalid".to_string());
        //     }}
        // }}
        
        if !errors.is_empty() {{
            return Err(ValidationError::new(errors));
        }}
        
        Ok(())
    }}
    
    // Add helper validation methods
    // fn is_valid_field(value: &Value) -> bool {{
    //     // Implement validation logic
    //     true
    // }}
}}

#[derive(Debug)]
pub struct ValidationError {{
    pub errors: HashMap<String, String>,
}}

impl ValidationError {{
    pub fn new(errors: HashMap<String, String>) -> Self {{
        Self {{ errors }}
    }}
}}

impl std::fmt::Display for ValidationError {{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{
        write!(f, "Validation failed: {{:?}}", self.errors)
    }}
}}

impl std::error::Error for ValidationError {{}}
"#, name, name);
    
    let filename = format!("src/validators/{}.rs", name.to_lowercase());
    fs::write(&filename, validator_template)?;
    
    println!("✅ Validator created: {}", filename);
    Ok(())
}