device_driver_macros/
lib.rs1#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
2
3use std::{fs::File, io::Read, ops::Deref, path::PathBuf};
4
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7use syn::{Ident, LitStr, braced};
8
9#[proc_macro]
33pub fn create_device(item: TokenStream) -> TokenStream {
34 let input = match syn::parse::<Input>(item) {
35 Ok(i) => i,
36 Err(e) => return e.into_compile_error().into(),
37 };
38
39 match input.generation_type {
40 #[cfg(feature = "dsl")]
41 GenerationType::Dsl(tokens) => {
42 device_driver_generation::transform_dsl(tokens, &input.device_name.to_string())
43 .parse()
44 .unwrap()
45 }
46 #[cfg(not(feature = "dsl"))]
47 GenerationType::Dsl(_tokens) => {
48 syn::Error::new(Span::call_site(), format!("The dsl feature is not enabled"))
49 .into_compile_error()
50 .into()
51 }
52 GenerationType::Manifest(path) => {
53 let result: Result<String, syn::Error> =
54 (|| {
55 let mut path = PathBuf::from(path.value());
56 if path.is_relative() {
57 let manifest_dir =
58 PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
59 path = manifest_dir.join(path);
60 }
61
62 let mut file_contents = String::new();
63 File::open(&path)
64 .map_err(|e| {
65 syn::Error::new(
66 Span::call_site(),
67 format!(
68 "Could not open the manifest file at '{}': {e}",
69 path.display()
70 ),
71 )
72 })?
73 .read_to_string(&mut file_contents)
74 .unwrap();
75
76 let extension = path.extension().map(|ext| ext.to_string_lossy()).ok_or(
77 syn::Error::new(Span::call_site(), "Manifest file has no file extension"),
78 )?;
79
80 match extension.deref() {
81 #[cfg(feature = "json")]
82 "json" => Ok(device_driver_generation::transform_json(
83 &file_contents,
84 &input.device_name.to_string(),
85 )),
86 #[cfg(not(feature = "json"))]
87 "json" => Err(syn::Error::new(
88 Span::call_site(),
89 format!("The json feature is not enabled"),
90 )),
91 #[cfg(feature = "yaml")]
92 "yaml" => Ok(device_driver_generation::transform_yaml(
93 &file_contents,
94 &input.device_name.to_string(),
95 )),
96 #[cfg(not(feature = "yaml"))]
97 "yaml" => Err(syn::Error::new(
98 Span::call_site(),
99 format!("The yaml feature is not enabled"),
100 )),
101 #[cfg(feature = "toml")]
102 "toml" => Ok(device_driver_generation::transform_toml(
103 &file_contents,
104 &input.device_name.to_string(),
105 )),
106 #[cfg(not(feature = "toml"))]
107 "toml" => Err(syn::Error::new(
108 Span::call_site(),
109 format!("The toml feature is not enabled"),
110 )),
111 #[cfg(feature = "dsl")]
112 "dsl" => Ok(device_driver_generation::transform_dsl(
113 syn::parse_str(&file_contents)?,
114 &input.device_name.to_string(),
115 )),
116 #[cfg(not(feature = "dsl"))]
117 "dsl" => Err(syn::Error::new(
118 Span::call_site(),
119 format!("The dsl feature is not enabled"),
120 )),
121 unknown => Err(syn::Error::new(
122 Span::call_site(),
123 format!("Unknown manifest file extension: '{unknown}'"),
124 )),
125 }
126 })();
127
128 match result {
129 Ok(tokens) => tokens.parse().unwrap(),
130 Err(e) => e.into_compile_error().into(),
131 }
132 }
133 }
134}
135
136struct Input {
137 device_name: Ident,
138 generation_type: GenerationType,
139}
140
141enum GenerationType {
142 Dsl(proc_macro2::TokenStream),
143 Manifest(LitStr),
144}
145
146impl syn::parse::Parse for Input {
147 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
148 input.parse::<kw::device_name>()?;
149 input.parse::<syn::Token![:]>()?;
150 let device_name = input.parse()?;
151 input.parse::<syn::Token![,]>()?;
152
153 let look = input.lookahead1();
154
155 if look.peek(kw::dsl) {
156 input.parse::<kw::dsl>()?;
157 input.parse::<syn::Token![:]>()?;
158
159 let braced;
160 braced!(braced in input);
161
162 let tokens = braced.parse()?;
163
164 Ok(Self {
165 device_name,
166 generation_type: GenerationType::Dsl(tokens),
167 })
168 } else if look.peek(kw::manifest) {
169 input.parse::<kw::manifest>()?;
170 input.parse::<syn::Token![:]>()?;
171
172 let path = input.parse()?;
173
174 Ok(Self {
175 device_name,
176 generation_type: GenerationType::Manifest(path),
177 })
178 } else {
179 Err(look.error())
180 }
181 }
182}
183
184mod kw {
185 syn::custom_keyword!(device_name);
186 syn::custom_keyword!(dsl);
187 syn::custom_keyword!(manifest);
188}