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
use crate::{error::NomResult, is_whitespace};
use nom::{bytes::complete::take_till1, combinator::map, error::context};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Owner {
    Email(String),
    Handle(String),
    Text(String),
}

impl Owner {
    fn handle<'a>(handle: &'a str) -> Self {
        Owner::Handle(handle.trim_start_matches('@').to_string())
    }

    fn email<'a>(email: &'a str) -> Self {
        Owner::Email(email.to_string())
    }

    fn text<'a>(text: &'a str) -> Self {
        Owner::Text(text.to_string())
    }
}

impl<'a> From<&'a str> for Owner {
    fn from(input: &'a str) -> Self {
        if input.starts_with('@') {
            return Owner::handle(input);
        }
        if input.contains('@') && !input.ends_with('@') {
            return Owner::email(input);
        }
        Owner::text(input)
    }
}

pub(crate) fn owner(input: &str) -> NomResult<Owner> {
    context("owner", map(take_till1(is_whitespace), Owner::from))(input)
}

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

    #[test]
    fn handle() {
        let (rem, parsed) = owner("@handle").unwrap();

        assert_eq!(parsed, Owner::Handle("handle".into()));
        assert!(rem.is_empty());
    }

    #[test]
    fn email() {
        let (rem, parsed) = owner("name@domain").unwrap();

        assert_eq!(parsed, Owner::Email("name@domain".into()));
        assert!(rem.is_empty());
    }

    #[test]
    fn text() {
        let (rem, parsed) = owner("text").unwrap();

        assert_eq!(parsed, Owner::Text("text".into()));
        assert!(rem.is_empty());
    }
}