hitman 0.12.1

A command line tool for hitting API endpoints
Documentation
use std::collections::HashMap;
use std::hash::Hash;

const TRUNC_COLUMN: usize = 92;

pub fn truncate(s: &str) -> String {
    match s.char_indices().nth(TRUNC_COLUMN - 3) {
        None => s.to_string(),
        Some((idx, _)) => format!("{}...", &s[..idx]),
    }
}

pub trait IterExt
where
    Self: Iterator + Sized,
{
    fn counted(self) -> HashMap<Self::Item, u32>
    where
        <Self as Iterator>::Item: Hash;
}

impl<T> IterExt for T
where
    T: Iterator,
    T::Item: Hash + Eq,
{
    fn counted(self) -> HashMap<T::Item, u32> {
        let mut map: HashMap<T::Item, u32> = HashMap::new();

        for item in self {
            map.entry(item).and_modify(|count| *count += 1).or_insert(1);
        }

        map
    }
}

pub struct SplitWork {
    total_count: i32,
    workers: i32,
}

pub fn split_work(total_count: i32, workers: i32) -> SplitWork {
    SplitWork {
        total_count,
        workers,
    }
}

impl Iterator for SplitWork {
    type Item = i32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.total_count > 0 {
            let c = (self.total_count + self.workers - 1) / self.workers;
            self.total_count -= c;
            self.workers -= 1;
            Some(c)
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_truncate_short() {
        assert_eq!(truncate("foo"), "foo");
    }

    #[test]
    fn test_truncate_long() {
        let long = "x".repeat(200);
        let expected = "x".repeat(89) + "...";
        assert_eq!(truncate(&long), expected);
    }

    #[test]
    fn test_truncate_unicode() {
        let long = "".repeat(200);
        let expected = "".repeat(89) + "...";
        assert_eq!(truncate(&long), expected);
    }

    #[test]
    fn counted_numbers() {
        let values: Vec<i32> = vec![100, 200, 200, 300, 200];

        let result = values.into_iter().counted();
        assert_eq!(1, result[&100]);
        assert_eq!(3, result[&200]);
        assert_eq!(1, result[&300]);
    }

    #[test]
    fn counted_strings() {
        let values: Vec<_> = vec!["100", "200", "200", "300", "200"];

        let result = values.into_iter().counted();
        assert_eq!(1, result["100"]);
        assert_eq!(3, result["200"]);
        assert_eq!(1, result["300"]);
    }

    #[test]
    fn splits_work_into_equal_chunks() {
        let chunks: Vec<_> = split_work(100, 4).collect();

        assert_eq!(chunks, vec![25, 25, 25, 25]);
    }

    #[test]
    fn splits_work_into_almost_equal_chunks() {
        let chunks: Vec<_> = split_work(15, 10).collect();

        assert_eq!(chunks, vec![2, 2, 2, 2, 2, 1, 1, 1, 1, 1]);
    }

    #[test]
    fn splits_work_and_eliminates_zeros() {
        let chunks: Vec<_> = split_work(4, 10).collect();

        assert_eq!(chunks, vec![1, 1, 1, 1]);
    }
}