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}