sifis-home / wot-td

Rust crate to produce and consume Web Of Things Thing Descriptions
13 stars 2 forks source link

Implement op_once for form builder #105

Open dodomorandi opened 1 year ago

dodomorandi commented 1 year ago

@lu-zero let me know if you'd rather tweak the struct names, i.e. a better one for FormBuilderInner.

codecov[bot] commented 1 year ago

Codecov Report

Merging #105 (9ac8bf8) into master (18e0a14) will decrease coverage by 0.19%. The diff coverage is 87.60%.

@@            Coverage Diff             @@
##           master     #105      +/-   ##
==========================================
- Coverage   96.95%   96.77%   -0.19%     
==========================================
  Files          11       11              
  Lines       12670    12884     +214     
==========================================
+ Hits        12284    12468     +184     
- Misses        386      416      +30     
Impacted Files Coverage Δ
src/builder.rs 97.31% <87.17%> (-0.57%) :arrow_down:
src/builder/affordance.rs 96.55% <100.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

lu-zero commented 1 year ago

I changed it to use 3 states:

Potentially we could do away the outer FromBuilder and have a from() that collapses ANY/MANY to NONE once we save it back in the ThingBuilder.

dodomorandi commented 1 year ago

I looked at your changes, and after thinking a bit I still have the impression that it is more convoluted that it should be. This corresponds to your implementation:

flowchart LR
    thing((Thing)) -->|form| any[CAN_ADD_ANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|op_once| none[CAN_ADD_NO_OPS]
    many[CAN_ADD_MANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS]  -->|from| Unlocked[FormBuilder::Unlocked]
    many[CAN_ADD_MANY_OPS] -->|from| Unlocked[FormBuilder::Unlocked]
    none[CAN_ADD_NO_OPS] -->|from| Locked[FormBuilder::Locked]
    Unlocked[FormBuilder::Unlocked] --> Form
    Locked[FormBuilder::Locked] --> Form

The only practical difference between this implementation and the previous one is that you cannot call op_once after op. But this does not make any sense from a developer perspective:

Given that this approach is just to fix some compile-time constraints elsewhere and that these constraints can be easily broken just deserializing a JSON representation (because you are not enforcing anything at runtime), I still think that this approach is more complex than it should be.

lu-zero commented 1 year ago

As you may see in wot-serve branch I needed to bind the http{get,post,patch} to the ANY state (no ops present) and we can do away the FormBuilder enum by having From implemented for a final state (either NONE or a FINAL one).

flowchart LR
    thing((Thing)) -->|form| start[CAN_HREF]
    start[CAN_HREF] -->|href| any[CAN_ADD_ANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|op_once| none[CAN_ADD_NO_OPS]
    many[CAN_ADD_MANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS]  -->|from| final[FINAL]
    many[CAN_ADD_MANY_OPS] -->|from| final[FINAL]
    none[CAN_ADD_NO_OPS] -->|from| final[FINAL]

For the wot-serve use-case I might even consider adding a CAN_ADD_ONCE state that let you call op only once.

dodomorandi commented 1 year ago

Sorry, but I still don't get it. In your branch the only constraint you are creating is that the returned form does not have any operation set. This could be just implemented without op_once, tracking the fact that an operation has been added using a const bool. Now it feel even more convoluted than before...

lu-zero commented 1 year ago

Indeed we can potentially not have an op_once at all since we could use the state to have

flowchart LR
    thing((Thing)) -->|form| start[CAN_HREF]
    start[CAN_HREF] -->|href| any[CAN_ADD_ANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|from| once[CAN_ADD_ONE_OPS]
    once[CAN_ADD_ONE_OPS] -->|op| final[CAN_ADD_NO_OPS]
    many[CAN_ADD_MANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS]  -->|from| final[CAN_ADD_NO_OPS]
    many[CAN_ADD_MANY_OPS] -->|from| final[CAN_ADD_NO_OPS]
    once[CAN_ADD_ONE_OPS] -->|from| final[CAN_ADD_NO_OPS]

And have From instead to restrict from ANY to ONCE and only have fn op implemented for each intermediate state.

dodomorandi commented 1 year ago

....which, again, brings to the fact that your last state is just the final FormBuilder, therefore your 3-state collapses into a boolean. Right?

lu-zero commented 1 year ago

Using more than a boolean let us avoid the FormBuilderInner, I started poking at it for that reason and then I realized it can be leverage to express more while I tried to use it in wot-serve.

With the last commit I can correctly restrict op when using http_{get,put,...} so you can set it only once.

dodomorandi commented 1 year ago

Sorry, I unable to follow you anymore. From your previous message and the relative flowchart it was pretty clear it was possible to remove one state, you just added one instead. Feel free to go along with the idea you have in mind, it is pretty difficult to me at this point to write an implementation that makes sense in your head but not in mine.

lu-zero commented 1 year ago

Here what I meant.

I managed to reduce the states compared to the diagram above, I'm afraid we cannot collapse everything further in a single binary state, but we do not have FormBuilderInner anymore :)

flowchart LR
    thing((Thing)) -->|form| start[CAN_ADD_ANY_OPS+NO_HREF]
    start[CAN_ADD_ANY_OPS+NO_HREF] -->|href| any[CAN_ADD_ANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS] -->|from| once[CAN_ADD_ONE_OPS]
    once[CAN_ADD_ONE_OPS] -->|op| final[CAN_ADD_NO_OPS]
    many[CAN_ADD_MANY_OPS] -->|op| many[CAN_ADD_MANY_OPS]
    any[CAN_ADD_ANY_OPS]  -->|from| final[CAN_ADD_NO_OPS]
    many[CAN_ADD_MANY_OPS] -->|from| final[CAN_ADD_NO_OPS]
    once[CAN_ADD_ONE_OPS] -->|from| final[CAN_ADD_NO_OPS]

(message delayed by a series of meeting)

lu-zero commented 1 year ago

We ended up adding the constraints directly in wot-serve.

In WoT 2.0 we might have either this constraint applied or not necessary anymore depending on what we'll have specified for the relationship between op and protocol binding.