| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- using Invercargill;
- using Invercargill.DataStructures;
- using Invercargill.Mapping;
- namespace Statum {
- /**
- * Fluent builder for the directive list an entrypoint/action returns.
- *
- * Started via {@link StatumEntrypoint.directives}/{@link StatumAction.directives}
- * (bound to the handler's {@link StateService}). Each mutator returns the
- * builder, so directives chain:
- *
- * ```
- * return directives()
- * .set(slot) // fresh slot: sign_slot -> set
- * .update(key, state) // mutate + auto-push to channels + sign -> set
- * .clear(other_key)
- * .notify("info", "Saved")
- * .navigate("/");
- * ```
- *
- * `set`/`update` are intents recorded in declaration order; pure directives
- * are stored inline. {@link build} is `async` and materialises the intents in
- * order (`update` yields to push to subscribed channels; `set` calls
- * `sign_slot`), preserving ordering, then returns the directives.
- */
- public class DirectiveBuilder : Object {
- private StateService state_service;
- private Series<Item> items = new Series<Item>();
- internal DirectiveBuilder(StateService state_service) {
- this.state_service = state_service;
- }
- /** Signs the (fresh) slot and emits a `set` directive for its frame. */
- public DirectiveBuilder set(Slot slot) {
- items.add(new Item() { kind = ItemKind.SLOT, slot = slot });
- return this;
- }
- /** Emits a `set` directive for an already-signed frame. */
- public DirectiveBuilder set_frame(Model.FrameDto frame) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.SetDirectiveDto.with_frame(frame) });
- return this;
- }
- /** Mutates the cached slot (`update` = sign + auto-push to channels) and emits a `set`. */
- public DirectiveBuilder update(string key, State state) {
- items.add(new Item() { kind = ItemKind.UPDATE, key = key, update_state = state });
- return this;
- }
- /** Emits a `clear` directive. */
- public DirectiveBuilder clear(string key) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.ClearDirectiveDto.with_key(key) });
- return this;
- }
- /** Emits an `invalidate` directive for a frame `signature` the server could not trust. */
- public DirectiveBuilder invalidate(string signature) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.InvalidateDirectiveDto.with_signature(signature) });
- return this;
- }
- /** Emits a `subscribe` directive (realtime channel). */
- public DirectiveBuilder subscribe(string key) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.SubscribeDirectiveDto.with_key(key) });
- return this;
- }
- /** Emits an `unsubscribe` directive. */
- public DirectiveBuilder unsubscribe(string key) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.UnsubscribeDirectiveDto.with_key(key) });
- return this;
- }
- /** Emits a `transmit` directive. */
- public DirectiveBuilder transmit(string key, bool retry = false) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.TransmitDirectiveDto.with(key, retry) });
- return this;
- }
- /** Emits a `notify` directive. */
- public DirectiveBuilder notify(string kind, string message) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.NotifyDirectiveDto.with(kind, message) });
- return this;
- }
- /** Emits an `error` directive. */
- public DirectiveBuilder error(string message, string? code = null, Properties? data = null) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.ErrorDirectiveDto() {
- message = message, code = code, data = data
- }});
- return this;
- }
- /** Emits a terminal `post` directive (full-page form POST). */
- public DirectiveBuilder post(string uri, Properties data) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.PostDirectiveDto.with(uri, data) });
- return this;
- }
- /** Emits a terminal `navigate` directive. */
- public DirectiveBuilder navigate(string uri) {
- items.add(new Item() { kind = ItemKind.DIRECTIVE, directive = new Model.NavigateDirectiveDto.with_uri(uri) });
- return this;
- }
- /** Materialises intents and returns the directive lot, in declaration order. */
- public async Lot<Model.DirectiveDto> build() throws GLib.Error {
- var directives = new Series<Model.DirectiveDto>();
- foreach (var item in items) {
- switch (item.kind) {
- case ItemKind.DIRECTIVE:
- if (item.directive != null) {
- directives.add(item.directive);
- }
- break;
- case ItemKind.SLOT:
- if (item.slot != null) {
- var frame = state_service.sign_slot(((!)item.slot).id);
- if (frame != null) {
- directives.add(new Model.SetDirectiveDto.with_frame((!)frame));
- }
- }
- break;
- case ItemKind.UPDATE:
- var frame = yield state_service.update(item.key, item.update_state);
- directives.add(new Model.SetDirectiveDto.with_frame(frame));
- break;
- }
- }
- return directives;
- }
- /** Alias for {@link build}. */
- public async Lot<Model.DirectiveDto> to_lot() throws GLib.Error {
- return yield build();
- }
- private enum ItemKind {
- DIRECTIVE,
- SLOT,
- UPDATE
- }
- private class Item : Object {
- public ItemKind kind;
- public Model.DirectiveDto? directive;
- public Slot? slot;
- public State? update_state;
- public string key;
- }
- }
- }
|