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
use std::fmt::{Debug, Formatter};
/// Java `String` 的 UTF-16 代码单元适配值。
///
/// 对应 Java: `java.lang.String`,由
/// `org.thymeleaf.util.LoggingUtils#loggifyTemplateName(String)` 使用。
///
/// Java `String#substring` 可以在代理对中间切分,产生 Rust `String` 无法表示的孤立
/// 代理项。本类型保存原始 UTF-16 代码单元,确保模板名日志截断不会用替换字符改变
/// 上游结果。
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Utf16String {
utf16: Vec<u16>,
}
impl Utf16String {
/// 从有效 Rust 字符串创建 Java 字符串。
///
/// # 参数
/// - `value`:待编码为 UTF-16 的字符串。
///
/// # 返回
/// 保存与 Java `String` 相同代码单元的值。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn from_rust_str(value: &str) -> Self {
Self {
utf16: value.encode_utf16().collect(),
}
}
/// 从任意 Java UTF-16 代码单元创建字符串。
///
/// # 参数
/// - `utf16`:包括可能孤立代理项在内的原始代码单元。
///
/// # 返回
/// 不执行 Unicode 修复或替换的 Java 字符串适配值。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn from_utf16(utf16: impl Into<Vec<u16>>) -> Self {
Self {
utf16: utf16.into(),
}
}
/// 返回 Java `String#length()`。
///
/// # 返回
/// UTF-16 代码单元数量,而不是 Unicode 标量或 UTF-8 字节数。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn len(&self) -> usize {
self.utf16.len()
}
/// 判断 Java 字符串是否为空。
///
/// # 返回
/// 不含 UTF-16 代码单元时返回 `true`。
/// 对应 Java 语义:Java 接口/超类方法 `isEmpty()` 的 Rust 移植(`None` 继承路径)。
#[must_use]
pub fn is_empty(&self) -> bool {
self.utf16.is_empty()
}
/// 返回原始 UTF-16 代码单元。
///
/// # 返回
/// 与 Java `charAt`/`substring` 使用的代码单元序列相同的只读切片。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn as_utf16(&self) -> &[u16] {
&self.utf16
}
/// 转换为可显示的 Rust 字符串。
///
/// # 返回
/// 有效代理对按原字符解码;孤立代理项按 Rust 标准规则显示为替换字符。
///
/// 精确协议比较应使用 [`Self::as_utf16`],不能使用本有损显示入口。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn to_string_lossy(&self) -> String {
String::from_utf16_lossy(&self.utf16)
}
}
impl Debug for Utf16String {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Utf16String")
.field("utf16", &self.utf16)
.finish()
}
}
/// `LoggingUtils` 返回的 Java 字符串引用或新值。
///
/// 对应 Java `String.replace(char,char)` 在找不到换行符时返回原对象,以及需要替换
/// 或截断时创建新对象的身份语义。
#[derive(Debug)]
pub enum Utf16StringResult<'a> {
/// 返回输入的同一个 Java 字符串对象。
Borrowed(&'a Utf16String),
/// 返回新创建的 Java 字符串对象。
Owned(Utf16String),
}
impl<'a> Utf16StringResult<'a> {
/// 返回结果 Java 字符串。
///
/// # 返回
/// 借用或拥有分支中的统一只读引用。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn as_utf16_string(&self) -> &Utf16String {
match self {
Self::Borrowed(value) => value,
Self::Owned(value) => value,
}
}
/// 判断结果是否借用了指定输入对象。
///
/// # 参数
/// - `source`:待比较引用身份的 Java 字符串。
///
/// # 返回
/// 结果为同一借用对象时返回 `true`。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn is_borrowed_from(&self, source: &Utf16String) -> bool {
matches!(self, Self::Borrowed(value) if std::ptr::eq(*value, source))
}
/// 将结果转换为独立拥有的 Java 字符串。
///
/// # 返回
/// 已拥有结果直接移动;借用结果克隆相同 UTF-16 代码单元。
/// 对应 Java 语义:Rust 侧辅助函数(Java 无直接对应)。
#[must_use]
pub fn into_owned(self) -> Utf16String {
match self {
Self::Borrowed(value) => value.clone(),
Self::Owned(value) => value,
}
}
}
#[cfg(test)]
mod tests {
use super::{Utf16String, Utf16StringResult};
#[test]
fn preserves_null_empty_and_short_reference_identity() {
let empty = Utf16String::from_rust_str("");
assert!(empty.is_empty());
let result = Utf16StringResult::Borrowed(&empty);
assert!(result.is_borrowed_from(&empty));
assert_eq!(result.as_utf16_string().as_utf16(), &[] as &[u16]);
assert_eq!(result.as_utf16_string().to_string_lossy(), "");
assert_eq!(format!("{empty:?}"), "Utf16String { utf16: [] }");
assert!(
Utf16StringResult::Owned(empty.clone())
.into_owned()
.as_utf16()
.is_empty()
);
}
#[test]
fn preserves_isolated_surrogates_without_replacement() {
let mut utf16 = vec![0xD83D];
utf16.extend("abc".encode_utf16());
let value = Utf16String::from_utf16(utf16);
assert_eq!(value.len(), 4);
assert_eq!(value.as_utf16()[0], 0xD83D);
}
}