Skip to content

Commit

Permalink
do not generate globals twice
Browse files Browse the repository at this point in the history
  • Loading branch information
vhdirk committed Feb 22, 2024
1 parent 6fe09ae commit ff78919
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 144 deletions.
111 changes: 3 additions & 108 deletions src/generators/bitflag.rs
Original file line number Diff line number Diff line change
@@ -1,115 +1,10 @@
use heck::{ToPascalCase, ToShoutySnakeCase};
use quote::quote;
use syn::{spanned::Spanned, Ident};

use super::{Context, KeyGenerator, SchemaFlag, SchemaKey};

pub fn key_generator<'a>(
key: &'a SchemaKey,
flag: &SchemaFlag,
aux_visibility: syn::Visibility,
_aux_visibility: syn::Visibility,
) -> KeyGenerator<'a> {
let flag_name = key.name.to_pascal_case();
KeyGenerator::new(
key,
Context::new_with_aux(
&flag_name,
bitflag_token_stream(&flag_name, flag, aux_visibility),
),
)
}

fn bitflag_token_stream(
name: &str,
flag: &SchemaFlag,
visibility: syn::Visibility,
) -> proc_macro2::TokenStream {
let value_idents = flag
.values
.iter()
.map(|value| Ident::new(&value.nick.to_shouty_snake_case(), value.nick.span()))
.collect::<Vec<_>>();

let flags_arms = value_idents
.iter()
.zip(flag.values.iter())
.map(|(value_ident, value)| {
let value = value.value;
quote! {
const #value_ident = #value;
}
});

let from_variant_arms =
value_idents
.iter()
.zip(flag.values.iter())
.map(|(value_ident, value)| {
let nick = &value.nick;
quote! {
#nick => this.insert(Self::#value_ident)
}
});

let to_variant_arms =
value_idents
.iter()
.zip(flag.values.iter())
.map(|(value_ident, value)| {
let nick = &value.nick;
quote! {
if self.contains(Self::#value_ident) {
string_array.push(#nick)
}
}
});

let name_pascal_case = name.to_pascal_case();
let ident = Ident::new(&name_pascal_case, name_pascal_case.span());

quote! {
gio::glib::bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#visibility struct #ident: u32 {
#(#flags_arms)*
}
}

impl gio::glib::variant::StaticVariantType for #ident {
fn static_variant_type() -> std::borrow::Cow<'static, gio::glib::VariantTy> {
std::borrow::Cow::Borrowed(gio::glib::VariantTy::STRING_ARRAY)
}
}

impl gio::glib::variant::FromVariant for #ident {
fn from_variant(variant: &gio::glib::Variant) -> Option<Self> {
let mut this = Self::empty();

for string in variant.get::<Vec<String>>()? {
match string.as_str() {
#(#from_variant_arms),*,
_ => return None,
}
}

Some(this)
}
}

impl gio::glib::variant::ToVariant for #ident {
fn to_variant(&self) -> gio::glib::Variant {
let mut string_array = Vec::new();

#(#to_variant_arms)*

gio::glib::variant::ToVariant::to_variant(&string_array)
}
}

impl std::convert::From<#ident> for gio::glib::Variant {
fn from(this: #ident) -> gio::glib::Variant {
gio::glib::variant::ToVariant::to_variant(&this)
}
}
}
let flag_type = super::type_name_from_id(&flag.id);
KeyGenerator::new(key, Context::new_dissimilar(&flag_type, &flag_type))
}
17 changes: 3 additions & 14 deletions src/generators/enumeration.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
use heck::ToPascalCase;

use super::{Context, KeyGenerator, SchemaEnum, SchemaKey};

