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 reverted = self.revert_keyval(
648 key,
649 primary_keyset,
650 value.as_ref(),
651 *use_default,
652 tokens.is_empty(),
653 )?;
654 tokens.extend(reverted);
655 }
656 }
657 Ok(Tokens::new(tokens))
659 }
660
661 fn revert_keyval(
662 &self,
663 key: &str,
664 keyset: &str,
665 value_opt: Option<&ArgWrap>,
666 use_default: bool,
667 is_first: bool,
668 ) -> Result<Vec<Token>> {
669 let keytype_stored = keyval_get(&keyval_qname(&self.prefix, keyset, key), "type");
671 let mut tokens = Vec::new();
673 if !is_first {
675 tokens.push(T_OTHER!(","));
676 }
677 tokens.extend(Explode!(key));
678 if !use_default && let Some(value) = value_opt {
680 tokens.push(T_OTHER!("="));
681 let mut reverted_tokens = Vec::new();
682 if let Some(Stored::Parameter(keytype)) = keytype_stored {
683 if let Some(reverted) = keytype.revert(Some(value.revert()?))? {
687 reverted_tokens.extend(reverted.unlist());
688 }
689 } else {
690 reverted_tokens.extend(value.revert()?.unlist());
691 }
692 tokens.extend(self.rebrace(Tokens::new(reverted_tokens)).unlist());
693 }
694 Ok(tokens)
695 }
696
697 fn rebrace(&self, tokens: Tokens) -> Tokens {
701 let mut level: i32 = 0;
702 let mut needs_brace = tokens.is_empty();
703 for t in tokens.unlist_ref() {
704 let cc = t.get_catcode();
705 if cc == Catcode::BEGIN {
706 level += 1;
707 }
708 if cc == Catcode::END {
709 level -= 1;
710 if level < 0 {
713 break;
714 }
715 } else if level <= 0 && cc == Catcode::OTHER && t.with_str(|s| s == ",") {
716 needs_brace = true;
718 break;
719 }
720 }
721 if needs_brace {
722 let mut wrapped = vec![T_BEGIN!()];
723 wrapped.extend(tokens.unlist());
724 wrapped.push(T_END!());
725 Tokens::new(wrapped)
726 } else {
727 tokens
728 }
729 }
730
731 pub fn add_value(
736 &mut self,
737 key: &str,
738 value_arg: ArgWrap,
739 use_default: bool,
740 no_rebuild: bool,
741 ) -> Result<()> {
742 let keysets = self.resolve_keyval_for(key);
744 let primary_keyset = self.get_primary_keyval(keysets.as_slice()).to_owned();
745
746 let value = if use_default {
748 match keyval_get(&keyval_qname(&self.prefix, &primary_keyset, key), "default") {
749 None => Some(ArgWrap::Tokens(Tokens!())), Some(v) => {
751 let arg: Result<ArgWrap> = v.into();
752 Some(arg?)
753 },
754 }
755 } else {
756 Some(value_arg)
757 };
758 self.tuples.push(KVData {
759 key: key.to_string(),
760 value,
761 use_default,
762 keysets,
763 primary_keyset,
764 digested_value: None,
765 });
766 if !no_rebuild {
769 self.rebuild(None);
770 }
771 Ok(())
772 }
773
774 pub fn set_value(&mut self, key: &str, value: ArgWrap, use_default: bool) -> Result<()> {
775 self.rebuild(Some(key));
777 match &value {
781 ArgWrap::None => {
782 Ok(())
784 },
785 _ => {
786 self.add_value(key, value, use_default, false)
788 },
789 }
790 }
791
792 fn rebuild(&mut self, skip_opt: Option<&str>) {
793 let mut newtuples: Vec<KVData> = Vec::new();
795 let mut pairs = Vec::new();
796 let mut hash: HashMap<String, Vec<ArgWrap>> = HashMap::default();
797 let mut hash_digested: HashMap<String, Vec<Digested>> = HashMap::default();
798
799 for tuple in self.tuples.drain(..) {
800 let KVData {
802 key,
803 value,
804 use_default,
805 primary_keyset,
806 keysets,
807 digested_value,
808 } = tuple;
809 let key_str = key.as_str();
811 if let Some(skip) = skip_opt
812 && skip == key_str
813 {
814 continue;
815 }
816 if let Some(v) = value.as_ref() {
817 pairs.push((key.clone(), v.clone()));
819
820 let entry = hash.entry(key.clone()).or_default();
822 entry.push(v.clone());
823 } else if let Some(ref dv) = digested_value {
824 let fallback = ArgWrap::Tokens(dv.revert().unwrap_or_default());
827 pairs.push((key.clone(), fallback.clone()));
828 let entry = hash.entry(key.clone()).or_default();
829 entry.push(fallback);
830 }
831 if let Some(ref dvalue) = digested_value {
833 let entry = hash_digested.entry(key.clone()).or_default();
834 entry.push(dvalue.clone());
835 }
836
837 newtuples.push(KVData {
839 key,
840 value,
841 use_default,
842 primary_keyset,
843 keysets,
844 digested_value,
845 });
846 }
847 self.cached_pairs = pairs;
849 self.cached_hash = hash;
850 self.cached_hash_digested = hash_digested;
851 self.tuples = newtuples;
852 }
853
854 pub fn read_from(&mut self, until: Token, silence_missing: bool) -> Result<()> {
863 let skip_missing = self.skip_missing.clone();
865 let hook_missing = self.hook_missing;
866 if silence_missing {
868 self.skip_missing = SkipMissing::All;
869 self.hook_missing = None;
870 }
871
872 let startloc = gullet::get_locator();
874 let _open = gullet::read_token()?;
876
877 let punct_tks = Tokens!(T_OTHER!(","));
878 let until_tks = Tokens!(until);
879 loop {
881 gullet::skip_spaces()?;
883 if gullet::if_next(T_BEGIN!())? {
884 gullet::read_token()?;
886 gullet::unread(gullet::read_balanced(ExpansionLevel::Off, false, false)?.strip_braces());
887 gullet::skip_spaces()?;
888 }
889 let (ktoks, mut delim_opt) = self.read_keyword_from(until)?;
891
892 if delim_opt.is_none() {
894 let message = s!(
895 "Fell off end expecting {} while reading KeyVal key",
896 until.stringify()
897 );
898 let message2 = s!("key started at {}", startloc.to_string());
899 Error!("expected", until, message, message2);
900 }
901
902 let key_str = ktoks.to_string();
904 let key = key_str.trim();
905
906 if !key.is_empty() {
908 let mut value = ArgWrap::None;
909 let is_explicit = delim_opt == Some(T_OTHER!("="));
911 if is_explicit {
912 let resolved_kv = self.resolve_keyval_for(key);
914 let keyset = self.get_primary_keyval(&resolved_kv);
915 let keytype_opt = keyval_get(&keyval_qname(&self.prefix, keyset, key), "type");
916 if let Some(Stored::Parameter(ref keytype)) = keytype_opt {
917 keytype.setup_catcodes();
918 }
919 let mut toks = Vec::new();
921 loop {
922 delim_opt = gullet::read_match(&[&punct_tks, &until_tks])?.map(|tks| tks.into());
926 if delim_opt.is_some() {
927 break; }
929 if let Some(tok) = gullet::read_token()? {
930 toks.push(tok);
932 if tok.get_catcode() == Catcode::BEGIN {
933 let balanced_arg = gullet::read_balanced(ExpansionLevel::Off, false, false)?;
934 if !balanced_arg.is_empty() {
935 toks.extend(balanced_arg.unlist());
936 }
937 toks.push(T_END!());
938 }
939 } else {
940 break;
941 }
942 }
943 if !toks.is_empty() {
945 let stripped_toks = Tokens::new(toks).strip_braces_n(2);
946 if !stripped_toks.is_empty() {
947 if let Some(Stored::Parameter(ref keytype)) = keytype_opt {
948 value = keytype.reparse(stripped_toks)?;
949 } else {
950 value = ArgWrap::Tokens(stripped_toks);
951 }
952 }
953 }
954 if value.is_none() {
966 value = ArgWrap::Tokens(Tokens!());
967 }
968 if let Some(Stored::Parameter(ref keydef)) = keytype_opt {
970 keydef.revert_catcodes()?;
971 }
972 }
973 if !silence_missing || self.can_resolve_keyval_for(key) {
975 self.add_value(key, value, !is_explicit, false)?;
976 }
977 }
978
979 if delim_opt.as_ref() == Some(&until) {
981 break;
982 }
983 }
984
985 self.rebuild(None);
987
988 if silence_missing {
990 self.skip_missing = skip_missing;
991 self.hook_missing = hook_missing;
992 }
993 Ok(())
994 }
995
996 pub fn into_tokens(self) -> Result<Tokens> {
1005 let mut tks: Vec<Token> = Vec::new();
1006 for (k, v) in self.cached_pairs.into_iter() {
1007 tks.push(T_OTHER!(k));
1008 match v {
1009 ArgWrap::Tokens(vtks) => {
1010 let expanded = gullet::do_expand(vtks)?;
1011 let mut exp_str = expanded.to_string();
1012 if exp_str == "{}" {
1013 exp_str = String::new();
1014 }
1015 tks.push(T_OTHER!(exp_str));
1016 },
1017 ArgWrap::Token(vtk) => tks.push(vtk),
1018 other => {
1019 emit_warn(
1020 "internal",
1021 "keyvals",
1022 &format!("Unexpected ArgWrap variant in KeyVals revert: {other:?}"),
1023 );
1024 },
1025 }
1026 }
1027 Ok(Tokens::new(tks))
1028 }
1029}
1030
1031impl From<KeyVals> for Result<Option<Digested>> {
1032 fn from(value: KeyVals) -> Result<Option<Digested>> {
1033 let tmp: Digested = value.into();
1034 tmp.into()
1035 }
1036}
1037
1038#[cfg(test)]
1039mod tests {
1040 use super::*;
1041
1042 #[test]
1043 fn skip_missing_default_is_none() {
1044 let s = SkipMissing::default();
1045 assert_eq!(s, SkipMissing::None);
1046 }
1047
1048 #[test]
1049 fn skip_missing_variants_not_equal() {
1050 assert_ne!(SkipMissing::None, SkipMissing::All);
1051 }
1052
1053 #[test]
1054 fn keyvals_config_default_all_empty() {
1055 let c = KeyvalsConfig::default();
1056 assert!(c.prefix.is_none());
1057 assert!(c.keysets.is_empty());
1058 assert!(!c.set_all);
1059 assert!(!c.set_internals);
1060 assert!(c.skip.is_empty());
1061 assert_eq!(c.skip_missing, SkipMissing::None);
1062 assert!(c.hook_missing.is_none());
1063 }
1064
1065 #[test]
1066 fn keyvals_default_prefix_and_anonymous_keyset() {
1067 let kv = KeyVals::default();
1069 assert_eq!(kv.prefix, "KV");
1070 assert_eq!(kv.keysets, vec!["_anonymous_".to_string()]);
1071 assert!(!kv.set_all);
1072 assert!(!kv.set_internals);
1073 }
1074
1075 #[test]
1076 fn keyvals_new_with_empty_keysets_defaults_to_anonymous() {
1077 let kv = KeyVals::new(KeyvalsConfig::default());
1078 assert_eq!(kv.keysets, vec!["_anonymous_".to_string()]);
1079 }
1080
1081 #[test]
1082 fn keyvals_new_with_custom_keysets_preserved() {
1083 let cfg = KeyvalsConfig {
1084 keysets: vec!["tabular".to_string(), "array".to_string()],
1085 ..KeyvalsConfig::default()
1086 };
1087 let kv = KeyVals::new(cfg);
1088 assert_eq!(kv.keysets.len(), 2);
1089 assert_eq!(kv.keysets[0], "tabular");
1090 }
1091
1092 #[test]
1093 fn keyvals_new_custom_prefix() {
1094 let cfg = KeyvalsConfig {
1095 prefix: Some("P".to_string()),
1096 ..KeyvalsConfig::default()
1097 };
1098 let kv = KeyVals::new(cfg);
1099 assert_eq!(kv.prefix, "P");
1100 }
1101
1102 #[test]
1103 fn keyvals_new_default_prefix_on_none() {
1104 let cfg = KeyvalsConfig {
1105 prefix: None,
1106 ..KeyvalsConfig::default()
1107 };
1108 let kv = KeyVals::new(cfg);
1109 assert_eq!(kv.prefix, "KV");
1110 }
1111
1112 #[test]
1113 fn keyvals_new_set_all_flag() {
1114 let cfg = KeyvalsConfig {
1115 set_all: true,
1116 ..KeyvalsConfig::default()
1117 };
1118 let kv = KeyVals::new(cfg);
1119 assert!(kv.set_all);
1120 }
1121
1122 #[test]
1123 fn keyvals_new_filters_empty_keysets() {
1124 let cfg = KeyvalsConfig {
1130 keysets: vec!["".to_string(), "pstricks".to_string()],
1131 ..KeyvalsConfig::default()
1132 };
1133 let kv = KeyVals::new(cfg);
1134 assert_eq!(kv.keysets, vec!["pstricks".to_string()]);
1135 }
1136
1137 #[test]
1138 fn keyvals_new_all_empty_keysets_defaults_to_anonymous() {
1139 let cfg = KeyvalsConfig {
1142 keysets: vec!["".to_string(), "".to_string()],
1143 ..KeyvalsConfig::default()
1144 };
1145 let kv = KeyVals::new(cfg);
1146 assert_eq!(kv.keysets, vec!["_anonymous_".to_string()]);
1147 }
1148}