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
use lazy_static::lazy_static;
use regex::Regex;

use super::frame::Frame;

lazy_static! {
    static ref RE_CMD_IDENTIFY: Regex = Regex::new(r"^@identify<(?P<channel_id>[a-zA-Z\d_-]+)>").unwrap();
    static ref RE_CMD_CHANNEL_MESSAGE: Regex =
        Regex::new(r"^@message<(?P<channel_id>[a-zA-Z\d_-]+)>(?P<message>.*)$").unwrap();
    static ref RE_CMD_TERMINATE: Regex = Regex::new(r"^@terminate<(?P<channel_id>[a-zA-Z\d_-]+)>").unwrap();
}


#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ServerCommand {
    /// Send message to give target.
    Message(Option<String>, String),

    /// Send identifier
    Identify(String),

    /// Terminate server.
    Terminate,
}

#[derive(Debug, Clone)]
pub enum ChannelCommand {
    /// Identify the connection endpoint, normally use `channel-id`.
    Identify(String),

    /// Terminate connection.
    Terminate(String),

    /// Channel message. normally sent by server.
    ChannelMessage((String, String)),

    /// Ping msg
    Ping,

    /// Pong msg
    Pong,
}

impl Into<Frame> for ChannelCommand {
    fn into(self) -> Frame {
        (match self {
            ChannelCommand::Identify(id) => format!("@identify<{}>", id),
            ChannelCommand::Terminate(id) => format!("@terminate<{}>", id),
            ChannelCommand::ChannelMessage((id, msg)) => {
                format!("@message<{}>{}", id, msg)
            }
            ChannelCommand::Ping => format!("@ping"),
            ChannelCommand::Pong => format!("@pong"),
        })
        .into()
    }
}

impl From<Frame> for ChannelCommand {
    fn from(value: Frame) -> Self {
        let value: String = value.into();
        if &value == "@ping" {
            return Self::Ping;
        } else if &value == "@pong" {
            return Self::Pong;
        }

        if RE_CMD_IDENTIFY.is_match(&value) {
            if let Some(cap) = RE_CMD_IDENTIFY.captures(&value) {
                if let Some(mat) = cap.name("channel_id") {
                    return Self::Identify(mat.as_str().to_string());
                }
            }
        } else if RE_CMD_TERMINATE.is_match(&value) {
            if let Some(cap) = RE_CMD_TERMINATE.captures(&value) {
                if let Some(mat) = cap.name("channel_id") {
                    return Self::Terminate(mat.as_str().to_string());
                }
            }
        } else if RE_CMD_CHANNEL_MESSAGE.is_match(&value) {
            if let Some(cap) = RE_CMD_CHANNEL_MESSAGE.captures(&value) {
                if let Some(mat_id) = cap.name("channel_id") {
                    if let Some(mat_msg) = cap.name("message") {
                        return Self::ChannelMessage((
                            mat_id.as_str().to_string(),
                            mat_msg.as_str().to_string(),
                        ));
                    }
                    return Self::ChannelMessage((mat_id.as_str().to_string(), String::new()));
                }
            }
        }
        panic!("Unknown AiTcpCommand input: `{}`", value);
    }
}