Skip to content

Commit 2eb2c7f

Browse files
extract spec gen and marker logic into shared helper
1 parent 3b64a8c commit 2eb2c7f

File tree

6 files changed

+79
-191
lines changed

6 files changed

+79
-191
lines changed

soroban-sdk-macros/src/derive_enum.rs

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use itertools::MultiUnzip;
22
use proc_macro2::{Literal, TokenStream as TokenStream2};
33
use quote::{format_ident, quote, ToTokens};
4-
use syn::{spanned::Spanned, Attribute, DataEnum, Error, Fields, Ident, Path, Visibility};
4+
use syn::{spanned::Spanned, Attribute, DataEnum, Error, Fields, Ident, Path, Type, Visibility};
55

66
use stellar_xdr::curr as stellar_xdr;
77
use stellar_xdr::{
@@ -156,48 +156,18 @@ pub fn derive_type_enum(
156156
None
157157
};
158158

159-
// Generated code spec.
160-
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
161-
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
162-
let spec_xdr_len = spec_xdr.len();
163-
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", enum_ident.to_string().to_uppercase());
164-
Some(quote! {
165-
#[cfg_attr(target_family = "wasm", link_section = "contractspecv0")]
166-
pub static #spec_ident: [u8; #spec_xdr_len] = #enum_ident::spec_xdr();
167-
168-
impl #enum_ident {
169-
pub const fn spec_xdr() -> [u8; #spec_xdr_len] {
170-
*#spec_xdr_lit
171-
}
172-
}
159+
// Flatten all variant field types for shaking calls, deduplicating
160+
// to avoid redundant calls for types that appear in multiple variants.
161+
let all_field_types: Vec<&Type> =
162+
itertools::Itertools::unique_by(variant_field_types.iter().flatten(), |t| {
163+
t.to_token_stream().to_string()
173164
})
174-
} else {
175-
None
176-
};
165+
.copied()
166+
.collect();
177167

178-
// SpecShakingMarker impl - only generated when spec is true and the
179-
// experimental_spec_shaking_v2 feature is enabled.
180-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
181-
spec_xdr.as_ref().map(|spec_xdr| {
182-
// Flatten all variant field types for shaking calls, deduplicating
183-
// to avoid redundant calls for types that appear in multiple variants.
184-
let all_field_types =
185-
itertools::Itertools::unique_by(variant_field_types.iter().flatten(), |t| {
186-
t.to_token_stream().to_string()
187-
});
188-
shaking::generate_marker_impl(
189-
path,
190-
quote!(#enum_ident),
191-
spec_xdr,
192-
all_field_types.cloned(),
193-
None,
194-
None,
195-
None,
196-
)
197-
})
198-
} else {
199-
None
200-
};
168+
// Generated code spec and SpecShakingMarker impl.
169+
let (spec_gen, spec_shaking_impl) =
170+
shaking::generate_type_spec_and_marker(path, &enum_ident, &spec_xdr, &all_field_types);
201171

202172
// Output.
203173
let mut output = quote! {

soroban-sdk-macros/src/derive_enum_int.rs

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use itertools::MultiUnzip;
22
use proc_macro2::TokenStream as TokenStream2;
3-
use quote::{format_ident, quote};
3+
use quote::quote;
44
use stellar_xdr::curr as stellar_xdr;
55
use stellar_xdr::{ScSpecUdtEnumV0, StringM};
66
use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path, Visibility};
@@ -78,42 +78,9 @@ pub fn derive_type_enum_int(
7878
None
7979
};
8080

81-
// Generated code spec.
82-
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
83-
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
84-
let spec_xdr_len = spec_xdr.len();
85-
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", enum_ident.to_string().to_uppercase());
86-
Some(quote! {
87-
#[cfg_attr(target_family = "wasm", link_section = "contractspecv0")]
88-
pub static #spec_ident: [u8; #spec_xdr_len] = #enum_ident::spec_xdr();
89-
90-
impl #enum_ident {
91-
pub const fn spec_xdr() -> [u8; #spec_xdr_len] {
92-
*#spec_xdr_lit
93-
}
94-
}
95-
})
96-
} else {
97-
None
98-
};
99-
100-
// SpecShakingMarker impl - only generated when spec is true and the
101-
// experimental_spec_shaking_v2 feature is enabled.
102-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
103-
spec_xdr.as_ref().map(|spec_xdr| {
104-
shaking::generate_marker_impl(
105-
path,
106-
quote!(#enum_ident),
107-
spec_xdr,
108-
std::iter::empty(),
109-
None,
110-
None,
111-
None,
112-
)
113-
})
114-
} else {
115-
None
116-
};
81+
// Generated code spec and SpecShakingMarker impl.
82+
let (spec_gen, spec_shaking_impl) =
83+
shaking::generate_type_spec_and_marker(path, &enum_ident, &spec_xdr, &[]);
11784

