1use core::slice::Iter;
2use std::{borrow::Cow, fmt};
3
4use libxml::tree::Node;
5use rustc_hash::FxHashMap as HashMap;
6
7use super::keyval::{has_keyval, keyval_get, keyval_qname};
8use crate::{
9 BoxOps, Digested, NO_PROPERTIES,
10 common::{
11 arena::SymHashMap,
12 error::{emit_warn, *},
13 font::Font,
14 object::Object,
15 store::Stored,
16 },
17 definition::argument::ArgWrap,
18 document::Document,
19 gullet::{self, ExpansionLevel},
20 state,
21 token::{Catcode, Token},
22 tokens::Tokens,
23};
24
25#[derive(Debug, Clone)]
26struct KVData {
27 key: String,
28 value: Option<ArgWrap>,
29 use_default: bool,
30 primary_keyset: String,
31 keysets: Vec<String>,
32 digested_value: Option<Digested>,
33}
34
35#[allow(dead_code)] #[derive(Debug, Clone)]
37pub struct KeyVals {
38 prefix: String,
40 keysets: Vec<String>,
43 skip: Vec<String>,
44 set_all: bool,
45 set_internals: bool,
46 skip_missing: SkipMissing,
47 was_digested: bool,
48 hook_missing: Option<Token>,
49 tuples: Vec<KVData>,
51 cached_pairs: Vec<(String, ArgWrap)>,
52 cached_hash: HashMap<String, Vec<ArgWrap>>,
53 cached_hash_digested: HashMap<String, Vec<Digested>>,
54}
55
56impl Default for KeyVals {
57 fn default() -> Self {
58 KeyVals {
59 prefix: "KV".to_string(),
60 keysets: vec!["_anonymous_".to_string()],
61 skip: Vec::new(),
62 set_all: false,
63 set_internals: false,
64 skip_missing: SkipMissing::None,
65 was_digested: false,
66 hook_missing: None,
67 tuples: Vec::new(),
68 cached_pairs: Vec::new(),
69 cached_hash: HashMap::default(),
70 cached_hash_digested: HashMap::default(),
71 }
72 }
73}
74
75impl PartialEq for KeyVals {
76 fn eq(&self, _other: &KeyVals) -> bool {
77 false }
79}
80
81impl fmt::Display for KeyVals {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 let mut first = true;
84 for (key, value) in &self.cached_pairs {
85 if !first {
86 write!(f, ",")?;
88 }
89 write!(f, "{}={}", key, value)?;
90 first = false;
91 }
92 Ok(())
93 }
94}
95
96impl Object for KeyVals {
97 fn stringify(&self) -> String { self.to_string() }
98
99 fn be_digested(mut self) -> Result<Digested> {
100 if self.was_digested {
101 Info!(
102 "ignore",
103 "keyvals",
104 "Skipping digestion of \\setkeys as requested (did you digest a KeyVals twice?) "
105 );
106 } else {
107 crate::stomach::digest(self.set_keys_expansion())?;
108 }
109
110 for tuple in self.tuples.iter_mut() {
112 let KVData {
113 key,
114 value,
115 primary_keyset,
116 digested_value,
117 ..
118 } = tuple;
119 if digested_value.is_none() {
120 let keytype_opt = keyval_get(&keyval_qname(&self.prefix, primary_keyset, key), "type");
122 let v = if let Some(Stored::Parameter(keytype)) = keytype_opt {
123 match value.take() {
124 Some(v) => keytype.digest(v, None)?,
125 _ => None,
126 }
127 } else {
128 match value.take() {
129 Some(v) => Some(v.be_digested()?),
130 _ => None,
131 }
132 };
133 tuple.digested_value = v;
134 }
135 }
136 self.rebuild(None);
141 self.was_digested = true;
142 Ok(self.into())
143 }
144}
145
146impl BoxOps for KeyVals {
147 fn with_properties<R, FnR>(&self, caller: FnR) -> R
148 where FnR: FnOnce(&SymHashMap<Stored>) -> R {
149 caller(&NO_PROPERTIES)
150 }
151 fn get_string(&self) -> Result<Cow<'_, str>> { Ok(Cow::Owned(self.to_string())) }
152 fn set_property<T: Into<Stored>>(&mut self, _key: &str, _value: T) {
153 emit_warn(
154 "internal",
155 "keyvals",
156 "set_property on KeyVals not supported",
157 );
158 }
159 fn be_absorbed(&self, _document: &mut Document) -> Result<Vec<Node>> { Ok(Vec::new()) } fn get_font(&self) -> Result<Option<std::rc::Rc<Font>>> { Ok(None) } fn compute_size(
162 &self,
163 _options: SymHashMap<Stored>,
164 ) -> Result<(
165 crate::common::dimension::Dimension,
166 crate::common::dimension::Dimension,
167 crate::common::dimension::Dimension,
168 )> {
169 use crate::common::dimension::Dimension;
170 Ok((
171 Dimension::default(),
172 Dimension::default(),
173 Dimension::default(),
174 ))
175 }
176}
177#[derive(Debug, Clone, Default, PartialEq)]
178pub enum SkipMissing {
179 #[default]
180 None,
182 All,
184 Store(Token),
186}
187
188#[derive(Default)]
189pub struct KeyvalsConfig {
190 pub prefix: Option<String>,
191 pub keysets: Vec<String>,
192 pub set_all: bool,
193 pub set_internals: bool,
194 pub skip: Vec<String>,
195 pub skip_missing: SkipMissing,
196 pub hook_missing: Option<Token>,
197}
198
199impl KeyVals {
200 pub fn new(options: KeyvalsConfig) -> Self {
209 let KeyvalsConfig {
211 prefix,
212 mut keysets,
213 set_all,
214 set_internals,
215 skip,
216 skip_missing,
217 hook_missing,
218 } = options;
219 let prefix = prefix.unwrap_or_else(|| String::from("KV"));
220 keysets.retain(|k| !k.is_empty());
229 if keysets.is_empty() {
230 keysets = vec![String::from("_anonymous_")];
231 }
232 KeyVals {
233 prefix,
234 keysets,
235 skip,
236 set_all,
237 set_internals,
238 skip_missing,
239 hook_missing,
240 ..KeyVals::default()
241 }
242 }
243
244 fn resolve_keyval_for(&self, key: &str) -> Vec<String> {
250 let prefix = &self.prefix;
251 let allkeysets = &self.keysets;
252 let keysets: Vec<_> = self
253 .keysets
254 .iter()
255 .filter(|kset| has_keyval(prefix, kset, key))
256 .collect();
257 if keysets.is_empty() {
260 if self.skip_missing == SkipMissing::None {
261 type SeenSet = rustc_hash::FxHashSet<(String, String, String)>;
271 thread_local! {
272 static SEEN_MISSING: std::cell::RefCell<SeenSet> =
273 std::cell::RefCell::new(SeenSet::default());
274 }
275 let all_joined = allkeysets.join(",");
276 let is_new = SEEN_MISSING.with(|cell| {
277 cell
278 .borrow_mut()
279 .insert((prefix.clone(), key.to_string(), all_joined.clone()))
280 });
281 if is_new {
282 if all_joined == "Frontmatter" {
300 Info!(
301 "undefined",
302 "Encountered unknown KeyVals key",
303 s!("'{key}' with prefix '{prefix}' not defined in '{all_joined}'")
304 );
305 } else {
306 Warn!(
307 "undefined",
308 "Encountered unknown KeyVals key",
309 s!(
310 "'{key}' with prefix '{prefix}' not defined in '{all_joined}', were you perhaps using \\setkeys instead of \\setkeys*?"
311 )
312 );
313 }
314 }
315 }
316 return Vec::new();
317 }
318 if self.set_all {
321 keysets.into_iter().cloned().collect()
322 } else {
323 vec![keysets[0].clone()]
324 }
325 }
326
327 fn can_resolve_keyval_for(&self, key: &str) -> bool {
328 self
330 .keysets
331 .iter()
332 .any(|keyset| has_keyval(&self.prefix, keyset, key))
333 }
334
335 fn get_primary_keyval<'a>(&'a self, keysets: &'a [String]) -> &'a str {
337 match keysets.first() {
338 None => self.keysets[0].as_str(),
339 Some(kset) => kset.as_str(),
340 }
341 }
342
343 fn read_keyword_from(&self, close: Token) -> Result<(Tokens, Option<Token>)> {
344 let mut tokens = Vec::new();
346 let delim = &[close, T_OTHER!(","), T_OTHER!("=")];
347 gullet::skip_spaces()?;
349
350 let mut last_token = None;
351 while let Some(token) = gullet::read_x_token(None, false, None)? {
352 if token == T_CS!("\\par") {
354 continue;
355 }
356 if delim.contains(&token) {
358 last_token = Some(token);
359 break;
360 }
361 tokens.push(token);
362 }
363 Ok((Tokens::new(tokens), last_token))
365 }
366
367 pub fn get_value(&self, key: &str) -> Option<&ArgWrap> {
374 match self.cached_hash.get(key) {
377 None => None,
378 Some(value) => value.last(),
379 }
380 }
381 pub fn get_value_digested(&self, key: &str) -> Option<&Digested> {
384 match self.cached_hash_digested.get(key) {
387 None => None,
388 Some(value) => value.last(),
389 }
390 }
391
392 pub fn get_values(&self, key: &str) -> Option<&Vec<ArgWrap>> { self.cached_hash.get(key) }
394
395 pub fn get_pairs(&self) -> Iter<'_, (String, ArgWrap)> { self.cached_pairs.iter() }
397 pub fn as_flat_hash(self) -> HashMap<String, ArgWrap> {
399 let mut flat_hash = HashMap::default();
400 for (k, mut vec) in self.cached_hash {
401 if let Some(v) = vec.pop() {
402 flat_hash.insert(k, v);
403 }
404 }
405 flat_hash
406 }
407 pub fn as_hash(self) -> HashMap<String, Vec<ArgWrap>> { self.cached_hash }
409 pub fn as_hash_digested(self) -> HashMap<String, Vec<Digested>> { self.cached_hash_digested }
411 pub fn get_hash(&self) -> HashMap<String, String> {
413 let mut hashed = HashMap::default();
414 for (k, v) in &self.cached_hash {
415 hashed.insert(
416 k.clone(),
417 v.iter()
418 .map(ToString::to_string)
419 .collect::<Vec<String>>()
420 .join(""),
421 );
422 }
423 hashed
424 }
425 pub fn get_hash_digested(&self) -> HashMap<String, String> {
427 let mut hashed = HashMap::default();
428 for (k, v) in &self.cached_hash_digested {
429 hashed.insert(
430 k.clone(),
431 v.iter()
432 .map(ToString::to_string)
433 .collect::<Vec<String>>()
434 .join(""),
435 );
436 }
437 hashed
438 }
439
440 pub fn get_keyvals(&self) -> &HashMap<String, Vec<ArgWrap>> { &self.cached_hash }
442
443 pub fn has_key(&self, key: &str) -> bool { self.cached_hash.contains_key(key) }
445
446 pub fn set_keys_expansion(&self) -> Tokens {
450 let skip_keys = &self.skip;
451 let set_internals = self.set_internals;
452 let prefix = &self.prefix;
453
454 let rmmacro = match &self.skip_missing {
456 SkipMissing::Store(token) => Some(*token),
457 _ => None,
458 };
459 let hook_missing = self.hook_missing;
460
461 let mut rmtokens: Vec<Token> = Vec::new();
463 if let Some(rm) = rmmacro
464 && state::has_meaning(&rm)
465 && let Ok(expanded) = gullet::do_expand(Tokens!(rm))
466 {
467 rmtokens = expanded.unlist();
468 }
469
470 let mut tokens: Vec<Token> = Vec::new();
471
472 if set_internals {
474 let keysets_joined = self.keysets.join(",");
475 let skip_joined = self.skip.join(",");
476 tokens.push(T_CS!("\\def"));
477 tokens.push(T_CS!("\\XKV@fams"));
478 tokens.push(T_BEGIN!());
479 tokens.extend(Explode!(keysets_joined));
480 tokens.push(T_END!());
481 tokens.push(T_CS!("\\def"));
482 tokens.push(T_CS!("\\XKV@na"));
483 tokens.push(T_BEGIN!());
484 tokens.extend(Explode!(skip_joined));
485 tokens.push(T_END!());
486 }
487
488 for tuple in &self.tuples {
490 let KVData {
491 key,
492 value,
493 use_default,
494 primary_keyset,
495 keysets,
496 ..
497 } = tuple;
498
499 if skip_keys.iter().any(|s| s == key) {
501 continue;
502 }
503
504 if keysets.is_empty() {
506 if rmmacro.is_some()
508 && let Ok(rev) = self.revert_keyval(
509 key,
510 primary_keyset,
511 value.as_ref(),
512 *use_default,
513 rmtokens.is_empty(),
514 )
515 {
516 rmtokens.extend(rev);
517 }
518 if let Some(hm) = hook_missing
520 && let Ok(rev) =
521 self.revert_keyval(key, primary_keyset, value.as_ref(), *use_default, true)
522 {
523 tokens.push(hm);
524 tokens.push(T_BEGIN!());
525 tokens.extend(rev);
526 tokens.push(T_END!());
527 }
528 continue;
529 }
530
531 for keyset in keysets {
533 let qname = keyval_qname(prefix, keyset, key);
534 if !has_keyval(prefix, keyset, key) {
535 Info!(
536 "undefined",
537 "Encountered unknown KeyVals key",
538 s!("'{key}' with prefix '{prefix}' not defined in '{keyset}'")
539 );
540 } else if matches!(keyval_get(&qname, "disabled"), Some(Stored::Bool(true))) {
541 Warn!("undefined", "keyval", s!("`{key}' has been disabled. "));
542 } else {
543 if set_internals {
545 tokens.push(T_CS!("\\def"));
546 tokens.push(T_CS!("\\XKV@prefix"));
547 tokens.push(T_BEGIN!());
548 tokens.extend(Explode!(s!("{prefix}@")));
549 tokens.push(T_END!());
550 tokens.push(T_CS!("\\def"));
551 tokens.push(T_CS!("\\XKV@tfam"));
552 tokens.push(T_BEGIN!());
553 tokens.extend(Explode!(keyset));
554 tokens.push(T_END!());
555 tokens.push(T_CS!("\\def"));
556 tokens.push(T_CS!("\\XKV@header"));
557 tokens.push(T_BEGIN!());
558 tokens.extend(Explode!(s!("{prefix}@{keyset}@")));
559 tokens.push(T_END!());
560 tokens.push(T_CS!("\\def"));
561 tokens.push(T_CS!("\\XKV@tkey"));
562 tokens.push(T_BEGIN!());
563 tokens.extend(Explode!(key));
564 tokens.push(T_END!());
565 }
566
567 if *use_default && state::has_meaning(&T_CS!(s!("\\{qname}@default"))) {
573 tokens.push(T_CS!(s!("\\{qname}@default")));
575 } else {
576 tokens.push(T_CS!(s!("\\{qname}")));
578 tokens.push(T_BEGIN!());
579 if let Some(v) = value
580 && let Ok(reverted) = v.revert()
581 {
582 tokens.extend(reverted.unlist());
583 }
584 tokens.push(T_END!());
585 }
586
587 if set_internals {
589 tokens.push(T_CS!("\\def"));
590 tokens.push(T_CS!("\\XKV@prefix"));
591 tokens.push(T_BEGIN!());
592 tokens.push(T_END!());
593 tokens.push(T_CS!("\\def"));
594 tokens.push(T_CS!("\\XKV@tfam"));
595 tokens.push(T_BEGIN!());
596 tokens.push(T_END!());
597 tokens.push(T_CS!("\\def"));
598 tokens.push(T_CS!("\\XKV@header"));
599 tokens.push(T_BEGIN!());
600 tokens.push(T_END!());
601 tokens.push(T_CS!("\\def"));
602 tokens.push(T_CS!("\\XKV@tkey"));
603 tokens.push(T_BEGIN!());
604 tokens.push(T_END!());
605 }
606 }
607 }
608 }
609
610 if let Some(rm) = rmmacro {
612 tokens.push(T_CS!("\\def"));
613 tokens.push(rm);
614 tokens.push(T_BEGIN!());
615 tokens.extend(rmtokens);
616 tokens.push(T_END!());
617 }
618
619 if set_internals {
621 tokens.push(T_CS!("\\def"));
622 tokens.push(T_CS!("\\XKV@fams"));
623 tokens.push(T_BEGIN!());
624 tokens.push(T_END!());
625 tokens.push(T_CS!("\\def"));
626 tokens.push(T_CS!("\\XKV@na"));
627 tokens.push(T_BEGIN!());
628 tokens.push(T_END!());
629 }
630
631 Tokens::new(tokens)
632 }
633
634 pub fn revert(&self) -> Result<Tokens> {
635 let mut tokens = Vec::new();
636 for tuple in &self.tuples {
638 let KVData {
639 key,
640 value,
641 use_default,
642 keysets: _,
643 primary_keyset,
644 digested_value,
645 } = tuple;
646 if !primary_keyset.is_empty() {
647 let digested_fallback = match (value, digested_value) {
656 (None, Some(dv)) => Some(ArgWrap::Tokens(dv.revert()?.strip_braces())),
657 _ => None,
658 };
659 let reverted = self.revert_keyval(
660 key,
661 primary_keyset,
662 value.as_ref().or(digested_fallback.as_ref()),
663 *use_default,
664 tokens.is_empty(),
665 )?;
666 tokens.extend(reverted);
667 }
668 }
669 Ok(Tokens::new(tokens))
671 }
672
673 fn revert_keyval(
674 &self,
675 key: &str,
676 keyset: &str,
677 value_opt: Option<&ArgWrap>,
678 use_default: bool,
679 is_first: bool,
680 ) -> Result<Vec<Token>> {
681 let keytype_stored = keyval_get(&keyval_qname(&self.prefix, keyset, key), "type");
683 let mut tokens = Vec::new();
685 if !is_first {
687 tokens.push(T_OTHER!(","));
688 }
689 tokens.extend(Explode!(key));
690 if !use_default && let Some(value) = value_opt {
692 tokens.push(T_OTHER!("="));
693 let mut reverted_tokens = Vec::new();
694 if let Some(Stored::Parameter(keytype)) = keytype_stored {
695 if let Some(reverted) = keytype.revert(Some(value.revert()?))? {
699 reverted_tokens.extend(reverted.unlist());
700 }
701 } else {
702 reverted_tokens.extend(value.revert()?.unlist());
703 }
704 tokens.extend(self.rebrace(Tokens::new(reverted_tokens)).unlist());
705 }
706 Ok(tokens)
707 }
708
709 fn rebrace(&self, tokens: Tokens) -> Tokens {
713 let mut level: i32 = 0;
714 let mut needs_brace = tokens.is_empty();
715 for t in tokens.unlist_ref() {
716 let cc = t.get_catcode();
717 if cc == Catcode::BEGIN {
718 level += 1;
719 }
720 if cc == Catcode::END {
721 level -= 1;
722 if level < 0 {
725 break;
726 }
727 } else if level <= 0 && cc == Catcode::OTHER && t.with_str(|s| s == ",") {
728 needs_brace = true;
730 break;
731 }
732 }
733 if needs_brace {
734 let mut wrapped = vec![T_BEGIN!()];
735 wrapped.extend(tokens.unlist());
736 wrapped.push(T_END!());
737 Tokens::new(wrapped)
738 } else {
739 tokens
740 }
741 }
742
743 pub fn add_value(
748 &mut self,
749 key: &str,
750 value_arg: ArgWrap,
751 use_default: bool,
752 no_rebuild: bool,
753 ) -> Result<()> {
754 let keysets = self.resolve_keyval_for(key);
756 let primary_keyset = self.get_primary_keyval(keysets.as_slice()).to_owned();
757
758 let value = if use_default {
760 match keyval_get(&keyval_qname(&self.prefix, &primary_keyset, key), "default") {
761 None => Some(ArgWrap::Tokens(Tokens!())), Some(v) => {
763 let arg: Result<ArgWrap> = v.into();
764 Some(arg?)
765 },
766 }
767 } else {
768 Some(value_arg)
769 };
770 self.tuples.push(KVData {
771 key: key.to_string(),
772 value,
773 use_default,
774 keysets,
775 primary_keyset,
776 digested_value: None,
777 });
778 if !no_rebuild {
781 self.rebuild(None);
782 }
783 Ok(())
784 }
785
786 pub fn set_value(&mut self, key: &str, value: ArgWrap, use_default: bool) -> Result<()> {
787 self.rebuild(Some(key));
789 match &value {
793 ArgWrap::None => {
794 Ok(())
796 },
797 _ => {
798 self.add_value(key, value, use_default, false)
800 },
801 }
802 }
803
804 fn rebuild(&mut self, skip_opt: Option<&str>) {
805 let mut newtuples: Vec<KVData> = Vec::new();
807 let mut pairs = Vec::new();
808 let mut hash: HashMap<String, Vec<ArgWrap>> = HashMap::default();
809 let mut hash_digested: HashMap<String, Vec<Digested>> = HashMap::default();
810
811 for tuple in self.tuples.drain(..) {
812 let KVData {
814 key,
815 value,
816 use_default,
817 primary_keyset,
818 keysets,
819 digested_value,
820 } = tuple;
821 let key_str = key.as_str();
823 if let Some(skip) = skip_opt
824 && skip == key_str
825 {
826 continue;
827 }
828 if let Some(v) = value.as_ref() {
829 pairs.push((key.clone(), v.clone()));
831
832 let entry = hash.entry(key.clone()).or_default();
834 entry.push(v.clone());
835 } else if let Some(ref dv) = digested_value {
836 let fallback = ArgWrap::Tokens(dv.revert().unwrap_or_default());
839 pairs.push((key.clone(), fallback.clone()));
840 let entry = hash.entry(key.clone()).or_default();
841 entry.push(fallback);
842 }
843 if let Some(ref dvalue) = digested_value {
845 let entry = hash_digested.entry(key.clone()).or_default();
846 entry.push(dvalue.clone());
847 }
848
849 newtuples.push(KVData {
851 key,
852 value,
853 use_default,
854 primary_keyset,
855 keysets,
856 digested_value,
857 });
858 }
859 self.cached_pairs = pairs;
861 self.cached_hash = hash;
862 self.cached_hash_digested = hash_digested;
863 self.tuples = newtuples;
864 }
865
866 pub fn read_from(&mut self, until: Token, silence_missing: bool) -> Result<()> {
875 let skip_missing = self.skip_missing.clone();
877 let hook_missing = self.hook_missing;
878 if silence_missing {
880 self.skip_missing = SkipMissing::All;
881 self.hook_missing = None;
882 }
883
884 let startloc = gullet::get_locator();
886 let _open = gullet::read_token()?;
888
889 let punct_tks = Tokens!(T_OTHER!(","));
890 let until_tks = Tokens!(until);
891 loop {
893 gullet::skip_spaces()?;
895 if gullet::if_next(T_BEGIN!())? {
896 gullet::read_token()?;
898 gullet::unread(gullet::read_balanced(ExpansionLevel::Off, false, false)?.strip_braces());
899 gullet::skip_spaces()?;
900 }
901 let (ktoks, mut delim_opt) = self.read_keyword_from(until)?;
903
904 if delim_opt.is_none() {
906 let message = s!(
907 "Fell off end expecting {} while reading KeyVal key",
908 until.stringify()
909 );
910 let message2 = s!("key started at {}", startloc.to_string());
911 Error!("expected", until, message, message2);
912 }
913
914 let key_str = ktoks.to_string();
916 let key = key_str.trim();
917
918 if !key.is_empty() {
920 let mut value = ArgWrap::None;
921 let is_explicit = delim_opt == Some(T_OTHER!("="));
923 if is_explicit {
924 let resolved_kv = self.resolve_keyval_for(key);
926 let keyset = self.get_primary_keyval(&resolved_kv);
927 let keytype_opt = keyval_get(&keyval_qname(&self.prefix, keyset, key), "type");
928 if let Some(Stored::Parameter(ref keytype)) = keytype_opt {
929 keytype.setup_catcodes();
930 }
931 let mut toks = Vec::new();
933 loop {
934 delim_opt = gullet::read_match(&[&punct_tks, &until_tks])?.map(|tks| tks.into());
938 if delim_opt.is_some() {
939 break; }
941 if let Some(tok) = gullet::read_token()? {
942 toks.push(tok);
944 if tok.get_catcode() == Catcode::BEGIN {
945 let balanced_arg = gullet::read_balanced(ExpansionLevel::Off, false, false)?;
946 if !balanced_arg.is_empty() {
947 toks.extend(balanced_arg.unlist());
948 }
949 toks.push(T_END!());
950 }
951 } else {
952 break;
953 }
954 }
955 if !toks.is_empty() {
957 let stripped_toks = Tokens::new(toks).strip_braces_n(2);
958 if !stripped_toks.is_empty() {
959 if let Some(Stored::Parameter(ref keytype)) = keytype_opt {
960 value = keytype.reparse(stripped_toks)?;
961 } else {
962 value = ArgWrap::Tokens(stripped_toks);
963 }
964 }
965 }
966 if value.is_none() {
978 value = ArgWrap::Tokens(Tokens!());
979 }
980 if let Some(Stored::Parameter(ref keydef)) = keytype_opt {
982 keydef.revert_catcodes()?;
983 }
984 }
985 if !silence_missing || self.can_resolve_keyval_for(key) {
987 self.add_value(key, value, !is_explicit, false)?;
988 }
989 }
990
991 if delim_opt.as_ref() == Some(&until) {
993 break;
994 }
995 }
996
997 self.rebuild(None);
999
1000 if silence_missing {
1002 self.skip_missing = skip_missing;
1003 self.hook_missing = hook_missing;
1004 }
1005 Ok(())
1006 }
1007
1008 pub fn into_tokens(self) -> Result<Tokens> {
1017 let mut tks: Vec<Token> = Vec::new();
1018 for (k, v) in self.cached_pairs.into_iter() {
1019 tks.push(T_OTHER!(k));
1020 match v {
1021 ArgWrap::Tokens(vtks) => {
1022 let expanded = gullet::do_expand(vtks)?;
1023 let mut exp_str = expanded.to_string();
1024 if exp_str == "{}" {
1025 exp_str = String::new();
1026 }
1027 tks.push(T_OTHER!(exp_str));
1028 },
1029 ArgWrap::Token(vtk) => tks.push(vtk),
1030 other => {
1031 emit_warn(
1032 "internal",
1033 "keyvals",
1034 &format!("Unexpected ArgWrap variant in KeyVals revert: {other:?}"),
1035 );
1036 },
1037 }
1038 }
1039 Ok(Tokens::new(tks))
1040 }
1041}
1042
1043impl From<KeyVals> for Result<Option<Digested>> {
1044 fn from(value: KeyVals) -> Result<Option<Digested>> {
1045 let tmp: Digested = value.into();
1046 tmp.into()
1047 }
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052 use super::*;
1053
1054 #[test]
1055 fn skip_missing_default_is_none() {
1056 let s = SkipMissing::default();
1057 assert_eq!(s, SkipMissing::None);
1058 }
1059
1060 #[test]
1061 fn skip_missing_variants_not_equal() {
1062 assert_ne!(SkipMissing::None, SkipMissing::All);
1063 }
1064
1065 #[test]
1066 fn keyvals_config_default_all_empty() {
1067 let c = KeyvalsConfig::default();
1068 assert!(c.prefix.is_none());
1069 assert!(c.keysets.is_empty());
1070 assert!(!c.set_all);
1071 assert!(!c.set_internals);
1072 assert!(c.skip.is_empty());
1073 assert_eq!(c.skip_missing, SkipMissing::None);
1074 assert!(c.hook_missing.is_none());
1075 }
1076
1077 #[test]
1078 fn keyvals_default_prefix_and_anonymous_keyset() {
1079 let kv = KeyVals::default();
1081 assert_eq!(kv.prefix, "KV");
1082 assert_eq!(kv.keysets, vec!["_anonymous_".to_string()]);
1083 assert!(!kv.set_all);
1084 assert!(!kv.set_internals);
1085 }
1086
1087 #[test]
1088 fn keyvals_new_with_empty_keysets_defaults_to_anonymous() {
1089 let kv = KeyVals::new(KeyvalsConfig::default());
1090 assert_eq!(kv.keysets, vec!["_anonymous_".to_string()]);
1091 }
1092
1093 #[test]
1094 fn keyvals_new_with_custom_keysets_preserved() {
1095 let cfg = KeyvalsConfig {
1096 keysets: vec!["tabular".to_string(), "array".to_string()],
1097 ..KeyvalsConfig::default()
1098 };
1099 let kv = KeyVals::new(cfg);
1100 assert_eq!(kv.keysets.len(), 2);
1101 assert_eq!(kv.keysets[0], "tabular");
1102 }
1103
1104 #[test]
1105 fn keyvals_new_custom_prefix() {
1106 let cfg = KeyvalsConfig {
1107 prefix: Some("P".to_string()),
1108 ..KeyvalsConfig::default()
1109 };
1110 let kv = KeyVals::new(cfg);
1111 assert_eq!(kv.prefix, "P");
1112 }
1113
1114 #[test]
1115 fn keyvals_new_default_prefix_on_none() {
1116 let cfg = KeyvalsConfig {
1117 prefix: None,
1118 ..KeyvalsConfig::default()
1119 };
1120 let kv = KeyVals::new(cfg);
1121 assert_eq!(kv.prefix, "KV");
1122 }
1123
1124 #[test]
1125 fn keyvals_new_set_all_flag() {
1126 let cfg = KeyvalsConfig {
1127 set_all: true,
1128 ..KeyvalsConfig::default()
1129 };
1130 let kv = KeyVals::new(cfg);
1131 assert!(kv.set_all);
1132 }
1133
1134 #[test]
1135 fn keyvals_new_filters_empty_keysets() {
1136 let cfg = KeyvalsConfig {
1142 keysets: vec!["".to_string(), "pstricks".to_string()],
1143 ..KeyvalsConfig::default()
1144 };
1145 let kv = KeyVals::new(cfg);
1146 assert_eq!(kv.keysets, vec!["pstricks".to_string()]);
1147 }
1148
1149 #[test]
1150 fn keyvals_new_all_empty_keysets_defaults_to_anonymous() {
1151 let cfg = KeyvalsConfig {
1154 keysets: vec!["".to_string(), "".to_string()],
1155 ..KeyvalsConfig::default()
1156 };
1157 let kv = KeyVals::new(cfg);
1158 assert_eq!(kv.keysets, vec!["_anonymous_".to_string()]);
1159 }
1160}