1use core::iter::Iterator;
2
3use proc_macro::{TokenStream, TokenTree};
4
5fn append_as(buf: &mut String, struct_name: &str, ty: &str, name: &str, field_name: &str, op: char, ops: &[usize]) {
6 let sufix = if ty == "usize" { "".to_string() } else { format!("_{ty}") };
7 let as_ty = if ty == "usize" { "".to_string() } else { format!("as {ty}") };
8
9 let mut operation = format!("self.{field_name} {as_ty}");
10
11 let op_sufix = if ty == "usize" { "" } else { ".0" };
12
13 for n in ops {
14 operation.push(op);
15 operation.push_str(&format!("{n}{op_sufix}"));
16 }
17
18 buf.push_str(&format!(r#"#[doc = "Returns this [`{struct_name}`] converted to {name}"]"#));
19 buf.push_str(&format!("pub fn as_{name}{sufix} (&self) -> {ty} {{"));
20 buf.push_str(&operation);
21 buf.push('}');
22}
23
24fn append_from(buf: &mut String, struct_name: &str, name: &str, field_name: &str, op: char, ops: &[usize]) {
25 buf.push_str(&format!(r#"
26 #[doc = "Builds a [`{struct_name}`] from it's value on {name}"]"#));
27 buf.push_str(&format!("pub fn from_{name} (n: usize) -> Self {{ Self {{ {field_name}: n"));
28
29 for n in ops {
30 buf.push(op);
31 buf.push_str(&format!("{n}"));
32 }
33
34 buf.push_str("}}");
35}
36
37macro_rules! err {
38 ($msg:expr) => {
39 return format!("compile_error!(\"{}\");", $msg).parse().unwrap()
40 };
41}
42macro_rules! unwrap {
43 ($args:expr, $t:ident) => {{
44 let Some(nx) = $args.next() else { err!("Unexpected end of token stream") };
45 let TokenTree:: $t (val) = nx else { err!(format!("Expected '{}' found '{nx}'", stringify!($t))) };
46 val
47 }};
48}
49
50macro_rules! expect_punct {
51 ($args:expr, $p:literal) => {{
52 let c = unwrap!($args, Punct).as_char();
53 if c != $p {
54 err!(format!("Expected punctuation to be '{}' but found '{c}'", $p));
55 }
56 }};
57}
58
59#[proc_macro]
60pub fn impl_as(args: TokenStream) -> TokenStream {
61 let mut args = args.into_iter().peekable();
62
63 let struct_name = unwrap!(args, Ident).to_string();
64 expect_punct!(args, ',');
65
66 let field_name = unwrap!(args, Ident).to_string();
67 expect_punct!(args, ',');
68
69 let mut block = format!("impl {struct_name} {{");
70
71 while args.peek().is_some() {
72 let func_name = unwrap!(args, Ident).to_string();
73 expect_punct!(args, '=');
74
75 let mut ops = vec![];
76 let mut op = '/';
77 while args.peek().unwrap().to_string() != ";" {
78 if !ops.is_empty() {
79 op = unwrap!(args, Punct).as_char();
80 }
81 let Ok(num) = unwrap!(args, Literal).to_string().parse::<usize>() else { err!("Error parsing literal, expected usize") };
82 ops.push(num);
83 }
84 expect_punct!(args, ';');
85
86 append_as(&mut block, &struct_name, "usize", &func_name, &field_name, op, &ops);
87 append_as(&mut block, &struct_name, "f32", &func_name, &field_name, op, &ops);
88 append_as(&mut block, &struct_name, "f64", &func_name, &field_name, op, &ops);
89 }
90 block.push('}');
91
92 block.parse().unwrap()
93}
94
95#[proc_macro]
96pub fn impl_from(args: TokenStream) -> TokenStream {
97 let mut args = args.into_iter().peekable();
98
99 let struct_name = unwrap!(args, Ident).to_string();
100 expect_punct!(args, ',');
101
102 let field_name = unwrap!(args, Ident).to_string();
103 expect_punct!(args, ',');
104
105 let mut block = format!("impl {struct_name} {{");
106
107 while args.peek().is_some() {
108 let func_name = unwrap!(args, Ident).to_string();
109 expect_punct!(args, '=');
110
111 let mut ops = vec![];
112 let mut op = '*';
113 while args.peek().unwrap().to_string() != ";" {
114 if !ops.is_empty() {
115 op = unwrap!(args, Punct).as_char();
116 }
117 let Ok(num) = unwrap!(args, Literal).to_string().parse::<usize>() else { err!("Error parsing literal, expected usize") };
118 ops.push(num);
119 }
120 expect_punct!(args, ';');
121
122 append_from(&mut block, &struct_name, &func_name, &field_name, op, &ops);
123 }
124 block.push('}');
125
126 block.parse().unwrap()
127}
128