about summary refs log tree commit diff
path: root/smalldog
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2025-03-27 05:11:46 -0500
committergennyble <gen@nyble.dev>2025-03-27 05:11:46 -0500
commitb79b84ce06ee34b5957d7f19aa19ebeff2af1df9 (patch)
tree5e725e3b8da40493fbb681e56cf14fdb54bac57d /smalldog
parent362bc937cee716ae0af77eb467756e420f9324a5 (diff)
downloadcorgi-b79b84ce06ee34b5957d7f19aa19ebeff2af1df9.tar.gz
corgi-b79b84ce06ee34b5957d7f19aa19ebeff2af1df9.zip
add smalldog as the module ffi crate
Diffstat (limited to 'smalldog')
-rw-r--r--smalldog/Cargo.toml6
-rw-r--r--smalldog/src/lib.rs156
2 files changed, 162 insertions, 0 deletions
diff --git a/smalldog/Cargo.toml b/smalldog/Cargo.toml
new file mode 100644
index 0000000..6ae5cc4
--- /dev/null
+++ b/smalldog/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "smalldog"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
diff --git a/smalldog/src/lib.rs b/smalldog/src/lib.rs
new file mode 100644
index 0000000..e1ada11
--- /dev/null
+++ b/smalldog/src/lib.rs
@@ -0,0 +1,156 @@
+use core::ffi;
+use std::{
+	borrow::Cow,
+	ffi::{CStr, CString},
+	ptr,
+	str::FromStr,
+	sync::Mutex,
+};
+
+#[repr(C)]
+pub struct ModuleRequest<'req> {
+	pub headers_len: ffi::c_ulong,
+	pub headers: &'req [[*const ffi::c_char; 2]],
+	pub body_len: ffi::c_ulong,
+	pub body: *const u8,
+}
+
+pub struct Request<'req> {
+	headers: Vec<(Cow<'req, str>, Cow<'req, str>)>,
+	body: Option<&'req [u8]>,
+}
+
+impl<'req> Request<'req> {
+	pub fn from_mod_request(request: *const ModuleRequest) -> Self {
+		let reqref = unsafe { request.as_ref() }.unwrap();
+
+		let mut headers = vec![];
+		for idx in 0..reqref.headers_len as usize {
+			let kvarr = reqref.headers[idx as usize];
+			let k = unsafe { CStr::from_ptr(kvarr[0]) }.to_string_lossy();
+			let v = unsafe { CStr::from_ptr(kvarr[1]) }.to_string_lossy();
+			headers.push((k, v));
+		}
+
+		let body = if reqref.body.is_null() {
+			None
+		} else {
+			Some(unsafe { std::slice::from_raw_parts(reqref.body, reqref.body_len as usize) })
+		};
+
+		Self { headers, body }
+	}
+
+	pub fn header(&self, key: &str) -> Option<&str> {
+		for (hkey, hval) in &self.headers {
+			if hkey == key {
+				return Some(hval);
+			}
+		}
+
+		None
+	}
+}
+
+#[repr(C)]
+pub struct ModuleResponse {
+	pub status: ffi::c_ushort,
+	pub headers_len: ffi::c_ulong,
+	pub headers: &'static [[*const ffi::c_char; 2]],
+	pub body_len: ffi::c_ulong,
+	pub body: *const u8,
+}
+
+pub static mut HEADERS: [[*const ffi::c_char; 2]; 64] = [[ptr::null(), ptr::null()]; 64];
+static RESPONSE: Mutex<Option<Response>> = Mutex::new(None);
+
+#[derive(Clone, Debug, PartialEq)]
+enum ResponseBody {
+	Building(String),
+	Built(CString),
+}
+
+impl ResponseBody {
+	pub fn get_built(&mut self) -> &CString {
+		match self {
+			ResponseBody::Building(body) => {
+				*self = ResponseBody::Built(CString::from_str(&body).unwrap());
+
+				if let ResponseBody::Built(bdy) = self {
+					bdy
+				} else {
+					unreachable!()
+				}
+			}
+			ResponseBody::Built(bdy) => bdy,
+		}
+	}
+}
+
+pub struct Response {
+	headers: Vec<(CString, CString)>,
+	body: ResponseBody,
+}
+
+impl Response {
+	pub fn new() -> Self {
+		Self {
+			headers: vec![],
+			body: ResponseBody::Building(String::new()),
+		}
+	}
+
+	pub fn into_mod_response(self, status: u16) -> *const ModuleResponse {
+		let mut lock = RESPONSE.lock().unwrap();
+		*lock = Some(self);
+		let this = lock.as_mut().unwrap();
+
+		for (idx, (key, value)) in this.headers.iter().enumerate() {
+			unsafe {
+				HEADERS[idx][0] = key.as_ptr();
+				HEADERS[idx][1] = value.as_ptr();
+			}
+		}
+
+		let (body_len, body) = {
+			let built = this.body.get_built();
+			(built.as_bytes().len() as u64, built.as_ptr() as *const u8)
+		};
+
+		let headers_len = this.headers.len() as u64;
+		let boxed = Box::new(ModuleResponse {
+			status,
+			headers_len,
+			headers: unsafe { &HEADERS[..headers_len as usize] },
+			body_len,
+			body,
+		});
+
+		Box::<ModuleResponse>::into_raw(boxed)
+	}
+
+	pub fn header<K: Into<CString>, V: Into<CString>>(&mut self, key: K, value: V) -> &mut Self {
+		self.headers.push((key.into(), value.into()));
+		self
+	}
+
+	pub fn push_str(&mut self, s: &str) {
+		match self.body {
+			ResponseBody::Building(ref mut body) => {
+				body.push_str(s);
+			}
+			_ => (),
+		}
+	}
+
+	pub fn cleanup(response: *const ModuleResponse) {
+		let mut lock = RESPONSE.lock().unwrap();
+		match lock.take() {
+			Some(response) => drop(response),
+			None => (),
+		}
+
+		let boxed = unsafe { Box::from_raw(response as *mut ModuleResponse) };
+		drop(boxed);
+	}
+}