vld/macros.rs
1/// Helper macro to resolve a field's JSON key.
2///
3/// If a rename literal is provided, use it; otherwise fall back to the field name.
4#[doc(hidden)]
5#[macro_export]
6macro_rules! __vld_resolve_key {
7 ($default:expr) => {
8 $default
9 };
10 ($default:expr, $override:expr) => {
11 $override
12 };
13}
14
15/// Define a validated struct with field-level schemas.
16///
17/// This macro generates:
18/// - A regular Rust struct with the specified fields and types
19/// - A `parse()` method that validates input and constructs the struct
20/// - A `parse_value()` method for direct `serde_json::Value` input
21/// - An implementation of [`VldParse`](crate::schema::VldParse) for use with framework extractors
22///
23/// # Syntax
24///
25/// ```ignore
26/// vld::schema! {
27/// #[derive(Debug, Clone)]
28/// pub struct MyStruct {
29/// pub field_name: FieldType => schema_expression,
30/// pub renamed_field: FieldType as "jsonKey" => schema_expression,
31/// // ...
32/// }
33/// }
34/// ```
35///
36/// Each field has the format: `name: Type [as "json_key"] => schema`.
37/// The optional `as "json_key"` overrides the JSON property name used for parsing.
38///
39/// # Example
40///
41/// ```
42/// use vld::prelude::*;
43///
44/// vld::schema! {
45/// #[derive(Debug)]
46/// pub struct User {
47/// pub name: String => vld::string().min(2).max(50),
48/// pub age: Option<i64> => vld::number().int().min(0).optional(),
49/// pub tags: Vec<String> => vld::array(vld::string()).max_len(5)
50/// .with_default(vec![]),
51/// }
52/// }
53///
54/// let user = User::parse(r#"{"name": "Alice", "age": 30}"#).unwrap();
55/// assert_eq!(user.name, "Alice");
56/// assert_eq!(user.age, Some(30));
57/// assert!(user.tags.is_empty());
58/// ```
59///
60/// # Renamed Fields
61///
62/// ```ignore
63/// vld::schema! {
64/// pub struct ApiResponse {
65/// pub first_name: String as "firstName" => vld::string().min(1),
66/// pub last_name: String as "lastName" => vld::string().min(1),
67/// }
68/// }
69///
70/// // Parses from camelCase JSON:
71/// let r = ApiResponse::parse(r#"{"firstName": "John", "lastName": "Doe"}"#).unwrap();
72/// assert_eq!(r.first_name, "John");
73/// ```
74///
75/// # Nested Structs
76///
77/// Use [`nested()`](crate::nested) to compose schemas:
78///
79/// ```ignore
80/// vld::schema! {
81/// pub struct Address {
82/// pub city: String => vld::string().min(1),
83/// }
84/// }
85///
86/// vld::schema! {
87/// pub struct User {
88/// pub name: String => vld::string(),
89/// pub address: Address => vld::nested(Address::parse_value),
90/// }
91/// }
92/// ```
93#[macro_export]
94macro_rules! schema {
95 (
96 $(#[$meta:meta])*
97 $vis:vis struct $name:ident {
98 $(
99 $(#[$field_meta:meta])*
100 $field_vis:vis $field_name:ident : $field_type:ty $(as $rename:literal)? => $schema:expr
101 ),* $(,)?
102 }
103 ) => {
104 $(#[$meta])*
105 $vis struct $name {
106 $(
107 $(#[$field_meta])*
108 $field_vis $field_name: $field_type,
109 )*
110 }
111
112 impl $name {
113 /// Parse and validate input data into this struct.
114 ///
115 /// Accepts any type implementing [`VldInput`]: JSON strings, file paths,
116 /// `serde_json::Value`, byte slices, etc.
117 pub fn parse<__VldInputT: $crate::input::VldInput + ?Sized>(
118 input: &__VldInputT,
119 ) -> ::std::result::Result<$name, $crate::error::VldError> {
120 let __vld_json = <__VldInputT as $crate::input::VldInput>::to_json_value(input)?;
121 Self::parse_value(&__vld_json)
122 }
123
124 /// Parse and validate directly from a `serde_json::Value`.
125 pub fn parse_value(
126 __vld_json: &$crate::serde_json::Value,
127 ) -> ::std::result::Result<$name, $crate::error::VldError> {
128 use $crate::schema::VldSchema as _;
129
130 let __vld_obj = __vld_json.as_object().ok_or_else(|| {
131 $crate::error::VldError::single(
132 $crate::error::IssueCode::InvalidType {
133 expected: ::std::string::String::from("object"),
134 received: $crate::error::value_type_name(__vld_json),
135 },
136 ::std::format!(
137 "Expected object, received {}",
138 $crate::error::value_type_name(__vld_json)
139 ),
140 )
141 })?;
142
143 let mut __vld_errors = $crate::error::VldError::new();
144
145 $(
146 #[allow(non_snake_case)]
147 let $field_name: ::std::option::Option<$field_type> = {
148 let __vld_field_schema = $schema;
149 let __vld_key = $crate::__vld_resolve_key!(
150 stringify!($field_name) $(, $rename)?
151 );
152 let __vld_field_value = __vld_obj
153 .get(__vld_key)
154 .unwrap_or(&$crate::serde_json::Value::Null);
155 match __vld_field_schema.parse_value(__vld_field_value) {
156 ::std::result::Result::Ok(v) => ::std::option::Option::Some(v),
157 ::std::result::Result::Err(e) => {
158 __vld_errors = $crate::error::VldError::merge(
159 __vld_errors,
160 $crate::error::VldError::with_prefix(
161 e,
162 $crate::error::PathSegment::Field(
163 ::std::string::String::from(__vld_key),
164 ),
165 ),
166 );
167 ::std::option::Option::None
168 }
169 }
170 };
171 )*
172
173 if !$crate::error::VldError::is_empty(&__vld_errors) {
174 return ::std::result::Result::Err(__vld_errors);
175 }
176
177 ::std::result::Result::Ok($name {
178 $(
179 $field_name: $field_name.unwrap(),
180 )*
181 })
182 }
183
184 }
185
186 impl $crate::schema::VldParse for $name {
187 fn vld_parse_value(
188 value: &$crate::serde_json::Value,
189 ) -> ::std::result::Result<Self, $crate::error::VldError> {
190 Self::parse_value(value)
191 }
192 }
193
194
195 $crate::__vld_if_serialize! {
196 impl $name {
197 /// Validate an existing Rust value that can be serialized to JSON.
198 ///
199 /// The value is serialized via `serde`, then validated against the
200 /// schema. Returns `Ok(())` on success, `Err(VldError)` with all
201 /// issues on failure.
202 ///
203 /// Requires the `serialize` feature.
204 pub fn validate<__VldT: $crate::serde::Serialize>(
205 instance: &__VldT,
206 ) -> ::std::result::Result<(), $crate::error::VldError> {
207 let __vld_json = $crate::serde_json::to_value(instance).map_err(|e| {
208 $crate::error::VldError::single(
209 $crate::error::IssueCode::ParseError,
210 ::std::format!("Serialization error: {}", e),
211 )
212 })?;
213 let _ = Self::parse_value(&__vld_json)?;
214 ::std::result::Result::Ok(())
215 }
216
217 /// Check if a value is valid against the schema.
218 ///
219 /// Shorthand for `validate(instance).is_ok()`.
220 ///
221 /// Requires the `serialize` feature.
222 pub fn is_valid<__VldT: $crate::serde::Serialize>(instance: &__VldT) -> bool {
223 Self::validate(instance).is_ok()
224 }
225 }
226 }
227
228 $crate::__vld_if_openapi! {
229 impl $name {
230 /// Generate a JSON Schema / OpenAPI 3.1 representation of this struct.
231 ///
232 /// Requires the `openapi` feature.
233 pub fn json_schema() -> $crate::serde_json::Value {
234 use $crate::json_schema::JsonSchema as _;
235 let mut __vld_properties = $crate::serde_json::Map::new();
236 let mut __vld_required: ::std::vec::Vec<::std::string::String> =
237 ::std::vec::Vec::new();
238
239 $(
240 {
241 let __vld_field_schema = $schema;
242 let __vld_key = $crate::__vld_resolve_key!(
243 stringify!($field_name) $(, $rename)?
244 );
245 __vld_properties.insert(
246 ::std::string::String::from(__vld_key),
247 __vld_field_schema.json_schema(),
248 );
249 __vld_required.push(
250 ::std::string::String::from(__vld_key),
251 );
252 }
253 )*
254
255 $crate::serde_json::json!({
256 "type": "object",
257 "required": __vld_required,
258 "properties": $crate::serde_json::Value::Object(__vld_properties),
259 })
260 }
261
262 /// Wrap `json_schema()` in a minimal OpenAPI 3.1 document.
263 ///
264 /// Requires the `openapi` feature.
265 pub fn to_openapi_document() -> $crate::serde_json::Value {
266 $crate::json_schema::to_openapi_document(stringify!($name), &Self::json_schema())
267 }
268
269 /// Collect `(name, json_schema_fn)` pairs for all nested schemas
270 /// used by fields of this struct.
271 ///
272 /// Used by `vld-utoipa`'s `impl_to_schema!` to automatically register
273 /// nested types as OpenAPI components.
274 #[doc(hidden)]
275 pub fn __vld_nested_schemas()
276 -> ::std::vec::Vec<$crate::json_schema::NestedSchemaEntry>
277 {
278 use $crate::json_schema::CollectNestedSchemas as _;
279 let mut __vld_out: ::std::vec::Vec<$crate::json_schema::NestedSchemaEntry> =
280 ::std::vec::Vec::new();
281
282 $(
283 {
284 let __vld_field_schema = $schema;
285 __vld_field_schema.collect_nested_schemas(&mut __vld_out);
286 }
287 )*
288
289 __vld_out
290 }
291 }
292 }
293 };
294}
295
296/// Generate `validate_fields()` and `parse_lenient()` methods for a struct
297/// previously defined with [`schema!`].
298///
299/// Syntax mirrors `schema!`, but without visibility/attributes:
300///
301/// ```ignore
302/// vld::impl_validate_fields!(User {
303/// name: String => vld::string().min(2),
304/// age: i64 => vld::number().int(),
305/// });
306/// ```
307///
308/// Fields can also use `as "json_key"` to match a renamed JSON property:
309///
310/// ```ignore
311/// vld::impl_validate_fields!(User {
312/// first_name: String as "firstName" => vld::string().min(2),
313/// });
314/// ```
315///
316/// Generated methods:
317///
318/// - **`validate_fields(input)`** — validate each field, return `Vec<FieldResult>`
319/// - **`parse_lenient(input)`** — build the struct even if some fields fail
320/// (uses `Default` for invalid fields), returns [`ParseResult<Self>`](crate::error::ParseResult)
321///
322/// The returned [`ParseResult`](crate::error::ParseResult) can be inspected,
323/// converted to JSON, or saved to a file at any time via `.save_to_file(path)`.
324///
325/// **Requires:**
326/// - Field output types: `serde::Serialize`
327/// - For `parse_lenient`: field types also need `Default`
328/// - For `save_to_file` / `to_json_string`: the struct needs `serde::Serialize`
329#[macro_export]
330macro_rules! impl_validate_fields {
331 (
332 $name:ident {
333 $( $field_name:ident : $field_type:ty $(as $rename:literal)? => $schema:expr ),* $(,)?
334 }
335 ) => {
336 impl $name {
337 /// Validate each field individually and return per-field results.
338 ///
339 /// Unlike `parse()`, this does **not** fail fast — every field is
340 /// validated and you see which fields passed and which failed.
341 pub fn validate_fields<__VldInputT: $crate::input::VldInput + ?Sized>(
342 input: &__VldInputT,
343 ) -> ::std::result::Result<
344 ::std::vec::Vec<$crate::error::FieldResult>,
345 $crate::error::VldError,
346 > {
347 let __vld_json = <__VldInputT as $crate::input::VldInput>::to_json_value(input)?;
348 Self::validate_fields_value(&__vld_json)
349 }
350
351 /// Validate each field individually from a `serde_json::Value`.
352 pub fn validate_fields_value(
353 __vld_json: &$crate::serde_json::Value,
354 ) -> ::std::result::Result<
355 ::std::vec::Vec<$crate::error::FieldResult>,
356 $crate::error::VldError,
357 > {
358 let __vld_obj = __vld_json.as_object().ok_or_else(|| {
359 $crate::error::VldError::single(
360 $crate::error::IssueCode::InvalidType {
361 expected: ::std::string::String::from("object"),
362 received: $crate::error::value_type_name(__vld_json),
363 },
364 ::std::format!(
365 "Expected object, received {}",
366 $crate::error::value_type_name(__vld_json)
367 ),
368 )
369 })?;
370
371 let mut __vld_results: ::std::vec::Vec<$crate::error::FieldResult> =
372 ::std::vec::Vec::new();
373
374 $(
375 {
376 let __vld_field_schema = $schema;
377 let __vld_key = $crate::__vld_resolve_key!(
378 stringify!($field_name) $(, $rename)?
379 );
380 let __vld_field_value = __vld_obj
381 .get(__vld_key)
382 .unwrap_or(&$crate::serde_json::Value::Null);
383
384 let __vld_result = $crate::object::DynSchema::dyn_parse(
385 &__vld_field_schema,
386 __vld_field_value,
387 );
388
389 __vld_results.push($crate::error::FieldResult {
390 name: ::std::string::String::from(__vld_key),
391 input: __vld_field_value.clone(),
392 result: __vld_result,
393 });
394 }
395 )*
396
397 ::std::result::Result::Ok(__vld_results)
398 }
399
400 /// Parse leniently: build the struct even when some fields fail.
401 ///
402 /// - Valid fields get their parsed value.
403 /// - Invalid fields fall back to `Default::default()`.
404 ///
405 /// Returns a [`ParseResult`](crate::error::ParseResult) that wraps
406 /// the struct and per-field diagnostics. You can inspect it, convert
407 /// to JSON, or save to a file whenever you need.
408 pub fn parse_lenient<__VldInputT: $crate::input::VldInput + ?Sized>(
409 input: &__VldInputT,
410 ) -> ::std::result::Result<
411 $crate::error::ParseResult<$name>,
412 $crate::error::VldError,
413 > {
414 let __vld_json = <__VldInputT as $crate::input::VldInput>::to_json_value(input)?;
415 Self::parse_lenient_value(&__vld_json)
416 }
417
418 /// Parse leniently from a `serde_json::Value`.
419 pub fn parse_lenient_value(
420 __vld_json: &$crate::serde_json::Value,
421 ) -> ::std::result::Result<
422 $crate::error::ParseResult<$name>,
423 $crate::error::VldError,
424 > {
425 use $crate::schema::VldSchema as _;
426
427 let __vld_obj = __vld_json.as_object().ok_or_else(|| {
428 $crate::error::VldError::single(
429 $crate::error::IssueCode::InvalidType {
430 expected: ::std::string::String::from("object"),
431 received: $crate::error::value_type_name(__vld_json),
432 },
433 ::std::format!(
434 "Expected object, received {}",
435 $crate::error::value_type_name(__vld_json)
436 ),
437 )
438 })?;
439
440 let mut __vld_results: ::std::vec::Vec<$crate::error::FieldResult> =
441 ::std::vec::Vec::new();
442
443 $(
444 #[allow(non_snake_case)]
445 let $field_name: $field_type = {
446 let __vld_field_schema = $schema;
447 let __vld_key = $crate::__vld_resolve_key!(
448 stringify!($field_name) $(, $rename)?
449 );
450 let __vld_field_value = __vld_obj
451 .get(__vld_key)
452 .unwrap_or(&$crate::serde_json::Value::Null);
453
454 match __vld_field_schema.parse_value(__vld_field_value) {
455 ::std::result::Result::Ok(v) => {
456 let __json_repr = $crate::serde_json::to_value(&v)
457 .unwrap_or_else(|_| __vld_field_value.clone());
458 __vld_results.push($crate::error::FieldResult {
459 name: ::std::string::String::from(__vld_key),
460 input: __vld_field_value.clone(),
461 result: ::std::result::Result::Ok(__json_repr),
462 });
463 v
464 }
465 ::std::result::Result::Err(e) => {
466 __vld_results.push($crate::error::FieldResult {
467 name: ::std::string::String::from(__vld_key),
468 input: __vld_field_value.clone(),
469 result: ::std::result::Result::Err(e),
470 });
471 <$field_type as ::std::default::Default>::default()
472 }
473 }
474 };
475 )*
476
477 let __vld_struct = $name {
478 $( $field_name, )*
479 };
480
481 ::std::result::Result::Ok(
482 $crate::error::ParseResult::new(__vld_struct, __vld_results)
483 )
484 }
485 }
486 };
487}
488
489/// Combined macro: generates the struct, `parse()`, **and** `validate_fields()` /
490/// `parse_lenient()` in a single declaration — no need to repeat field schemas.
491///
492/// This is equivalent to calling `schema!` + `impl_validate_fields!` together.
493///
494/// **Extra requirements** compared to `schema!`:
495/// - All field types must implement `serde::Serialize` (for per-field JSON output)
496/// - All field types must implement `Default` (for lenient fallback values)
497///
498/// # Example
499///
500/// ```ignore
501/// vld::schema_validated! {
502/// #[derive(Debug, serde::Serialize)]
503/// pub struct User {
504/// pub name: String => vld::string().min(2),
505/// pub age: Option<i64> => vld::number().int().optional(),
506/// }
507/// }
508///
509/// // Has parse(), validate_fields(), parse_lenient(), etc.
510/// let result = User::parse_lenient(r#"{"name":"X"}"#).unwrap();
511/// result.save_to_file(std::path::Path::new("out.json")).unwrap();
512/// ```
513#[macro_export]
514macro_rules! schema_validated {
515 (
516 $(#[$meta:meta])*
517 $vis:vis struct $name:ident {
518 $(
519 $(#[$field_meta:meta])*
520 $field_vis:vis $field_name:ident : $field_type:ty $(as $rename:literal)? => $schema:expr
521 ),* $(,)?
522 }
523 ) => {
524 // 1. Generate the struct + parse/parse_value (same as schema!)
525 $crate::schema! {
526 $(#[$meta])*
527 $vis struct $name {
528 $(
529 $(#[$field_meta])*
530 $field_vis $field_name : $field_type $(as $rename)? => $schema
531 ),*
532 }
533 }
534
535 // 2. Generate validate_fields + parse_lenient (same as impl_validate_fields!)
536 $crate::impl_validate_fields!($name {
537 $( $field_name : $field_type $(as $rename)? => $schema ),*
538 });
539 };
540}
541
542/// Attach validation rules to an **existing** struct.
543///
544/// Unlike [`schema!`] which creates the struct, this macro takes a struct you
545/// already have and generates `validate()` and `is_valid()` instance methods.
546///
547/// The struct must implement `serde::Serialize`.
548///
549/// The struct does **not** need `#[derive(Serialize)]` or `#[derive(Debug)]` —
550/// each field is serialized individually (standard types like `String`, `f64`,
551/// `Vec<T>` already implement `Serialize`).
552///
553/// # Example
554///
555/// ```
556/// use vld::prelude::*;
557///
558/// // No Serialize or Debug required on the struct itself
559/// struct Product {
560/// name: String,
561/// price: f64,
562/// tags: Vec<String>,
563/// }
564///
565/// vld::impl_rules!(Product {
566/// name => vld::string().min(2).max(100),
567/// price => vld::number().positive(),
568/// tags => vld::array(vld::string().min(1)).max_len(10),
569/// });
570///
571/// let p = Product {
572/// name: "Widget".into(),
573/// price: 9.99,
574/// tags: vec!["sale".into()],
575/// };
576/// assert!(p.is_valid());
577///
578/// let bad = Product {
579/// name: "X".into(),
580/// price: -1.0,
581/// tags: vec![],
582/// };
583/// assert!(!bad.is_valid());
584/// let err = bad.validate().unwrap_err();
585/// assert!(err.issues.len() >= 2);
586/// ```
587#[macro_export]
588macro_rules! impl_rules {
589 (
590 $name:ident {
591 $( $field:ident => $schema:expr ),* $(,)?
592 }
593 ) => {
594 impl $name {
595 /// Validate this instance against the declared rules.
596 ///
597 /// Each field is serialized to JSON individually and checked
598 /// against its schema. All errors are accumulated.
599 pub fn validate(&self) -> ::std::result::Result<(), $crate::error::VldError> {
600 use $crate::schema::VldSchema as _;
601 let mut __vld_errors = $crate::error::VldError::new();
602
603 $(
604 {
605 let __vld_field_json = $crate::serde_json::to_value(&self.$field)
606 .map_err(|e| {
607 $crate::error::VldError::single(
608 $crate::error::IssueCode::ParseError,
609 ::std::format!(
610 "Serialization error for field '{}': {}",
611 stringify!($field), e
612 ),
613 )
614 });
615 match __vld_field_json {
616 ::std::result::Result::Ok(ref __vld_val) => {
617 let __vld_field_schema = $schema;
618 if let ::std::result::Result::Err(e) =
619 __vld_field_schema.parse_value(__vld_val)
620 {
621 __vld_errors = $crate::error::VldError::merge(
622 __vld_errors,
623 $crate::error::VldError::with_prefix(
624 e,
625 $crate::error::PathSegment::Field(
626 ::std::string::String::from(stringify!($field)),
627 ),
628 ),
629 );
630 }
631 }
632 ::std::result::Result::Err(e) => {
633 __vld_errors = $crate::error::VldError::merge(
634 __vld_errors,
635 $crate::error::VldError::with_prefix(
636 e,
637 $crate::error::PathSegment::Field(
638 ::std::string::String::from(stringify!($field)),
639 ),
640 ),
641 );
642 }
643 }
644 }
645 )*
646
647 if __vld_errors.is_empty() {
648 ::std::result::Result::Ok(())
649 } else {
650 ::std::result::Result::Err(__vld_errors)
651 }
652 }
653
654 /// Check if this instance passes all validation rules.
655 pub fn is_valid(&self) -> bool {
656 self.validate().is_ok()
657 }
658 }
659 };
660}
661
662/// Generate `impl Default` for a struct created by [`schema!`].
663///
664/// Use this instead of `#[derive(Default)]` to automatically generate a
665/// `Default` implementation bounded on all field types implementing `Default`.
666///
667/// # Example
668///
669/// ```
670/// use vld::prelude::*;
671///
672/// vld::schema! {
673/// #[derive(Debug)]
674/// pub struct Config {
675/// pub host: String => vld::string().with_default("localhost".into()),
676/// pub port: Option<i64> => vld::number().int().optional(),
677/// pub tags: Vec<String> => vld::array(vld::string()).with_default(vec![]),
678/// }
679/// }
680///
681/// vld::impl_default!(Config { host, port, tags });
682///
683/// let cfg = Config::default();
684/// assert_eq!(cfg.host, ""); // String::default()
685/// assert_eq!(cfg.port, None); // Option::default()
686/// assert!(cfg.tags.is_empty()); // Vec::default()
687/// ```
688#[macro_export]
689macro_rules! impl_default {
690 ($name:ident { $($field:ident),* $(,)? }) => {
691 impl ::std::default::Default for $name {
692 fn default() -> Self {
693 Self {
694 $( $field: ::std::default::Default::default(), )*
695 }
696 }
697 }
698 };
699}
700
701/// Create a union schema from 2 or more schemas.
702///
703/// Dispatches to `vld::union()` for 2 schemas and `vld::union3()` for 3.
704/// For 4+ schemas, unions are nested automatically.
705///
706/// # Examples
707///
708/// ```rust
709/// use vld::prelude::*;
710///
711/// // 2 schemas
712/// let s = vld::union!(vld::string(), vld::number().int());
713/// assert!(s.parse(r#""hello""#).is_ok());
714/// assert!(s.parse("42").is_ok());
715///
716/// // 3 schemas
717/// let s = vld::union!(vld::string(), vld::number().int(), vld::boolean());
718/// assert!(s.parse("true").is_ok());
719/// ```
720#[macro_export]
721macro_rules! union {
722 // 2 schemas
723 ($a:expr, $b:expr $(,)?) => {
724 $crate::union($a, $b)
725 };
726 // 3 schemas
727 ($a:expr, $b:expr, $c:expr $(,)?) => {
728 $crate::union3($a, $b, $c)
729 };
730 // 4 schemas — nest as union(union(a, b), union(c, d))
731 ($a:expr, $b:expr, $c:expr, $d:expr $(,)?) => {
732 $crate::union($crate::union($a, $b), $crate::union($c, $d))
733 };
734 // 5 schemas
735 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr $(,)?) => {
736 $crate::union($crate::union3($a, $b, $c), $crate::union($d, $e))
737 };
738 // 6 schemas
739 ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr $(,)?) => {
740 $crate::union($crate::union3($a, $b, $c), $crate::union3($d, $e, $f))
741 };
742}