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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
use std::sync::Arc;
use super::IProcessor;
/// Java `Set<IProcessor>` 的 Rust 等价集合。
///
/// 这是 `org.thymeleaf.dialect.IProcessorDialect#getProcessors(String)` 返回边界所需的
/// Rust 扩展类型,不计入 Java 对象迁移分子。集合保留以下 Java 可观察合同:
///
/// - 每个逻辑元素最多出现一次;
/// - `None` 对应集合中的 Java `null`,同样最多出现一次;
/// - 迭代顺序保留具体 Java `Set` 的实际迭代顺序;
/// - 非空元素使用 [`Arc`] 保留同一 Processor 对象身份。
///
/// 固定上游 Processor 均继承 `Object` 的身份相等语义,所以 [`Self::insert`] 使用
/// [`Arc::ptr_eq`] 去重。第三方 Processor 若在 Java 中覆盖 `equals`,可使用
/// [`Self::insert_with`] 显式提供同一等价关系。
#[derive(Default)]
/// 对应 Java 语义:Rust 侧内部类型(Java 无直接对应对象)。
pub struct ProcessorSet {
entries: Vec<Option<Arc<dyn IProcessor>>>,
}
impl ProcessorSet {
/// 创建空 Processor 集合。
///
/// # 返回
///
/// 不包含任何元素的集合。
#[must_use]
pub const fn new() -> Self {
Self {
entries: Vec::new(),
}
}
/// 按 Java `Object` 默认身份相等语义插入 Processor。
///
/// # 参数
///
/// - `processor`:待插入的共享 Processor;`None` 对应 Java `null`。
///
/// # 返回
///
/// 元素此前不存在并完成插入时返回 `true`;相同 `Arc` 身份或第二个 `None`
/// 已存在时返回 `false`。
pub fn insert(&mut self, processor: Option<Arc<dyn IProcessor>>) -> bool {
self.insert_with(processor, |candidate, existing| {
std::ptr::eq(candidate, existing)
})
}
/// 按显式 Java `equals` 等价关系插入 Processor。
///
/// 该入口用于第三方 Processor 在 Java 中覆盖 `Object#equals` 的场景。谓词参数
/// 顺序与 `HashSet#add` 的关键比较方向一致:新候选在前、已存在元素在后。
/// `None` 唯一性由集合自身处理,不会调用谓词。
///
/// # 参数
///
/// - `processor`:待插入的共享 Processor;`None` 对应 Java `null`;
/// - `equivalent`:判断新候选与已有非空元素是否逻辑相等的谓词。
///
/// # 返回
///
/// 没有等价元素并完成插入时返回 `true`,否则返回 `false`。
pub fn insert_with<F>(
&mut self,
processor: Option<Arc<dyn IProcessor>>,
mut equivalent: F,
) -> bool
where
F: FnMut(&dyn IProcessor, &dyn IProcessor) -> bool,
{
let duplicate = self
.entries
.iter()
.any(|existing| match (&processor, existing) {
(None, None) => true,
(Some(candidate), Some(existing)) => {
equivalent(candidate.as_ref(), existing.as_ref())
}
_ => false,
});
if duplicate {
return false;
}
self.entries.push(processor);
true
}
/// 返回集合中的唯一元素数。
///
/// # 返回
///
/// 包含可选 `null` 元素在内的元素数量。
#[must_use]
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
pub fn len(&self) -> usize {
self.entries.len()
}
/// 判断集合是否为空。
///
/// # 返回
///
/// 没有任何元素时返回 `true`。
#[must_use]
/// 对应 Java 语义:Java 接口/超类方法 `isEmpty()` 的 Rust 移植(`None` 继承路径)。
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
/// 按底层集合的实际迭代顺序访问 Processor。
///
/// # 返回
///
/// 对集合中共享 Processor 身份的只读迭代器;`None` 对应 Java `null` 元素。
pub fn iter(&self) -> impl ExactSizeIterator<Item = Option<&Arc<dyn IProcessor>>> {
self.entries.iter().map(Option::as_ref)
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::ProcessorSet;
use crate::processor::IProcessor;
use crate::templatemode::TemplateMode;
struct ProbeProcessor {
precedence: i32,
}
impl IProcessor for ProbeProcessor {
fn get_template_mode(&self) -> Option<TemplateMode> {
Some(TemplateMode::HTML)
}
fn get_precedence(&self) -> i32 {
self.precedence
}
}
#[test]
fn preserves_null_identity_uniqueness_iteration_order_and_custom_equality() {
let mut processors = ProcessorSet::new();
assert!(processors.is_empty());
assert_eq!(processors.len(), 0);
let first: Arc<dyn IProcessor> = Arc::new(ProbeProcessor { precedence: 10 });
let same_value: Arc<dyn IProcessor> = Arc::new(ProbeProcessor { precedence: 10 });
let distinct: Arc<dyn IProcessor> = Arc::new(ProbeProcessor { precedence: 20 });
assert!(processors.insert(None));
assert!(!processors.insert(None));
assert!(processors.insert(Some(Arc::clone(&first))));
assert!(!processors.insert(Some(Arc::clone(&first))));
assert!(processors.insert(Some(Arc::clone(&same_value))));
assert!(processors.insert(Some(Arc::clone(&distinct))));
assert!(!processors.insert_with(
Some(Arc::new(ProbeProcessor { precedence: 10 })),
|candidate, existing| candidate.get_precedence() == existing.get_precedence(),
));
assert!(processors.insert_with(
Some(Arc::new(ProbeProcessor { precedence: 30 })),
|candidate, existing| candidate.get_precedence() == existing.get_precedence(),
));
let observed = processors
.iter()
.map(|processor| {
processor.map(|value| (value.get_template_mode(), value.get_precedence()))
})
.collect::<Vec<_>>();
assert_eq!(
observed,
[
None,
Some((Some(TemplateMode::HTML), 10)),
Some((Some(TemplateMode::HTML), 10)),
Some((Some(TemplateMode::HTML), 20)),
Some((Some(TemplateMode::HTML), 30)),
]
);
assert_eq!(processors.iter().len(), 5);
assert!(!processors.is_empty());
assert_eq!(processors.len(), 5);
}
}