Skip to content

Commit

Permalink
[refactor]: Add compile-time assertions if #[view(into = Type)] is mi…
Browse files Browse the repository at this point in the history
…ssed

Signed-off-by: Shanin Roman <[email protected]>
  • Loading branch information
Erigara committed Jul 22, 2022
1 parent 362a6ba commit 9b44805
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 0 deletions.
42 changes: 42 additions & 0 deletions config/base/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,11 +622,15 @@ pub fn view(input: TokenStream) -> TokenStream {
let view = gen_view_struct(ast);
let impl_from = gen_impl_from(&original, &view);
let impl_default = gen_impl_default(&original, &view);
let impl_has_view = gen_impl_has_view(&original);
let assertions = gen_assertions(&view);
let out = quote! {
#original
#impl_has_view
#view
#impl_from
#impl_default
#assertions
};
out.into()
}
Expand Down Expand Up @@ -741,6 +745,39 @@ fn gen_impl_default(original: &ViewInput, view: &ViewInput) -> proc_macro2::Toke
}
}

fn gen_impl_has_view(original: &ViewInput) -> proc_macro2::TokenStream {
let ViewInput {
generics,
ident: view_ident,
..
} = original;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

quote! {
impl #impl_generics iroha_config_base::view::HasView for #view_ident #ty_generics #where_clause {}
}
}

fn gen_assertions(view: &ViewInput) -> proc_macro2::TokenStream {
let ViewInput { fields, .. } = view;
let field_types = extract_field_types(fields);
let messages: Vec<String> = extract_field_idents(fields)
.iter()
.map(|ident| {
format!("Field `{ident}` has it's own view, consider adding attribute #[view(into = ViewType)]")
})
.collect();
quote! {
/// Assert that every field of 'View' doesn't implement `HasView` trait
const _: () = {
use iroha_config_base::view::NoView;
#(
const _: () = assert!(!iroha_config_base::view::IsHasView::<#field_types>::IS_HAS_VIEW, #messages);
)*
};
}
}

/// Change [`Field`] type to `Type` if `#[view(type = Type)]` is present
fn view_field_change_type(field: &mut Field) {
if let Some(ty) = field
Expand Down Expand Up @@ -791,3 +828,8 @@ fn extract_field_idents(fields: &[Field]) -> Vec<&Ident> {
})
.collect::<Vec<_>>()
}

/// Return [`Vec`] of fields types
fn extract_field_types(fields: &[Field]) -> Vec<&Type> {
fields.iter().map(|field| &field.ty).collect::<Vec<_>>()
}
24 changes: 24 additions & 0 deletions config/base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,27 @@ pub trait Configurable: Serialize + DeserializeOwned {
/// Get inner documentation for non-leaf fields
fn get_inner_docs() -> String;
}

pub mod view {
//! Module for view related traits and structs
/// Marker trait to set default value `IS_HAS_VIEW` to `false`
pub trait NoView {
/// [`Self`] doesn't implement [`HasView`]
const IS_HAS_VIEW: bool = false;
}
impl<T> NoView for T {}

/// Marker traits for types for which views are implemented
pub trait HasView {}

/// Wrapper structure used to check if type implements `[HasView]`
/// If `T` doesn't implement [`HasView`] then `NoView::IS_HAS_VIEW` (`false`) will be used
/// Otherwise `IsHasView::IS_HAS_VIEW` (`true`) from `impl` block will shadow `NoView::IS_HAS_VIEW`
pub struct IsHasView<T>(std::marker::PhantomData<T>);

impl<T: HasView> IsHasView<T> {
/// `T` implements trait [`HasView`]
pub const IS_HAS_VIEW: bool = true;
}
}

0 comments on commit 9b44805

Please sign in to comment.