use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse}; use crate::error::ServiceError; use futures::Future; use http_body_util::{combinators::BoxBody, BodyExt}; use hyper::{ header::{HeaderValue, CONTENT_TYPE}, Response, }; use serde::Deserialize; use serde_yml::Value; use std::{fs::read_to_string, path::PathBuf, pin::Pin, sync::Arc}; pub struct FileKind; #[derive(Debug, Deserialize)] struct FileConfig { path: Option, content: Option, r#type: Option, } #[derive(Debug, Deserialize)] struct File { content: String, r#type: String, } impl NodeKind for FileKind { fn name(&self) -> &'static str { "file" } fn instanciate(&self, config: Value) -> anyhow::Result> { let conf = serde_yml::from_value::(config)?; Ok(Arc::new(File { content: conf .content .or(conf .path .map(|p| Ok::<_, ServiceError>(read_to_string(p)?)) .transpose()?) .unwrap_or_default(), r#type: conf.r#type.unwrap_or("text/html".to_string()), // TODO infer mime from ext })) } } impl Node for File { fn handle<'a>( &'a self, _context: &'a mut NodeContext, _request: NodeRequest, ) -> Pin> + Send + Sync + 'a>> { Box::pin(async move { let mut r = Response::new(BoxBody::<_, ServiceError>::new( self.content.clone().map_err(|_| unreachable!()), )); r.headers_mut() .insert(CONTENT_TYPE, HeaderValue::from_str(&self.r#type).unwrap()); Ok(r) }) } }