WIP rust implementation
This commit is contained in:
parent
03bfdd3d99
commit
5247d69ebd
6 changed files with 1146 additions and 1 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,3 +4,4 @@
|
||||||
__pycache__
|
__pycache__
|
||||||
*.log
|
*.log
|
||||||
/powerisa-instructions.xml
|
/powerisa-instructions.xml
|
||||||
|
/target
|
||||||
|
|
|
||||||
75
Cargo.lock
generated
Normal file
75
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parse_powerisa_pdf"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.111"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "parse_powerisa_pdf"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "LGPL-3.0-or-later"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|
||||||
2
src/lib.rs
Normal file
2
src/lib.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
mod pdf;
|
||||||
|
mod util;
|
||||||
956
src/pdf.rs
Normal file
956
src/pdf.rs
Normal file
|
|
@ -0,0 +1,956 @@
|
||||||
|
use crate::util::ArcOrRef;
|
||||||
|
use serde::{de, forward_to_deserialize_any};
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
collections::BTreeMap,
|
||||||
|
convert::Infallible,
|
||||||
|
fmt::{self, Write},
|
||||||
|
iter::FusedIterator,
|
||||||
|
marker::PhantomData,
|
||||||
|
num::NonZero,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum PdfParseError {
|
||||||
|
InvalidFieldKind {
|
||||||
|
containing_ty: &'static str,
|
||||||
|
field: &'static str,
|
||||||
|
expected_kind: &'static str,
|
||||||
|
kind: &'static str,
|
||||||
|
},
|
||||||
|
Custom(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Infallible> for PdfParseError {
|
||||||
|
fn from(value: Infallible) -> Self {
|
||||||
|
match value {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PdfParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
&PdfParseError::InvalidFieldKind {
|
||||||
|
containing_ty,
|
||||||
|
field,
|
||||||
|
expected_kind,
|
||||||
|
kind,
|
||||||
|
} => write!(
|
||||||
|
f,
|
||||||
|
"invalid field kind: {containing_ty}.{field}: expected {expected_kind}, got {kind}"
|
||||||
|
),
|
||||||
|
PdfParseError::Custom(msg) => f.write_str(msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for PdfParseError {}
|
||||||
|
|
||||||
|
impl de::Error for PdfParseError {
|
||||||
|
fn custom<T>(msg: T) -> Self
|
||||||
|
where
|
||||||
|
T: fmt::Display,
|
||||||
|
{
|
||||||
|
PdfParseError::Custom(msg.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> de::IntoDeserializer<'de, PdfParseError> for PdfName {
|
||||||
|
type Deserializer = PdfObject;
|
||||||
|
|
||||||
|
fn into_deserializer(self) -> Self::Deserializer {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> de::IntoDeserializer<'de, PdfParseError> for PdfObject {
|
||||||
|
type Deserializer = Self;
|
||||||
|
|
||||||
|
fn into_deserializer(self) -> Self::Deserializer {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfObject {
|
||||||
|
const SERDE_FIELD_NAME: &str = "__PdfObject__look_in_thread_local";
|
||||||
|
const SERDE_NAME_AND_FIELDS: (&str, &[&str]) = ("PdfObject", &[Self::SERDE_FIELD_NAME]);
|
||||||
|
fn with_thread_local<R>(f: impl FnOnce(&RefCell<Option<Self>>) -> R) -> R {
|
||||||
|
thread_local! {
|
||||||
|
static CURRENT_OBJECT: RefCell<Option<PdfObject>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
CURRENT_OBJECT.with(f)
|
||||||
|
}
|
||||||
|
fn set_thread_local_scoped<R>(self, f: impl FnOnce() -> R) -> R {
|
||||||
|
Self::with_thread_local(|current_object| {
|
||||||
|
struct PutBackOnDrop<'a> {
|
||||||
|
current_object: &'a RefCell<Option<PdfObject>>,
|
||||||
|
old_object: Option<PdfObject>,
|
||||||
|
}
|
||||||
|
impl Drop for PutBackOnDrop<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.current_object.replace(self.old_object.take());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let put_back_on_drop = PutBackOnDrop {
|
||||||
|
current_object,
|
||||||
|
old_object: current_object.replace(Some(self)),
|
||||||
|
};
|
||||||
|
let retval = f();
|
||||||
|
drop(put_back_on_drop);
|
||||||
|
retval
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn take_thread_local() -> Option<PdfObject> {
|
||||||
|
Self::with_thread_local(RefCell::take)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait PdfObjectDeserializeHelperTrait: Sized {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result;
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PdfObjectDeserializeHelper<T: PdfObjectDeserializeHelperTrait>(T);
|
||||||
|
|
||||||
|
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Deserialize<'de>
|
||||||
|
for PdfObjectDeserializeHelper<T>
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct PdfObjectVisitor<T>(PhantomData<T>);
|
||||||
|
fn expected_pdf_object<E: de::Error, T: PdfObjectDeserializeHelperTrait>() -> E {
|
||||||
|
de::Error::invalid_type(de::Unexpected::Map, &PdfObjectVisitor::<T>(PhantomData))
|
||||||
|
}
|
||||||
|
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Visitor<'de> for PdfObjectVisitor<T> {
|
||||||
|
type Value = PdfObject;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
T::expecting(formatter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
struct Field<T>(PhantomData<T>);
|
||||||
|
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Deserialize<'de> for Field<T> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_identifier(Field(PhantomData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Visitor<'de> for Field<T> {
|
||||||
|
type Value = Self;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
T::expecting(formatter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if v == PdfObject::SERDE_FIELD_NAME {
|
||||||
|
Ok(self)
|
||||||
|
} else {
|
||||||
|
Err(expected_pdf_object::<E, T>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (Field::<T>(PhantomData), ()) = map
|
||||||
|
.next_entry()?
|
||||||
|
.ok_or_else(expected_pdf_object::<A::Error, T>)?;
|
||||||
|
let None = map.next_entry::<Field<T>, ()>()? else {
|
||||||
|
return Err(expected_pdf_object::<_, T>());
|
||||||
|
};
|
||||||
|
PdfObject::take_thread_local().ok_or_else(expected_pdf_object::<_, T>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (name, fields) = PdfObject::SERDE_NAME_AND_FIELDS;
|
||||||
|
let pdf_object =
|
||||||
|
deserializer.deserialize_struct(name, fields, PdfObjectVisitor::<T>(PhantomData))?;
|
||||||
|
T::from_pdf_object::<D::Error>(pdf_object, &PdfObjectVisitor::<T>(PhantomData)).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! forward_deserialize_to_pdf_object_helper {
|
||||||
|
($ty:ty) => {
|
||||||
|
impl<'de> de::Deserialize<'de> for $ty {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let PdfObjectDeserializeHelper(v) = de::Deserialize::deserialize(deserializer)?;
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_deserialize_to_pdf_object_helper!(PdfObject);
|
||||||
|
|
||||||
|
impl PdfObjectDeserializeHelperTrait for PdfObject {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PdfObject")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
_expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_deserialize_to_pdf_object_helper!(PdfObjectIndirect);
|
||||||
|
|
||||||
|
impl PdfObjectDeserializeHelperTrait for PdfObjectIndirect {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PdfObjectIndirect")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
match value {
|
||||||
|
PdfObject::Indirect(v) => Ok(v),
|
||||||
|
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_deserialize_to_pdf_object_helper!(PdfString);
|
||||||
|
|
||||||
|
impl PdfObjectDeserializeHelperTrait for PdfString {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PdfString")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
match value {
|
||||||
|
PdfObject::String(v) => Ok(v),
|
||||||
|
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_deserialize_to_pdf_object_helper!(PdfName);
|
||||||
|
|
||||||
|
impl PdfObjectDeserializeHelperTrait for PdfName {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PdfName")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
match value {
|
||||||
|
PdfObject::Name(v) => Ok(v),
|
||||||
|
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_deserialize_to_pdf_object_helper!(PdfArray);
|
||||||
|
|
||||||
|
impl PdfObjectDeserializeHelperTrait for PdfArray {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PdfArray")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
match value {
|
||||||
|
PdfObject::Array(v) => Ok(v),
|
||||||
|
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_deserialize_to_pdf_object_helper!(PdfDictionary);
|
||||||
|
|
||||||
|
impl PdfObjectDeserializeHelperTrait for PdfDictionary {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PdfDictionary")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
match value {
|
||||||
|
PdfObject::Dictionary(v) => Ok(v),
|
||||||
|
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_deserialize_to_pdf_object_helper!(PdfStream);
|
||||||
|
|
||||||
|
impl PdfObjectDeserializeHelperTrait for PdfStream {
|
||||||
|
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PdfStream")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_pdf_object<E: de::Error>(
|
||||||
|
value: PdfObject,
|
||||||
|
expected: &dyn de::Expected,
|
||||||
|
) -> Result<Self, E> {
|
||||||
|
match value {
|
||||||
|
PdfObject::Stream(v) => Ok(v),
|
||||||
|
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> de::Deserializer<'de> for PdfObject {
|
||||||
|
type Error = PdfParseError;
|
||||||
|
|
||||||
|
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: de::Visitor<'de>,
|
||||||
|
{
|
||||||
|
match PdfObjectDirect::from(self) {
|
||||||
|
PdfObjectDirect::Boolean(v) => visitor.visit_bool(v),
|
||||||
|
PdfObjectDirect::Integer(v) => visitor.visit_i32(v),
|
||||||
|
PdfObjectDirect::Real(v) => visitor.visit_f32(v),
|
||||||
|
v @ (PdfObjectDirect::String(_) | PdfObjectDirect::Stream(_)) => {
|
||||||
|
Err(de::Error::invalid_type(v.as_unexpected(), &visitor))
|
||||||
|
}
|
||||||
|
PdfObjectDirect::Name(v) => {
|
||||||
|
if let Ok(v) = str::from_utf8(v.as_bytes()) {
|
||||||
|
visitor.visit_str(v)
|
||||||
|
} else {
|
||||||
|
Err(de::Error::invalid_type(
|
||||||
|
PdfObject::from(v).as_unexpected(),
|
||||||
|
&visitor,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PdfObjectDirect::Array(v) => {
|
||||||
|
visitor.visit_seq(de::value::SeqDeserializer::new(v.iter().cloned()))
|
||||||
|
}
|
||||||
|
PdfObjectDirect::Dictionary(v) => {
|
||||||
|
visitor.visit_map(de::value::MapDeserializer::new(v.into_iter()))
|
||||||
|
}
|
||||||
|
PdfObjectDirect::Null(PdfNull {}) => visitor.visit_unit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_struct<V>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
fields: &'static [&'static str],
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: de::Visitor<'de>,
|
||||||
|
{
|
||||||
|
match (name, fields) {
|
||||||
|
PdfObject::SERDE_NAME_AND_FIELDS => self.set_thread_local_scoped(|| {
|
||||||
|
visitor.visit_map(de::value::MapDeserializer::new(std::iter::once((
|
||||||
|
PdfObject::SERDE_FIELD_NAME,
|
||||||
|
(),
|
||||||
|
))))
|
||||||
|
}),
|
||||||
|
_ => self.deserialize_any(visitor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: de::Visitor<'de>,
|
||||||
|
{
|
||||||
|
let is_null = match self {
|
||||||
|
Self::Indirect(ref v) => !v.exists(),
|
||||||
|
Self::Null(_) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if is_null {
|
||||||
|
visitor.visit_none()
|
||||||
|
} else {
|
||||||
|
visitor.visit_some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_newtype_struct<V>(
|
||||||
|
self,
|
||||||
|
_name: &'static str,
|
||||||
|
visitor: V,
|
||||||
|
) -> Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: de::Visitor<'de>,
|
||||||
|
{
|
||||||
|
visitor.visit_newtype_struct(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_to_deserialize_any! {
|
||||||
|
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
|
||||||
|
bytes byte_buf unit unit_struct seq tuple
|
||||||
|
tuple_struct map enum identifier ignored_any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub(crate) struct PdfString {
|
||||||
|
bytes: ArcOrRef<'static, [u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for PdfString {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("PdfString")
|
||||||
|
.field("bytes", &&*self.bytes)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfString {
|
||||||
|
pub(crate) fn new(bytes: ArcOrRef<'static, [u8]>) -> Self {
|
||||||
|
Self { bytes }
|
||||||
|
}
|
||||||
|
pub(crate) fn bytes(&self) -> &ArcOrRef<'static, [u8]> {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub(crate) struct PdfName {
|
||||||
|
bytes: ArcOrRef<'static, [u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfName {
|
||||||
|
pub(crate) fn try_new(bytes: impl Into<ArcOrRef<'static, [u8]>>) -> Option<Self> {
|
||||||
|
let bytes = bytes.into();
|
||||||
|
if bytes.contains(&0) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Self { bytes })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub(crate) const fn new_static(bytes: &'static [u8]) -> Self {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < bytes.len() {
|
||||||
|
if bytes[i] == 0 {
|
||||||
|
panic!("shouldn't contain any nul bytes");
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
bytes: ArcOrRef::Ref(bytes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub(crate) fn new(bytes: ArcOrRef<'static, [u8]>) -> Self {
|
||||||
|
Self::try_new(bytes).expect("shouldn't contain any nul bytes")
|
||||||
|
}
|
||||||
|
pub(crate) fn as_bytes(&self) -> &ArcOrRef<'static, [u8]> {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! make_pdf_names {
|
||||||
|
(
|
||||||
|
$vis:vis mod $pdf_names:ident {
|
||||||
|
$($ident:ident;)*
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
$vis mod $pdf_names {
|
||||||
|
$(#[allow(non_upper_case_globals)]
|
||||||
|
$vis const $ident: $crate::pdf::PdfName = $crate::pdf::PdfName::new_static(stringify!($ident).as_bytes());)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
make_pdf_names! {
|
||||||
|
pub(crate) mod pdf_names {
|
||||||
|
DecodeParms;
|
||||||
|
DL;
|
||||||
|
F;
|
||||||
|
FDecodeParms;
|
||||||
|
FFilter;
|
||||||
|
Filter;
|
||||||
|
Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PdfName {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "PdfName({self})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PdfName {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("/")?;
|
||||||
|
for &b in self.bytes.iter() {
|
||||||
|
match b {
|
||||||
|
0x21..=0x7E if b != b'#' => f.write_char(b.into())?,
|
||||||
|
_ => write!(f, "#{b:02X}")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! make_pdf_object {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
#[from = $($from:ident)?, as_unexpected = |$as_unexpected_arg:pat_param| $as_unexpected_expr:expr]
|
||||||
|
$Variant:ident($ty:ty),
|
||||||
|
)+
|
||||||
|
) => {
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub(crate) enum PdfObjectNonNull {
|
||||||
|
$($Variant($ty),)*
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub(crate) enum PdfObjectDirect {
|
||||||
|
$($Variant($ty),)*
|
||||||
|
Null(PdfNull),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub(crate) enum PdfObject {
|
||||||
|
$($Variant($ty),)*
|
||||||
|
Null(PdfNull),
|
||||||
|
Indirect(PdfObjectIndirect),
|
||||||
|
}
|
||||||
|
|
||||||
|
$($(
|
||||||
|
impl From<$ty> for PdfObjectNonNull {
|
||||||
|
fn $from(value: $ty) -> Self {
|
||||||
|
Self::$Variant(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<$ty> for PdfObjectDirect {
|
||||||
|
fn $from(value: $ty) -> Self {
|
||||||
|
Self::$Variant(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<$ty> for PdfObject {
|
||||||
|
fn $from(value: $ty) -> Self {
|
||||||
|
Self::$Variant(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<$ty>> for PdfObjectDirect {
|
||||||
|
fn $from(value: Option<$ty>) -> Self {
|
||||||
|
match value {
|
||||||
|
Some(value) => Self::$Variant(value),
|
||||||
|
None => Self::Null(PdfNull),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<$ty>> for PdfObject {
|
||||||
|
fn $from(value: Option<$ty>) -> Self {
|
||||||
|
match value {
|
||||||
|
Some(value) => Self::$Variant(value),
|
||||||
|
None => Self::Null(PdfNull),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)?)*
|
||||||
|
|
||||||
|
impl From<PdfObjectNonNull> for PdfObjectDirect {
|
||||||
|
fn from(value: PdfObjectNonNull) -> Self {
|
||||||
|
match value {
|
||||||
|
$(PdfObjectNonNull::$Variant(v) => Self::$Variant(v),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PdfObjectNonNull> for PdfObject {
|
||||||
|
fn from(value: PdfObjectNonNull) -> Self {
|
||||||
|
match value {
|
||||||
|
$(PdfObjectNonNull::$Variant(v) => Self::$Variant(v),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PdfObjectDirect> for PdfObject {
|
||||||
|
fn from(value: PdfObjectDirect) -> Self {
|
||||||
|
match value {
|
||||||
|
$(PdfObjectDirect::$Variant(v) => Self::$Variant(v),)*
|
||||||
|
PdfObjectDirect::Null(v) => Self::Null(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PdfObject> for PdfObjectDirect {
|
||||||
|
fn from(value: PdfObject) -> Self {
|
||||||
|
match value {
|
||||||
|
$(PdfObject::$Variant(v) => Self::$Variant(v),)*
|
||||||
|
PdfObject::Null(v) => Self::Null(v),
|
||||||
|
PdfObject::Indirect(v) => v.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfObjectNonNull {
|
||||||
|
fn as_unexpected(&self) -> de::Unexpected<'static> {
|
||||||
|
match *self {
|
||||||
|
$(PdfObjectNonNull::$Variant($as_unexpected_arg) => $as_unexpected_expr,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfObjectDirect {
|
||||||
|
fn as_unexpected(&self) -> de::Unexpected<'static> {
|
||||||
|
match *self {
|
||||||
|
$(PdfObjectDirect::$Variant($as_unexpected_arg) => $as_unexpected_expr,)*
|
||||||
|
PdfObjectDirect::Null(_) => de::Unexpected::Option,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfObject {
|
||||||
|
fn as_unexpected(&self) -> de::Unexpected<'static> {
|
||||||
|
match *self {
|
||||||
|
$(PdfObject::$Variant($as_unexpected_arg) => $as_unexpected_expr,)*
|
||||||
|
PdfObject::Null(_) => de::Unexpected::Option,
|
||||||
|
PdfObject::Indirect(ref v) => v.get().as_unexpected(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
fn _assert_impls_deserialize<T: de::DeserializeOwned>() {}
|
||||||
|
|
||||||
|
$(let _ = _assert_impls_deserialize::<$ty>;)*
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
make_pdf_object! {
|
||||||
|
#[from = from, as_unexpected = |v| de::Unexpected::Bool(v)]
|
||||||
|
Boolean(bool),
|
||||||
|
#[from = from, as_unexpected = |v| de::Unexpected::Signed(v.into())]
|
||||||
|
Integer(i32),
|
||||||
|
#[from = from, as_unexpected = |v| de::Unexpected::Float(v.into())]
|
||||||
|
Real(f32),
|
||||||
|
#[from = from, as_unexpected = |_| de::Unexpected::Other("PdfString")]
|
||||||
|
String(PdfString),
|
||||||
|
#[from = from, as_unexpected = |_| de::Unexpected::Other("PdfName")]
|
||||||
|
Name(PdfName),
|
||||||
|
#[from = from, as_unexpected = |_| de::Unexpected::Seq]
|
||||||
|
Array(PdfArray),
|
||||||
|
#[from = from, as_unexpected = |_| de::Unexpected::Map]
|
||||||
|
Dictionary(PdfDictionary),
|
||||||
|
#[from = from, as_unexpected = |_| de::Unexpected::Other("PdfStream")]
|
||||||
|
Stream(PdfStream),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct PdfNull;
|
||||||
|
|
||||||
|
impl From<PdfNull> for PdfObjectDirect {
|
||||||
|
fn from(v: PdfNull) -> Self {
|
||||||
|
Self::Null(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PdfNull> for PdfObject {
|
||||||
|
fn from(v: PdfNull) -> Self {
|
||||||
|
Self::Null(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PdfObjectIndirect> for PdfObject {
|
||||||
|
fn from(v: PdfObjectIndirect) -> Self {
|
||||||
|
Self::Indirect(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct PdfObjectIndirect {
|
||||||
|
xref_table: Weak<PdfObjects>,
|
||||||
|
object_number: NonZero<u32>,
|
||||||
|
generation_number: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for PdfObjectIndirect {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
let Self {
|
||||||
|
xref_table,
|
||||||
|
object_number,
|
||||||
|
generation_number,
|
||||||
|
} = self;
|
||||||
|
xref_table.ptr_eq(&other.xref_table)
|
||||||
|
&& *object_number == other.object_number
|
||||||
|
&& *generation_number == other.generation_number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfObjectIndirect {
|
||||||
|
pub fn exists(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
pub fn get(&self) -> PdfObjectDirect {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PdfObjectIndirect> for PdfObjectDirect {
|
||||||
|
fn from(value: PdfObjectIndirect) -> Self {
|
||||||
|
value.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub(crate) struct PdfDictionary {
|
||||||
|
fields: Arc<BTreeMap<PdfName, PdfObject>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfDictionary {
|
||||||
|
pub(crate) fn fields(&self) -> &Arc<BTreeMap<PdfName, PdfObject>> {
|
||||||
|
&self.fields
|
||||||
|
}
|
||||||
|
pub(crate) fn into_fields(self) -> Arc<BTreeMap<PdfName, PdfObject>> {
|
||||||
|
self.fields
|
||||||
|
}
|
||||||
|
pub(crate) fn iter(&self) -> std::collections::btree_map::Iter<'_, PdfName, PdfObject> {
|
||||||
|
self.fields.iter()
|
||||||
|
}
|
||||||
|
pub(crate) fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
|
||||||
|
where
|
||||||
|
PdfName: std::borrow::Borrow<Q> + Ord,
|
||||||
|
Q: Ord,
|
||||||
|
{
|
||||||
|
self.fields.contains_key(key)
|
||||||
|
}
|
||||||
|
pub(crate) fn get<Q: ?Sized>(&self, key: &Q) -> Option<&PdfObject>
|
||||||
|
where
|
||||||
|
PdfName: std::borrow::Borrow<Q> + Ord,
|
||||||
|
Q: Ord,
|
||||||
|
{
|
||||||
|
self.fields.get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromIterator<(PdfName, PdfObject)> for PdfDictionary {
|
||||||
|
fn from_iter<T: IntoIterator<Item = (PdfName, PdfObject)>>(iter: T) -> Self {
|
||||||
|
Self {
|
||||||
|
fields: Arc::new(BTreeMap::from_iter(
|
||||||
|
iter.into_iter()
|
||||||
|
.filter(|(_name, value)| !matches!(value, PdfObject::Null(_))),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for PdfDictionary {
|
||||||
|
type Item = (PdfName, PdfObject);
|
||||||
|
type IntoIter = std::collections::btree_map::IntoIter<PdfName, PdfObject>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
Arc::unwrap_or_clone(self.fields).into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a PdfDictionary {
|
||||||
|
type Item = (&'a PdfName, &'a PdfObject);
|
||||||
|
type IntoIter = std::collections::btree_map::Iter<'a, PdfName, PdfObject>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.fields.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PdfDictionary {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_map().entries(self).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, PartialEq)]
|
||||||
|
pub(crate) struct PdfArray {
|
||||||
|
elements: Arc<[PdfObject]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfArray {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
pub(crate) fn elements(&self) -> &Arc<[PdfObject]> {
|
||||||
|
&self.elements
|
||||||
|
}
|
||||||
|
pub(crate) fn into_elements(self) -> Arc<[PdfObject]> {
|
||||||
|
self.elements
|
||||||
|
}
|
||||||
|
pub(crate) fn iter(&self) -> std::slice::Iter<'_, PdfObject> {
|
||||||
|
self.elements.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromIterator<PdfObject> for PdfArray {
|
||||||
|
fn from_iter<T: IntoIterator<Item = PdfObject>>(iter: T) -> Self {
|
||||||
|
Self {
|
||||||
|
elements: Arc::from_iter(iter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct PdfArrayIntoIter {
|
||||||
|
indexes: std::ops::Range<usize>,
|
||||||
|
elements: Arc<[PdfObject]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for PdfArrayIntoIter {
|
||||||
|
type Item = PdfObject;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.indexes.next().map(|i| self.elements[i].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.indexes.size_hint()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
|
self.indexes.nth(n).map(|i| self.elements[i].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last(self) -> Option<Self::Item> {
|
||||||
|
self.indexes.last().map(|i| self.elements[i].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold<B, F>(self, init: B, mut f: F) -> B
|
||||||
|
where
|
||||||
|
F: FnMut(B, Self::Item) -> B,
|
||||||
|
{
|
||||||
|
self.indexes
|
||||||
|
.fold(init, |init, i| f(init, self.elements[i].clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for PdfArrayIntoIter {}
|
||||||
|
|
||||||
|
impl DoubleEndedIterator for PdfArrayIntoIter {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
self.indexes.next_back().map(|i| self.elements[i].clone())
|
||||||
|
}
|
||||||
|
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
|
self.indexes.nth_back(n).map(|i| self.elements[i].clone())
|
||||||
|
}
|
||||||
|
fn rfold<B, F>(self, init: B, mut f: F) -> B
|
||||||
|
where
|
||||||
|
F: FnMut(B, Self::Item) -> B,
|
||||||
|
{
|
||||||
|
self.indexes
|
||||||
|
.rfold(init, |init, i| f(init, self.elements[i].clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for PdfArrayIntoIter {}
|
||||||
|
|
||||||
|
impl IntoIterator for PdfArray {
|
||||||
|
type Item = PdfObject;
|
||||||
|
type IntoIter = PdfArrayIntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
PdfArrayIntoIter {
|
||||||
|
indexes: 0..self.elements.len(),
|
||||||
|
elements: self.elements,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a PdfArray {
|
||||||
|
type Item = &'a PdfObject;
|
||||||
|
type IntoIter = std::slice::Iter<'a, PdfObject>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.elements.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PdfArray {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.elements.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub(crate) struct PdfStream {
|
||||||
|
dictionary: PdfDictionary,
|
||||||
|
data: Arc<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum PdfBody {}
|
||||||
|
|
||||||
|
pub(crate) struct PdfObjects {}
|
||||||
|
|
||||||
|
pub(crate) struct PdfXRefTable {}
|
||||||
|
|
||||||
|
pub(crate) struct Pdf {
|
||||||
|
pub(crate) header: PdfHeader,
|
||||||
|
pub(crate) body: PdfBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct PdfHeader {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_dict() -> Result<(), PdfParseError> {
|
||||||
|
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||||
|
struct TestStruct {
|
||||||
|
a: i32,
|
||||||
|
c: i32,
|
||||||
|
b: i32,
|
||||||
|
#[serde(flatten)]
|
||||||
|
others: PdfDictionary,
|
||||||
|
}
|
||||||
|
|
||||||
|
let v: TestStruct =
|
||||||
|
de::Deserialize::deserialize(PdfObject::from(PdfDictionary::from_iter([
|
||||||
|
(PdfName::new_static(b"a"), 1.into()),
|
||||||
|
(PdfName::new_static(b"c"), 7.into()),
|
||||||
|
(PdfName::new_static(b"b"), 5.into()),
|
||||||
|
(PdfName::new_static(b"d"), false.into()),
|
||||||
|
(PdfName::new_static(b"e"), PdfNull.into()),
|
||||||
|
(
|
||||||
|
PdfName::new_static(b"f"),
|
||||||
|
PdfString::new(ArcOrRef::Ref(b"test")).into(),
|
||||||
|
),
|
||||||
|
])))?;
|
||||||
|
let expected = TestStruct {
|
||||||
|
a: 1,
|
||||||
|
c: 7,
|
||||||
|
b: 5,
|
||||||
|
others: PdfDictionary::from_iter([
|
||||||
|
(PdfName::new_static(b"d"), false.into()),
|
||||||
|
(
|
||||||
|
PdfName::new_static(b"f"),
|
||||||
|
PdfString::new(ArcOrRef::Ref(b"test")).into(),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
assert_eq!(v, expected);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/util.rs
Normal file
102
src/util.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
fmt,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) enum ArcOrRef<'a, T: ?Sized> {
|
||||||
|
Arc(Arc<T>),
|
||||||
|
Ref(&'a T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized> AsRef<T> for ArcOrRef<'a, T> {
|
||||||
|
fn as_ref(&self) -> &T {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized> Borrow<T> for ArcOrRef<'a, T> {
|
||||||
|
fn borrow(&self) -> &T {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized> From<Arc<T>> for ArcOrRef<'a, T> {
|
||||||
|
fn from(value: Arc<T>) -> Self {
|
||||||
|
Self::Arc(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized> From<&'a T> for ArcOrRef<'a, T> {
|
||||||
|
fn from(value: &'a T) -> Self {
|
||||||
|
Self::Ref(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized> Default for ArcOrRef<'a, T>
|
||||||
|
where
|
||||||
|
&'a T: Default,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Ref(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Clone for ArcOrRef<'_, T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Arc(v) => Self::Arc(v.clone()),
|
||||||
|
Self::Ref(v) => Self::Ref(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Hash> Hash for ArcOrRef<'_, T> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
T::hash(self, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, T: ?Sized + PartialEq<U>, U: ?Sized> PartialEq<ArcOrRef<'b, U>> for ArcOrRef<'a, T> {
|
||||||
|
fn eq(&self, other: &ArcOrRef<'b, U>) -> bool {
|
||||||
|
T::eq(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Eq> Eq for ArcOrRef<'_, T> {}
|
||||||
|
|
||||||
|
impl<'a, 'b, T: ?Sized + PartialOrd<U>, U: ?Sized> PartialOrd<ArcOrRef<'b, U>> for ArcOrRef<'a, T> {
|
||||||
|
fn partial_cmp(&self, other: &ArcOrRef<'b, U>) -> Option<std::cmp::Ordering> {
|
||||||
|
T::partial_cmp(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Ord> Ord for ArcOrRef<'_, T> {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
T::cmp(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> std::ops::Deref for ArcOrRef<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
ArcOrRef::Arc(v) => v,
|
||||||
|
ArcOrRef::Ref(v) => v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + fmt::Debug> fmt::Debug for ArcOrRef<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
T::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + fmt::Display> fmt::Display for ArcOrRef<'_, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
T::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue