hayro_syntax/object/
ref.rs

1//! Object references.
2
3use crate::object::ObjectIdentifier;
4use crate::object::ObjectLike;
5use crate::reader::{Readable, Reader, Skippable};
6use crate::xref::XRef;
7use std::fmt::{Debug, Formatter};
8
9/// A reference to an object.
10#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
11pub struct ObjRef {
12    /// The object number.
13    pub obj_number: i32,
14    /// The generation nunber.
15    pub gen_number: i32,
16}
17
18impl ObjRef {
19    /// Create a new object reference.
20    pub fn new(obj_number: i32, gen_number: i32) -> Self {
21        Self {
22            obj_number,
23            gen_number,
24        }
25    }
26}
27
28impl From<ObjRef> for ObjectIdentifier {
29    fn from(value: ObjRef) -> Self {
30        ObjectIdentifier::new(value.obj_number, value.gen_number)
31    }
32}
33
34impl Skippable for ObjRef {
35    fn skip<const PLAIN: bool>(r: &mut Reader<'_>) -> Option<()> {
36        r.skip_non_plain::<i32>()?;
37        r.skip_white_spaces();
38        r.skip_non_plain::<i32>()?;
39        r.skip_white_spaces();
40        r.forward_tag(b"R")?;
41
42        Some(())
43    }
44}
45
46impl Readable<'_> for ObjRef {
47    fn read<const PLAIN: bool>(r: &mut Reader<'_>, _: &XRef) -> Option<Self> {
48        let obj_ref = r.read_without_xref::<i32>()?;
49        r.skip_white_spaces();
50        let gen_num = r.read_without_xref::<i32>()?;
51        r.skip_white_spaces();
52        r.forward_tag(b"R")?;
53
54        Some(Self::new(obj_ref, gen_num))
55    }
56}
57
58/// A struct that is either an object or a reference to an object.
59pub enum MaybeRef<T> {
60    /// A reference to an object.
61    Ref(ObjRef),
62    /// An object.
63    NotRef(T),
64}
65
66#[allow(private_bounds)]
67impl<'a, T> MaybeRef<T>
68where
69    T: ObjectLike<'a>,
70{
71    /// Resolve the `MaybeRef` object with the given xref table.
72    pub fn resolve(self, xref: &'a XRef) -> Option<T> {
73        match self {
74            MaybeRef::Ref(r) => xref.get::<T>(r.into()),
75            MaybeRef::NotRef(t) => Some(t),
76        }
77    }
78}
79
80impl<T> TryFrom<MaybeRef<T>> for ObjRef {
81    type Error = ();
82
83    fn try_from(value: MaybeRef<T>) -> Result<Self, Self::Error> {
84        match value {
85            MaybeRef::Ref(r) => Ok(r),
86            MaybeRef::NotRef(_) => Err(()),
87        }
88    }
89}
90
91impl<T> Debug for MaybeRef<T>
92where
93    T: Debug,
94{
95    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96        match self {
97            MaybeRef::Ref(r) => write!(f, "{:?}", r),
98            MaybeRef::NotRef(nr) => write!(f, "{:?}", nr),
99        }
100    }
101}
102
103impl<T> Skippable for MaybeRef<T>
104where
105    T: Skippable,
106{
107    fn skip<const PLAIN: bool>(r: &mut Reader<'_>) -> Option<()> {
108        r.skip::<PLAIN, ObjRef>()
109            .or_else(|| r.skip::<PLAIN, T>())
110            .map(|_| {})
111    }
112}
113
114impl<'a, T> Readable<'a> for MaybeRef<T>
115where
116    T: Readable<'a>,
117{
118    fn read<const PLAIN: bool>(r: &mut Reader<'a>, xref: &'a XRef) -> Option<Self> {
119        if let Some(obj) = r.read::<PLAIN, ObjRef>(xref) {
120            Some(Self::Ref(obj))
121        } else {
122            Some(Self::NotRef(r.read::<PLAIN, T>(xref)?))
123        }
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use crate::object::r#ref::ObjRef;
130    use crate::reader::Reader;
131
132    #[test]
133    fn ref_1() {
134        assert_eq!(
135            Reader::new("34 1 R".as_bytes())
136                .read_without_xref::<ObjRef>()
137                .unwrap(),
138            ObjRef::new(34, 1)
139        );
140    }
141
142    #[test]
143    fn ref_trailing() {
144        assert_eq!(
145            Reader::new("256 0 R (hi)".as_bytes())
146                .read_without_xref::<ObjRef>()
147                .unwrap(),
148            ObjRef::new(256, 0)
149        );
150    }
151
152    #[test]
153    fn ref_invalid_1() {
154        assert!(
155            Reader::new("256 R".as_bytes())
156                .read_without_xref::<ObjRef>()
157                .is_none()
158        );
159    }
160
161    #[test]
162    fn ref_invalid_2() {
163        assert!(
164            Reader::new("256 257".as_bytes())
165                .read_without_xref::<ObjRef>()
166                .is_none()
167        );
168    }
169}