Today I'm going to implement HTTP request & response mocks for testing purposes. But first, some minor config tweaks...
This architecture allows me to impl Top for any HTTP implementation, and also allows me to use a custom mock for running the test suite, or for iocaine runtime decide.
One complicating factor is that different methods may return different types. Most of them return a String currently, but for example .header() would return an Option<String>.
This means that functions like Dom::method() and Dom::path() are essentially the same, and differ only in the type they tx.send(), and differ from .header() in the type they return. I wonder if I can use some generics trickery to remove the boilerplate.
Hah! I CAN!
impl Dom {
fn communicate<T, F>(&self, req: F) -> T
where
F: FnOnce(oneshot::Sender<T>) -> DomRequest,
{
let (resp_tx, resp_rx) = oneshot::channel();
let tx = self.channel.clone();
tx.send(req(resp_tx)).ok().unwrap();
tokio::task::block_in_place(move || resp_rx.blocking_recv().unwrap())
}
pub fn method(&self) -> String {
self.communicate(|responder| DomRequest::Method { responder })
}
}
Sweet.
Yes, this will panic when communicating on the channel fails. I think that's more suitable behaviour than trying to handle such a catastrophic situation on the scripting side.
And yes, I will change the .unwrap() to .expect() with a reasonable message.
Neat, neat, I can run a test suite with mock HTTP requests. No cookies or query strings yet, but those will be coming soon, too.
❯ cargo run -q -- runtime decide -m HEAD -p /foo -H host=example.com
DEBUG: HEAD /foo example.com
default
Whee!
Soon, soon, I shall start re-implementing Nam-Shub of Enki. Well, not that soon, because I need matchers and configuration support first. Or maybe I can get away without both?
I mean, to implement the Three Ifs In A Trenchcoat... I don't necessarily need any of that.
Yay, I have the skeleton code for dealing with HTTP requests! The way I'm approaching this, to remain mostly HTTP implementation-agnostic, is that there's gonna be two main types exposed to the iocaine runtime:
RequestandResponse. On the Rust side, these are a simple struct that wrap anmpscchannel, and provide a new function:Where
Topis a trait that requires implementing a few functions like.method(&self) -> impl Into<String>,path(&self) -> impl Into<String>, and so on. Those implementations send a request over the wrappedmpscchannel, along with aoneshot::Senderto respond on, then wait for the response, and return that.