Chandra

 view release on metacpan or  search on metacpan

xs/hotreload.xs  view on Meta::CPAN

MODULE = Chandra    PACKAGE = Chandra::HotReload

PROTOTYPES: DISABLE

SV *
new(class, ...)
    const char *class
CODE:
{
    HV *self_hv = newHV();
    I32 i;
    double interval = 1.0;

    /* Parse %args from stack */
    for (i = 1; i + 1 < items; i += 2) {
        const char *key = SvPV_nolen(ST(i));
        SV *val = ST(i + 1);
        if (strEQ(key, "interval")) {
            interval = SvNV(val);
        }
    }

    (void)hv_stores(self_hv, "watches", newRV_noinc((SV *)newAV()));
    (void)hv_stores(self_hv, "interval", newSVnv(interval));
    (void)hv_stores(self_hv, "_last_check", newSVnv(0.0));

    RETVAL = sv_bless(newRV_noinc((SV *)self_hv), gv_stashpv(class, GV_ADD));
}
OUTPUT:
    RETVAL

SV *
watch(self, path_sv, callback)
    SV *self
    SV *path_sv
    SV *callback
CODE:
{
    HV *hv = (HV *)SvRV(self);
    const char *path;
    AV *watches_av;
    SV **watches_svp;
    HV *entry_hv;
    HV *files_hv;

    /* Validate path */
    if (!SvOK(path_sv)) {
        croak("watch() requires a path");
    }
    path = SvPV_nolen(path_sv);

    /* Validate callback */
    if (!SvOK(callback) || SvROK(callback) == 0 || SvTYPE(SvRV(callback)) != SVt_PVCV) {
        croak("watch() requires a callback");
    }

    /* Check path exists */
    {
        Stat_t st;
        if (PerlLIO_stat(path, &st) != 0) {
            croak("watch() path does not exist: %s", path);
        }
    }

    /* Scan files via Perl helper */
    {
        dSP;
        int count;
        ENTER; SAVETMPS;
        PUSHMARK(SP);
        XPUSHs(self);
        XPUSHs(path_sv);
        PUTBACK;
        count = call_method("_scan_files", G_SCALAR);
        SPAGAIN;
        files_hv = (HV *)SvRV(POPs);
        SvREFCNT_inc_simple_void((SV *)files_hv);
        PUTBACK;
        FREETMPS; LEAVE;
    }

    /* Build watch entry hash */
    entry_hv = newHV();
    (void)hv_stores(entry_hv, "path", newSVsv(path_sv));
    (void)hv_stores(entry_hv, "callback", SvREFCNT_inc(callback));
    (void)hv_stores(entry_hv, "files", newRV_noinc((SV *)files_hv));

    /* Push onto watches array */
    watches_svp = hv_fetchs(hv, "watches", 0);
    watches_av = (AV *)SvRV(*watches_svp);
    av_push(watches_av, newRV_noinc((SV *)entry_hv));

    RETVAL = SvREFCNT_inc(self);
}
OUTPUT:
    RETVAL

int
poll(self)
    SV *self
CODE:
{
    HV *hv = (HV *)SvRV(self);
    SV **interval_svp, **last_check_svp, **watches_svp;
    AV *watches_av;
    double now, interval, last_check;
    I32 wi, wlen;
    int total_changed = 0;

    now = (double)time(NULL);

    interval_svp = hv_fetchs(hv, "interval", 0);
    interval = SvNV(*interval_svp);

    last_check_svp = hv_fetchs(hv, "_last_check", 0);
    last_check = SvNV(*last_check_svp);

    if ((now - last_check) < interval) {
        RETVAL = 0;
        goto done;

xs/hotreload.xs  view on Meta::CPAN

    (void)hv_stores(hv, "watches", newRV_noinc((SV *)newAV()));
    RETVAL = SvREFCNT_inc(self);
}
OUTPUT:
    RETVAL

void
watched_paths(self)
    SV *self
PPCODE:
{
    HV *hv = (HV *)SvRV(self);
    SV **watches_svp = hv_fetchs(hv, "watches", 0);
    AV *watches_av = (AV *)SvRV(*watches_svp);
    I32 i, len = av_len(watches_av) + 1;

    if (GIMME_V == G_SCALAR) {
        EXTEND(SP, 1);
        PUSHs(sv_2mortal(newSViv(len)));
    } else {
        EXTEND(SP, len);
        for (i = 0; i < len; i++) {
            SV **entry_svp = av_fetch(watches_av, i, 0);
            HV *entry_hv = (HV *)SvRV(*entry_svp);
            SV **path_svp = hv_fetchs(entry_hv, "path", 0);
            PUSHs(sv_2mortal(newSVsv(*path_svp)));
        }
    }
}

SV *
interval(self, ...)
    SV *self
CODE:
{
    HV *hv = (HV *)SvRV(self);
    SV **svp;

    if (items > 1) {
        (void)hv_stores(hv, "interval", newSVnv(SvNV(ST(1))));
    }

    svp = hv_fetchs(hv, "interval", 0);
    RETVAL = newSVnv(SvNV(*svp));
}
OUTPUT:
    RETVAL

SV *
_scan_files(self, path_sv)
    SV *self
    SV *path_sv
CODE:
{
    const char *path = SvPV_nolen(path_sv);
    HV *files_hv = newHV();
    Stat_t st;

    PERL_UNUSED_VAR(self);

    if (PerlLIO_stat(path, &st) == 0) {
        if (S_ISREG(st.st_mode)) {
            /* Single file */
            STRLEN plen = SvCUR(path_sv);
            (void)hv_store(files_hv, path, (I32)plen, newSVnv((NV)st.st_mtime), 0);
        } else if (S_ISDIR(st.st_mode)) {
            /* Recursive C directory walk - no File::Find / eval_pv */
            _hotreload_scan_recursive(aTHX_ path, files_hv);
        }
    }

    RETVAL = newRV_noinc((SV *)files_hv);
}
OUTPUT:
    RETVAL



( run in 3.183 seconds using v1.01-cache-2.11-cpan-364913b4093 )