Skip to content

Commit effe988

Browse files
Merge pull request #410 from Vlad-Shcherbina/generics-args-order
Keep order of generic args (fixes #400)
2 parents 8095af4 + 9e7d719 commit effe988

File tree

4 files changed

+58
-41
lines changed

4 files changed

+58
-41
lines changed

specta-serde/src/validate.rs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::{BTreeMap, HashSet};
1+
use std::collections::HashSet;
22

33
use specta::{
44
datatype::{DataType, Enum, EnumRepr, Fields, Generic, Literal, Primitive},
@@ -13,28 +13,18 @@ use crate::Error;
1313
/// Validate the type and apply the Serde transformations.
1414
pub fn validate(types: &TypeCollection) -> Result<(), Error> {
1515
for ndt in types.into_unsorted_iter() {
16-
inner(
17-
ndt.ty(),
18-
&types,
19-
&Default::default(),
20-
&mut Default::default(),
21-
)?;
16+
inner(ndt.ty(), &types, &[], &mut Default::default())?;
2217
}
2318

2419
Ok(())
2520
}
2621

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

3126
for ndt in types.into_unsorted_iter() {
32-
inner(
33-
ndt.ty(),
34-
&types,
35-
&Default::default(),
36-
&mut Default::default(),
37-
)?;
27+
inner(ndt.ty(), &types, &[], &mut Default::default())?;
3828
}
3929

4030
Ok(())
@@ -43,7 +33,7 @@ pub fn validate_dt(ty: &DataType, types: &TypeCollection) -> Result<(), Error> {
4333
fn inner(
4434
dt: &DataType,
4535
types: &TypeCollection,
46-
generics: &BTreeMap<Generic, DataType>,
36+
generics: &[(Generic, DataType)],
4737
checked_references: &mut HashSet<SpectaID>,
4838
) -> Result<(), Error> {
4939
match dt {
@@ -91,7 +81,7 @@ fn inner(
9181
}
9282
DataType::Reference(r) => {
9383
for (_, dt) in r.generics() {
94-
inner(dt, types, &Default::default(), checked_references)?;
84+
inner(dt, types, &[], checked_references)?;
9585
}
9686

9787
#[allow(clippy::panic)]
@@ -113,7 +103,7 @@ fn inner(
113103
fn is_valid_map_key(
114104
key_ty: &DataType,
115105
types: &TypeCollection,
116-
generics: &BTreeMap<Generic, DataType>,
106+
generics: &[(Generic, DataType)],
117107
) -> Result<(), Error> {
118108
match key_ty {
119109
DataType::Primitive(ty) => match ty {
@@ -177,13 +167,16 @@ fn is_valid_map_key(
177167
}
178168
DataType::Reference(r) => {
179169
let ty = types.get(r.sid()).expect("Type was never populated"); // TODO: Error properly
180-
181170
is_valid_map_key(ty.ty(), types, r.generics())
182171
}
183172
DataType::Generic(g) => {
184-
let ty = generics.get(g).expect("bruh");
173+
let ty = generics
174+
.iter()
175+
.find(|(gen, _)| gen == g)
176+
.map(|(_, dt)| dt)
177+
.expect("bruh");
185178

186-
is_valid_map_key(&ty, types, &Default::default())
179+
is_valid_map_key(ty, types, &[])
187180
}
188181
_ => Err(Error::InvalidMapKey),
189182
}

specta-typescript/src/inline.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
//! Helpers for generating [Type::reference] implementations.
22
3-
use std::collections::HashMap;
4-
53
use specta::TypeCollection;
64

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

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

1412
pub(crate) fn inline(dt: &mut DataType, types: &TypeCollection) {
15-
inner(dt, types, false, true, &Default::default(), 0)
13+
inner(dt, types, false, true, &[], 0)
1614
}
1715

1816
fn field(
1917
f: &mut Field,
2018
types: &TypeCollection,
2119
truely_force_inline: bool,
22-
generics: &HashMap<Generic, DataType>,
20+
generics: &[(Generic, DataType)],
2321
depth: usize,
2422
) {
2523
// TODO: truely_force_inline
@@ -38,7 +36,7 @@ fn fields(
3836
f: &mut Fields,
3937
types: &TypeCollection,
4038
truely_force_inline: bool,
41-
generics: &HashMap<Generic, DataType>,
39+
generics: &[(Generic, DataType)],
4240
depth: usize,
4341
) {
4442
match f {
@@ -61,7 +59,7 @@ fn inner(
6159
types: &TypeCollection,
6260
force_inline: bool,
6361
truely_force_inline: bool,
64-
generics: &HashMap<Generic, DataType>,
62+
generics: &[(Generic, DataType)],
6563
depth: usize,
6664
) {
6765
// TODO: Can we be smart enough to determine loops, instead of just trying X times and bailing out????
@@ -125,15 +123,20 @@ fn inner(
125123
}
126124
}
127125
DataType::Generic(g) => {
128-
let mut ty = generics.get(g).unwrap().clone(); // TODO: Properly handle this error
126+
let mut ty = generics
127+
.iter()
128+
.find(|(gen, _)| gen == g)
129+
.map(|(_, dt)| dt)
130+
.unwrap()
131+
.clone(); // TODO: Properly handle this error
129132

130133
if truely_force_inline {
131134
inner(
132135
&mut ty,
133136
types,
134137
false,
135138
truely_force_inline,
136-
&Default::default(), // TODO: What should this be?
139+
&[], // TODO: What should this be?
137140
depth + 1,
138141
);
139142
*dt = ty;
@@ -150,13 +153,13 @@ fn inner(
150153
false,
151154
truely_force_inline,
152155
&r.generics()
153-
.clone()
154-
.into_iter()
156+
.iter()
157+
.cloned()
155158
.map(|(g, mut dt)| {
156159
resolve_generics(&mut dt, generics);
157160
(g, dt)
158161
})
159-
.collect(),
162+
.collect::<Vec<_>>(),
160163
depth + 1,
161164
);
162165
*dt = ty;
@@ -168,7 +171,7 @@ fn inner(
168171
}
169172

170173
/// Following all `DataType::Reference`'s filling in any `DataType::Generic`'s with the correct value.
171-
fn resolve_generics(dt: &mut DataType, generics: &HashMap<Generic, DataType>) {
174+
fn resolve_generics(dt: &mut DataType, generics: &[(Generic, DataType)]) {
172175
// TODO: This could so only re-alloc if the type has a generics that needs replacing.
173176
match dt {
174177
DataType::List(l) => {
@@ -201,8 +204,9 @@ fn resolve_generics(dt: &mut DataType, generics: &HashMap<Generic, DataType>) {
201204
// This method is run when not inlining so for `export` we do expect `DataType::Generic`.
202205
// TODO: Functions main documentation should explain this.
203206
*dt = generics
204-
.get(g)
205-
.cloned()
207+
.iter()
208+
.find(|(gen, _)| gen == g)
209+
.map(|(_, dt)| dt.clone())
206210
.unwrap_or(DataType::Generic(g.clone()));
207211
}
208212
_ => {}

specta/src/datatype/reference.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//! Helpers for generating [Type::reference] implementations.
22
3-
use std::collections::BTreeMap;
4-
53
use crate::SpectaID;
64

75
use super::{DataType, Generic};
@@ -11,15 +9,15 @@ use super::{DataType, Generic};
119
#[non_exhaustive]
1210
pub struct Reference {
1311
pub(crate) sid: SpectaID,
14-
pub(crate) generics: BTreeMap<Generic, DataType>,
12+
pub(crate) generics: Vec<(Generic, DataType)>,
1513
pub(crate) inline: bool,
1614
}
1715

1816
impl Reference {
1917
/// TODO: Explain invariant.
2018
pub fn construct(
2119
sid: SpectaID,
22-
generics: impl Into<BTreeMap<Generic, DataType>>,
20+
generics: impl Into<Vec<(Generic, DataType)>>,
2321
inline: bool,
2422
) -> Self {
2523
Self {
@@ -35,12 +33,12 @@ impl Reference {
3533
}
3634

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

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

tests/tests/ts_rs/generics.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,25 @@ fn inline() {
219219

220220
// assert_ts_export!(D::<&str, 41>, "export type D<T> = { t: Array<T>, }")
221221
// }
222+
223+
// https://github.com/specta-rs/specta/issues/400
224+
#[test]
225+
fn generic_parameter_order_preserved() {
226+
#[derive(Type)]
227+
#[specta(export = false)]
228+
struct Pair<Z, A> {
229+
first: Z,
230+
second: A,
231+
}
232+
233+
#[derive(Type)]
234+
#[specta(export = false)]
235+
struct Container {
236+
pair: Pair<i32, String>,
237+
}
238+
239+
assert_ts_export!(
240+
Container,
241+
"export type Container = { pair: Pair<number, string> };"
242+
);
243+
}

0 commit comments

Comments
 (0)