Skip to main content

code_moniker_core/core/moniker/
encoding.rs

1//! ```text
2//! [version u8 = 2]
3//! [project_len u16 LE] [project bytes]
4//!   segment[i] := [kind_len u16 LE] [kind bytes] [name_len u16 LE] [name bytes]
5//!   (repeated until end of buffer)
6//! ```
7
8use std::fmt;
9
10pub const VERSION: u8 = 2;
11pub const HEADER_FIXED_LEN: usize = 1 + 2;
12
13#[derive(Copy, Clone, Eq, PartialEq, Debug)]
14pub enum EncodingError {
15	Truncated,
16	UnknownVersion(u8),
17	ProjectOverflow,
18	SegmentOverflow,
19	EmptyProject,
20	NonUtf8Project,
21	NonUtf8SegmentKind,
22	NonUtf8SegmentName,
23	InvalidSegmentKind,
24	ProjectTooLong(usize),
25	SegmentKindTooLong(usize),
26	SegmentNameTooLong(usize),
27}
28
29impl fmt::Display for EncodingError {
30	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31		match self {
32			Self::Truncated => write!(f, "buffer too short for header"),
33			Self::UnknownVersion(v) => write!(f, "unknown encoding version: {v}"),
34			Self::ProjectOverflow => write!(f, "project bytes extend past buffer"),
35			Self::SegmentOverflow => write!(f, "segment extends past buffer"),
36			Self::EmptyProject => write!(f, "project must not be empty"),
37			Self::NonUtf8Project => write!(f, "project must be valid UTF-8"),
38			Self::NonUtf8SegmentKind => write!(f, "segment kind must be valid UTF-8"),
39			Self::NonUtf8SegmentName => write!(f, "segment name must be valid UTF-8"),
40			Self::InvalidSegmentKind => write!(
41				f,
42				"segment kind must be a URI identifier ([A-Za-z][A-Za-z0-9_]*)"
43			),
44			Self::ProjectTooLong(len) => {
45				write!(f, "project longer than u16::MAX bytes ({len})")
46			}
47			Self::SegmentKindTooLong(len) => {
48				write!(f, "segment kind longer than u16::MAX bytes ({len})")
49			}
50			Self::SegmentNameTooLong(len) => {
51				write!(f, "segment name longer than u16::MAX bytes ({len})")
52			}
53		}
54	}
55}
56
57impl std::error::Error for EncodingError {}
58
59pub fn read_u16(buf: &[u8], off: usize) -> u16 {
60	u16::from_le_bytes([buf[off], buf[off + 1]])
61}
62
63pub fn write_u16(buf: &mut Vec<u8>, value: u16) {
64	buf.extend_from_slice(&value.to_le_bytes());
65}
66
67pub fn is_uri_kind(bytes: &[u8]) -> bool {
68	let Some((first, rest)) = bytes.split_first() else {
69		return false;
70	};
71	first.is_ascii_alphabetic()
72		&& rest
73			.iter()
74			.all(|byte| byte.is_ascii_alphanumeric() || *byte == b'_')
75}