summaryrefslogtreecommitdiffstats
path: root/third_party/rust/darling/examples
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/darling/examples
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/darling/examples')
-rw-r--r--third_party/rust/darling/examples/automatic_bounds.rs73
-rw-r--r--third_party/rust/darling/examples/consume_fields.rs175
-rw-r--r--third_party/rust/darling/examples/fallible_read.rs85
-rw-r--r--third_party/rust/darling/examples/shorthand_or_long_field.rs79
-rw-r--r--third_party/rust/darling/examples/supports_struct.rs61
5 files changed, 473 insertions, 0 deletions
diff --git a/third_party/rust/darling/examples/automatic_bounds.rs b/third_party/rust/darling/examples/automatic_bounds.rs
new file mode 100644
index 0000000000..8312afed4c
--- /dev/null
+++ b/third_party/rust/darling/examples/automatic_bounds.rs
@@ -0,0 +1,73 @@
+use darling::{FromDeriveInput, FromMeta};
+
+#[derive(FromMeta, PartialEq, Eq, Debug)]
+enum Volume {
+ Whisper,
+ Talk,
+ Shout,
+}
+
+/// A more complex example showing the ability to skip at a field or struct
+/// level while still tracking which type parameters need to be bounded.
+/// This can be seen by expanding this example using `cargo expand`.
+#[derive(FromMeta)]
+#[allow(dead_code)]
+enum Emphasis<T> {
+ Constant(Volume),
+ Variable(darling::util::PathList),
+ #[darling(skip)]
+ PerPhoneme(Option<T>),
+ Strided {
+ #[darling(skip)]
+ step: Vec<T>,
+ #[darling(multiple)]
+ volume: Vec<Volume>,
+ },
+}
+
+#[derive(FromDeriveInput)]
+#[darling(attributes(speak))]
+struct SpeakingOptions<T, U> {
+ max_volume: U,
+ #[darling(skip, default)]
+ additional_data: Vec<T>,
+}
+
+#[derive(Default)]
+struct Phoneme {
+ #[allow(dead_code)]
+ first: String,
+}
+
+// This is probably the holy grail for `darling`'s own internal use-case:
+// Auto-apply `Default` bound to skipped *field* types in `where` clause.
+impl<T, U> Default for SpeakingOptions<T, U>
+where
+ Vec<T>: Default,
+ U: Default,
+{
+ fn default() -> Self {
+ Self {
+ max_volume: Default::default(),
+ additional_data: Default::default(),
+ }
+ }
+}
+
+fn main() {
+ let derive_input = syn::parse_str(
+ r#"
+ #[derive(Speak)]
+ #[speak(max_volume = "shout")]
+ enum HtmlElement {
+ Div(String)
+ }
+ "#,
+ )
+ .unwrap();
+
+ let parsed: SpeakingOptions<Phoneme, Volume> =
+ FromDeriveInput::from_derive_input(&derive_input).unwrap();
+ assert_eq!(parsed.max_volume, Volume::Shout);
+ assert_eq!(parsed.additional_data.len(), 0);
+}
diff --git a/third_party/rust/darling/examples/consume_fields.rs b/third_party/rust/darling/examples/consume_fields.rs
new file mode 100644
index 0000000000..f5cd435b90
--- /dev/null
+++ b/third_party/rust/darling/examples/consume_fields.rs
@@ -0,0 +1,175 @@
+// The use of fields in debug print commands does not count as "used",
+// which causes the fields to trigger an unwanted dead code warning.
+#![allow(dead_code)]
+
+//! This example shows how to do struct and field parsing using darling.
+
+use darling::{ast, FromDeriveInput, FromField, FromMeta};
+use proc_macro2::TokenStream;
+use quote::{quote, ToTokens};
+use syn::parse_str;
+
+/// A speaking volume. Deriving `FromMeta` will cause this to be usable
+/// as a string value for a meta-item key.
+#[derive(Debug, Clone, Copy, FromMeta)]
+#[darling(default)]
+enum Volume {
+ Normal,
+ Whisper,
+ Shout,
+}
+
+impl Default for Volume {
+ fn default() -> Self {
+ Volume::Normal
+ }
+}
+
+/// Support parsing from a full derive input. Unlike FromMeta, this isn't
+/// composable; each darling-dependent crate should have its own struct to handle
+/// when its trait is derived.
+#[derive(Debug, FromDeriveInput)]
+// This line says that we want to process all attributes declared with `my_trait`,
+// and that darling should panic if this receiver is given an enum.
+#[darling(attributes(my_trait), supports(struct_any))]
+struct MyInputReceiver {
+ /// The struct ident.
+ ident: syn::Ident,
+
+ /// The type's generics. You'll need these any time your trait is expected
+ /// to work with types that declare generics.
+ generics: syn::Generics,
+
+ /// Receives the body of the struct or enum. We don't care about
+ /// struct fields because we previously told darling we only accept structs.
+ data: ast::Data<(), MyFieldReceiver>,
+
+ /// The Input Receiver demands a volume, so use `Volume::Normal` if the
+ /// caller doesn't provide one.
+ #[darling(default)]
+ volume: Volume,
+}
+
+impl ToTokens for MyInputReceiver {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let MyInputReceiver {
+ ref ident,
+ ref generics,
+ ref data,
+ volume,
+ } = *self;
+
+ let (imp, ty, wher) = generics.split_for_impl();
+ let fields = data
+ .as_ref()
+ .take_struct()
+ .expect("Should never be enum")
+ .fields;
+
+ // Generate the format string which shows each field and its name
+ let fmt_string = fields
+ .iter()
+ .enumerate()
+ .map(|(i, f)| {
+ // We have to preformat the ident in this case so we can fall back
+ // to the field index for unnamed fields. It's not easy to read,
+ // unfortunately.
+ format!(
+ "{} = {{}}",
+ f.ident
+ .as_ref()
+ .map(|v| format!("{}", v))
+ .unwrap_or_else(|| format!("{}", i))
+ )
+ })
+ .collect::<Vec<_>>()
+ .join(", ");
+
+ // Generate the actual values to fill the format string.
+ let field_list = fields
+ .into_iter()
+ .enumerate()
+ .map(|(i, f)| {
+ let field_volume = f.volume.unwrap_or(volume);
+
+ // This works with named or indexed fields, so we'll fall back to the index so we can
+ // write the output as a key-value pair.
+ let field_ident = f.ident
+ .as_ref()
+ .map(|v| quote!(#v))
+ .unwrap_or_else(|| {
+ let i = syn::Index::from(i);
+ quote!(#i)
+ });
+
+ match field_volume {
+ Volume::Normal => quote!(self.#field_ident),
+ Volume::Shout => {
+ quote!(::std::string::ToString::to_string(&self.#field_ident).to_uppercase())
+ }
+ Volume::Whisper => {
+ quote!(::std::string::ToString::to_string(&self.#field_ident).to_lowercase())
+ }
+ }
+ })
+ .collect::<Vec<_>>();
+
+ tokens.extend(quote! {
+ impl #imp Speak for #ident #ty #wher {
+ fn speak(&self, writer: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ write!(writer, #fmt_string, #(#field_list),*)
+ }
+ }
+ });
+ }
+}
+
+#[derive(Debug, FromField)]
+#[darling(attributes(my_trait))]
+struct MyFieldReceiver {
+ /// Get the ident of the field. For fields in tuple or newtype structs or
+ /// enum bodies, this can be `None`.
+ ident: Option<syn::Ident>,
+
+ /// This magic field name pulls the type from the input.
+ ty: syn::Type,
+
+ /// We declare this as an `Option` so that during tokenization we can write
+ /// `field.volume.unwrap_or(derive_input.volume)` to facilitate field-level
+ /// overrides of struct-level settings.
+ ///
+ /// Because this field is an `Option`, we don't need to include `#[darling(default)]`
+ volume: Option<Volume>,
+}
+
+fn main() {
+ let input = r#"#[derive(MyTrait)]
+#[my_trait(volume = "shout")]
+pub struct Foo {
+ #[my_trait(volume = "whisper")]
+ bar: bool,
+
+ baz: i64,
+}"#;
+
+ let parsed = parse_str(input).unwrap();
+ let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap();
+ let tokens = quote!(#receiver);
+
+ println!(
+ r#"
+INPUT:
+
+{}
+
+PARSED AS:
+
+{:?}
+
+EMITS:
+
+{}
+ "#,
+ input, receiver, tokens
+ );
+}
diff --git a/third_party/rust/darling/examples/fallible_read.rs b/third_party/rust/darling/examples/fallible_read.rs
new file mode 100644
index 0000000000..32cc10c9a5
--- /dev/null
+++ b/third_party/rust/darling/examples/fallible_read.rs
@@ -0,0 +1,85 @@
+//! This example demonstrates techniques for performing custom error handling
+//! in a derive-input receiver.
+//!
+//! 1. Using `darling::Result` as a carrier to preserve the error for later display
+//! 1. Using `Result<T, syn::Meta>` to attempt a recovery in imperative code
+//! 1. Using the `map` darling meta-item to post-process a field before returning
+//! 1. Using the `and_then` darling meta-item to post-process the receiver before returning
+
+use darling::{FromDeriveInput, FromMeta};
+use syn::parse_str;
+
+#[derive(Debug, FromDeriveInput)]
+#[darling(attributes(my_trait), and_then = "MyInputReceiver::autocorrect")]
+pub struct MyInputReceiver {
+ /// This field must be present and a string or else parsing will panic.
+ #[darling(map = "MyInputReceiver::make_string_shouty")]
+ name: String,
+
+ /// If this field fails to parse, the struct can still be built; the field
+ /// will contain the error. The consuming struct can then decide if this
+ /// blocks code generation. If so, panic or fail in `and_then`.
+ frequency: darling::Result<i64>,
+
+ /// If this field fails to parse, the struct can still be built; the field
+ /// will contain an `Err` with the original `syn::Meta`. This can be used
+ /// for alternate parsing attempts before panicking.
+ amplitude: Result<u64, syn::Meta>,
+}
+
+impl MyInputReceiver {
+ /// This function will be called by `darling` _after_ it's finished parsing the
+ /// `name` field but before initializing `name` with the resulting value. It's
+ /// a good place for transforms that are easiest to express on already-built
+ /// types.
+ fn make_string_shouty(s: String) -> String {
+ s.to_uppercase()
+ }
+
+ /// This function will be called by `darling` _after_ it's finished parsing the
+ /// input but before returning to the caller. This is a good place to initialize
+ /// skipped fields or to perform corrections that don't lend themselves to being
+ /// done elsewhere.
+ fn autocorrect(self) -> darling::Result<Self> {
+ let Self {
+ name,
+ frequency,
+ amplitude,
+ } = self;
+
+ // Amplitude doesn't have a sign, so if we received a negative number then
+ // we'll go ahead and make it positive.
+ let amplitude = match amplitude {
+ Ok(amp) => amp,
+ Err(mi) => (i64::from_meta(&mi)?).unsigned_abs(),
+ };
+
+ Ok(Self {
+ name,
+ frequency,
+ amplitude: Ok(amplitude),
+ })
+ }
+}
+
+fn main() {
+ let input = r#"#[derive(MyTrait)]
+#[my_trait(name="Jon", amplitude = "-1", frequency = 1)]
+pub struct Foo;"#;
+
+ let parsed = parse_str(input).unwrap();
+ let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap();
+
+ println!(
+ r#"
+INPUT:
+
+{}
+
+PARSED AS:
+
+{:?}
+ "#,
+ input, receiver
+ );
+}
diff --git a/third_party/rust/darling/examples/shorthand_or_long_field.rs b/third_party/rust/darling/examples/shorthand_or_long_field.rs
new file mode 100644
index 0000000000..750d83efbc
--- /dev/null
+++ b/third_party/rust/darling/examples/shorthand_or_long_field.rs
@@ -0,0 +1,79 @@
+//! Example showing potentially-nested meta item parsing with `darling::util::Override`.
+//!
+//! Based on https://stackoverflow.com/q/68046070/86381 by https://github.com/peterjoel
+
+// The use of fields in debug print commands does not count as "used",
+// which causes the fields to trigger an unwanted dead code warning.
+#![allow(dead_code)]
+
+use std::borrow::Cow;
+
+use darling::{util::Override, FromDeriveInput, FromMeta};
+use syn::{Ident, Path};
+
+#[derive(Debug, FromDeriveInput)]
+#[darling(attributes(myderive))]
+struct MyDeriveInput {
+ ident: Ident,
+ /// We can infer the right "table" behavior for this derive, but we want the caller to be
+ /// explicit that they're expecting the inference behavior to avoid cluttering some hypothetical
+ /// database. Therefore this field is required, but can take word form or key-value form.
+ ///
+ /// To make this field optional, we could add `#[darling(default)]`, or we could
+ /// wrap it in `Option` if the presence or absence of the word makes a difference.
+ table: Override<Table>,
+}
+
+impl MyDeriveInput {
+ fn table(&self) -> Cow<'_, Table> {
+ match &self.table {
+ Override::Explicit(value) => Cow::Borrowed(value),
+ Override::Inherit => Cow::Owned(Table {
+ name: self.ident.to_string(),
+ value: None,
+ }),
+ }
+ }
+}
+
+#[derive(Debug, Clone, FromMeta)]
+struct Table {
+ name: String,
+ value: Option<Path>,
+}
+
+fn from_str(s: &str) -> darling::Result<MyDeriveInput> {
+ FromDeriveInput::from_derive_input(&syn::parse_str(s)?)
+}
+
+fn main() {
+ let missing = from_str(
+ r#"
+ #[derive(MyTrait)]
+ struct Foo(u64);
+ "#,
+ )
+ .unwrap_err();
+
+ let short_form = from_str(
+ r#"
+ #[derive(MyTrait)]
+ #[myderive(table)]
+ struct Foo(u64);
+ "#,
+ )
+ .unwrap();
+
+ let long_form = from_str(
+ r#"
+ #[derive(MyTrait)]
+ #[myderive(table(name = "Custom"))]
+ struct Foo(u64);
+ "#,
+ )
+ .unwrap();
+
+ println!("Error when missing: {}", missing);
+ println!("Short form: {:?}", short_form.table());
+ println!("Long form: {:?}", long_form.table());
+}
diff --git a/third_party/rust/darling/examples/supports_struct.rs b/third_party/rust/darling/examples/supports_struct.rs
new file mode 100644
index 0000000000..97a5bb9c05
--- /dev/null
+++ b/third_party/rust/darling/examples/supports_struct.rs
@@ -0,0 +1,61 @@
+// The use of fields in debug print commands does not count as "used",
+// which causes the fields to trigger an unwanted dead code warning.
+#![allow(dead_code)]
+
+use darling::{ast, util, FromDeriveInput, FromField};
+use syn::{Ident, Type};
+
+#[derive(Debug, FromField)]
+#[darling(attributes(lorem))]
+pub struct LoremField {
+ ident: Option<Ident>,
+ ty: Type,
+ #[darling(default)]
+ skip: bool,
+}
+
+#[derive(Debug, FromDeriveInput)]
+#[darling(attributes(lorem), supports(struct_named))]
+pub struct Lorem {
+ ident: Ident,
+ data: ast::Data<util::Ignored, LoremField>,
+}
+
+fn main() {
+ let good_input = r#"#[derive(Lorem)]
+pub struct Foo {
+ #[lorem(skip)]
+ bar: bool,
+
+ baz: i64,
+}"#;
+
+ let bad_input = r#"#[derive(Lorem)]
+ pub struct BadFoo(String, u32);"#;
+
+ let parsed = syn::parse_str(good_input).unwrap();
+ let receiver = Lorem::from_derive_input(&parsed).unwrap();
+ let wrong_shape_parsed = syn::parse_str(bad_input).unwrap();
+ let wrong_shape = Lorem::from_derive_input(&wrong_shape_parsed).expect_err("Shape was wrong");
+
+ println!(
+ r#"
+INPUT:
+
+{}
+
+PARSED AS:
+
+{:?}
+
+BAD INPUT:
+
+{}
+
+PRODUCED ERROR:
+
+{}
+ "#,
+ good_input, receiver, bad_input, wrong_shape
+ );
+}