Skip to content

Commit

Permalink
tweak: Use ; to separate SBOR types to avoid clash with commas in c…
Browse files Browse the repository at this point in the history
…omplex types
  • Loading branch information
dhedey committed Dec 3, 2024
1 parent 9c98fde commit af7aeaf
Show file tree
Hide file tree
Showing 6 changed files with 19 additions and 17 deletions.
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

0 comments on commit af7aeaf

Please sign in to comment.