wz_fmt/
lib.rs

1//! Types for generating wz's output
2//!
3//! [wz]: https://crates.io/crates/wz
4
5pub mod json;
6pub mod table;
7
8pub type Message = (String, Result<Stats, String>);
9
10pub trait Output {
11    type Options;
12    type Error;
13    fn to_writer<W: Write>(self, options: Self::Options, writter: W) -> Result<(), Self::Error>;
14}
15
16use std::{
17    fmt::Display,
18    io::Write,
19    ops::{Add, AddAssign},
20};
21
22use serde::Serialize;
23use tabled::Tabled;
24use wz_core::*;
25
26/// Collector for [wz-utf8] counters
27///
28/// [wz-utf8]: https://crates.io/crates/wz-utf8
29#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Default, Tabled)]
30pub struct Stats {
31    #[tabled(display_with = "display_option")]
32    #[serde(skip_serializing_if = "Option::is_none")]
33    lines: Option<usize>,
34    #[tabled(display_with = "display_option")]
35    #[serde(skip_serializing_if = "Option::is_none")]
36    words: Option<usize>,
37    #[tabled(display_with = "display_option")]
38    #[serde(skip_serializing_if = "Option::is_none")]
39    characters: Option<usize>,
40    #[tabled(display_with = "display_option")]
41    #[serde(skip_serializing_if = "Option::is_none")]
42    bytes: Option<usize>,
43    #[tabled(display_with = "display_option")]
44    #[serde(skip_serializing_if = "Option::is_none")]
45    max: Option<usize>,
46}
47
48impl Stats {
49    // Creates a new identity stats
50    pub fn identity() -> Self {
51        Self {
52            lines: Some(0),
53            words: Some(0),
54            characters: Some(0),
55            bytes: Some(0),
56            max: Some(0),
57        }
58    }
59}
60/// Defines display representation on [`Table`]
61///
62/// [`Table`]: crate::table::Table
63fn display_option<T: Display>(opt: &Option<T>) -> String {
64    match opt {
65        Some(x) => x.to_string(),
66        None => Default::default(),
67    }
68}
69
70/// Creates a variable that combines two values with the same name on two
71/// structs
72macro_rules! add_name {
73    ( $($x1:ident $x2:ident $name:tt ), * ) => {
74        $(let $name = $x1.$name.zip($x2.$name).map(|(x,y)|x+y) ;)*
75    };
76}
77
78impl Add for Stats {
79    type Output = Stats;
80
81    fn add(self, rhs: Self) -> Self::Output {
82        add_name!(
83            self rhs lines,
84            self rhs words,
85            self rhs characters,
86            self rhs bytes
87        );
88        let max_line_length = self.max.zip(rhs.max).map(|(x, y)| std::cmp::max(x, y));
89        Self {
90            lines,
91            words,
92            characters,
93            bytes,
94            max: max_line_length,
95        }
96    }
97}
98
99impl AddAssign for Stats {
100    fn add_assign(&mut self, rhs: Self) {
101        *self = *self + rhs
102    }
103}
104
105/// Implements a collector-like trait on stats
106macro_rules! impl_collector_stats {
107    ( $($name:ty=>$field:tt), *) => {
108        $(
109            impl $name for Stats {
110                fn collect(&mut self, count: usize) {
111                    self.$field = Some(count);
112                }
113            }
114        )*
115    };
116}
117
118impl_collector_stats!(
119    LinesCollector=>lines,
120    WordsCollector=>words,
121    CharsCollector=>characters,
122    BytesCollector=>bytes,
123    MaxLineLengthCollector=>max
124);