interoptopus_backend_cpython 0.14.25

Generates CPython bindings.
Documentation
use crate::{DocConfig, PythonWriter};
use interoptopus::lang::c::{CType, CompositeType, Function};
use interoptopus::patterns::{LibraryPattern, TypePattern};
use interoptopus::writer::{IndentWriter, WriteFor};
use interoptopus::{indented, non_service_functions};
use interoptopus::{Error, Inventory};
use std::fs::File;
use std::path::Path;

pub struct DocGenerator<'a, W> {
    inventory: &'a Inventory,
    python_writer: &'a W,
    doc_config: DocConfig,
}

impl<'a, W: PythonWriter> DocGenerator<'a, W> {
    pub fn new(inventory: &'a Inventory, w: &'a W, config: DocConfig) -> Self {
        Self {
            inventory,
            python_writer: w,
            doc_config: config,
        }
    }

    pub fn inventory(&self) -> &Inventory {
        self.inventory
    }

    pub fn config(&self) -> &DocConfig {
        &self.doc_config
    }

    pub fn write_toc(&self, w: &mut IndentWriter) -> Result<(), Error> {
        indented!(w, r#"## API Overview"#)?;

        w.newline()?;
        indented!(w, r#"### Functions"#)?;
        indented!(w, r#"Freestanding callables inside the module."#)?;

        for the_type in non_service_functions(self.inventory) {
            let doc = the_type.meta().documentation().lines().first().cloned().unwrap_or_default();

            indented!(w, r#" - **[{}](#{})** - {}"#, the_type.name(), the_type.name(), doc)?;
        }

        w.newline()?;
        indented!(w, r#"### Classes"#)?;
        indented!(w, r#"Methods operating on common state."#)?;

        for pattern in self.inventory.patterns().iter().map(|x| match x {
            LibraryPattern::Service(s) => s,
        }) {
            let prefix = pattern.common_prefix();
            let doc = pattern.the_type().meta().documentation().lines().first().cloned().unwrap_or_default();
            let name = pattern.the_type().rust_name();

            indented!(w, r#" - **[{}](#{})** - {}"#, name, name, doc)?;

            for x in pattern.constructors() {
                let target = format!("{}.{}", name, x.name().replace(&prefix, ""));
                let doc = x.meta().documentation().lines().first().cloned().unwrap_or_default();
                indented!(w, r#"     - **[{}](#{})** <sup>**ctor**</sup> - {}"#, x.name().replace(&prefix, ""), target, doc)?;
            }
            for x in pattern.methods() {
                let target = format!("{}.{}", name, x.name().replace(&prefix, ""));
                let doc = x.meta().documentation().lines().first().cloned().unwrap_or_default();
                indented!(w, r#"     - **[{}](#{})** - {}"#, x.name().replace(&prefix, ""), target, doc)?;
            }
        }

        w.newline()?;
        indented!(w, r#"### Enums"#)?;
        indented!(w, r#"Groups of related constants."#)?;

        for the_type in self.inventory.ctypes().iter().filter_map(|x| match x {
            CType::Enum(e) => Some(e),
            _ => None,
        }) {
            let doc = the_type.meta().documentation().lines().first().cloned().unwrap_or_default();
            indented!(w, r#" - **[{}](#{})** - {}"#, the_type.rust_name(), the_type.rust_name(), doc)?;
        }

        w.newline()?;
        indented!(w, r#"### Data Structs"#)?;
        indented!(w, r#"Composite data used by functions and methods."#)?;

        for the_type in self.inventory.ctypes().iter() {
            match the_type {
                CType::Composite(c) => {
                    let doc = c.meta().documentation().lines().first().cloned().unwrap_or_default();
                    indented!(w, r#" - **[{}](#{})** - {}"#, c.rust_name(), c.rust_name(), doc)?;
                }
                CType::Pattern(p @ TypePattern::Option(_)) => {
                    let c = p.fallback_type().as_composite_type().cloned().unwrap();
                    indented!(w, r#" - **[{}](#{})** - A boolean flag and optionally data."#, c.rust_name(), c.rust_name())?;
                }
                CType::Pattern(p @ TypePattern::Slice(_)) => {
                    let c = p.fallback_type().as_composite_type().cloned().unwrap();
                    indented!(w, r#" - **[{}](#{})** - A pointer and length of un-owned elements."#, c.rust_name(), c.rust_name())?;
                }
                _ => continue,
            }
        }

        Ok(())
    }

    pub fn write_types(&self, w: &mut IndentWriter) -> Result<(), Error> {
        indented!(w, r#"# Types "#)?;

        for the_type in self.inventory().ctypes() {
            match the_type {
                CType::Composite(e) => self.write_composite(w, e)?,
                CType::Pattern(p @ TypePattern::Option(_)) => self.write_composite(w, p.fallback_type().as_composite_type().unwrap())?,
                CType::Pattern(p @ TypePattern::Slice(_)) => self.write_composite(w, p.fallback_type().as_composite_type().unwrap())?,
                _ => continue,
            };

            w.newline()?;
            indented!(w, r#"---"#)?;
            w.newline()?;
        }

        Ok(())
    }

    pub fn write_composite(&self, w: &mut IndentWriter, composite: &CompositeType) -> Result<(), Error> {
        let meta = composite.meta();

        w.newline()?;
        w.newline()?;

        indented!(w, r#" ### <a name="{}">**{}**</a>"#, composite.rust_name(), composite.rust_name())?;
        w.newline()?;

        for line in meta.documentation().lines() {
            indented!(w, r#"{}"#, line.trim())?;
        }

        w.newline()?;

        indented!(w, r#"#### Fields "#)?;
        for f in composite.fields() {
            let doc = f.documentation().lines().join("\n");
            indented!(w, r#"- **{}** - {} "#, f.name(), doc)?;
        }

        indented!(w, r#"#### Definition "#)?;
        indented!(w, r#"```python"#)?;
        self.python_writer.write_struct(w, composite, WriteFor::Docs)?;
        indented!(w, r#"```"#)?;

        Ok(())
    }

    pub fn write_enums(&self, w: &mut IndentWriter) -> Result<(), Error> {
        indented!(w, r#"# Enums "#)?;

        for the_type in self.inventory().ctypes() {
            let the_enum = match the_type {
                CType::Enum(e) => e,
                _ => continue,
            };

            let meta = the_enum.meta();

            w.newline()?;
            w.newline()?;

            indented!(w, r#" ### <a name="{}">**{}**</a>"#, the_type.name_within_lib(), the_type.name_within_lib())?;
            w.newline()?;

            for line in meta.documentation().lines() {
                indented!(w, r#"{}"#, line.trim())?;
            }
            w.newline()?;

            indented!(w, r#"#### Variants "#)?;
            for v in the_enum.variants() {
                let doc = v.documentation().lines().join("\n");
                indented!(w, r#"- **{}** - {} "#, v.name(), doc)?;
            }

            indented!(w, r#"#### Definition "#)?;
            indented!(w, r#"```python"#)?;
            self.python_writer.write_enum(w, the_enum, WriteFor::Docs)?;
            indented!(w, r#"```"#)?;
            w.newline()?;
            indented!(w, r#"---"#)?;
            w.newline()?;
        }

        Ok(())
    }

    pub fn write_functions(&self, w: &mut IndentWriter) -> Result<(), Error> {
        indented!(w, r#"# Functions"#)?;

        for the_type in non_service_functions(self.inventory()) {
            self.write_function(w, the_type)?;
        }

        Ok(())
    }

    fn write_function(&self, w: &mut IndentWriter, function: &Function) -> Result<(), Error> {
        indented!(w, r#"## {} "#, function.name())?;

        for line in function.meta().documentation().lines() {
            if line.trim().starts_with('#') {
                write!(w.writer(), "##")?;
            }
            indented!(w, r#"{}"#, line.trim())?;
        }

        indented!(w, r#"#### Definition "#)?;
        indented!(w, r#"```python"#)?;
        self.python_writer.write_function(w, function, WriteFor::Docs)?;
        indented!(w, r#"```"#)?;
        w.newline()?;
        indented!(w, r#"---"#)?;
        w.newline()?;

        Ok(())
    }

    pub fn write_services(&self, w: &mut IndentWriter) -> Result<(), Error> {
        indented!(w, r#"# Services"#)?;

        for pattern in self.inventory.patterns().iter().map(|x| match x {
            LibraryPattern::Service(s) => s,
        }) {
            let prefix = pattern.common_prefix();
            let doc = pattern.the_type().meta().documentation().lines();
            let class_name = pattern.the_type().rust_name();

            indented!(w, r#"## <a name="{}">**{}**</a> <sup>ctor</sup>"#, class_name, class_name)?;

            for line in doc {
                let line = line.replace(" # ", " #### ");
                let line = line.replace(" ## ", " ##### ");
                let line = line.replace(" ### ", " ###### ");

                indented!(w, r#"{}"#, line)?;
            }

            for x in pattern.constructors() {
                let fname = x.name().replace(&prefix, "");
                let target = format!("{}.{}", class_name, fname);
                indented!(w, r#"### <a name="{}">**{}**</a> <sup>ctor</sup>"#, target, fname)?;

                let doc = x.meta().documentation().lines();
                for line in doc {
                    let line = line.replace(" # ", " #### ");
                    let line = line.replace(" ## ", " ##### ");
                    let line = line.replace(" ### ", " ###### ");
                    indented!(w, r#"{}"#, line)?;
                }

                w.newline()?;
                indented!(w, r#"#### Definition "#)?;
                indented!(w, r#"```python"#)?;
                indented!(w, r#"class {}:"#, class_name)?;
                w.newline()?;
                self.python_writer.write_pattern_class_ctor(w, pattern, x, WriteFor::Docs)?;
                indented!(w, [_ _], r#"..."#)?;
                indented!(w, r#"```"#)?;
                w.newline()?;
                indented!(w, r#"---"#)?;
                w.newline()?;
            }

            for x in pattern.methods() {
                let fname = x.name().replace(&prefix, "");
                let target = format!("{}.{}", class_name, fname);
                indented!(w, r#"### <a name="{}">**{}**</a>"#, target, fname)?;

                let doc = x.meta().documentation().lines();
                for line in doc {
                    let line = line.replace(" # ", " #### ");
                    let line = line.replace(" ## ", " ##### ");
                    let line = line.replace(" ### ", " ###### ");
                    indented!(w, r#"{}"#, line)?;
                }

                w.newline()?;
                indented!(w, r#"#### Definition "#)?;
                indented!(w, r#"```python"#)?;
                indented!(w, r#"class {}:"#, class_name)?;
                w.newline()?;
                self.python_writer.write_pattern_class_method(w, pattern, x, WriteFor::Docs)?;
                indented!(w, [_ _], r#"..."#)?;
                indented!(w, r#"```"#)?;
                w.newline()?;
                indented!(w, r#"---"#)?;
                w.newline()?;
            }

            w.newline()?;
            w.newline()?;
        }

        Ok(())
    }

    pub fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> {
        writeln!(w.writer(), "{}", self.config().header)?;

        self.write_toc(w)?;
        self.write_types(w)?;
        self.write_enums(w)?;
        self.write_functions(w)?;
        self.write_services(w)?;

        w.newline()?;

        Ok(())
    }

    pub fn write_file<P: AsRef<Path>>(&self, file_name: P) -> Result<(), Error> {
        let mut file = File::create(file_name)?;
        let mut writer = IndentWriter::new(&mut file);

        self.write_to(&mut writer)
    }
}