ggen-core 26.7.2

Core graph-aware code generation engine
Documentation
//! Adapter code generation via SPARQL + Tera templates
//!
//! Extracts interface definitions from RDF spec and generates Genesis membrane bindings
//! in the target language (Rust, Erlang, etc.)

use crate::utils::error::Result;
use serde_json::json;

use super::{PartManifest, PartSpec};

/// Adapter code generator
#[derive(Clone, Debug)]
pub struct AdapterGenerator {
    // In production, would hold Tera templates and SPARQL engine
}

impl AdapterGenerator {
    /// Create a new adapter generator
    pub fn new() -> Self {
        Self {}
    }

    /// Generate adapter code from part spec and manifest
    ///
    /// # Errors
    ///
    /// Returns error if SPARQL queries fail or template rendering fails
    pub async fn generate(&self, spec: &PartSpec, manifest: &PartManifest) -> Result<String> {
        // In production:
        // 1. Parse RDF source to triplestore
        // 2. Run SPARQL CONSTRUCT queries to extract interfaces
        // 3. Render Tera templates with interface bindings
        // 4. Return generated source code

        // For now, generate stub adapter code
        match spec.target_language.as_str() {
            "rust" => self.generate_rust_adapter(spec, manifest),
            "erlang" => self.generate_erlang_adapter(spec, manifest),
            _ => Err(crate::utils::error::ggen_error!(
                "Unsupported target language: {}",
                spec.target_language
            )),
        }
    }

    fn generate_rust_adapter(&self, _spec: &PartSpec, manifest: &PartManifest) -> Result<String> {
        let mut code = String::new();
        code.push_str("//! Generated Genesis membrane adapter\n");
        code.push_str("//! Auto-generated by PartFoundry\n\n");

        code.push_str("use std::ffi::{c_void, CStr};\n\n");

        code.push_str("/// Genesis part interface\n");
        code.push_str("pub mod genesis_part {\n");
        code.push_str("    use super::*;\n\n");

        // Generate interface functions
        for iface in &manifest.interfaces {
            code.push_str(&format!("    /// Interface: {}\n", iface.name));
            code.push_str(&format!(
                "    pub extern \"C\" fn {}(input: *const c_void) -> *const c_void {{\n",
                iface.name.to_lowercase()
            ));
            code.push_str("        // Future implementation: Implement interface\n");
            code.push_str("        std::ptr::null()\n");
            code.push_str("    }\n\n");
        }

        code.push_str("}\n\n");

        code.push_str("#[no_mangle]\n");
        code.push_str("pub extern \"C\" fn part_init() {\n");
        code.push_str("    // Part initialization\n");
        code.push_str("}\n\n");

        code.push_str("#[no_mangle]\n");
        code.push_str("pub extern \"C\" fn part_fini() {\n");
        code.push_str("    // Part finalization\n");
        code.push_str("}\n");

        Ok(code)
    }

    fn generate_erlang_adapter(&self, spec: &PartSpec, manifest: &PartManifest) -> Result<String> {
        let mut code = String::new();
        code.push_str("%% Generated Genesis membrane adapter\n");
        code.push_str("%% Auto-generated by PartFoundry\n\n");

        code.push_str(&format!(
            "-module(genesis_{}).\n",
            spec.id.replace('-', "_")
        ));
        code.push_str("-export([init/0, fini/0]).\n\n");

        // Generate interface exports
        for iface in &manifest.interfaces {
            code.push_str(&format!("-export([{}/1]).\n", iface.name.to_lowercase()));
        }
        code.push('\n');

        code.push_str("init() ->\n");
        code.push_str("    ok.\n\n");

        code.push_str("fini() ->\n");
        code.push_str("    ok.\n\n");

        // Generate interface functions
        for iface in &manifest.interfaces {
            code.push_str(&format!("{}(Input) ->\n", iface.name.to_lowercase()));
            code.push_str("    {ok, Input}.\n\n");
        }

        Ok(code)
    }
}

impl Default for AdapterGenerator {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_rust_adapter_generation() {
        let gen = AdapterGenerator::new();
        let spec = PartSpec {
            id: "test-part".to_string(),
            version: "1.0.0".to_string(),
            part_type: "wasm32".to_string(),
            rdf_source: "(RDF)".to_string(),
            target_language: "rust".to_string(),
        };

        let manifest = PartManifest {
            interfaces: vec![crate::parts_foundry::InterfaceSpec {
                name: "Process".to_string(),
                params: json!({"input": "bytes"}),
                returns: json!({"output": "bytes"}),
            }],
            dependencies: Default::default(),
            config: json!({}),
        };

        let code = gen.generate(&spec, &manifest).await.unwrap();
        assert!(code.contains("Generated Genesis membrane adapter"));
        assert!(code.contains("pub extern"));
        assert!(code.contains("process"));
    }

    #[tokio::test]
    async fn test_erlang_adapter_generation() {
        let gen = AdapterGenerator::new();
        let spec = PartSpec {
            id: "test-part".to_string(),
            version: "1.0.0".to_string(),
            part_type: "beam".to_string(),
            rdf_source: "(RDF)".to_string(),
            target_language: "erlang".to_string(),
        };

        let manifest = PartManifest {
            interfaces: vec![crate::parts_foundry::InterfaceSpec {
                name: "Process".to_string(),
                params: json!({"input": "bytes"}),
                returns: json!({"output": "bytes"}),
            }],
            dependencies: Default::default(),
            config: json!({}),
        };

        let code = gen.generate(&spec, &manifest).await.unwrap();
        assert!(code.contains("Generated Genesis membrane adapter"));
        assert!(code.contains("-module("));
        assert!(code.contains("process/1"));
    }

    #[tokio::test]
    async fn test_unsupported_language() {
        let gen = AdapterGenerator::new();
        let spec = PartSpec {
            id: "test-part".to_string(),
            version: "1.0.0".to_string(),
            part_type: "wasm32".to_string(),
            rdf_source: "(RDF)".to_string(),
            target_language: "python".to_string(),
        };

        let manifest = PartManifest {
            interfaces: vec![],
            dependencies: Default::default(),
            config: json!({}),
        };

        let result = gen.generate(&spec, &manifest).await;
        assert!(result.is_err());
    }
}