hayro_syntax/object/
ref.rs

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