From bf3a0937cceb29eca11df207076b9e1b942ba7bb Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Fri, 6 Dec 2024 13:06:14 +0800 Subject: [PATCH] upgrade rust exporter and add `Typescript::format` --- specta-rust/src/lib.rs | 69 +++++++++++++++++------------ specta-typescript/src/typescript.rs | 25 +++++++---- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/specta-rust/src/lib.rs b/specta-rust/src/lib.rs index 194ea1a..fe14054 100644 --- a/specta-rust/src/lib.rs +++ b/specta-rust/src/lib.rs @@ -5,7 +5,10 @@ html_favicon_url = "https://github.com/oscartbeaumont/specta/raw/main/.github/logo-128.png" )] -use specta::{datatype::DataType, Generics, Type, TypeCollection}; +use specta::{ + datatype::{DataType, NamedDataType, StructFields}, + Generics, Type, TypeCollection, +}; /// TODO pub fn export() -> Result { @@ -15,7 +18,19 @@ pub fn export() -> Result { )) } -fn datatype(t: &DataType) -> Result { +pub fn export_named_datatype( + // conf: &Typescript, + typ: &NamedDataType, + // type_map: &TypeCollection, +) -> Result { + Ok(format!( + "pub type {} = {}", + typ.name(), + datatype(&typ.inner)? + )) +} + +pub fn datatype(t: &DataType) -> Result { // TODO: This system does lossy type conversions. That is something I want to fix in the future but for now this works. Eg. `HashSet` will be exported as `Vec` // TODO: Serde serialize + deserialize on types @@ -42,36 +57,34 @@ fn datatype(t: &DataType) -> Result { .join(", ") ), }, - DataType::Struct(s) => { - // match &s.fields()[..] { - // [] => "struct {name}".to_string(), - // fields => { - // let generics = (!s.generics().is_empty()) - // .then(|| format!("<{}>", s.generics().join(", "))) - // .unwrap_or_default(); + DataType::Struct(s) => match &s.fields() { + StructFields::Unit => "struct {name}".to_string(), + StructFields::Named(_) => todo!(), + StructFields::Unnamed(_) => todo!(), + // fields => { + // let generics = (!s.generics().is_empty()) + // .then(|| format!("<{}>", s.generics().join(", "))) + // .unwrap_or_default(); - // let fields = fields - // .iter() - // .map(|f| { - // let name = &f.name; - // let typ = datatype(&f.ty)?; - // Ok(format!("\t{name}: {typ}")) - // }) - // .collect::, String>>()? - // .join(", "); + // let fields = fields + // .iter() + // .map(|f| { + // let name = &f.name; + // let typ = datatype(&f.ty)?; + // Ok(format!("\t{name}: {typ}")) + // }) + // .collect::, String>>()? + // .join(", "); - // let tag = s - // .tag() - // .clone() - // .map(|t| format!("{t}: String")) - // .unwrap_or_default(); + // let tag = s + // .tag() + // .clone() + // .map(|t| format!("{t}: String")) + // .unwrap_or_default(); - // format!("struct {}{generics} {{ {fields}{tag} }}\n", s.name()) - // } + // format!("struct {}{generics} {{ {fields}{tag} }}\n", s.name()) // } - - todo!(); - } + }, DataType::Enum(_) => todo!(), DataType::Reference(reference) => match &reference.generics()[..] { [] => reference.name().to_string(), diff --git a/specta-typescript/src/typescript.rs b/specta-typescript/src/typescript.rs index ff74c91..d895c27 100644 --- a/specta-typescript/src/typescript.rs +++ b/specta-typescript/src/typescript.rs @@ -65,7 +65,7 @@ impl Default for Typescript { Self { header: Cow::Borrowed(""), framework_header: Cow::Borrowed( - "// This file has been generated by Specta. DO NOT EDIT.\n", + "// This file has been generated by Specta. DO NOT EDIT.", ), bigint: Default::default(), comment_exporter: Some(comments::js_doc), @@ -83,11 +83,9 @@ impl Typescript { /// Override the header for the exported file. /// You should prefer `Self::header` instead unless your a framework. #[doc(hidden)] // Although this is hidden it's still public API. - pub fn framework_header(self, header: impl Into>) -> Self { - Self { - header: header.into(), - ..Default::default() - } + pub fn framework_header(mut self, header: impl Into>) -> Self { + self.framework_header = header.into(); + self } /// Configure a header for the file. @@ -132,11 +130,11 @@ impl Typescript { /// TODO pub fn export(&self, types: &TypeCollection) -> Result { let mut out = self.header.to_string(); - out += &self.framework_header; if !out.is_empty() { out.push('\n'); } - out.push('\n'); + out += &self.framework_header; + out.push_str("\n\n"); if let Some((ty_name, l0, l1)) = detect_duplicate_type_names(&types).into_iter().next() { return Err(ExportError::DuplicateTypeName(ty_name, l0, l1)); @@ -159,6 +157,9 @@ impl Typescript { types: &TypeCollection, ) -> Result<(), ExportError> { let path = path.as_ref(); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent)?; + } std::fs::write( &path, self.export(types).map(|s| format!("{}{s}", self.header))?, @@ -168,4 +169,12 @@ impl Typescript { } Ok(()) } + + /// TODO + pub fn format(&self, path: impl AsRef) -> Result<(), ExportError> { + if let Some(formatter) = self.formatter { + formatter(path.as_ref())?; + } + Ok(()) + } }