concept_graph/
identifiers.rs1use std::io::{self, Read, Write};
13
14use crate::ordinal::{Ordinal, to_usize};
15
16const MAGIC: &[u8; 8] = b"FTIDENT\0";
17const VERSION: u32 = 1;
18
19#[derive(Debug, thiserror::Error)]
21pub enum IdentifiersError {
22 #[error("identifiers I/O failed")]
24 Io(#[from] io::Error),
25 #[error("not an identifiers artifact")]
27 Magic,
28 #[error("identifiers layout version {found}, expected {expected}")]
30 Version {
31 found: u32,
33 expected: u32,
35 },
36 #[error("too many identifiers")]
38 TooMany(#[source] std::num::TryFromIntError),
39 #[error("an alternate identifier is not UTF-8")]
41 Text(#[from] std::string::FromUtf8Error),
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Default)]
46pub struct Identifiers {
47 entries: Vec<(u64, String, u32)>,
48}
49
50impl Identifiers {
51 #[must_use]
53 pub fn new(mut entries: Vec<(u64, String, Ordinal)>) -> Self {
54 entries.sort();
55 entries.dedup();
56 Self {
57 entries: entries
58 .into_iter()
59 .map(|(scheme, code, concept)| (scheme, code, concept.index()))
60 .collect(),
61 }
62 }
63
64 #[must_use]
67 pub fn size_in_bytes(&self) -> usize {
68 self.entries
69 .iter()
70 .fold(crate::footprint::vector(&self.entries), |total, entry| {
71 total.saturating_add(entry.1.capacity())
72 })
73 }
74
75 #[must_use]
77 pub fn lookup(&self, scheme: u64, code: &str) -> Option<Ordinal> {
78 self.entries
79 .binary_search_by(|(s, c, _)| (*s, c.as_str()).cmp(&(scheme, code)))
80 .ok()
81 .and_then(|i| self.entries.get(i))
82 .map(|(_, _, concept)| Ordinal::new(*concept))
83 }
84
85 #[must_use]
87 pub fn schemes(&self) -> Vec<u64> {
88 let mut schemes: Vec<u64> = self.entries.iter().map(|(s, _, _)| *s).collect();
89 schemes.dedup();
90 schemes
91 }
92
93 #[must_use]
95 pub fn len(&self) -> usize {
96 self.entries.len()
97 }
98
99 #[must_use]
101 pub fn is_empty(&self) -> bool {
102 self.entries.is_empty()
103 }
104
105 pub fn write_to(&self, out: &mut impl Write) -> Result<(), IdentifiersError> {
111 out.write_all(MAGIC)?;
112 out.write_all(&VERSION.to_le_bytes())?;
113 let count = u32::try_from(self.entries.len()).map_err(IdentifiersError::TooMany)?;
114 out.write_all(&count.to_le_bytes())?;
115 for (scheme, code, concept) in &self.entries {
116 out.write_all(&scheme.to_le_bytes())?;
117 let len = u32::try_from(code.len()).map_err(IdentifiersError::TooMany)?;
118 out.write_all(&len.to_le_bytes())?;
119 out.write_all(code.as_bytes())?;
120 out.write_all(&concept.to_le_bytes())?;
121 }
122 Ok(())
123 }
124
125 pub fn read_from(input: &mut impl Read) -> Result<Self, IdentifiersError> {
131 let mut magic = [0_u8; 8];
132 input.read_exact(&mut magic)?;
133 if &magic != MAGIC {
134 return Err(IdentifiersError::Magic);
135 }
136 let version = read_u32(input)?;
137 if version != VERSION {
138 return Err(IdentifiersError::Version {
139 found: version,
140 expected: VERSION,
141 });
142 }
143 let count = read_u32(input)?;
144 let mut entries = Vec::with_capacity(to_usize(count));
145 for _ in 0..count {
146 let mut long = [0_u8; 8];
147 input.read_exact(&mut long)?;
148 let len = to_usize(read_u32(input)?);
149 let mut bytes = vec![0_u8; len];
150 input.read_exact(&mut bytes)?;
151 let concept = read_u32(input)?;
152 entries.push((u64::from_le_bytes(long), String::from_utf8(bytes)?, concept));
153 }
154 entries.sort();
155 Ok(Self { entries })
156 }
157}
158
159fn read_u32(input: &mut impl Read) -> Result<u32, IdentifiersError> {
160 let mut bytes = [0_u8; 4];
161 input.read_exact(&mut bytes)?;
162 Ok(u32::from_le_bytes(bytes))
163}
164
165#[cfg(test)]
166mod tests {
167 use super::{Identifiers, IdentifiersError};
168 use crate::ordinal::Ordinal;
169
170 #[test]
171 fn codes_resolve_per_scheme_and_the_layout_round_trips() {
172 let identifiers = Identifiers::new(vec![
173 (705_114_005, String::from("54486-6"), Ordinal::new(3)),
174 (705_114_005, String::from("1234-5"), Ordinal::new(4)),
175 (900, String::from("54486-6"), Ordinal::new(5)),
176 ]);
177 assert_eq!(
178 identifiers.lookup(705_114_005, "54486-6"),
179 Some(Ordinal::new(3))
180 );
181 assert_eq!(identifiers.lookup(900, "54486-6"), Some(Ordinal::new(5)));
182 assert_eq!(identifiers.lookup(705_114_005, "9999"), None);
183 assert_eq!(identifiers.schemes(), [900, 705_114_005]);
184 let mut bytes = Vec::new();
185 identifiers.write_to(&mut bytes).expect("writes");
186 assert_eq!(
187 Identifiers::read_from(&mut bytes.as_slice()).expect("reads"),
188 identifiers
189 );
190 assert!(matches!(
191 Identifiers::read_from(&mut b"XXXXXXXX\0\0\0\0".as_slice()),
192 Err(IdentifiersError::Magic)
193 ));
194 }
195}