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