1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use std::{
6 collections::HashMap,
7 pin::Pin,
8 task::{Context, Poll},
9};
10
11use bytes::Bytes;
12use futures_util::Stream;
13use hashlink::LinkedHashMap;
14use http_body::Body;
15use tokio_util::io::ReaderStream;
16
17#[cfg(feature = "client")]
18pub mod client;
19#[cfg(feature = "server")]
20pub mod server;
21
22#[derive(Debug)]
24pub struct CgiIncoming<R> {
25 pub(crate) inner: Pin<Box<ReaderStream<R>>>,
26}
27
28#[allow(unused)]
29impl<R> CgiIncoming<R>
30where
31 R: tokio::io::AsyncRead,
32{
33 pub(crate) fn new(inner: R) -> Self {
35 Self {
36 inner: Box::pin(ReaderStream::new(inner)),
37 }
38 }
39}
40
41impl<R> Body for CgiIncoming<R>
42where
43 R: tokio::io::AsyncRead,
44{
45 type Data = Bytes;
46 type Error = std::io::Error;
47
48 fn poll_frame(
49 mut self: Pin<&mut Self>,
50 cx: &mut Context<'_>,
51 ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
52 match Pin::new(&mut self.inner).poll_next(cx) {
53 Poll::Ready(Some(Ok(data))) => Poll::Ready(Some(Ok(http_body::Frame::data(data)))),
54 Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err))),
55 Poll::Ready(None) => Poll::Ready(None),
56 Poll::Pending => Poll::Pending,
57 }
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Hash)]
63pub struct CgiEnvironment {
64 pub(crate) inner: hashlink::LinkedHashMap<String, String>,
65}
66
67impl CgiEnvironment {
68 pub fn get(&self, key: &str) -> Option<&str> {
70 let key = key.to_uppercase();
71 self.inner.get(&key).map(|value| value.as_str())
72 }
73
74 pub fn contains_key(&self, key: &str) -> bool {
76 let key = key.to_uppercase();
77 self.inner.contains_key(&key)
78 }
79
80 pub fn iter<'a>(&'a self) -> hashlink::linked_hash_map::Iter<'a, String, String> {
82 self.inner.iter()
83 }
84
85 pub fn len(&self) -> usize {
87 self.inner.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.inner.is_empty()
93 }
94}
95
96impl std::ops::Index<&str> for CgiEnvironment {
97 type Output = str;
98
99 fn index(&self, key: &str) -> &Self::Output {
100 let key = key.to_uppercase();
101 self
102 .inner
103 .get(&key)
104 .unwrap_or_else(|| panic!("Missing environment variable: {}", key))
105 .as_str()
106 }
107}
108
109impl IntoIterator for CgiEnvironment {
110 type Item = (String, String);
111 type IntoIter = hashlink::linked_hash_map::IntoIter<String, String>;
112
113 fn into_iter(self) -> Self::IntoIter {
114 self.inner.into_iter()
115 }
116}
117
118impl<'a> IntoIterator for &'a CgiEnvironment {
119 type Item = (&'a String, &'a String);
120 type IntoIter = hashlink::linked_hash_map::Iter<'a, String, String>;
121
122 fn into_iter(self) -> Self::IntoIter {
123 self.inner.iter()
124 }
125}
126
127impl From<LinkedHashMap<String, String>> for CgiEnvironment {
128 fn from(map: LinkedHashMap<String, String>) -> Self {
129 Self { inner: map }
130 }
131}
132
133impl From<HashMap<String, String>> for CgiEnvironment {
134 fn from(map: HashMap<String, String>) -> Self {
135 Self {
136 inner: map.into_iter().collect(),
137 }
138 }
139}
140
141impl FromIterator<(String, String)> for CgiEnvironment {
142 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
143 Self {
144 inner: iter.into_iter().collect(),
145 }
146 }
147}