aboutsummaryrefslogtreecommitdiff
path: root/ops/src/stats.rs
blob: 49f406853331ce0bc2b56d918546002d95b29643 (plain)
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
//! Per-nick word/line statistics

use ilc_base::{self, Context, Decode, Event};
use ilc_base::event::Type;

use std::collections::HashMap;
use std::io::BufRead;

use serde::ser::{MapVisitor, Serialize, Serializer};

pub struct Stats {
    pub freqs: HashMap<String, NickStat>,
}

impl Serialize for Stats {
    fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        struct Visitor<'a>(&'a Stats);
        impl<'a> MapVisitor for Visitor<'a> {
            fn visit<S>(&mut self, s: &mut S) -> Result<Option<()>, S::Error>
                where S: Serializer
            {
                try!(s.serialize_struct_elt("freqs", &self.0.freqs));
                Ok(None)
            }

            fn len(&self) -> Option<usize> {
                Some(1)
            }
        }
        s.serialize_struct("Stats", Visitor(self))
    }
}

pub struct NickStat {
    pub lines: u32,
    pub alpha_lines: u32,
    pub words: u32,
}

impl Serialize for NickStat {
    fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        struct Visitor<'a>(&'a NickStat);
        impl<'a> MapVisitor for Visitor<'a> {
            fn visit<S>(&mut self, s: &mut S) -> Result<Option<()>, S::Error>
                where S: Serializer
            {
                try!(s.serialize_struct_elt("lines", self.0.lines));
                try!(s.serialize_struct_elt("alpha_lines", self.0.alpha_lines));
                try!(s.serialize_struct_elt("words", self.0.words));
                Ok(None)
            }

            fn len(&self) -> Option<usize> {
                Some(3)
            }
        }

        s.serialize_struct("NickStat", Visitor(self))
    }
}

fn words_alpha(s: &str) -> (u32, bool) {
    let mut alpha = false;
    let mut words = 0;
    for w in s.split_whitespace() {
        if !w.is_empty() {
            words += 1;
            if w.chars().any(char::is_alphabetic) {
                alpha = true
            }
        }
    }
    (words, alpha)
}

fn strip_nick(s: &str) -> &str {
    if s.is_empty() {
        return s;
    }
    match s.as_bytes()[0] {
        b'~' | b'&' | b'@' | b'%' | b'+' => &s[1..],
        _ => s,
    }
    .trim_right_matches('_')
}

/// Return all active nicks, with lines, words and words per lines counted.
pub fn stats(ctx: &Context, input: &mut BufRead, decoder: &mut Decode) -> ilc_base::Result<Stats> {
    let mut freqs: HashMap<String, NickStat> = HashMap::new();

    for e in decoder.decode(&ctx, input) {
        let m = try!(e);
        match m {
            Event { ty: Type::Msg { ref from, ref content, .. }, .. } => {
                let nick = strip_nick(from);
                if freqs.contains_key(nick) {
                    let p: &mut NickStat = freqs.get_mut(nick).unwrap();
                    let (words, alpha) = words_alpha(content);
                    p.lines += 1;
                    if alpha {
                        p.alpha_lines += 1
                    }
                    p.words += words;
                } else {
                    let (words, alpha) = words_alpha(content);
                    freqs.insert(nick.to_owned(),
                                 NickStat {
                                     lines: 1,
                                     alpha_lines: if alpha { 1 } else { 0 },
                                     words: words,
                                 });
                }
            }
            _ => (),
        }
    }

    Ok(Stats { freqs: freqs })
}