Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support htv:methodName #3

Merged
merged 9 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/thing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ async fn main() {
.href("/say_hello/{action_id}")
.http_get(|| async { "Checking ..." })
.op(wot_td::thing::FormOperation::QueryAction)
})
.form(|b| {
b.ext(())
.href("/say_hello/{action_id}")
.http_delete(|| async { "Canceling ..." })
.op(wot_td::thing::FormOperation::CancelAction)
})
Expand Down
91 changes: 83 additions & 8 deletions src/servient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,56 @@ impl Servient<Nil> {
///
/// By default it sets the CORS headers to allow any origin, you may disable the behaviour
/// by calling [ServientSettings::http_disable_permissive_cors].
///
/// # Examples
///
/// This should fail:
/// ```compile_fail
/// # use wot_serve::{Servient, servient::{BuildServient,HttpRouter}};
/// # use wot_td::thing::FormOperation;
/// let servient = Servient::builder("test")
/// .finish_extend()
/// .form(|f| {
/// f.href("/ref")
/// .http_get(|| async { "Hello, World!" })
/// .op(FormOperation::ReadAllProperties)
/// .op(FormOperation::WriteAllProperties)
/// .into()
/// })
/// .build_servient()
/// .unwrap();
/// ```
///
/// This should fail as well:
/// ```compile_fail
/// # use wot_serve::{Servient, servient::{BuildServient,HttpRouter}};
/// # use wot_td::thing::FormOperation;
/// let servient = Servient::builder("test")
/// .finish_extend()
/// .form(|f| {
/// f.href("/ref")
/// .http_get(|| async { "Hello, World!" })
/// .http_put(|| async { "Hello, World!" })
/// .into()
/// })
/// .build_servient()
/// .unwrap();
/// ```
///
/// This should work instead.
/// ```
/// # use wot_serve::{Servient, servient::{BuildServient,HttpRouter}};
/// # use wot_td::thing::FormOperation;
/// let servient = Servient::builder("test")
/// .finish_extend()
/// .form(|f| {
/// f.href("/ref")
/// .http_get(|| async { "Hello, World!" })
/// .op(FormOperation::ReadAllProperties)
/// })
/// .build_servient()
/// .unwrap();
/// ```
pub fn builder(title: impl Into<String>) -> ThingBuilder<NilPlus<ServientExtension>, ToExtend> {
ThingBuilder::<NilPlus<ServientExtension>, ToExtend>::new(title)
}
Expand Down Expand Up @@ -86,18 +136,38 @@ mod test {

use super::*;

#[derive(serde::Serialize)]
struct E {}

impl ExtendableThing for E {
type Form = ();
type DataSchema = ();
type ArraySchema = ();
type ObjectSchema = ();
type EventAffordance = ();
type ActionAffordance = ();
type ExpectedResponse = ();
type PropertyAffordance = ();
type InteractionAffordance = ();
}

#[test]
fn build_servient() {
let servient = Servient::builder("test")
.ext(E {})
.finish_extend()
.security(|b| b.basic())
.form(|f| {
f.href("/ref")
f.ext(())
.href("/ref")
.http_get(|| async { "Hello, World!" })
.op(FormOperation::ReadAllProperties)
})
.form(|f| {
f.href("/ref2")
.http_get(|| async { "Hello, World! 2" })
.ext(())
.security("basic")
.op(FormOperation::ReadAllProperties)
})
.build_servient()
Expand All @@ -111,13 +181,18 @@ mod test {
let servient = Servient::builder("test")
.finish_extend()
.property("hello", |b| {
b.finish_extend_data_schema().null().form(|f| {
f.href("/hello")
.http_get(|| async { "Reading Hello, World!" })
.http_put(|| async { "Writing Hello, World!" })
.op(FormOperation::ReadProperty)
.op(FormOperation::WriteProperty)
})
b.finish_extend_data_schema()
.null()
.form(|f| {
f.href("/hello")
.http_get(|| async { "Reading Hello, World!" })
.op(FormOperation::ReadProperty)
})
.form(|f| {
f.href("/hello")
.http_put(|| async { "Writing Hello, World!" })
.op(FormOperation::WriteProperty)
})
})
.build_servient()
.unwrap();
Expand Down
139 changes: 120 additions & 19 deletions src/servient/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ use datta::{Operator, UriTemplate};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use wot_td::{
builder::{FormBuilder, ThingBuilder},
extend::ExtendableThing,
builder::{AdditionalExpectedResponseBuilder, FormBuilder, ThingBuilder},
extend::{Extend, ExtendableThing},
protocol::http,
thing::FormOperation,
};

#[doc(hidden)]
Expand Down Expand Up @@ -49,12 +51,9 @@ impl Default for ServientExtension {
pub struct Form {
#[serde(skip)]
method_router: MethodRouter,
}

impl From<MethodRouter> for Form {
fn from(method_router: MethodRouter) -> Self {
Self { method_router }
}
#[serde(flatten)]
htv: http::Form,
}

impl ExtendableThing for ServientExtension {
Expand All @@ -63,7 +62,7 @@ impl ExtendableThing for ServientExtension {
type ActionAffordance = ();
type EventAffordance = ();
type Form = Form;
type ExpectedResponse = ();
type ExpectedResponse = http::Response;
type DataSchema = ();
type ObjectSchema = ();
type ArraySchema = ();
Expand Down Expand Up @@ -274,12 +273,104 @@ pub trait HttpRouter {
T: 'static;
}

pub struct ServientFormBuilder<Other: ExtendableThing, Href, OtherForm, const HAS_OP: bool>(
FormBuilder<Other, Href, OtherForm>,
);

impl<Other: ExtendableThing, OtherForm, const HAS_OP: bool>
ServientFormBuilder<Other, (), OtherForm, HAS_OP>
{
/// Create a new builder with the specified Href
///
/// See [FormBuilder::href].
#[inline]
pub fn href(
self,
value: impl Into<String>,
) -> ServientFormBuilder<Other, String, OtherForm, HAS_OP> {
ServientFormBuilder(self.0.href(value))
}
}

impl<Other: ExtendableThing, Href, OtherForm, const HAS_OP: bool>
ServientFormBuilder<Other, Href, OtherForm, HAS_OP>
{
/// Set the security definitions that must be satisfied to access the resource
///
/// See [FormBuilder::security].
#[inline]
pub fn security(self, value: impl Into<String>) -> Self {
Self(self.0.security(value))
}

/// Set the authorization scope identifiers
///
/// See [FormBuilder::scope]
#[inline]
pub fn scope(self, value: impl Into<String>) -> Self {
Self(self.0.scope(value))
}

/// Adds an additional response to the form builder.
///
/// See [FormBuilder::additional_response]
#[inline]
pub fn additional_response<F>(self, f: F) -> Self
where
F: FnOnce(&mut AdditionalExpectedResponseBuilder) -> &mut AdditionalExpectedResponseBuilder,
{
Self(self.0.additional_response(f))
}

/// Extends the form, passing a closure that returns `T`.
///
/// See [FormBuilder::ext_with]
#[inline]
pub fn ext_with<F, T>(self, f: F) -> ServientFormBuilder<Other, Href, OtherForm::Target, HAS_OP>
where
OtherForm: Extend<T>,
F: FnOnce() -> T,
{
ServientFormBuilder(self.0.ext_with(f))
}

/// Extends the form with an additional element.
///
/// See [FormBuilder::ext].
#[inline]
pub fn ext<T>(self, t: T) -> ServientFormBuilder<Other, Href, OtherForm::Target, HAS_OP>
where
OtherForm: Extend<T>,
{
ServientFormBuilder(self.0.ext(t))
}
}

impl<Other: ExtendableThing, Href, OtherForm> ServientFormBuilder<Other, Href, OtherForm, false> {
/// Set the form intended operation
///
/// See [FormBuilder::op].
pub fn op(self, new_op: FormOperation) -> ServientFormBuilder<Other, Href, OtherForm, true> {
ServientFormBuilder(self.0.op(new_op))
}
}

impl<Other: ExtendableThing, Href, OtherForm, const HAS_OP: bool>
From<ServientFormBuilder<Other, Href, OtherForm, HAS_OP>>
for FormBuilder<Other, Href, OtherForm>
{
#[inline]
fn from(value: ServientFormBuilder<Other, Href, OtherForm, HAS_OP>) -> Self {
value.0
}
}

impl<Other, Href, OtherForm> HttpRouter for FormBuilder<Other, Href, OtherForm>
where
Other: ExtendableThing + Holder<ServientExtension>,
OtherForm: Holder<Form>,
{
type Target = FormBuilder<Other, Href, OtherForm>;
type Target = ServientFormBuilder<Other, Href, OtherForm, false>;

/// Route GET requests to the given handler.
fn http_get<H, T>(mut self, handler: H) -> Self::Target
Expand All @@ -288,8 +379,10 @@ where
T: 'static,
{
let method_router = std::mem::take(&mut self.other.field_mut().method_router);
self.other.field_mut().method_router = method_router.get(handler);
self
let f = self.other.field_mut();
f.method_router = method_router.get(handler);
f.htv.method_name = Some(http::Method::Get);
ServientFormBuilder(self)
}
/// Route PUT requests to the given handler.
fn http_put<H, T>(mut self, handler: H) -> Self::Target
Expand All @@ -298,8 +391,10 @@ where
T: 'static,
{
let method_router = std::mem::take(&mut self.other.field_mut().method_router);
self.other.field_mut().method_router = method_router.put(handler);
self
let f = self.other.field_mut();
f.method_router = method_router.put(handler);
f.htv.method_name = Some(http::Method::Put);
ServientFormBuilder(self)
}
/// Route POST requests to the given handler.
fn http_post<H, T>(mut self, handler: H) -> Self::Target
Expand All @@ -308,8 +403,10 @@ where
T: 'static,
{
let method_router = std::mem::take(&mut self.other.field_mut().method_router);
self.other.field_mut().method_router = method_router.post(handler);
self
let f = self.other.field_mut();
f.method_router = method_router.post(handler);
f.htv.method_name = Some(http::Method::Post);
ServientFormBuilder(self)
}
/// Route PATCH requests to the given handler.
fn http_patch<H, T>(mut self, handler: H) -> Self::Target
Expand All @@ -318,8 +415,10 @@ where
T: 'static,
{
let method_router = std::mem::take(&mut self.other.field_mut().method_router);
self.other.field_mut().method_router = method_router.patch(handler);
self
let f: &mut Form = self.other.field_mut();
f.method_router = method_router.patch(handler);
f.htv.method_name = Some(http::Method::Patch);
ServientFormBuilder(self)
}
/// Route DELETE requests to the given handler.
fn http_delete<H, T>(mut self, handler: H) -> Self::Target
Expand All @@ -328,8 +427,10 @@ where
T: 'static,
{
let method_router = std::mem::take(&mut self.other.field_mut().method_router);
self.other.field_mut().method_router = method_router.delete(handler);
self
let f: &mut Form = self.other.field_mut();
f.method_router = method_router.delete(handler);
f.htv.method_name = Some(http::Method::Delete);
ServientFormBuilder(self)
}
}

Expand Down