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

Validate Codegen Settings #11

Merged
merged 39 commits into from
Dec 8, 2023
Merged

Conversation

tadeohepperle
Copy link
Contributor

This PR adds functionality to check if the provided type substitute and derive settings are appropriate for a given Type Registry. The validation incrementally builds up an error type that is then returned, containing all the type paths for which special settings were defined, but that do not exist in the metadata.
There is also a function similar_type_paths_in_registry to suggest similar type paths that the user might have meant instead.

@tadeohepperle tadeohepperle changed the base branch from master to recursive-derives December 7, 2023 17:58
Comment on lines 96 to 103
impl std::fmt::Display for SettingsValidationError {
fn fmt(
&self,
f: &mut scale_info::prelude::fmt::Formatter<'_>,
) -> scale_info::prelude::fmt::Result {
f.write_fmt(format_args!("{:?}", self))
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this could be replaced with the thiserror attr on the struct:

#[error = "{:?}"]

But moreover, I think that a proper Display impl should do better than just debug printing :)


use super::{error::SettingsValidationError, settings::substitutes::TryIntoSynPath};

impl<'a> TypeGenerator<'a> {
Copy link
Collaborator

@jsdw jsdw Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels sortof weird to me to have a separate impl block in a different place from the type; I like being able to quite easily see all of the methods that something exposes in a single impl block (of it different constraints, a few impl blocks next to eachother) next to the type definition.

If you want to keep the validation stuff in one place, then perhaps it's best to make this a freestanding function like the ones below?

I also wonder how useful this function actually is, since it will check everything (and in Subxt at least, some things don't want to be checked). I can imagine that for Subxt we'd prefer to use the functions below? Do you have a use case for this overall one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right, since we want to be very specific about what type paths to check in subxt, maybe this general validation function is not the right approach. :)
About the splitting into different files: It's just my personal style influenced by how you often define extension classes in C# or Dart. But It is probably better to merge it all into one impl block for people reading the code.

Comment on lines +354 to +357
pub trait TryIntoSynPath {
/// Turns the type into a [`syn::Path`]. Returns None for empty paths.
fn syn_path(self) -> Option<syn::Path>;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cleaner than the PathSegments stuff I think :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like it so much that we have to define a new trait for this, but due to the orphan rule, we cannot use TryFrom<T> for syn::Path :(

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I thought about that too and realised that you had no other choice! The other half way option would have been to define an enum Path { SynPath(syn::Path), TypeInfoPath(typeinfo::Path) } and then a From on that, but I think your approach is cleaner since you only care about getting one of those back!

@jsdw
Copy link
Collaborator

jsdw commented Dec 7, 2023

Generally LGTM! Just a couple of small things!

Base automatically changed from recursive-derives to master December 7, 2023 21:29
Copy link
Collaborator

@jsdw jsdw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me; the real test will be how it works in Subxt/subxt-explorer :)

pub substitutes_for_unknown_types: Vec<(syn::Path, syn::Path)>,
}

impl std::fmt::Display for SettingsValidationError {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit; It would be interesting to see if we could provide alternative types. Since we cannot find type a::b::Ty, we could inspect the type registry for Ty and return all valid paths.

Which for an xcm MultiLocation it would return both v2 and v3 entries, which might make things easier during development. Tho, this could probably be a follow-up

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling that Subxt won't be able to use the function that produces this error, so it may well be that it's not that useful for us actually, and instead we'll lean on those other validation.rs functions, one of which does provide a list of similar paths

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the similar_type_paths_in_registry function can do that, but I have not put it into the SettingsValidationError, to not bloat it too much. We should definitely do this though from the subxt macro side.

///
/// # Panics
///
/// If no type with the given id found in the type registry.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are returning a result here, would it be dificult to make this method not panic? Same for below?

Copy link
Collaborator

@jsdw jsdw Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point; in most places we just have an error variant to capture "type ID not found" so it's probably good to do that here too for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good catch! The docs are out of date, it actually returns an Err(..) if a wrong id was provided.

}

/// Returns types in the PortableRegistry that share the identifier with the input path.
pub fn similar_type_paths_in_registry(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this is only used for testing atm, would changing the code a bit make this more informative?

  • registry_contains_type_path -> Result<(), Vec<syn::Path>>
  • if the path is in the registry, return Ok(())
  • when the paths not present, return alternatives with this method

Then we could say if let Err(alternatives) = registry_contains_type_path(..) and extend the error object with those

Or we could keep registry_contains_type_path as it is, and call similar_type_paths_in_registry on false (thinking out loud :D)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we could keep registry_contains_type_path as it is, and call similar_type_paths_in_registry on false

I am inclined to do this (as currently done in validate_substitutes_and_derives_against_registry), because it keeps it a bit more decoupled. Ultimately I am not even sure if we will use validate_substitutes_and_derives_against_registry from the subxt side or if we build some separate logic on top of registry_contains_type_path and similar_type_paths_in_registry.

}

/// Returns types in the PortableRegistry that share the identifier with the input path.
pub fn similar_type_paths_in_registry(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good for now; eventually we might want to improve it to find paths that are close (to cater for typos) but that would be a reasonable amount of extra work and dependencies

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, maybe some Levenshtein distance calculations? But should be too much for now.

@lexnv
Copy link

lexnv commented Dec 8, 2023

Looks solid! Nice job here!
Left a couple of thoughts; mostly removing the panics. We could probably incorporate the suggestion type paths in another PR if it turns out to be too complicated

@tadeohepperle tadeohepperle merged commit d742b08 into master Dec 8, 2023
2 checks passed
@tadeohepperle tadeohepperle deleted the tadeohepperle/validate-codegen branch December 8, 2023 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants