sci_units_proc_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use syn::parse::{Parse, ParseStream};
5use syn::{DeriveInput, Token};
6
7struct Parameters {
8    parameters: Vec<(String, syn::Ident)>,
9}
10
11impl Parameters {
12    fn get_token(&self, name: &str) -> syn::Ident {
13        let expected_value = {
14            let mut value = String::from("missing parameter:");
15            value.push_str(name);
16            value
17        };
18        self.parameters
19            .iter()
20            .filter(|e| e.0 == name)
21            .nth(0)
22            .expect(expected_value.as_str())
23            .1
24            .clone()
25    }
26}
27
28impl Parse for Parameters {
29    fn parse(input: ParseStream) -> syn::Result<Self> {
30        let content;
31
32        syn::parenthesized!(content in input);
33        let mut result = Parameters { parameters: vec![] };
34
35        loop {
36            let key: syn::Ident = content.parse()?;
37            content.parse::<Token![=]>()?;
38            let value = content.parse()?;
39            result.parameters.push((key.to_string(), value));
40
41            match content.parse::<Token![,]>() {
42                Err(_) => break,
43                _ => continue,
44            }
45        }
46        Ok(result)
47    }
48}
49
50#[proc_macro_derive(SiDisplay)]
51pub fn display_macro_derive(input: TokenStream) -> TokenStream {
52    // Construct a representation of Rust code as a syntax tree
53    // that we can manipulate
54    let ast: DeriveInput = syn::parse(input).unwrap();
55    // Build the trait implementation
56    impl_display_macro(&ast)
57}
58
59#[proc_macro_derive(SiAddSubtract)]
60pub fn add_subtract_macro_derive(input: TokenStream) -> TokenStream {
61    // Construct a representation of Rust code as a syntax tree
62    // that we can manipulate
63    let ast = syn::parse(input).unwrap();
64
65    // Build the trait implementation
66    impl_add_subtract_macro(&ast)
67}
68
69fn impl_add_subtract_macro(ast: &syn::DeriveInput) -> TokenStream {
70    let name = &ast.ident;
71    let generate = quote::quote! {
72        impl core::cmp::PartialEq for #name {
73            fn eq(&self, rhs: &Self) -> bool {
74                #[cfg(feature = "f32")]
75                {
76                    let lhs_log = libm::floorf(libm::log10f(self.native)) as i32;
77                    let rhs_log = libm::floorf(libm::log10f(rhs.native)) as i32;
78                    if( lhs_log != rhs_log ){
79                        return false;
80                    }
81                    let power_of = (crate::SIGNIFICANT_FIGURES - lhs_log - 1) as crate::NativeType;
82                    (libm::roundf((self.native - rhs.native) * libm::powf(10.0 as crate::NativeType, power_of))) as i32 == 0
83                }
84
85                #[cfg(not(feature = "f32"))]
86                {
87                    let lhs_log = libm::floor(libm::log10(self.native)) as i32;
88                    let rhs_log = libm::floor(libm::log10(rhs.native)) as i32;
89                    if( lhs_log != rhs_log ){
90                        return false;
91                    }
92                    let power_of = (crate::SIGNIFICANT_FIGURES - lhs_log - 1) as crate::NativeType;
93                    (libm::round((self.native - rhs.native) * libm::pow(10.0 as crate::NativeType, power_of))) as i32 == 0
94                }
95            }
96        }
97
98        impl #name {
99            pub const fn new(native: crate::NativeType) -> Self {
100                Self{ native }
101            }
102
103            pub fn abs(&self) -> Self {
104                Self{ native: self.native.abs() }
105            }
106
107        }
108
109        impl Into<crate::NativeType> for #name {
110            fn into(self) -> crate::NativeType {
111                self.native
112            }
113        }
114
115        impl From<crate::NativeType> for #name {
116            fn from(native: crate::NativeType) -> #name {
117                #name{ native }
118            }
119        }
120
121        impl core::cmp::PartialOrd for #name {
122            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
123                self.native.partial_cmp(&other.native)
124            }
125        }
126
127        impl core::ops::Add for #name {
128            type Output = Self;
129            fn add(self, rhs: Self) -> Self {
130               Self::from(self.native + rhs.native)
131            }
132        }
133
134        impl core::ops::Mul<crate::Scalar> for #name {
135            type Output = Self;
136            fn mul(self, rhs: crate::Scalar) -> Self {
137               Self::from(self.native * rhs.native)
138            }
139        }
140
141        impl core::ops::Div<#name> for #name {
142            type Output = crate::Scalar;
143            fn div(self, rhs: #name) -> crate::Scalar {
144               crate::Scalar::from(self.native / rhs.native)
145            }
146        }
147
148        impl core::ops::AddAssign for #name {
149            fn add_assign(&mut self, rhs: Self) {
150               *self = Self::from(self.native + rhs.native);
151            }
152        }
153
154        impl core::ops::Sub for #name {
155            type Output = Self;
156            fn sub(self, rhs: Self) -> Self {
157               Self::from(self.native - rhs.native)
158            }
159        }
160
161        impl core::ops::SubAssign for #name {
162            fn sub_assign(&mut self, rhs: Self) {
163               *self = Self::from(self.native - rhs.native);
164            }
165        }
166    };
167    generate.into()
168}
169
170#[proc_macro_derive(SiMultiplyDivideScalar)]
171pub fn add_subtract_no_divide_macro_derive(input: TokenStream) -> TokenStream {
172    // Construct a representation of Rust code as a syntax tree
173    // that we can manipulate
174    let ast = syn::parse(input).unwrap();
175
176    // Build the trait implementation
177    impl_multiply_divide_scalar_macro(&ast)
178}
179
180fn impl_multiply_divide_scalar_macro(ast: &syn::DeriveInput) -> TokenStream {
181    let name = &ast.ident;
182    let generate = quote::quote! {
183
184        impl core::ops::Mul<crate::Decibel<#name>> for #name
185        {
186            type Output = Self;
187            fn mul(self, rhs: crate::Decibel<#name>) -> Self {
188                Self::from(self * rhs.ratio())
189            }
190        }
191
192        impl core::ops::Mul<#name> for crate::Scalar {
193            type Output = #name;
194            fn mul(self, rhs: #name) -> #name {
195               #name::from(self.native * rhs.native)
196            }
197        }
198
199        impl core::ops::Div<crate::Scalar> for #name {
200            type Output = #name;
201            fn div(self, rhs: crate::Scalar) -> #name {
202               #name::from(self.native / rhs.native)
203            }
204        }
205
206    };
207    generate.into()
208}
209
210fn get_parameters(ast: &DeriveInput, expect: &'static str) -> Parameters {
211    let attribute = ast
212        .attrs
213        .iter()
214        .find(|a| a.path.segments.len() == 1 && a.path.segments[0].ident == "parameters")
215        .expect(expect);
216
217    syn::parse2(attribute.tokens.clone()).expect("Invalid parameters attribute!")
218}
219
220#[proc_macro_derive(SiMultiply, attributes(parameters))]
221pub fn mult_macro_derive(input: TokenStream) -> TokenStream {
222    // Construct a representation of Rust code as a syntax tree
223    // that we can manipulate
224    let ast: DeriveInput = syn::parse(input).unwrap();
225    let parameters = get_parameters(&ast, "parameters required for deriving SiMultiply");
226
227    // Build the trait implementation
228    let name = &ast.ident;
229    let lhs = parameters.get_token("lhs_mult");
230    let rhs = parameters.get_token("rhs_mult");
231    gen_multiply(name, lhs, rhs)
232}
233
234#[proc_macro_derive(SiMultiplyAlt, attributes(parameters))]
235pub fn mult_alt_macro_derive(input: TokenStream) -> TokenStream {
236    // Construct a representation of Rust code as a syntax tree
237    // that we can manipulate
238    let ast: DeriveInput = syn::parse(input).unwrap();
239    let parameters = get_parameters(&ast, "parameters required for deriving SiMultiply");
240
241    // Build the trait implementation
242    let name = &ast.ident;
243    let lhs = parameters.get_token("lhs_mult_alt");
244    let rhs = parameters.get_token("rhs_mult_alt");
245    gen_multiply(name, lhs, rhs)
246}
247
248fn gen_multiply(name: &syn::Ident, lhs: syn::Ident, rhs: syn::Ident) -> TokenStream {
249    let generate = quote::quote! {
250        impl core::ops::Mul<#rhs> for #lhs {
251            type Output = #name;
252            fn mul(self, rhs: #rhs) -> #name {
253               #name::from(self.native * rhs.native)
254            }
255        }
256
257        impl core::ops::Mul<#lhs> for #rhs {
258            type Output = #name;
259            fn mul(self, rhs: #lhs) -> #name {
260               #name::from(self.native * rhs.native)
261            }
262        }
263
264
265        impl core::ops::Div<#rhs> for #name {
266            type Output = #lhs;
267            fn div(self, rhs: #rhs) -> #lhs {
268               #lhs::from(self.native / rhs.native)
269            }
270        }
271
272        impl core::ops::Div<#lhs> for #name {
273            type Output = #rhs;
274            fn div(self, rhs: #lhs) -> #rhs {
275               #rhs::from(self.native / rhs.native)
276            }
277        }
278    };
279    generate.into()
280}
281
282#[proc_macro_derive(SiSquare, attributes(parameters))]
283pub fn square_macro_derive(input: TokenStream) -> TokenStream {
284    let ast: DeriveInput = syn::parse(input).unwrap();
285    let parameters = get_parameters(&ast, "parameters required for deriving SiSquare");
286    let name = &ast.ident;
287    let square = parameters.get_token("square");
288    let generate = quote::quote! {
289        impl core::ops::Mul<#square> for #square {
290            type Output = #name;
291            fn mul(self, square: #square) -> #name {
292               #name::from(self.native * square.native)
293            }
294        }
295
296        impl core::ops::Div<#square> for #name {
297            type Output = #square;
298            fn div(self, square: #square) -> #square {
299               #square::from(self.native / square.native)
300            }
301        }
302
303        impl #name {
304            pub fn sqrt(&self) -> #square {
305                #[cfg(feature = "f32")]
306                {
307                    #square::from(libm::sqrtf(self.native))
308                }
309
310                #[cfg(not(feature = "f32"))]
311                {
312                    #square::from(libm::sqrt(self.native))
313                }
314            }
315        }
316
317    };
318    generate.into()
319}
320
321#[proc_macro_derive(SiInvert, attributes(parameters))]
322pub fn invert_macro_derive(input: TokenStream) -> TokenStream {
323    let ast: DeriveInput = syn::parse(input).unwrap();
324    let parameters = get_parameters(&ast, "parameters required for deriving SiSquare");
325    let name = &ast.ident;
326    let inv = parameters.get_token("inv");
327    let generate = quote::quote! {
328        impl core::ops::Mul<#inv> for #name {
329            type Output = crate::Scalar;
330            fn mul(self, inv: #inv) -> crate::Scalar {
331               crate::Scalar::from(self.native * inv.native)
332            }
333        }
334
335        impl core::ops::Mul<#name> for #inv {
336            type Output = crate::Scalar;
337            fn mul(self, name: #name) -> crate::Scalar {
338               crate::Scalar::from(self.native * name.native)
339            }
340        }
341
342
343        impl core::ops::Div<#inv> for crate::Scalar {
344            type Output = #name;
345            fn div(self, inv: #inv) -> #name {
346               #name::from(self.native / inv.native)
347            }
348        }
349
350
351        impl core::ops::Div<#name> for crate::Scalar {
352            type Output = #inv;
353            fn div(self, name: #name) -> #inv {
354               #inv::from(self.native / name.native)
355            }
356        }
357
358    };
359    generate.into()
360}
361
362#[proc_macro_derive(SiDivide, attributes(parameters))]
363pub fn divide_macro_derive(input: TokenStream) -> TokenStream {
364    // Construct a representation of Rust code as a syntax tree
365    // that we can manipulate
366    let ast: DeriveInput = syn::parse(input).unwrap();
367    let parameters = get_parameters(&ast, "parameters required for deriving SiDivide");
368
369    let name = &ast.ident;
370    let lhs = parameters.get_token("lhs_div");
371    let rhs = parameters.get_token("rhs_div");
372    let generate = quote::quote! {
373        impl core::ops::Div<#rhs> for #lhs {
374            type Output = #name;
375            fn div(self, rhs: #rhs) -> #name {
376               #name::from(self.native / rhs.native)
377            }
378        }
379
380        impl core::ops::Div<#name> for #lhs {
381            type Output = #rhs;
382            fn div(self, rhs: #name) -> #rhs {
383               #rhs::from(self.native / rhs.native)
384            }
385        }
386
387        impl core::ops::Mul<#rhs> for #name {
388            type Output = #lhs;
389            fn mul(self, rhs: #rhs) -> #lhs {
390               #lhs::from(self.native * rhs.native)
391            }
392        }
393
394        impl core::ops::Mul<#name> for #rhs {
395            type Output = #lhs;
396            fn mul(self, rhs: #name) -> #lhs {
397               #lhs::from(self.native * rhs.native)
398            }
399        }
400    };
401    generate.into()
402}
403
404#[proc_macro_derive(SiConvert, attributes(parameters))]
405pub fn convert_macro_derive(input: TokenStream) -> TokenStream {
406    // Construct a representation of Rust code as a syntax tree
407    // that we can manipulate
408    let ast: DeriveInput = syn::parse(input).unwrap();
409    let parameters = get_parameters(&ast, "parameters required for deriving SiDivide");
410
411    let name = &ast.ident;
412    let multiplier = parameters.get_token("multiplier");
413    let offset = parameters.get_token("offset");
414    let into = parameters.get_token("into");
415    let generate = quote::quote! {
416
417        impl From<#into> for #name {
418            fn from(value: #into) -> Self {
419                Self::from(value.native * #multiplier + #offset)
420            }
421        }
422
423        impl Into<#into> for #name {
424            fn into(self) -> #into {
425                #into::from((self.native - #offset) / #multiplier)
426            }
427        }
428    };
429    generate.into()
430}
431
432struct UnitType {
433    name: &'static str,
434    label: &'static str,
435}
436
437const UNITS: &[UnitType] = &[
438    UnitType {
439        name: "Scalar",
440        label: "scalar",
441    },
442    UnitType {
443        name: "DecibelV",
444        label: "decibelsV",
445    },
446    UnitType {
447        name: "Decibel",
448        label: "decibels",
449    },
450    UnitType {
451        name: "Mass",
452        label: "kilograms",
453    },
454    UnitType {
455        name: "Time",
456        label: "seconds",
457    },
458    UnitType {
459        name: "ElectricCurrent",
460        label: "amps",
461    },
462    UnitType {
463        name: "ThermodynamicTemperature",
464        label: "kelvin",
465    },
466    UnitType {
467        name: "AmountOfSubstance",
468        label: "moles",
469    },
470    UnitType {
471        name: "Length",
472        label: "meters",
473    },
474    UnitType {
475        name: "LengthInverse",
476        label: "1/meter",
477    },
478    UnitType {
479        name: "OrthogonalLength",
480        label: "meters",
481    },
482    UnitType {
483        name: "LuminousIntensity",
484        label: "candelas",
485    },
486    UnitType {
487        name: "PlaneAngle",
488        label: "radians",
489    },
490    UnitType {
491        name: "PlaneAngleInverse",
492        label: "1/radians",
493    },
494    UnitType {
495        name: "SolidAngle",
496        label: "steradians",
497    },
498    UnitType {
499        name: "Frequency",
500        label: "hertz",
501    },
502    UnitType {
503        name: "FrequencySquared",
504        label: "hertz^2",
505    },
506    UnitType {
507        name: "Area",
508        label: "meters^2",
509    },
510    UnitType {
511        name: "Volume",
512        label: "meters^3",
513    },
514    UnitType {
515        name: "Liters",
516        label: "liters",
517    },
518    UnitType {
519        name: "Velocity",
520        label: "meters/second",
521    },
522    UnitType {
523        name: "VelocitySquared",
524        label: "(meters/second)^2",
525    },
526    UnitType {
527        name: "Acceleration",
528        label: "meters/second^2",
529    },
530    UnitType {
531        name: "Jerk",
532        label: "meters/second^3",
533    },
534    UnitType {
535        name: "Force",
536        label: "newtons",
537    },
538    UnitType {
539        name: "Pressure",
540        label: "pascals",
541    },
542    UnitType {
543        name: "Energy",
544        label: "joules",
545    },
546    UnitType {
547        name: "EnergyPerFrequency",
548        label: "joules/hertz",
549    },
550    UnitType {
551        name: "Power",
552        label: "watts",
553    },
554    UnitType {
555        name: "ElectricCharge",
556        label: "coulombs",
557    },
558    UnitType {
559        name: "ElectricPotential",
560        label: "volts",
561    },
562    UnitType {
563        name: "Capacitance",
564        label: "farads",
565    },
566    UnitType {
567        name: "ElectricResistance",
568        label: "ohms",
569    },
570    UnitType {
571        name: "ElectricConductance",
572        label: "siemens",
573    },
574    UnitType {
575        name: "MagneticFlux",
576        label: "webers",
577    },
578    UnitType {
579        name: "MagneticFluxDensity",
580        label: "teslas",
581    },
582    UnitType {
583        name: "Inductance",
584        label: "henries",
585    },
586    UnitType {
587        name: "Temperature",
588        label: "celcius",
589    },
590    UnitType {
591        name: "LuminousFlux",
592        label: "lumens",
593    },
594    UnitType {
595        name: "Illuminance",
596        label: "lux",
597    },
598    UnitType {
599        name: "DynamicViscosity",
600        label: "pascals*seconds",
601    },
602    UnitType {
603        name: "MomentOfForce",
604        label: "newtons*meters",
605    },
606    UnitType {
607        name: "Torque",
608        label: "newtons*meters",
609    },
610    UnitType {
611        name: "AngularVelocity",
612        label: "radians/second",
613    },
614    UnitType {
615        name: "AngularVelocitySquared",
616        label: "(radians/second)^2",
617    },
618    UnitType {
619        name: "AngularAcceleration",
620        label: "radians/second^2",
621    },
622    UnitType {
623        name: "SurfaceTension",
624        label: "newtons/meter",
625    },
626    UnitType {
627        name: "HeatFluxDensity",
628        label: "watts/meter^2",
629    },
630    UnitType {
631        name: "HeatCapacity",
632        label: "joules/kelvin",
633    },
634    UnitType {
635        name: "SpecificHeatCapacity",
636        label: "joules/(kilogram*kelvin)",
637    },
638    UnitType {
639        name: "SpecificEnergy",
640        label: "joules/kilogram",
641    },
642    UnitType {
643        name: "ThermalConductivity",
644        label: "watts/(meter*kelvin)",
645    },
646    UnitType {
647        name: "EnergyDensity",
648        label: "joules/meter^3",
649    },
650    UnitType {
651        name: "ElectricFieldStrength",
652        label: "volts/meter",
653    },
654    UnitType {
655        name: "ElectricChargeDensity",
656        label: "coulombs/meter^3",
657    },
658    UnitType {
659        name: "ElectricFluxDensity",
660        label: "coulombs/meter^2",
661    },
662    UnitType {
663        name: "Permittivity",
664        label: "farads/meter",
665    },
666    UnitType {
667        name: "Permeability",
668        label: "henries/meter",
669    },
670    UnitType {
671        name: "MolarEnergy",
672        label: "joules/mole",
673    },
674    UnitType {
675        name: "MolarHeatCapacity",
676        label: "joules/(mole*kelvin)",
677    },
678    UnitType {
679        name: "Radiance",
680        label: "watts/(meter^2*steradian)",
681    },
682    UnitType {
683        name: "MassThermodynamicTemperature",
684        label: "kilograms*kelvin",
685    },
686    UnitType {
687        name: "LengthThermodynamicTemperature",
688        label: "meters*kelvin",
689    },
690    UnitType {
691        name: "AmountOfSubstanceThermodynamicTemperature",
692        label: "moles*kelvin",
693    },
694    UnitType {
695        name: "AreaSolidAngle",
696        label: "meters^2/steradian",
697    },
698    UnitType {
699        name: "PerAmountOfSubstance",
700        label: "1/mole",
701    },
702    UnitType {
703        name: "EnergyPerFrequency",
704        label: "joules/hertz",
705    },
706    UnitType {
707        name: "MassDensity",
708        label: "kilograms/meter^3",
709    },
710    // Imperial units
711    UnitType {
712        name: "Feet",
713        label: "feet",
714    },
715    UnitType {
716        name: "Yard",
717        label: "yards",
718    },
719    UnitType {
720        name: "Inch",
721        label: "inches",
722    },
723    UnitType {
724        name: "Miles",
725        label: "miles",
726    },
727    UnitType {
728        name: "Acres",
729        label: "acres",
730    },
731    UnitType {
732        name: "SquareMiles",
733        label: "squaremiles",
734    },
735    UnitType {
736        name: "Pints",
737        label: "pt",
738    },
739    UnitType {
740        name: "Quarts",
741        label: "qt",
742    },
743    UnitType {
744        name: "Gallons",
745        label: "gal",
746    },
747    UnitType {
748        name: "Degrees",
749        label: "degrees",
750    },
751    UnitType {
752        name: "DegreesPerSecond",
753        label: "degrees/second",
754    },
755    UnitType {
756        name: "DegreesPerSecondSquared",
757        label: "degrees/second^2",
758    },
759    UnitType {
760        name: "DegreesFahrenheit",
761        label: "degreesF",
762    },
763    UnitType {
764        name: "DegreesRankine",
765        label: "degreesR",
766    },
767    UnitType {
768        name: "Revolutions",
769        label: "revolutions",
770    },
771    UnitType {
772        name: "RevolutionsPerMinute",
773        label: "rpm",
774    },
775    UnitType {
776        name: "NauticalMiles",
777        label: "nauticalmiles",
778    },
779    UnitType {
780        name: "Knots",
781        label: "knots",
782    },
783    UnitType {
784        name: "FeetPerSecond",
785        label: "feet/second",
786    },
787    UnitType {
788        name: "FeetPerSecondSquared",
789        label: "feet/second^2",
790    },
791    UnitType {
792        name: "FeetPerMinute",
793        label: "feet/minute",
794    },
795    UnitType {
796        name: "G",
797        label: "g",
798    },
799    UnitType {
800        name: "PoundsForce",
801        label: "lbsforce",
802    },
803    UnitType {
804        name: "Pounds",
805        label: "lbs",
806    },
807    UnitType {
808        name: "Ounces",
809        label: "oz",
810    },
811    UnitType {
812        name: "PoundsPerSquareInch",
813        label: "psi",
814    },
815    UnitType {
816        name: "PoundsPerSquareFoot",
817        label: "psf",
818    },
819    UnitType {
820        name: "InchesMercury",
821        label: "inHg",
822    },
823    UnitType {
824        name: "FootPounds",
825        label: "ftlbs",
826    },
827];
828
829fn find_unit(name: String) -> &'static UnitType {
830    for unit in UNITS {
831        if name == unit.name {
832            return unit;
833        }
834    }
835    panic!("no unit named {} was found", name);
836}
837
838fn impl_display_macro(ast: &syn::DeriveInput) -> TokenStream {
839    let name = &ast.ident;
840    let name_string = name.to_string();
841    let current_unit = find_unit(name_string);
842    let label: &'static str = current_unit.label;
843
844    let generate = quote::quote! {
845
846      #[cfg(feature = "use_defmt")]
847      impl defmt::Format for #name {
848        fn format(&self, f: defmt::Formatter<'_>) {
849          defmt::write!(f, "{} {}", self.native, #label);
850        }
851      }
852
853      #[cfg(feature = "std")]
854      impl serde::Serialize for #name {
855          fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
856          where
857              S: serde::Serializer,
858          {
859              // Format however you want
860              let s = std::format!("{}_{}", self.native, #label);
861              serializer.serialize_str(&s)
862          }
863      }
864
865      #[cfg(feature = "std")]
866      impl<'de> serde::Deserialize<'de> for #name {
867          fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
868          where
869              D: serde::Deserializer<'de>,
870          {
871              struct UnitsVisitor;
872
873              impl<'de> serde::de::Visitor<'de> for UnitsVisitor {
874                  type Value = #name;
875
876                  fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
877                      f.write_str(format!("a string like `10.0_{}`", #label).as_str())
878                  }
879
880                  fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
881                  where
882                      E: serde::de::Error,
883                  {
884                      // Expect format "<value>_meters"
885                      if let Some((value_str, #label)) = v.rsplit_once('_') {
886                          let native: crate::NativeType = value_str
887                              .parse()
888                              .map_err(|_| E::custom(format!("invalid float in {}", #label).as_str()))?;
889                          return Ok(#name { native });
890                      }
891
892                      Err(E::custom(format!("expected format `<float>_{}`", #label).as_str()))
893                  }
894              }
895
896              deserializer.deserialize_str(UnitsVisitor)
897          }
898      }
899
900      #[cfg(feature = "std")]
901      impl std::fmt::Display for #name {
902        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
903          write!(f, "{} {}", self.native, #label)
904        }
905      }
906
907      #[cfg(feature = "std")]
908      impl std::fmt::Debug for #name {
909        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
910          f.debug_struct(stringify!(#name))
911              .field("value", &self.to_string())
912              .field("label", &#label)
913              .finish()
914        }
915      }
916    };
917    generate.into()
918}