Skip to main content

hayro_syntax/object/
ref.rs

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