1use std::collections::BTreeMap;
6
7#[derive(Debug, Clone, Default, PartialEq, Eq)]
9pub struct Dictionary {
10 entries: BTreeMap<String, String>,
11}
12
13impl Dictionary {
14 pub fn new() -> Dictionary {
15 Dictionary::default()
16 }
17
18 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
19 self.entries.insert(key.into(), value.into());
20 }
21
22 pub fn get(&self, key: &str) -> Option<&str> {
23 self.entries.get(key).map(String::as_str)
24 }
25
26 pub fn get_int(&self, key: &str) -> Option<i64> {
28 self.get(key).and_then(|v| v.parse().ok())
29 }
30
31 pub fn get_bitrate(&self, key: &str) -> Option<i64> {
35 let v = self.get(key)?.trim();
36 let (num, mult) = match v.chars().last() {
37 Some('k') | Some('K') => (&v[..v.len() - 1], 1_000i64),
38 Some('m') | Some('M') => (&v[..v.len() - 1], 1_000_000i64),
39 _ => (v, 1),
40 };
41 num.trim()
42 .parse::<f64>()
43 .ok()
44 .map(|n| (n * mult as f64) as i64)
45 }
46
47 pub fn is_empty(&self) -> bool {
48 self.entries.is_empty()
49 }
50
51 pub fn len(&self) -> usize {
52 self.entries.len()
53 }
54
55 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
56 self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str()))
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::Dictionary;
63
64 #[test]
65 fn get_bitrate_parses_suffixes() {
66 let mut d = Dictionary::new();
67 d.set("b", "128k");
68 assert_eq!(d.get_bitrate("b"), Some(128_000));
69 d.set("b", "1M");
70 assert_eq!(d.get_bitrate("b"), Some(1_000_000));
71 d.set("b", "24000");
72 assert_eq!(d.get_bitrate("b"), Some(24_000));
73 d.set("b", "1.5M");
74 assert_eq!(d.get_bitrate("b"), Some(1_500_000));
75 d.set("b", "96K");
76 assert_eq!(d.get_bitrate("b"), Some(96_000));
77 assert_eq!(d.get_bitrate("missing"), None);
78 d.set("b", "garbage");
79 assert_eq!(d.get_bitrate("b"), None);
80 }
81}