specta 2.0.0-rc.9

Easily export your Rust types to other languages
Documentation
use std::{borrow::Cow, collections::BTreeMap, path::Path};

use crate::{
    ts::{self, ExportConfig, ExportError},
    TypeMap,
};

use super::get_types;

/// Exports all types in the [`TYPES`](static@crate::export::TYPES) map to the provided TypeScript file.
pub fn ts<P: AsRef<Path>>(path: P) -> Result<(), ExportError> {
    ts_with_cfg(path, Cow::Borrowed(""), &ExportConfig::default())
}

/// Exports all types in the [`TYPES`](static@crate::export::TYPES) map to the provided TypeScript file but allow you to provide a configuration for the exporter.
pub fn ts_with_cfg<P: AsRef<Path>>(
    path: P,
    // TODO: Do we want this here or throw it into `ExportConfig` for Specta v2?
    header: Cow<'static, str>,
    conf: &ExportConfig,
) -> Result<(), ExportError> {
    let mut out = format!("{header} // This file has been generated by Specta. DO NOT EDIT.\n\n");

    // We sort by name to detect duplicate types BUT also to ensure the output is deterministic. The SID can change between builds so is not suitable for this.
    let types = get_types().collect::<BTreeMap<_, _>>();

    // This is a clone of `detect_duplicate_type_names` but using a `BTreeMap` for deterministic ordering
    let mut map = BTreeMap::new();
    for (sid, dt) in &types {
        if let Some(ext) = &dt.ext {
            if let Some((existing_sid, existing_impl_location)) =
                map.insert(dt.name.clone(), (sid, ext.impl_location))
            {
                if existing_sid != sid {
                    return Err(ExportError::DuplicateTypeName(
                        dt.name.clone(),
                        ext.impl_location,
                        existing_impl_location,
                    ));
                }
            }
        }
    }

    for (_, typ) in types.iter() {
        out += &ts::export_named_datatype(
            conf,
            typ,
            // TODO: Do this without using private API's
            &TypeMap {
                map: types
                    .iter()
                    .map(|(k, v)| (*k, Some(v.clone())))
                    .collect::<BTreeMap<_, _>>(),
                flatten_stack: vec![],
            },
        )?;
        out += "\n\n";
    }

    std::fs::write(path, out).map_err(Into::into)
}