From e55ce6ca80f1af7ba0b79d79783a531819935b12 Mon Sep 17 00:00:00 2001 From: CerulanLumina Date: Fri, 9 Dec 2022 12:32:03 -0500 Subject: [PATCH 1/2] First rough pass at date types. More work is needed for Go/TS. --- Cargo.lock | 2 +- core/src/language/go.rs | 1 + core/src/language/kotlin.rs | 11 ++++++++++- core/src/language/swift.rs | 1 + core/src/language/typescript.rs | 1 + core/src/rust_types.rs | 10 ++++++++-- 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b9fcd69..db193256 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,7 +601,7 @@ dependencies = [ [[package]] name = "typeshare-cli" -version = "1.0.0" +version = "1.0.1" dependencies = [ "clap", "clap_complete_command", diff --git a/core/src/language/go.rs b/core/src/language/go.rs index 161e9f68..69b36aee 100644 --- a/core/src/language/go.rs +++ b/core/src/language/go.rs @@ -72,6 +72,7 @@ impl Language for Go { self.format_type(rtype1, generic_types)?, self.format_type(rtype2, generic_types)? ), + SpecialRustType::DateTime => todo!(), SpecialRustType::Unit => "struct{}".into(), SpecialRustType::String => "string".into(), SpecialRustType::I8 diff --git a/core/src/language/kotlin.rs b/core/src/language/kotlin.rs index f40e507b..64e34bbc 100644 --- a/core/src/language/kotlin.rs +++ b/core/src/language/kotlin.rs @@ -44,7 +44,8 @@ impl Language for Kotlin { self.format_type(rtype1, generic_types)?, self.format_type(rtype2, generic_types)? ) - } + }, + SpecialRustType::DateTime => "java.time.Instant".into(), SpecialRustType::Unit => "Unit".into(), SpecialRustType::String => "String".into(), // https://kotlinlang.org/docs/basic-types.html#integer-types @@ -70,12 +71,20 @@ impl Language for Kotlin { writeln!(w, " */")?; writeln!(w)?; writeln!(w, "@file:NoLiveLiterals")?; + writeln!(w, "@file:UseSerializers(JavaInstantSerializer::class)")?; writeln!(w)?; writeln!(w, "package {}", self.package)?; writeln!(w)?; writeln!(w, "import androidx.compose.runtime.NoLiveLiterals")?; writeln!(w, "import kotlinx.serialization.*")?; writeln!(w)?; + writeln!(w, r#"object JavaInstantSerializer : KSerializer {{ + override val descriptor = PrimitiveDescriptor("Instant", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: java.time.Instant) = encoder.encodeString(value) + override fun deserialize(decoder: Decoder): java.time.Instant = java.time.Instant.parse(decoder.decodeString()) +}} +"# )?; + writeln!(w)?; } Ok(()) diff --git a/core/src/language/swift.rs b/core/src/language/swift.rs index bab9082e..f4e3e7c6 100644 --- a/core/src/language/swift.rs +++ b/core/src/language/swift.rs @@ -130,6 +130,7 @@ impl Language for Swift { self.format_type(rtype1, generic_types)?, self.format_type(rtype2, generic_types)? ), + SpecialRustType::DateTime => "Date".into(), SpecialRustType::Unit => { self.should_emit_codable_void.store(true, Ordering::SeqCst); "CodableVoid".into() diff --git a/core/src/language/typescript.rs b/core/src/language/typescript.rs index c6f43804..8bcfe3bb 100644 --- a/core/src/language/typescript.rs +++ b/core/src/language/typescript.rs @@ -38,6 +38,7 @@ impl Language for TypeScript { }, self.format_type(rtype2, generic_types)? )), + SpecialRustType::DateTime => todo!(), SpecialRustType::Unit => Ok("undefined".into()), SpecialRustType::String => Ok("string".into()), SpecialRustType::I8 diff --git a/core/src/rust_types.rs b/core/src/rust_types.rs index 0fc0b3c3..d5a70f6b 100644 --- a/core/src/rust_types.rs +++ b/core/src/rust_types.rs @@ -112,6 +112,8 @@ pub enum SpecialRustType { HashMap(Box, Box), /// Represents `Option` from the standard library Option(Box), + /// Represents chrono::DateTime from chrono + DateTime, /// Represents `()` Unit, /// Represents `String` from the standard library @@ -213,6 +215,7 @@ impl TryFrom<&syn::Type> for RustType { params.next().unwrap().into(), )) } + "DateTime" => RustType::Special(SpecialRustType::DateTime), "str" | "String" => RustType::Special(SpecialRustType::String), // Since we do not need to box types in other languages, we treat this type // as its inner type. @@ -311,7 +314,8 @@ impl SpecialRustType { match &self { Self::Vec(rty) | Self::Option(rty) => rty.contains_type(ty), Self::HashMap(rty1, rty2) => rty1.contains_type(ty) || rty2.contains_type(ty), - Self::Unit + Self::DateTime + | Self::Unit | Self::String | Self::I8 | Self::I16 @@ -340,6 +344,7 @@ impl SpecialRustType { Self::Vec(_) => "Vec", Self::Option(_) => "Option", Self::HashMap(_, _) => "HashMap", + Self::DateTime => "DateTime", Self::String => "String", Self::Bool => "bool", Self::I8 => "i8", @@ -365,7 +370,8 @@ impl SpecialRustType { Self::HashMap(rtype1, rtype2) => { Box::new([rtype1.as_ref(), rtype2.as_ref()].into_iter()) } - Self::Unit + Self::DateTime + | Self::Unit | Self::String | Self::I8 | Self::I16 From f20ba35523ade66a5e1bc8dc702d632ab07fd7c0 Mon Sep 17 00:00:00 2001 From: CerulanLumina Date: Thu, 15 Dec 2022 13:07:55 -0500 Subject: [PATCH 2/2] Implement DateTime support across all languages --- cli/src/main.rs | 2 ++ core/src/language/go.rs | 10 ++++++++-- core/src/language/kotlin.rs | 32 ++++++++++++++++++++++++-------- core/src/language/typescript.rs | 16 +++++++++++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index e8263839..62b96c47 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -170,9 +170,11 @@ fn main() { package: config.kotlin.package, module_name: config.kotlin.module_name, type_mappings: config.kotlin.type_mappings, + ..Default::default() }), Some("typescript") => Box::new(TypeScript { type_mappings: config.typescript.type_mappings, + ..Default::default() }), #[cfg(feature = "go")] Some("go") => Box::new(Go { diff --git a/core/src/language/go.rs b/core/src/language/go.rs index 69b36aee..b6517dfd 100644 --- a/core/src/language/go.rs +++ b/core/src/language/go.rs @@ -72,7 +72,7 @@ impl Language for Go { self.format_type(rtype1, generic_types)?, self.format_type(rtype2, generic_types)? ), - SpecialRustType::DateTime => todo!(), + SpecialRustType::DateTime => "Time".into(), SpecialRustType::Unit => "struct{}".into(), SpecialRustType::String => "string".into(), SpecialRustType::I8 @@ -101,7 +101,13 @@ impl Language for Go { )?; writeln!(w, "package {}", self.package)?; writeln!(w)?; - writeln!(w, "import \"encoding/json\"")?; + writeln!( + w, + r#"import ( + "encoding/json" + "time" +)"# + )?; writeln!(w)?; Ok(()) } diff --git a/core/src/language/kotlin.rs b/core/src/language/kotlin.rs index 64e34bbc..a921ece9 100644 --- a/core/src/language/kotlin.rs +++ b/core/src/language/kotlin.rs @@ -8,6 +8,7 @@ use crate::{ use itertools::Itertools; use joinery::JoinableIterator; use lazy_format::lazy_format; +use std::sync::atomic::{AtomicBool, Ordering}; use std::{collections::HashMap, io::Write}; /// All information needed for Kotlin type-code @@ -19,6 +20,8 @@ pub struct Kotlin { pub module_name: String, /// Conversions from Rust type names to Kotlin type names. pub type_mappings: HashMap, + /// Whether we've outputted a Date (requires custom serializer) + pub has_date: AtomicBool, } impl Language for Kotlin { @@ -44,8 +47,11 @@ impl Language for Kotlin { self.format_type(rtype1, generic_types)?, self.format_type(rtype2, generic_types)? ) - }, - SpecialRustType::DateTime => "java.time.Instant".into(), + } + SpecialRustType::DateTime => { + self.has_date.store(true, Ordering::SeqCst); + "java.time.Instant".into() + } SpecialRustType::Unit => "Unit".into(), SpecialRustType::String => "String".into(), // https://kotlinlang.org/docs/basic-types.html#integer-types @@ -71,23 +77,33 @@ impl Language for Kotlin { writeln!(w, " */")?; writeln!(w)?; writeln!(w, "@file:NoLiveLiterals")?; - writeln!(w, "@file:UseSerializers(JavaInstantSerializer::class)")?; writeln!(w)?; writeln!(w, "package {}", self.package)?; writeln!(w)?; writeln!(w, "import androidx.compose.runtime.NoLiveLiterals")?; + writeln!(w)?; + } + + Ok(()) + } + + fn end_file(&self, w: &mut dyn Write) -> std::io::Result<()> { + if self.has_date.load(Ordering::SeqCst) { writeln!(w, "import kotlinx.serialization.*")?; writeln!(w)?; - writeln!(w, r#"object JavaInstantSerializer : KSerializer {{ + writeln!( + w, + r#"object JavaInstantSerializer : KSerializer {{ override val descriptor = PrimitiveDescriptor("Instant", PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: java.time.Instant) = encoder.encodeString(value) override fun deserialize(decoder: Decoder): java.time.Instant = java.time.Instant.parse(decoder.decodeString()) }} -"# )?; - writeln!(w)?; +"# + )?; + Ok(()) + } else { + Ok(()) } - - Ok(()) } fn write_type_alias(&self, w: &mut dyn Write, ty: &RustTypeAlias) -> std::io::Result<()> { diff --git a/core/src/language/typescript.rs b/core/src/language/typescript.rs index 8bcfe3bb..2a92ccba 100644 --- a/core/src/language/typescript.rs +++ b/core/src/language/typescript.rs @@ -3,6 +3,7 @@ use crate::{ language::Language, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; +use std::sync::atomic::{AtomicBool, Ordering}; use std::{collections::HashMap, io::Write}; /// All information needed to generate Typescript type-code @@ -10,6 +11,7 @@ use std::{collections::HashMap, io::Write}; pub struct TypeScript { /// Mappings from Rust type names to Typescript type names pub type_mappings: HashMap, + pub has_date: AtomicBool, } impl Language for TypeScript { @@ -38,7 +40,10 @@ impl Language for TypeScript { }, self.format_type(rtype2, generic_types)? )), - SpecialRustType::DateTime => todo!(), + SpecialRustType::DateTime => { + self.has_date.store(true, Ordering::SeqCst); + Ok("Date".into()) + } SpecialRustType::Unit => Ok("undefined".into()), SpecialRustType::String => Ok("string".into()), SpecialRustType::I8 @@ -69,6 +74,15 @@ impl Language for TypeScript { Ok(()) } + fn end_file(&self, w: &mut dyn Write) -> std::io::Result<()> { + if self.has_date.load(Ordering::SeqCst) { + writeln!(w, "export function TypeshareDateReviver(key, value): Date {{ return new Date(value); }}")?; + Ok(()) + } else { + Ok(()) + } + } + fn write_type_alias(&self, w: &mut dyn Write, ty: &RustTypeAlias) -> std::io::Result<()> { self.write_comments(w, 0, &ty.comments)?;