tsain-pattern 0.1.0

Chain TypeScript and Rust in fast and secure way
Documentation
use super::formatter::{DefaultPropsFormatter, PropsFormatter};

mod props;
mod vars;

pub use props::*;
pub use vars::*;

/// Object Pattern
pub enum TsainPattern {
    Enum(EnumPattern),
    Struct(PropsPattern),
}

pub struct Composition<T> {
    /// Rs type name
    /// * Enum / Struct: Rs ident name
    /// * Variant: "{enum-name}{var-name}"
    rs_name: &'static str,
    /// Ts type name
    /// * Enum / Struct: Ts export name
    /// * Variant: "{enum-name}{var-name}"
    ts_name: &'static str,
    ///
    type_args: Vec<&'static str>,
    /// * Enum: vars
    /// * Struct / Variant: props
    list: T,
    /// skip default brand of "__brand"
    skip_brand: bool,
    ///
    /// [[brand-key, brand's value]]
    extra_brands: Vec<[&'static str; 2]>,
}

impl<T> Composition<T> {
    pub fn new(
        rs_name: &'static str,
        ts_name: &'static str,
        type_args: Vec<&'static str>,
        list: T,
        skip_brand: bool,
        extra_brands: Vec<[&'static str; 2]>,
    ) -> Self {
        Self {
            rs_name,
            ts_name,
            type_args,
            list,
            skip_brand,
            extra_brands,
        }
    }

    fn build_brands_script(&self) -> String {
        let mut pairs = Vec::new();
        if !self.skip_brand {
            pairs.push(format!(r#"readonly __brand: "{}""#, self.ts_name));
        }
        pairs.extend(
            self.extra_brands.iter().map(|[brand_name, brand_ts_type]| {
                format!("readonly {brand_name}: {brand_ts_type}")
            }),
        );

        if pairs.is_empty() {
            Default::default()
        } else {
            let pairs = pairs.join(", ");
            format!(" & {{{}}}", pairs)
        }
    }
}

impl TsainPattern {
    pub fn new_enum(
        rs_name: &'static str,
        ts_name: &'static str,
        type_args: Vec<&'static str>,
        // [Option<(var-"full"-ts-name, props-pattern)>]
        list: Vec<Option<(&'static str, PropsPattern)>>,
    ) -> Self {
        Self::Enum(EnumPattern::new(
            rs_name,
            ts_name,
            type_args,
            list,
            // enum itself should not have brand
            true,
            Default::default(),
        ))
    }

    pub fn new_struct(
        rs_name: &'static str,
        ts_name: &'static str,
        type_args: Vec<&'static str>,
        list: Vec<(&'static str, &'static str)>,
        skip_brand: bool,
        extra_brands: Vec<[&'static str; 2]>,
    ) -> Self {
        Self::Struct(PropsPattern::new(
            rs_name,
            ts_name,
            type_args,
            list,
            skip_brand,
            extra_brands,
        ))
    }

    pub fn rs_name(&self) -> &str {
        match self {
            Self::Enum(Composition { rs_name, .. }) | Self::Struct(Composition { rs_name, .. }) => {
                rs_name
            }
        }
    }

    pub fn format_type_script(&self) -> String {
        match self {
            Self::Enum(e) => e.format_enum_type_script(&DefaultPropsFormatter),
            Self::Struct(e) => DefaultPropsFormatter.format_type_script(e, None),
        }
    }
}