oxidize_pdf/objects/
primitive.rs

1use crate::objects::Dictionary;
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct ObjectId {
6    number: u32,
7    generation: u16,
8}
9
10impl ObjectId {
11    pub fn new(number: u32, generation: u16) -> Self {
12        Self { number, generation }
13    }
14
15    pub fn number(&self) -> u32 {
16        self.number
17    }
18
19    pub fn generation(&self) -> u16 {
20        self.generation
21    }
22}
23
24impl fmt::Display for ObjectId {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        write!(f, "{} {} R", self.number, self.generation)
27    }
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum Object {
32    Null,
33    Boolean(bool),
34    Integer(i64),
35    Real(f64),
36    String(String),
37    Name(String),
38    Array(Vec<Object>),
39    Dictionary(Dictionary),
40    Stream(Dictionary, Vec<u8>),
41    Reference(ObjectId),
42}
43
44impl Object {
45    pub fn is_null(&self) -> bool {
46        matches!(self, Object::Null)
47    }
48
49    pub fn as_bool(&self) -> Option<bool> {
50        match self {
51            Object::Boolean(b) => Some(*b),
52            _ => None,
53        }
54    }
55
56    pub fn as_integer(&self) -> Option<i64> {
57        match self {
58            Object::Integer(i) => Some(*i),
59            _ => None,
60        }
61    }
62
63    pub fn as_real(&self) -> Option<f64> {
64        match self {
65            Object::Real(f) => Some(*f),
66            Object::Integer(i) => Some(*i as f64),
67            _ => None,
68        }
69    }
70
71    pub fn as_string(&self) -> Option<&str> {
72        match self {
73            Object::String(s) => Some(s),
74            _ => None,
75        }
76    }
77
78    pub fn as_name(&self) -> Option<&str> {
79        match self {
80            Object::Name(n) => Some(n),
81            _ => None,
82        }
83    }
84
85    pub fn as_array(&self) -> Option<&Vec<Object>> {
86        match self {
87            Object::Array(arr) => Some(arr),
88            _ => None,
89        }
90    }
91
92    pub fn as_dict(&self) -> Option<&Dictionary> {
93        match self {
94            Object::Dictionary(dict) => Some(dict),
95            _ => None,
96        }
97    }
98}
99
100impl From<bool> for Object {
101    fn from(b: bool) -> Self {
102        Object::Boolean(b)
103    }
104}
105
106impl From<i32> for Object {
107    fn from(i: i32) -> Self {
108        Object::Integer(i as i64)
109    }
110}
111
112impl From<i64> for Object {
113    fn from(i: i64) -> Self {
114        Object::Integer(i)
115    }
116}
117
118impl From<f32> for Object {
119    fn from(f: f32) -> Self {
120        Object::Real(f as f64)
121    }
122}
123
124impl From<f64> for Object {
125    fn from(f: f64) -> Self {
126        Object::Real(f)
127    }
128}
129
130impl From<String> for Object {
131    fn from(s: String) -> Self {
132        Object::String(s)
133    }
134}
135
136impl From<&str> for Object {
137    fn from(s: &str) -> Self {
138        Object::String(s.to_string())
139    }
140}
141
142impl From<Vec<Object>> for Object {
143    fn from(v: Vec<Object>) -> Self {
144        Object::Array(v)
145    }
146}
147
148impl From<Dictionary> for Object {
149    fn from(d: Dictionary) -> Self {
150        Object::Dictionary(d)
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test_object_id_new() {
160        let id = ObjectId::new(42, 5);
161        assert_eq!(id.number(), 42);
162        assert_eq!(id.generation(), 5);
163    }
164
165    #[test]
166    fn test_object_id_display() {
167        let id = ObjectId::new(10, 0);
168        assert_eq!(format!("{}", id), "10 0 R");
169        
170        let id2 = ObjectId::new(999, 65535);
171        assert_eq!(format!("{}", id2), "999 65535 R");
172    }
173
174    #[test]
175    fn test_object_id_equality() {
176        let id1 = ObjectId::new(1, 0);
177        let id2 = ObjectId::new(1, 0);
178        let id3 = ObjectId::new(2, 0);
179        let id4 = ObjectId::new(1, 1);
180        
181        assert_eq!(id1, id2);
182        assert_ne!(id1, id3);
183        assert_ne!(id1, id4);
184    }
185
186    #[test]
187    fn test_object_null() {
188        let obj = Object::Null;
189        assert!(obj.is_null());
190        assert!(obj.as_bool().is_none());
191        assert!(obj.as_integer().is_none());
192        assert!(obj.as_real().is_none());
193        assert!(obj.as_string().is_none());
194        assert!(obj.as_name().is_none());
195        assert!(obj.as_array().is_none());
196        assert!(obj.as_dict().is_none());
197    }
198
199    #[test]
200    fn test_object_boolean() {
201        let obj_true = Object::Boolean(true);
202        let obj_false = Object::Boolean(false);
203        
204        assert!(!obj_true.is_null());
205        assert_eq!(obj_true.as_bool(), Some(true));
206        assert_eq!(obj_false.as_bool(), Some(false));
207        assert!(obj_true.as_integer().is_none());
208    }
209
210    #[test]
211    fn test_object_integer() {
212        let obj = Object::Integer(42);
213        
214        assert_eq!(obj.as_integer(), Some(42));
215        assert_eq!(obj.as_real(), Some(42.0));
216        assert!(obj.as_bool().is_none());
217        assert!(obj.as_string().is_none());
218    }
219
220    #[test]
221    fn test_object_real() {
222        let obj = Object::Real(3.14159);
223        
224        assert_eq!(obj.as_real(), Some(3.14159));
225        assert!(obj.as_integer().is_none());
226        assert!(obj.as_bool().is_none());
227    }
228
229    #[test]
230    fn test_object_string() {
231        let obj = Object::String("Hello PDF".to_string());
232        
233        assert_eq!(obj.as_string(), Some("Hello PDF"));
234        assert!(obj.as_name().is_none());
235        assert!(obj.as_integer().is_none());
236    }
237
238    #[test]
239    fn test_object_name() {
240        let obj = Object::Name("Type".to_string());
241        
242        assert_eq!(obj.as_name(), Some("Type"));
243        assert!(obj.as_string().is_none());
244        assert!(obj.as_integer().is_none());
245    }
246
247    #[test]
248    fn test_object_array() {
249        let arr = vec![
250            Object::Integer(1),
251            Object::Integer(2),
252            Object::Integer(3),
253        ];
254        let obj = Object::Array(arr.clone());
255        
256        assert_eq!(obj.as_array(), Some(&arr));
257        assert!(obj.as_dict().is_none());
258    }
259
260    #[test]
261    fn test_object_dictionary() {
262        let mut dict = Dictionary::new();
263        dict.set("Key", "Value");
264        let obj = Object::Dictionary(dict.clone());
265        
266        assert_eq!(obj.as_dict(), Some(&dict));
267        assert!(obj.as_array().is_none());
268    }
269
270    #[test]
271    fn test_object_stream() {
272        let mut dict = Dictionary::new();
273        dict.set("Length", 5);
274        let data = vec![1, 2, 3, 4, 5];
275        let obj = Object::Stream(dict, data);
276        
277        // Stream doesn't have as_stream method, but we can pattern match
278        if let Object::Stream(d, data) = obj {
279            assert_eq!(d.get("Length"), Some(&Object::Integer(5)));
280            assert_eq!(data.len(), 5);
281        } else {
282            panic!("Expected Stream object");
283        }
284    }
285
286    #[test]
287    fn test_object_reference() {
288        let id = ObjectId::new(10, 0);
289        let obj = Object::Reference(id);
290        
291        // Reference doesn't have as_reference method, but we can pattern match
292        if let Object::Reference(ref_id) = obj {
293            assert_eq!(ref_id, id);
294        } else {
295            panic!("Expected Reference object");
296        }
297    }
298
299    #[test]
300    fn test_from_bool() {
301        let obj: Object = true.into();
302        assert_eq!(obj, Object::Boolean(true));
303        
304        let obj2: Object = false.into();
305        assert_eq!(obj2, Object::Boolean(false));
306    }
307
308    #[test]
309    fn test_from_integers() {
310        let obj: Object = 42i32.into();
311        assert_eq!(obj, Object::Integer(42));
312        
313        let obj2: Object = 9999i64.into();
314        assert_eq!(obj2, Object::Integer(9999));
315        
316        let obj3: Object = (-100i32).into();
317        assert_eq!(obj3, Object::Integer(-100));
318    }
319
320    #[test]
321    fn test_from_floats() {
322        let obj: Object = 3.14f32.into();
323        if let Object::Real(val) = obj {
324            assert!((val - 3.14).abs() < 0.001);
325        } else {
326            panic!("Expected Real object");
327        }
328        
329        let obj2: Object = 2.71828f64.into();
330        assert_eq!(obj2, Object::Real(2.71828));
331    }
332
333    #[test]
334    fn test_from_strings() {
335        let obj: Object = "Hello".into();
336        assert_eq!(obj, Object::String("Hello".to_string()));
337        
338        let obj2: Object = String::from("World").into();
339        assert_eq!(obj2, Object::String("World".to_string()));
340    }
341
342    #[test]
343    fn test_from_vec() {
344        let vec = vec![Object::Integer(1), Object::Integer(2)];
345        let obj: Object = vec.clone().into();
346        assert_eq!(obj, Object::Array(vec));
347    }
348
349    #[test]
350    fn test_from_dictionary() {
351        let mut dict = Dictionary::new();
352        dict.set("Test", 123);
353        let obj: Object = dict.clone().into();
354        
355        if let Object::Dictionary(d) = obj {
356            assert_eq!(d.get("Test"), Some(&Object::Integer(123)));
357        } else {
358            panic!("Expected Dictionary object");
359        }
360    }
361
362    #[test]
363    fn test_object_equality() {
364        assert_eq!(Object::Null, Object::Null);
365        assert_eq!(Object::Boolean(true), Object::Boolean(true));
366        assert_ne!(Object::Boolean(true), Object::Boolean(false));
367        assert_eq!(Object::Integer(42), Object::Integer(42));
368        assert_ne!(Object::Integer(42), Object::Integer(43));
369        assert_eq!(Object::String("A".to_string()), Object::String("A".to_string()));
370        assert_ne!(Object::String("A".to_string()), Object::String("B".to_string()));
371    }
372}