typst_pack/opendal/
pack_archive.rs1use super::BoxError;
4use super::read::{
5 ExactPathReadOperation, ResolvedOperators, exact_path_absent_error, read_exact_path,
6};
7use super::{Location, LocationRoleError, OperatorResolver};
8use crate::PackArchiveBytes;
9use crate::pack_archive::{ReadLimitError, ReadLimits, ReadResource};
10use crate::redacted_error::RedactedError;
11
12#[derive(Clone, Debug)]
14pub struct PackArchiveReadRequest {
15 source: Location,
16 limits: ReadLimits,
17}
18
19impl PackArchiveReadRequest {
20 pub fn new(source: Location, limits: ReadLimits) -> Result<Self, PackArchiveReadRequestError> {
22 if let Err(role_error) = source.require_object() {
23 return Err(PackArchiveReadRequestError::InvalidSourceRole {
24 location: source,
25 source: role_error,
26 });
27 }
28
29 Ok(Self { source, limits })
30 }
31
32 pub fn source(&self) -> &Location {
34 &self.source
35 }
36
37 pub const fn limits(&self) -> ReadLimits {
39 self.limits
40 }
41}
42
43#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
45#[non_exhaustive]
46pub enum PackArchiveReadRequestError {
47 #[error("Pack Archive source {location} is not an exact object: {source}")]
48 InvalidSourceRole {
49 location: Location,
50 #[source]
51 source: LocationRoleError,
52 },
53}
54
55#[allow(clippy::result_large_err)]
81pub async fn read_pack_archive<R: OperatorResolver + ?Sized>(
82 resolver: &R,
83 request: &PackArchiveReadRequest,
84) -> Result<PackArchiveBytes, PackArchiveReadError> {
85 let error = |cause| PackArchiveReadError {
86 source_location: request.source().clone(),
87 cause: RedactedError::new(cause),
88 };
89 let mut operators = ResolvedOperators::new(resolver);
90 let resolved = operators
91 .resolve(request.source().binding())
92 .map_err(|source| error(PackArchiveReadErrorCause::ResolveOperator(Box::new(source))))?;
93 if !resolved.read {
94 return Err(error(PackArchiveReadErrorCause::ReadUnsupported));
95 }
96 let ceiling = request.limits().archive_bytes();
97 let operation = PackArchiveExactPathOperation { request };
98 let bytes = read_exact_path(
99 &resolved.operator,
100 request.source().dispatch_path(),
101 ceiling,
102 ceiling,
103 &operation,
104 )
105 .await?
106 .ok_or_else(|| {
107 operation.error(PackArchiveReadErrorCause::ObjectAbsent(
108 exact_path_absent_error(),
109 ))
110 })?;
111
112 Ok(PackArchiveBytes::from_vec(bytes))
113}
114
115#[derive(Debug, thiserror::Error)]
120#[error(
121 "Pack Archive Read failed for binding {binding} at exact-object operation path {operation_path:?}: {cause}",
122 binding = .source_location.binding(),
123 operation_path = .source_location.operation_path(),
124)]
125pub struct PackArchiveReadError {
126 source_location: Location,
127 #[source]
128 cause: RedactedError<PackArchiveReadErrorCause>,
129}
130
131impl PackArchiveReadError {
132 pub fn source_location(&self) -> &Location {
134 &self.source_location
135 }
136
137 pub fn cause(&self) -> &PackArchiveReadErrorCause {
139 self.cause.inner()
140 }
141}
142
143#[derive(Debug, thiserror::Error)]
145#[non_exhaustive]
146pub enum PackArchiveReadErrorCause {
147 #[error("operator resolution failed")]
148 ResolveOperator(#[source] BoxError),
149 #[error("read capability is unsupported")]
150 ReadUnsupported,
151 #[error("the exact object is absent")]
152 ObjectAbsent(#[source] ::opendal::Error),
153 #[error("the exact object read failed")]
154 Read(#[source] ::opendal::Error),
155 #[error("the archive byte limit failed")]
156 Limit(#[source] ReadLimitError),
157}
158
159struct PackArchiveExactPathOperation<'a> {
160 request: &'a PackArchiveReadRequest,
161}
162
163impl PackArchiveExactPathOperation<'_> {
164 fn error(&self, cause: PackArchiveReadErrorCause) -> PackArchiveReadError {
165 PackArchiveReadError {
166 source_location: self.request.source().clone(),
167 cause: RedactedError::new(cause),
168 }
169 }
170}
171
172impl ExactPathReadOperation for PackArchiveExactPathOperation<'_> {
173 type Error = PackArchiveReadError;
174
175 fn read(&self, source: ::opendal::Error) -> PackArchiveReadError {
176 self.error(PackArchiveReadErrorCause::Read(source))
177 }
178
179 fn limit_exceeded(&self, ceiling: u64, _: u64) -> PackArchiveReadError {
180 self.error(PackArchiveReadErrorCause::Limit(ReadLimitError::exceeded(
181 ReadResource::ArchiveBytes,
182 ceiling,
183 )))
184 }
185
186 fn accounting_overflow(&self) -> PackArchiveReadError {
187 self.error(PackArchiveReadErrorCause::Limit(
188 ReadLimitError::AccountingOverflow {
189 resource: ReadResource::ArchiveBytes,
190 },
191 ))
192 }
193}