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) => (index == 0).then_some(item),
105 Self::Constants(items) => items.get_mut(index),
106 Self::OverloadedFunctions(items) => items.get_mut(index),
107 }
108 }
109
110 pub fn iter_items(&self) -> ParseItemIter<'_> {
111 match self {
112 Self::Empty => ParseItemIter { next: None, other: None },
113 Self::Single(item) => ParseItemIter { next: Some(item), other: None },
114 Self::Constants(items) => ParseItemIter { next: None, other: Some(items.iter()) },
115 Self::OverloadedFunctions(items) => {
116 ParseItemIter { next: None, other: Some(items.iter()) }
117 }
118 }
119 }
120
121 pub fn iter_items_mut(&mut self) -> ParseItemIterMut<'_> {
122 match self {
123 Self::Empty => ParseItemIterMut { next: None, other: None },
124 Self::Single(item) => ParseItemIterMut { next: Some(item), other: None },
125 Self::Constants(items) => {
126 ParseItemIterMut { next: None, other: Some(items.iter_mut()) }
127 }
128 Self::OverloadedFunctions(items) => {
129 ParseItemIterMut { next: None, other: Some(items.iter_mut()) }
130 }
131 }
132 }
133}
134
135#[derive(Debug)]
136pub struct ParseItemIter<'a> {
137 next: Option<&'a ParseItem>,
138 other: Option<std::slice::Iter<'a, ParseItem>>,
139}
140
141impl<'a> Iterator for ParseItemIter<'a> {
142 type Item = &'a ParseItem;
143
144 fn next(&mut self) -> Option<Self::Item> {
145 if let Some(next) = self.next.take() {
146 return Some(next);
147 }
148 if let Some(other) = self.other.as_mut() {
149 return other.next();
150 }
151
152 None
153 }
154}
155
156#[derive(Debug)]
157pub struct ParseItemIterMut<'a> {
158 next: Option<&'a mut ParseItem>,
159 other: Option<IterMut<'a, ParseItem>>,
160}
161
162impl<'a> Iterator for ParseItemIterMut<'a> {
163 type Item = &'a mut ParseItem;
164
165 fn next(&mut self) -> Option<Self::Item> {
166 if let Some(next) = self.next.take() {
167 return Some(next);
168 }
169 if let Some(other) = self.other.as_mut() {
170 return other.next();
171 }
172
173 None
174 }
175}
176
177macro_rules! read_context {
180 ($doc:expr, $id:expr, $variant:ident) => {
181 $doc.get_from_context($id).and_then(|out| match out {
182 PreprocessorOutput::$variant(inner) => Some(inner),
184 _ => None,
185 })
186 };
187}
188
189pub(crate) use read_context;