1use std::{
9 collections::HashMap,
10 fmt::{self, Display, Formatter},
11 str::FromStr,
12};
13
14use tokio::io::AsyncBufRead;
15
16mod cgi;
17
18#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
20#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
21pub enum CgiDialect {
22 #[default]
25 Rfc3875,
26}
27
28impl CgiDialect {
29 pub fn prepare_environment_variables(
30 self,
31 parts: http::request::Parts,
32 env: &mut HashMap<String, String>,
33 ) {
34 match self {
35 CgiDialect::Rfc3875 => cgi::prepare_environment_variables(parts, env),
36 }
37 }
38
39 pub async fn extract_response_header(
46 self,
47 stdout: &mut (impl AsyncBufRead + Unpin),
48 ) -> Result<http::response::Parts, CgiError> {
49 match self {
50 CgiDialect::Rfc3875 => cgi::extract_response_header(stdout).await,
51 }
52 }
53
54 pub const fn to_str(self) -> &'static str {
55 match self {
56 CgiDialect::Rfc3875 => "rfc-3875",
57 }
58 }
59}
60
61impl FromStr for CgiDialect {
62 type Err = UnknownCgiDialect;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 match s {
66 "rfc-3875" => Ok(CgiDialect::Rfc3875),
67 _ => Err(UnknownCgiDialect),
68 }
69 }
70}
71
72impl Display for CgiDialect {
73 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
74 write!(f, "{}", self.to_str())
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct UnknownCgiDialect;
80
81impl Display for UnknownCgiDialect {
82 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
83 write!(f, "Unknown CGI dialect")
84 }
85}
86
87impl std::error::Error for UnknownCgiDialect {}
88
89#[derive(Debug)]
90pub enum CgiError {
91 StdoutRead(std::io::Error),
92 InvalidHeaders {
93 error: http::Error,
94 header: String,
95 value: String,
96 },
97 MalformedWcgiHeader {
98 error: ::wcgi::WcgiError,
99 header: String,
100 },
101}
102
103impl std::error::Error for CgiError {
104 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
105 match self {
106 CgiError::StdoutRead(e) => Some(e),
107 CgiError::InvalidHeaders { error, .. } => Some(error),
108 CgiError::MalformedWcgiHeader { error, .. } => error.source(),
109 }
110 }
111}
112
113impl fmt::Display for CgiError {
114 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
115 match self {
116 CgiError::StdoutRead(_) => write!(f, "Unable to read the STDOUT pipe"),
117 CgiError::InvalidHeaders { header, value, .. } => {
118 write!(f, "Unable to parse header ({header}: {value})")
119 }
120 CgiError::MalformedWcgiHeader { header, .. } => {
121 write!(f, "Unable to parse WCGI header ({header})")
122 }
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn round_trip_cgi_dialect_to_string() {
133 let dialects = [CgiDialect::Rfc3875];
134
135 for dialect in dialects {
136 let repr = dialect.to_string();
137 let round_tripped: CgiDialect = repr.parse().unwrap();
138 assert_eq!(round_tripped, dialect);
139 }
140 }
141}