kegani 0.1.0

A developer-friendly, ergonomic, production-ready Rust web framework
Documentation
//! OpenAPI document builder for Kegani
//!
//! Provides utilities for building OpenAPI 3.0 documentation.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// OpenAPI 3.0 document
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenApiDoc {
    pub openapi: String,
    pub info: InfoObject,
    pub servers: Vec<ServerObject>,
    pub paths: HashMap<String, PathItem>,
    pub components: ComponentsObject,
    pub tags: Vec<TagObject>,
}

impl OpenApiDoc {
    /// Create a new OpenAPI document
    pub fn new(title: &str, version: &str) -> Self {
        Self {
            openapi: "3.0.3".to_string(),
            info: InfoObject {
                title: title.to_string(),
                description: None,
                version: version.to_string(),
            },
            servers: vec![],
            paths: HashMap::new(),
            components: ComponentsObject::new(),
            tags: vec![],
        }
    }

    /// Add a server
    pub fn server(mut self, url: &str) -> Self {
        self.servers.push(ServerObject {
            url: url.to_string(),
            description: None,
        });
        self
    }

    /// Add a path
    pub fn path(mut self, path: &str, item: PathItem) -> Self {
        self.paths.insert(path.to_string(), item);
        self
    }

    /// Add a tag
    pub fn tag(mut self, name: &str, description: &str) -> Self {
        self.tags.push(TagObject {
            name: name.to_string(),
            description: Some(description.to_string()),
        });
        self
    }

    /// Add a schema
    pub fn schema(mut self, name: &str, schema: SchemaObject) -> Self {
        self.components.schemas.insert(name.to_string(), schema);
        self
    }
}

/// Info object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InfoObject {
    pub title: String,
    pub description: Option<String>,
    pub version: String,
}

/// Server object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerObject {
    pub url: String,
    pub description: Option<String>,
}

/// Path item
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathItem {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub get: Option<OperationObject>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub post: Option<OperationObject>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub put: Option<OperationObject>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub delete: Option<OperationObject>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub patch: Option<OperationObject>,
}

/// Operation object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperationObject {
    pub summary: Option<String>,
    pub description: Option<String>,
    pub operation_id: Option<String>,
    pub tags: Option<Vec<String>>,
    pub request_body: Option<RequestBodyObject>,
    pub responses: HashMap<String, ResponseObject>,
    pub parameters: Option<Vec<ParameterObject>>,
}

/// Request body object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestBodyObject {
    pub required: bool,
    pub content: HashMap<String, MediaTypeObject>,
}

/// Media type object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MediaTypeObject {
    pub schema: SchemaObject,
}

/// Response object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseObject {
    pub description: String,
    pub content: Option<HashMap<String, MediaTypeObject>>,
}

/// Parameter object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterObject {
    pub name: String,
    pub location: String, // "path", "query", "header", "cookie"
    pub required: bool,
    pub schema: SchemaObject,
    pub description: Option<String>,
}

/// Components object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentsObject {
    pub schemas: HashMap<String, SchemaObject>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub security_schemes: Option<HashMap<String, SecuritySchemeObject>>,
}

impl ComponentsObject {
    pub fn new() -> Self {
        Self {
            schemas: HashMap::new(),
            security_schemes: None,
        }
    }
}

/// Schema object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaObject {
    pub type_: Option<String>,
    pub format: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<HashMap<String, SchemaObject>>,
    pub items: Option<Box<SchemaObject>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub required: Option<Vec<String>>,
}

/// Security scheme object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecuritySchemeObject {
    pub type_: String,
    pub scheme: Option<String>,
    pub bearer_format: Option<String>,
}

/// Tag object
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TagObject {
    pub name: String,
    pub description: Option<String>,
}

/// OpenAPI builder
pub struct OpenApiBuilder {
    doc: OpenApiDoc,
}

impl OpenApiBuilder {
    /// Create a new builder
    pub fn new(title: &str, version: &str) -> Self {
        Self {
            doc: OpenApiDoc::new(title, version),
        }
    }

    /// Add a server
    pub fn server(mut self, url: &str) -> Self {
        self.doc = self.doc.server(url);
        self
    }

    /// Add a tag
    pub fn tag(mut self, name: &str, description: &str) -> Self {
        self.doc = self.doc.tag(name, description);
        self
    }

    /// Add a GET path
    pub fn path_get(mut self, path: &str, summary: &str) -> Self {
        let mut item = PathItem::default();
        item.get = Some(OperationObject {
            summary: Some(summary.to_string()),
            description: None,
            operation_id: None,
            tags: None,
            request_body: None,
            responses: HashMap::new(),
            parameters: None,
        });
        self.doc = self.doc.path(path, item);
        self
    }

    /// Add a schema
    pub fn schema<T: serde::Serialize>(mut self, name: &str) -> Self {
        // Basic schema generation from type
        let schema = SchemaObject {
            type_: Some("object".to_string()),
            format: None,
            properties: None,
            items: None,
            required: None,
        };
        self.doc = self.doc.schema(name, schema);
        self
    }

    /// Build the OpenAPI document
    pub fn build(self) -> OpenApiDoc {
        self.doc
    }
}

impl Default for PathItem {
    fn default() -> Self {
        Self {
            get: None,
            post: None,
            put: None,
            delete: None,
            patch: None,
        }
    }
}