sim_lib_serial_core/
alphabet.rs1use crate::AlphabetError;
4use std::collections::BTreeMap;
5use std::fmt::{Debug, Display, Formatter};
6
7#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct AlphabetId(String);
13
14impl AlphabetId {
15 pub fn try_new(value: impl Into<String>) -> Result<Self, AlphabetError> {
17 let value = value.into();
18 validate_stable_id(&value).map_err(|reason| AlphabetError::InvalidId {
19 value: value.clone(),
20 reason,
21 })?;
22 Ok(Self(value))
23 }
24
25 pub fn as_str(&self) -> &str {
27 &self.0
28 }
29}
30
31impl Display for AlphabetId {
32 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
33 Display::fmt(&self.0, formatter)
34 }
35}
36
37pub trait SerialAlphabet: Clone + Eq + Debug {
39 type Symbol: Clone + Eq + Ord + Debug;
41
42 fn id(&self) -> &AlphabetId;
44
45 fn symbols(&self) -> &[Self::Symbol];
47}
48
49#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct FiniteAlphabet<S>
52where
53 S: Clone + Eq + Ord + Debug,
54{
55 id: AlphabetId,
56 symbols: Vec<S>,
57}
58
59impl<S> FiniteAlphabet<S>
60where
61 S: Clone + Eq + Ord + Debug,
62{
63 pub fn try_new(id: AlphabetId, symbols: Vec<S>) -> Result<Self, AlphabetError> {
65 validate_symbols(&id, &symbols)?;
66 Ok(Self { id, symbols })
67 }
68
69 pub fn position(&self, symbol: &S) -> Option<usize> {
71 self.symbols
72 .iter()
73 .position(|candidate| candidate == symbol)
74 }
75}
76
77impl<S> SerialAlphabet for FiniteAlphabet<S>
78where
79 S: Clone + Eq + Ord + Debug,
80{
81 type Symbol = S;
82
83 fn id(&self) -> &AlphabetId {
84 &self.id
85 }
86
87 fn symbols(&self) -> &[Self::Symbol] {
88 &self.symbols
89 }
90}
91
92#[derive(Clone, Debug, PartialEq, Eq)]
96pub struct AlphabetRegistry<A: SerialAlphabet> {
97 alphabets: BTreeMap<AlphabetId, A>,
98}
99
100impl<A: SerialAlphabet> Default for AlphabetRegistry<A> {
101 fn default() -> Self {
102 Self {
103 alphabets: BTreeMap::new(),
104 }
105 }
106}
107
108impl<A: SerialAlphabet> AlphabetRegistry<A> {
109 pub fn new() -> Self {
111 Self::default()
112 }
113
114 pub fn insert(&mut self, alphabet: A) -> Result<(), AlphabetError> {
116 validate_alphabet(&alphabet)?;
117 if self.alphabets.contains_key(alphabet.id()) {
118 return Err(AlphabetError::DuplicateId(alphabet.id().clone()));
119 }
120 self.alphabets.insert(alphabet.id().clone(), alphabet);
121 Ok(())
122 }
123
124 pub fn get(&self, id: &AlphabetId) -> Option<&A> {
126 self.alphabets.get(id)
127 }
128
129 pub fn len(&self) -> usize {
131 self.alphabets.len()
132 }
133
134 pub fn is_empty(&self) -> bool {
136 self.alphabets.is_empty()
137 }
138}
139
140pub(crate) fn validate_alphabet<A: SerialAlphabet>(
141 alphabet: &A,
142) -> Result<BTreeMap<A::Symbol, usize>, AlphabetError> {
143 validate_symbols(alphabet.id(), alphabet.symbols())
144}
145
146fn validate_symbols<S>(id: &AlphabetId, symbols: &[S]) -> Result<BTreeMap<S, usize>, AlphabetError>
147where
148 S: Clone + Eq + Ord + Debug,
149{
150 if symbols.is_empty() {
151 return Err(AlphabetError::Empty { id: id.clone() });
152 }
153 let mut positions = BTreeMap::new();
154 for (position, symbol) in symbols.iter().cloned().enumerate() {
155 if let Some(first) = positions.insert(symbol, position) {
156 return Err(AlphabetError::DuplicateSymbol {
157 id: id.clone(),
158 first,
159 duplicate: position,
160 });
161 }
162 }
163 Ok(positions)
164}
165
166pub(crate) fn validate_stable_id(value: &str) -> Result<(), &'static str> {
167 if value.is_empty() {
168 return Err("id must not be empty");
169 }
170 if value.starts_with('/') || value.ends_with('/') || value.contains("//") {
171 return Err("id must use non-empty path segments");
172 }
173 if !value
174 .bytes()
175 .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-' | b'/'))
176 {
177 return Err("id contains a non-portable character");
178 }
179 Ok(())
180}