1use std::borrow::Cow;
2use std::path::Path;
3use std::sync::Arc;
4
5use pulldown_cmark::Event;
6use thiserror::Error;
7
8use crate::{
9 CMarkData, CMarkDataIter, Config, DisallowUrlsWithPrefixError, File, FileDocs,
10 FileDocsFromFileError, FileFromPathError, Manifest, Package,
11};
12
13#[derive(Clone, Debug, Default, PartialEq)]
15pub struct CMarkDocs<P, M> {
16 data: CMarkData,
17 package_path: P,
18 manifest: M,
19}
20
21impl<'a> CMarkDocs<&'a Path, &'a Manifest> {
22 pub fn from_package_with_default_config(
27 package: &'a Package,
28 ) -> Result<Self, CMarkDocsFromPackageError> {
29 Self::from_package_and_config(package, &Config::default())
30 }
31
32 pub fn from_package_and_config(
37 package: &'a Package,
38 config: &Config<'_>,
39 ) -> Result<Self, CMarkDocsFromPackageError> {
40 let path = package.manifest().default_relative_target_path();
41 let file = Arc::new(File::from_path(path.to_path_buf(), Some(package.path()))?);
42 let package_path = package.path();
43 let manifest = package.manifest();
44 Ok(Self::from_file_and_config_and_package_path_and_manifest(
45 file,
46 config,
47 package_path,
48 manifest,
49 )?)
50 }
51}
52
53impl CMarkDocs<(), ()> {
54 pub fn from_file_and_config(
58 file: Arc<File>,
59 config: &Config<'_>,
60 ) -> Result<Self, FileDocsFromFileError> {
61 Self::from_file_and_config_and_package_path_and_manifest(file, config, (), ())
62 }
63}
64
65impl<'a, P, M> CMarkDocs<P, M> {
66 pub fn with_package_path(self, package_path: &'a Package) -> CMarkDocs<&'a Package, M> {
68 CMarkDocs {
69 data: self.data,
70 package_path,
71 manifest: self.manifest,
72 }
73 }
74
75 pub fn with_manifest(self, manifest: &'a Manifest) -> CMarkDocs<P, &'a Manifest> {
77 CMarkDocs {
78 data: self.data,
79 package_path: self.package_path,
80 manifest,
81 }
82 }
83
84 pub fn from_file_and_config_and_package_path_and_manifest(
86 file: Arc<File>,
87 config: &Config<'_>,
88 package_path: P,
89 manifest: M,
90 ) -> Result<Self, FileDocsFromFileError> {
91 let file_docs = Arc::new(FileDocs::from_file(file, config)?);
92 Ok(Self::from_file_docs_and_package_path_and_manifest(
93 file_docs,
94 package_path,
95 manifest,
96 ))
97 }
98
99 pub fn from_file_docs_and_package_path_and_manifest(
101 file_docs: Arc<FileDocs>,
102 package_path: P,
103 manifest: M,
104 ) -> Self {
105 let data = CMarkData::from_file_docs(file_docs);
106 Self::from_data_chunks_package_pach_and_manifest(data, package_path, manifest)
107 }
108
109 pub fn from_data_chunks_package_pach_and_manifest(
111 data: CMarkData,
112 package_path: P,
113 manifest: M,
114 ) -> Self {
115 Self {
116 data,
117 package_path,
118 manifest,
119 }
120 }
121
122 pub fn data(&self) -> &CMarkData {
124 &self.data
125 }
126
127 pub fn into_data(self) -> CMarkData {
129 self.data
130 }
131
132 pub fn package_path(&self) -> &P {
134 &self.package_path
135 }
136
137 pub fn manifest(&self) -> &M {
139 &self.manifest
140 }
141
142 pub fn iter(&self) -> CMarkDataIter<'_> {
144 self.data.iter()
145 }
146
147 pub fn iter_events(&self) -> impl Iterator<Item = &Event<'_>> {
149 self.data.iter().filter_map(|item| item.event())
150 }
151
152 fn map<F>(mut self, func: F) -> CMarkDocs<P, M>
153 where
154 F: FnOnce(CMarkData) -> CMarkData,
155 {
156 self.data = func(self.data);
157 self
158 }
159
160 fn map_result<F, E>(mut self, func: F) -> Result<CMarkDocs<P, M>, E>
161 where
162 F: FnOnce(CMarkData) -> Result<CMarkData, E>,
163 {
164 self.data = func(self.data)?;
165 Ok(self)
166 }
167
168 pub fn concat_texts(self) -> CMarkDocs<P, M> {
179 self.map(|data| data.concat_texts())
180 }
181
182 pub fn increment_heading_levels(self) -> CMarkDocs<P, M> {
190 self.map(|data| data.increment_heading_levels())
191 }
192
193 pub fn add_title(self, text: &str) -> CMarkDocs<P, M> {
197 self.map(|data| data.add_title(text))
198 }
199
200 pub fn remove_section(self, heading: &str, level: u32) -> Self {
202 self.map(|data| data.remove_section(heading, level))
203 }
204
205 pub fn remove_codeblock_tag(self, tag: &str) -> CMarkDocs<P, M> {
207 self.map(|data| data.remove_codeblock_tag(tag))
208 }
209
210 pub fn remove_codeblock_tags(self, tags: &[&str]) -> CMarkDocs<P, M> {
212 self.map(|data| data.remove_codeblock_tags(tags))
213 }
214
215 pub fn remove_codeblock_rust_test_tags(self) -> CMarkDocs<P, M> {
219 self.map(|data| data.remove_codeblock_rust_test_tags())
220 }
221
222 pub fn use_default_codeblock_tag(self, tag: &str) -> CMarkDocs<P, M> {
224 self.map(|data| data.use_default_codeblock_tag(tag))
225 }
226
227 pub fn use_default_codeblock_rust_tag(self) -> CMarkDocs<P, M> {
229 self.map(|data| data.use_default_codeblock_rust_tag())
230 }
231
232 pub fn remove_hidden_rust_code(self) -> CMarkDocs<P, M> {
236 self.map(|data| data.remove_hidden_rust_code())
237 }
238
239 pub fn disallow_absolute_docs_links(
242 self,
243 package_name: &str,
244 documentation_url: &str,
245 ) -> Result<CMarkDocs<P, M>, DisallowUrlsWithPrefixError> {
246 self.map_result(|data| data.disallow_absolute_docs_links(package_name, documentation_url))
247 }
248
249 pub fn use_absolute_docs_urls(
252 self,
253 package_name: &str,
254 documentation_url: &str,
255 ) -> CMarkDocs<P, M> {
256 self.map(|data| data.use_absolute_docs_urls(package_name, documentation_url))
257 }
258}
259
260impl<'a, P> CMarkDocs<P, &'a Manifest> {
261 pub fn add_package_title(self) -> CMarkDocs<P, &'a Manifest> {
265 let name = self.manifest.package.name.clone();
266 self.add_title(&name)
267 }
268
269 pub fn disallow_absolute_package_docs_links(
272 self,
273 ) -> Result<CMarkDocs<P, &'a Manifest>, DisallowAbsolutePackageDocsLinksError> {
274 let name = self.manifest.package.name.clone();
275 let documentation = self
276 .manifest
277 .package
278 .documentation
279 .clone()
280 .ok_or(DisallowAbsolutePackageDocsLinksError::DocsUrlNotFound)?;
281 Ok(self.disallow_absolute_docs_links(&name, &documentation)?)
282 }
283
284 pub fn use_absolute_package_docs_urls(
287 self,
288 ) -> Result<CMarkDocs<P, &'a Manifest>, UseAbsolutePackageDocsUrlsError> {
289 let name = self.manifest.package.name.clone();
290 let documentation = self
291 .manifest
292 .package
293 .documentation
294 .clone()
295 .ok_or(UseAbsolutePackageDocsUrlsError::DocsUrlNotFound)?;
296 Ok(self.use_absolute_docs_urls(&name, &documentation))
297 }
298
299 pub fn map_links<F>(self, func: F, note: impl Into<Cow<'static, str>>) -> Self
301 where
302 for<'b> F: FnMut(&'b str) -> Cow<'b, str>,
303 {
304 self.map(|data| data.map_links(func, note))
305 }
306}
307
308#[derive(Debug, Error)]
310pub enum CMarkDocsFromPackageError {
311 #[error(transparent)]
313 FileError(#[from] FileFromPathError),
314 #[error("Documentation parsing failed: {0}")]
316 ParseError(#[from] FileDocsFromFileError),
317}
318
319#[derive(Clone, Debug, Error)]
321pub enum DisallowAbsolutePackageDocsLinksError {
322 #[error(transparent)]
324 DisallowUrlsWithPrefixError(#[from] DisallowUrlsWithPrefixError),
325 #[error("Manifest does not contain package.documentation field")]
327 DocsUrlNotFound,
328}
329
330#[derive(Clone, Copy, Debug, Error)]
333pub enum UseAbsolutePackageDocsUrlsError {
334 #[error("Manifest does not contain package.documentation field")]
335 DocsUrlNotFound,
336}