stache/ruby/
runtime.rs

1pub const RUNTIME: &'static str = r#"
2#include "ruby.h"
3#include <stdbool.h>
4#include <string.h>
5
6static const char *DOT = ".";
7
8static ID id_escape_html;
9static ID id_key_p;
10static ID id_to_s;
11
12static VALUE cCGI;
13
14static VALUE fetch(VALUE context, VALUE key) {
15    if (RSTRING_LEN(key) == 1 && strncmp(StringValuePtr(key), DOT, 1) == 0) {
16        return context;
17    }
18
19    switch (rb_type(context)) {
20        case T_HASH:
21            if (RTEST(rb_funcall(context, id_key_p, 1, key))) {
22                return rb_hash_aref(context, key);
23            } else {
24                return Qundef;
25            }
26        case T_STRUCT:
27            // TODO Check rb_struct_members to avoid name error.
28            return rb_struct_aref(context, key);
29        case T_OBJECT: {
30            ID method = rb_to_id(key);
31            if (rb_respond_to(context, method) && rb_obj_method_arity(context, method) == 0) {
32                return rb_funcall(context, method, 0);
33            } else {
34                return Qundef;
35            }
36        }
37        default:
38            return Qundef;
39    }
40}
41
42static VALUE context_fetch(VALUE stack, VALUE key) {
43    for (long i = RARRAY_LEN(stack) - 1; i >= 0; i--) {
44        VALUE context = rb_ary_entry(stack, i);
45        VALUE value = fetch(context, key);
46        if (value != Qundef) {
47            return value;
48        }
49    }
50    return Qnil;
51}
52
53static VALUE fetch_path(VALUE stack, VALUE path) {
54    VALUE value = context_fetch(stack, rb_ary_entry(path, 0));
55    if (value == Qnil) {
56        return Qnil;
57    }
58    for (long i = 1; i < RARRAY_LEN(path); i++) {
59        value = fetch(value, rb_ary_entry(path, i));
60        if (value == Qundef) {
61            return Qnil;
62        }
63    }
64    return value;
65}
66
67static void append_value(VALUE buf, VALUE stack, VALUE path, bool escape) {
68    VALUE value = fetch_path(stack, path);
69    switch (rb_type(value)) {
70        case T_NIL:
71        case T_UNDEF:
72            return;
73        case T_STRING:
74            break;
75        default:
76            value = rb_funcall(value, id_to_s, 0);
77            break;
78    }
79    rb_str_buf_append(buf, escape ? rb_funcall(cCGI, id_escape_html, 1, value) : value);
80}
81
82static void section(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
83    VALUE value = fetch_path(stack, path);
84    switch (rb_type(value)) {
85        case T_ARRAY:
86            for (long i = 0; i < RARRAY_LEN(value); i++) {
87                rb_ary_push(stack, rb_ary_entry(value, i));
88                block(buf, stack);
89                rb_ary_pop(stack);
90            }
91            break;
92        case T_HASH:
93        case T_OBJECT:
94        case T_STRUCT:
95            rb_ary_push(stack, value);
96            block(buf, stack);
97            rb_ary_pop(stack);
98            break;
99        case T_NIL:
100        case T_UNDEF:
101        case T_FALSE:
102            break;
103        default:
104            block(buf, stack);
105            break;
106    }
107}
108
109static void inverted(VALUE buf, VALUE stack, VALUE path, void (*block)(VALUE, VALUE)) {
110    VALUE value = fetch_path(stack, path);
111    switch (rb_type(value)) {
112        case T_ARRAY:
113            if (RARRAY_LEN(value) == 0) {
114                block(buf, stack);
115            }
116            break;
117        case T_NIL:
118        case T_UNDEF:
119        case T_FALSE:
120            block(buf, stack);
121            break;
122    }
123}
124"#;