diff --git a/book/src/config_api.md b/book/src/config_api.md index dd6a0d936..091a77474 100644 --- a/book/src/config_api.md +++ b/book/src/config_api.md @@ -24,6 +24,13 @@ single_version_file = true # which do not have an override for `generate_display_trait` # (defaults to "true") generate_display_trait = true +# Generation of Variant serialization traits enabled for all enums and flags which do not +# have an override for `generate_variant_traits`. Generates traits for StaticVariantType, +# ToVariant, FromVariant, and Into. Can be "none" to skip generating traits, +# "repr" to serialize to INT (for enums) and UINT (for flags), or "string" to serialize to +# string values. +# (defaults to "none") +generate_variant_traits = "repr" # Trust the nullability information about return values. If this is disabled # then any pointer return type is assumed to be nullable unless there is an # explicit override for it. @@ -110,6 +117,8 @@ version = "3.12" cfg_condition = "mycond" # if you want to override default option Ex. for write your own Display implementation generate_display_trait = false +# if you want to override default option Ex. for write your own Variant serialization +generate_variant_traits = "none" # if you want to generate builder with name SomeClassBuilder generate_builder = true # trust return value nullability annotations for this specific type. diff --git a/src/codegen/enums.rs b/src/codegen/enums.rs index 91f0aea2d..60a3470cc 100644 --- a/src/codegen/enums.rs +++ b/src/codegen/enums.rs @@ -13,9 +13,9 @@ use crate::{ cfg_deprecated, derives, doc_alias, version_condition, version_condition_no_doc, version_condition_string, }, - generate_default_impl, + generate_default_impl, generate_variant_impls, }, - config::gobjects::GObject, + config::{config::VariantTraitMode, gobjects::GObject}, env::Env, file_saver, library::*, @@ -387,14 +387,14 @@ impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{ )?; } + let configured_functions = config.functions.matched("get_type"); + let version = std::iter::once(enum_.version) + .chain(configured_functions.iter().map(|f| f.version)) + .max() + .flatten(); + // Generate StaticType trait implementation. if let Some(ref get_type) = enum_.glib_get_type { - let configured_functions = config.functions.matched("get_type"); - let version = std::iter::once(enum_.version) - .chain(configured_functions.iter().map(|f| f.version)) - .max() - .flatten(); - version_condition(w, env, None, version, false, 0)?; cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?; allow_deprecated(w, enum_.deprecated_version, false, 0)?; @@ -506,5 +506,47 @@ impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{ }, )?; + match config.generate_variant_traits { + VariantTraitMode::Repr => generate_variant_impls( + w, + env, + config, + &enum_.name, + version, + enum_.deprecated_version, + "i", + "match unsafe { FromGlib::from_glib(variant.get::()?) } { + Self::__Unknown(_) => None, + value => Some(value), + }", + "self.into_glib().to_variant()", + )?, + VariantTraitMode::String => { + let enumclass = use_glib_type(env, "EnumClass"); + generate_variant_impls( + w, + env, + config, + &enum_.name, + version, + enum_.deprecated_version, + "s", + &format!( + "if !variant.is::() {{ + return None; + }} + let enum_class = {enumclass}::new(::static_type()).unwrap(); + let value = enum_class.value_by_nick(variant.str()?)?.value(); + Some(unsafe {{ FromGlib::from_glib(value) }})", + ), + &format!( + "let enum_class = {enumclass}::new(::static_type()).unwrap(); + enum_class.value(self.into_glib()).unwrap().nick().to_variant()", + ) + )? + } + _ => {} + } + Ok(()) } diff --git a/src/codegen/flags.rs b/src/codegen/flags.rs index 992044ab7..cc8bb4161 100644 --- a/src/codegen/flags.rs +++ b/src/codegen/flags.rs @@ -12,9 +12,9 @@ use crate::{ cfg_deprecated, derives, doc_alias, version_condition, version_condition_doc, version_condition_no_doc, version_condition_string, }, - generate_default_impl, + generate_default_impl, generate_variant_impls, }, - config::gobjects::GObject, + config::{config::VariantTraitMode, gobjects::GObject}, env::Env, file_saver, library::*, @@ -258,13 +258,13 @@ impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{ assert = assert )?; - if let Some(ref get_type) = flags.glib_get_type { - let configured_functions = config.functions.matched("get_type"); - let version = std::iter::once(flags.version) - .chain(configured_functions.iter().map(|f| f.version)) - .max() - .flatten(); + let configured_functions = config.functions.matched("get_type"); + let version = std::iter::once(flags.version) + .chain(configured_functions.iter().map(|f| f.version)) + .max() + .flatten(); + if let Some(ref get_type) = flags.glib_get_type { version_condition(w, env, None, version, false, 0)?; cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?; allow_deprecated(w, flags.deprecated_version, false, 0)?; @@ -357,5 +357,43 @@ impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{ writeln!(w)?; } + match config.generate_variant_traits { + VariantTraitMode::Repr => generate_variant_impls( + w, + env, + config, + &flags.name, + version, + flags.deprecated_version, + "u", + "Some(Self::from_bits(variant.get::()?)?)", + "self.into_glib().to_variant()", + )?, + VariantTraitMode::String => { + let flagsclass = use_glib_type(env, "FlagsClass"); + generate_variant_impls( + w, + env, + config, + &flags.name, + version, + flags.deprecated_version, + "s", + &format!( + "if !variant.is::() {{ + return None; + }} + let flags_class = {flagsclass}::new(::static_type()).unwrap(); + Self::from_bits(flags_class.from_nick_string(variant.str()?).ok()?)", + ), + &format!( + "let flags_class = {flagsclass}::new(::static_type()).unwrap(); + flags_class.to_nick_string(self.into_glib()).to_variant()", + ) + )? + } + _ => {} + } + Ok(()) } diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index a9b45c97d..9e63871aa 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -11,6 +11,7 @@ use crate::{ env::Env, file_saver::*, library::Member, + nameutil::use_glib_type, version::Version, }; @@ -43,6 +44,8 @@ mod trampoline; mod trampoline_from_glib; mod visibility; pub use visibility::Visibility; + +use self::general::{allow_deprecated, cfg_condition_no_doc}; mod trampoline_to_glib; pub mod translate_from_glib; pub mod translate_to_glib; @@ -168,3 +171,82 @@ pub fn generate_default_impl< Ok(()) } } + +pub fn generate_variant_impls( + w: &mut dyn Write, + env: &Env, + config: &GObject, + type_name: &str, + type_version: Option, + deprecated_version: Option, + static_variant_type_str: &str, + from_variant_impl: &str, + to_variant_impl: &str, +) -> Result<()> { + let assert = if env.config.generate_safety_asserts { + "skip_assert_initialized!();\n\t\t" + } else { + "" + }; + let gvariant = use_glib_type(env, "Variant"); + let tovariant = use_glib_type(env, "ToVariant"); + + version_condition(w, env, None, type_version, false, 0)?; + cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?; + allow_deprecated(w, deprecated_version, false, 0)?; + writeln!( + w, + "impl {staticvarianttype} for {type_name} {{ + fn static_variant_type() -> std::borrow::Cow<'static, {variantty}> {{ + {assert}std::borrow::Cow::Borrowed(unsafe {{ + {variantty}::from_str_unchecked(\"{static_variant_type_str}\") + }}) + }} +}}", + staticvarianttype = use_glib_type(env, "StaticVariantType"), + variantty = use_glib_type(env, "VariantTy"), + )?; + writeln!(w)?; + + version_condition(w, env, None, type_version, false, 0)?; + cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?; + allow_deprecated(w, deprecated_version, false, 0)?; + writeln!( + w, + "impl {fromvariant} for {type_name} {{ + fn from_variant(variant: &{gvariant}) -> Option {{ + {assert}{from_variant_impl} + }} +}}", + fromvariant = use_glib_type(env, "FromVariant"), + )?; + writeln!(w)?; + + version_condition(w, env, None, type_version, false, 0)?; + cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?; + allow_deprecated(w, deprecated_version, false, 0)?; + writeln!( + w, + "impl {tovariant} for {type_name} {{ + fn to_variant(&self) -> {gvariant} {{ + {assert}{to_variant_impl} + }} +}}" + )?; + writeln!(w)?; + + version_condition(w, env, None, type_version, false, 0)?; + cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?; + allow_deprecated(w, deprecated_version, false, 0)?; + writeln!( + w, + "impl From<{type_name}> for {gvariant} {{ + fn from(v: {type_name}) -> Self {{ + {assert}{tovariant}::to_variant(&v) + }} +}}", + )?; + writeln!(w)?; + + Ok(()) +} diff --git a/src/config/config.rs b/src/config/config.rs index 9113d3787..ab803659e 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -90,6 +90,38 @@ impl GirVersion { } } +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum VariantTraitMode { + #[default] + None, + Repr, + String, +} + +impl VariantTraitMode { + pub fn is_none(self) -> bool { + self == Self::None + } + pub fn is_repr(self) -> bool { + self == Self::Repr + } + pub fn is_string(self) -> bool { + self == Self::String + } +} + +impl FromStr for VariantTraitMode { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "none" => Ok(Self::None), + "repr" => Ok(Self::Repr), + "string" => Ok(Self::String), + e => Err(format!("Wrong variant trait mode: \"{}\"", e)), + } + } +} + #[derive(Debug)] pub struct Config { pub work_mode: WorkMode, @@ -113,6 +145,7 @@ pub struct Config { pub concurrency: library::Concurrency, pub single_version_file: Option, pub generate_display_trait: bool, + pub generate_variant_traits: VariantTraitMode, pub trust_return_value_nullability: bool, pub docs_rs_features: Vec, pub disable_format: bool, @@ -255,6 +288,13 @@ impl Config { None => true, }; + let generate_variant_traits = match toml.lookup("options.generate_variant_traits") { + Some(v) => v + .as_result_str("options.generate_variant_traits")? + .parse()?, + None => Default::default(), + }; + let trust_return_value_nullability = match toml.lookup("options.trust_return_value_nullability") { Some(v) => v.as_result_bool("options.trust_return_value_nullability")?, @@ -281,6 +321,7 @@ impl Config { t, concurrency, generate_display_trait, + generate_variant_traits, generate_builder, trust_return_value_nullability, ) @@ -291,6 +332,7 @@ impl Config { &toml, concurrency, generate_display_trait, + generate_variant_traits, generate_builder, trust_return_value_nullability, ); @@ -367,6 +409,7 @@ impl Config { concurrency, single_version_file, generate_display_trait, + generate_variant_traits, trust_return_value_nullability, docs_rs_features, disable_format, diff --git a/src/config/gobjects.rs b/src/config/gobjects.rs index 18a2c6aff..ecf095463 100644 --- a/src/config/gobjects.rs +++ b/src/config/gobjects.rs @@ -9,6 +9,7 @@ use toml::Value; use super::{ child_properties::ChildProperties, + config::VariantTraitMode, constants::Constants, derives::Derives, functions::Functions, @@ -84,6 +85,7 @@ pub struct GObject { pub must_use: bool, pub conversion_type: Option, pub generate_display_trait: bool, + pub generate_variant_traits: VariantTraitMode, pub trust_return_value_nullability: bool, pub manual_traits: Vec, pub align: Option, @@ -122,6 +124,7 @@ impl Default for GObject { must_use: false, conversion_type: None, generate_display_trait: true, + generate_variant_traits: Default::default(), trust_return_value_nullability: false, manual_traits: Vec::default(), align: None, @@ -145,6 +148,7 @@ pub fn parse_toml( toml_objects: &Value, concurrency: library::Concurrency, generate_display_trait: bool, + generate_variant_traits: VariantTraitMode, generate_builder: bool, trust_return_value_nullability: bool, ) -> GObjects { @@ -154,6 +158,7 @@ pub fn parse_toml( toml_object, concurrency, generate_display_trait, + generate_variant_traits, generate_builder, trust_return_value_nullability, ); @@ -228,6 +233,7 @@ fn parse_object( toml_object: &Value, concurrency: library::Concurrency, default_generate_display_trait: bool, + default_generate_variant_traits: VariantTraitMode, generate_builder: bool, trust_return_value_nullability: bool, ) -> GObject { @@ -263,6 +269,7 @@ fn parse_object( "cfg_condition", "must_use", "generate_display_trait", + "generate_variant_traits", "trust_return_value_nullability", "manual_traits", "align", @@ -355,6 +362,10 @@ fn parse_object( .lookup("generate_display_trait") .and_then(Value::as_bool) .unwrap_or(default_generate_display_trait); + let generate_variant_traits = toml_object + .lookup("generate_variant_traits") + .map(|v| VariantTraitMode::from_str(v.as_str().unwrap()).unwrap()) + .unwrap_or(default_generate_variant_traits); let trust_return_value_nullability = toml_object .lookup("trust_return_value_nullability") .and_then(Value::as_bool) @@ -491,6 +502,7 @@ fn parse_object( must_use, conversion_type, generate_display_trait, + generate_variant_traits, trust_return_value_nullability, manual_traits, align, @@ -511,6 +523,7 @@ pub fn parse_status_shorthands( toml: &Value, concurrency: library::Concurrency, generate_display_trait: bool, + generate_variant_traits: VariantTraitMode, generate_builder: bool, trust_return_value_nullability: bool, ) { @@ -522,6 +535,7 @@ pub fn parse_status_shorthands( toml, concurrency, generate_display_trait, + generate_variant_traits, generate_builder, trust_return_value_nullability, ); @@ -534,6 +548,7 @@ fn parse_status_shorthand( toml: &Value, concurrency: library::Concurrency, generate_display_trait: bool, + generate_variant_traits: VariantTraitMode, generate_builder: bool, trust_return_value_nullability: bool, ) { @@ -549,6 +564,7 @@ fn parse_status_shorthand( status, concurrency, generate_display_trait, + generate_variant_traits, trust_return_value_nullability, generate_builder, ..Default::default() @@ -606,7 +622,14 @@ status = "generate" "#, ); - let object = parse_object(toml, Concurrency::default(), false, false, false); + let object = parse_object( + toml, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert_eq!(object.conversion_type, None); } @@ -620,7 +643,14 @@ conversion_type = "Option" "#, ); - let object = parse_object(&toml, Concurrency::default(), false, false, false); + let object = parse_object( + &toml, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert_eq!(object.conversion_type, Some(ConversionType::Option)); } @@ -635,7 +665,14 @@ status = "generate" "#, ); - let object = parse_object(toml, Concurrency::default(), false, false, false); + let object = parse_object( + toml, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert_eq!(object.conversion_type, Some(ConversionType::Option)); } @@ -650,7 +687,14 @@ status = "generate" "#, ); - let object = parse_object(toml, Concurrency::default(), false, false, false); + let object = parse_object( + toml, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert_eq!( object.conversion_type, Some(ConversionType::Result { @@ -672,7 +716,14 @@ status = "generate" "#, ); - let object = parse_object(toml, Concurrency::default(), false, false, false); + let object = parse_object( + toml, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert_eq!( object.conversion_type, Some(ConversionType::Result { @@ -695,7 +746,14 @@ status = "generate" "#, ); - let object = parse_object(toml, Concurrency::default(), false, false, false); + let object = parse_object( + toml, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert_eq!( object.conversion_type, Some(ConversionType::Result { @@ -723,7 +781,16 @@ status = "generate" let object = toml .lookup("object") - .map(|t| parse_toml(t, Concurrency::default(), false, false, false)) + .map(|t| { + parse_toml( + t, + Concurrency::default(), + false, + Default::default(), + false, + false, + ) + }) .expect("parsing failed"); assert_eq!( object["Test"].constants, @@ -752,7 +819,14 @@ generate_doc = false "#, ); - let object = parse_object(r, Concurrency::default(), false, false, false); + let object = parse_object( + r, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert!(!object.generate_doc); // Ensure that the default value is "true". @@ -762,7 +836,14 @@ name = "Test" status = "generate" "#, ); - let object = parse_object(r, Concurrency::default(), false, false, false); + let object = parse_object( + r, + Concurrency::default(), + false, + Default::default(), + false, + false, + ); assert!(object.generate_doc); } }