11885
// Output.
11986
let mut output = quote! {

soroban-sdk-macros/src/derive_error_enum_int.rs

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use itertools::MultiUnzip;
22
use proc_macro2::TokenStream as TokenStream2;
3-
use quote::{format_ident, quote};
3+
use quote::quote;
44
use stellar_xdr::curr as stellar_xdr;
55
use stellar_xdr::{ScSpecEntry, ScSpecUdtErrorEnumCaseV0, ScSpecUdtErrorEnumV0, StringM, WriteXdr};
66
use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path};
@@ -76,42 +76,9 @@ pub fn derive_type_error_enum_int(
7676
None
7777
};
7878

79-
// Generated code spec.
80-
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
81-
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
82-
let spec_xdr_len = spec_xdr.len();
83-
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", enum_ident.to_string().to_uppercase());
84-
Some(quote! {
85-
#[cfg_attr(target_family = "wasm", link_section = "contractspecv0")]
86-
pub static #spec_ident: [u8; #spec_xdr_len] = #enum_ident::spec_xdr();
87-
88-
impl #enum_ident {
89-
pub const fn spec_xdr() -> [u8; #spec_xdr_len] {
90-
*#spec_xdr_lit
91-
}
92-
}
93-
})
94-
} else {
95-
None
96-
};
97-
98-
// SpecShakingMarker impl - only generated when spec is true and the
99-
// experimental_spec_shaking_v2 feature is enabled.
100-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
101-
spec_xdr.as_ref().map(|spec_xdr| {
102-
shaking::generate_marker_impl(
103-
path,
104-
quote!(#enum_ident),
105-
spec_xdr,
106-
std::iter::empty(),
107-
None,
108-
None,
109-
None,
110-
)
111-
})
112-
} else {
113-
None
114-
};
79+
// Generated code spec and SpecShakingMarker impl.
80+
let (spec_gen, spec_shaking_impl) =
81+
shaking::generate_type_spec_and_marker(path, &enum_ident, &spec_xdr, &[]);
11582

11683
// Output.
11784
quote! {

soroban-sdk-macros/src/derive_struct.rs

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use itertools::Itertools;
22
use proc_macro2::{Literal, TokenStream as TokenStream2};
3-
use quote::{format_ident, quote};
3+
use quote::quote;
44
use syn::{Attribute, DataStruct, Error, Ident, Path, Visibility};
55

66
use stellar_xdr::curr as stellar_xdr;
@@ -88,42 +88,9 @@ pub fn derive_type_struct(
8888
None
8989
};
9090

91-
// Generated code spec.
92-
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
93-
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
94-
let spec_xdr_len = spec_xdr.len();
95-
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", ident.to_string().to_uppercase());
96-
Some(quote! {
97-
#[cfg_attr(target_family = "wasm", link_section = "contractspecv0")]
98-
pub static #spec_ident: [u8; #spec_xdr_len] = #ident::spec_xdr();
99-
100-
impl #ident {
101-
pub const fn spec_xdr() -> [u8; #spec_xdr_len] {
102-
*#spec_xdr_lit
103-
}
104-
}
105-
})
106-
} else {
107-
None
108-
};
109-
110-
// SpecShakingMarker impl - only generated when spec is true and the
111-
// experimental_spec_shaking_v2 feature is enabled.
112-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
113-
spec_xdr.as_ref().map(|spec_xdr| {
114-
shaking::generate_marker_impl(
115-
path,
116-
quote!(#ident),
117-
spec_xdr,
118-
field_types.iter().cloned(),
119-
None,
120-
None,
121-
None,
122-
)
123-
})
124-
} else {
125-
None
126-
};
91+
// Generated code spec and SpecShakingMarker impl.
92+
let (spec_gen, spec_shaking_impl) =
93+
shaking::generate_type_spec_and_marker(path, &ident, &spec_xdr, &field_types);
12794

