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