Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweak/sbor types separator #2035

Merged
merged 1 commit into from
Jan 13, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use sbor::SborEnum;

#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
#[sbor(categorize_types = "U, O")]
#[sbor(categorize_types = "U; O")]
pub enum GenericMetadataValue<U, O> {
#[sbor(discriminator(METADATA_VALUE_STRING_DISCRIMINATOR))]
String(String),
Expand Down
2 changes: 1 addition & 1 deletion sbor-derive-common/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ mod tests {

#[test]
fn test_decode_struct_with_generic_params() {
let input = TokenStream::from_str("#[sbor(categorize_types = \"T1, T2\")] struct Test<'a, S, T1, T2> {a: &'a u32, b: S, c: Vec<T1>, d: Vec<T2>}").unwrap();
let input = TokenStream::from_str("#[sbor(categorize_types = \"T1; T2\")] struct Test<'a, S, T1, T2> {a: &'a u32, b: S, c: Vec<T1>, d: Vec<T2>}").unwrap();
let output = handle_decode(input, None).unwrap();

assert_code_eq(
Expand Down
20 changes: 11 additions & 9 deletions sbor-derive-common/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,11 +654,11 @@ pub fn parse_single_type(source_string: &LitStr) -> syn::Result<Type> {
source_string.parse()
}

pub fn parse_comma_separated_types(source_string: &LitStr) -> syn::Result<Vec<Type>> {
pub fn parse_semicolon_separated_types(source_string: &LitStr) -> syn::Result<Vec<Type>> {
let span = source_string.span();
source_string
.value()
.split(',')
.split(';')
.map(|s| s.trim().to_owned())
.filter(|f| f.len() > 0)
.map(|s| LitStr::new(&s, span).parse())
Expand All @@ -670,27 +670,29 @@ pub fn parse_comma_separated_types(source_string: &LitStr) -> syn::Result<Vec<Ty
/// `Encode` / `Decode` / `Describe`.
///
/// By default, like e.g. the default `Clone` impl, we assume that all generic types are
/// child types. But a user can override this with the `#[sbor(child_types = "A,B")]` attribute.
/// child types. But a user can override this with the `#[sbor(child_types = "A; B")]` attribute.
///
/// One of the prime use cases for this is where associated types are used, for example
/// a type `<T> MyStruct(T::MyAssociatedType)` should use `#[sbor(child_types = "T::MyAssociatedType")]`.
fn get_child_types(attributes: &[Attribute], existing_generics: &Generics) -> Result<Vec<Type>> {
let Some(comma_separated_types) = get_sbor_attribute_string_value(attributes, "child_types")?
let Some(child_types_attribute_value) =
get_sbor_attribute_string_value(attributes, "child_types")?
else {
// If no explicit child_types list is set, we use all pre-existing generic type parameters.
// This means (eg) that they all have to implement the relevant trait (Encode/Decode/Describe)
// This means (e.g.) that they all have to implement the relevant trait (Encode/Decode/Describe)
// This is essentially what derived traits such as Clone do: https://github.com/rust-lang/rust/issues/26925
// It's not perfect - but it's typically good enough!
return Ok(get_generic_types(existing_generics));
};

parse_comma_separated_types(&comma_separated_types)
parse_semicolon_separated_types(&child_types_attribute_value)
}

fn get_types_requiring_categorize_bound_for_encode_and_decode(
attributes: &[Attribute],
) -> Result<Vec<Type>> {
let comma_separated_types = get_sbor_attribute_string_value(attributes, "categorize_types")?;
let categorize_types_attribute_value =
get_sbor_attribute_string_value(attributes, "categorize_types")?;
// We need to work out what the default behaviour is if no `categorize_types` are provided.
//
// Now, for a given generic parameter T, we have a few cases how it appears in the type:
Expand Down Expand Up @@ -718,8 +720,8 @@ fn get_types_requiring_categorize_bound_for_encode_and_decode(
// We used to use (C), but we have now switched to (A) because we believe it to be clearer, on balance.
// Also, providing #[sbor(categorize_types = "T")] feels more explicit sometimes than confusingly having
// to add #[sbor(categorize_types = "")] sometimes.
if let Some(comma_separated_types) = comma_separated_types {
parse_comma_separated_types(&comma_separated_types)
if let Some(categorize_types_attribute_value) = categorize_types_attribute_value {
parse_semicolon_separated_types(&categorize_types_attribute_value)
} else {
Ok(vec![])
}
Expand Down
2 changes: 1 addition & 1 deletion sbor-tests/tests/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct BasicSample {
}

#[derive(Sbor)]
#[sbor(categorize_types = "S, T")]
#[sbor(categorize_types = "S; T")]
pub struct AdvancedSample<T, S> {
pub a: (),
pub b: u32,
Expand Down
8 changes: 4 additions & 4 deletions sbor/src/schema/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::*;

define_single_versioned!(
#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
#[sbor(child_types = "S::CustomLocalTypeKind, S::CustomTypeValidation")]
#[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
pub VersionedSchema(SchemaVersions)<S: CustomSchema> => Schema<S> = SchemaV1::<S>
);

Expand Down Expand Up @@ -32,7 +32,7 @@ impl<S: CustomSchema> Default for VersionedSchema<S> {
/// A serializable record of the schema of a single type.
/// Intended for historical backwards compatibility checking of a single type.
#[derive(Debug, Clone, Sbor)]
#[sbor(child_types = "S::CustomLocalTypeKind, S::CustomTypeValidation")]
#[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
pub struct SingleTypeSchema<S: CustomSchema> {
pub schema: VersionedSchema<S>,
pub type_id: LocalTypeId,
Expand All @@ -58,7 +58,7 @@ impl<S: CustomSchema> SingleTypeSchema<S> {
///
/// For example, traits, or blueprint interfaces.
#[derive(Debug, Clone, Sbor)]
#[sbor(child_types = "S::CustomLocalTypeKind, S::CustomTypeValidation")]
#[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
pub struct TypeCollectionSchema<S: CustomSchema> {
pub schema: VersionedSchema<S>,
pub type_ids: IndexMap<String, LocalTypeId>,
Expand All @@ -84,7 +84,7 @@ impl<S: CustomSchema> TypeCollectionSchema<S> {
// * Via TypeKind, S::CustomLocalTypeKind gets embedded
// * Via TypeValidation, S::CustomTypeValidation gets embedded
// So theses are the child types which need to be registered with the sbor macro for it to compile
#[sbor(child_types = "S::CustomLocalTypeKind, S::CustomTypeValidation")]
#[sbor(child_types = "S::CustomLocalTypeKind; S::CustomTypeValidation")]
pub struct SchemaV1<S: CustomSchema> {
pub type_kinds: Vec<LocalTypeKind<S>>,
pub type_metadata: Vec<TypeMetadata>, // TODO: reconsider adding type hash when it's ready!
Expand Down
2 changes: 1 addition & 1 deletion sbor/src/schema/type_data/type_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub type AggregatorTypeKind<S> =

/// A schema for the values that a codec can decode / views as valid
#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
#[sbor(child_types = "T, L", categorize_types = "L")]
#[sbor(child_types = "T; L", categorize_types = "L")]
pub enum TypeKind<T: CustomTypeKind<L>, L: SchemaTypeLink> {
Any,

Expand Down
Loading