pub fn key_generator<'a>(
key: &'a SchemaKey,
enum_: &SchemaEnum,
aux_visibility: syn::Visibility,
_aux_visibility: syn::Visibility,
) -> KeyGenerator<'a> {
let enum_name = key.name.to_pascal_case();
let enum_token_stream = super::new_variant_enum(
&enum_name,
&enum_
.values
.iter()
.map(|value| (value.nick.as_str(), Some(value.value)))
.collect::<Vec<_>>(),
aux_visibility,
);
KeyGenerator::new(key, Context::new_with_aux(&enum_name, enum_token_stream))
let enum_type = super::type_name_from_id(&enum_.id);
KeyGenerator::new(key, Context::new_dissimilar(&enum_type, &enum_type))
}
109 changes: 106 additions & 3 deletions src/generators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ mod bitflag;
mod enumeration;
mod string;

use heck::ToSnakeCase;
use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase};

use proc_macro2::Span;
use proc_macro_error::abort_call_site;
use quote::{format_ident, quote};
use std::collections::{HashMap, HashSet};
use syn::Ident;
use syn::{spanned::Spanned, Ident};

use std::fmt::Write;

Expand Down Expand Up @@ -336,7 +337,7 @@ impl Context {
/// and [`StaticVariantType`](gio::glib::variant::StaticVariantType).
///
/// The input names are converted to pascal case
fn new_variant_enum(
pub(crate) fn enum_token_stream(
name: &str,
variants: &[(&str, Option<i32>)],
visibility: syn::Visibility,
Expand Down Expand Up @@ -430,3 +431,105 @@ fn new_variant_enum(
}
}
}

pub(crate) fn type_name_from_id(id: &str) -> String {
id.split('.')
.last()
.map(|part| part.to_pascal_case())
.unwrap_or(id.to_string())
}

pub(crate) fn bitflag_token_stream(
name: &str,
flag: &SchemaFlag,
visibility: syn::Visibility,
) -> proc_macro2::TokenStream {
let value_idents = flag
.values
.iter()
.map(|value| Ident::new(&value.nick.to_shouty_snake_case(), value.nick.span()))
.collect::<Vec<_>>();

let flags_arms = value_idents
.iter()
.zip(flag.values.iter())
.map(|(value_ident, value)| {
let value = value.value;
quote! {
const #value_ident = #value;
}
});

let from_variant_arms =
value_idents
.iter()
.zip(flag.values.iter())
.map(|(value_ident, value)| {
let nick = &value.nick;
quote! {
#nick => this.insert(Self::#value_ident)
}
});

let to_variant_arms =
value_idents
.iter()
.zip(flag.values.iter())
.map(|(value_ident, value)| {
let nick = &value.nick;
quote! {
if self.contains(Self::#value_ident) {
string_array.push(#nick)
}
}
});

let name_pascal_case = name.to_pascal_case();
let ident = Ident::new(&name_pascal_case, name_pascal_case.span());

quote! {
gio::glib::bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#visibility struct #ident: u32 {
#(#flags_arms)*
}
}

impl gio::glib::variant::StaticVariantType for #ident {
fn static_variant_type() -> std::borrow::Cow<'static, gio::glib::VariantTy> {
std::borrow::Cow::Borrowed(gio::glib::VariantTy::STRING_ARRAY)
}
}

impl gio::glib::variant::FromVariant for #ident {
fn from_variant(variant: &gio::glib::Variant) -> Option<Self> {
let mut this = Self::empty();

for string in variant.get::<Vec<String>>()? {
match string.as_str() {
#(#from_variant_arms),*,
_ => return None,
}
}

Some(this)
}
}

impl gio::glib::variant::ToVariant for #ident {
fn to_variant(&self) -> gio::glib::Variant {
let mut string_array = Vec::new();

#(#to_variant_arms)*

gio::glib::variant::ToVariant::to_variant(&string_array)
}
}

