Skip to content
Draft
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
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ version = "=28.0.0"
default-features = false

[patch.crates-io]
# Pending https://github.com/stellar/rs-stellar-xdr/pull/562, which adds the
# borrowing `*View` types and their const XDR serializers that the contract spec
# Pending https://github.com/stellar/rs-stellar-xdr/pull/566, which widens the
# user-defined type name limit so a fully qualified name fits, and which carries
# https://github.com/stellar/rs-stellar-xdr/pull/562 adding the borrowing
# `*View` types and their const XDR serializers that the contract spec
# generation in soroban-sdk-macros builds on.
stellar-xdr = { git = "https://github.com/stellar/rs-stellar-xdr", rev = "595950c5dfd2dab0333922ebb26fab27dac3188a" }
stellar-xdr = { git = "https://github.com/stellar/rs-stellar-xdr", rev = "4144fbd8d1c6281f122f382989a5a788431e5e19" }
#soroban-env-common = { path = "../rs-soroban-env/soroban-env-common" }
#soroban-env-guest = { path = "../rs-soroban-env/soroban-env-guest" }
#soroban-env-host = { path = "../rs-soroban-env/soroban-env-host" }
Expand Down
62 changes: 39 additions & 23 deletions soroban-sdk-macros/src/derive_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use stellar_xdr::{

use crate::{
doc::docs_from_attrs,
map_type::{const_view_string, const_view_type_def, map_type},
map_type::{const_view_string, const_view_type_def, map_type, spec_type_name_gen},
shaking,
};

Expand Down Expand Up @@ -156,33 +156,47 @@ pub fn derive_type_enum(
cases: spec_cases.try_into().unwrap(),
};

// The fully qualified name the spec knows this type by, emitted for every
// type so that a reference to it from anywhere can reach it.
let spec_type_name = spec_type_name_gen(enum_ident);

// Generated code spec. The spec entry is rendered as the equivalent const
// ScSpecEntryView, which the contract crate encodes to XDR at compile time.
let spec_gen = {
let doc = const_view_string(path, &spec.doc);
let lib = const_view_string(path, &spec.lib);
let name = const_view_string(path, &spec.name);
let cases = spec.cases.iter().map(|c| match c {
ScSpecUdtUnionCaseV0::VoidV0(c) => {
let doc = const_view_string(path, &c.doc);
let name = const_view_string(path, &c.name);
quote!(#path::xdr::ScSpecUdtUnionCaseV0View::VoidV0(
#path::xdr::ScSpecUdtUnionCaseVoidV0View { doc: #doc, name: #name }
))
}
ScSpecUdtUnionCaseV0::TupleV0(c) => {
let doc = const_view_string(path, &c.doc);
let name = const_view_string(path, &c.name);
let type_ = c.type_.iter().map(|t| const_view_type_def(path, t));
quote!(#path::xdr::ScSpecUdtUnionCaseV0View::TupleV0(
#path::xdr::ScSpecUdtUnionCaseTupleV0View {
doc: #doc,
name: #name,
type_: #path::xdr::VecMView::new(&[#(#type_),*]),
}
))
}
});
let name = quote!(#path::xdr::StringMView::new_str(#enum_ident::spec_type_name()));
// Each case's Rust field types, so a reference to a user-defined type in
// a case resolves to the name that type reports for itself.
let cases = spec
.cases
.iter()
.zip(&variant_field_types)
.map(|(c, field_types)| match c {
ScSpecUdtUnionCaseV0::VoidV0(c) => {
let doc = const_view_string(path, &c.doc);
let name = const_view_string(path, &c.name);
quote!(#path::xdr::ScSpecUdtUnionCaseV0View::VoidV0(
#path::xdr::ScSpecUdtUnionCaseVoidV0View { doc: #doc, name: #name }
))
}
ScSpecUdtUnionCaseV0::TupleV0(c) => {
let doc = const_view_string(path, &c.doc);
let name = const_view_string(path, &c.name);
let type_ = c
.type_
.iter()
.zip(field_types.iter().copied())
.map(|(t, rust)| const_view_type_def(path, t, Some(rust)));
quote!(#path::xdr::ScSpecUdtUnionCaseV0View::TupleV0(
#path::xdr::ScSpecUdtUnionCaseTupleV0View {
doc: #doc,
name: #name,
type_: #path::xdr::VecMView::new(&[#(#type_),*]),
}
))
}
});
let spec_view = quote! {
#path::xdr::ScSpecEntryView::UdtUnionV0(#path::xdr::ScSpecUdtUnionV0View {
doc: #doc,
Expand Down Expand Up @@ -230,6 +244,8 @@ pub fn derive_type_enum(

// Output.
let mut output = quote! {
#spec_type_name

#spec_gen

#spec_shaking_impl
Expand Down
14 changes: 12 additions & 2 deletions soroban-sdk-macros/src/derive_enum_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ use syn::{

use stellar_xdr::ScSpecUdtEnumCaseV0;

use crate::{doc::docs_from_attrs, map_type::const_view_string, shaking};
use crate::{
doc::docs_from_attrs,
map_type::{const_view_string, spec_type_name_gen},
shaking,
};

// TODO: Add conversions to/from ScVal types.

Expand Down Expand Up @@ -76,10 +80,14 @@ pub fn derive_type_enum_int(

// Generated code spec. The spec entry is rendered as the equivalent const
// ScSpecEntryView, which the contract crate encodes to XDR at compile time.
// The fully qualified name the spec knows this type by, emitted for every
// type so that a reference to it from anywhere can reach it.
let spec_type_name = spec_type_name_gen(enum_ident);

let spec_gen = {
let doc = const_view_string(path, &spec.doc);
let lib = const_view_string(path, &spec.lib);
let name = const_view_string(path, &spec.name);
let name = quote!(#path::xdr::StringMView::new_str(#enum_ident::spec_type_name()));
let cases = spec.cases.iter().map(|c| {
let doc = const_view_string(path, &c.doc);
let name = const_view_string(path, &c.name);
Expand Down Expand Up @@ -125,6 +133,8 @@ pub fn derive_type_enum_int(

// Output.
let mut output = quote! {
#spec_type_name

#spec_gen

#spec_shaking_impl
Expand Down
14 changes: 12 additions & 2 deletions soroban-sdk-macros/src/derive_error_enum_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ use syn::{
ext::IdentExt as _, spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path,
};

use crate::{doc::docs_from_attrs, map_type::const_view_string, shaking};
use crate::{
doc::docs_from_attrs,
map_type::{const_view_string, spec_type_name_gen},
shaking,
};

pub fn derive_type_error_enum_int(
path: &Path,
Expand Down Expand Up @@ -73,10 +77,14 @@ pub fn derive_type_error_enum_int(

// Generated code spec. The spec entry is rendered as the equivalent const
// ScSpecEntryView, which the contract crate encodes to XDR at compile time.
// The fully qualified name the spec knows this type by, emitted for every
// type so that a reference to it from anywhere can reach it.
let spec_type_name = spec_type_name_gen(enum_ident);

let spec_gen = {
let doc = const_view_string(path, &spec.doc);
let lib = const_view_string(path, &spec.lib);
let name = const_view_string(path, &spec.name);
let name = quote!(#path::xdr::StringMView::new_str(#enum_ident::spec_type_name()));
let cases = spec.cases.iter().map(|c| {
let doc = const_view_string(path, &c.doc);
let name = const_view_string(path, &c.name);
Expand Down Expand Up @@ -122,6 +130,8 @@ pub fn derive_type_error_enum_int(

// Output.
quote! {
#spec_type_name

#spec_gen

#spec_shaking_impl
Expand Down
52 changes: 35 additions & 17 deletions soroban-sdk-macros/src/derive_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ use proc_macro2::Span;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use stellar_xdr::{
ScSpecEventDataFormat, ScSpecEventParamLocationV0, ScSpecEventParamV0, ScSpecEventV0, ScSymbol,
StringM,
ScSpecEventDataFormat, ScSpecEventParamLocationV0, ScSpecEventParamV0, ScSpecEventV0, StringM,
};
use syn::{ext::IdentExt as _, parse2, spanned::Spanned, Data, DeriveInput, Fields, LitStr, Path};

Expand Down Expand Up @@ -94,7 +93,7 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
let path = &args.crate_path;

// Check event name length
const EVENT_NAME_LENGTH: u32 = 32;
const EVENT_NAME_LENGTH: u32 = 256;
let event_name = input.ident.unraw().to_string();
let event_name_len = event_name.len();
let event_name: StringM<EVENT_NAME_LENGTH> = errors
Expand Down Expand Up @@ -183,7 +182,7 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
data_format: args.data_format.into(),
doc: docs_from_attrs(&input.attrs),
lib: args.lib.as_deref().unwrap_or_default().try_into().unwrap(),
name: ScSymbol(event_name),
name: event_name,
prefix_topics: prefix_topics
.iter()
.map(|t| t.try_into().unwrap())
Expand All @@ -208,23 +207,29 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
let spec_view = {
let doc = const_view_string(path, &spec.doc);
let lib = const_view_string(path, &spec.lib);
let name = const_view_symbol(path, &spec.name);
let name = quote!(#path::xdr::StringMView::new_str(#ident::spec_type_name()));
let prefix_topics = spec
.prefix_topics
.iter()
.map(|t| const_view_symbol(path, t));
let params = spec.params.iter().map(|p| {
let doc = const_view_string(path, &p.doc);
let name = const_view_string(path, &p.name);
let type_ = const_view_type_def(path, &p.type_);
let location = format_ident!("{}", p.location.name());
quote!(#path::xdr::ScSpecEventParamV0View {
doc: #doc,
name: #name,
type_: #type_,
location: #path::xdr::ScSpecEventParamLocationV0::#location,
})
});
// Each param's Rust type, so a reference to a user-defined type in a
// param resolves to the name that type reports for itself.
let params = spec
.params
.iter()
.zip(field_types.iter().copied())
.map(|(p, rust)| {
let doc = const_view_string(path, &p.doc);
let name = const_view_string(path, &p.name);
let type_ = const_view_type_def(path, &p.type_, Some(rust));
let location = format_ident!("{}", p.location.name());
quote!(#path::xdr::ScSpecEventParamV0View {
doc: #doc,
name: #name,
type_: #type_,
location: #path::xdr::ScSpecEventParamLocationV0::#location,
})
});
let data_format = format_ident!("{}", spec.data_format.name());
quote! {
#path::xdr::ScSpecEntryView::EventV0(#path::xdr::ScSpecEventV0View {
Expand Down Expand Up @@ -345,7 +350,20 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
};

// Output.
// Unlike other user-defined types, an event struct can carry generics
// (e.g. a lifetime on borrowed fields), so the impl repeats them.
let spec_type_name_lit = input.ident.unraw().to_string();
let spec_type_name = quote! {
impl #gen_impl #ident #gen_types #gen_where {
#[doc(hidden)]
pub const fn spec_type_name() -> &'static str {
::core::concat!(::core::module_path!(), "::", #spec_type_name_lit)
}
}
};
let output = quote! {
#spec_type_name

#spec_gen

#spec_shaking_impl
Expand Down
35 changes: 28 additions & 7 deletions soroban-sdk-macros/src/derive_spec_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,23 @@ pub fn derive_fn_spec(
ReturnType::Default => vec![],
};

// The Rust types the argument and return spec types were mapped from, so a
// reference to a user-defined type resolves to the name that type reports
// for itself. Kept element-for-element with `spec_args` above, including the
// unsupported receiver, so the two line up.
let arg_types: Vec<Option<&Type>> = inputs
.iter()
.skip(if env_input.is_some() { 1 } else { 0 })
.map(|a| match a {
FnArg::Typed(pat_type) => Some(&*pat_type.ty),
FnArg::Receiver(_) => None,
})
.collect();
let output_type: Option<&Type> = match output {
ReturnType::Type(_, ty) => Some(ty),
ReturnType::Default => None,
};

// Generated code spec.
let name = &ident.unraw().to_string();
let spec_entry = ScSpecFunctionV0 {
Expand All @@ -168,16 +185,20 @@ pub fn derive_fn_spec(
let spec_view = {
let doc = const_view_string(path, &spec_entry.doc);
let name = const_view_symbol(path, &spec_entry.name);
let inputs = spec_entry.inputs.iter().map(|i| {
let doc = const_view_string(path, &i.doc);
let name = const_view_string(path, &i.name);
let type_ = const_view_type_def(path, &i.type_);
quote!(#path::xdr::ScSpecFunctionInputV0View { doc: #doc, name: #name, type_: #type_ })
});
let inputs = spec_entry
.inputs
.iter()
.zip(arg_types.iter().copied())
.map(|(i, rust)| {
let doc = const_view_string(path, &i.doc);
let name = const_view_string(path, &i.name);
let type_ = const_view_type_def(path, &i.type_, rust);
quote!(#path::xdr::ScSpecFunctionInputV0View { doc: #doc, name: #name, type_: #type_ })
});
let outputs = spec_entry
.outputs
.iter()
.map(|o| const_view_type_def(path, o));
.map(|o| const_view_type_def(path, o, output_type));
quote! {
#path::xdr::ScSpecEntryView::FunctionV0(#path::xdr::ScSpecFunctionV0View {
doc: #doc,
Expand Down
24 changes: 18 additions & 6 deletions soroban-sdk-macros/src/derive_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use stellar_xdr::{ScSpecTypeDef, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, Stri

use crate::{
doc::docs_from_attrs,
map_type::{const_view_string, const_view_type_def, map_type},
map_type::{const_view_string, const_view_type_def, map_type, spec_type_name_gen},
shaking,
};

Expand Down Expand Up @@ -83,18 +83,28 @@ pub fn derive_type_struct(
fields: spec_fields.try_into().unwrap(),
};

// The fully qualified name the spec knows this type by, emitted for every
// type so that a reference to it from anywhere can reach it.
let spec_type_name = spec_type_name_gen(ident);

// Generated code spec. The spec entry is rendered as the equivalent const
// ScSpecEntryView, which the contract crate encodes to XDR at compile time.
let spec_gen = {
let doc = const_view_string(path, &spec.doc);
let lib = const_view_string(path, &spec.lib);
let name = const_view_string(path, &spec.name);
let fields = spec.fields.iter().map(|f| {
let name = quote!(#path::xdr::StringMView::new_str(#ident::spec_type_name()));
// Each field's Rust type, so a reference to a user-defined type in a
// field resolves to the name that type reports for itself.
let fields = spec
.fields
.iter()
.zip(field_types.iter().copied())
.map(|(f, rust)| {
let doc = const_view_string(path, &f.doc);
let name = const_view_string(path, &f.name);
let type_ = const_view_type_def(path, &f.type_);
quote!(#path::xdr::ScSpecUdtStructFieldV0View { doc: #doc, name: #name, type_: #type_ })
});
let type_ = const_view_type_def(path, &f.type_, Some(rust));
quote!(#path::xdr::ScSpecUdtStructFieldV0View { doc: #doc, name: #name, type_: #type_ })
});
let spec_view = quote! {
#path::xdr::ScSpecEntryView::UdtStructV0(#path::xdr::ScSpecUdtStructV0View {
doc: #doc,
Expand Down Expand Up @@ -134,6 +144,8 @@ pub fn derive_type_struct(

// Output.
let mut output = quote! {
#spec_type_name

#spec_gen

#spec_shaking_impl
Expand Down
Loading
Loading