12895
// Output.
12996
let mut output = quote! {

soroban-sdk-macros/src/derive_struct_tuple.rs

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use itertools::MultiUnzip;
22
use proc_macro2::{Literal, TokenStream as TokenStream2};
3-
use quote::{format_ident, quote};
3+
use quote::quote;
44
use syn::{Attribute, DataStruct, Error, Ident, Path, Visibility};
55

66
use stellar_xdr::curr as stellar_xdr;
@@ -77,42 +77,9 @@ pub fn derive_type_struct_tuple(
7777
None
7878
};
7979

80-
// Generated code spec.
81-
let spec_gen = if let Some(ref spec_xdr) = spec_xdr {
82-
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
83-
let spec_xdr_len = spec_xdr.len();
84-
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", ident.to_string().to_uppercase());
85-
Some(quote! {
86-
#[cfg_attr(target_family = "wasm", link_section = "contractspecv0")]
87-
pub static #spec_ident: [u8; #spec_xdr_len] = #ident::spec_xdr();
88-
89-
impl #ident {
90-
pub const fn spec_xdr() -> [u8; #spec_xdr_len] {
91-
*#spec_xdr_lit
92-
}
93-
}
94-
})
95-
} else {
96-
None
97-
};
98-
99-
// SpecShakingMarker impl - only generated when spec is true and the
100-
// experimental_spec_shaking_v2 feature is enabled.
101-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
102-
spec_xdr.as_ref().map(|spec_xdr| {
103-
shaking::generate_marker_impl(
104-
path,
105-
quote!(#ident),
106-
spec_xdr,
107-
field_types.iter().cloned(),
108-
None,
109-
None,
110-
None,
111-
)
112-
})
113-
} else {
114-
None
115-
};
80+
// Generated code spec and SpecShakingMarker impl.
81+
let (spec_gen, spec_shaking_impl) =
82+
shaking::generate_type_spec_and_marker(path, &ident, &spec_xdr, &field_types);
11683

11784
// Output.
11885
let mut output = quote! {

soroban-sdk-macros/src/shaking.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,58 @@
1515
//! 4. Strip unused specs from contractspecv0
1616
1717
use proc_macro2::TokenStream as TokenStream2;
18-
use quote::quote;
19-
use syn::{Path, Type};
18+
use quote::{format_ident, quote};
19+
use syn::{Ident, Path, Type};
20+
21+
/// Generates the spec XDR static, `spec_xdr()` const fn, and optional
22+
/// `SpecShakingMarker` impl for a contract type.
23+
///
24+
/// Returns `(spec_gen, spec_shaking_impl)` where:
25+
/// - `spec_gen` contains the `__SPEC_XDR_TYPE_*` static and `spec_xdr()` method
26+
/// - `spec_shaking_impl` contains the `SpecShakingMarker` impl (only when the
27+
/// `experimental_spec_shaking_v2` feature is enabled)
28+
///
29+
/// Both are `None` when `spec_xdr` is `None` (i.e., spec generation is disabled).
30+
pub fn generate_type_spec_and_marker(
31+
path: &Path,
32+
ident: &Ident,
33+
spec_xdr: &Option<Vec<u8>>,
34+
field_types: &[&Type],
35+
) -> (Option<TokenStream2>, Option<TokenStream2>) {
36+
let spec_gen = spec_xdr.as_ref().map(|spec_xdr| {
37+
let spec_xdr_lit = proc_macro2::Literal::byte_string(spec_xdr.as_slice());
38+
let spec_xdr_len = spec_xdr.len();
39+
let spec_ident = format_ident!("__SPEC_XDR_TYPE_{}", ident.to_string().to_uppercase());
40+
quote! {
41+
#[cfg_attr(target_family = "wasm", link_section = "contractspecv0")]
42+
pub static #spec_ident: [u8; #spec_xdr_len] = #ident::spec_xdr();
43+
44+
impl #ident {
45+
pub const fn spec_xdr() -> [u8; #spec_xdr_len] {
46+
*#spec_xdr_lit
47+
}
48+
}
49+
}
50+
});
51+
52+
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
53+
spec_xdr.as_ref().map(|spec_xdr| {
54+
generate_marker_impl(
55+
path,
56+
quote!(#ident),
57+
spec_xdr,
58+
field_types.iter().copied(),
59+
None,
60+
None,
61+
None,
62+
)
63+
})
64+
} else {
65+
None
66+
};
67+
68+
(spec_gen, spec_shaking_impl)
69+
}
2070

2171
/// Generates the `SpecShakingMarker` impl for a type.
2272
///

0 commit comments

Comments
 (0)