asciidork_parser/tasks/directives/includes/
include_resolver.rs1use std::{any::Any, fmt};
2
3use crate::internal::*;
4
5#[derive(Debug, Eq, PartialEq, Clone)]
6pub enum IncludeTarget {
7 FilePath(String),
8 Uri(String),
9}
10
11impl IncludeTarget {
12 pub const fn is_path(&self) -> bool {
13 matches!(self, IncludeTarget::FilePath(_))
14 }
15
16 pub const fn is_uri(&self) -> bool {
17 matches!(self, IncludeTarget::Uri(_))
18 }
19
20 pub fn path(&self) -> Path {
21 match self {
22 IncludeTarget::FilePath(path) => Path::new(path),
23 IncludeTarget::Uri(uri) => Path::new(uri),
24 }
25 }
26}
27
28impl From<Path> for IncludeTarget {
29 fn from(path: Path) -> Self {
30 if path.is_uri() {
31 IncludeTarget::Uri(path.to_string())
32 } else {
33 IncludeTarget::FilePath(path.to_string())
34 }
35 }
36}
37
38pub trait IncludeResolver: Any {
39 fn resolve(
40 &mut self,
41 target: IncludeTarget,
42 buffer: &mut dyn IncludeBuffer,
43 ) -> std::result::Result<usize, ResolveError>;
44
45 fn get_base_dir(&self) -> Option<String> {
46 None
47 }
48 fn clone_box(&self) -> Box<dyn IncludeResolver>;
49}
50
51pub trait IncludeBuffer {
52 fn as_bytes_mut(&mut self) -> &mut [u8];
53 fn initialize(&mut self, len: usize);
54}
55
56impl IncludeBuffer for Vec<u8> {
57 fn initialize(&mut self, len: usize) {
58 self.reserve(len + 1); self.resize(len, 0);
60 }
61
62 fn as_bytes_mut(&mut self) -> &mut [u8] {
63 self
64 }
65}
66
67impl IncludeBuffer for BumpVec<'_, u8> {
68 fn initialize(&mut self, len: usize) {
69 self.reserve(len + 1); self.resize(len, 0);
71 }
72
73 fn as_bytes_mut(&mut self) -> &mut [u8] {
74 self
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum ResolveError {
80 NotFound,
81 Io(String),
82 UriReadNotSupported,
83 UriRead(String),
84 BaseDirRequired,
85 CaseMismatch(Option<String>),
86}
87
88impl fmt::Display for ResolveError {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 match self {
91 ResolveError::NotFound => write!(f, "File not found"),
92 ResolveError::Io(e) => write!(f, "I/O error: {e}"),
93 ResolveError::UriReadNotSupported => write!(f, "URI read not supported"),
94 ResolveError::UriRead(e) => write!(f, "Error reading URI: {e}"),
95 ResolveError::CaseMismatch(Some(path)) => {
96 write!(
97 f,
98 "Case mismatch in file path. Maybe you meant to include `{path}`?",
99 )
100 }
101 ResolveError::CaseMismatch(None) => write!(f, "Case mismatch in file path"),
102 ResolveError::BaseDirRequired => {
103 write!(
104 f,
105 "Include resolvers must supply a base_dir for relative includes from primary document"
106 )
107 }
108 }
109 }
110}
111
112impl From<std::io::Error> for ResolveError {
113 fn from(e: std::io::Error) -> Self {
114 ResolveError::Io(e.to_string())
115 }
116}
117
118#[cfg(debug_assertions)]
121#[derive(Clone)]
122pub struct ConstResolver(pub Vec<u8>);
123#[cfg(debug_assertions)]
124impl IncludeResolver for ConstResolver {
125 fn resolve(
126 &mut self,
127 _: IncludeTarget,
128 buffer: &mut dyn IncludeBuffer,
129 ) -> std::result::Result<usize, ResolveError> {
130 buffer.initialize(self.0.len());
131 let bytes = buffer.as_bytes_mut();
132 bytes.copy_from_slice(&self.0);
133 Ok(self.0.len())
134 }
135
136 fn get_base_dir(&self) -> Option<String> {
137 Some("/".to_string())
138 }
139 fn clone_box(&self) -> Box<dyn IncludeResolver> {
140 Box::new(self.clone())
141 }
142}
143
144#[cfg(debug_assertions)]
145#[derive(Clone)]
146pub struct ErrorResolver(pub ResolveError);
147#[cfg(debug_assertions)]
148impl IncludeResolver for ErrorResolver {
149 fn resolve(
150 &mut self,
151 _: IncludeTarget,
152 _: &mut dyn IncludeBuffer,
153 ) -> std::result::Result<usize, ResolveError> {
154 Err(self.0.clone())
155 }
156
157 fn get_base_dir(&self) -> Option<String> {
158 Some("/".to_string())
159 }
160 fn clone_box(&self) -> Box<dyn IncludeResolver> {
161 Box::new(self.clone())
162 }
163}