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 {
super::*,
crate::{
content_search::ContentMatch,
},
bet::*,
std::path::Path,
};
/// A pattern composing other ones with operators
#[derive(Debug, Clone)]
pub struct CompositePattern {
pub expr: BeTree<PatternOperator, Pattern>,
}
impl CompositePattern {
pub fn new(expr: BeTree<PatternOperator, Pattern>) -> Self {
Self { expr }
}
pub fn score_of_string(&self, candidate: &str) -> Option<i32> {
use PatternOperator::*;
let composite_result: Option<Option<i32>> = self.expr.eval(
// score evaluation
|pat| pat.score_of_string(candidate),
// operator
|op, a, b| {
match (op, a, b) {
(And, None, _) => None, // normally not called due to short-circuit
(And, Some(sa), Some(Some(sb))) => Some(sa + sb),
(Or, None, Some(Some(sb))) => Some(sb),
(Or, Some(sa), Some(None)) => Some(sa),
(Or, Some(sa), Some(Some(sb))) => Some(sa + sb),
(Not, Some(_), _) => None,
(Not, None, _) => Some(1),
_ => None,
}
},
// short-circuit. We don't short circuit on 'or' because
// we want to use both scores
|op, a| match (op, a) {
(And, None) => true,
_ => false,
},
);
composite_result
.unwrap_or_else(||{
warn!("unexpectedly missing result ");
None
})
}
pub fn score_of(&self, candidate: Candidate) -> Option<i32> {
use PatternOperator::*;
let composite_result: Option<Option<i32>> = self.expr.eval(
// score evaluation
|pat| pat.score_of(candidate),
// operator
|op, a, b| {
match (op, a, b) {
(And, None, _) => None, // normally not called due to short-circuit
(And, Some(sa), Some(Some(sb))) => Some(sa + sb),
(Or, None, Some(Some(sb))) => Some(sb),
(Or, Some(sa), Some(None)) => Some(sa),
(Or, Some(sa), Some(Some(sb))) => Some(sa + sb),
(Not, Some(_), _) => None,
(Not, None, _) => Some(1),
_ => None,
}
},
// short-circuit. We don't short circuit on 'or' because
// we want to use both scores
|op, a| match (op, a) {
(And, None) => true,
_ => false,
},
);
composite_result
.unwrap_or_else(||{
warn!("unexpectedly missing result ");
None
})
}
pub fn search_string(&self, candidate: &str) -> Option<NameMatch> {
// an ideal algorithm would call score_of on patterns when the object is different
// to deal with exclusions but I'll start today with something simpler
use PatternOperator::*;
let composite_result: Option<Option<NameMatch>> = self.expr.eval(
// score evaluation
|pat| pat.search_string(candidate),
// operator
|op, a, b| match (op, a, b) {
(Not, Some(_), _) => None,
(_, Some(ma), _) => Some(ma),
(_, None, Some(omb)) => omb,
_ => None,
},
|op, a| match (op, a) {
(Or, Some(_)) => true,
_ => false,
},
);
// it's possible we didn't find a result because the composition
composite_result
.unwrap_or_else(||{
warn!("unexpectedly missing result ");
None
})
}
pub fn search_content(
&self,
candidate: &Path,
desired_len: usize, // available space for content match display
) -> Option<ContentMatch> {
use PatternOperator::*;
let composite_result: Option<Option<ContentMatch>> = self.expr.eval(
// score evaluation
|pat| pat.search_content(candidate, desired_len),
// operator
|op, a, b| match (op, a, b) {
(Not, Some(_), _) => None,
(_, Some(ma), _) => Some(ma),
(_, None, Some(omb)) => omb,
_ => None,
},
|op, a| match (op, a) {
(Or, Some(_)) => true,
_ => false,
},
);
composite_result
.unwrap_or_else(||{
warn!("unexpectedly missing result ");
None
})
}
pub fn has_real_scores(&self) -> bool {
self.expr.iter_atoms()
.fold(false, |r, p| match p {
Pattern::NameFuzzy(_) | Pattern::PathFuzzy(_) => true,
_ => r,
})
}
}