tsain-core 0.1.0

Chain TypeScript and Rust in fast and secure way
Documentation
use super::*;

#[derive(Default)]
pub struct TsScript {
    comment: Option<String>,
    exclusive: Vec<String>,
    exclude: Vec<String>,
}

impl TsScript {
    pub fn new() -> Self {
        Self::default()
    }

    /// Set comment
    pub fn with_comment<T: Into<String>>(mut self, comment: T) -> Self {
        self.comment.replace(comment.into());
        self
    }

    /// Set exclusive type list
    pub fn with_exclusive<T: Into<String>>(mut self, exclusive: Vec<T>) -> Self {
        self.exclusive = exclusive.into_iter().map(|x| x.into()).collect::<Vec<_>>();
        self
    }

    /// Add excluded type list
    pub fn with_exclude<T: Into<String>>(mut self, exclude: Vec<T>) -> Self {
        self.exclude.extend(exclude.into_iter().map(|x| x.into()));
        self
    }

    /// Build and return the script
    pub fn build_script(&self) -> String {
        build_ts_script_with(
            self.comment.as_ref(),
            self.exclusive.as_slice(),
            self.exclude.as_slice(),
        )
    }

    /// Create a script file at the path
    pub fn export_script(&self, path: impl AsRef<std::path::Path>) {
        let script = self.build_script();

        let path = path.as_ref();
        if let Some(parent) = path.parent() {
            std::fs::create_dir_all(parent).expect("Failed to create parent directories");
        }
        std::fs::write(path, script).expect("Failed to write Tsain TypeScript file");
        println!(
            "cargo:warning=Exported Tsain TypeScript definitions to {}",
            path.display()
        );
    }

    /// Create a script file at the path
    pub fn export(path: impl AsRef<std::path::Path>) {
        Self::new().export_script(path);
    }
}

fn build_ts_script_with(
    comment: Option<&String>,
    exclusive: &[String],
    exclude: &[String],
) -> String {
    #[cfg(not(target_arch = "wasm32"))]
    {
        let mut script = include_str!("../script/head.txt").to_owned();
        script.push_str("\n");

        // 0. Comment
        if let Some(comment) = comment {
            script.push_str(comment);
            script.push('\n');
        }

        script.push_str("\n\n");

        // 1. Tsain Pattern
        for def in inventory::iter::<TsainPatternDefinition>() {
            let pattern = (def.__pattern_fn)();
            let exclude = (!exclusive.is_empty()
                && !exclusive
                    .iter()
                    .find(|x| x.eq(&pattern.rs_name()))
                    .is_some())
                || exclude.iter().find(|x| x.eq(&pattern.rs_name())).is_some();
            if exclude {
                continue;
            }
            script.push_str(&pattern.format_type_script());
            script.push('\n');
        }

        // 2. Tsain Alias
        for def in inventory::iter::<TsainAliasDefinition>() {
            let script_ = def.__script;
            script.push_str(script_);
            script.push('\n');
        }

        return script;
    }

    #[allow(unreachable_code)]
    return String::from("CAN NOT BUILD SCRIPT IN WASM TARGET");
}

// inventory

#[cfg(not(target_arch = "wasm32"))]
pub struct TsainPatternDefinition {
    pub __pattern_fn: fn() -> TsainPattern,
}

#[cfg(not(target_arch = "wasm32"))]
inventory::collect!(TsainPatternDefinition);

#[cfg(not(target_arch = "wasm32"))]
pub struct TsainAliasDefinition {
    pub __script: &'static str,
}
#[cfg(not(target_arch = "wasm32"))]
inventory::collect!(TsainAliasDefinition);