1mod convert;
2mod dump;
3mod load;
4mod value_arena;
5
6pub use self::convert::BTreeMapFromValueError;
7pub use self::convert::DisplayByteString;
8pub use self::convert::FromValue;
9pub use self::convert::FromValueContext;
10pub use self::convert::FromValueError;
11pub use self::convert::HashMapFromValueError;
12pub use self::convert::IntoValue;
13pub use self::convert::IntoValueError;
14pub use self::dump::dump;
15pub use self::load::load;
16pub use self::value_arena::ArrayValue;
17pub use self::value_arena::BoolValue;
18pub use self::value_arena::FixnumValue;
19pub use self::value_arena::HashValue;
20pub use self::value_arena::NilValue;
21pub use self::value_arena::ObjectValue;
22pub use self::value_arena::StringValue;
23pub use self::value_arena::SymbolValue;
24pub use self::value_arena::TypedValueHandle;
25pub use self::value_arena::UserDefinedValue;
26pub use self::value_arena::Value;
27pub use self::value_arena::ValueArena;
28pub use self::value_arena::ValueHandle;
29pub use self::value_arena::ValueKind;
30
31const MAJOR_VERSION: u8 = 4;
32const MINOR_VERSION: u8 = 8;
33
34const VALUE_KIND_NIL: u8 = b'0';
35const VALUE_KIND_TRUE: u8 = b'T';
36const VALUE_KIND_FALSE: u8 = b'F';
37const VALUE_KIND_FIXNUM: u8 = b'i';
38const VALUE_KIND_SYMBOL: u8 = b':';
39const VALUE_KIND_SYMBOL_LINK: u8 = b';';
40const VALUE_KIND_OBJECT_LINK: u8 = b'@';
41const VALUE_KIND_INSTANCE_VARIABLES: u8 = b'I';
42const VALUE_KIND_ARRAY: u8 = b'[';
43const VALUE_KIND_HASH: u8 = b'{';
44const VALUE_KIND_HASH_DEFAULT: u8 = b'}';
45const VALUE_KIND_OBJECT: u8 = b'o';
46const VALUE_KIND_STRING: u8 = b'"';
47const VALUE_KIND_USER_DEFINED: u8 = b'u';
48
49#[derive(Debug)]
51pub enum Error {
52 InvalidVersion {
54 major: u8,
56
57 minor: u8,
59 },
60
61 Io { error: std::io::Error },
63
64 InvalidValueKind { kind: u8 },
66
67 InvalidValueHandle {
69 handle: ValueHandle,
71 },
72
73 InvalidFixnumSize { size: u8 },
75
76 FixnumInvalidUSize { error: std::num::TryFromIntError },
78
79 USizeInvalidFixnum { error: std::num::TryFromIntError },
81
82 MissingSymbolLink { index: usize },
84
85 MissingObjectLink { index: usize },
87
88 UnexpectedValueKind { expected: u8, actual: u8 },
90
91 NotAnObject,
93
94 DuplicateInstanceVariable {
96 name: Vec<u8>,
98 },
99}
100
101impl std::fmt::Display for Error {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 Self::InvalidVersion { major, minor } => write!(f, "invalid version {major}.{minor}"),
105 Self::Io { .. } => write!(f, "I/O error"),
106 Self::InvalidValueKind { kind } => write!(f, "invalid value kind {kind}"),
107 Self::InvalidValueHandle { .. } => write!(f, "invalid value handle"),
108 Self::InvalidFixnumSize { size } => write!(f, "invalid fixnum size {size}"),
109 Self::FixnumInvalidUSize { .. } => write!(f, "fixnum is not a valid usize"),
110 Self::USizeInvalidFixnum { .. } => write!(f, "usize is not a valid Fixnum"),
111 Self::MissingSymbolLink { index } => write!(f, "missing symbol link {index}"),
112 Self::MissingObjectLink { index } => write!(f, "missing object link {index}"),
113 Self::UnexpectedValueKind { expected, actual } => write!(
114 f,
115 "unexpected value kind, expected {expected} but got {actual}"
116 ),
117 Self::NotAnObject => write!(f, "not an object"),
118 Self::DuplicateInstanceVariable { name } => {
119 write!(f, "duplicate instance variable \"{name:?}\"")
120 }
121 }
122 }
123}
124
125impl std::error::Error for Error {
126 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
127 match self {
128 Self::Io { error } => Some(error),
129 Self::FixnumInvalidUSize { error } => Some(error),
130 Self::USizeInvalidFixnum { error } => Some(error),
131 _ => None,
132 }
133 }
134}
135
136impl From<std::io::Error> for Error {
137 fn from(error: std::io::Error) -> Self {
138 Error::Io { error }
139 }
140}
141
142#[cfg(test)]
143mod test {
144 use super::*;
145 use std::io::Read;
146
147 #[test]
148 fn kitchen_sink() {
149 for entry in std::fs::read_dir("test_data").expect("failed to read \"test_data\"") {
150 let entry = entry.expect("failed to read entry");
151 let file_type = entry.file_type().expect("failed to get file type");
152 if file_type.is_dir() {
153 continue;
154 }
155 let entry_path = entry.path();
156
157 let data = std::fs::read(&entry_path).expect("failed to read entry");
158 let mut data_reader = std::io::Cursor::new(&data);
159
160 let value_arena = load(&mut data_reader).expect("failed to load");
161
162 let mut new_data = Vec::new();
163 dump(&mut new_data, &value_arena).expect("failed to dump");
164
165 let read_end_result = data_reader.read(&mut [0]);
166 let is_eof = matches!(read_end_result, Ok(0));
167 assert!(is_eof);
168 assert!(data == new_data, "{data:?} != {new_data:?}");
169 }
170 }
171
172 #[test]
173 fn duped_symbol() {
174 let mut value_arena = ValueArena::new();
175
176 let symbol_1 = value_arena.create_symbol("symbol".into()).into();
177 let symbol_2 = value_arena.create_symbol("symbol".into()).into();
178
179 let deduped_array = value_arena.create_array(vec![symbol_1, symbol_1]);
180 let duped_array = value_arena.create_array(vec![symbol_1, symbol_2]);
181
182 let mut deduped_data = Vec::new();
183 let mut duped_data = Vec::new();
184
185 value_arena.replace_root(deduped_array);
186 dump(&mut deduped_data, &value_arena).expect("failed to dump");
187
188 value_arena.replace_root(duped_array);
189 dump(&mut duped_data, &value_arena).expect("failed to dump");
190
191 assert!(deduped_data == duped_data);
192 }
193}