1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
use std::{
    ops::{Add, AddAssign},
    time::Duration,
};

use crate::util::NiceDuration;

/// Summary statistics produced at the end of a search.
///
/// When statistics are reported by a printer, they correspond to all searches
/// executed with that printer.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Stats {
    elapsed: NiceDuration,
    searches: u64,
    searches_with_match: u64,
    bytes_searched: u64,
    bytes_printed: u64,
    matched_lines: u64,
    matches: u64,
}

impl Stats {
    /// Return a new value for tracking aggregate statistics across searches.
    ///
    /// All statistics are set to `0`.
    pub fn new() -> Stats {
        Stats::default()
    }

    /// Return the total amount of time elapsed.
    pub fn elapsed(&self) -> Duration {
        self.elapsed.0
    }

    /// Return the total number of searches executed.
    pub fn searches(&self) -> u64 {
        self.searches
    }

    /// Return the total number of searches that found at least one match.
    pub fn searches_with_match(&self) -> u64 {
        self.searches_with_match
    }

    /// Return the total number of bytes searched.
    pub fn bytes_searched(&self) -> u64 {
        self.bytes_searched
    }

    /// Return the total number of bytes printed.
    pub fn bytes_printed(&self) -> u64 {
        self.bytes_printed
    }

    /// Return the total number of lines that participated in a match.
    ///
    /// When matches may contain multiple lines then this includes every line
    /// that is part of every match.
    pub fn matched_lines(&self) -> u64 {
        self.matched_lines
    }

    /// Return the total number of matches.
    ///
    /// There may be multiple matches per line.
    pub fn matches(&self) -> u64 {
        self.matches
    }

    /// Add to the elapsed time.
    pub fn add_elapsed(&mut self, duration: Duration) {
        self.elapsed.0 += duration;
    }

    /// Add to the number of searches executed.
    pub fn add_searches(&mut self, n: u64) {
        self.searches += n;
    }

    /// Add to the number of searches that found at least one match.
    pub fn add_searches_with_match(&mut self, n: u64) {
        self.searches_with_match += n;
    }

    /// Add to the total number of bytes searched.
    pub fn add_bytes_searched(&mut self, n: u64) {
        self.bytes_searched += n;
    }

    /// Add to the total number of bytes printed.
    pub fn add_bytes_printed(&mut self, n: u64) {
        self.bytes_printed += n;
    }

    /// Add to the total number of lines that participated in a match.
    pub fn add_matched_lines(&mut self, n: u64) {
        self.matched_lines += n;
    }

    /// Add to the total number of matches.
    pub fn add_matches(&mut self, n: u64) {
        self.matches += n;
    }
}

impl Add for Stats {
    type Output = Stats;

    fn add(self, rhs: Stats) -> Stats {
        self + &rhs
    }
}

impl<'a> Add<&'a Stats> for Stats {
    type Output = Stats;

    fn add(self, rhs: &'a Stats) -> Stats {
        Stats {
            elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0),
            searches: self.searches + rhs.searches,
            searches_with_match: self.searches_with_match
                + rhs.searches_with_match,
            bytes_searched: self.bytes_searched + rhs.bytes_searched,
            bytes_printed: self.bytes_printed + rhs.bytes_printed,
            matched_lines: self.matched_lines + rhs.matched_lines,
            matches: self.matches + rhs.matches,
        }
    }
}

impl AddAssign for Stats {
    fn add_assign(&mut self, rhs: Stats) {
        *self += &rhs;
    }
}

impl<'a> AddAssign<&'a Stats> for Stats {
    fn add_assign(&mut self, rhs: &'a Stats) {
        self.elapsed.0 += rhs.elapsed.0;
        self.searches += rhs.searches;
        self.searches_with_match += rhs.searches_with_match;
        self.bytes_searched += rhs.bytes_searched;
        self.bytes_printed += rhs.bytes_printed;
        self.matched_lines += rhs.matched_lines;
        self.matches += rhs.matches;
    }
}

#[cfg(feature = "serde")]
impl serde::Serialize for Stats {
    fn serialize<S: serde::Serializer>(
        &self,
        s: S,
    ) -> Result<S::Ok, S::Error> {
        use serde::ser::SerializeStruct;

        let mut state = s.serialize_struct("Stats", 7)?;
        state.serialize_field("elapsed", &self.elapsed)?;
        state.serialize_field("searches", &self.searches)?;
        state.serialize_field(
            "searches_with_match",
            &self.searches_with_match,
        )?;
        state.serialize_field("bytes_searched", &self.bytes_searched)?;
        state.serialize_field("bytes_printed", &self.bytes_printed)?;
        state.serialize_field("matched_lines", &self.matched_lines)?;
        state.serialize_field("matches", &self.matches)?;
        state.end()
    }
}