Skip to main content

zvariant_derive/
lib.rs

1#![deny(rust_2018_idioms)]
2#![doc(
3    html_logo_url = "https://raw.githubusercontent.com/z-galaxy/zbus/9f7a90d2b594ddc48b7a5f39fda5e00cd56a7dfb/logo.png"
4)]
5#![doc = include_str!("../README.md")]
6#![doc(test(attr(
7    warn(unused),
8    deny(warnings),
9    allow(dead_code),
10    // W/o this, we seem to get some bogus warning about `extern crate zbus`.
11    allow(unused_extern_crates),
12)))]
13
14use proc_macro::TokenStream;
15use syn::DeriveInput;
16
17mod dict;
18mod signature;
19mod r#type;
20mod utils;
21mod value;
22
23/// Derive macro to add [`Type`] implementation to structs and enums.
24///
25/// # Examples
26///
27/// For structs it works just like serde's [`Serialize`] and [`Deserialize`] macros:
28///
29/// ```
30/// use zvariant::{serialized::Context, to_bytes, Type, LE};
31/// use serde::{Deserialize, Serialize};
32///
33/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
34/// struct Struct<'s> {
35///     field1: u16,
36///     field2: i64,
37///     field3: &'s str,
38/// }
39///
40/// assert_eq!(Struct::SIGNATURE, "(qxs)");
41/// let s = Struct {
42///     field1: 42,
43///     field2: i64::max_value(),
44///     field3: "hello",
45/// };
46/// let ctxt = Context::new_dbus(LE, 0);
47/// let encoded = to_bytes(ctxt, &s).unwrap();
48/// let decoded: Struct = encoded.deserialize().unwrap().0;
49/// assert_eq!(decoded, s);
50/// ```
51///
52/// Same with enum, except that all variants of the enum must have the same number and types of
53/// fields (if any). If you want the encoding size of the (unit-type) enum to be dictated by
54/// `repr` attribute (like in the example below), you'll also need [serde_repr] crate.
55///
56/// ```
57/// use zvariant::{serialized::Context, to_bytes, Type, LE};
58/// use serde::{Deserialize, Serialize};
59/// use serde_repr::{Deserialize_repr, Serialize_repr};
60///
61/// #[repr(u8)]
62/// #[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
63/// enum Enum {
64///     Variant1,
65///     Variant2,
66/// }
67/// assert_eq!(Enum::SIGNATURE, u8::SIGNATURE);
68/// let ctxt = Context::new_dbus(LE, 0);
69/// let encoded = to_bytes(ctxt, &Enum::Variant2).unwrap();
70/// let decoded: Enum = encoded.deserialize().unwrap().0;
71/// assert_eq!(decoded, Enum::Variant2);
72///
73/// #[repr(i64)]
74/// #[derive(Deserialize_repr, Serialize_repr, Type)]
75/// enum Enum2 {
76///     Variant1,
77///     Variant2,
78/// }
79/// assert_eq!(Enum2::SIGNATURE, i64::SIGNATURE);
80///
81/// // w/o repr attribute, u32 representation is chosen
82/// #[derive(Deserialize, Serialize, Type)]
83/// enum NoReprEnum {
84///     Variant1,
85///     Variant2,
86/// }
87/// assert_eq!(NoReprEnum::SIGNATURE, u32::SIGNATURE);
88///
89/// // Not-unit enums are represented as a structure, with the first field being a u32 denoting the
90/// // variant and the second as the actual value.
91/// #[derive(Deserialize, Serialize, Type)]
92/// enum NewType {
93///     Variant1(f64),
94///     Variant2(f64),
95/// }
96/// assert_eq!(NewType::SIGNATURE, "(ud)");
97///
98/// #[derive(Deserialize, Serialize, Type)]
99/// enum StructFields {
100///     Variant1(u16, i64, &'static str),
101///     Variant2 { field1: u16, field2: i64, field3: &'static str },
102/// }
103/// assert_eq!(StructFields::SIGNATURE, "(u(qxs))");
104/// ```
105///
106/// # Custom signatures
107///
108/// There are times when you'd find yourself wanting to specify a hardcoded signature yourself for
109/// the type. The `signature` attribute exists for this purpose. A typical use case is when you'd
110/// need to encode your type as a dictionary (signature `a{sv}`) type. For convenience, `dict` is
111/// an alias for `a{sv}`. Here is an example:
112///
113/// ```
114/// use zvariant::{
115///     serialized::Context, as_value, to_bytes, Type, LE,
116/// };
117/// use serde::{Deserialize, Serialize};
118///
119/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
120/// // `#[zvariant(signature = "a{sv}")]` would be the same.
121/// #[zvariant(signature = "dict")]
122/// struct Struct {
123///     #[serde(with = "as_value")]
124///     field1: u16,
125///     #[serde(with = "as_value")]
126///     field2: i64,
127///     #[serde(with = "as_value")]
128///     field3: String,
129/// }
130///
131/// assert_eq!(Struct::SIGNATURE, "a{sv}");
132/// let s = Struct {
133///     field1: 42,
134///     field2: i64::max_value(),
135///     field3: "hello".to_string(),
136/// };
137/// let ctxt = Context::new_dbus(LE, 0);
138/// let encoded = to_bytes(ctxt, &s).unwrap();
139/// let decoded: Struct = encoded.deserialize().unwrap().0;
140/// assert_eq!(decoded, s);
141/// ```
142///
143/// Another common use for custom signatures is (de)serialization of unit enums as strings:
144///
145/// ```
146/// use zvariant::{serialized::Context, to_bytes, Type, LE};
147/// use serde::{Deserialize, Serialize};
148///
149/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
150/// #[zvariant(signature = "s")]
151/// enum StrEnum {
152///     Variant1,
153///     Variant2,
154///     Variant3,
155/// }
156///
157/// assert_eq!(StrEnum::SIGNATURE, "s");
158/// let ctxt = Context::new_dbus(LE, 0);
159/// let encoded = to_bytes(ctxt, &StrEnum::Variant2).unwrap();
160/// assert_eq!(encoded.len(), 13);
161/// let decoded: StrEnum = encoded.deserialize().unwrap().0;
162/// assert_eq!(decoded, StrEnum::Variant2);
163/// ```
164///
165/// # Custom crate path
166///
167/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
168/// you can specify the crate path using the `crate` attribute:
169///
170/// ```
171/// use zvariant::Type;
172///
173/// #[derive(Type)]
174/// #[zvariant(crate = "zvariant")]
175/// struct MyStruct {
176///     field: String,
177/// }
178/// ```
179///
180/// [`Type`]: https://docs.rs/zvariant/latest/zvariant/trait.Type.html
181/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
182/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
183/// [serde_repr]: https://crates.io/crates/serde_repr
184#[proc_macro_derive(Type, attributes(zbus, zvariant))]
185pub fn type_macro_derive(input: TokenStream) -> TokenStream {
186    let ast: DeriveInput = syn::parse(input).unwrap();
187    r#type::expand_derive(ast)
188        .unwrap_or_else(|err| err.to_compile_error())
189        .into()
190}
191
192/// Adds [`Serialize`] implementation to structs to be serialized as a D-Bus dictionary type.
193///
194/// The dictionary type is determined by the `signature` attribute. The default is `a{sv}`
195/// (string keys, variant values), but nested forms like `a{sa{sv}}` and `a{oa{sv}}` are also
196/// supported — fields whose value type is itself a dict (or any non-`Variant` type) are
197/// serialized directly through their own `Serialize` impl rather than wrapped as a variant.
198///
199/// Such dictionary types are very commonly used with
200/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
201/// and GVariant.
202///
203/// # Alternative Approaches
204///
205/// There are two approaches to serializing structs as dictionaries:
206///
207/// 1. Using this macro (simpler, but less control).
208/// 2. Using the `Serialize` derive with `zvariant::as_value` (more verbose, but more control).
209///
210/// See the example below and the relevant [FAQ entry] in our book for more details on the
211/// alternative approach.
212///
213/// # Example
214///
215/// ## Approach #1
216///
217/// ```
218/// use zvariant::{SerializeDict, Type};
219///
220/// #[derive(Debug, Default, SerializeDict, Type)]
221/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
222/// pub struct MyStruct {
223///     field1: Option<u32>,
224///     field2: String,
225/// }
226/// ```
227///
228/// ## Approach #2
229///
230/// ```
231/// use serde::Serialize;
232/// use zvariant::{Type, as_value};
233///
234/// #[derive(Debug, Default, Serialize, Type)]
235/// #[zvariant(signature = "a{sv}")]
236/// #[serde(default, rename_all = "PascalCase")]
237/// pub struct MyStruct {
238///     #[serde(with = "as_value::optional", skip_serializing_if = "Option::is_none")]
239///     field1: Option<u32>,
240///     #[serde(with = "as_value")]
241///     field2: String,
242/// }
243/// ```
244///
245/// ## Nested dictionaries
246///
247/// To represent shapes like `a{sa{sv}}` (the body type of
248/// `org.freedesktop.DBus.ObjectManager.GetManagedObjects` and similar APIs), nest one
249/// `SerializeDict`/`DeserializeDict` struct inside another:
250///
251/// ```
252/// use zvariant::{DeserializeDict, SerializeDict, Type};
253///
254/// #[derive(SerializeDict, DeserializeDict, Type, Default)]
255/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
256/// pub struct AdapterProperties {
257///     address: Option<String>,
258///     name: Option<String>,
259/// }
260///
261/// #[derive(SerializeDict, DeserializeDict, Type, Default)]
262/// #[zvariant(signature = "a{sa{sv}}")]
263/// pub struct InterfaceProperties {
264///     #[zvariant(rename = "org.bluez.Adapter1")]
265///     adapter: Option<AdapterProperties>,
266/// }
267/// ```
268///
269/// # Custom crate path
270///
271/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
272/// you can specify the crate path using the `crate` attribute:
273///
274/// ```
275/// use zvariant::{SerializeDict, Type};
276///
277/// #[derive(SerializeDict, Type)]
278/// #[zvariant(signature = "a{sv}", crate = "zvariant")]
279/// struct MyStruct {
280///     field: String,
281/// }
282/// ```
283///
284/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
285/// [FAQ entry]: https://z-galaxy.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary
286#[proc_macro_derive(SerializeDict, attributes(zbus, zvariant))]
287pub fn serialize_dict_macro_derive(input: TokenStream) -> TokenStream {
288    let input: DeriveInput = syn::parse(input).unwrap();
289    dict::expand_serialize_derive(input)
290        .unwrap_or_else(|err| err.to_compile_error())
291        .into()
292}
293
294/// Adds [`Deserialize`] implementation to structs to be deserialized from a D-Bus dictionary type.
295///
296/// The dictionary type is determined by the `signature` attribute. The default is `a{sv}`
297/// (string keys, variant values), but nested forms like `a{sa{sv}}` and `a{oa{sv}}` are also
298/// supported — fields whose value type is itself a dict (or any non-`Variant` type) are
299/// deserialized directly through their own `Deserialize` impl rather than unwrapped from a
300/// variant. See [`SerializeDict`] for a nested example.
301///
302/// Such dictionary types are very commonly used with
303/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
304/// and GVariant.
305///
306/// # Alternative Approaches
307///
308/// There are two approaches to deserializing dictionaries as structs:
309///
310/// 1. Using this macro (simpler, but less control).
311/// 2. Using the `Deserialize` derive with `zvariant::as_value` (more verbose, but more control).
312///
313/// See the example below and the relevant [FAQ entry] in our book for more details on the
314/// alternative approach.
315///
316/// # Example
317///
318/// ## Approach #1
319///
320/// ```
321/// use zvariant::{DeserializeDict, Type};
322///
323/// #[derive(Debug, Default, DeserializeDict, Type)]
324/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
325/// pub struct MyStruct {
326///     field1: Option<u32>,
327///     field2: String,
328/// }
329/// ```
330///
331/// ## Approach #2
332///
333/// ```
334/// use serde::Deserialize;
335/// use zvariant::{Type, as_value};
336///
337/// #[derive(Debug, Default, Deserialize, Type)]
338/// #[zvariant(signature = "a{sv}")]
339/// #[serde(default, rename_all = "PascalCase")]
340/// pub struct MyStruct {
341///     #[serde(with = "as_value::optional")]
342///     field1: Option<u32>,
343///     #[serde(with = "as_value")]
344///     field2: String,
345/// }
346/// ```
347///
348/// # Custom crate path
349///
350/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
351/// you can specify the crate path using the `crate` attribute:
352///
353/// ```
354/// use zvariant::{DeserializeDict, Type};
355///
356/// #[derive(DeserializeDict, Type)]
357/// #[zvariant(signature = "a{sv}", crate = "zvariant")]
358/// struct MyStruct {
359///     field: String,
360/// }
361/// ```
362///
363/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
364/// [FAQ entry]: https://z-galaxy.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary
365#[proc_macro_derive(DeserializeDict, attributes(zbus, zvariant))]
366pub fn deserialize_dict_macro_derive(input: TokenStream) -> TokenStream {
367    let input: DeriveInput = syn::parse(input).unwrap();
368    dict::expand_deserialize_derive(input)
369        .unwrap_or_else(|err| err.to_compile_error())
370        .into()
371}
372
373/// Implements conversions for your type to/from [`Value`].
374///
375/// Implements `TryFrom<Value>` and `Into<Value>` for your type.
376///
377/// # Examples
378///
379/// Simple owned strutures:
380///
381/// ```
382/// use zvariant::{OwnedObjectPath, OwnedValue, Value};
383///
384/// #[derive(Clone, Value, OwnedValue)]
385/// struct OwnedStruct {
386///     owned_str: String,
387///     owned_path: OwnedObjectPath,
388/// }
389///
390/// let s = OwnedStruct {
391///     owned_str: String::from("hi"),
392///     owned_path: OwnedObjectPath::try_from("/blah").unwrap(),
393/// };
394/// let value = Value::from(s.clone());
395/// let _ = OwnedStruct::try_from(value).unwrap();
396/// let value = OwnedValue::try_from(s).unwrap();
397/// let s = OwnedStruct::try_from(value).unwrap();
398/// assert_eq!(s.owned_str, "hi");
399/// assert_eq!(s.owned_path.as_str(), "/blah");
400/// ```
401///
402/// Now for the more exciting case of unowned structures:
403///
404/// ```
405/// use zvariant::{ObjectPath, Str};
406/// # use zvariant::{OwnedValue, Value};
407/// #
408/// #[derive(Clone, Value, OwnedValue)]
409/// struct UnownedStruct<'a> {
410///     s: Str<'a>,
411///     path: ObjectPath<'a>,
412/// }
413///
414/// let hi = String::from("hi");
415/// let s = UnownedStruct {
416///     s: Str::from(&hi),
417///     path: ObjectPath::try_from("/blah").unwrap(),
418/// };
419/// let value = Value::from(s.clone());
420/// let s = UnownedStruct::try_from(value).unwrap();
421///
422/// let value = OwnedValue::try_from(s).unwrap();
423/// let s = UnownedStruct::try_from(value).unwrap();
424/// assert_eq!(s.s, "hi");
425/// assert_eq!(s.path, "/blah");
426/// ```
427///
428/// Generic structures also supported:
429///
430/// ```
431/// # use zvariant::{OwnedObjectPath, OwnedValue, Value};
432/// #
433/// #[derive(Clone, Value, OwnedValue)]
434/// struct GenericStruct<S, O> {
435///     field1: S,
436///     field2: O,
437/// }
438///
439/// let s = GenericStruct {
440///     field1: String::from("hi"),
441///     field2: OwnedObjectPath::try_from("/blah").unwrap(),
442/// };
443/// let value = Value::from(s.clone());
444/// let _ = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
445/// let value = OwnedValue::try_from(s).unwrap();
446/// let s = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
447/// assert_eq!(s.field1, "hi");
448/// assert_eq!(s.field2.as_str(), "/blah");
449/// ```
450///
451/// Enums also supported but currently only with unit variants:
452///
453/// ```
454/// # use zvariant::{OwnedValue, Value};
455/// #
456/// #[derive(Debug, PartialEq, Value, OwnedValue)]
457/// // Default representation is `u32`.
458/// #[repr(u8)]
459/// enum Enum {
460///     Variant1 = 0,
461///     Variant2,
462/// }
463///
464/// let value = Value::from(Enum::Variant1);
465/// let e = Enum::try_from(value).unwrap();
466/// assert_eq!(e, Enum::Variant1);
467/// assert_eq!(e as u8, 0);
468/// let value = OwnedValue::try_from(Enum::Variant2).unwrap();
469/// let e = Enum::try_from(value).unwrap();
470/// assert_eq!(e, Enum::Variant2);
471/// ```
472///
473/// String-encoded enums are also supported:
474///
475/// ```
476/// # use zvariant::{OwnedValue, Value};
477/// #
478/// #[derive(Debug, PartialEq, Value, OwnedValue)]
479/// #[zvariant(signature = "s")]
480/// enum StrEnum {
481///     Variant1,
482///     Variant2,
483/// }
484///
485/// let value = Value::from(StrEnum::Variant1);
486/// let e = StrEnum::try_from(value).unwrap();
487/// assert_eq!(e, StrEnum::Variant1);
488/// let value = OwnedValue::try_from(StrEnum::Variant2).unwrap();
489/// let e = StrEnum::try_from(value).unwrap();
490/// assert_eq!(e, StrEnum::Variant2);
491/// ```
492///
493/// # Renaming fields
494///
495/// ## Auto Renaming
496///
497/// The macro supports specifying a Serde-like `#[zvariant(rename_all = "case")]` attribute on
498/// structures. The attribute allows to rename all the fields from snake case to another case
499/// automatically.
500///
501/// Currently the macro supports the following values for `case`:
502///
503/// * `"lowercase"`
504/// * `"UPPERCASE"`
505/// * `"PascalCase"`
506/// * `"camelCase"`
507/// * `"snake_case"`
508/// * `"kebab-case"`
509///
510/// ## Individual Fields
511///
512/// It's still possible to specify custom names for individual fields using the
513/// `#[zvariant(rename = "another-name")]` attribute even when the `rename_all` attribute is
514/// present.
515///
516/// Here is an example using both `rename` and `rename_all`:
517///
518/// ```
519/// # use zvariant::{OwnedValue, Value, Dict};
520/// # use std::collections::HashMap;
521/// #
522/// #[derive(Clone, Value, OwnedValue)]
523/// #[zvariant(signature = "dict", rename_all = "PascalCase")]
524/// struct RenamedStruct {
525///     #[zvariant(rename = "MyValue")]
526///     field1: String,
527///     field2: String,
528/// }
529///
530/// let s = RenamedStruct {
531///     field1: String::from("hello"),
532///     field2: String::from("world")
533/// };
534/// let v = Value::from(s);
535/// let d = Dict::try_from(v).unwrap();
536/// let hm: HashMap<String, String> = HashMap::try_from(d).unwrap();
537/// assert_eq!(hm.get("MyValue").unwrap().as_str(), "hello");
538/// assert_eq!(hm.get("Field2").unwrap().as_str(), "world");
539/// ```
540///
541/// # Dictionary encoding
542///
543/// For treating your type as a dictionary, you can use the `signature = "dict"` attribute. See
544/// [`Type`] for more details and an example use. Please note that this macro can only handle
545/// `dict` or `a{sv}` values. All other values will be ignored.
546///
547/// # Custom crate path
548///
549/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
550/// you can specify the crate path using the `crate` attribute:
551///
552/// ```
553/// use zvariant::Value;
554///
555/// #[derive(Clone, Value)]
556/// #[zvariant(crate = "zvariant")]
557/// struct MyStruct {
558///     field: String,
559/// }
560/// ```
561///
562/// [`Value`]: https://docs.rs/zvariant/latest/zvariant/enum.Value.html
563/// [`Type`]: crate::Type#custom-signatures
564#[proc_macro_derive(Value, attributes(zbus, zvariant))]
565pub fn value_macro_derive(input: TokenStream) -> TokenStream {
566    let ast: DeriveInput = syn::parse(input).unwrap();
567    value::expand_derive(ast, value::ValueType::Value)
568        .unwrap_or_else(|err| err.to_compile_error())
569        .into()
570}
571
572/// Implements conversions for your type to/from [`OwnedValue`].
573///
574/// Implements `TryFrom<OwnedValue>` and `TryInto<OwnedValue>` for your type.
575///
576/// See [`Value`] documentation for examples.
577///
578/// [`OwnedValue`]: https://docs.rs/zvariant/latest/zvariant/struct.OwnedValue.html
579#[proc_macro_derive(OwnedValue, attributes(zbus, zvariant))]
580pub fn owned_value_macro_derive(input: TokenStream) -> TokenStream {
581    let ast: DeriveInput = syn::parse(input).unwrap();
582    value::expand_derive(ast, value::ValueType::OwnedValue)
583        .unwrap_or_else(|err| err.to_compile_error())
584        .into()
585}
586
587/// Constructs a const [`Signature`] with compile-time validation.
588///
589/// This macro creates a `Signature` from a string literal at compile time, validating
590/// that the signature string is valid D-Bus signature. Invalid signatures will cause
591/// a compilation error.
592///
593/// # Examples
594///
595/// ## Basic usage
596///
597/// ```
598/// use zvariant::signature;
599///
600/// // Create signatures for basic types
601/// let sig = signature!("s");  // String signature
602/// assert_eq!(sig.to_string(), "s");
603///
604/// let sig = signature!("i");  // 32-bit integer signature
605/// assert_eq!(sig.to_string(), "i");
606/// ```
607///
608/// ## Container types
609///
610/// ```
611/// use zvariant::signature;
612///
613/// // Array of strings
614/// let sig = signature!("as");
615/// assert_eq!(sig.to_string(), "as");
616///
617/// // Dictionary mapping strings to variants
618/// let sig = signature!("a{sv}");
619/// assert_eq!(sig.to_string(), "a{sv}");
620///
621/// // Structures
622/// let sig = signature!("(isx)");
623/// assert_eq!(sig.to_string(), "(isx)");
624/// ```
625///
626/// ## Const signatures
627///
628/// The macro can be used to create const signatures, which is especially useful
629/// for defining signatures at compile time:
630///
631/// ```
632/// use zvariant::{signature, Signature};
633///
634/// const MY_SIGNATURE: Signature = signature!("a{sv}");
635///
636/// fn process_data(_data: &str) {
637///     assert_eq!(MY_SIGNATURE.to_string(), "a{sv}");
638/// }
639/// ```
640///
641/// ## Using the `dict` alias
642///
643/// For convenience, `dict` is an alias for `a{sv}` (string-to-variant dictionary):
644///
645/// ```
646/// use zvariant::signature;
647///
648/// let sig = signature!("dict");
649/// assert_eq!(sig.to_string(), "a{sv}");
650/// ```
651///
652/// ## Compile-time validation
653///
654/// Invalid signatures will be caught at compile time:
655///
656/// ```compile_fail
657/// use zvariant::signature;
658///
659/// // This will fail to compile because 'z' is not a valid D-Bus type
660/// let sig = signature!("z");
661/// ```
662///
663/// [`Signature`]: https://docs.rs/zvariant/latest/zvariant/enum.Signature.html
664#[proc_macro]
665pub fn signature(input: TokenStream) -> TokenStream {
666    signature::expand_signature_macro(input.into())
667        .unwrap_or_else(|err| err.to_compile_error())
668        .into()
669}