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

✨ zd,zv: Value derive improvements for enums #540

Merged
merged 3 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions zvariant/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ mod tests {
use crate::Fd;
use crate::{
serialized::{Context, Format},
Array, Basic, DeserializeDict, DeserializeValue, Dict, Error, ObjectPath, Result,
SerializeDict, SerializeValue, Str, Structure, Type, Value, BE, LE, NATIVE_ENDIAN,
Array, Basic, DeserializeDict, DeserializeValue, Dict, Error, ObjectPath, OwnedValue,
Result, SerializeDict, SerializeValue, Str, Structure, Type, Value, BE, LE, NATIVE_ENDIAN,
};

// Test through both generic and specific API (wrt byte order)
Expand Down Expand Up @@ -1213,7 +1213,7 @@ mod tests {
// Now a test case for https://github.com/dbus2/zbus/issues/549
#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct Data {
inner: zvariant::OwnedValue,
inner: OwnedValue,
}

let value = zvariant::Value::new("variant-value");
Expand Down Expand Up @@ -1395,7 +1395,7 @@ mod tests {
let _: UnitStruct = encoded.deserialize().unwrap().0;

#[repr(u8)]
#[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
#[derive(Deserialize_repr, Serialize_repr, Type, Value, OwnedValue, Debug, PartialEq)]
enum Enum {
Variant1,
Variant2,
Expand All @@ -1408,8 +1408,12 @@ mod tests {
let decoded: Enum = encoded.deserialize().unwrap().0;
assert_eq!(decoded, Enum::Variant3);

assert_eq!(Value::from(Enum::Variant1), Value::U8(0));
assert_eq!(Enum::try_from(Value::U8(2)), Ok(Enum::Variant3));
assert_eq!(Enum::try_from(Value::U8(4)), Err(Error::IncorrectType));

#[repr(i64)]
#[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
#[derive(Deserialize_repr, Serialize_repr, Type, Value, OwnedValue, Debug, PartialEq)]
enum Enum2 {
Variant1,
Variant2,
Expand All @@ -1422,7 +1426,11 @@ mod tests {
let decoded: Enum2 = encoded.deserialize().unwrap().0;
assert_eq!(decoded, Enum2::Variant2);

#[derive(Deserialize, Serialize, Type, Debug, PartialEq)]
assert_eq!(Value::from(Enum2::Variant1), Value::I64(0));
assert_eq!(Enum2::try_from(Value::I64(2)), Ok(Enum2::Variant3));
assert_eq!(Enum2::try_from(Value::I64(4)), Err(Error::IncorrectType));

#[derive(Deserialize, Serialize, Type, Value, OwnedValue, Debug, PartialEq)]
enum NoReprEnum {
Variant1,
Variant2,
Expand All @@ -1439,10 +1447,10 @@ mod tests {
let decoded: NoReprEnum = encoded.deserialize().unwrap().0;
assert_eq!(decoded, NoReprEnum::Variant2);

#[derive(Deserialize, Serialize, Type, Debug, PartialEq)]
#[zvariant(signature = "s")]
#[derive(Deserialize, Serialize, Type, Value, OwnedValue, Debug, PartialEq)]
#[zvariant(signature = "s", rename_all = "snake_case")]
enum StrEnum {
Variant1,
VariantOne,
Variant2,
Variant3,
}
Expand All @@ -1453,6 +1461,20 @@ mod tests {
let decoded: StrEnum = encoded.deserialize().unwrap().0;
assert_eq!(decoded, StrEnum::Variant2);

assert_eq!(
StrEnum::try_from(Value::Str("variant_one".into())),
Ok(StrEnum::VariantOne)
);
assert_eq!(
StrEnum::try_from(Value::Str("variant2".into())),
Ok(StrEnum::Variant2)
);
assert_eq!(
StrEnum::try_from(Value::Str("variant4".into())),
Err(Error::IncorrectType)
);
assert_eq!(StrEnum::try_from(Value::U32(0)), Err(Error::IncorrectType));

#[derive(Deserialize, Serialize, Type)]
enum NewType {
Variant1(f64),
Expand Down
24 changes: 4 additions & 20 deletions zvariant_derive/src/dict.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Error, Field};
use zvariant_utils::{case, macros};
use zvariant_utils::macros;

use crate::utils::*;

Expand All @@ -10,25 +10,9 @@ fn dict_name_for_field(
rename_attr: Option<String>,
rename_all_attr: Option<&str>,
) -> Result<String, Error> {
if let Some(name) = rename_attr {
Ok(name)
} else {
let ident = f.ident.as_ref().unwrap().to_string();

match rename_all_attr {
Some("lowercase") => Ok(ident.to_ascii_lowercase()),
Some("UPPERCASE") => Ok(ident.to_ascii_uppercase()),
Some("PascalCase") => Ok(case::pascal_or_camel_case(&ident, true)),
Some("camelCase") => Ok(case::pascal_or_camel_case(&ident, false)),
Some("snake_case") => Ok(case::snake_or_kebab_case(&ident, true)),
Some("kebab-case") => Ok(case::snake_or_kebab_case(&ident, false)),
None => Ok(ident),
Some(other) => Err(Error::new(
f.span(),
format!("invalid `rename_all` attribute value {other}"),
)),
}
}
let ident = f.ident.as_ref().unwrap().to_string();

rename_identifier(ident, f.span(), rename_attr, rename_all_attr)
}

pub fn expand_serialize_derive(input: DeriveInput) -> Result<TokenStream, Error> {
Expand Down
32 changes: 27 additions & 5 deletions zvariant_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,26 +382,48 @@ pub fn deserialize_dict_macro_derive(input: TokenStream) -> TokenStream {
/// assert_eq!(s.field2.as_str(), "/blah");
/// ```
///
/// Enums also supported but currently only simple ones w/ an integer representation:
/// Enums also supported but currently only with unit variants:
///
/// ```
/// # use zvariant::{OwnedValue, Value};
/// #
/// #[derive(Debug, PartialEq, Value, OwnedValue)]
/// // Default representation is `u32`.
/// #[repr(u8)]
/// enum Enum {
/// Variant1 = 1,
/// Variant2 = 2,
/// Variant1 = 0,
/// Variant2,
/// }
///
/// let value = Value::from(Enum::Variant1);
/// let e = Enum::try_from(value).unwrap();
/// assert_eq!(e, Enum::Variant1);
/// assert_eq!(e as u8, 0);
/// let value = OwnedValue::try_from(Enum::Variant2).unwrap();
/// let e = Enum::try_from(value).unwrap();
/// assert_eq!(e, Enum::Variant2);
/// ```
///
/// String-encoded enums are also supported:
///
/// ```
/// # use zvariant::{OwnedValue, Value};
/// #
/// #[derive(Debug, PartialEq, Value, OwnedValue)]
/// #[zvariant(signature = "s")]
/// enum StrEnum {
/// Variant1,
/// Variant2,
/// }
///
/// let value = Value::from(StrEnum::Variant1);
/// let e = StrEnum::try_from(value).unwrap();
/// assert_eq!(e, StrEnum::Variant1);
/// let value = OwnedValue::try_from(StrEnum::Variant2).unwrap();
/// let e = StrEnum::try_from(value).unwrap();
/// assert_eq!(e, StrEnum::Variant2);
/// ```
///
/// # Dictionary encoding
///
/// For treating your type as a dictionary, you can use the `signature = "dict"` attribute. See
Expand All @@ -410,7 +432,7 @@ pub fn deserialize_dict_macro_derive(input: TokenStream) -> TokenStream {
///
/// [`Value`]: https://docs.rs/zvariant/latest/zvariant/enum.Value.html
/// [`Type`]: derive.Type.html#custom-types
#[proc_macro_derive(Value)]
#[proc_macro_derive(Value, attributes(zbus, zvariant))]
pub fn value_macro_derive(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
value::expand_derive(ast, value::ValueType::Value)
Expand All @@ -425,7 +447,7 @@ pub fn value_macro_derive(input: TokenStream) -> TokenStream {
/// See [`Value`] documentation for examples.
///
/// [`OwnedValue`]: https://docs.rs/zvariant/latest/zvariant/struct.OwnedValue.html
#[proc_macro_derive(OwnedValue)]
#[proc_macro_derive(OwnedValue, attributes(zbus, zvariant))]
pub fn owned_value_macro_derive(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
value::expand_derive(ast, value::ValueType::OwnedValue)
Expand Down
31 changes: 30 additions & 1 deletion zvariant_derive/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use proc_macro_crate::{crate_name, FoundCrate};
use quote::{format_ident, quote};
use zvariant_utils::def_attrs;
use zvariant_utils::{case, def_attrs};

pub fn zvariant_path() -> TokenStream {
if let Ok(FoundCrate::Name(name)) = crate_name("zvariant") {
Expand All @@ -15,11 +15,40 @@ pub fn zvariant_path() -> TokenStream {
}
}

pub fn rename_identifier(
ident: String,
span: proc_macro2::Span,
rename_attr: Option<String>,
rename_all_attr: Option<&str>,
) -> Result<String, syn::Error> {
if let Some(name) = rename_attr {
Ok(name)
} else {
match rename_all_attr {
Some("lowercase") => Ok(ident.to_ascii_lowercase()),
Some("UPPERCASE") => Ok(ident.to_ascii_uppercase()),
Some("PascalCase") => Ok(case::pascal_or_camel_case(&ident, true)),
Some("camelCase") => Ok(case::pascal_or_camel_case(&ident, false)),
Some("snake_case") => Ok(case::snake_or_kebab_case(&ident, true)),
Some("kebab-case") => Ok(case::snake_or_kebab_case(&ident, false)),
None => Ok(ident),
Some(other) => Err(syn::Error::new(
span,
format!("invalid `rename_all` attribute value {other}"),
)),
}
}
}

def_attrs! {
crate zbus, zvariant;

/// Attributes defined on structures.
pub StructAttributes("struct") { signature str, rename_all str, deny_unknown_fields none };
/// Attributes defined on fields.
pub FieldAttributes("field") { rename str };
/// Attributes defined on enumerations.
pub EnumAttributes("enum") { signature str, rename_all str };
/// Attributes defined on variants.
pub VariantAttributes("variant") { rename str };
}
Loading
Loading