-
Notifications
You must be signed in to change notification settings - Fork 57
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
feat: Introduce high-level types for templates #1124
base: next
Are you sure you want to change the base?
Conversation
c3d7263
to
2d5a9b1
Compare
8449edd
to
6a6505a
Compare
6a6505a
to
386f794
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Not a full review yet.
I wonder if FeltType
and WordType
are the right approach, since users won't be able to extend them with their own types. It doesn't feel right to have FeltType::TokenSymbol
or WordType::RpoFalcon512PublicKey
, as these should not be hard-coded. If that's just the approach for now, then I suppose that's fine, but to support user-extensibility, we'd need a different approach, right?
I think something like the TemplateType
trait is a good direction for extensibility. Ideally we could implement this for Felt
, Word
within miden-base, and users can implement it for UserType
and then register all of them in some Map<TypeName, Box<dyn TemplateType>>
(or something like that). If some entry is specified to be of type TokenSymbol
, then we look this up in the map and find an opaque parse function which only needs to convert a &str
to a Felt
, afaict, and in the process verify that it is a valid instance of TokenSymbol
. In that way, we should be able to be opaque over the concrete types. I'm not completely sure yet whether every type should be convertible to Felt
and Word
. TokenSymbol
only converts to Felt
and RpoFalcon512PublicKey
only converts to Word
, so maybe this needs to be reflected in the approach, perhaps by having to different FeltTemplateType
and WordTemplateType
traits?
Does this make any sense and do you think this would be possible and support user extensibility? I'm not saying that we have to do this in this PR or at all, just trying to get at the limitations of the current approach.
name: "test".into(), | ||
description: "desc".into(), | ||
version: Version::parse("0.1.0").unwrap(), | ||
targets: BTreeSet::new(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Not necessarily in this PR, but should we align the naming of AccountComponentMetadata::targets
with AccountComponent::supported_types
? Seems like it would be good to make the naming consistent (in fields, methods and docs). I don't have a strong preference just wanted to bring it up.
/// ); | ||
/// let storage_entry = StorageEntry::new_value(0, word_representation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This binding is not actually passed to AccountComponentTemplate::new
, which seems to have been the intention?
The test passes anyway, because the data in InitStorageData
is just not used. I wonder if it should error if not all values from the InitStorageData
are used? But this may be somewhat complicated to track. As long as we don't have default values, then that should be fine, since a mistyped key would still produce an error and it would not simply fallback to the default.
|
||
/// Returns the name associated with the word representation. | ||
/// - For the `Template` variant, it always returns a reference to the name. | ||
/// - For the `Value` variant, it returns `Some(&str)` if a name is present, or `None` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// - For the `Value` variant, it returns `Some(&str)` if a name is present, or `None` | |
/// - For the `Value` variant, it returns `Some(&StorageValueName)` if a name is present, or `None` |
The type seems incorrect.
Nit: I think we could also omit writing the type in the docs and only mention when it returns Some
or None
.
} | ||
} | ||
|
||
/// Returns the default value (an array of 4 `FeltRepresentation`s) if this is a `Value` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Is it correct to say "default value" when we don't always have a value (i.e. when it's a Template
variant)? "Default" suggests it is always available.
let placeholder_prefix = placeholder_prefix.with_suffix(placeholder_key); | ||
let value = init_storage_data.get(&placeholder_prefix); | ||
if let Some(v) = value { | ||
let parsed_value = r#type.try_parse_word(v).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let parsed_value = r#type.try_parse_word(v).unwrap(); | |
let parsed_value = r#type.try_parse_word(v)?; |
This should propagate, right?
match key.as_str() { | ||
"token_metadata.max_supply" | ||
| "token_metadata.decimals" | ||
| "default_recallable_height" => continue, | ||
"map_entry.map_key_template" => assert!(requirement.r#type.to_string() == "word"), | ||
_ => panic!("all cases should have been covered"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think this would be more readable if we collect
the iterator into a BTreeSet
and then write individual asserts/assert_eqs to check that each key exists/has the expected value.
if !key.inner().is_empty() { | ||
key.fully_qualified_name.push('.'); | ||
} | ||
key.fully_qualified_name.push_str(&suffix.to_string()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
key.fully_qualified_name.push_str(&suffix.to_string()); | |
key.fully_qualified_name.push_str(suffix.inner()); |
Nit: Avoid allocation.
pub fn inner(&self) -> &str { | ||
&self.key | ||
&self.fully_qualified_name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I would prefer naming these kinds of functions as_str
to follow the rust convention and to be more readable. When I read inner
somewhere else in the code I don't really know what this returns, whereas as_str
gives me that information.
if !segment.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-') { | ||
return Err(StorageValueNameError::InvalidCharacter { | ||
part: segment.to_string(), | ||
character: segment | ||
.chars() | ||
.find(|c| !(c.is_ascii_alphanumeric() || *c == '_' || *c == '-')) | ||
.unwrap(), | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we not also use find(...)
directly? If it returns None
the string is valid and if it returns Some(c)
we already have the offending character that we can put in the error message.
} | ||
} | ||
} | ||
impl Serializable for StorageValueName { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need to implement Serializable
for all the template-related types? Where would we use the binary representations? Looks like they are currently unused.
Closes #1109
Provides a way of expressing types in placeholder values within a component template (currently hardcoded to a list of variants).
Comes with some refactors, hopefully improving code a bit. There are some other refactors that I would like to make and will make explicit in this PR later. Multi-slot value templating has not been implemented yet due to lack of native types that would use them.
Currently allows for toml definitions such as this: