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

Add support for field defaults to proc-macro frontend #1560

Merged
merged 2 commits into from
May 25, 2023
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
6 changes: 6 additions & 0 deletions docs/manual/src/proc_macro/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ will fail).
pub struct MyRecord {
pub field_a: String,
pub field_b: Option<Arc<MyObject>>,
// Fields can have a default value.
// Currently, only string, integer, float and boolean literals are supported as defaults.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it is worth adding something like "these work the same way as documented for UDL files" or similar (the docs need a restructure, but udl/structs.md does describe this in some detail)

#[uniffi(default = "hello")]
pub greeting: String,
#[uniffi(default = true)]
pub some_flag: bool,
}
```

Expand Down
9 changes: 9 additions & 0 deletions fixtures/metadata/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use uniffi_meta::*;
mod person {
#[derive(uniffi::Record, Debug)]
pub struct Person {
#[uniffi(default = "test")]
name: String,
age: u16,
}
Expand Down Expand Up @@ -173,10 +174,14 @@ mod test_metadata {
FieldMetadata {
name: "name".into(),
ty: Type::String,
default: Some(Literal::Str {
value: "test".to_owned(),
}),
},
FieldMetadata {
name: "age".into(),
ty: Type::U16,
default: None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missed opportunity here for default: Default::default() :)

},
],
},
Expand Down Expand Up @@ -225,6 +230,7 @@ mod test_metadata {
fields: vec![FieldMetadata {
name: "data".into(),
ty: Type::String,
default: None,
}],
},
VariantMetadata {
Expand All @@ -234,6 +240,7 @@ mod test_metadata {
ty: Type::Record {
name: "Person".into(),
},
default: None,
}],
},
],
Expand Down Expand Up @@ -281,6 +288,7 @@ mod test_metadata {
fields: vec![FieldMetadata {
name: "reason".into(),
ty: Type::String,
default: None,
}],
},
VariantMetadata {
Expand All @@ -290,6 +298,7 @@ mod test_metadata {
ty: Type::Enum {
name: "Weapon".into(),
},
default: None,
}],
},
],
Expand Down
32 changes: 22 additions & 10 deletions uniffi_bindgen/src/interface/enum_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,20 @@ impl Enum {
}
}

impl From<uniffi_meta::EnumMetadata> for Enum {
fn from(meta: uniffi_meta::EnumMetadata) -> Self {
impl TryFrom<uniffi_meta::EnumMetadata> for Enum {
type Error = anyhow::Error;

fn try_from(meta: uniffi_meta::EnumMetadata) -> Result<Self> {
let flat = meta.variants.iter().all(|v| v.fields.is_empty());
Self {
Ok(Self {
name: meta.name,
variants: meta.variants.into_iter().map(Into::into).collect(),
variants: meta
.variants
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_>>()?,
flat,
}
})
}
}

Expand Down Expand Up @@ -210,12 +216,18 @@ impl Variant {
}
}

impl From<uniffi_meta::VariantMetadata> for Variant {
fn from(meta: uniffi_meta::VariantMetadata) -> Self {
Self {
impl TryFrom<uniffi_meta::VariantMetadata> for Variant {
type Error = anyhow::Error;

fn try_from(meta: uniffi_meta::VariantMetadata) -> Result<Self> {
Ok(Self {
name: meta.name,
fields: meta.fields.into_iter().map(Into::into).collect(),
}
fields: meta
.fields
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_>>()?,
})
}
}

Expand Down
16 changes: 11 additions & 5 deletions uniffi_bindgen/src/interface/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,22 @@ impl Error {
}
}

impl From<uniffi_meta::ErrorMetadata> for Error {
fn from(meta: uniffi_meta::ErrorMetadata) -> Self {
Self {
impl TryFrom<uniffi_meta::ErrorMetadata> for Error {
type Error = anyhow::Error;

fn try_from(meta: uniffi_meta::ErrorMetadata) -> Result<Self> {
Ok(Self {
name: meta.name.clone(),
enum_: Enum {
name: meta.name,
variants: meta.variants.into_iter().map(Into::into).collect(),
variants: meta
.variants
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_>>()?,
flat: meta.flat,
},
}
})
}
}

Expand Down
62 changes: 61 additions & 1 deletion uniffi_bindgen/src/interface/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! This module provides support for interpreting literal values from the UDL,
//! which appear in places such as default arguments.

use anyhow::{bail, Result};
use anyhow::{bail, ensure, Context, Result};
use uniffi_meta::Checksum;

use super::types::Type;
Expand All @@ -34,6 +34,66 @@ pub enum Literal {
Null,
}

impl Literal {
pub(crate) fn from_metadata(
name: &str,
ty: &Type,
default: uniffi_meta::Literal,
) -> Result<Self> {
Ok(match default {
uniffi_meta::Literal::Str { value } => {
ensure!(
matches!(ty, Type::String),
"field {name} of type {ty:?} can't have a default value of type string"
);
Self::String(value)
}
uniffi_meta::Literal::Int { base10_digits } => {
macro_rules! parse_int {
($ty:ident, $variant:ident) => {
Self::$variant(
base10_digits
.parse::<$ty>()
.with_context(|| format!("parsing default for field {}", name))?
.into(),
Radix::Decimal,
ty.to_owned(),
)
};
}

match ty {
Type::UInt8 => parse_int!(u8, UInt),
Type::Int8 => parse_int!(i8, Int),
Type::UInt16 => parse_int!(u16, UInt),
Type::Int16 => parse_int!(i16, Int),
Type::UInt32 => parse_int!(u32, UInt),
Type::Int32 => parse_int!(i32, Int),
Type::UInt64 => parse_int!(u64, UInt),
Type::Int64 => parse_int!(i64, Int),
_ => {
bail!("field {name} of type {ty:?} can't have a default value of type integer");
}
}
}
uniffi_meta::Literal::Float { base10_digits } => match ty {
Type::Float32 => Self::Float(base10_digits, Type::Float32),
Type::Float64 => Self::Float(base10_digits, Type::Float64),
_ => {
bail!("field {name} of type {ty:?} can't have a default value of type float");
}
},
uniffi_meta::Literal::Bool { value } => {
ensure!(
matches!(ty, Type::String),
"field {name} of type {ty:?} can't have a default value of type boolean"
);
Self::Boolean(value)
}
})
}
}

// Represent the radix of integer literal values.
// We preserve the radix into the generated bindings for readability reasons.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Checksum)]
Expand Down
38 changes: 26 additions & 12 deletions uniffi_bindgen/src/interface/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,18 @@ impl Record {
}
}

impl From<uniffi_meta::RecordMetadata> for Record {
fn from(meta: uniffi_meta::RecordMetadata) -> Self {
Self {
impl TryFrom<uniffi_meta::RecordMetadata> for Record {
type Error = anyhow::Error;

fn try_from(meta: uniffi_meta::RecordMetadata) -> Result<Self> {
Ok(Self {
name: meta.name,
fields: meta.fields.into_iter().map(Into::into).collect(),
}
fields: meta
.fields
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_>>()?,
})
}
}

Expand Down Expand Up @@ -135,13 +141,21 @@ impl Field {
}
}

impl From<uniffi_meta::FieldMetadata> for Field {
fn from(meta: uniffi_meta::FieldMetadata) -> Self {
Self {
name: meta.name,
type_: convert_type(&meta.ty),
default: None,
}
impl TryFrom<uniffi_meta::FieldMetadata> for Field {
type Error = anyhow::Error;

fn try_from(meta: uniffi_meta::FieldMetadata) -> Result<Self> {
let name = meta.name;
let type_ = convert_type(&meta.ty);
let default = meta
.default
.map(|d| Literal::from_metadata(&name, &type_, d))
.transpose()?;
Ok(Self {
name,
type_,
default,
})
}
}

Expand Down
6 changes: 3 additions & 3 deletions uniffi_bindgen/src/macro_metadata/ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ pub fn add_to_ci(
iface.types.add_known_type(&ty)?;
iface.types.add_type_definition(&meta.name, ty)?;

let record: Record = meta.into();
let record: Record = meta.try_into()?;
iface.add_record_definition(record)?;
}
Metadata::Enum(meta) => {
let ty = Type::Enum(meta.name.clone());
iface.types.add_known_type(&ty)?;
iface.types.add_type_definition(&meta.name, ty)?;

let enum_: Enum = meta.into();
let enum_: Enum = meta.try_into()?;
iface.add_enum_definition(enum_)?;
}
Metadata::Object(meta) => {
Expand All @@ -95,7 +95,7 @@ pub fn add_to_ci(
iface.types.add_known_type(&ty)?;
iface.types.add_type_definition(&meta.name, ty)?;

let error: Error = meta.into();
let error: Error = meta.try_into()?;
iface.add_error_definition(error)?;
}
}
Expand Down
6 changes: 6 additions & 0 deletions uniffi_core/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ pub mod codes {
pub const TYPE_FUTURE: u8 = 24;
pub const TYPE_FOREIGN_EXECUTOR: u8 = 25;
pub const TYPE_UNIT: u8 = 255;

// Literal codes
pub const LIT_STR: u8 = 0;
pub const LIT_INT: u8 = 1;
pub const LIT_FLOAT: u8 = 2;
pub const LIT_BOOL: u8 = 3;
}

const BUF_SIZE: usize = 2048;
Expand Down
2 changes: 2 additions & 0 deletions uniffi_macros/src/enum_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ pub fn variant_metadata(enum_: &DataEnum) -> syn::Result<Vec<TokenStream>> {
#(
.concat_str(#field_names)
.concat(<#field_types as ::uniffi::FfiConverter<crate::UniFfiTag>>::TYPE_ID_META)
// field defaults not yet supported for enums
.concat_bool(false)
)*
})
})
Expand Down
2 changes: 1 addition & 1 deletion uniffi_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub fn export(attr_args: TokenStream, input: TokenStream) -> TokenStream {
.into()
}

#[proc_macro_derive(Record)]
#[proc_macro_derive(Record, attributes(uniffi))]
pub fn derive_record(input: TokenStream) -> TokenStream {
expand_record(parse_macro_input!(input)).into()
}
Expand Down
Loading