std1.0.0-beta-7 SafeC docs

Source wasm/hydrate.sc

View in the repository

// SafeC Standard Library — WASM client hydration implementation (see
// hydrate.h).
#pragma once
#include <std/wasm/hydrate.h>
#include <std/wasm/dom.h>
#include <std/wasm/wasm_rt.h>
#include <std/reactive/signal.h>
#include <std/mem.h>

namespace std {

struct HydrateTextBinding {
    &Signal<int> signal;
    int          elementHandle;
};

static void __hydrate_text_int_listener(?&HydrateTextBinding bq) {
    match (bq) {
        case null: return;
        case some(b): {
            char buf[24];
            unsafe {
                wasm_itoa((long long)b.signal.get(), buf[..]);
                dom_set_text(b.elementHandle, buf);
            }
        }
    }
}

void hydrate_bind_text_int_handle(&Signal<int> signal, int elementHandle) {
    ?&HydrateTextBinding bq = null;
    unsafe { bq = (?&HydrateTextBinding)alloc_raw_(sizeof(struct HydrateTextBinding)); }
    match (bq) {
        case null: return;
        case some(b): {
            b.signal = signal;
            b.elementHandle = elementHandle;
        }
    }
    signal_subscribe(signal, __hydrate_text_int_listener, bq);
}

// One record type for every String/int binding below; 'mode' picks what
// it writes.
#define HYD_TEXT_    0
#define HYD_ATTR_    1
#define HYD_CLASS_   2
#define HYD_VISIBLE_ 3
#define HYD_VALUE_   4
#define HYD_CHECKED_ 5

struct HydrateStrBinding {
    &Signal<String> signal;
    int             handle;
    int             mode;
    struct String   name;     // attribute name
    int             writing;  // set while the binding itself stores into the signal
};

struct HydrateIntBinding {
    &Signal<int>  signal;
    int           handle;
    int           mode;
    struct String name;       // class name
    int           writing;
};

static void hydrate_str_apply_(?&HydrateStrBinding bq) {
    match (bq) {
        case null: { }
        case some(b): {
            if (b.writing) { return; }
            &String v = b.signal.get_ref();
            if (b.mode == HYD_TEXT_) { dom_set_text(b.handle, v.as_ptr()); }
            else if (b.mode == HYD_ATTR_) { dom_set_attr(b.handle, b.name.as_ptr(), v.as_ptr()); }
            else if (b.mode == HYD_VALUE_) { dom_set_value(b.handle, v.as_ptr()); }
        }
    }
}

static void hydrate_int_apply_(?&HydrateIntBinding bq) {
    match (bq) {
        case null: { }
        case some(b): {
            if (b.writing) { return; }
            int v = b.signal.get();
            if (b.mode == HYD_CLASS_) {
                if (v != 0) { dom_class_add(b.handle, b.name.as_ptr()); }
                else { dom_class_remove(b.handle, b.name.as_ptr()); }
            } else if (b.mode == HYD_VISIBLE_) {
                dom_set_visible(b.handle, v != 0);
            } else if (b.mode == HYD_CHECKED_) {
                dom_set_checked(b.handle, v != 0);
            }
        }
    }
}

static ?&HydrateStrBinding hydrate_str_new_(&Signal<String> signal, int handle, int mode, const &char name) {
    ?&HydrateStrBinding bq = null;
    unsafe { bq = (?&HydrateStrBinding)alloc_raw_(sizeof(struct HydrateStrBinding)); }
    match (bq) {
        case null: { }
        case some(b): {
            b.signal = signal;
            b.handle = handle;
            b.mode = mode;
            b.name = String::from(name);
            b.writing = 0;
        }
    }
    hydrate_str_apply_(bq);
    signal_subscribe(signal, hydrate_str_apply_, bq);
    return bq;
}

static ?&HydrateIntBinding hydrate_int_new_(&Signal<int> signal, int handle, int mode, const &char name) {
    ?&HydrateIntBinding bq = null;
    unsafe { bq = (?&HydrateIntBinding)alloc_raw_(sizeof(struct HydrateIntBinding)); }
    match (bq) {
        case null: { }
        case some(b): {
            b.signal = signal;
            b.handle = handle;
            b.mode = mode;
            b.name = String::from(name);
            b.writing = 0;
        }
    }
    hydrate_int_apply_(bq);
    signal_subscribe(signal, hydrate_int_apply_, bq);
    return bq;
}

void hydrate_bind_text(&Signal<String> signal, int elementHandle) {
    hydrate_str_new_(signal, elementHandle, HYD_TEXT_, "");
}

void hydrate_bind_attr(&Signal<String> signal, int elementHandle, const &char attr) {
    hydrate_str_new_(signal, elementHandle, HYD_ATTR_, attr);
}

void hydrate_bind_class(&Signal<int> signal, int elementHandle, const &char cls) {
    hydrate_int_new_(signal, elementHandle, HYD_CLASS_, cls);
}

void hydrate_bind_visible(&Signal<int> signal, int elementHandle) {
    hydrate_int_new_(signal, elementHandle, HYD_VISIBLE_, "");
}

static void hydrate_on_input_(?&HydrateStrBinding bq, &DomEvent ev) {
    match (bq) {
        case null: { }
        case some(b): {
            struct String v = ev.target_value();
            &String cur = b.signal.get_ref();
            if (!cur.eq(&v)) {
                b.writing = 1;
                cur.free();
                *cur = v;
                b.signal.version = b.signal.version + 1UL;
                b.signal.notify();
                b.writing = 0;
            } else {
                v.free();
            }
        }
    }
}

int hydrate_bind_value(&Signal<String> signal, int elementHandle) {
    ?&HydrateStrBinding bq = hydrate_str_new_(signal, elementHandle, HYD_VALUE_, "");
    return dom_on(elementHandle, "input", hydrate_on_input_, bq);
}

static void hydrate_on_change_(?&HydrateIntBinding bq, &DomEvent ev) {
    match (bq) {
        case null: { }
        case some(b): {
            int v = ev.target_checked();
            if (b.signal.get() != v) {
                b.writing = 1;
                b.signal.set(v);
                b.writing = 0;
            }
        }
    }
}

int hydrate_bind_checked(&Signal<int> signal, int elementHandle) {
    ?&HydrateIntBinding bq = hydrate_int_new_(signal, elementHandle, HYD_CHECKED_, "");
    return dom_on(elementHandle, "change", hydrate_on_change_, bq);
}

int hydrate_bind_text_int(&Signal<int> signal, const &char selector) {
    int handle = dom_get_element(selector);
    if (handle < 0) {
        return -1;
    }
    hydrate_bind_text_int_handle(signal, handle);
    return handle;
}

} // namespace std