tsain-pattern 0.1.0

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

/// # Contains List of (Ts prop name, Ts-type): For Struct or Variant
pub type PropsPattern = Composition<Vec<(&'static str, &'static str)>>;

impl PropsPattern {
    pub fn list(&self) -> &Vec<(&'static str, &'static str)> {
        &self.list
    }

    /// Comma joined props names
    fn prop_names(&self) -> String {
        self.list
            .iter()
            .map(|(n, _)| *n)
            .collect::<Vec<_>>()
            .join(", ")
    }

    /// Comma joined props types
    fn prop_types(&self) -> String {
        self.list
            .iter()
            .map(|(_, t)| *t)
            .collect::<Vec<_>>()
            .join(", ")
    }

    /// comma joined props' "name: type"
    fn prop_name2types(&self) -> String {
        self.list
            .iter()
            .map(|(n, t)| format!("{n}: {t}"))
            .collect::<Vec<_>>()
            .join(", ")
    }
}

/// Default formatting
impl PropsPattern {
    fn default_declaration_script(&self, var_id: Option<u32>) -> String {
        let ts_name = &self.ts_name;
        let prop_types = self.prop_types();
        let type_args = crate::format_type_args(&self.type_args);

        let brands_script = self.build_brands_script();

        match var_id {
            None => format!(r#"export type {ts_name}{type_args} = [{prop_types}]{brands_script};"#),
            Some(var_id) => format!(
                r#"export type {ts_name}{type_args} = [{var_id}, [{prop_types}]]{brands_script};"#
            ),
        }
    }

    fn default_constructor_name(&self) -> String {
        format!("{}_", self.ts_name)
    }

    fn default_constructor_script(&self, var_id: Option<u32>) -> String {
        let ts_name = &self.ts_name;
        let type_args = crate::format_type_args(&self.type_args);
        let constructor_name = self.default_constructor_name();

        let prop_names = self.prop_names();
        let prop_name2types = self.prop_name2types();

        match var_id {
            None => format!(
                "export const {constructor_name} = {type_args}({prop_name2types}) => [{prop_names}] as {ts_name}{type_args};"
            ),
            Some(var_id) => format!(
                "export const {constructor_name} = {type_args}({prop_name2types}) => [{var_id}, [{prop_names}]] as {ts_name}{type_args};"
            ),
        }
    }

    fn default_prop_getter_script(
        &self,
        prop_name: &str,
        prop_type: &str,
        prop_idx: usize,
        is_variant: bool,
    ) -> String {
        let ts_name = &self.ts_name;
        let type_args = crate::format_type_args(&self.type_args);

        // prop id
        let mut script = format!("export const {ts_name}_{prop_name}_id: number = {prop_idx};\n");

        // getter
        let getter = match is_variant {
            false => {
                format!(
                    "export const {ts_name}_{prop_name} = {type_args}(v: {ts_name}{type_args}): {prop_type} => v[{prop_idx}];\n"
                )
            }
            true => {
                format!(
                    "export const {ts_name}_{prop_name} = {type_args}(v: {ts_name}{type_args}): {prop_type} => v[1][{prop_idx}];\n"
                )
            }
        };
        script.push_str(&getter);
        return script;
    }

    fn default_prop_getters_script(&self, is_variant: bool) -> String {
        self.list
            .iter()
            .enumerate()
            .map(|(i, (name, ty))| self.default_prop_getter_script(name, ty, i, is_variant))
            .collect::<Vec<_>>()
            .join("\n")
    }

    pub(crate) fn default_as_variant_script(
        &self,
        enum_ts_name: &str,
        enum_type_args: &str,
        var_id: u32,
    ) -> String {
        let ts_name = self.ts_name;
        let type_args = crate::format_type_args(&self.type_args);
        format!(
            "export const as{ts_name} = {enum_type_args}(e: {enum_ts_name}{enum_type_args}): {ts_name}{type_args} | undefined => e[0] == {var_id} ? e as {ts_name}{type_args} : undefined;"
        )
    }
}

impl PropsFormatter for DefaultPropsFormatter {
    fn props_head_script(&self, props: &PropsPattern, var_id: Option<u32>) -> String {
        let ts_name = &props.ts_name;
        match var_id {
            None => format!("// # Struct {ts_name}"),
            Some(var_id) => format!("// ## Variant {var_id}: {ts_name}"),
        }
    }

    fn props_script_list(
        &self,
        props: &PropsPattern,
        var_id: Option<u32>,
    ) -> Vec<(String, String)> {
        vec![
            (
                // 1. type Declaration
                format!("// 1. Type"),
                props.default_declaration_script(var_id),
            ),
            (
                // 2. Constructor
                format!("// 2. Constructor"),
                props.default_constructor_script(var_id),
            ),
            (
                // 3. Prop(Field) Getters
                format!("// 3. Getters"),
                props.default_prop_getters_script(var_id.is_some()),
            ),
        ]
    }

    fn as_variant_script(
        &self,
        props: &PropsPattern,
        enum_ts_name: &str,
        enum_type_args: &str,
        var_id: u32,
    ) -> String {
        props.default_as_variant_script(enum_ts_name, enum_type_args, var_id)
    }
}