Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 13 additions & 20 deletions specta-serde/src/validate.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::{BTreeMap, HashSet};
use std::collections::HashSet;

use specta::{
datatype::{DataType, Enum, EnumRepr, Fields, Generic, Literal, Primitive},
Expand All @@ -13,28 +13,18 @@ use crate::Error;
/// Validate the type and apply the Serde transformations.
pub fn validate(types: &TypeCollection) -> Result<(), Error> {
for ndt in types.into_unsorted_iter() {
inner(
ndt.ty(),
&types,
&Default::default(),
&mut Default::default(),
)?;
inner(ndt.ty(), &types, &[], &mut Default::default())?;
}

Ok(())
}

// TODO: Remove this once we redo the Typescript exporter.
pub fn validate_dt(ty: &DataType, types: &TypeCollection) -> Result<(), Error> {
inner(ty, &types, &Default::default(), &mut Default::default())?;
inner(ty, &types, &[], &mut Default::default())?;

for ndt in types.into_unsorted_iter() {
inner(
ndt.ty(),
&types,
&Default::default(),
&mut Default::default(),
)?;
inner(ndt.ty(), &types, &[], &mut Default::default())?;
}

Ok(())
Expand All @@ -43,7 +33,7 @@ pub fn validate_dt(ty: &DataType, types: &TypeCollection) -> Result<(), Error> {
fn inner(
dt: &DataType,
types: &TypeCollection,
generics: &BTreeMap<Generic, DataType>,
generics: &[(Generic, DataType)],
checked_references: &mut HashSet<SpectaID>,
) -> Result<(), Error> {
match dt {
Expand Down Expand Up @@ -91,7 +81,7 @@ fn inner(
}
DataType::Reference(r) => {
for (_, dt) in r.generics() {
inner(dt, types, &Default::default(), checked_references)?;
inner(dt, types, &[], checked_references)?;
}

#[allow(clippy::panic)]
Expand All @@ -113,7 +103,7 @@ fn inner(
fn is_valid_map_key(
key_ty: &DataType,
types: &TypeCollection,
generics: &BTreeMap<Generic, DataType>,
generics: &[(Generic, DataType)],
) -> Result<(), Error> {
match key_ty {
DataType::Primitive(ty) => match ty {
Expand Down Expand Up @@ -177,13 +167,16 @@ fn is_valid_map_key(
}
DataType::Reference(r) => {
let ty = types.get(r.sid()).expect("Type was never populated"); // TODO: Error properly

is_valid_map_key(ty.ty(), types, r.generics())
}
DataType::Generic(g) => {
let ty = generics.get(g).expect("bruh");
let ty = generics
.iter()
.find(|(gen, _)| gen == g)
.map(|(_, dt)| dt)
.expect("bruh");

is_valid_map_key(&ty, types, &Default::default())
is_valid_map_key(ty, types, &[])
}
_ => Err(Error::InvalidMapKey),
}
Expand Down
34 changes: 19 additions & 15 deletions specta-typescript/src/inline.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
//! Helpers for generating [Type::reference] implementations.

use std::collections::HashMap;

use specta::TypeCollection;

use specta::datatype::{DataType, Field, Fields, Generic, NamedDataType};

#[doc(hidden)] // TODO: Make this private
pub fn inline_and_flatten_ndt(ndt: &mut NamedDataType, types: &TypeCollection) {
inner(ndt.ty_mut(), types, false, false, &Default::default(), 0);
inner(ndt.ty_mut(), types, false, false, &[], 0);
}

pub(crate) fn inline(dt: &mut DataType, types: &TypeCollection) {
inner(dt, types, false, true, &Default::default(), 0)
inner(dt, types, false, true, &[], 0)
}

fn field(
f: &mut Field,
types: &TypeCollection,
truely_force_inline: bool,
generics: &HashMap<Generic, DataType>,
generics: &[(Generic, DataType)],
depth: usize,
) {
// TODO: truely_force_inline
Expand All @@ -38,7 +36,7 @@ fn fields(
f: &mut Fields,
types: &TypeCollection,
truely_force_inline: bool,
generics: &HashMap<Generic, DataType>,
generics: &[(Generic, DataType)],
depth: usize,
) {
match f {
Expand All @@ -61,7 +59,7 @@ fn inner(
types: &TypeCollection,
force_inline: bool,
truely_force_inline: bool,
generics: &HashMap<Generic, DataType>,
generics: &[(Generic, DataType)],
depth: usize,
) {
// TODO: Can we be smart enough to determine loops, instead of just trying X times and bailing out????
Expand Down Expand Up @@ -125,15 +123,20 @@ fn inner(
}
}
DataType::Generic(g) => {
let mut ty = generics.get(g).unwrap().clone(); // TODO: Properly handle this error
let mut ty = generics
.iter()
.find(|(gen, _)| gen == g)
.map(|(_, dt)| dt)
.unwrap()
.clone(); // TODO: Properly handle this error

if truely_force_inline {
inner(
&mut ty,
types,
false,
truely_force_inline,
&Default::default(), // TODO: What should this be?
&[], // TODO: What should this be?
depth + 1,
);
*dt = ty;
Expand All @@ -150,13 +153,13 @@ fn inner(
false,
truely_force_inline,
&r.generics()
.clone()
.into_iter()
.iter()
.cloned()
.map(|(g, mut dt)| {
resolve_generics(&mut dt, generics);
(g, dt)
})
.collect(),
.collect::<Vec<_>>(),
depth + 1,
);
*dt = ty;
Expand All @@ -168,7 +171,7 @@ fn inner(
}

/// Following all `DataType::Reference`'s filling in any `DataType::Generic`'s with the correct value.
fn resolve_generics(dt: &mut DataType, generics: &HashMap<Generic, DataType>) {
fn resolve_generics(dt: &mut DataType, generics: &[(Generic, DataType)]) {
// TODO: This could so only re-alloc if the type has a generics that needs replacing.
match dt {
DataType::List(l) => {
Expand Down Expand Up @@ -201,8 +204,9 @@ fn resolve_generics(dt: &mut DataType, generics: &HashMap<Generic, DataType>) {
// This method is run when not inlining so for `export` we do expect `DataType::Generic`.
// TODO: Functions main documentation should explain this.
*dt = generics
.get(g)
.cloned()
.iter()
.find(|(gen, _)| gen == g)
.map(|(_, dt)| dt.clone())
.unwrap_or(DataType::Generic(g.clone()));
}
_ => {}
Expand Down
10 changes: 4 additions & 6 deletions specta/src/datatype/reference.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//! Helpers for generating [Type::reference] implementations.

use std::collections::BTreeMap;

use crate::SpectaID;

use super::{DataType, Generic};
Expand All @@ -11,15 +9,15 @@ use super::{DataType, Generic};
#[non_exhaustive]
pub struct Reference {
pub(crate) sid: SpectaID,
pub(crate) generics: BTreeMap<Generic, DataType>,
pub(crate) generics: Vec<(Generic, DataType)>,
pub(crate) inline: bool,
}

impl Reference {
/// TODO: Explain invariant.
pub fn construct(
sid: SpectaID,
generics: impl Into<BTreeMap<Generic, DataType>>,
generics: impl Into<Vec<(Generic, DataType)>>,
inline: bool,
) -> Self {
Self {
Expand All @@ -35,12 +33,12 @@ impl Reference {
}

/// Get the generic parameters set on this reference which will be filled in by the [NamedDataType].
pub fn generics(&self) -> &BTreeMap<Generic, DataType> {
pub fn generics(&self) -> &[(Generic, DataType)] {
&self.generics
}

/// Get the generic parameters set on this reference which will be filled in by the [NamedDataType].
pub fn generics_mut(&mut self) -> &mut BTreeMap<Generic, DataType> {
pub fn generics_mut(&mut self) -> &mut Vec<(Generic, DataType)> {
&mut self.generics
}

Expand Down
22 changes: 22 additions & 0 deletions tests/tests/ts_rs/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,25 @@ fn inline() {

// assert_ts_export!(D::<&str, 41>, "export type D<T> = { t: Array<T>, }")
// }

// https://github.com/specta-rs/specta/issues/400
#[test]
fn generic_parameter_order_preserved() {
#[derive(Type)]
#[specta(export = false)]
struct Pair<Z, A> {
first: Z,
second: A,
}

#[derive(Type)]
#[specta(export = false)]
struct Container {
pair: Pair<i32, String>,
}

assert_ts_export!(
Container,
"export type Container = { pair: Pair<number, string> };"
);
}
Loading