1use nom::{bytes::complete::take, number::complete::le_u64};
18use std::io::Write;
19use std::time::{Duration, SystemTime};
20use thiserror::Error;
21
22use super::read::SudachiNomResult;
23use crate::error::{SudachiError, SudachiResult};
24
25#[derive(Error, Debug, Eq, PartialEq)]
27#[non_exhaustive]
28pub enum HeaderError {
29 #[error("Invalid header version")]
30 InvalidVersion,
31
32 #[error("Invalid system dictionary version")]
33 InvalidSystemDictVersion,
34
35 #[error("Invalid user dictionary version")]
36 InvalidUserDictVersion,
37
38 #[error("Unable to parse")]
39 CannotParse,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum HeaderVersion {
45 SystemDict(SystemDictVersion),
46 UserDict(UserDictVersion),
47}
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum SystemDictVersion {
50 Version1,
52 Version2,
53}
54
55impl HeaderVersion {
56 pub fn to_u64(&self) -> u64 {
57 #[allow(unreachable_patterns)]
58 match self {
59 HeaderVersion::SystemDict(SystemDictVersion::Version1) => {
60 HeaderVersion::SYSTEM_DICT_VERSION_1
61 }
62 HeaderVersion::SystemDict(SystemDictVersion::Version2) => {
63 HeaderVersion::SYSTEM_DICT_VERSION_2
64 }
65 HeaderVersion::UserDict(UserDictVersion::Version1) => {
66 HeaderVersion::USER_DICT_VERSION_1
67 }
68 HeaderVersion::UserDict(UserDictVersion::Version2) => {
69 HeaderVersion::USER_DICT_VERSION_2
70 }
71 HeaderVersion::UserDict(UserDictVersion::Version3) => {
72 HeaderVersion::USER_DICT_VERSION_3
73 }
74 _ => panic!("unknown version {:?}", self),
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub enum UserDictVersion {
81 Version1,
82 Version2,
83 Version3,
84}
85impl HeaderVersion {
86 const SYSTEM_DICT_VERSION_1: u64 = 0x7366d3f18bd111e7;
88 const SYSTEM_DICT_VERSION_2: u64 = 0xce9f011a92394434;
90 const USER_DICT_VERSION_1: u64 = 0xa50f31188bd211e7;
92 const USER_DICT_VERSION_2: u64 = 0x9fdeb5a90168d868;
94 const USER_DICT_VERSION_3: u64 = 0xca9811756ff64fb0;
96
97 pub fn from_u64(v: u64) -> Option<Self> {
98 match v {
99 HeaderVersion::SYSTEM_DICT_VERSION_1 => {
100 Some(Self::SystemDict(SystemDictVersion::Version1))
101 }
102 HeaderVersion::SYSTEM_DICT_VERSION_2 => {
103 Some(Self::SystemDict(SystemDictVersion::Version2))
104 }
105 HeaderVersion::USER_DICT_VERSION_1 => Some(Self::UserDict(UserDictVersion::Version1)),
106 HeaderVersion::USER_DICT_VERSION_2 => Some(Self::UserDict(UserDictVersion::Version2)),
107 HeaderVersion::USER_DICT_VERSION_3 => Some(Self::UserDict(UserDictVersion::Version3)),
108 _ => None,
109 }
110 }
111}
112
113#[derive(Debug, Clone, Eq, PartialEq)]
117pub struct Header {
118 pub version: HeaderVersion,
119 pub create_time: u64,
120 pub description: String,
121}
122
123impl Default for Header {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl Header {
130 const DESCRIPTION_SIZE: usize = 256;
131 pub const STORAGE_SIZE: usize = 8 + 8 + Header::DESCRIPTION_SIZE;
132
133 pub fn new() -> Self {
136 let unix_time = SystemTime::now()
137 .duration_since(SystemTime::UNIX_EPOCH)
138 .expect("unix time error");
139
140 Self {
141 version: HeaderVersion::SystemDict(SystemDictVersion::Version2),
142 create_time: unix_time.as_secs(),
143 description: String::new(),
144 }
145 }
146
147 pub fn set_time(&mut self, time: SystemTime) -> SystemTime {
149 let unix_time = time
150 .duration_since(SystemTime::UNIX_EPOCH)
151 .expect("unix time error");
152
153 let old_unix_secs = std::mem::replace(&mut self.create_time, unix_time.as_secs());
154
155 SystemTime::UNIX_EPOCH + Duration::from_secs(old_unix_secs)
156 }
157
158 pub fn parse(bytes: &[u8]) -> Result<Header, HeaderError> {
160 let (_rest, (version, create_time, description)) =
161 header_parser(bytes).map_err(|_| HeaderError::CannotParse)?;
162
163 let version = HeaderVersion::from_u64(version).ok_or(HeaderError::InvalidVersion)?;
164
165 Ok(Header {
166 version,
167 create_time,
168 description,
169 })
170 }
171
172 pub fn has_grammar(&self) -> bool {
174 matches!(
175 self.version,
176 HeaderVersion::SystemDict(_)
177 | HeaderVersion::UserDict(UserDictVersion::Version2)
178 | HeaderVersion::UserDict(UserDictVersion::Version3)
179 )
180 }
181
182 pub fn has_synonym_group_ids(&self) -> bool {
184 matches!(
185 self.version,
186 HeaderVersion::SystemDict(SystemDictVersion::Version2)
187 | HeaderVersion::UserDict(UserDictVersion::Version3)
188 )
189 }
190
191 pub fn write_to<W: Write>(&self, w: &mut W) -> SudachiResult<usize> {
192 if self.description.len() > Header::DESCRIPTION_SIZE {
193 return Err(SudachiError::InvalidDataFormat(
194 Header::DESCRIPTION_SIZE,
195 self.description.clone(),
196 ));
197 }
198
199 w.write_all(&self.version.to_u64().to_le_bytes())?;
200 w.write_all(&self.create_time.to_le_bytes())?;
201 w.write_all(self.description.as_bytes())?;
202 for _ in 0..Header::DESCRIPTION_SIZE - self.description.len() {
203 w.write_all(&[0])?;
204 }
205 Ok(Header::STORAGE_SIZE)
206 }
207}
208
209fn nul_terminated_str_from_slice(buf: &[u8]) -> String {
211 let str_bytes: &[u8] = if let Some(nul_idx) = buf.iter().position(|b| *b == 0) {
212 &buf[..nul_idx]
213 } else {
214 buf
215 };
216 String::from_utf8_lossy(str_bytes).to_string()
217}
218
219fn description_parser(input: &[u8]) -> SudachiNomResult<&[u8], String> {
220 let (rest, description_bytes) = take(Header::DESCRIPTION_SIZE)(input)?;
221 Ok((rest, nul_terminated_str_from_slice(description_bytes)))
222}
223
224fn header_parser(input: &[u8]) -> SudachiNomResult<&[u8], (u64, u64, String)> {
225 nom::sequence::tuple((le_u64, le_u64, description_parser))(input)
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 fn header_from_parts<T: AsRef<[u8]>>(
233 version: u64,
234 create_time: u64,
235 description: T,
236 ) -> Result<Header, HeaderError> {
237 let mut bytes = Vec::new();
238 bytes.extend(&version.to_le_bytes());
239 bytes.extend(&create_time.to_le_bytes());
240 bytes.extend(description.as_ref());
241
242 Header::parse(&bytes)
243 }
244
245 #[test]
246 fn graceful_failure() {
247 assert_eq!(Header::parse(&[]), Err(HeaderError::CannotParse));
249
250 assert_eq!(
251 header_from_parts(42, 0, vec![0; Header::DESCRIPTION_SIZE]),
252 Err(HeaderError::InvalidVersion)
253 );
254 }
255
256 #[test]
257 fn simple_header() {
258 let mut description: Vec<u8> = Vec::new();
259 let description_str = "My Description";
260 description.extend(description_str.bytes());
261 description.extend(&vec![0; Header::DESCRIPTION_SIZE]);
262
263 assert_eq!(
264 header_from_parts(HeaderVersion::SYSTEM_DICT_VERSION_1, 1337, &description),
265 Ok(Header {
266 version: HeaderVersion::SystemDict(SystemDictVersion::Version1),
267 description: description_str.to_string(),
268 create_time: 1337,
269 })
270 );
271 }
272
273 #[test]
274 fn write_system() {
275 let header = Header::new();
276 let mut data: Vec<u8> = Vec::new();
277 assert_eq!(header.write_to(&mut data).unwrap(), Header::STORAGE_SIZE);
278 let header2 = Header::parse(&data).unwrap();
279 assert_eq!(header, header2);
280 }
281
282 #[test]
283 fn write_user() {
284 let mut header = Header::new();
285 header.version = HeaderVersion::UserDict(UserDictVersion::Version3);
286 header.description = String::from("some great header");
287 let mut data: Vec<u8> = Vec::new();
288 assert_eq!(header.write_to(&mut data).unwrap(), Header::STORAGE_SIZE);
289 let header2 = Header::parse(&data).unwrap();
290 assert_eq!(header, header2);
291 }
292}