spacegate_plugin/plugins/
static_resource.rs

1use std::path::PathBuf;
2
3use hyper::{
4    body::Bytes,
5    header::{HeaderValue, CONTENT_TYPE},
6    Response, StatusCode,
7};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use spacegate_kernel::{extension::Reflect, BoxError, SgBody};
11
12use crate::{Plugin, PluginError};
13
14/// StaticResourceConfig
15#[derive(Debug, Clone, Serialize, Deserialize)]
16#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
17pub struct StaticResourceConfig {
18    /// response status code
19    pub code: u16,
20    /// response content type
21    pub content_type: String,
22    /// response body
23    pub body: BodyEnum,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
28#[serde(tag = "kind", content = "value")]
29pub enum BodyEnum {
30    /// json value
31    Json(Value),
32    /// plain text
33    Text(String),
34    /// read a static file from file system
35    File(PathBuf),
36}
37
38#[derive(Debug, Clone)]
39pub struct StaticResource {
40    pub code: StatusCode,
41    pub content_type: HeaderValue,
42    pub body: Bytes,
43}
44
45pub struct StaticResourcePlugin {
46    pub code: StatusCode,
47    pub content_type: HeaderValue,
48    pub body: Bytes,
49}
50
51impl Plugin for StaticResourcePlugin {
52    const CODE: &'static str = "static-resource";
53    #[cfg(feature = "schema")]
54    fn schema_opt() -> Option<schemars::schema::RootSchema> {
55        Some(<Self as crate::PluginSchemaExt>::schema())
56    }
57
58    async fn call(&self, req: hyper::Request<SgBody>, _inner: spacegate_kernel::helper_layers::function::Inner) -> Result<Response<SgBody>, BoxError> {
59        let mut resp = Response::builder()
60            .header(CONTENT_TYPE, self.content_type.clone())
61            .status(self.code)
62            .body(SgBody::full(self.body.clone()))
63            .map_err(PluginError::internal_error::<StaticResourcePlugin>)?;
64        if let Some(reflect) = req.into_parts().0.extensions.remove::<Reflect>() {
65            resp.extensions_mut().extend(reflect.into_inner());
66        }
67        Ok(resp)
68    }
69
70    fn create(config: crate::PluginConfig) -> Result<Self, spacegate_kernel::BoxError> {
71        let plugin_config: StaticResourceConfig = serde_json::from_value(config.spec)?;
72        let content_type = plugin_config.content_type.clone();
73        let content_type = HeaderValue::from_maybe_shared(content_type)?;
74        let body = match &plugin_config.body {
75            BodyEnum::Json(value) => Bytes::copy_from_slice(value.to_string().as_bytes()),
76            BodyEnum::Text(text) => Bytes::copy_from_slice(text.as_bytes()),
77            BodyEnum::File(path) => Bytes::copy_from_slice(&std::fs::read(path)?),
78        };
79        let code = StatusCode::from_u16(plugin_config.code)?;
80        Ok(Self { content_type, code, body })
81    }
82}
83
84#[cfg(feature = "schema")]
85crate::schema!(StaticResourcePlugin, StaticResourceConfig);