1use super::prelude::*;
7use adviseable::*;
8
9use OptionDetails as OD;
10
11#[derive(Debug, Copy, Clone)]
15pub enum OpContext {
16 TemplateDefinition,
20 TemplateAdhoc,
24 DriverApplicationCapture,
28 ModuleDefinition,
32 DriverApplicationPassed,
36}
37
38#[derive(Default, Debug, Clone)]
46pub struct UnprocessedOptions {
47 ts: TokenStream,
48 pub beta_enabled: Option<beta::Enabled>,
49}
50impl_to_tokens!(UnprocessedOptions, ts);
51
52#[derive(Default, Debug, Clone)]
54pub struct DdOptions {
55 pub dbg: bool,
56 pub driver_kind: Option<DdOptVal<ExpectedDriverKind>>,
57 pub expect_target: Option<DdOptVal<check::Target>>,
58 pub beta_enabled: Option<beta::Enabled>,
59}
60
61#[derive(Debug)]
63struct DdOption {
64 pub kw_span: Span,
65 pub od: OptionDetails,
66}
67
68#[derive(Debug, Clone)]
70#[allow(non_camel_case_types)] enum OptionDetails {
72 dbg,
73 For(DdOptVal<ExpectedDriverKind>),
74 expect(DdOptVal<check::Target>),
75 beta_deftly(beta::Enabled),
76}
77
78#[derive(Debug, Clone, Copy)]
83pub struct DdOptVal<V> {
84 pub value: V,
85 pub span: Span,
86}
87
88pub trait DdOptValDescribable {
90 const DESCRIPTION: &'static str;
91}
92
93#[derive(Debug, Clone, Copy, Eq, PartialEq, EnumString, Display)]
100#[strum(serialize_all = "snake_case")]
101pub enum ExpectedDriverKind {
102 Struct,
103 Enum,
104 Union,
105}
106
107#[derive(Debug, Copy, Clone)]
108pub struct OpCompatVersions {
109 major: OpCompatVersionNumber,
114
115 minor: OpCompatVersionNumber,
121
122 span: Span,
124}
125type OpCompatVersionNumber = u32;
126impl OpCompatVersions {
127 pub fn ours() -> Self {
128 OpCompatVersions {
129 major: 1,
130 minor: 0,
131 span: Span::call_site(),
132 }
133 }
134}
135
136impl DdOptValDescribable for ExpectedDriverKind {
137 const DESCRIPTION: &'static str = "expected driver kind (in `for` option)";
138}
139
140impl OpContext {
143 fn allowed(self, option: &DdOption) -> syn::Result<()> {
144 use OpContext as OC;
145 match self {
146 OC::ModuleDefinition => {
147 return match &option.od {
148 OD::beta_deftly(..) => Ok(()),
149 _other => Err(option.kw_span.error(
150 "only beta_deftly is allowed in module definitions",
151 )),
152 }
153 }
154 _other => {}
155 }
156 match &option.od {
157 OD::dbg => return Ok(()),
158 OD::expect(v) => return check::check_expect_opcontext(v, self),
159 OD::For(..) => {}
160 OD::beta_deftly(..) => {}
161 }
162 match self {
163 OC::TemplateDefinition => Ok(()),
164 OC::TemplateAdhoc => Ok(()),
165 OC::DriverApplicationCapture | OC::DriverApplicationPassed => {
166 Err(option.kw_span.error(
167 "this derive-deftly option is only supported in templates",
168 ))
169 }
170 OC::ModuleDefinition => panic!("handled earlier"),
171 }
172 }
173
174 fn parse_versions(
175 self,
176 input: ParseStream,
177 ) -> syn::Result<OpCompatVersions> {
178 use OpContext as OC;
179 let ours = OpCompatVersions::ours();
180 let got = match self {
181 OC::TemplateDefinition
182 | OC::TemplateAdhoc
183 | OC::ModuleDefinition
184 | OC::DriverApplicationCapture => ours,
185 OC::DriverApplicationPassed => input.parse()?,
186 };
187 if got.major != ours.major {
188 return Err(got.error(format_args!(
189 "Incompatible major version for AOPTIONS (driver {}, template/engine {})",
190 got.major,
191 ours.major,
192 )));
193 }
194 Ok(got)
195 }
196}
197
198impl ToTokens for OpCompatVersions {
199 fn to_tokens(&self, out: &mut TokenStream) {
200 let OpCompatVersions { major, minor, span } = OpCompatVersions::ours();
201 out.extend(quote_spanned! {span=> #major #minor });
202 }
203}
204
205impl Parse for OpCompatVersions {
206 fn parse(input: ParseStream) -> syn::Result<Self> {
207 let number = move || {
208 let lit: syn::LitInt = input.parse()?;
209 Ok::<_, syn::Error>((lit.span(), lit.base10_parse()?))
210 };
211 let (span, major) = number()?;
212 let (_, minor) = number()?;
213 Ok(OpCompatVersions { major, minor, span })
214 }
215}
216
217fn continue_options(input: ParseStream) -> Option<Lookahead1> {
218 if input.is_empty() {
219 return None;
220 }
221 let la = input.lookahead1();
222 if la.peek(Token![:]) || la.peek(Token![=]) {
223 return None;
224 }
225 Some(la)
226}
227
228impl UnprocessedOptions {
229 pub fn parse(
230 input: ParseStream,
231 opcontext: OpContext,
232 ) -> syn::Result<Self> {
233 let mut beta_enabled = None;
234
235 DdOption::parse_several(&input.fork(), opcontext, |opt| {
237 match &opt.od {
238 OD::beta_deftly(be) => beta_enabled = Some(*be),
239 _ => {}
240 };
241 Ok(())
242 })?;
243
244 let mut out = TokenStream::new();
246 while continue_options(input).is_some() {
247 let tt: TokenTree = input.parse()?;
248 out.extend([tt]);
249 }
250 Ok(UnprocessedOptions {
251 ts: out,
252 beta_enabled,
253 })
254 }
255}
256
257impl DdOptions {
258 pub fn parse_update(
259 &mut self,
260 input: ParseStream,
261 opcontext: OpContext,
262 ) -> syn::Result<()> {
263 DdOption::parse_several(input, opcontext, |option| {
264 self.update_from_option(option)
265 })
266 }
267}
268
269impl DdOption {
270 fn parse_several(
275 input: ParseStream,
276 opcontext: OpContext,
277 mut each: impl FnMut(DdOption) -> syn::Result<()>,
278 ) -> syn::Result<()> {
279 let _versions = opcontext
280 .parse_versions(input)
281 .map_err(advise_incompatibility)?;
282
283 while let Some(la) = continue_options(input) {
284 if !la.peek(Ident::peek_any) {
285 return Err(la.error());
286 }
287 let option = input.parse()?;
288 opcontext.allowed(&option)?;
289 each(option)?;
290
291 let la = if let Some(la) = continue_options(input) {
292 la
293 } else {
294 break;
295 };
296 if !la.peek(Token![,]) {
297 return Err(la.error());
298 }
299 let _: Token![,] = input.parse()?;
300 }
301 Ok(())
302 }
303}
304
305impl Parse for DdOption {
306 fn parse(input: ParseStream) -> syn::Result<Self> {
307 let kw: IdentAny = input.parse()?;
308
309 let from_od = |od| {
310 Ok(DdOption {
311 kw_span: kw.span(),
312 od,
313 })
314 };
315
316 macro_rules! keyword { { $($args:tt)* } => {
318 keyword_general! { kw from_od OD; $($args)* }
319 } }
320
321 keyword! { dbg }
322 keyword! { "for": For(input.parse()?) }
323 keyword! { expect(input.parse()?) }
324 keyword! { beta_deftly(beta::Enabled::new_for_dd_option(kw.span())?) }
325
326 Err(kw.error("unknown derive-deftly option"))
327 }
328}
329
330impl<V: FromStr + DdOptValDescribable> Parse for DdOptVal<V> {
331 fn parse(input: ParseStream) -> syn::Result<Self> {
332 let kw: IdentAny = input.parse()?;
333 let value = kw.to_string().parse().map_err(|_| {
334 kw.error(format_args!("unknown value for {}", V::DESCRIPTION))
335 })?;
336 let span = kw.span();
337 Ok(DdOptVal { value, span })
338 }
339}
340
341impl UnprocessedOptions {
344 #[allow(dead_code)] pub fn is_empty(&self) -> bool {
346 self.ts.is_empty()
347 }
348}
349
350impl DdOptions {
351 fn update_from_option(&mut self, option: DdOption) -> syn::Result<()> {
355 fn store<V>(
356 already: &mut Option<DdOptVal<V>>,
357 new: DdOptVal<V>,
358 ) -> syn::Result<()>
359 where
360 V: PartialEq + DdOptValDescribable,
361 {
362 match already {
363 Some(already) if already.value == new.value => Ok(()),
364 Some(already) => {
365 Err([(already.span, "first"), (new.span, "second")].error(
366 format_args!(
367 "contradictory values for {}",
368 V::DESCRIPTION,
369 ),
370 ))
371 }
372 None => {
373 *already = Some(new);
374 Ok(())
375 }
376 }
377 }
378
379 Ok(match option.od {
380 OD::dbg => self.dbg = true,
381 OD::expect(spec) => store(&mut self.expect_target, spec)?,
382 OD::For(spec) => store(&mut self.driver_kind, spec)?,
383 OD::beta_deftly(be) => self.beta_enabled = Some(be),
384 })
385 }
386}