kodumaro_http_cli/cli/
param.rs

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
use std::{
    fmt::{Display, Formatter},
    str::FromStr,
    sync::LazyLock,
};

use eyre::eyre;
use regex::Regex;
use reqwest::Url;
use serde_json::{Map, Value};

use super::util::parse_string;


static FILE_PAYLOAD_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::from_str(r#"^@.+$"#).unwrap());
static HEADER_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::from_str(r#"^([\w-]+):(.*)$"#).unwrap());
static QUERY_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::from_str(r#"^([^=:]+)==(.*)$"#).unwrap());
static PAYLOAD_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::from_str(r#"^([^=:]+)=(.*)$"#).unwrap());


#[derive(Clone, Debug, PartialEq)]
pub enum Param {
    Header(String, String),
    Payload(Value),
    Query(String, String),
}

impl Display for Param {

    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Header(name, value) => write!(f, "{}:{}", name, value),
            Self::Payload(value) => write!(f, "{}", value),
            Self::Query(key, value) => {
                let mut buf = Url::parse("http://localhost/").unwrap();
                buf.query_pairs_mut().append_pair(key, value);
                write!(f, "{}", buf.query().unwrap())
            }
        }
    }
}

impl FromStr for Param {

    type Err = eyre::Error;

    fn from_str(value: &str) -> Result<Self, Self::Err> {

        if FILE_PAYLOAD_REGEX.is_match(value) {
            let payload: Value = serde_json::from_str(&parse_string(value)?)?;
            return Ok(Self::Payload(payload));
        }

        if let Some(pair) = QUERY_REGEX.captures(value) {
            let key = pair.get(1).ok_or(eyre!("invalid query {}", value))?.as_str();
            let value = pair.get(2).ok_or(eyre!("invalid query {}", value))?.as_str();
            return Ok(Self::Query(key.to_owned(), parse_string(value)?));
        }

        if let Some(pair) = PAYLOAD_REGEX.captures(value) {
            let key = pair.get(1).ok_or(eyre!("invalid attribute {}", value))?.as_str();
            let value = pair.get(2).ok_or(eyre!("invalid attribute {}", value))?.as_str();
            let value = parse_string(value)?;
            let value: Value = serde_json::from_str(&value).unwrap_or(Value::String(value));
            let mut payload = Map::new();
            payload.insert(key.to_owned(), value);
            return Ok(Self::Payload(Value::Object(payload)))
        }

        if let Some(pair) = HEADER_REGEX.captures(value) {
            let key = pair.get(1)
                .ok_or(eyre!("invalid header {}", value))?
                .as_str()
                .trim();
            let value = pair.get(2)
                .ok_or(eyre!("invalid header {}", value))?
                .as_str()
                .trim();
            return Ok(Self::Header(key.to_owned(), parse_string(value)?));
        }

        Err(eyre!("could not parse {}", value))
    }
}


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

    #[test]
    fn test_parse_header() {
        let param = Param::from_str("Content-Type: application/json").unwrap();
        assert_eq!(Param::Header("Content-Type".to_string(), "application/json".to_string()), param);
    }

    #[test]
    fn test_parse_query() {
        let param = Param::from_str("foo==bar").unwrap();
        assert_eq!(Param::Query("foo".to_string(), "bar".to_string()), param);
    }

    #[test]
    fn test_parse_payload() {
        let param = Param::from_str("num=42").unwrap();
        if let Param::Payload(Value::Object(param)) = param {
            let num= param.get("num").unwrap();
            assert!(num.is_i64());
            assert_eq!(42, num.as_i64().unwrap());
        } else {
            panic!();
        }
    }

    #[test]
    fn test_invalid_param() {
        let param = Param::from_str("invalid param");
        assert!(param.is_err());
    }
}