Skip to content

Commit

Permalink
Opt out from default derives and attributes (#925)
Browse files Browse the repository at this point in the history
* No default derives

* Clippy

* Clippy

* Review

* Review

* Typo
  • Loading branch information
pmikolajczyk41 authored Apr 24, 2023
1 parent 97bd198 commit d3e20e9
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 49 deletions.
61 changes: 57 additions & 4 deletions cli/src/commands/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ pub struct Opts {
/// Additional derives
#[clap(long = "derive")]
derives: Vec<String>,
/// Additional attributes
#[clap(long = "attribute")]
attributes: Vec<String>,
/// Additional derives for a given type.
///
/// Example `--derive-for-type my_module::my_type=serde::Serialize`.
#[clap(long = "derive-for-type", value_parser = derive_for_type_parser)]
derives_for_type: Vec<(String, String)>,
/// Additional attributes for a given type.
///
/// Example `--attributes-for-type my_module::my_type=#[allow(clippy::all)]`.
#[clap(long = "attributes-for-type", value_parser = attributes_for_type_parser)]
attributes_for_type: Vec<(String, String)>,
/// Substitute a type for another.
///
/// Example `--substitute-type sp_runtime::MultiAddress<A,B>=subxt::utils::Static<::sp_runtime::MultiAddress<A,B>>`
Expand All @@ -39,8 +47,15 @@ pub struct Opts {
#[clap(long, action)]
no_docs: bool,
/// Whether to limit code generation to only runtime types.
///
/// Defaults to `false` (all types are generated).
#[clap(long)]
runtime_types_only: bool,
/// Do not provide default trait derivations for the generated types.
///
/// Defaults to `false` (default trait derivations are provided).
#[clap(long)]
no_default_derives: bool,
}

fn derive_for_type_parser(src: &str) -> Result<(String, String), String> {
Expand All @@ -51,6 +66,14 @@ fn derive_for_type_parser(src: &str) -> Result<(String, String), String> {
Ok((ty.to_string(), derive.to_string()))
}

fn attributes_for_type_parser(src: &str) -> Result<(String, String), String> {
let (ty, attribute) = src
.split_once('=')
.ok_or_else(|| String::from("Invalid pattern for `attribute-type`. It should be `type=attribute`, like `my_type=serde::#[allow(clippy::all)]`"))?;

Ok((ty.to_string(), attribute.to_string()))
}

fn substitute_type_parser(src: &str) -> Result<(String, String), String> {
let (from, to) = src
.split_once('=')
Expand All @@ -65,41 +88,71 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
codegen(
&bytes,
opts.derives,
opts.attributes,
opts.derives_for_type,
opts.attributes_for_type,
opts.substitute_types,
opts.crate_path,
opts.no_docs,
opts.runtime_types_only,
opts.no_default_derives,
)?;
Ok(())
}

#[derive(Clone, Debug)]
struct OuterAttribute(syn::Attribute);

impl syn::parse::Parse for OuterAttribute {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
Ok(Self(input.call(syn::Attribute::parse_outer)?[0].clone()))
}
}

#[allow(clippy::too_many_arguments)]
fn codegen(
metadata_bytes: &[u8],
raw_derives: Vec<String>,
raw_attributes: Vec<String>,
derives_for_type: Vec<(String, String)>,
attributes_for_type: Vec<(String, String)>,
substitute_types: Vec<(String, String)>,
crate_path: Option<String>,
no_docs: bool,
runtime_types_only: bool,
no_default_derives: bool,
) -> color_eyre::Result<()> {
let item_mod = syn::parse_quote!(
pub mod api {}
);

let p = raw_derives
let universal_derives = raw_derives
.iter()
.map(|raw| syn::parse_str(raw))
.collect::<Result<Vec<_>, _>>()?;
let universal_attributes = raw_attributes
.iter()
.map(|raw| syn::parse_str(raw))
.map(|attr: syn::Result<OuterAttribute>| attr.map(|attr| attr.0))
.collect::<Result<Vec<_>, _>>()?;

let crate_path = crate_path.map(Into::into).unwrap_or_default();
let mut derives = DerivesRegistry::new(&crate_path);
derives.extend_for_all(p.into_iter());
let mut derives = if no_default_derives {
DerivesRegistry::new()
} else {
DerivesRegistry::with_default_derives(&crate_path)
};
derives.extend_for_all(universal_derives, universal_attributes);

for (ty, derive) in derives_for_type {
let ty = syn::parse_str(&ty)?;
let derive = syn::parse_str(&derive)?;
derives.extend_for_type(ty, std::iter::once(derive), &crate_path);
derives.extend_for_type(ty, std::iter::once(derive), vec![]);
}
for (ty, attr) in attributes_for_type {
let ty = syn::parse_str(&ty)?;
let attribute: OuterAttribute = syn::parse_str(&attr)?;
derives.extend_for_type(ty, vec![], std::iter::once(attribute.0));
}

let mut type_substitutes = TypeSubstitutes::new(&crate_path);
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
//! pub mod api {}
//! );
//! // Default module derivatives.
//! let mut derives = DerivesRegistry::new(&CratePath::default());
//! let mut derives = DerivesRegistry::with_default_derives(&CratePath::default());
//! // Default type substitutes.
//! let substs = TypeSubstitutes::new(&CratePath::default());
//! // Generate the Runtime API.
Expand Down
54 changes: 43 additions & 11 deletions codegen/src/types/derives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,55 @@ pub struct DerivesRegistry {
specific_type_derives: HashMap<syn::TypePath, Derives>,
}

impl Default for DerivesRegistry {
fn default() -> Self {
Self::new()
}
}

impl DerivesRegistry {
/// Creates a new `DerivesRegistry` with the supplied `crate_path`.
/// Creates a new `DerivesRegistry` with no default derives.
pub fn new() -> Self {
Self {
default_derives: Derives::new(),
specific_type_derives: Default::default(),
}
}

/// Creates a new `DerivesRegistry` with default derives.
///
/// The `crate_path` denotes the `subxt` crate access path in the
/// generated code.
pub fn new(crate_path: &CratePath) -> Self {
pub fn with_default_derives(crate_path: &CratePath) -> Self {
Self {
default_derives: Derives::new(crate_path),
default_derives: Derives::with_defaults(crate_path),
specific_type_derives: Default::default(),
}
}

/// Insert derives to be applied to all generated types.
pub fn extend_for_all(&mut self, derives: impl IntoIterator<Item = syn::Path>) {
self.default_derives.derives.extend(derives)
pub fn extend_for_all(
&mut self,
derives: impl IntoIterator<Item = syn::Path>,
attributes: impl IntoIterator<Item = syn::Attribute>,
) {
self.default_derives.derives.extend(derives);
self.default_derives.attributes.extend(attributes);
}

/// Insert derives to be applied to a specific generated type.
pub fn extend_for_type(
&mut self,
ty: syn::TypePath,
derives: impl IntoIterator<Item = syn::Path>,
crate_path: &CratePath,
attributes: impl IntoIterator<Item = syn::Attribute>,
) {
let type_derives = self
.specific_type_derives
.entry(ty)
.or_insert_with(|| Derives::new(crate_path));
type_derives.derives.extend(derives)
.or_insert_with(Derives::new);
type_derives.derives.extend(derives);
type_derives.attributes.extend(attributes);
}

/// Returns the derives to be applied to all generated types.
Expand Down Expand Up @@ -73,6 +93,12 @@ pub struct Derives {
attributes: HashSet<syn::Attribute>,
}

impl Default for Derives {
fn default() -> Self {
Self::new()
}
}

impl FromIterator<syn::Path> for Derives {
fn from_iter<T: IntoIterator<Item = Path>>(iter: T) -> Self {
let derives = iter.into_iter().collect();
Expand All @@ -84,9 +110,17 @@ impl FromIterator<syn::Path> for Derives {
}

impl Derives {
/// Creates an empty instance of `Derives` (with no default derives).
pub fn new() -> Self {
Self {
derives: HashSet::new(),
attributes: HashSet::new(),
}
}

/// Creates a new instance of `Derives` with the `crate_path` prepended
/// to the set of default derives that reside in `subxt`.
pub fn new(crate_path: &CratePath) -> Self {
pub fn with_defaults(crate_path: &CratePath) -> Self {
let mut derives = HashSet::new();
let mut attributes = HashSet::new();

Expand All @@ -99,8 +133,6 @@ impl Derives {
attributes.insert(syn::parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]));

derives.insert(syn::parse_quote!(#crate_path::ext::codec::Encode));
attributes.insert(syn::parse_quote!(#[codec(crate = #crate_path::ext::codec)]));

derives.insert(syn::parse_quote!(#crate_path::ext::codec::Decode));
attributes.insert(syn::parse_quote!(#[codec(crate = #crate_path::ext::codec)]));

Expand Down
Loading

0 comments on commit d3e20e9

Please sign in to comment.