Skip to content

Commit

Permalink
C API for streaming content mutations
Browse files Browse the repository at this point in the history
  • Loading branch information
kornelski committed Nov 6, 2024
1 parent 5c1fc4a commit 953e7de
Show file tree
Hide file tree
Showing 12 changed files with 645 additions and 220 deletions.
6 changes: 5 additions & 1 deletion c-api/c-tests/src/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ int run_tests() {
subtest("Element API", element_api_test);
subtest("Document end API", document_end_api_test);
subtest("Memory limiting", test_memory_limiting);
return done_testing();
int res = done_testing();
if (res) {
fprintf(stderr, "\nSome tests have failed\n");
}
return res;
}
112 changes: 112 additions & 0 deletions c-api/c-tests/src/test_element_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,117 @@ static void test_insert_content_around_element(lol_html_selector_t *selector, vo
);
}

//-------------------------------------------------------------------------
EXPECT_OUTPUT(
streaming_mutations_output_sink,
"&amp;before<div><!--prepend-->Hi<!--append--></div>&amp;after\xf0\x9f\x98\x82",
&EXPECTED_USER_DATA,
sizeof(EXPECTED_USER_DATA)
);

static void loltest_drop(void *user_data) {
int *drops = user_data;
(*drops)++;
}

static int loltest_write_all_callback_before(lol_html_streaming_sink_t *sink, void *user_data) {
int *counter = user_data;
ok(*counter >= 100 && *counter <= 103);

const char *before = "&before";
return lol_html_streaming_sink_write_str(sink, before, strlen(before), false);
}

static int loltest_write_all_callback_after(lol_html_streaming_sink_t *sink, void *user_data) {
int *counter = user_data;
ok(*counter >= 100 && *counter <= 103);

const char *after = "&after";
const char emoji[] = {0xf0,0x9f,0x98,0x82};
return lol_html_streaming_sink_write_str(sink, after, strlen(after), false) ||
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[0], 1, false) ||
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[1], 1, false) ||
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[2], 1, false) ||
lol_html_streaming_sink_write_utf8_chunk(sink, &emoji[3], 1, false);
}

static int loltest_write_all_callback_prepend(lol_html_streaming_sink_t *sink, void *user_data) {
int *counter = user_data;
ok(*counter >= 100 && *counter <= 103);

const char *prepend1 = "<!--pre";
const char *prepend2 = "pend-->";
return lol_html_streaming_sink_write_utf8_chunk(sink, prepend1, strlen(prepend1), true) ||
lol_html_streaming_sink_write_utf8_chunk(sink, prepend2, strlen(prepend2), true);
}

static int loltest_write_all_callback_append(lol_html_streaming_sink_t *sink, void *user_data) {
int *counter = user_data;
ok(*counter >= 100 && *counter <= 103);

const char *append = "<!--append-->";
return lol_html_streaming_sink_write_str(sink, append, strlen(append), true);
}

static lol_html_rewriter_directive_t streaming_mutations_around_element(
lol_html_element_t *element,
void *user_data
) {
note("Stream before/prepend");
ok(!lol_html_element_streaming_before(element, &(lol_html_streaming_handler_t){
.write_all_callback = loltest_write_all_callback_before,
.user_data = user_data,
.drop_callback = loltest_drop,
}));
ok(!lol_html_element_streaming_prepend(element, &(lol_html_streaming_handler_t){
.write_all_callback = loltest_write_all_callback_prepend,
.user_data = user_data,
// tests null drop callback
}));
note("Stream after/append");
ok(!lol_html_element_streaming_append(element, &(lol_html_streaming_handler_t){
.write_all_callback = loltest_write_all_callback_append,
.user_data = user_data,
.drop_callback = loltest_drop,
}));
ok(!lol_html_element_streaming_after(element, &(lol_html_streaming_handler_t){
.write_all_callback = loltest_write_all_callback_after,
.user_data = user_data,
.drop_callback = loltest_drop,
}));

return LOL_HTML_CONTINUE;
}

static void test_streaming_mutations_around_element(lol_html_selector_t *selector, void *user_data) {
UNUSED(user_data);
lol_html_rewriter_builder_t *builder = lol_html_rewriter_builder_new();

int drop_count = 100;

int err = lol_html_rewriter_builder_add_element_content_handlers(
builder,
selector,
&streaming_mutations_around_element,
&drop_count,
NULL,
NULL,
NULL,
NULL
);

ok(!err);

run_rewriter(
builder,
"<div>Hi</div>",
streaming_mutations_output_sink,
user_data
);

ok(drop_count == 103); // one has no drop callback on purpose
}

//-------------------------------------------------------------------------
EXPECT_OUTPUT(
set_element_inner_content_output_sink,
Expand Down Expand Up @@ -706,6 +817,7 @@ void element_api_test() {
test_iterate_attributes(selector, &user_data);
test_get_and_modify_attributes(selector, &user_data);
test_insert_content_around_element(selector, &user_data);
test_streaming_mutations_around_element(selector, &user_data);

lol_html_selector_free(selector);
}
Expand Down
16 changes: 16 additions & 0 deletions c-api/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# To generate a header:
#
# cargo expand > tmp.rs
# cbindgen tmp.rs

language = "C"
tab_width = 4
documentation = true
documentation_style = "c99"
documentation_length = "full"

[export]
prefix = "lol_html_"

[export.mangle]
rename_types = "SnakeCase"
Loading

0 comments on commit 953e7de

Please sign in to comment.