diff options
Diffstat (limited to 'src/response.rs')
| -rw-r--r-- | src/response.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/response.rs b/src/response.rs new file mode 100644 index 0000000..f6f0fa0 --- /dev/null +++ b/src/response.rs @@ -0,0 +1,133 @@ +mod builder; + +use get::Get; + +use mlua::{FromLua, Lua, Value}; + +use std::collections::HashMap; + +use tokio::{ + fs::File, + io::{self, AsyncWrite, AsyncWriteExt}, +}; + +use strum::{Display, EnumString, FromRepr}; + +use builder::Builder; + +use crate::lua; + +#[allow(unreachable_patterns)] +#[derive(Debug, Clone, Copy, Display, EnumString, FromRepr)] +pub enum Status { + #[strum(to_string = "200 OK", serialize = "OK")] + Ok = 200, + + #[strum(to_string = "404 Not Found", serialize = "Not Found")] + NotFound = 404, + + #[strum( + to_string = "500 Internal Server Error", + serialize = "Internal ServerError" + )] + InternalServerError = 500, +} + +#[derive(Debug)] +pub enum Body { + File(File), + Buffer(Vec<u8>), + Empty, +} + +#[derive(Debug, Get)] +pub struct Response { + status: Status, + headers: HashMap<String, Vec<u8>>, + body: Body, +} + +impl Response { + pub fn builder() -> Builder { + Builder::new() + } +} + +impl Response { + pub async fn serialize<W: AsyncWrite + Unpin>( + mut self, + mut writer: W, + ) -> Result<(), io::Error> { + writer.write_all(b"HTTP/1.1 ").await?; + writer.write_all(self.status.to_string().as_bytes()).await?; + writer.write_all(b"\r\n").await?; + + for (key, value) in &self.headers { + writer.write_all(key.as_bytes()).await?; + writer.write_all(b": ").await?; + writer.write_all(value).await?; + writer.write_all(b"\r\n").await?; + } + + writer.write_all(b"\r\n").await?; + + match &mut self.body { + Body::File(file) => { + io::copy(file, &mut writer).await?; + } + Body::Buffer(buf) => { + writer.write_all(buf).await?; + } + Body::Empty => (), + } + + Ok(()) + } +} + +impl FromLua for Response { + fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> { + match value { + Value::Table(table) => { + let status = match table.get("status")? { + Value::Integer(i) => match Status::from_repr(i as usize) { + Some(status) => status, + None => return Err(mlua::Error::runtime(format!("invalid status: {i}"))), + }, + _ => return Err(mlua::Error::runtime("invalid status")), + }; + + let headers = match table.get("headers")? { + Value::Table(table) => { + let mut headers = HashMap::new(); + + for result in table.pairs() { + let (key, value): (String, mlua::String) = result?; + + headers.insert(key, value.as_bytes().to_vec()); + } + + headers + } + Value::Nil => HashMap::new(), + _ => return Err(mlua::Error::runtime("invalid headers")), + }; + + let body = match table.get("body")? { + Value::String(string) => Body::Buffer(string.as_bytes().to_vec()), + Value::UserData(userdata) if userdata.is::<lua::File>() => { + Body::File(userdata.borrow_mut::<lua::File>().unwrap().take().unwrap()) + } + _ => return Err(mlua::Error::runtime("invalid body")), + }; + + Ok(Self { + status, + headers, + body, + }) + } + _ => Err(mlua::Error::runtime("invalid response, expected a table")), + } + } +} |
