dothttp/output/
mod.rs

1pub mod print;
2
3mod ci;
4#[cfg(test)]
5mod tests;
6
7use std::fmt;
8
9use color_eyre::eyre::anyhow;
10
11pub use self::{ci::CiOutput, print::FormattedOutput};
12use crate::{
13    http::{Method, Request, Response},
14    script_engine::report::TestsReport,
15    Result,
16};
17
18#[derive(Debug, Eq, PartialEq, Clone)]
19pub enum FormatItem {
20    FirstLine,
21    Headers,
22    Body,
23    Tests,
24    Name,
25    Chars(String),
26}
27
28pub fn parse_format(format: &str) -> Result<Vec<FormatItem>> {
29    let mut result = Vec::new();
30    let mut marker = false;
31    let mut buff = String::new();
32    for ch in format.chars() {
33        if marker {
34            marker = false;
35            let action = match ch {
36                '%' => None,
37                'R' => Some(FormatItem::FirstLine),
38                'H' => Some(FormatItem::Headers),
39                'B' => Some(FormatItem::Body),
40                'T' => Some(FormatItem::Tests),
41                'N' => Some(FormatItem::Name),
42                _ => return Err(anyhow!("Invalid formatting character '{}'", ch)),
43            };
44            if let Some(a) = action {
45                if !buff.is_empty() {
46                    result.push(FormatItem::Chars(buff));
47                    buff = String::new();
48                }
49                result.push(a);
50            } else {
51                buff.push(ch);
52            }
53        } else if ch == '%' {
54            marker = true;
55        } else {
56            buff.push(ch);
57        }
58    }
59    if !buff.is_empty() {
60        result.push(FormatItem::Chars(buff));
61    }
62    Ok(result)
63}
64
65fn prettify_response_body(body: &str) -> String {
66    match serde_json::from_str(body) {
67        Ok(serde_json::Value::Object(response_body)) => {
68            serde_json::to_string_pretty(&response_body).unwrap()
69        }
70        _ => String::from(body),
71    }
72}
73
74pub trait Output {
75    fn response(&mut self, response: &Response, tests: &TestsReport) -> Result<()>;
76    fn request(&mut self, request: &Request, request_name: &str) -> Result<()>;
77    fn tests(&mut self, tests: Vec<(String, String, TestsReport)>) -> Result<()>;
78
79    fn exit_code(&mut self) -> std::process::ExitCode {
80        std::process::ExitCode::SUCCESS
81    }
82}
83
84impl Output for Box<dyn Output> {
85    fn response(&mut self, response: &Response, tests: &TestsReport) -> Result<()> {
86        (**self).response(response, tests)
87    }
88
89    fn request(&mut self, request: &Request, request_name: &str) -> Result<()> {
90        (**self).request(request, request_name)
91    }
92
93    fn tests(&mut self, tests: Vec<(String, String, TestsReport)>) -> Result<()> {
94        (**self).tests(tests)
95    }
96
97    fn exit_code(&mut self) -> std::process::ExitCode {
98        (**self).exit_code()
99    }
100}
101
102impl fmt::Display for Method {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        let method = match *self {
105            Method::Get => "GET",
106            Method::Post => "POST",
107            Method::Delete => "DELETE",
108            Method::Put => "PUT",
109            Method::Patch => "PATCH",
110            Method::Options => "OPTIONS",
111        };
112        f.write_str(method)
113    }
114}