daab/diagnostics/textual.rs
1
2
3use super::Doctor;
4use super::BuilderHandle;
5use super::ArtifactHandle;
6use super::CanBase;
7
8use std::io::Write;
9use cfg_if::cfg_if;
10
11/// Output options for [`TextualDoc`].
12///
13/// **Notice: This struc is only available if the `diagnostics` feature has been activated**.
14///
15/// This struct contains outputting options for the `TextualDoc`.
16///
17/// It has a `Default` impl with the following value:
18/// ```
19/// # use daab::diagnostics::TextualDocOptions;
20/// // Value of default()
21/// let opts = TextualDocOptions {
22/// show_builder_values: false,
23/// show_artifact_values: false,
24/// show_addresses: false,
25/// tynm_m_n: Some((0,0)),
26/// };
27/// assert_eq!(opts, TextualDocOptions::default());
28/// ```
29///
30///[`TextualDoc`]: struct.TextualDoc.html
31///
32///
33/// ## Features
34///
35/// By default, this `Doctor` prints the type names of the builders and artifacts encountered.
36/// The stringification is done via `std::any::type_name()`. However, this usually
37/// generates long names, therefore this carte has the **`tynm`** feature, which adds
38/// the [`tynm`] crate and allows to abbreviate the type names configured by this
39/// struct's `tynm_m_n` field. Also see the [tynm docs] for details about `m` and `n`.
40///
41///[`tynm`]: https://crates.io/crates/tynm
42///[tynm docs]: https://docs.rs/tynm/
43///
44#[derive(Debug, Copy, Clone, PartialEq, Eq)]
45pub struct TextualDocOptions {
46 /// Configures whether builders should be only visualized by their
47 /// value (`true`) instead of by their type (`false`)
48 /// .
49 pub show_builder_values: bool,
50
51 /// Configures whether artifacts should be only visualized by their
52 /// value (`true`) instead of by their type (`false`)
53 pub show_artifact_values: bool,
54
55 /// Configures whether the pointer of artifacts and builders should be
56 /// printed for better identification (`true`) or
57 /// not for better readability (`false`).
58 pub show_addresses: bool,
59
60 /// Configures type name abbreviations according to `tynm`s `type_namemn()` function.
61 ///
62 /// `None` specifies to use the normal `std::any::type_name()`, and is the
63 /// fallback if the **`tynm`** feature is not activated.
64 ///
65 /// See the [tynm docs] for details about how to specify `m` and `n`.
66 ///
67 /// **Notice:** the **`tynm`** feature is required for this field to take effect.
68 ///
69 ///[tynm docs]: https://docs.rs/tynm/
70 ///
71 pub tynm_m_n: Option<(usize, usize)>,
72}
73
74impl Default for TextualDocOptions {
75 fn default() -> Self {
76 TextualDocOptions {
77 show_builder_values: false,
78 show_artifact_values: false,
79 show_addresses: false,
80 tynm_m_n: Some((0,0)),
81 }
82 }
83}
84
85/// Debugger outputting human-readable text file e.g. on the terminal.
86///
87/// **Notice: This struct is only available if the `diagnostics` feature has been activated**.
88///
89/// The Textual Doctor generates writes the list of events in text form
90/// to its output (e.g. `stdout`).
91///
92/// ## Example
93///
94/// ```no_run
95/// use std::fs::File;
96/// use daab::rc::Cache;
97/// use daab::diagnostics::{TextualDoc, TextualDocOptions};
98/// use std::io::stdout;
99///
100/// let mut cache = Cache::new_with_doctor(
101/// TextualDoc::new(
102/// TextualDocOptions {
103/// show_builder_values: false,
104/// show_artifact_values: true,
105/// show_addresses: false,
106/// tynm_m_n: Some((0,0)),
107/// },
108/// stdout()
109/// )
110/// );
111///
112/// //...
113/// ```
114///
115/// Example output:
116///
117/// ```text
118/// resolves BuilderSimpleNode -> BuilderLeaf
119/// built #0.0 BuilderLeaf => Rc<Leaf>
120/// built #0.1 BuilderSimpleNode => Rc<SimpleNode>
121/// resolves BuilderSimpleNode -> BuilderLeaf
122/// built #0.2 BuilderSimpleNode => Rc<SimpleNode>
123/// ```
124///
125pub struct TextualDoc<W: Write> {
126 /// Output options
127 opts: TextualDocOptions,
128
129 /// Output Write
130 output: W,
131
132 /// Counts (generation, instance) of artifacts
133 /// It is used to making each artifact unique.
134 /// The generation increases whenever a artifact might be recreated
135 /// i.e. after a call to `clear()` or `invalidate()`.
136 count: (u64, u64),
137}
138
139impl<W: Write> TextualDoc<W> {
140 /// Creates a new Textual Doctor
141 ///
142 pub fn new(opts: TextualDocOptions, output: W) -> Self {
143
144 //writeln!(output, "strict digraph {{ graph [labeljust = l];").unwrap();
145
146 TextualDoc {
147 opts,
148 output,
149 count: (0, 0),
150 }
151 }
152
153 fn tynm(&self, ty: &str) -> String {
154 cfg_if! {
155 if #[cfg(feature = "tynm")] {
156 if let Some((m, n)) = self.opts.tynm_m_n {
157 use tynm::TypeName;
158
159 let tn: TypeName = ty.into();
160
161 tn.as_str_mn(m, n)
162 } else {
163 ty.to_string()
164 }
165 } else {
166 ty.to_string()
167 }
168 }
169 }
170
171 /// Strigify given builder entry.
172 fn builder_str<'a, BCan>(&self, builder: &'a BuilderHandle<BCan>) -> String {
173 if self.opts.show_builder_values {
174 builder.dbg_text.clone()
175 } else {
176 self.tynm(builder.type_name)
177 }
178 }
179
180 /// Auxiliary to get the output by `&mut`.
181 /// depricated
182 fn output(&mut self) -> &mut W {
183 &mut self.output
184 }
185
186 /// Dismantles this struct and returns the inner `Write`.
187 ///
188 pub fn into_inner(self) -> W {
189 self.output
190 }
191}
192
193impl<ArtCan: CanBase, BCan, W: Write> Doctor<ArtCan, BCan> for TextualDoc<W> {
194 fn resolve(&mut self, builder: &BuilderHandle<BCan>, used: &BuilderHandle<BCan>) {
195
196 let bs = self.builder_str(builder);
197 let us = self.builder_str(used);
198
199 if self.opts.show_addresses {
200 writeln!(self.output(),
201 r#"resolves [{:p}] {} -> [{:p}] {}"#,
202 builder.id(),
203 bs,
204 used.id(),
205 us,
206 ).unwrap();
207 } else {
208 writeln!(self.output(),
209 r#"resolves {} -> {}"#,
210 bs,
211 us,
212 ).unwrap();
213 }
214 }
215
216
217 fn build(&mut self, builder: &BuilderHandle<BCan>, artifact: &ArtifactHandle<ArtCan>) {
218 let count = self.count;
219
220 let bs = self.builder_str(builder);
221 if self.opts.show_addresses {
222 write!(self.output(),
223 r#"built #{}.{} [{:p}] {} => [{:p}] "#,
224 count.0,
225 count.1,
226 builder.id(),
227 bs,
228 artifact.value.can_as_ptr(),
229 ).unwrap();
230 } else {
231 write!(self.output(),
232 r#"built #{}.{} {} => "#,
233 count.0,
234 count.1,
235 bs,
236 ).unwrap();
237 }
238
239 if self.opts.show_artifact_values {
240 writeln!(self.output(),
241 "{}",
242 artifact.dbg_text,
243 ).unwrap();
244 } else {
245 let s = self.tynm(artifact.type_name);
246 writeln!(self.output(),
247 "{}",
248 s,
249 ).unwrap();
250 }
251
252 self.output().flush().unwrap();
253
254 self.count.1 += 1;
255
256 }
257
258 fn clear(&mut self) {
259 let count = self.count;
260
261 writeln!(self.output(),
262 r"Clears generation #{}",
263 count.0,
264 ).unwrap();
265
266 // Generations inc
267 self.count.0 += 1;
268 self.count.1 = 0;
269 }
270
271 fn invalidate(&mut self, builder: &BuilderHandle<BCan>) {
272 let count = self.count;
273
274 write!(self.output(),
275 r"Invalidates generation #{} targeting ",
276 count.0,
277 ).unwrap();
278
279 let bs = self.builder_str(builder);
280 if self.opts.show_addresses {
281 write!(self.output(),
282 "[{:p}] {}",
283 builder.id(),
284 bs,
285 ).unwrap();
286 } else {
287 write!(self.output(),
288 "{}",
289 bs,
290 ).unwrap();
291 }
292
293 // Generations inc
294 self.count.0 += 1;
295 self.count.1 = 0;
296 }
297}
298
299
300