main
tag: launch-2026-06-01
tag: magicmirror-v0.1.1
tag: magicmirror-v0.3.0
tag: mnw-cli-v0.1.2
tag: mnw-cli-v0.1.3
tag: mnw-cli-v0.1.4
tag: pom-v0.4.1
tag: pom-v0.4.2
tag: pom-v0.4.3
tag: pom-v0.4.4
tag: pom-v0.4.5
tag: wam-v0.3.0
tag: wam-v0.3.1
Files
Commits
Tags
Notes
Issues
tagtree: count max_length in characters, not bytes
TagConfig, TagError::TooLong, and the crate docs all describe max_length
in characters; validate_with measured bytes. A multi-byte tag under the
limit was reported as TooLong rather than InvalidChar.
Byte length bounds character count from above, so the cheap check still
gates the O(n) one and the ASCII path is unchanged. Non-ASCII tags were
rejected before and are rejected now; only the reported error moves.
Co-Authored-By
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 files changed,
+33 insertions,
-1 deletion
2
2
3
3
Notable changes to tagtree. Versions follow semver.
4
4
5
+
## Unreleased
6
+
7
+
### Fixed
8
+
9
+
- `max_length` now counts characters, matching what `TagConfig`, `TagError::TooLong`,
10
+
and the crate docs have always said. It measured bytes, so a multi-byte tag under
11
+
the limit was reported as `TooLong` instead of `InvalidChar`. Only the reported
12
+
error changes: non-ASCII tags were rejected before and are rejected now.
13
+
5
14
## 0.4.0 (2026-07-16)
6
15
7
16
### Breaking
141
141
if tag.is_empty() {
142
142
return Err(TagError::Empty);
143
143
}
144
-
if tag.len() > config.max_length {
144
+
// `max_length` is documented in characters. Byte length is an exact upper
145
+
// bound on character count, so the cheap check gates the O(n) one: only a
146
+
// tag whose bytes overflow can possibly overflow in characters.
147
+
if tag.len() > config.max_length && tag.chars().count() > config.max_length {
145
148
return Err(TagError::TooLong { max: config.max_length });
146
149
}
147
150
if tag.starts_with(SEPARATOR) || tag.ends_with(SEPARATOR) {
1026
1029
assert!(validate_with("abcdefghijk", &cfg).is_err()); // 11 chars
1027
1030
}
1028
1031
1032
+
#[test]
1033
+
fn max_length_counts_characters_not_bytes() {
1034
+
let cfg = TagConfig {
1035
+
max_depth: 10,
1036
+
max_length: 5,
1037
+
semantic_depth: 0,
1038
+
};
1039
+
// 3 chars, 9 bytes. Over the limit in bytes, under it in characters,
1040
+
// so the charset check gets to report the real problem.
1041
+
assert_eq!(
1042
+
validate_with("日本語", &cfg),
1043
+
Err(TagError::InvalidChar('日'))
1044
+
);
1045
+
// Still TooLong when the character count genuinely overflows.
1046
+
assert_eq!(
1047
+
validate_with("日本語日本語", &cfg),
1048
+
Err(TagError::TooLong { max: 5 })
1049
+
);
1050
+
}
1051
+
1029
1052
#[test]
1030
1053
fn config_enforces_semantic_depth() {
1031
1054
// semantic_depth=1 requires at least 2 segments