From 87b17db431c48c0f7457b880d65dc1c413213bdf Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Thu, 9 Feb 2023 22:13:08 -0800 Subject: [PATCH 1/8] write out types in topological order --- .../tests/can_generate_generic_enum/output.kt | 10 +- .../can_generate_generic_enum/output.swift | 16 +- .../tests/can_generate_generic_enum/output.ts | 8 +- core/data/tests/can_order_types/input.rs | 16 ++ core/data/tests/can_order_types/output.go | 91 +++++++ core/data/tests/can_order_types/output.kt | 38 +++ core/data/tests/can_order_types/output.swift | 78 ++++++ core/data/tests/orders_types/input.rs | 0 core/data/tests/orders_types/output.go | 5 + core/data/tests/orders_types/output.kt | 11 + core/data/tests/orders_types/output.swift | 5 + core/src/language/go.rs | 21 +- core/src/language/mod.rs | 21 +- core/src/lib.rs | 1 + core/src/parser.rs | 1 + core/src/rust_types.rs | 23 +- core/src/topsort.rs | 225 ++++++++++++++++++ core/tests/snapshot_tests.rs | 3 + 18 files changed, 540 insertions(+), 33 deletions(-) create mode 100644 core/data/tests/can_order_types/input.rs create mode 100644 core/data/tests/can_order_types/output.go create mode 100644 core/data/tests/can_order_types/output.kt create mode 100644 core/data/tests/can_order_types/output.swift create mode 100644 core/data/tests/orders_types/input.rs create mode 100644 core/data/tests/orders_types/output.go create mode 100644 core/data/tests/orders_types/output.kt create mode 100644 core/data/tests/orders_types/output.swift create mode 100644 core/src/topsort.rs diff --git a/core/data/tests/can_generate_generic_enum/output.kt b/core/data/tests/can_generate_generic_enum/output.kt index 2baaaa05..0bec79b2 100644 --- a/core/data/tests/can_generate_generic_enum/output.kt +++ b/core/data/tests/can_generate_generic_enum/output.kt @@ -9,11 +9,6 @@ package com.agilebits.onepassword import androidx.compose.runtime.NoLiveLiterals import kotlinx.serialization.* -@Serializable -data class StructUsingGenericEnum ( - val enum_field: GenericEnum -) - @Serializable sealed class GenericEnum { @Serializable @@ -24,6 +19,11 @@ sealed class GenericEnum { data class VariantB(val content: B): GenericEnum() } +@Serializable +data class StructUsingGenericEnum ( + val enum_field: GenericEnum +) + @Serializable sealed class GenericEnumUsingGenericEnum { @Serializable diff --git a/core/data/tests/can_generate_generic_enum/output.swift b/core/data/tests/can_generate_generic_enum/output.swift index 685426cd..11aa2dbf 100644 --- a/core/data/tests/can_generate_generic_enum/output.swift +++ b/core/data/tests/can_generate_generic_enum/output.swift @@ -4,14 +4,6 @@ import Foundation -public struct CoreStructUsingGenericEnum: Codable { - public let enum_field: CoreGenericEnum - - public init(enum_field: CoreGenericEnum) { - self.enum_field = enum_field - } -} - public enum CoreGenericEnum: Codable { case variantA(A) case variantB(B) @@ -57,6 +49,14 @@ public enum CoreGenericEnum: Codable { } } +public struct CoreStructUsingGenericEnum: Codable { + public let enum_field: CoreGenericEnum + + public init(enum_field: CoreGenericEnum) { + self.enum_field = enum_field + } +} + public enum CoreGenericEnumUsingGenericEnum: Codable { case variantC(CoreGenericEnum) case variantD(CoreGenericEnum) diff --git a/core/data/tests/can_generate_generic_enum/output.ts b/core/data/tests/can_generate_generic_enum/output.ts index 9367530a..fa70f941 100644 --- a/core/data/tests/can_generate_generic_enum/output.ts +++ b/core/data/tests/can_generate_generic_enum/output.ts @@ -2,14 +2,14 @@ Generated by typeshare 1.0.0 */ -export interface StructUsingGenericEnum { - enum_field: GenericEnum; -} - export type GenericEnum = | { type: "VariantA", content: A } | { type: "VariantB", content: B }; +export interface StructUsingGenericEnum { + enum_field: GenericEnum; +} + export type GenericEnumUsingGenericEnum = | { type: "VariantC", content: GenericEnum } | { type: "VariantD", content: GenericEnum> } diff --git a/core/data/tests/can_order_types/input.rs b/core/data/tests/can_order_types/input.rs new file mode 100644 index 00000000..47eb8524 --- /dev/null +++ b/core/data/tests/can_order_types/input.rs @@ -0,0 +1,16 @@ +#[typeshare] +#[serde(tag = "type", content = "content")] +pub enum SomeEnum { + Unit, + Alg(SomeStruct), + Anon {the_field: SomeStruct}, +} + +#[typeshare] +type SomeTypeAlias = SomeStruct; + +#[typeshare] +pub struct SomeStruct { + some_field: SomeEnum, + some_special_field: Vec>>, +} diff --git a/core/data/tests/can_order_types/output.go b/core/data/tests/can_order_types/output.go new file mode 100644 index 00000000..27482d01 --- /dev/null +++ b/core/data/tests/can_order_types/output.go @@ -0,0 +1,91 @@ +// Code generated by typeshare 1.0.0. DO NOT EDIT. +package proto + +import "encoding/json" + +type SomeTypeAlias SomeStruct + +type SomeStruct struct { + SomeField SomeEnum `json:"some_field"` + SomeSpecialField []*map[T]SomeEnum `json:"some_special_field"` +} +// Generated type representing the anonymous struct variant `Anon` of the `SomeEnum` Rust enum +type SomeEnumAnonInner struct { + TheField SomeStruct `json:"the_field"` +} +type SomeEnumTypes string +const ( + SomeEnumTypeVariantUnit SomeEnumTypes = "Unit" + SomeEnumTypeVariantAlg SomeEnumTypes = "Alg" + SomeEnumTypeVariantAnon SomeEnumTypes = "Anon" +) +type SomeEnum struct{ + Type SomeEnumTypes `json:"type"` + content interface{} +} + +func (s *SomeEnum) UnmarshalJSON(data []byte) error { + var enum struct { + Tag SomeEnumTypes `json:"type"` + Content json.RawMessage `json:"content"` + } + if err := json.Unmarshal(data, &enum); err != nil { + return err + } + + s.Type = enum.Tag + switch s.Type { + case SomeEnumTypeVariantUnit: + return nil + case SomeEnumTypeVariantAlg: + var res SomeStruct + s.content = &res + case SomeEnumTypeVariantAnon: + var res SomeEnumAnonInner + s.content = &res + + } + if err := json.Unmarshal(enum.Content, &s.content); err != nil { + return err + } + + return nil +} + +func (s SomeEnum) MarshalJSON() ([]byte, error) { + var enum struct { + Tag SomeEnumTypes `json:"type"` + Content interface{} `json:"content,omitempty"` + } + enum.Tag = s.Type + enum.Content = s.content + return json.Marshal(enum) +} + +func (s SomeEnum) Alg() *SomeStruct { + res, _ := s.content.(*SomeStruct) + return res +} +func (s SomeEnum) Anon() *SomeEnumAnonInner { + res, _ := s.content.(*SomeEnumAnonInner) + return res +} + +func NewSomeEnumTypeVariantUnit() SomeEnum { + return SomeEnum{ + Type: SomeEnumTypeVariantUnit, + } +} +func NewSomeEnumTypeVariantAlg(content *SomeStruct) SomeEnum { + return SomeEnum{ + Type: SomeEnumTypeVariantAlg, + content: content, + } +} +func NewSomeEnumTypeVariantAnon(content *SomeEnumAnonInner) SomeEnum { + return SomeEnum{ + Type: SomeEnumTypeVariantAnon, + content: content, + } +} + diff --git a/core/data/tests/can_order_types/output.kt b/core/data/tests/can_order_types/output.kt new file mode 100644 index 00000000..c0717499 --- /dev/null +++ b/core/data/tests/can_order_types/output.kt @@ -0,0 +1,38 @@ +/** + * Generated by typeshare 1.0.0 + */ + +@file:NoLiveLiterals + +package com.agilebits.onepassword + +import androidx.compose.runtime.NoLiveLiterals +import kotlinx.serialization.* + +/// Generated type representing the anonymous struct variant `Anon` of the `SomeEnum` Rust enum +@Serializable +data class SomeEnumAnonInner ( + val the_field: SomeStruct +) + +@Serializable +sealed class SomeEnum { + @Serializable + @SerialName("Unit") + object Unit: SomeEnum() + @Serializable + @SerialName("Alg") + data class Alg(val content: SomeStruct): SomeEnum() + @Serializable + @SerialName("Anon") + data class Anon(val content: SomeEnumAnonInner): SomeEnum() +} + +@Serializable +data class SomeStruct ( + val some_field: SomeEnum, + val some_special_field: List?> +) + +typealias SomeTypeAlias = SomeStruct + diff --git a/core/data/tests/can_order_types/output.swift b/core/data/tests/can_order_types/output.swift new file mode 100644 index 00000000..5d3f93b2 --- /dev/null +++ b/core/data/tests/can_order_types/output.swift @@ -0,0 +1,78 @@ +/* + Generated by typeshare 1.0.0 + */ + +import Foundation + + +/// Generated type representing the anonymous struct variant `Anon` of the `SomeEnum` Rust enum +public struct SomeEnumAnonInner: Codable { + public let the_field: SomeStruct + + public init(the_field: SomeStruct) { + self.the_field = the_field + } +} +public enum SomeEnum: Codable { + case unit + case alg(SomeStruct) + case anon(SomeEnumAnonInner) + + enum CodingKeys: String, CodingKey, Codable { + case unit = "Unit", + alg = "Alg", + anon = "Anon" + } + + private enum ContainerCodingKeys: String, CodingKey { + case type, content + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: ContainerCodingKeys.self) + if let type = try? container.decode(CodingKeys.self, forKey: .type) { + switch type { + case .unit: + self = .unit + return + case .alg: + if let content = try? container.decode(SomeStruct.self, forKey: .content) { + self = .alg(content) + return + } + case .anon: + if let content = try? container.decode(SomeEnumAnonInner.self, forKey: .content) { + self = .anon(content) + return + } + } + } + throw DecodingError.typeMismatch(SomeEnum.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for SomeEnum")) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: ContainerCodingKeys.self) + switch self { + case .unit: + try container.encode(CodingKeys.unit, forKey: .type) + case .alg(let content): + try container.encode(CodingKeys.alg, forKey: .type) + try container.encode(content, forKey: .content) + case .anon(let content): + try container.encode(CodingKeys.anon, forKey: .type) + try container.encode(content, forKey: .content) + } + } +} + +public struct SomeStruct: Codable { + public let some_field: SomeEnum + public let some_special_field: [[T: SomeEnum]?] + + public init(some_field: SomeEnum, some_special_field: [[T: SomeEnum]?]) { + self.some_field = some_field + self.some_special_field = some_special_field + } +} + +public typealias SomeTypeAlias = SomeStruct diff --git a/core/data/tests/orders_types/input.rs b/core/data/tests/orders_types/input.rs new file mode 100644 index 00000000..e69de29b diff --git a/core/data/tests/orders_types/output.go b/core/data/tests/orders_types/output.go new file mode 100644 index 00000000..66c2ee4e --- /dev/null +++ b/core/data/tests/orders_types/output.go @@ -0,0 +1,5 @@ +// Code generated by typeshare 1.0.0. DO NOT EDIT. +package proto + +import "encoding/json" + diff --git a/core/data/tests/orders_types/output.kt b/core/data/tests/orders_types/output.kt new file mode 100644 index 00000000..8b6de751 --- /dev/null +++ b/core/data/tests/orders_types/output.kt @@ -0,0 +1,11 @@ +/** + * Generated by typeshare 1.0.0 + */ + +@file:NoLiveLiterals + +package com.agilebits.onepassword + +import androidx.compose.runtime.NoLiveLiterals +import kotlinx.serialization.* + diff --git a/core/data/tests/orders_types/output.swift b/core/data/tests/orders_types/output.swift new file mode 100644 index 00000000..2cf1f000 --- /dev/null +++ b/core/data/tests/orders_types/output.swift @@ -0,0 +1,5 @@ +/* + Generated by typeshare 1.0.0 + */ + +import Foundation diff --git a/core/src/language/go.rs b/core/src/language/go.rs index 16099e03..0d86f8d2 100644 --- a/core/src/language/go.rs +++ b/core/src/language/go.rs @@ -2,10 +2,11 @@ use std::io::Write; use crate::parser::ParsedData; use crate::rename::RenameExt; -use crate::rust_types::{RustTypeFormatError, SpecialRustType}; +use crate::rust_types::{RustThing, RustTypeFormatError, SpecialRustType}; use crate::{ language::Language, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, + topsort::topsort, }; use std::collections::{HashMap, HashSet}; @@ -36,16 +37,28 @@ impl Language for Go { self.begin_file(w)?; + let mut things: Vec = vec![]; + for a in &data.aliases { - self.write_type_alias(w, a)?; + things.push(RustThing::TypeAlias(a)) } for s in &data.structs { - self.write_struct(w, s)?; + things.push(RustThing::Struct(s)) } for e in &data.enums { - self.write_enum(w, e, &types_mapping_to_struct)?; + things.push(RustThing::Enum(e)) + } + + let sorted = topsort(things.iter().collect()); + + for &thing in &sorted { + match thing { + RustThing::Enum(e) => self.write_enum(w, e, &types_mapping_to_struct)?, + RustThing::Struct(s) => self.write_struct(w, s)?, + RustThing::TypeAlias(a) => self.write_type_alias(w, a)?, + } } self.end_file(w)?; diff --git a/core/src/language/mod.rs b/core/src/language/mod.rs index 36f4bbcb..5feb13b4 100644 --- a/core/src/language/mod.rs +++ b/core/src/language/mod.rs @@ -1,6 +1,7 @@ use crate::{ parser::ParsedData, - rust_types::{Id, RustEnum, RustEnumVariant, RustStruct, RustTypeAlias}, + rust_types::{Id, RustEnum, RustEnumVariant, RustStruct, RustThing, RustTypeAlias}, + topsort::topsort, }; use itertools::Itertools; use std::{collections::HashMap, io::Write}; @@ -31,16 +32,28 @@ pub trait Language { ) -> std::io::Result<()> { self.begin_file(writable)?; + let mut things: Vec = vec![]; + for a in &data.aliases { - self.write_type_alias(writable, a)?; + things.push(RustThing::TypeAlias(a)) } for s in &data.structs { - self.write_struct(writable, s)?; + things.push(RustThing::Struct(s)) } for e in &data.enums { - self.write_enum(writable, e)?; + things.push(RustThing::Enum(e)) + } + + let sorted = topsort(things.iter().collect()); + + for &thing in &sorted { + match thing { + RustThing::Enum(e) => self.write_enum(writable, e)?, + RustThing::Struct(s) => self.write_struct(writable, s)?, + RustThing::TypeAlias(a) => self.write_type_alias(writable, a)?, + } } self.end_file(writable)?; diff --git a/core/src/lib.rs b/core/src/lib.rs index f239bf03..e0b86fa6 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -13,6 +13,7 @@ pub mod language; pub mod parser; /// Codifying Rust types and how they convert to various languages. pub mod rust_types; +pub mod topsort; #[derive(Debug, Error)] #[allow(missing_docs)] diff --git a/core/src/parser.rs b/core/src/parser.rs index 6187542d..9d3ef041 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -79,6 +79,7 @@ pub fn parse(input: &str) -> Result { // Parse and process the input, ensuring we parse only items marked with // `#[typeshare] let source = syn::parse_file(input)?; + for item in &source.items { match item { syn::Item::Struct(s) if has_typeshare_annotation(&s.attrs) => { diff --git a/core/src/rust_types.rs b/core/src/rust_types.rs index f7eb5237..5dfbcfc6 100644 --- a/core/src/rust_types.rs +++ b/core/src/rust_types.rs @@ -4,7 +4,7 @@ use std::{collections::HashMap, convert::TryFrom}; use thiserror::Error; /// Identifier used in Rust structs, enums, and fields. It includes the `original` name and the `renamed` value after the transformation based on `serde` attributes. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Id { /// The original identifier name pub original: String, @@ -25,7 +25,7 @@ impl std::fmt::Display for Id { } /// Rust struct. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct RustStruct { /// The identifier for the struct. pub id: Id, @@ -45,7 +45,7 @@ pub struct RustStruct { /// ``` /// pub struct MasterPassword(String); /// ``` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct RustTypeAlias { /// The identifier for the alias. pub id: Id, @@ -58,7 +58,7 @@ pub struct RustTypeAlias { } /// Rust field definition. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct RustField { /// Identifier for the field. pub id: Id, @@ -383,7 +383,7 @@ impl SpecialRustType { } /// Parsed information about a Rust enum definition -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum RustEnum { /// A unit enum /// @@ -433,7 +433,7 @@ impl RustEnum { } /// Enum information shared among different enum types -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct RustEnumShared { /// The enum's ident pub id: Id, @@ -453,7 +453,7 @@ pub struct RustEnumShared { } /// Parsed information about a Rust enum variant -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum RustEnumVariant { /// A unit variant Unit(RustEnumVariantShared), @@ -485,10 +485,17 @@ impl RustEnumVariant { } /// Variant information shared among different variant types -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct RustEnumVariantShared { /// The variant's ident pub id: Id, /// Comments applied to the variant pub comments: Vec, } + +#[derive(Debug, PartialEq)] +pub enum RustThing<'a> { + Enum(&'a RustEnum), + TypeAlias(&'a RustTypeAlias), + Struct(&'a RustStruct), +} diff --git a/core/src/topsort.rs b/core/src/topsort.rs new file mode 100644 index 00000000..0d4db265 --- /dev/null +++ b/core/src/topsort.rs @@ -0,0 +1,225 @@ +use std::collections::{HashMap, HashSet}; + +use crate::rust_types::{ + RustEnum, RustEnumVariant, RustStruct, RustThing, RustType, RustTypeAlias, SpecialRustType, +}; + +fn get_dependencies_from_type( + tp: &RustType, + types: &HashMap, + res: &mut Vec, + seen: &mut HashSet, +) { + match tp { + RustType::Generic { id, parameters } => { + if let Some(&tp) = types.get(id) { + if seen.insert(id.clone()) { + res.push(id.clone()); + get_dependencies(tp, types, res, seen); + for parameter in parameters { + let id = parameter.id().to_string(); + if let Some(&tp) = types.get(&id) { + if seen.insert(id.clone()) { + res.push(id.clone()); + get_dependencies(tp, types, res, seen); + seen.remove(&id.clone()); + } + } + } + seen.remove(&id.clone()); + } + } + } + RustType::Simple { id } => { + if let Some(&tp) = types.get(id) { + if seen.insert(id.clone()) { + res.push(id.clone()); + get_dependencies(tp, types, res, seen); + seen.remove(&id.clone()); + } + } + } + RustType::Special(special) => match special { + SpecialRustType::HashMap(kt, vt) => { + get_dependencies_from_type(kt, types, res, seen); + get_dependencies_from_type(vt, types, res, seen); + } + SpecialRustType::Option(inner) => { + get_dependencies_from_type(inner, types, res, seen); + } + SpecialRustType::Vec(inner) => { + get_dependencies_from_type(inner, types, res, seen); + } + _ => {} + }, + }; + seen.remove(&tp.id().to_string()); +} + +fn get_enum_dependencies( + enm: &RustEnum, + types: &HashMap, + res: &mut Vec, + seen: &mut HashSet, +) { + match enm { + RustEnum::Unit(_) => {} + RustEnum::Algebraic { + tag_key: _, + content_key: _, + shared, + } => { + if seen.insert(shared.id.original.to_string()) { + res.push(shared.id.original.to_string()); + for variant in &shared.variants { + match variant { + RustEnumVariant::Unit(_) => {} + RustEnumVariant::AnonymousStruct { + fields: _, + shared: _, + } => {} + RustEnumVariant::Tuple { ty, shared: _ } => { + get_dependencies_from_type(ty, types, res, seen) + } + } + } + seen.remove(&shared.id.original.to_string()); + } + } + } +} + +fn get_struct_dependencies( + strct: &RustStruct, + types: &HashMap, + res: &mut Vec, + seen: &mut HashSet, +) { + if seen.insert(strct.id.original.to_string()) { + for field in &strct.fields { + get_dependencies_from_type(&field.ty, types, res, seen) + } + seen.remove(&strct.id.original.to_string()); + } +} + +fn get_type_alias_dependencies( + ta: &RustTypeAlias, + types: &HashMap, + res: &mut Vec, + seen: &mut HashSet, +) { + if seen.insert(ta.id.original.to_string()) { + get_dependencies_from_type(&ta.r#type, types, res, seen); + for generic in &ta.generic_types { + if let Some(&thing) = types.get(generic) { + get_dependencies(thing, types, res, seen) + } + } + seen.remove(&ta.id.original.to_string()); + } +} + +fn get_dependencies( + thing: &RustThing, + types: &HashMap, + res: &mut Vec, + seen: &mut HashSet, +) { + match thing { + RustThing::Enum(en) => get_enum_dependencies(en, types, res, seen), + RustThing::Struct(strct) => get_struct_dependencies(strct, types, res, seen), + RustThing::TypeAlias(alias) => get_type_alias_dependencies(alias, types, res, seen), + } +} + +fn get_index(thing: &RustThing, things: &[&RustThing]) -> usize { + things + .iter() + .position(|&r| r == thing) + .expect("Unable to find thing in things!") +} + +fn toposort_impl(graph: &Vec>) -> Vec { + fn inner( + graph: &Vec>, + nodes: &Vec, + res: &mut Vec, + processed: &mut Vec, + seen: &mut Vec, + ) { + for dependant in nodes { + if !processed.contains(dependant) { + if !seen.contains(dependant) { + seen.push(*dependant); + } else { + // cycle + return; + } + // recurse + let dependencies = &graph[*dependant]; + inner(graph, dependencies, res, processed, seen); + if let Some(position) = seen.iter().position(|&other| other == *dependant) { + seen.remove(position); + } + processed.push(*dependant); + res.push(*dependant); + } + } + } + let mut res = vec![]; + let mut seen = vec![]; + let mut processed = vec![]; + inner( + graph, + &(0..graph.len()).collect(), + &mut res, + &mut processed, + &mut seen, + ); + res +} + +pub(crate) fn topsort<'a>(things: Vec<&'a RustThing>) -> Vec<&'a RustThing<'a>> { + let types = HashMap::from_iter(things.iter().map(|&thing| { + let id = match thing { + RustThing::Enum(e) => match e { + RustEnum::Algebraic { + tag_key: _, + content_key: _, + shared, + } => shared.id.original.clone(), + RustEnum::Unit(shared) => shared.id.original.clone(), + }, + RustThing::Struct(strct) => strct.id.original.clone(), + RustThing::TypeAlias(ta) => ta.id.original.clone(), + }; + (id, thing) + })); + let dag: Vec> = things + .iter() + .map(|&thing| { + let mut deps = vec![]; + get_dependencies(thing, &types, &mut deps, &mut HashSet::new()); + deps.iter() + .map(|dep| get_index(types.get(dep).unwrap(), &things)) + .collect() + }) + .collect(); + let sorted = toposort_impl(&dag); + sorted.iter().map(|&idx| things[idx]).collect() +} + +#[test] +fn test_toposort_impl() { + let dag = vec![vec![], vec![0], vec![0, 1]]; + let res = toposort_impl(&dag); + assert_eq!(res, vec![0, 1, 2]) +} + +#[test] +fn test_toposort_impl_cycles() { + let dag = vec![vec![1], vec![0], vec![1]]; + let res = toposort_impl(&dag); + assert!((res == vec![0, 1, 2]) || (res == vec![1, 0, 2])) +} diff --git a/core/tests/snapshot_tests.rs b/core/tests/snapshot_tests.rs index ea26031a..2a85ce31 100644 --- a/core/tests/snapshot_tests.rs +++ b/core/tests/snapshot_tests.rs @@ -455,6 +455,9 @@ tests! { can_generate_unit_structs: [swift, kotlin, typescript, go]; kebab_case_rename: [swift, kotlin, typescript, go]; + /// Globals get topologically sorted + orders_types: [swift, kotlin, go]; + /// Other use_correct_integer_types: [swift, kotlin, typescript, go]; // Only swift supports generating types with keywords From 7c7577bb278a94e129b638f74e26a98814689bc3 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Thu, 9 Feb 2023 22:22:02 -0800 Subject: [PATCH 2/8] remove extra folder; make mod private --- core/data/tests/can_order_types/input.rs | 16 ---- core/data/tests/can_order_types/output.go | 91 -------------------- core/data/tests/can_order_types/output.kt | 38 -------- core/data/tests/can_order_types/output.swift | 78 ----------------- core/src/lib.rs | 2 +- 5 files changed, 1 insertion(+), 224 deletions(-) delete mode 100644 core/data/tests/can_order_types/input.rs delete mode 100644 core/data/tests/can_order_types/output.go delete mode 100644 core/data/tests/can_order_types/output.kt delete mode 100644 core/data/tests/can_order_types/output.swift diff --git a/core/data/tests/can_order_types/input.rs b/core/data/tests/can_order_types/input.rs deleted file mode 100644 index 47eb8524..00000000 --- a/core/data/tests/can_order_types/input.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[typeshare] -#[serde(tag = "type", content = "content")] -pub enum SomeEnum { - Unit, - Alg(SomeStruct), - Anon {the_field: SomeStruct}, -} - -#[typeshare] -type SomeTypeAlias = SomeStruct; - -#[typeshare] -pub struct SomeStruct { - some_field: SomeEnum, - some_special_field: Vec>>, -} diff --git a/core/data/tests/can_order_types/output.go b/core/data/tests/can_order_types/output.go deleted file mode 100644 index 27482d01..00000000 --- a/core/data/tests/can_order_types/output.go +++ /dev/null @@ -1,91 +0,0 @@ -// Code generated by typeshare 1.0.0. DO NOT EDIT. -package proto - -import "encoding/json" - -type SomeTypeAlias SomeStruct - -type SomeStruct struct { - SomeField SomeEnum `json:"some_field"` - SomeSpecialField []*map[T]SomeEnum `json:"some_special_field"` -} -// Generated type representing the anonymous struct variant `Anon` of the `SomeEnum` Rust enum -type SomeEnumAnonInner struct { - TheField SomeStruct `json:"the_field"` -} -type SomeEnumTypes string -const ( - SomeEnumTypeVariantUnit SomeEnumTypes = "Unit" - SomeEnumTypeVariantAlg SomeEnumTypes = "Alg" - SomeEnumTypeVariantAnon SomeEnumTypes = "Anon" -) -type SomeEnum struct{ - Type SomeEnumTypes `json:"type"` - content interface{} -} - -func (s *SomeEnum) UnmarshalJSON(data []byte) error { - var enum struct { - Tag SomeEnumTypes `json:"type"` - Content json.RawMessage `json:"content"` - } - if err := json.Unmarshal(data, &enum); err != nil { - return err - } - - s.Type = enum.Tag - switch s.Type { - case SomeEnumTypeVariantUnit: - return nil - case SomeEnumTypeVariantAlg: - var res SomeStruct - s.content = &res - case SomeEnumTypeVariantAnon: - var res SomeEnumAnonInner - s.content = &res - - } - if err := json.Unmarshal(enum.Content, &s.content); err != nil { - return err - } - - return nil -} - -func (s SomeEnum) MarshalJSON() ([]byte, error) { - var enum struct { - Tag SomeEnumTypes `json:"type"` - Content interface{} `json:"content,omitempty"` - } - enum.Tag = s.Type - enum.Content = s.content - return json.Marshal(enum) -} - -func (s SomeEnum) Alg() *SomeStruct { - res, _ := s.content.(*SomeStruct) - return res -} -func (s SomeEnum) Anon() *SomeEnumAnonInner { - res, _ := s.content.(*SomeEnumAnonInner) - return res -} - -func NewSomeEnumTypeVariantUnit() SomeEnum { - return SomeEnum{ - Type: SomeEnumTypeVariantUnit, - } -} -func NewSomeEnumTypeVariantAlg(content *SomeStruct) SomeEnum { - return SomeEnum{ - Type: SomeEnumTypeVariantAlg, - content: content, - } -} -func NewSomeEnumTypeVariantAnon(content *SomeEnumAnonInner) SomeEnum { - return SomeEnum{ - Type: SomeEnumTypeVariantAnon, - content: content, - } -} - diff --git a/core/data/tests/can_order_types/output.kt b/core/data/tests/can_order_types/output.kt deleted file mode 100644 index c0717499..00000000 --- a/core/data/tests/can_order_types/output.kt +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Generated by typeshare 1.0.0 - */ - -@file:NoLiveLiterals - -package com.agilebits.onepassword - -import androidx.compose.runtime.NoLiveLiterals -import kotlinx.serialization.* - -/// Generated type representing the anonymous struct variant `Anon` of the `SomeEnum` Rust enum -@Serializable -data class SomeEnumAnonInner ( - val the_field: SomeStruct -) - -@Serializable -sealed class SomeEnum { - @Serializable - @SerialName("Unit") - object Unit: SomeEnum() - @Serializable - @SerialName("Alg") - data class Alg(val content: SomeStruct): SomeEnum() - @Serializable - @SerialName("Anon") - data class Anon(val content: SomeEnumAnonInner): SomeEnum() -} - -@Serializable -data class SomeStruct ( - val some_field: SomeEnum, - val some_special_field: List?> -) - -typealias SomeTypeAlias = SomeStruct - diff --git a/core/data/tests/can_order_types/output.swift b/core/data/tests/can_order_types/output.swift deleted file mode 100644 index 5d3f93b2..00000000 --- a/core/data/tests/can_order_types/output.swift +++ /dev/null @@ -1,78 +0,0 @@ -/* - Generated by typeshare 1.0.0 - */ - -import Foundation - - -/// Generated type representing the anonymous struct variant `Anon` of the `SomeEnum` Rust enum -public struct SomeEnumAnonInner: Codable { - public let the_field: SomeStruct - - public init(the_field: SomeStruct) { - self.the_field = the_field - } -} -public enum SomeEnum: Codable { - case unit - case alg(SomeStruct) - case anon(SomeEnumAnonInner) - - enum CodingKeys: String, CodingKey, Codable { - case unit = "Unit", - alg = "Alg", - anon = "Anon" - } - - private enum ContainerCodingKeys: String, CodingKey { - case type, content - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: ContainerCodingKeys.self) - if let type = try? container.decode(CodingKeys.self, forKey: .type) { - switch type { - case .unit: - self = .unit - return - case .alg: - if let content = try? container.decode(SomeStruct.self, forKey: .content) { - self = .alg(content) - return - } - case .anon: - if let content = try? container.decode(SomeEnumAnonInner.self, forKey: .content) { - self = .anon(content) - return - } - } - } - throw DecodingError.typeMismatch(SomeEnum.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for SomeEnum")) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: ContainerCodingKeys.self) - switch self { - case .unit: - try container.encode(CodingKeys.unit, forKey: .type) - case .alg(let content): - try container.encode(CodingKeys.alg, forKey: .type) - try container.encode(content, forKey: .content) - case .anon(let content): - try container.encode(CodingKeys.anon, forKey: .type) - try container.encode(content, forKey: .content) - } - } -} - -public struct SomeStruct: Codable { - public let some_field: SomeEnum - public let some_special_field: [[T: SomeEnum]?] - - public init(some_field: SomeEnum, some_special_field: [[T: SomeEnum]?]) { - self.some_field = some_field - self.some_special_field = some_special_field - } -} - -public typealias SomeTypeAlias = SomeStruct diff --git a/core/src/lib.rs b/core/src/lib.rs index e0b86fa6..65a8c8c3 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -13,7 +13,7 @@ pub mod language; pub mod parser; /// Codifying Rust types and how they convert to various languages. pub mod rust_types; -pub mod topsort; +mod topsort; #[derive(Debug, Error)] #[allow(missing_docs)] From 2c111f64824210d7905fbfd432c22735d3b0e422 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Thu, 9 Feb 2023 22:22:46 -0800 Subject: [PATCH 3/8] revert formatting change --- core/src/parser.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/parser.rs b/core/src/parser.rs index 9d3ef041..6187542d 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -79,7 +79,6 @@ pub fn parse(input: &str) -> Result { // Parse and process the input, ensuring we parse only items marked with // `#[typeshare] let source = syn::parse_file(input)?; - for item in &source.items { match item { syn::Item::Struct(s) if has_typeshare_annotation(&s.attrs) => { From c397e4b11b817a2dcae9a2e9b7346c4b09ed5683 Mon Sep 17 00:00:00 2001 From: Jane Lewis Date: Mon, 6 Mar 2023 19:58:57 -0800 Subject: [PATCH 4/8] Update version numbers for new test --- core/data/tests/orders_types/output.go | 2 +- core/data/tests/orders_types/output.kt | 2 +- core/data/tests/orders_types/output.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/data/tests/orders_types/output.go b/core/data/tests/orders_types/output.go index 66c2ee4e..20eb7f8c 100644 --- a/core/data/tests/orders_types/output.go +++ b/core/data/tests/orders_types/output.go @@ -1,4 +1,4 @@ -// Code generated by typeshare 1.0.0. DO NOT EDIT. +// Code generated by typeshare 1.1.0. DO NOT EDIT. package proto import "encoding/json" diff --git a/core/data/tests/orders_types/output.kt b/core/data/tests/orders_types/output.kt index 8b6de751..c4179df6 100644 --- a/core/data/tests/orders_types/output.kt +++ b/core/data/tests/orders_types/output.kt @@ -1,5 +1,5 @@ /** - * Generated by typeshare 1.0.0 + * Generated by typeshare 1.1.0 */ @file:NoLiveLiterals diff --git a/core/data/tests/orders_types/output.swift b/core/data/tests/orders_types/output.swift index 2cf1f000..17bd3e21 100644 --- a/core/data/tests/orders_types/output.swift +++ b/core/data/tests/orders_types/output.swift @@ -1,5 +1,5 @@ /* - Generated by typeshare 1.0.0 + Generated by typeshare 1.1.0 */ import Foundation From c149d3820065e48e972cb73ce30768d62fc51203 Mon Sep 17 00:00:00 2001 From: Jane Lewis Date: Mon, 27 Mar 2023 22:33:10 -0700 Subject: [PATCH 5/8] Fix build and update tests --- core/data/tests/can_generate_generic_enum/output.ts | 4 ---- core/data/tests/orders_types/output.go | 1 - core/data/tests/orders_types/output.kt | 4 ---- core/data/tests/orders_types/output.swift | 4 ---- core/src/language/mod.rs | 2 -- 5 files changed, 15 deletions(-) diff --git a/core/data/tests/can_generate_generic_enum/output.ts b/core/data/tests/can_generate_generic_enum/output.ts index b8ada95d..d02fc456 100644 --- a/core/data/tests/can_generate_generic_enum/output.ts +++ b/core/data/tests/can_generate_generic_enum/output.ts @@ -1,7 +1,3 @@ -export interface StructUsingGenericEnum { - enum_field: GenericEnum; -} - export type GenericEnum = | { type: "VariantA", content: A } | { type: "VariantB", content: B }; diff --git a/core/data/tests/orders_types/output.go b/core/data/tests/orders_types/output.go index 20eb7f8c..0509d1ca 100644 --- a/core/data/tests/orders_types/output.go +++ b/core/data/tests/orders_types/output.go @@ -1,4 +1,3 @@ -// Code generated by typeshare 1.1.0. DO NOT EDIT. package proto import "encoding/json" diff --git a/core/data/tests/orders_types/output.kt b/core/data/tests/orders_types/output.kt index c4179df6..5a87b606 100644 --- a/core/data/tests/orders_types/output.kt +++ b/core/data/tests/orders_types/output.kt @@ -1,7 +1,3 @@ -/** - * Generated by typeshare 1.1.0 - */ - @file:NoLiveLiterals package com.agilebits.onepassword diff --git a/core/data/tests/orders_types/output.swift b/core/data/tests/orders_types/output.swift index 17bd3e21..fecc4ab4 100644 --- a/core/data/tests/orders_types/output.swift +++ b/core/data/tests/orders_types/output.swift @@ -1,5 +1 @@ -/* - Generated by typeshare 1.1.0 - */ - import Foundation diff --git a/core/src/language/mod.rs b/core/src/language/mod.rs index 767f36cd..6285ed45 100644 --- a/core/src/language/mod.rs +++ b/core/src/language/mod.rs @@ -1,8 +1,6 @@ use crate::{ - parser::ParsedData, parser::{ParseError, ParsedData}, rust_types::{Id, RustEnum, RustEnumVariant, RustStruct, RustThing, RustTypeAlias}, - rust_types::{Id, RustEnum, RustEnumVariant, RustStruct, RustTypeAlias}, topsort::topsort, }; use itertools::Itertools; From 869ec9566ebce957c0c95414b7c5527a6988eff5 Mon Sep 17 00:00:00 2001 From: Jane Lewis Date: Tue, 28 Mar 2023 11:41:16 -0700 Subject: [PATCH 6/8] Write orders_types snapshot test --- core/data/tests/orders_types/input.rs | 31 +++++++++++++++++ core/data/tests/orders_types/output.go | 16 +++++++++ core/data/tests/orders_types/output.kt | 26 ++++++++++++++ core/data/tests/orders_types/output.swift | 42 +++++++++++++++++++++++ 4 files changed, 115 insertions(+) diff --git a/core/data/tests/orders_types/input.rs b/core/data/tests/orders_types/input.rs index e69de29b..c74e91c0 100644 --- a/core/data/tests/orders_types/input.rs +++ b/core/data/tests/orders_types/input.rs @@ -0,0 +1,31 @@ +#[typeshare] +#[serde(rename_all = "camelCase")] +pub struct E { + depends_on: D, +} + +#[typeshare] +#[serde(rename_all = "camelCase")] +pub struct D { + depends_on: C, + also_depends_on: Option, +} + +#[typeshare] +#[serde(rename_all = "camelCase")] +pub struct C { + depends_on: B +} + +#[typeshare] +#[serde(rename_all = "camelCase")] +pub struct B { + depends_on: A, +} + +#[typeshare] +#[serde(rename_all = "camelCase")] +pub struct A { + field: u32 +} + diff --git a/core/data/tests/orders_types/output.go b/core/data/tests/orders_types/output.go index 0509d1ca..278a605d 100644 --- a/core/data/tests/orders_types/output.go +++ b/core/data/tests/orders_types/output.go @@ -2,3 +2,19 @@ package proto import "encoding/json" +type A struct { + Field uint32 `json:"field"` +} +type B struct { + DependsOn A `json:"dependsOn"` +} +type C struct { + DependsOn B `json:"dependsOn"` +} +type D struct { + DependsOn C `json:"dependsOn"` + AlsoDependsOn *E `json:"alsoDependsOn,omitempty"` +} +type E struct { + DependsOn D `json:"dependsOn"` +} diff --git a/core/data/tests/orders_types/output.kt b/core/data/tests/orders_types/output.kt index 5a87b606..d746fffa 100644 --- a/core/data/tests/orders_types/output.kt +++ b/core/data/tests/orders_types/output.kt @@ -5,3 +5,29 @@ package com.agilebits.onepassword import androidx.compose.runtime.NoLiveLiterals import kotlinx.serialization.* +@Serializable +data class A ( + val field: UInt +) + +@Serializable +data class B ( + val dependsOn: A +) + +@Serializable +data class C ( + val dependsOn: B +) + +@Serializable +data class D ( + val dependsOn: C, + val alsoDependsOn: E? = null +) + +@Serializable +data class E ( + val dependsOn: D +) + diff --git a/core/data/tests/orders_types/output.swift b/core/data/tests/orders_types/output.swift index fecc4ab4..3b5277a4 100644 --- a/core/data/tests/orders_types/output.swift +++ b/core/data/tests/orders_types/output.swift @@ -1 +1,43 @@ import Foundation + +public struct A: Codable { + public let field: UInt32 + + public init(field: UInt32) { + self.field = field + } +} + +public struct B: Codable { + public let dependsOn: A + + public init(dependsOn: A) { + self.dependsOn = dependsOn + } +} + +public struct C: Codable { + public let dependsOn: B + + public init(dependsOn: B) { + self.dependsOn = dependsOn + } +} + +public struct D: Codable { + public let dependsOn: C + public let alsoDependsOn: E? + + public init(dependsOn: C, alsoDependsOn: E?) { + self.dependsOn = dependsOn + self.alsoDependsOn = alsoDependsOn + } +} + +public struct E: Codable { + public let dependsOn: D + + public init(dependsOn: D) { + self.dependsOn = dependsOn + } +} From 6af5286d7e135cd6986ead9bee1b6cba62195e48 Mon Sep 17 00:00:00 2001 From: Jane Lewis Date: Wed, 29 Mar 2023 21:16:20 -0700 Subject: [PATCH 7/8] Refactor code to use a single RustItem type --- core/src/language/go.rs | 18 +++++++++--------- core/src/language/mod.rs | 18 +++++++++--------- core/src/parser.rs | 36 ++++++++++++++---------------------- core/src/rust_types.rs | 21 ++++++++++++--------- core/src/topsort.rs | 30 +++++++++++++++--------------- 5 files changed, 59 insertions(+), 64 deletions(-) diff --git a/core/src/language/go.rs b/core/src/language/go.rs index e00157de..a260c2f9 100644 --- a/core/src/language/go.rs +++ b/core/src/language/go.rs @@ -2,7 +2,7 @@ use std::io::Write; use crate::parser::ParsedData; use crate::rename::RenameExt; -use crate::rust_types::{RustThing, RustTypeFormatError, SpecialRustType}; +use crate::rust_types::{RustItem, RustTypeFormatError, SpecialRustType}; use crate::{ language::Language, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, @@ -40,27 +40,27 @@ impl Language for Go { self.begin_file(w)?; - let mut things: Vec = vec![]; + let mut items: Vec = vec![]; for a in &data.aliases { - things.push(RustThing::TypeAlias(a)) + items.push(RustItem::Alias(a.clone())) } for s in &data.structs { - things.push(RustThing::Struct(s)) + items.push(RustItem::Struct(s.clone())) } for e in &data.enums { - things.push(RustThing::Enum(e)) + items.push(RustItem::Enum(e.clone())) } - let sorted = topsort(things.iter().collect()); + let sorted = topsort(items.iter().collect()); for &thing in &sorted { match thing { - RustThing::Enum(e) => self.write_enum(w, e, &types_mapping_to_struct)?, - RustThing::Struct(s) => self.write_struct(w, s)?, - RustThing::TypeAlias(a) => self.write_type_alias(w, a)?, + RustItem::Enum(e) => self.write_enum(w, e, &types_mapping_to_struct)?, + RustItem::Struct(s) => self.write_struct(w, s)?, + RustItem::Alias(a) => self.write_type_alias(w, a)?, } } diff --git a/core/src/language/mod.rs b/core/src/language/mod.rs index 6285ed45..198a0296 100644 --- a/core/src/language/mod.rs +++ b/core/src/language/mod.rs @@ -1,6 +1,6 @@ use crate::{ parser::{ParseError, ParsedData}, - rust_types::{Id, RustEnum, RustEnumVariant, RustStruct, RustThing, RustTypeAlias}, + rust_types::{Id, RustEnum, RustEnumVariant, RustItem, RustStruct, RustTypeAlias}, topsort::topsort, }; use itertools::Itertools; @@ -69,27 +69,27 @@ pub trait Language { ) -> std::io::Result<()> { self.begin_file(writable)?; - let mut things: Vec = vec![]; + let mut items: Vec = vec![]; for a in &data.aliases { - things.push(RustThing::TypeAlias(a)) + items.push(RustItem::Alias(a.clone())) } for s in &data.structs { - things.push(RustThing::Struct(s)) + items.push(RustItem::Struct(s.clone())) } for e in &data.enums { - things.push(RustThing::Enum(e)) + items.push(RustItem::Enum(e.clone())) } - let sorted = topsort(things.iter().collect()); + let sorted = topsort(items.iter().collect()); for &thing in &sorted { match thing { - RustThing::Enum(e) => self.write_enum(writable, e)?, - RustThing::Struct(s) => self.write_struct(writable, s)?, - RustThing::TypeAlias(a) => self.write_type_alias(writable, a)?, + RustItem::Enum(e) => self.write_enum(writable, e)?, + RustItem::Struct(s) => self.write_struct(writable, s)?, + RustItem::Alias(a) => self.write_type_alias(writable, a)?, } } diff --git a/core/src/parser.rs b/core/src/parser.rs index c359fccd..d3e6bf6a 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -2,7 +2,7 @@ use crate::{ language::SupportedLanguage, rename::RenameExt, rust_types::{ - Id, RustEnum, RustEnumShared, RustEnumVariant, RustEnumVariantShared, RustField, + Id, RustEnum, RustEnumShared, RustEnumVariant, RustEnumVariantShared, RustField, RustItem, RustStruct, RustType, RustTypeAlias, RustTypeParseError, }, }; @@ -40,11 +40,11 @@ impl ParsedData { self.aliases.append(&mut other.aliases); } - fn push_rust_thing(&mut self, rust_thing: RustThing) { + fn push_rust_thing(&mut self, rust_thing: RustItem) { match rust_thing { - RustThing::Struct(s) => self.structs.push(s), - RustThing::Enum(e) => self.enums.push(e), - RustThing::Alias(a) => self.aliases.push(a), + RustItem::Struct(s) => self.structs.push(s), + RustItem::Enum(e) => self.enums.push(e), + RustItem::Alias(a) => self.aliases.push(a), } } } @@ -106,20 +106,12 @@ pub fn parse(input: &str) -> Result { Ok(parsed_data) } -/// Allows parsing functions to return different things. -// TODO: this exists to allow for hacks in the code below, remove this -enum RustThing { - Struct(RustStruct), - Enum(RustEnum), - Alias(RustTypeAlias), -} - /// Parses a struct into a definition that more succinctly represents what /// typeshare needs to generate code for other languages. /// /// This function can currently return something other than a struct, which is a /// hack. -fn parse_struct(s: &ItemStruct) -> Result { +fn parse_struct(s: &ItemStruct) -> Result { let serde_rename_all = serde_rename_all(&s.attrs); let generic_types = s @@ -136,7 +128,7 @@ fn parse_struct(s: &ItemStruct) -> Result { // TODO: we shouldn't lie and return a type alias when parsing a struct. this // is a temporary hack if let Some(ty) = get_serialized_as_type(&s.attrs) { - return Ok(RustThing::Alias(RustTypeAlias { + return Ok(RustItem::Alias(RustTypeAlias { id: get_ident(Some(&s.ident), &s.attrs, &None), r#type: ty.parse()?, comments: parse_comment_attrs(&s.attrs), @@ -171,7 +163,7 @@ fn parse_struct(s: &ItemStruct) -> Result { }) .collect::>()?; - RustThing::Struct(RustStruct { + RustItem::Struct(RustStruct { id: get_ident(Some(&s.ident), &s.attrs, &None), generic_types, fields, @@ -192,7 +184,7 @@ fn parse_struct(s: &ItemStruct) -> Result { RustType::try_from(&f.ty)? }; - RustThing::Alias(RustTypeAlias { + RustItem::Alias(RustTypeAlias { id: get_ident(Some(&s.ident), &s.attrs, &None), r#type: ty, comments: parse_comment_attrs(&s.attrs), @@ -200,7 +192,7 @@ fn parse_struct(s: &ItemStruct) -> Result { }) } // Unit structs or `None` - Fields::Unit => RustThing::Struct(RustStruct { + Fields::Unit => RustItem::Struct(RustStruct { id: get_ident(Some(&s.ident), &s.attrs, &None), generic_types, fields: vec![], @@ -215,7 +207,7 @@ fn parse_struct(s: &ItemStruct) -> Result { /// /// This function can currently return something other than an enum, which is a /// hack. -fn parse_enum(e: &ItemEnum) -> Result { +fn parse_enum(e: &ItemEnum) -> Result { let generic_types = e .generics .params @@ -231,7 +223,7 @@ fn parse_enum(e: &ItemEnum) -> Result { // TODO: we shouldn't lie and return a type alias when parsing an enum. this // is a temporary hack if let Some(ty) = get_serialized_as_type(&e.attrs) { - return Ok(RustThing::Alias(RustTypeAlias { + return Ok(RustItem::Alias(RustTypeAlias { id: get_ident(Some(&e.ident), &e.attrs, &None), r#type: ty.parse()?, comments: parse_comment_attrs(&e.attrs), @@ -291,7 +283,7 @@ fn parse_enum(e: &ItemEnum) -> Result { }); } - Ok(RustThing::Enum(RustEnum::Unit(shared))) + Ok(RustItem::Enum(RustEnum::Unit(shared))) } else { // At least one enum variant is either a tuple or an anonymous struct @@ -302,7 +294,7 @@ fn parse_enum(e: &ItemEnum) -> Result { enum_ident: original_enum_ident.clone(), })?; - Ok(RustThing::Enum(RustEnum::Algebraic { + Ok(RustItem::Enum(RustEnum::Algebraic { tag_key, content_key, shared, diff --git a/core/src/rust_types.rs b/core/src/rust_types.rs index eb8679a6..0a55a1fd 100644 --- a/core/src/rust_types.rs +++ b/core/src/rust_types.rs @@ -399,7 +399,7 @@ impl SpecialRustType { } /// Parsed information about a Rust enum definition -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum RustEnum { /// A unit enum /// @@ -449,7 +449,7 @@ impl RustEnum { } /// Enum information shared among different enum types -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct RustEnumShared { /// The enum's ident pub id: Id, @@ -469,7 +469,7 @@ pub struct RustEnumShared { } /// Parsed information about a Rust enum variant -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum RustEnumVariant { /// A unit variant Unit(RustEnumVariantShared), @@ -501,7 +501,7 @@ impl RustEnumVariant { } /// Variant information shared among different variant types -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct RustEnumVariantShared { /// The variant's ident pub id: Id, @@ -509,9 +509,12 @@ pub struct RustEnumVariantShared { pub comments: Vec, } -#[derive(Debug, PartialEq)] -pub enum RustThing<'a> { - Enum(&'a RustEnum), - TypeAlias(&'a RustTypeAlias), - Struct(&'a RustStruct), +/// An enum that encapsulates units of code generation for Typeshare. +/// Analogous to `syn::Item`, even though our variants are more limited. +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub enum RustItem { + Struct(RustStruct), + Enum(RustEnum), + Alias(RustTypeAlias), } diff --git a/core/src/topsort.rs b/core/src/topsort.rs index 0d4db265..80526c52 100644 --- a/core/src/topsort.rs +++ b/core/src/topsort.rs @@ -1,12 +1,12 @@ use std::collections::{HashMap, HashSet}; use crate::rust_types::{ - RustEnum, RustEnumVariant, RustStruct, RustThing, RustType, RustTypeAlias, SpecialRustType, + RustEnum, RustEnumVariant, RustItem, RustStruct, RustType, RustTypeAlias, SpecialRustType, }; fn get_dependencies_from_type( tp: &RustType, - types: &HashMap, + types: &HashMap, res: &mut Vec, seen: &mut HashSet, ) { @@ -58,7 +58,7 @@ fn get_dependencies_from_type( fn get_enum_dependencies( enm: &RustEnum, - types: &HashMap, + types: &HashMap, res: &mut Vec, seen: &mut HashSet, ) { @@ -91,7 +91,7 @@ fn get_enum_dependencies( fn get_struct_dependencies( strct: &RustStruct, - types: &HashMap, + types: &HashMap, res: &mut Vec, seen: &mut HashSet, ) { @@ -105,7 +105,7 @@ fn get_struct_dependencies( fn get_type_alias_dependencies( ta: &RustTypeAlias, - types: &HashMap, + types: &HashMap, res: &mut Vec, seen: &mut HashSet, ) { @@ -121,19 +121,19 @@ fn get_type_alias_dependencies( } fn get_dependencies( - thing: &RustThing, - types: &HashMap, + thing: &RustItem, + types: &HashMap, res: &mut Vec, seen: &mut HashSet, ) { match thing { - RustThing::Enum(en) => get_enum_dependencies(en, types, res, seen), - RustThing::Struct(strct) => get_struct_dependencies(strct, types, res, seen), - RustThing::TypeAlias(alias) => get_type_alias_dependencies(alias, types, res, seen), + RustItem::Enum(en) => get_enum_dependencies(en, types, res, seen), + RustItem::Struct(strct) => get_struct_dependencies(strct, types, res, seen), + RustItem::Alias(alias) => get_type_alias_dependencies(alias, types, res, seen), } } -fn get_index(thing: &RustThing, things: &[&RustThing]) -> usize { +fn get_index(thing: &RustItem, things: &[&RustItem]) -> usize { things .iter() .position(|&r| r == thing) @@ -180,10 +180,10 @@ fn toposort_impl(graph: &Vec>) -> Vec { res } -pub(crate) fn topsort<'a>(things: Vec<&'a RustThing>) -> Vec<&'a RustThing<'a>> { +pub(crate) fn topsort<'a>(things: Vec<&'a RustItem>) -> Vec<&'a RustItem> { let types = HashMap::from_iter(things.iter().map(|&thing| { let id = match thing { - RustThing::Enum(e) => match e { + RustItem::Enum(e) => match e { RustEnum::Algebraic { tag_key: _, content_key: _, @@ -191,8 +191,8 @@ pub(crate) fn topsort<'a>(things: Vec<&'a RustThing>) -> Vec<&'a RustThing<'a>> } => shared.id.original.clone(), RustEnum::Unit(shared) => shared.id.original.clone(), }, - RustThing::Struct(strct) => strct.id.original.clone(), - RustThing::TypeAlias(ta) => ta.id.original.clone(), + RustItem::Struct(strct) => strct.id.original.clone(), + RustItem::Alias(ta) => ta.id.original.clone(), }; (id, thing) })); From c36944f8bf648398d3b401831f79fe1c55b2796d Mon Sep 17 00:00:00 2001 From: Jane Lewis Date: Thu, 30 Mar 2023 13:25:37 -0700 Subject: [PATCH 8/8] Clippy --- core/src/rust_types.rs | 3 +++ core/src/topsort.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/rust_types.rs b/core/src/rust_types.rs index 0a55a1fd..2c2223ba 100644 --- a/core/src/rust_types.rs +++ b/core/src/rust_types.rs @@ -514,7 +514,10 @@ pub struct RustEnumVariantShared { #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub enum RustItem { + /// A `struct` definition Struct(RustStruct), + /// An `enum` definition Enum(RustEnum), + /// A `type` definition or newtype struct. Alias(RustTypeAlias), } diff --git a/core/src/topsort.rs b/core/src/topsort.rs index 80526c52..68ef37ea 100644 --- a/core/src/topsort.rs +++ b/core/src/topsort.rs @@ -180,7 +180,7 @@ fn toposort_impl(graph: &Vec>) -> Vec { res } -pub(crate) fn topsort<'a>(things: Vec<&'a RustItem>) -> Vec<&'a RustItem> { +pub(crate) fn topsort(things: Vec<&RustItem>) -> Vec<&RustItem> { let types = HashMap::from_iter(things.iter().map(|&thing| { let id = match thing { RustItem::Enum(e) => match e {