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

Opt out from default derives and attributes #925

Merged
Merged
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);
jsdw marked this conversation as resolved.
Show resolved Hide resolved
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