impl std::convert::From<#ident> for gio::glib::Variant {
fn from(this: #ident) -> gio::glib::Variant {
gio::glib::variant::ToVariant::to_variant(&this)
}
}
}
}
2 changes: 1 addition & 1 deletion src/generators/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::{Context, KeyGenerator, SchemaKey};
pub fn key_generator(key: &SchemaKey, aux_visibility: syn::Visibility) -> KeyGenerator<'_> {
if let Some(ref choices) = key.choices {
let choice_enum_name = key.name.to_pascal_case();
let choice_enum_token_stream = super::new_variant_enum(
let choice_enum_token_stream = super::enum_token_stream(
&choice_enum_name,
&choices
.choices
Expand Down
47 changes: 36 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ mod generators;
mod schema;

use deluxe::SpannedValue;
use generators::{bitflag_token_stream, enum_token_stream, type_name_from_id};
use proc_macro_error::{abort, emit_call_site_error, emit_error, proc_macro_error};
use quote::{quote, ToTokens};
use schema::Schema;

use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Expand All @@ -24,21 +25,21 @@ use crate::{

// TODO:
// * Replace proc-macro-error dep with syn::Result
// * Decouple enum and flags generation from key generation, make them standalone
// * Use `quote_spanned` where applicable for better error propagation on generated code
// * Remove serde and deluxe dependencies (consider using quick-xml directly or xmlserde)
// * Improve enum generation (create enum based on its definition, instead of by key; also add doc alias for its id)
// * Add way to map setter and getters value
// * Add `bind_#key writable`, `user_#key_value`, `connect_#key_writable_changed` variants
// * Add trybuild tests
// * Support for multiple schema

#[derive(deluxe::ParseMetaItem)]
struct GenSettings {
file: SpannedValue<String>,
id: Option<SpannedValue<String>>,
#[deluxe(default=true)]
default: bool
#[deluxe(default = true)]
default: bool,
#[deluxe(default = true)]
globals: bool,
}

#[derive(deluxe::ParseAttributes)]
Expand Down Expand Up @@ -294,6 +295,7 @@ pub fn gen_settings(
file: file_attr,
id: id_attr,
default: impl_default,
globals,
} = match deluxe::parse2(attr.into()) {
Ok(gen_settings) => gen_settings,
Err(err) => return err.to_compile_error().into(),
Expand All @@ -315,10 +317,8 @@ pub fn gen_settings(
[] => {
abort!(file_attr_span, "schema file must have a single schema");
}
[schema] => {
schema
}
_schemas if id_attr.is_none() => {
[schema] => schema,
_schemas if id_attr.is_none() => {
abort!(
file_attr_span,
"schema file contains multiple schemas, specify one with `id`"
Expand All @@ -333,7 +333,7 @@ pub fn gen_settings(
});
let id_attr = SpannedValue::into_inner(id_attr.clone());
schemas
.into_iter()
.iter()
.find(|schema| schema.id == id_attr)
.unwrap_or_else(|| abort!(file_attr_span, "schema with id `{}` not found", id_attr))
}
Expand Down Expand Up @@ -472,14 +472,39 @@ pub fn gen_settings(
.iter()
.map(|flag| (flag.id.to_string(), flag))
.collect::<HashMap<_, _>>();
let mut key_generators = KeyGenerators::with_defaults(enums, flags);

let mut key_generators = KeyGenerators::with_defaults(enums.clone(), flags.clone());
key_generators.add_signature_overrides(signature_overrides);
key_generators.add_key_name_overrides(key_name_overrides);

// Generate code
let mut aux_token_stream = proc_macro2::TokenStream::new();
let mut keys_token_stream = proc_macro2::TokenStream::new();

if globals {
for (id, enumeration) in enums {
let enum_type = type_name_from_id(&id);
aux_token_stream.extend(enum_token_stream(
&enum_type,
&enumeration
.values
.iter()
.map(|value| (value.nick.as_str(), Some(value.value)))
.collect::<Vec<_>>(),
settings_struct.vis.clone(),
));
}

for (id, flag) in flags {
let flag_type = type_name_from_id(&id);
aux_token_stream.extend(bitflag_token_stream(
&flag_type,
flag,
settings_struct.vis.clone(),
));
}
}

for key in &schema.keys {
match key_generators
.get(key, settings_struct.vis.clone())
Expand Down
Loading

0 comments on commit ff78919

Please sign in to comment.