Skip to main content

latexml_core/definition/
argument.rs

1use std::{
2  borrow::Cow,
3  fmt::{self, Display},
4  rc::Rc,
5};
6
7use crate::{
8  Locator,
9  alignment::template::Template,
10  common::{
11    dimension::Dimension, error::Result, float::Float, glue::Glue, mudimension::MuDimension,
12    muglue::MuGlue, number::Number, numeric_ops::NumericOps, object::Object, store::Stored,
13  },
14  definition::{Digested, register::RegisterValue},
15  keyvals::KeyVals,
16  token::Token,
17  tokens::{NO_BORROWED_TOKENS, Tokens},
18};
19
20#[derive(Debug, Clone, Default)]
21pub enum ArgWrap {
22  Token(Token),
23  Tokens(Tokens),
24  Number(Number),
25  Float(Float),
26  Dimension(Dimension),
27  Glue(Glue),
28  MuGlue(MuGlue),
29  MuDimension(MuDimension),
30  KV(Box<KeyVals>),
31  AlignmentTemplate(Box<Template>),
32  Pair(crate::common::pair::Pair),
33  // TODO: what do we do with this custom case? feels iffy
34  RegisterDefinition(Box<(Token, Vec<ArgWrap>)>),
35  #[default]
36  None,
37}
38
39impl Display for ArgWrap {
40  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41    match self {
42      ArgWrap::Token(t) => write!(f, "{t}"),
43      ArgWrap::Tokens(ts) => write!(f, "{ts}"),
44      ArgWrap::Number(n) => write!(f, "{n}"),
45      ArgWrap::Float(fl) => write!(f, "{fl}"),
46      ArgWrap::Dimension(d) => write!(f, "{d}"),
47      ArgWrap::Glue(gl) => write!(f, "{gl}"),
48      ArgWrap::MuGlue(mugl) => write!(f, "{mugl}"),
49      ArgWrap::MuDimension(mudim) => write!(f, "{mudim}"),
50      ArgWrap::KV(kv) => write!(f, "{kv}"),
51      ArgWrap::AlignmentTemplate(at) => write!(f, "{at}"),
52      ArgWrap::Pair(p) => write!(f, "{p}"),
53      ArgWrap::RegisterDefinition(dbox) => write!(f, "({},{:?})", dbox.0, dbox.1),
54      ArgWrap::None => write!(f, "None"),
55    }
56  }
57}
58
59impl Object for ArgWrap {
60  fn get_locator(&self) -> Option<Locator> {
61    // Most argument wrappers carry no intrinsic source locator; a few delegate
62    // to their contained value. (Patterns qualified with `ArgWrap::` so the
63    // body's `None` is unambiguously `Option::None`, not the `ArgWrap::None`
64    // variant.)
65    match self {
66      ArgWrap::Glue(t) => t.get_locator(),
67      ArgWrap::MuGlue(t) => t.get_locator(),
68      ArgWrap::MuDimension(t) => t.get_locator(),
69      ArgWrap::KV(kv) => kv.get_locator(),
70      _ => None,
71    }
72  }
73  fn be_digested(self) -> Result<Digested> {
74    use ArgWrap::*;
75    // TODO: Should we just "do nothing" for the None cases, instead of panicking?
76    match self {
77      Token(t) => t.be_digested(),
78      Tokens(t) => t.be_digested(),
79      Number(t) => t.be_digested(),
80      Float(t) => t.be_digested(),
81      Dimension(t) => t.be_digested(),
82      Glue(t) => t.be_digested(),
83      MuGlue(t) => t.be_digested(),
84      MuDimension(t) => t.be_digested(),
85      KV(kv) => kv.be_digested(),
86      Pair(p) => p.be_digested(),
87      None => Ok(Digested::default()),
88      AlignmentTemplate(_) => Ok(Digested::default()), // templates don't digest directly
89      RegisterDefinition(_) => Ok(Digested::default()), // register defs don't digest directly
90    }
91  }
92  fn revert(&self) -> Result<Tokens> {
93    use ArgWrap::*;
94    match self {
95      Token(t) => Ok(Tokens!(*t)),
96      Tokens(t) => Ok(t.clone()),
97      Number(t) => t.revert(),
98      Float(t) => t.revert(),
99      Dimension(t) => t.revert(),
100      Glue(t) => t.revert(),
101      MuGlue(t) => t.revert(),
102      MuDimension(t) => t.revert(),
103      KV(kv) => kv.revert(),
104      Pair(p) => p.revert(),
105      None => Ok(Tokens!()),
106      AlignmentTemplate(_) => Ok(Tokens!()), // templates don't revert directly
107      RegisterDefinition(_) => Ok(Tokens!()), // register defs don't revert directly
108    }
109  }
110}
111
112impl ArgWrap {
113  pub fn is_some(&self) -> bool { !self.is_none() }
114  pub fn is_none(&self) -> bool { matches!(self, ArgWrap::None) }
115  pub fn is_tokens(&self) -> bool { matches!(self, ArgWrap::Tokens(_) | ArgWrap::Token(_)) }
116
117  /// Zero-alloc equivalent of `self.to_string() == target` for keyword
118  /// checks in DefMacro bodies. Forwards to `Token::with_str` /
119  /// `Tokens::eq_text` for the Token/Tokens variants (covering ~all
120  /// keyword-argument checks in practice); for other variants falls
121  /// back to `to_string() == target` since the Display impl allocates
122  /// anyway. See `Tokens::eq_text` for the walk semantics.
123  pub fn eq_text(&self, target: &str) -> bool {
124    match self {
125      ArgWrap::Tokens(tks) => tks.eq_text(target),
126      ArgWrap::Token(t) => t.with_str(|s| s == target),
127      other => other.to_string() == target,
128    }
129  }
130
131  /// Zero-alloc `self.to_string().starts_with(prefix)` for Tokens/Token
132  /// variants; falls back to full to_string for others.
133  pub fn starts_with_text(&self, prefix: &str) -> bool {
134    match self {
135      ArgWrap::Tokens(tks) => tks.starts_with_text(prefix),
136      ArgWrap::Token(t) => t.with_str(|s| s.starts_with(prefix)),
137      other => other.to_string().starts_with(prefix),
138    }
139  }
140  pub fn mut_tokens(&mut self) -> Option<&mut Tokens> {
141    match self {
142      ArgWrap::Tokens(tks) => Some(tks),
143      _ => None,
144    }
145  }
146  pub fn owned_tokens(self) -> Option<Tokens> {
147    match self {
148      ArgWrap::Tokens(tks) => Some(tks),
149      ArgWrap::Token(t) => Some(Tokens::new(vec![t])),
150      ArgWrap::Number(n) => {
151        let tks: Tokens = n.into();
152        Some(tks)
153      },
154      ArgWrap::KV(kv) => kv.revert().ok(),
155      _ => None,
156    }
157  }
158
159  pub fn try_to_token(self) -> Result<Token> {
160    match self {
161      ArgWrap::Token(t) => Ok(t),
162      ArgWrap::Tokens(tks) => {
163        let mut list = tks.unlist();
164        if list.is_empty() {
165          Err("try_to_token: empty Tokens".into())
166        } else {
167          Ok(list.remove(0))
168        }
169      },
170      _ => Err(
171        s!(
172          "Hard assumption for Token argument failed. Got instead: {:?}",
173          self
174        )
175        .into(),
176      ),
177    }
178  }
179  pub fn expected_token(self) -> Token {
180    match self.try_to_token() {
181      Ok(t) => t,
182      Err(e) => panic!("{e}"),
183    }
184  }
185  pub fn undigested(self) -> Option<Digested> {
186    if self.is_none() {
187      None
188    } else {
189      Some(Digested::from(self.owned_tokens().unwrap_or_default()))
190    }
191  }
192
193  /// Convert to an attribute string value (for constructor templates)
194  /// Uses attribute_format for dimensions (1 decimal place) matching Perl
195  pub fn to_attribute(&self) -> String {
196    match self {
197      ArgWrap::Dimension(d) => d.to_attribute(),
198      ArgWrap::Glue(g) => g.to_attribute(),
199      ArgWrap::MuGlue(mg) => mg.to_attribute(),
200      ArgWrap::MuDimension(md) => md.to_attribute(),
201      _ => self.to_string(),
202    }
203  }
204
205  pub fn as_tokens(&self) -> Result<Option<Cow<'_, Tokens>>> {
206    use ArgWrap::*;
207    let result = match self {
208      Token(t) => Some(Cow::Owned(Tokens!(*t))),
209      Tokens(tks) => Some(Cow::Borrowed(tks)),
210      Number(_) | Float(_) | Dimension(_) | Glue(_) | MuGlue(_) | MuDimension(_) | KV(_)
211      | Pair(_) => Some(Cow::Owned(self.revert()?)),
212      None => Some(Cow::Borrowed(NO_BORROWED_TOKENS)),
213      AlignmentTemplate(_) | RegisterDefinition(_) => Some(Cow::Owned(Tokens!())),
214    };
215    Ok(result)
216  }
217
218  pub fn value_of(&self) -> i64 {
219    use ArgWrap::*;
220    match self {
221      Number(v) => v.value_of(),
222      Float(v) => v.value_of(),
223      Dimension(v) => v.value_of(),
224      Glue(v) => v.value_of(),
225      MuGlue(v) => v.value_of(),
226      MuDimension(v) => v.value_of(),
227      None => 0,
228      _ => panic!("ArgWrap::value_of not (yet?) defined on {:?}", self),
229    }
230  }
231  pub fn value_f64(&self) -> f64 {
232    use ArgWrap::*;
233    match self {
234      Number(v) => v.value_f64(),
235      Float(v) => v.value_f64(),
236      Dimension(v) => v.value_f64(),
237      Glue(v) => v.value_f64(),
238      MuGlue(v) => v.value_f64(),
239      MuDimension(v) => v.value_f64(),
240      None => 0.0,
241      _ => panic!("ArgWrap::value_of not (yet?) defined on {:?}", self),
242    }
243  }
244
245  pub fn try_to_number(self) -> Result<Number> {
246    use ArgWrap::*;
247    match self {
248      Number(v) => Ok(v),
249      Token(t) => Ok(t.to_number()),
250      Tokens(tks) => Ok(tks.to_number()),
251      None => Ok(crate::common::number::Number::new(0)),
252      _ => Err(format!("ArgWrap::to_number not (yet?) defined on {:?}", self).into()),
253    }
254  }
255  pub fn expect_number(self) -> Number {
256    match self.try_to_number() {
257      Ok(v) => v,
258      Err(e) => panic!("{e}"),
259    }
260  }
261
262  pub fn try_to_dimension(self) -> Result<Dimension> {
263    use ArgWrap::*;
264    match self {
265      None => Ok(crate::common::dimension::Dimension::default()),
266      Number(v) => Ok(v.into()),
267      Dimension(v) => Ok(v),
268      Token(t) => Ok(t.to_dimension()),
269      Tokens(tks) => Ok(tks.to_dimension()),
270      _ => Err(format!("ArgWrap::to_dimension not (yet?) defined on {:?}", self).into()),
271    }
272  }
273  pub fn expect_dimension(self) -> Dimension {
274    match self.try_to_dimension() {
275      Ok(d) => d,
276      Err(e) => panic!("{e}"),
277    }
278  }
279  pub fn try_to_mu_dimension(self) -> Result<MuDimension> {
280    use ArgWrap::*;
281    match self {
282      // Number(v) => Ok(v.into()), // ???
283      MuDimension(v) => Ok(v),
284      Token(t) => Ok(t.to_mu_dimension()),
285      Tokens(tks) => Ok(tks.to_mu_dimension()),
286      _ => Err(format!("ArgWrap::to_dimension not (yet?) defined on {:?}", self).into()),
287    }
288  }
289  pub fn expect_mu_dimension(self) -> MuDimension {
290    match self.try_to_mu_dimension() {
291      Ok(d) => d,
292      Err(e) => panic!("{e}"),
293    }
294  }
295
296  pub fn try_to_glue(self) -> Result<Glue> {
297    use ArgWrap::*;
298    match self {
299      Glue(v) => Ok(v),
300      Number(v) => Ok(v.into()),
301      Token(t) => Ok(t.to_glue()),
302      Tokens(tks) => Ok(tks.to_glue()),
303      _ => Err(format!("ArgWrap::try_to_glue not (yet?) defined on {:?}", self).into()),
304    }
305  }
306  pub fn expect_glue(self) -> Glue {
307    match self.try_to_glue() {
308      Ok(d) => d,
309      Err(e) => panic!("{e}"),
310    }
311  }
312
313  pub fn try_to_mu_glue(self) -> Result<MuGlue> {
314    use ArgWrap::*;
315    match self {
316      MuGlue(v) => Ok(v),
317      Number(v) => Ok(v.into()),
318      Token(t) => Ok(t.to_mu_glue()),
319      Tokens(tks) => Ok(tks.to_mu_glue()),
320      _ => Err(format!("ArgWrap::try_to_mu_glue not (yet?) defined on {:?}", self).into()),
321    }
322  }
323  pub fn expect_mu_glue(self) -> MuGlue {
324    match self.try_to_mu_glue() {
325      Ok(d) => d,
326      Err(e) => panic!("{e}"),
327    }
328  }
329  pub fn to_mu_dimension(self) -> MuDimension {
330    use ArgWrap::*;
331    match self {
332      MuDimension(v) => v,
333      Token(t) => t.to_mu_dimension(),
334      Tokens(tks) => tks.to_mu_dimension(),
335      _ => panic!("ArgWrap::to_mu_dimension not (yet?) defined on {self:?}"),
336    }
337  }
338  pub fn to_glue(self) -> Glue {
339    use ArgWrap::*;
340    match self {
341      Glue(v) => v,
342      Token(t) => t.to_glue(),
343      Tokens(tks) => tks.to_glue(),
344      _ => panic!("ArgWrap::to_glue not (yet?) defined on {:?}", self),
345    }
346  }
347  pub fn to_mu_glue(self) -> MuGlue {
348    use ArgWrap::*;
349    match self {
350      MuGlue(v) => v,
351      Token(t) => t.to_mu_glue(),
352      Tokens(tks) => tks.to_mu_glue(),
353      _ => panic!("ArgWrap::to_mu_glue not (yet?) defined on {:?}", self),
354    }
355  }
356  pub fn expected_keyvals(self) -> KeyVals {
357    match self.try_to_keyvals() {
358      Ok(t) => t,
359      Err(e) => panic!("{e}"),
360    }
361  }
362
363  pub fn try_to_keyvals(self) -> Result<KeyVals> {
364    use ArgWrap::*;
365    match self {
366      KV(v) => Ok(*v),
367      Tokens(tks) => tks.to_keyvals(),
368      None => Ok(KeyVals::default()),
369      _ => panic!("ArgWrap::to_keyvals not (yet?) defined on {:?}", self),
370    }
371  }
372
373  pub fn try_to_float(self) -> Result<Float> {
374    use ArgWrap::*;
375    match self {
376      Float(v) => Ok(v),
377      Token(t) => Ok(t.to_float()),
378      Tokens(tks) => Ok(tks.to_float()),
379      None => Ok(crate::common::float::Float::default()),
380      _ => Err(format!("ArgWrap::to_float not (yet?) defined on {:?}", self).into()),
381    }
382  }
383  pub fn expect_float(self) -> Float {
384    match self.try_to_float() {
385      Ok(v) => v,
386      Err(e) => panic!("{e}"),
387    }
388  }
389
390  pub fn unlist(self) -> Vec<Token> {
391    match self {
392      ArgWrap::Tokens(tks) => tks.unlist(),
393      ArgWrap::Token(t) => vec![t],
394      ArgWrap::Number(n) => {
395        let tks: Tokens = n.into();
396        tks.unlist()
397      },
398      ArgWrap::None => Vec::new(),
399      _ => self.revert().unwrap_or_default().unlist(),
400    }
401  }
402
403  /// Borrow the token slice backing an ArgWrap::Tokens, falling back
404  /// to an owned `Vec<Token>` for the other variants. Avoids a full
405  /// Tokens clone in the common `ArgWrap::Tokens(_)` case.
406  pub fn unlist_cow(&self) -> Cow<'_, [Token]> {
407    match self {
408      ArgWrap::Tokens(tks) => Cow::Borrowed(tks.unlist_ref()),
409      ArgWrap::None => Cow::Borrowed(&[]),
410      _ => Cow::Owned(self.clone().unlist()),
411    }
412  }
413
414  pub fn is_empty(&self) -> bool {
415    use ArgWrap::*;
416    match self {
417      None => true,
418      Tokens(tks) => tks.is_empty(),
419      _ => false,
420    }
421  }
422}
423
424impl From<ArgWrap> for Option<Tokens> {
425  fn from(t: ArgWrap) -> Option<Tokens> { t.owned_tokens() }
426}
427
428impl From<ArgWrap> for Tokens {
429  fn from(t: ArgWrap) -> Tokens {
430    match t.owned_tokens() {
431      Some(tks) => tks,
432      None => Tokens!(),
433    }
434  }
435}
436
437impl<T> From<Option<T>> for ArgWrap
438where T: Into<ArgWrap> + Sized
439{
440  fn from(t: Option<T>) -> Self {
441    match t {
442      Some(t) => t.into(),
443      None => ArgWrap::None,
444    }
445  }
446}
447
448impl From<Stored> for Result<ArgWrap> {
449  // A maintenance detail here is that whenever a Parameter can read a new concrete type of data,
450  // then ArgWrap will have a new variant, which must then be possible to store in Stored,
451  // and possible to cast back from storage into ArgWrap
452  fn from(t: Stored) -> Result<ArgWrap> {
453    Ok(match t {
454      Stored::Tokens(t) => ArgWrap::Tokens(t),
455      Stored::Token(t) => ArgWrap::Token(t),
456      Stored::MuDimension(d) => ArgWrap::MuDimension(d),
457      Stored::Glue(g) => ArgWrap::Glue(g),
458      Stored::MuGlue(g) => ArgWrap::MuGlue(g),
459      Stored::Number(n) => ArgWrap::Number(n),
460      Stored::Float(f) => ArgWrap::Float(f),
461      Stored::Dimension(d) => ArgWrap::Dimension(d),
462      // we could just map "_" to None, but it is safer to enumerate, to avoid missing
463      // meaningful cases.
464      Stored::None => ArgWrap::None,
465      Stored::Mouth(_)
466      | Stored::Primitive(_)
467      | Stored::Bool(_)
468      | Stored::Parameter(_)
469      | Stored::MathPrimitive(_)
470      | Stored::Conditional(_)
471      | Stored::Constructor(_)
472      | Stored::Charcode(_)
473      | Stored::Expandable(_)
474      | Stored::Ligature(_)
475      | Stored::HashStored(_)
476      | Stored::HashTagData(_)
477      | Stored::FrontmatterRaw(_)
478      | Stored::HashString(_)
479      | Stored::Digested(_)
480      | Stored::FontDirective(_)
481      | Stored::Register(_)
482      | Stored::Rewrite(_)
483      | Stored::Stash(_)
484      | Stored::Fontmap(_)
485      | Stored::Int(_)
486      | Stored::String(_)
487      | Stored::Strings(_)
488      | Stored::Node(_)
489      | Stored::IfFrame(_)
490      | Stored::Font(_)
491      | Stored::Reversion(_)
492      | Stored::Catcode(_)
493      | Stored::Locator(_)
494      | Stored::VecDequeStored(_)
495      | Stored::VecDigested(_)
496      | Stored::Chars(_)
497      | Stored::KeyVal(_)
498      | Stored::KeyVals(_)
499      | Stored::Template(_) => {
500        Error!(
501          "stored",
502          "type",
503          "Failed to cast to argument; Found stored:",
504          t
505        );
506        ArgWrap::None
507      },
508    })
509  }
510}
511
512impl From<ArgWrap> for Result<Stored> {
513  fn from(t: ArgWrap) -> Result<Stored> {
514    Ok(match t {
515      ArgWrap::Tokens(ts) => Stored::Tokens(ts),
516      ArgWrap::Token(ts) => Stored::Token(ts),
517      ArgWrap::Dimension(v) => Stored::Dimension(v),
518      ArgWrap::MuDimension(v) => Stored::MuDimension(v),
519      ArgWrap::Number(n) => Stored::Number(n),
520      ArgWrap::Glue(v) => Stored::Glue(v),
521      ArgWrap::MuGlue(v) => Stored::MuGlue(v),
522      ArgWrap::Float(v) => Stored::Float(v),
523      ArgWrap::None => Stored::None,
524      ArgWrap::AlignmentTemplate(t) => Stored::Template(Rc::new(*t)),
525      ArgWrap::Pair(_) => Stored::None, // TODO: add Stored::Pair
526      ArgWrap::KV(_) | ArgWrap::RegisterDefinition(_) => {
527        Error!(
528          "stored",
529          "type",
530          "Failed to cast into Stored (no equivalent). Extend Stored if intended.",
531          t
532        );
533        Stored::None
534      },
535    })
536  }
537}
538
539impl From<Token> for ArgWrap {
540  fn from(t: Token) -> Self { ArgWrap::Token(t) }
541}
542
543impl From<Tokens> for ArgWrap {
544  fn from(t: Tokens) -> Self { ArgWrap::Tokens(t) }
545}
546
547impl From<KeyVals> for ArgWrap {
548  fn from(kv: KeyVals) -> Self { ArgWrap::KV(Box::new(kv)) }
549}
550
551impl From<Number> for ArgWrap {
552  fn from(t: Number) -> Self { ArgWrap::Number(t) }
553}
554
555impl From<Float> for ArgWrap {
556  fn from(t: Float) -> Self { ArgWrap::Float(t) }
557}
558
559impl From<Dimension> for ArgWrap {
560  fn from(t: Dimension) -> Self { ArgWrap::Dimension(t) }
561}
562
563impl From<MuDimension> for ArgWrap {
564  fn from(t: MuDimension) -> Self { ArgWrap::MuDimension(t) }
565}
566
567impl From<Glue> for ArgWrap {
568  fn from(t: Glue) -> Self { ArgWrap::Glue(t) }
569}
570
571impl From<MuGlue> for ArgWrap {
572  fn from(t: MuGlue) -> Self { ArgWrap::MuGlue(t) }
573}
574
575impl From<()> for ArgWrap {
576  fn from(_: ()) -> Self { ArgWrap::default() }
577}
578impl From<RegisterValue> for ArgWrap {
579  fn from(t: RegisterValue) -> Self {
580    match t {
581      RegisterValue::Number(n) => ArgWrap::Number(n),
582      RegisterValue::Dimension(n) => ArgWrap::Dimension(n),
583      RegisterValue::Glue(n) => ArgWrap::Glue(n),
584      RegisterValue::Token(n) => ArgWrap::Token(n),
585      RegisterValue::Tokens(n) => ArgWrap::Tokens(n),
586      RegisterValue::MuGlue(n) => ArgWrap::MuGlue(n),
587      RegisterValue::MuDimension(n) => ArgWrap::MuDimension(n),
588      RegisterValue::Pair(p) => ArgWrap::Pair(p),
589    }
590  }
591}
592impl From<Template> for ArgWrap {
593  fn from(t: Template) -> Self { ArgWrap::AlignmentTemplate(Box::new(t)) }
594}
595
596impl TryFrom<ArgWrap> for Number {
597  type Error = crate::common::error::Error;
598  fn try_from(aw: ArgWrap) -> Result<Number> { aw.try_to_number() }
599}
600impl TryFrom<ArgWrap> for Dimension {
601  type Error = crate::common::error::Error;
602  fn try_from(aw: ArgWrap) -> Result<Dimension> { aw.try_to_dimension() }
603}
604impl TryFrom<ArgWrap> for MuDimension {
605  type Error = crate::common::error::Error;
606  fn try_from(aw: ArgWrap) -> Result<MuDimension> { aw.try_to_mu_dimension() }
607}
608impl TryFrom<ArgWrap> for Glue {
609  type Error = crate::common::error::Error;
610  fn try_from(aw: ArgWrap) -> Result<Glue> { aw.try_to_glue() }
611}
612impl TryFrom<ArgWrap> for MuGlue {
613  type Error = crate::common::error::Error;
614  fn try_from(aw: ArgWrap) -> Result<MuGlue> { aw.try_to_mu_glue() }
615}
616impl TryFrom<ArgWrap> for Float {
617  type Error = crate::common::error::Error;
618  fn try_from(aw: ArgWrap) -> Result<Float> { aw.try_to_float() }
619}
620
621impl TryFrom<ArgWrap> for Token {
622  type Error = crate::common::error::Error;
623  fn try_from(aw: ArgWrap) -> Result<Token> { aw.try_to_token() }
624}
625
626impl TryFrom<ArgWrap> for KeyVals {
627  type Error = crate::common::error::Error;
628  fn try_from(aw: ArgWrap) -> Result<KeyVals> { aw.try_to_keyvals() }
629}
630
631impl From<ArgWrap> for Option<KeyVals> {
632  fn from(aw: ArgWrap) -> Option<KeyVals> {
633    match aw {
634      ArgWrap::KV(kv) => Some(*kv),
635      _ => None,
636    }
637  }
638}
639
640impl From<ArgWrap> for Template {
641  fn from(aw: ArgWrap) -> Template {
642    match aw {
643      ArgWrap::AlignmentTemplate(t) => *t,
644      other => panic!("illegal auto-cast to alignment::Template on {other:?}"),
645    }
646  }
647}
648
649#[cfg(test)]
650mod tests {
651  use super::*;
652
653  #[test]
654  fn argwrap_default_is_none() {
655    let a = ArgWrap::default();
656    assert!(a.is_none());
657    assert!(!a.is_some());
658  }
659
660  #[test]
661  fn argwrap_number_is_some() {
662    let a = ArgWrap::Number(Number::new(42));
663    assert!(a.is_some());
664    assert!(!a.is_none());
665  }
666
667  #[test]
668  fn argwrap_is_tokens_for_token_and_tokens() {
669    let a = ArgWrap::Tokens(Tokens::new(vec![]));
670    assert!(a.is_tokens());
671    let b = ArgWrap::Token(Token::default());
672    assert!(
673      b.is_tokens(),
674      "is_tokens is true for both Token and Tokens variants"
675    );
676    let c = ArgWrap::Number(Number::new(0));
677    assert!(!c.is_tokens());
678    let d = ArgWrap::None;
679    assert!(!d.is_tokens());
680  }
681
682  #[test]
683  fn argwrap_value_of_number() {
684    let a = ArgWrap::Number(Number::new(42));
685    assert_eq!(a.value_of(), 42);
686  }
687
688  #[test]
689  fn argwrap_value_of_dimension() {
690    let a = ArgWrap::Dimension(Dimension::new(65536));
691    assert_eq!(a.value_of(), 65536);
692  }
693
694  #[test]
695  fn argwrap_value_f64_float() {
696    let a = ArgWrap::Float(Float(3.125));
697    assert!((a.value_f64() - 3.125).abs() < 1e-6);
698  }
699
700  #[test]
701  #[allow(non_snake_case)]
702  fn argwrap_display_none_is_the_word_None() {
703    // Discovered: ArgWrap::None's Display writes "None" (the variant
704    // name), not empty string. Capital-N in fn name preserves the
705    // literal distinction — don't "fix" to lowercase.
706    let a = ArgWrap::None;
707    assert_eq!(format!("{a}"), "None");
708  }
709
710  #[test]
711  fn argwrap_display_number() {
712    let a = ArgWrap::Number(Number::new(42));
713    assert_eq!(format!("{a}"), "42");
714  }
715
716  #[test]
717  fn argwrap_try_to_number_from_number() {
718    let a = ArgWrap::Number(Number::new(42));
719    let n = a.try_to_number().unwrap();
720    assert_eq!(n.value_of(), 42);
721  }
722
723  #[test]
724  fn argwrap_expect_number_from_number() {
725    let a = ArgWrap::Number(Number::new(42));
726    assert_eq!(a.expect_number().value_of(), 42);
727  }
728
729  #[test]
730  fn argwrap_owned_tokens_from_none_is_none() {
731    let a = ArgWrap::None;
732    assert!(a.owned_tokens().is_none());
733  }
734}