forge_doc/
document.rs
1use crate::{DocBuilder, ParseItem, PreprocessorId, PreprocessorOutput};
2use alloy_primitives::map::HashMap;
3use std::{
4 path::{Path, PathBuf},
5 slice::IterMut,
6 sync::Mutex,
7};
8
9#[derive(Debug)]
12pub struct Document {
13 pub content: DocumentContent,
15 pub item_path: PathBuf,
17 pub item_content: String,
19 pub target_path: PathBuf,
21 pub identity: String,
23 context: Mutex<HashMap<PreprocessorId, PreprocessorOutput>>,
25 pub from_library: bool,
27 pub out_target_dir: PathBuf,
29}
30
31impl Document {
32 pub fn new(
34 item_path: PathBuf,
35 target_path: PathBuf,
36 from_library: bool,
37 out_target_dir: PathBuf,
38 ) -> Self {
39 Self {
40 item_path,
41 target_path,
42 from_library,
43 item_content: String::default(),
44 identity: String::default(),
45 content: DocumentContent::Empty,
46 out_target_dir,
47 context: Mutex::new(HashMap::default()),
48 }
49 }
50
51 #[must_use]
53 pub fn with_content(mut self, content: DocumentContent, identity: String) -> Self {
54 self.content = content;
55 self.identity = identity;
56 self
57 }
58
59 pub fn add_context(&self, id: PreprocessorId, output: PreprocessorOutput) {
61 let mut context = self.context.lock().expect("failed to lock context");
62 context.insert(id, output);
63 }
64
65 pub fn get_from_context(&self, id: PreprocessorId) -> Option<PreprocessorOutput> {
67 let context = self.context.lock().expect("failed to lock context");
68 context.get(&id).cloned()
69 }
70
71 fn try_relative_output_path(&self) -> Option<&Path> {
72 self.target_path.strip_prefix(&self.out_target_dir).ok()?.strip_prefix(DocBuilder::SRC).ok()
73 }
74
75 pub fn relative_output_path(&self) -> &Path {
77 self.try_relative_output_path().unwrap_or(self.target_path.as_path())
78 }
79}
80
81#[derive(Debug)]
83#[allow(clippy::large_enum_variant)]
84pub enum DocumentContent {
85 Empty,
86 Single(ParseItem),
87 Constants(Vec<ParseItem>),
88 OverloadedFunctions(Vec<ParseItem>),
89}
90
91impl DocumentContent {
92 pub(crate) fn len(&self) -> usize {
93 match self {
94 Self::Empty => 0,
95 Self::Single(_) => 1,
96 Self::Constants(items) => items.len(),
97 Self::OverloadedFunctions(items) => items.len(),
98 }
99 }
100
101 pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut ParseItem> {
102 match self {
103 Self::Empty => None,
104 Self::Single(item) => {
105 if index == 0 {
106 Some(item)
107 } else {
108 None
109 }
110 }
111 Self::Constants(items) => items.get_mut(index),
112 Self::OverloadedFunctions(items) => items.get_mut(index),
113 }
114 }
115
116 pub fn iter_items(&self) -> ParseItemIter<'_> {
117 match self {
118 Self::Empty => ParseItemIter { next: None, other: None },
119 Self::Single(item) => ParseItemIter { next: Some(item), other: None },
120 Self::Constants(items) => ParseItemIter { next: None, other: Some(items.iter()) },
121 Self::OverloadedFunctions(items) => {
122 ParseItemIter { next: None, other: Some(items.iter()) }
123 }
124 }
125 }
126
127 pub fn iter_items_mut(&mut self) -> ParseItemIterMut<'_> {
128 match self {
129 Self::Empty => ParseItemIterMut { next: None, other: None },
130 Self::Single(item) => ParseItemIterMut { next: Some(item), other: None },
131 Self::Constants(items) => {
132 ParseItemIterMut { next: None, other: Some(items.iter_mut()) }
133 }
134 Self::OverloadedFunctions(items) => {
135 ParseItemIterMut { next: None, other: Some(items.iter_mut()) }
136 }
137 }
138 }
139}
140
141#[derive(Debug)]
142pub struct ParseItemIter<'a> {
143 next: Option<&'a ParseItem>,
144 other: Option<std::slice::Iter<'a, ParseItem>>,
145}
146
147impl<'a> Iterator for ParseItemIter<'a> {
148 type Item = &'a ParseItem;
149
150 fn next(&mut self) -> Option<Self::Item> {
151 if let Some(next) = self.next.take() {
152 return Some(next)
153 }
154 if let Some(other) = self.other.as_mut() {
155 return other.next()
156 }
157
158 None
159 }
160}
161
162#[derive(Debug)]
163pub struct ParseItemIterMut<'a> {
164 next: Option<&'a mut ParseItem>,
165 other: Option<IterMut<'a, ParseItem>>,
166}
167
168impl<'a> Iterator for ParseItemIterMut<'a> {
169 type Item = &'a mut ParseItem;
170
171 fn next(&mut self) -> Option<Self::Item> {
172 if let Some(next) = self.next.take() {
173 return Some(next)
174 }
175 if let Some(other) = self.other.as_mut() {
176 return other.next()
177 }
178
179 None
180 }
181}
182
183macro_rules! read_context {
186 ($doc: expr, $id: expr, $variant: ident) => {
187 $doc.get_from_context($id).and_then(|out| match out {
188 PreprocessorOutput::$variant(inner) => Some(inner),
190 _ => None,
191 })
192 };
193}
194
195pub(crate) use read_context;