Skip to main content

sim_table_http/
options.rs

1//! HTTP directory option types.
2
3use sim_kernel::{Error, Result, Symbol};
4
5/// The HTTP method used for table `set`.
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
7pub enum HttpWriteMethod {
8    /// Write with `PUT`.
9    #[default]
10    Put,
11    /// Write with `POST`.
12    Post,
13}
14
15impl HttpWriteMethod {
16    /// Returns the wire method token.
17    pub fn as_str(self) -> &'static str {
18        match self {
19            Self::Put => "PUT",
20            Self::Post => "POST",
21        }
22    }
23
24    fn from_str(value: &str) -> Result<Self> {
25        match value {
26            "PUT" => Ok(Self::Put),
27            "POST" => Ok(Self::Post),
28            other => Err(Error::Eval(format!(
29                "table/http: unsupported write method {other}"
30            ))),
31        }
32    }
33}
34
35/// Configuration for an [`HttpDir`](crate::HttpDir).
36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct HttpDirOptions {
38    /// Base URL whose children are addressed by table keys.
39    pub base_url: String,
40    /// Codec used to decode response bodies and encode request bodies.
41    pub codec: Symbol,
42    /// Write method used by [`Table::set`](sim_kernel::Table::set).
43    pub write_method: HttpWriteMethod,
44    /// Socket read/write timeout in milliseconds.
45    pub timeout_ms: u64,
46    /// Maximum response body size in bytes.
47    pub max_body_bytes: usize,
48}
49
50impl HttpDirOptions {
51    /// Builds options for `base_url` with the Lisp codec, `PUT`, a five-second
52    /// timeout, and a 1 MiB response body cap.
53    pub fn new(base_url: impl Into<String>) -> Self {
54        Self {
55            base_url: base_url.into(),
56            codec: Symbol::qualified("codec", "lisp"),
57            write_method: HttpWriteMethod::Put,
58            timeout_ms: 5_000,
59            max_body_bytes: 1024 * 1024,
60        }
61    }
62
63    /// Returns options using `codec`.
64    pub fn with_codec(mut self, codec: Symbol) -> Self {
65        self.codec = codec;
66        self
67    }
68
69    /// Returns options using `write_method` for `set`.
70    pub fn with_write_method(mut self, write_method: HttpWriteMethod) -> Self {
71        self.write_method = write_method;
72        self
73    }
74
75    /// Returns options using `timeout_ms`.
76    pub fn with_timeout_ms(mut self, timeout_ms: u64) -> Self {
77        self.timeout_ms = timeout_ms;
78        self
79    }
80
81    /// Returns options using `max_body_bytes`.
82    pub fn with_max_body_bytes(mut self, max_body_bytes: usize) -> Self {
83        self.max_body_bytes = max_body_bytes;
84        self
85    }
86}
87
88pub(crate) fn validate_options(options: &HttpDirOptions) -> Result<()> {
89    if options.timeout_ms == 0 {
90        return Err(Error::Eval(
91            "table/http: timeout_ms must be non-zero".to_owned(),
92        ));
93    }
94    if options.base_url.trim().is_empty() {
95        return Err(Error::Eval("table/http: base_url is empty".to_owned()));
96    }
97    let _ = sim_lib_net_core::parse_url(options.base_url.trim())
98        .map_err(|err| Error::Eval(format!("table/http: {err}")))?;
99    Ok(())
100}
101
102pub(crate) fn normalize_options(mut options: HttpDirOptions) -> HttpDirOptions {
103    options.base_url = options.base_url.trim().trim_end_matches('/').to_owned();
104    options
105}
106
107impl TryFrom<crate::HttpDirDescriptor> for HttpDirOptions {
108    type Error = Error;
109
110    fn try_from(value: crate::HttpDirDescriptor) -> Result<Self> {
111        Ok(Self {
112            base_url: value.base_url,
113            codec: value.codec,
114            write_method: HttpWriteMethod::from_str(&value.write_method)?,
115            timeout_ms: value.timeout_ms,
116            max_body_bytes: value.max_body_bytes,
117        })
118    }
119}