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