Skip to main content

pepl_stdlib/modules/
http.rs

1//! `http` capability module — HTTP request functions (host-delegated).
2//!
3//! Functions: get, post, put, patch, delete.
4//! All HTTP operations are host-delegated — the runtime host performs actual
5//! requests via `env.host_call(cap_id=1, fn_id, payload)`. This module
6//! validates arguments and returns `CapabilityCall` errors to signal the
7//! caller to route the call to the host.
8//!
9//! # Cap ID / Fn ID Mapping
10//!
11//! | fn_id | Function |
12//! |-------|----------|
13//! | 1     | get      |
14//! | 2     | post     |
15//! | 3     | put      |
16//! | 4     | patch    |
17//! | 5     | delete   |
18
19use crate::capability::{CAP_HTTP, HTTP_DELETE, HTTP_GET, HTTP_PATCH, HTTP_POST, HTTP_PUT};
20use crate::error::StdlibError;
21use crate::module::StdlibModule;
22use crate::value::Value;
23
24/// The `http` capability module.
25pub struct HttpModule;
26
27impl HttpModule {
28    pub fn new() -> Self {
29        Self
30    }
31}
32
33impl Default for HttpModule {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl StdlibModule for HttpModule {
40    fn name(&self) -> &'static str {
41        "http"
42    }
43
44    fn has_function(&self, function: &str) -> bool {
45        matches!(function, "get" | "post" | "put" | "patch" | "delete")
46    }
47
48    fn call(&self, function: &str, args: Vec<Value>) -> Result<Value, StdlibError> {
49        match function {
50            "get" => self.get(args),
51            "post" => self.post(args),
52            "put" => self.put(args),
53            "patch" => self.patch(args),
54            "delete" => self.delete(args),
55            _ => Err(StdlibError::unknown_function("http", function)),
56        }
57    }
58}
59
60impl HttpModule {
61    /// `http.get(url: string, options?: HttpOptions) -> Result<HttpResponse, HttpError>`
62    ///
63    /// Validates: 1 or 2 args, first must be string.
64    /// Returns `CapabilityCall` with cap_id=1, fn_id=1.
65    fn get(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
66        if args.is_empty() || args.len() > 2 {
67            return Err(StdlibError::wrong_args("http.get", 1, args.len()));
68        }
69        validate_string("http.get", &args[0], 1)?;
70        Err(StdlibError::capability_call(
71            "http", "get", CAP_HTTP, HTTP_GET, args,
72        ))
73    }
74
75    /// `http.post(url: string, body: string, options?: HttpOptions) -> Result<HttpResponse, HttpError>`
76    ///
77    /// Validates: 2 or 3 args, first two must be strings.
78    /// Returns `CapabilityCall` with cap_id=1, fn_id=2.
79    fn post(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
80        if args.len() < 2 || args.len() > 3 {
81            return Err(StdlibError::wrong_args("http.post", 2, args.len()));
82        }
83        validate_string("http.post", &args[0], 1)?;
84        validate_string("http.post", &args[1], 2)?;
85        Err(StdlibError::capability_call(
86            "http", "post", CAP_HTTP, HTTP_POST, args,
87        ))
88    }
89
90    /// `http.put(url: string, body: string, options?: HttpOptions) -> Result<HttpResponse, HttpError>`
91    ///
92    /// Validates: 2 or 3 args, first two must be strings.
93    /// Returns `CapabilityCall` with cap_id=1, fn_id=3.
94    fn put(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
95        if args.len() < 2 || args.len() > 3 {
96            return Err(StdlibError::wrong_args("http.put", 2, args.len()));
97        }
98        validate_string("http.put", &args[0], 1)?;
99        validate_string("http.put", &args[1], 2)?;
100        Err(StdlibError::capability_call(
101            "http", "put", CAP_HTTP, HTTP_PUT, args,
102        ))
103    }
104
105    /// `http.patch(url: string, body: string, options?: HttpOptions) -> Result<HttpResponse, HttpError>`
106    ///
107    /// Validates: 2 or 3 args, first two must be strings.
108    /// Returns `CapabilityCall` with cap_id=1, fn_id=4.
109    fn patch(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
110        if args.len() < 2 || args.len() > 3 {
111            return Err(StdlibError::wrong_args("http.patch", 2, args.len()));
112        }
113        validate_string("http.patch", &args[0], 1)?;
114        validate_string("http.patch", &args[1], 2)?;
115        Err(StdlibError::capability_call(
116            "http", "patch", CAP_HTTP, HTTP_PATCH, args,
117        ))
118    }
119
120    /// `http.delete(url: string, options?: HttpOptions) -> Result<HttpResponse, HttpError>`
121    ///
122    /// Validates: 1 or 2 args, first must be string.
123    /// Returns `CapabilityCall` with cap_id=1, fn_id=5.
124    fn delete(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
125        if args.is_empty() || args.len() > 2 {
126            return Err(StdlibError::wrong_args("http.delete", 1, args.len()));
127        }
128        validate_string("http.delete", &args[0], 1)?;
129        Err(StdlibError::capability_call(
130            "http",
131            "delete",
132            CAP_HTTP,
133            HTTP_DELETE,
134            args,
135        ))
136    }
137}
138
139// ── Helpers ──────────────────────────────────────────────────────────────────
140
141fn validate_string(func: &str, val: &Value, pos: usize) -> Result<(), StdlibError> {
142    match val {
143        Value::String(_) => Ok(()),
144        _ => Err(StdlibError::type_mismatch(
145            func,
146            pos,
147            "string",
148            val.type_name(),
149        )),
150    }
151}