1use std::str::FromStr;
16
17use proc_macro2::Span;
18use quote::{quote, quote_spanned, ToTokens};
19use syn::{parse_macro_input, spanned::Spanned};
20
21#[proc_macro]
30pub fn env_cast(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
31 let cast = parse_macro_input!(args as syn::Expr);
32
33 let (var, ty) = if let syn::Expr::Cast(cast) = cast {
34 (cast.expr, cast.ty)
35 } else {
36 return compile_error("Must be of the form <var> as <type>", cast.span());
37 };
38
39 let lit = if let syn::Expr::Lit(lit) = &*var {
40 lit
41 } else {
42 return compile_error("Must be a string literal", var.span());
43 };
44
45 let lit_str = if let syn::Lit::Str(lit_str) = &lit.lit {
46 lit_str.value()
47 } else {
48 return compile_error("Must be a string literal", lit.span());
49 };
50
51 let cast_as = match &*ty {
52 syn::Type::Path(path) => {
53 if path.path.is_ident("i8") {
54 CastAs::I8
55 } else if path.path.is_ident("u8") {
56 CastAs::U8
57 } else if path.path.is_ident("i16") {
58 CastAs::I16
59 } else if path.path.is_ident("u16") {
60 CastAs::U16
61 } else if path.path.is_ident("i32") {
62 CastAs::I32
63 } else if path.path.is_ident("u32") {
64 CastAs::U32
65 } else if path.path.is_ident("i64") {
66 CastAs::I64
67 } else if path.path.is_ident("u64") {
68 CastAs::U64
69 } else if path.path.is_ident("f32") {
70 CastAs::F32
71 } else if path.path.is_ident("f64") {
72 CastAs::F64
73 } else {
74 return compile_error(
75 "Must be one of i8, u8, i16, u16, i32, u32, i64, u64, f32, f64",
76 ty.span(),
77 );
78 }
79 }
80 _ => {
81 return compile_error(
82 "Must be one of i8, u8, i16, u16, i32, u32, i64, u64, f32, f64",
83 ty.span(),
84 );
85 }
86 };
87
88 let val_str = match std::env::var(lit_str) {
89 Ok(val) => val,
90 Err(err) => {
91 return compile_error(&format!("Failed to read environment: {}", err), lit.span())
92 }
93 };
94
95 match cast_as {
96 CastAs::I8 => parse::<i8>(&val_str, lit.span()),
97 CastAs::U8 => parse::<u8>(&val_str, lit.span()),
98 CastAs::I16 => parse::<i16>(&val_str, lit.span()),
99 CastAs::U16 => parse::<u16>(&val_str, lit.span()),
100 CastAs::I32 => parse::<i32>(&val_str, lit.span()),
101 CastAs::U32 => parse::<u32>(&val_str, lit.span()),
102 CastAs::I64 => parse::<i64>(&val_str, lit.span()),
103 CastAs::U64 => parse::<u64>(&val_str, lit.span()),
104 CastAs::F32 => parse::<f32>(&val_str, lit.span()),
105 CastAs::F64 => parse::<f64>(&val_str, lit.span()),
106 }
107}
108
109fn compile_error(msg: &str, span: Span) -> proc_macro::TokenStream {
110 quote_spanned! {span=>
111 compile_error!(#msg);
112 }
113 .into()
114}
115
116enum CastAs {
117 I8,
118 U8,
119 I16,
120 U16,
121 I32,
122 U32,
123 I64,
124 U64,
125 F32,
126 F64,
127}
128
129fn parse<T: FromStr + ToTokens>(str: &str, span: Span) -> proc_macro::TokenStream {
130 match str.parse::<T>() {
131 Ok(val) => quote! {#val}.into(),
132 Err(_) => compile_error("Environment variable not parseable", span),
133 }
134}