forked from typst/codex
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshared.rs
More file actions
148 lines (130 loc) · 4.62 KB
/
shared.rs
File metadata and controls
148 lines (130 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use std::ops::{AddAssign, Deref};
macro_rules! declare_types {
($(<$lt:lifetime>)?
$(derive($($Der:ident),*),)?
str = $s:ty,
List = $List:ident<_>
) => {
/// A module of definitions.
$(#[derive($($Der),*)])?
pub struct Module<$($lt)?>($List<($s, Binding<$($lt)?>)>);
/// A definition bound in a module, with metadata.
$(#[derive($($Der),*)])?
pub struct Binding<$($lt)?> {
/// The bound definition.
pub def: Def<$($lt)?>,
/// A deprecation message for the definition, if it is deprecated.
pub deprecation: Option<$s>,
}
impl<$($lt)?> Binding<$($lt)?> {
/// Create a new bound definition.
pub const fn new(definition: Def<$($lt)?>) -> Self {
Self { def: definition, deprecation: None }
}
}
/// A definition in a module.
$(#[derive($($Der),*)])?
pub enum Def<$($lt)?> {
/// A symbol, potentially with modifiers.
Symbol(Symbol<$($lt)?>),
/// A nested module.
Module(Module<$($lt)?>),
}
/// A symbol, either a leaf or with modifiers.
$(#[derive($($Der),*)])?
pub enum Symbol<$($lt)?> {
/// A symbol without modifiers.
Single(char),
/// A symbol with named modifiers.
/// The symbol defaults to its first variant.
Multi($List<(ModifierSet<$s>, char)>),
}
};
}
/// A set of modifiers.
#[derive(Debug, Copy, Clone)]
pub struct ModifierSet<S>(S);
impl<S: Deref<Target = str>> ModifierSet<S> {
/// Convert the underlying string to a slice.
pub fn as_deref(&self) -> ModifierSet<&str> {
ModifierSet(&self.0)
}
/// Construct a modifier set from a string,
/// where modifiers are separated by the character `.`.
///
/// It is not unsafe to use this function wrongly, but it can produce
/// unexpected results down the line. Correct usage should ensure that
/// `s` does not contain any empty modifiers (i.e. the sequence `..`)
/// and that no modifier occurs twice.
pub fn new_unchecked(s: S) -> Self {
Self(s)
}
/// Construct an empty modifier set.
pub fn empty() -> Self
where
S: Default,
{
Self(S::default())
}
/// Whether `self` is empty.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Add a modifier to the set, without checking that it is a valid modifier.
///
/// It is not unsafe to use this method wrongly, but that can produce
/// unexpected results down the line. Correct usage should ensure that
/// `modifier` is not empty and doesn't contain the character `.`.
pub fn add_unchecked(&mut self, m: &str)
where
S: for<'a> AddAssign<&'a str>,
{
if !self.0.is_empty() {
self.0 += ".";
}
self.0 += m;
}
/// Iterate over the list of modifiers in an arbitrary order.
pub fn iter(&self) -> impl Iterator<Item = &str> {
self.0.split('.').filter(|s| !s.is_empty())
}
/// Whether the set contains the modifier `m`.
pub fn contains(&self, m: &str) -> bool {
self.iter().any(|lhs| lhs == m)
}
/// Whether all modifiers in `self` are also present in `other`.
pub fn is_subset(&self, other: ModifierSet<&str>) -> bool {
self.iter().all(|m| other.contains(m))
}
/// Find the best match from the list.
///
/// To be considered a match, the modifier set must be a superset of
/// (or equal to) `self`. Among different matches, the best one is selected
/// by the following two criteria (in order):
/// 1. Number of modifiers in common with `self` (more is better).
/// 2. Total number of modifiers (fewer is better).
pub fn best_match_in<'a, T>(
&self,
variants: impl Iterator<Item = (ModifierSet<&'a str>, T)>,
) -> Option<T> {
let mut best = None;
let mut best_score = None;
// Find the best table entry with this name.
for candidate in variants.filter(|(set, _)| self.is_subset(*set)) {
let mut matching = 0;
let mut total = 0;
for modifier in candidate.0.iter() {
if self.contains(modifier) {
matching += 1;
}
total += 1;
}
let score = (matching, core::cmp::Reverse(total));
if best_score.map_or(true, |b| score > b) {
best = Some(candidate.1);
best_score = Some(score);
}
}
best
}
}