tslink 0.4.2

Creates typescript definitions based on rust code
Documentation
use super::Interpreter;
use crate::{
    error::E,
    interpreter::Offset,
    nature::{Composite, Nature, Natures, Referred, TypeAsString},
};
use std::{
    fs::File,
    io::{BufWriter, Write},
};

impl Interpreter for Composite {
    fn reference(
        &self,
        natures: &Natures,
        buf: &mut BufWriter<File>,
        offset: Offset,
    ) -> Result<(), E> {
        match self {
            Self::Array(ty) => {
                ty.reference(natures, buf, offset)?;
                buf.write_all("[]".as_bytes())?;
            }
            Self::Vec(_, ty) => {
                if let Some(ty) = ty {
                    ty.reference(natures, buf, offset)?;
                    buf.write_all("[]".as_bytes())?;
                } else {
                    return Err(E::Parsing(String::from(
                        "Type Vec doesn't include reference to type",
                    )));
                }
            }
            Self::HashMap(_, key, ty) => {
                if let (Some(key), Some(ty)) = (key, ty) {
                    buf.write_all("{ [key: ".as_bytes())?;
                    key.reference(natures, buf, offset.clone())?;
                    buf.write_all("]: ".as_bytes())?;
                    ty.reference(natures, buf, offset)?;
                    buf.write_all(" }".as_bytes())?;
                } else {
                    return Err(E::Parsing(String::from(
                        "Type HashMap doesn't include reference to type or key",
                    )));
                }
            }
            Self::Func(_, args, out, asyncness, constructor) => {
                buf.write_all("(".as_bytes())?;
                let mut generic = false;
                for (i, nature) in args.iter().enumerate() {
                    if let Nature::Referred(Referred::FuncArg(name, _context, nature, binding)) =
                        nature
                    {
                        buf.write_all(format!("{name}: ").as_bytes())?;
                        if let Some(ref_name) = binding {
                            buf.write_all(ref_name.as_bytes())?;
                        } else {
                            nature.reference(natures, buf, offset.clone())?;
                        }
                    } else {
                        generic = true;
                        buf.write_all(format!("arg{i}: ").as_bytes())?;
                        nature.reference(natures, buf, offset.clone())?;
                    }
                    if i < args.len() - 1 {
                        buf.write_all(", ".as_bytes())?;
                    }
                }
                if *constructor && generic {
                    return Err(E::Parsing(String::from(
                        "Constructor with generic types aren't supported",
                    )));
                }
                if *constructor {
                    buf.write_all(")".as_bytes())?;
                    return Ok(());
                }
                buf.write_all(format!("){} ", if generic { " =>" } else { ":" }).as_bytes())?;
                if *asyncness {
                    buf.write_all("Promise<".as_bytes())?;
                }
                if let Some(out) = out {
                    out.reference(natures, buf, offset.clone())?;
                } else {
                    buf.write_all("void".as_bytes())?;
                }
                if *asyncness {
                    buf.write_all(">".as_bytes())?;
                }
            }
            Self::Tuple(_, tys) => {
                buf.write_all("[".as_bytes())?;
                let last = tys.len() - 1;
                for (i, ty) in tys.iter().enumerate() {
                    ty.reference(natures, buf, offset.clone())?;
                    if i < last {
                        buf.write_all(", ".as_bytes())?;
                    }
                }
                buf.write_all("]".as_bytes())?;
            }
            Self::Option(_, ty) => {
                if let Some(ty) = ty {
                    ty.reference(natures, buf, offset)?;
                    buf.write_all(" | null".as_bytes())?;
                } else {
                    return Err(E::Parsing(String::from(
                        "Type Option doesn't include reference to type",
                    )));
                }
            }
            Self::Result(_, res, err, exception_suppression, asyncness) => {
                if let Some(res) = res {
                    res.reference(natures, buf, offset.clone())?;
                }
                if *asyncness {
                    if res.is_none() {
                        buf.write_all("void".as_bytes())?;
                    }
                    return Ok(());
                }
                let err_ext = if let Some(err) = err {
                    format!("(Error & {{ err?: {}}})", err.type_as_string()?)
                } else {
                    "Error".to_owned()
                };
                if res.is_some() && *exception_suppression {
                    buf.write_all(format!(" | {err_ext}",).as_bytes())?;
                }
                if res.is_none() && *exception_suppression {
                    buf.write_all(format!("{err_ext} | void",).as_bytes())?;
                }
                if res.is_none() && !*exception_suppression {
                    buf.write_all("void".as_bytes())?;
                }
            }
            Self::Undefined(_) => {
                buf.write_all("void".as_bytes())?;
            }
        }
        Ok(())
    }
}