asciidork_parser/tasks/directives/includes/
include_resolver.rs1use std::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 {
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}
49
50pub trait IncludeBuffer {
51 fn as_bytes_mut(&mut self) -> &mut [u8];
52 fn initialize(&mut self, len: usize);
53}
54
55impl IncludeBuffer for Vec<u8> {
56 fn initialize(&mut self, len: usize) {
57 self.reserve(len + 1); self.resize(len, 0);
59 }
60
61 fn as_bytes_mut(&mut self) -> &mut [u8] {
62 self
63 }
64}
65
66impl IncludeBuffer for BumpVec<'_, u8> {
67 fn initialize(&mut self, len: usize) {
68 self.reserve(len + 1); self.resize(len, 0);
70 }
71
72 fn as_bytes_mut(&mut self) -> &mut [u8] {
73 self
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub enum ResolveError {
79 NotFound,
80 Io(String),
81 UriReadNotSupported,
82 UriRead(String),
83 BaseDirRequired,
84 CaseMismatch(Option<String>),
85}
86
87impl fmt::Display for ResolveError {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 match self {
90 ResolveError::NotFound => write!(f, "File not found"),
91 ResolveError::Io(e) => write!(f, "I/O error: {}", e),
92 ResolveError::UriReadNotSupported => write!(f, "URI read not supported"),
93 ResolveError::UriRead(e) => write!(f, "Error reading URI: {}", e),
94 ResolveError::CaseMismatch(Some(path)) => {
95 write!(
96 f,
97 "Case mismatch in file path. Maybe you meant to include `{path}`?",
98 )
99 }
100 ResolveError::CaseMismatch(None) => write!(f, "Case mismatch in file path"),
101 ResolveError::BaseDirRequired => {
102 write!(
103 f,
104 "Include resolvers must supply a base_dir for relative includes from primary document"
105 )
106 }
107 }
108 }
109}
110
111impl From<std::io::Error> for ResolveError {
112 fn from(e: std::io::Error) -> Self {
113 ResolveError::Io(e.to_string())
114 }
115}
116
117#[cfg(debug_assertions)]
120pub struct ConstResolver(pub Vec<u8>);
121#[cfg(debug_assertions)]
122impl IncludeResolver for ConstResolver {
123 fn resolve(
124 &mut self,
125 _: IncludeTarget,
126 buffer: &mut dyn IncludeBuffer,
127 ) -> std::result::Result<usize, ResolveError> {
128 buffer.initialize(self.0.len());
129 let bytes = buffer.as_bytes_mut();
130 bytes.copy_from_slice(&self.0);
131 Ok(self.0.len())
132 }
133
134 fn get_base_dir(&self) -> Option<String> {
135 Some("/".to_string())
136 }
137}
138
139#[cfg(debug_assertions)]
140pub struct ErrorResolver(pub ResolveError);
141#[cfg(debug_assertions)]
142impl IncludeResolver for ErrorResolver {
143 fn resolve(
144 &mut self,
145 _: IncludeTarget,
146 _: &mut dyn IncludeBuffer,
147 ) -> std::result::Result<usize, ResolveError> {
148 Err(self.0.clone())
149 }
150
151 fn get_base_dir(&self) -> Option<String> {
152 Some("/".to_string())
153 }
154}