Skip to content

Commit 558fa83

Browse files
authored
Add WpContextualExcludeFromFields attribute (#833)
* Add WpContextualExcludeFromFields attribute Allow fields to be excluded from generated field enums while still being included in contextual types. Useful for catch-all fields like additional_fields that shouldn't be part of field filtering. * Add integration-tests feature to wp_contextual Fixes cfg warnings for integration-tests feature used in generated code.
1 parent a4790f1 commit 558fa83

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

wp_contextual/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ version = "0.1.0"
44
edition = "2024"
55
autotests = false
66

7+
[features]
8+
integration-tests = []
9+
710
[lib]
811
proc-macro = true
912

wp_contextual/src/lib.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,45 @@
268268
//! ```
269269
//! ---
270270
//!
271+
//! In some cases, you might want to exclude certain fields from the generated field enums (like
272+
//! `SparseFooFieldWithEditContext`). This is particularly useful for fields that are not suitable for
273+
//! field filtering, such as catch-all fields for additional data or serde flattened fields. The
274+
//! `WpContextualExcludeFromFields` attribute can be used for this purpose:
275+
//!
276+
//! ```
277+
//! # use wp_contextual::WpContextual;
278+
//! #[derive(WpContextual)]
279+
//! pub struct SparseComment {
280+
//! #[WpContext(edit, embed, view)]
281+
//! pub id: Option<u32>,
282+
//! #[WpContext(edit, embed, view)]
283+
//! #[WpContextualExcludeFromFields]
284+
//! pub additional_fields: Option<String>,
285+
//! }
286+
//! # // We need these 2 lines for UniFFI
287+
//! # uniffi::setup_scaffolding!();
288+
//! # fn main() {}
289+
//! ```
290+
//!
291+
//! This will generate the contextual types as usual, but the field enums will exclude the `additional_fields` field:
292+
//!
293+
//! ```
294+
//! pub enum SparseCommentFieldWithEditContext {
295+
//! Id,
296+
//! // Note: additional_fields is excluded from field filtering
297+
//! }
298+
//! pub enum SparseCommentFieldWithEmbedContext {
299+
//! Id,
300+
//! // Note: additional_fields is excluded from field filtering
301+
//! }
302+
//! pub enum SparseCommentFieldWithViewContext {
303+
//! Id,
304+
//! // Note: additional_fields is excluded from field filtering
305+
//! }
306+
//! ```
307+
//!
308+
//! ---
309+
//!
271310
//! Most endpoints support filtering the fields by `_fields` argument which means some of the
272311
//! fields might be missing from the response. In these cases, we want to use the `Sparse` type.
273312
//! In order to make it easier to combine `context` & `_fields` arguments, and get the most useful
@@ -342,6 +381,8 @@ mod wp_contextual;
342381
/// type with the appropriate contextual type: `BazWithEditContext`, `BazWithEmbedContext` or
343382
/// `BazWithViewContext`.
344383
/// * `[WpContextualOption]` is used to tell the compiler to keep the field's `Option` type.
384+
/// * `[WpContextualExcludeFromFields]` is used to exclude a field from the generated field enums
385+
/// (`SparseFooFieldWithEditContext`, etc.) while still including it in the contextual types.
345386
/// * Generated types will have the following derive macros:
346387
/// `#[derive(Debug, serde::Serialize, serde::Deserialize, uniffi::Record)]`. These types are meant
347388
/// to be used for the
@@ -453,7 +494,12 @@ mod wp_contextual;
453494
/// `#[WpContextualOption]`.
454495
#[proc_macro_derive(
455496
WpContextual,
456-
attributes(WpContext, WpContextualField, WpContextualOption)
497+
attributes(
498+
WpContext,
499+
WpContextualField,
500+
WpContextualOption,
501+
WpContextualExcludeFromFields
502+
)
457503
)]
458504
pub fn derive(input: TokenStream) -> TokenStream {
459505
wp_contextual::wp_contextual(parse_macro_input!(input))

wp_contextual/src/wp_contextual.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ fn parse_fields(
148148
if is_wp_contextual_option_ident(segment_ident) {
149149
return Ok(WpParsedAttr::ParsedWpContextualOption);
150150
}
151+
if is_wp_contextual_exclude_from_fields_ident(segment_ident) {
152+
return Ok(WpParsedAttr::ParsedWpContextualExcludeFromFields);
153+
}
151154
if is_wp_context_ident(segment_ident) {
152155
if let syn::Meta::List(meta_list) = &attr.meta {
153156
let contexts = parse_contexts_from_tokens(meta_list.tokens.clone())?;
@@ -228,6 +231,9 @@ fn generate_sparse_field_type(
228231
let mut variant_idents = Vec::with_capacity(fields.len());
229232
let mut as_field_names = Vec::with_capacity(fields.len());
230233
for f in fields {
234+
if f.is_wp_contextual_exclude_from_fields {
235+
continue;
236+
}
231237
if let Some(f_ident) = &f.field.ident {
232238
let field_name = f_ident.to_string();
233239
let variant_ident = format_ident!("{}", field_name.to_case(Case::UpperCamel));
@@ -335,6 +341,7 @@ fn generate_integration_test_helper(
335341
struct GeneratedContextualField {
336342
field: syn::Field,
337343
is_wp_contextual_option: bool,
344+
is_wp_contextual_exclude_from_fields: bool,
338345
}
339346

340347
impl GeneratedContextualField {
@@ -365,6 +372,10 @@ impl GeneratedContextualField {
365372
.parsed_attrs
366373
.contains(&WpParsedAttr::ParsedWpContextualOption);
367374

375+
let is_wp_contextual_exclude_from_fields = pf
376+
.parsed_attrs
377+
.contains(&WpParsedAttr::ParsedWpContextualExcludeFromFields);
378+
368379
let new_type = if is_wp_contextual_option {
369380
f.ty.clone()
370381
} else {
@@ -411,6 +422,7 @@ impl GeneratedContextualField {
411422
Ok(Self {
412423
field: new_field,
413424
is_wp_contextual_option,
425+
is_wp_contextual_exclude_from_fields,
414426
})
415427
})
416428
.collect()
@@ -667,6 +679,10 @@ fn is_wp_contextual_option_ident(ident: &Ident) -> bool {
667679
ident.to_string().eq("WpContextualOption")
668680
}
669681

682+
fn is_wp_contextual_exclude_from_fields_ident(ident: &Ident) -> bool {
683+
ident.to_string().eq("WpContextualExcludeFromFields")
684+
}
685+
670686
// ```
671687
// #[WpContextual]
672688
// pub struct SparseFoo {
@@ -716,6 +732,7 @@ struct WpParsedField {
716732
enum WpParsedAttr {
717733
ParsedWpContextualField,
718734
ParsedWpContextualOption,
735+
ParsedWpContextualExcludeFromFields,
719736
ParsedWpContext { contexts: Vec<WpContextAttr> },
720737
ExternalAttr { attr: Box<syn::Attribute> },
721738
}

0 commit comments

Comments
 (0)