fix: stabilize runtime follow-up regressions
This commit is contained in:
@@ -10,11 +10,19 @@ dtree_map
|
||||
dtree_omit
|
||||
dtree_pick
|
||||
dtree_values
|
||||
each
|
||||
get_by_path
|
||||
has
|
||||
is_array
|
||||
is_list
|
||||
set_status
|
||||
String
|
||||
StringList
|
||||
StringMap
|
||||
to_bool
|
||||
to_f64
|
||||
to_json
|
||||
to_s64
|
||||
to_string
|
||||
to_stringmap
|
||||
to_u64
|
||||
|
||||
@@ -42,11 +42,18 @@ You will encounter `DTree` throughout the runtime, especially in:
|
||||
- `.key("key")` returns a child pointer when it already exists.
|
||||
- `.get_or_create("key")` returns a child pointer and creates it when missing.
|
||||
- `.get_by_path("a/b/c")` traverses nested children without creating missing keys.
|
||||
- `.to_string()` reads scalar content as text.
|
||||
- `.to_s64()`, `.to_u64()`, and `.to_f64()` perform best-effort numeric conversion.
|
||||
- `.to_bool()` performs best-effort boolean conversion.
|
||||
- `.to_string(default)` reads scalar content as text.
|
||||
- `.to_s64(default)`, `.to_u64(default)`, and `.to_f64(default)` perform best-effort numeric conversion.
|
||||
- `.to_bool(default)` performs best-effort boolean conversion.
|
||||
- `.to_stringmap()` converts a map-shaped tree into `StringMap`.
|
||||
|
||||
All read accessors are `const` and never modify the tree; they work directly on `const DTree&` values such as `each()` callback parameters. Every `to_*` conversion takes an optional default that is returned when the value is missing or cannot be converted — see the individual pages (`to_string`, `to_s64`, `to_u64`, `to_f64`, `to_bool`) for the exact rules:
|
||||
|
||||
```cpp
|
||||
String title = context.props["title"].to_string("Untitled");
|
||||
s64 page_size = context.cfg.get_by_path("app/page_size").to_s64(25);
|
||||
```
|
||||
|
||||
`operator[]` creates missing entries, just like `std::map`. `.has()` and `.key()` are the non-mutating lookup helpers, and `.get_by_path()` is the non-creating traversal helper.
|
||||
|
||||
`json_decode()` currently stores JSON numbers as string-valued `DTree` nodes, so typed numeric conversion is the normal way to consume those values.
|
||||
@@ -60,7 +67,7 @@ References are dereferenced automatically in most normal reads.
|
||||
- `bool` values convert numerically to `1` and `0`.
|
||||
- Pointer values convert numerically when read as numbers.
|
||||
- Single-value maps can act as scalar wrappers for numeric and boolean conversion.
|
||||
- Invalid numeric input falls back to `0`.
|
||||
- Missing values and invalid numeric input fall back to the accessor's `default_value` argument (`0`, `""`, or `false` when not given).
|
||||
- `.to_stringmap()` converts map children key-by-key using each child's `to_string()`.
|
||||
|
||||
## Writing Values
|
||||
@@ -93,11 +100,11 @@ Useful inspection helpers include:
|
||||
|
||||
## each()
|
||||
|
||||
`each(std::function<void (DTree t, String key)> f)` iterates over the current tree value.
|
||||
`each(std::function<void (const DTree& t, String key)> f)` iterates over the current tree value (see the `each` page for details).
|
||||
|
||||
For map-shaped `DTree` values, the callback runs once per child entry and receives:
|
||||
|
||||
- `t` as the child value
|
||||
- `t` as the child value, by const reference (no copy)
|
||||
- `key` as the child key
|
||||
|
||||
For non-map values, `each()` still invokes the callback once:
|
||||
@@ -106,7 +113,7 @@ For non-map values, `each()` still invokes the callback once:
|
||||
- `key` is an empty string
|
||||
|
||||
```cpp
|
||||
context.connection["items"].each([&](DTree item, String key) {
|
||||
context.connection["items"].each([&](const DTree& item, String key) {
|
||||
print(key, ": ", item.to_string(), "\n");
|
||||
});
|
||||
```
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
:sig
|
||||
void DTree::each(function<void (const DTree& item, String key)> f) const
|
||||
|
||||
:params
|
||||
f : callback invoked per child with the child node and its key
|
||||
return value : none
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
dtree_map
|
||||
dtree_filter
|
||||
dtree_keys
|
||||
is_list
|
||||
|
||||
:content
|
||||
Iterates a `DTree`. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
For map-shaped nodes the callback runs once per child, in key order, receiving the child and its key. For scalar nodes it runs exactly once with the node itself and an empty key.
|
||||
|
||||
The callback receives the child as `const DTree&` — no copy is made, and all read accessors (`to_string()`, `to_s64()`, `get_by_path()`, ...) work on it directly:
|
||||
|
||||
```cpp
|
||||
rows.each([&](const DTree& row, String key) {
|
||||
out("<li>", html_escape(row.get_by_path("title").to_string("Untitled")), "</li>");
|
||||
});
|
||||
```
|
||||
|
||||
Declaring the callback parameter as plain `DTree` also works but deep-copies every child; prefer `const DTree&`. To transform or filter into a new tree, use `dtree_map()` / `dtree_filter()` instead of mutating inside the callback.
|
||||
|
||||
List-shaped trees (see `is_list`) iterate in numeric index order, matching `json_encode()` and the other serializers. Keyed maps iterate in string key order.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `foreach ($tree as $key => $item)`
|
||||
- JavaScript: `Object.entries(obj).forEach(([key, item]) => ...)`
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
DTree DTree::get_by_path(String path, String delim = "/")
|
||||
DTree DTree::get_by_path(String path, String delim = "/") const
|
||||
|
||||
:params
|
||||
path : slash-delimited path to traverse
|
||||
@@ -11,16 +11,21 @@ return value : the resolved child node, or an empty `DTree` when the path cannot
|
||||
0_Request
|
||||
>types
|
||||
json_decode
|
||||
has
|
||||
to_string
|
||||
|
||||
:content
|
||||
Traverses a nested `DTree` without creating missing keys.
|
||||
Traverses a nested `DTree` without creating missing keys. This is a read accessor: it is `const` and never modifies the tree, unlike `operator[]`, which creates missing entries.
|
||||
|
||||
Empty path segments are ignored, so leading and trailing `/` characters are harmless. If any intermediate node is not a map or a segment is missing, `get_by_path()` returns an empty `DTree`.
|
||||
|
||||
A missing path therefore reads like an empty value — combine it with the `to_*` default arguments to express a fallback in one call:
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
context.cfg.get_by_path("theme/options/portal-dark/label").to_string()
|
||||
String label = context.cfg.get_by_path("theme/options/portal-dark/label").to_string("Portal Dark");
|
||||
s64 page_size = context.cfg.get_by_path("app/page_size").to_s64(25);
|
||||
```
|
||||
|
||||
## Related Concepts
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
:sig
|
||||
bool DTree::has(String s) const
|
||||
|
||||
:params
|
||||
s : child key to test
|
||||
return value : true when the node is map-shaped and contains the key
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
get_by_path
|
||||
each
|
||||
is_array
|
||||
|
||||
:content
|
||||
Tests whether a map-shaped `DTree` contains a child key, without creating it. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
Returns `false` for scalar nodes and for missing keys.
|
||||
|
||||
This matters because `operator[]` creates missing entries, exactly like `std::map`. Use `has()` (or `get_by_path()`) when you only want to look:
|
||||
|
||||
```cpp
|
||||
if(context.props.has("avatar"))
|
||||
print(component("components/basic/avatar", context.props["avatar"], context));
|
||||
```
|
||||
|
||||
`has()` only checks one level. For nested checks, combine with `get_by_path()`:
|
||||
|
||||
```cpp
|
||||
bool configured = context.cfg.get_by_path("mail/smtp").is_array();
|
||||
```
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `array_key_exists()` / `isset()`
|
||||
- JavaScript: `Object.hasOwn(obj, key)` / `key in obj`
|
||||
@@ -0,0 +1,34 @@
|
||||
:sig
|
||||
bool DTree::is_array() const
|
||||
|
||||
:params
|
||||
return value : true when the node is map-shaped
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
is_list
|
||||
has
|
||||
each
|
||||
|
||||
:content
|
||||
Tests whether a `DTree` node is map-shaped, i.e. holds nested child values rather than a scalar. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
`is_array()` is true for both list-like and keyed containers; use `is_list()` to distinguish the two:
|
||||
|
||||
```cpp
|
||||
DTree rows = sqlite_query(db, "select * from notes");
|
||||
if(rows.is_array())
|
||||
{
|
||||
rows.each([&](const DTree& row, String key) {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Scalar values (strings, numbers, booleans, pointers) return `false`, as do unresolvable references.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `is_array()`
|
||||
- JavaScript: `typeof value === "object"`
|
||||
@@ -0,0 +1,35 @@
|
||||
:sig
|
||||
bool DTree::is_list() const
|
||||
|
||||
:params
|
||||
return value : true when the node is a sequential, numerically indexed container
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
is_array
|
||||
push
|
||||
each
|
||||
dtree_values
|
||||
|
||||
:content
|
||||
Tests whether a map-shaped `DTree` represents a list: its keys are the numeric strings `"0"`, `"1"`, `"2"`, ... in unbroken sequence. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
Containers built with `push()` are lists. An empty container counts as a list when it was created with `set_array()` or `push()`; a keyed map (or a map with gaps in its numeric keys) is `is_array()` but not `is_list()`.
|
||||
|
||||
```cpp
|
||||
DTree items;
|
||||
items.push(first_item);
|
||||
items.push(second_item);
|
||||
// items.is_list() == true
|
||||
|
||||
items["custom"] = "x";
|
||||
// items.is_list() == false, items.is_array() == true
|
||||
```
|
||||
|
||||
`dtree_map()` and `dtree_filter()` use this distinction to decide whether results re-index from zero or keep their original keys.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `array_is_list()`
|
||||
- JavaScript: `Array.isArray()`
|
||||
@@ -1,5 +1,9 @@
|
||||
:sig
|
||||
bool DTree::to_bool()
|
||||
bool DTree::to_bool(bool default_value = false) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or empty
|
||||
return value : the value as a boolean, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
@@ -7,12 +11,27 @@ bool DTree::to_bool()
|
||||
json_decode
|
||||
to_f64
|
||||
to_u64
|
||||
to_string
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a boolean.
|
||||
Reads a `DTree` value as a boolean. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values such as `true`, `yes`, `on`, and `1` read as true. Values such as `false`, `no`, `off`, `0`, and `null` read as false.
|
||||
String values such as `true`, `yes`, `on`, and `1` read as true. Values such as `false`, `no`, `off`, `0`, and `null` read as false. Numeric values read as true when non-zero.
|
||||
|
||||
Numeric values read as true when non-zero. Empty strings read as false.
|
||||
`default_value` is returned when the node is unset, holds an empty string, or is an unresolvable reference. Note the truthiness rule: a non-empty string that parses as neither a boolean word nor a number still reads as `true` — only missing/empty values fall back to the default.
|
||||
|
||||
A map-shaped node with exactly one entry unwraps to that entry's value; other maps read as true when non-empty.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
bool log_requests = context.cfg.get_by_path("app/log_requests").to_bool(true);
|
||||
bool wants_compact = context.props["compact"].to_bool();
|
||||
```
|
||||
|
||||
Use this when consuming request data, JSON-decoded values, config trees, or component props where the original input may be string-shaped.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `filter_var($v, FILTER_VALIDATE_BOOLEAN)`
|
||||
- JavaScript: `Boolean(value)` plus string handling
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
:sig
|
||||
f64 DTree::to_f64()
|
||||
f64 DTree::to_f64(f64 default_value = 0) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or cannot be parsed as a number
|
||||
return value : the value as a floating-point number, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
@@ -7,11 +11,32 @@ f64 DTree::to_f64()
|
||||
json_decode
|
||||
float_val
|
||||
to_bool
|
||||
to_s64
|
||||
to_u64
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a floating-point number.
|
||||
Reads a `DTree` value as a floating-point number. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values are parsed using the same permissive conversion rules used by the runtime's scalar helpers. Boolean values become `1.0` or `0.0`.
|
||||
String values are trimmed and parsed permissively: numeric forms and the boolean words understood by `to_bool()` both convert. Boolean values become `1.0` or `0.0`.
|
||||
|
||||
`default_value` is returned when:
|
||||
|
||||
- the node is unset or holds an empty string
|
||||
- the string does not parse as a finite number
|
||||
- the node is map-shaped with more than one entry, or an unresolvable reference
|
||||
|
||||
A map-shaped node with exactly one entry unwraps to that entry's value before converting.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
f64 ratio = context.props["ratio"].to_f64(1.0);
|
||||
f64 threshold = context.cfg.get_by_path("alerts/threshold").to_f64(0.75);
|
||||
```
|
||||
|
||||
Use this for numeric config values, JSON-decoded fields, component props, and request data that should be treated as a number.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `floatval()` with a fallback
|
||||
- JavaScript: `parseFloat(x) || fallback`
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
:sig
|
||||
String DTree::to_json(char quote_char = '"') const
|
||||
|
||||
:params
|
||||
quote_char : quote character used around string output
|
||||
return value : the scalar as a single JSON token
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
json_encode
|
||||
json_decode
|
||||
to_string
|
||||
|
||||
:content
|
||||
Renders a single scalar `DTree` value as a JSON token. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
Strings are escaped and quoted, numbers render as numeric literals, and booleans render as `true` / `false`.
|
||||
|
||||
This is **not** a serializer for nested data: map-shaped nodes render as the placeholder string `"(array)"`. Use `json_encode()` to serialize a whole tree.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
String token = context.props["label"].to_json(); // "\"Don't panic\""
|
||||
String body = json_encode(context.props); // full tree serialization
|
||||
```
|
||||
|
||||
The `quote_char` parameter exists for embedding output inside single-quoted contexts; for HTML attributes prefer double quotes plus `html_escape()`.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- JavaScript: `JSON.stringify(value)` for a single scalar
|
||||
- PHP: `json_encode($scalar)`
|
||||
@@ -0,0 +1,41 @@
|
||||
:sig
|
||||
s64 DTree::to_s64(s64 default_value = 0) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or cannot be parsed as a number
|
||||
return value : the value as a signed 64-bit integer, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
to_u64
|
||||
to_f64
|
||||
to_bool
|
||||
to_string
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as a signed integer. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values are trimmed and parsed permissively: plain integers, floating-point forms (truncated toward zero), and the boolean words understood by `to_bool()` (`yes` reads as `1`) all convert. Results outside the `s64` range clamp to the range boundaries. Boolean values become `1` or `0`.
|
||||
|
||||
`default_value` is returned when:
|
||||
|
||||
- the node is unset or holds an empty string
|
||||
- the string does not parse as a number (`"not-a-number"`)
|
||||
- the node is map-shaped with more than one entry, or an unresolvable reference
|
||||
|
||||
A map-shaped node with exactly one entry unwraps to that entry's value before converting — this matches how single-value rows from query results read.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
s64 page = context.get["page"].to_s64(1);
|
||||
s64 limit = context.cfg.get_by_path("app/page_size").to_s64(25);
|
||||
```
|
||||
|
||||
`json_decode()` stores JSON numbers as string-valued nodes, so this is the normal way to consume decoded numeric fields.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `intval()` with a fallback
|
||||
- JavaScript: `parseInt(x) || fallback`
|
||||
@@ -0,0 +1,40 @@
|
||||
:sig
|
||||
String DTree::to_string(String default_value = "") const
|
||||
|
||||
:params
|
||||
default_value : returned when the node holds no usable text
|
||||
return value : the scalar content as text, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
to_s64
|
||||
to_f64
|
||||
to_bool
|
||||
to_json
|
||||
get_by_path
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as text. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
`default_value` is returned when the value is missing or not text-convertible:
|
||||
|
||||
- the node is unset or holds an empty string (a missing key read via `get_by_path()` looks exactly like this)
|
||||
- the node is map-shaped (use `json_encode()` or `to_stringmap()` for those)
|
||||
- the node is an unresolvable reference
|
||||
|
||||
Other scalar kinds convert instead of falling back: `f64` values format with six decimal places (`std::to_string`), `bool` values become `(true)` / `(false)`, and pointers format as their numeric address.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
String title = context.props["title"].to_string("Untitled");
|
||||
String theme = context.cfg.get_by_path("theme/key").to_string("light");
|
||||
```
|
||||
|
||||
The default argument replaces the older `first(x.to_string(), "fallback")` idiom.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `$value ?? 'fallback'` / `strval()`
|
||||
- JavaScript: `String(value || "fallback")`
|
||||
@@ -0,0 +1,36 @@
|
||||
:sig
|
||||
StringMap DTree::to_stringmap() const
|
||||
|
||||
:params
|
||||
return value : a flat `StringMap` projection of the node
|
||||
|
||||
:see
|
||||
>types
|
||||
0_DTree
|
||||
StringMap
|
||||
to_string
|
||||
dtree_keys
|
||||
dtree_values
|
||||
|
||||
:content
|
||||
Converts a `DTree` into a flat `StringMap`. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
- Map-shaped nodes produce one entry per child, with each child read via `to_string()`. Nested maps flatten to empty strings — this is a one-level projection, not a serializer.
|
||||
- A non-empty scalar produces a single `"value"` entry holding the scalar.
|
||||
- Empty values and unresolvable references produce an empty map.
|
||||
|
||||
Use this when handing request- or config-shaped data to APIs that take `StringMap`, such as `sqlite_query()` / `mysql_query()` parameter maps or `encode_query()`.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
StringMap params = context.props["filters"].to_stringmap();
|
||||
DTree rows = sqlite_query(db, "select * from notes where author = :author", params);
|
||||
```
|
||||
|
||||
For a faithful representation of nested data, use `json_encode()` instead.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: casting a one-level array with `array_map('strval', $a)`
|
||||
- JavaScript: `Object.fromEntries(Object.entries(o).map(([k, v]) => [k, String(v)]))`
|
||||
@@ -1,5 +1,9 @@
|
||||
:sig
|
||||
u64 DTree::to_u64()
|
||||
u64 DTree::to_u64(u64 default_value = 0) const
|
||||
|
||||
:params
|
||||
default_value : returned when the value is missing or cannot be parsed as a number
|
||||
return value : the value as an unsigned 64-bit integer, or `default_value`
|
||||
|
||||
:see
|
||||
>types
|
||||
@@ -7,11 +11,32 @@ u64 DTree::to_u64()
|
||||
json_decode
|
||||
int_val
|
||||
to_bool
|
||||
to_s64
|
||||
to_f64
|
||||
|
||||
:content
|
||||
Reads a `DTree` value as an unsigned integer.
|
||||
Reads a `DTree` value as an unsigned integer. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
|
||||
|
||||
String values are parsed numerically. Boolean values become `1` or `0`. Negative values clamp to `0`.
|
||||
String values are trimmed and parsed permissively, like `to_s64()`. Boolean values become `1` or `0`. Negative values clamp to `0`, and results above the `u64` range clamp to the maximum.
|
||||
|
||||
`default_value` is returned when:
|
||||
|
||||
- the node is unset or holds an empty string
|
||||
- the string does not parse as a number
|
||||
- the node is map-shaped with more than one entry, or an unresolvable reference
|
||||
|
||||
A map-shaped node with exactly one entry unwraps to that entry's value before converting. Note that a *present* negative value clamps to `0` rather than returning the default — the default signals "missing or unparseable", not "out of range".
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
u64 limit = context.get["limit"].to_u64(50);
|
||||
u64 owner_id = row.get_by_path("owner/id").to_u64();
|
||||
```
|
||||
|
||||
Use this for counts, identifiers, limits, timestamps, and other non-negative numeric values stored inside a `DTree`.
|
||||
|
||||
## Related Concepts
|
||||
|
||||
- PHP: `intval()` with a fallback
|
||||
- JavaScript: `parseInt(x) || fallback` (within unsigned range)
|
||||
|
||||
@@ -26,7 +26,7 @@ void app_asset_tags(Request& context, String kind, DTree assets)
|
||||
app_asset_tag_once(context, kind, scalar);
|
||||
return;
|
||||
}
|
||||
assets.each([&](DTree item, String key) {
|
||||
assets.each([&](const DTree& item, String key) {
|
||||
app_asset_tag_once(context, kind, item.to_string());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@ COMPONENT(Request& context)
|
||||
<script src="<?= app_asset_url("js/morphdom.js", context) ?>"></script>
|
||||
<script src="<?= app_asset_url("js/site.js", context) ?>"></script>
|
||||
<?: context.call["fragments"]["head"].to_string() ?>
|
||||
<?: context.call["fragments"]["once"].to_string() ?>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ RENDER(Request& context)
|
||||
StringMap http_headers = split_http_headers("GET /demo.uce?x=1 HTTP/1.1\r\nHost: example.test\r\nX-Empty:\r\n");
|
||||
StringMap leading_crlf_headers = split_http_headers("\r\nGET /lead.uce HTTP/1.1\r\nHost: lead.example\r\n");
|
||||
StringMap header_only = split_http_headers("Host: example.test\nX-Token: abc\n");
|
||||
StringMap colon_uri_headers = split_http_headers("GET /clock.uce?t=12:30 HTTP/1.1\r\nHost: colon.example\r\n");
|
||||
check("split_http_headers() request line with colon in URI", colon_uri_headers["REQUEST_METHOD"] == "GET" && colon_uri_headers["DOCUMENT_URI"] == "/clock.uce" && colon_uri_headers["QUERY_STRING"] == "t=12:30" && colon_uri_headers["HTTP_HOST"] == "colon.example", var_dump(colon_uri_headers));
|
||||
check("trim() / split_kv() / split_http_headers()", trim(" padded value ") == "padded value" && kv["alpha"] == "one" && kv["empty"] == "" && http_headers["REQUEST_METHOD"] == "GET" && http_headers["DOCUMENT_URI"] == "/demo.uce" && http_headers["QUERY_STRING"] == "x=1" && http_headers["HTTP_X_EMPTY"] == "" && leading_crlf_headers["REQUEST_METHOD"] == "GET" && leading_crlf_headers["DOCUMENT_URI"] == "/lead.uce" && leading_crlf_headers["HTTP_HOST"] == "lead.example" && header_only["REQUEST_METHOD"] == "" && header_only["HTTP_HOST"] == "example.test" && header_only["HTTP_X_TOKEN"] == "abc", trim(" padded value ") + " / " + var_dump(kv) + " / " + var_dump(http_headers) + " / " + var_dump(leading_crlf_headers) + " / " + var_dump(header_only));
|
||||
check("replace()", replace("alpha-beta-beta", "beta", "done") == "alpha-done-done", replace("alpha-beta-beta", "beta", "done"));
|
||||
check("html_escape() attribute-safe quotes", html_escape("<&>\"Don't") == "<&>"Don't", html_escape("<&>\"Don't"));
|
||||
@@ -84,6 +86,29 @@ RENDER(Request& context)
|
||||
DTree nav_dash_public = dtree_omit(nav_dash, {"section"});
|
||||
check("DTree collection helpers", empty_pop.to_string() == "" && app_nav.is_list() && nav_titles["0"].to_string() == "Dashboard" && grouped_nav["app"]["1"]["title"].to_string() == "Themes" && join(dtree_keys(nav_dash_summary), ",") == "title" && dtree_values(nav_dash_public)["0"].to_string() == "Dashboard", json_encode(grouped_nav));
|
||||
|
||||
DTree conv;
|
||||
conv["name"] = "ada";
|
||||
conv["count"] = "12";
|
||||
conv["junk"] = "not-a-number";
|
||||
conv["flag"] = "off";
|
||||
check("DTree to_* defaults", conv.get_by_path("missing/key").to_string("fallback") == "fallback" && conv["name"].to_string("fallback") == "ada" && conv["junk"].to_s64(-7) == -7 && conv["count"].to_s64(-7) == 12 && conv["junk"].to_f64(2.5) == 2.5 && conv["junk"].to_u64(9) == 9 && conv.get_by_path("nope").to_bool(true) && !conv["flag"].to_bool(true), json_encode(conv));
|
||||
|
||||
const DTree& conv_read = conv;
|
||||
String const_each_keys = "";
|
||||
conv_read.each([&](const DTree& item, String key) { const_each_keys += key + ":" + item.to_string("-") + " "; });
|
||||
check("DTree const read accessors", conv_read.has("name") && conv_read.get_by_path("name").to_string() == "ada" && conv_read.is_array() && !conv_read.is_list() && conv_read.to_stringmap()["count"] == "12" && conv_read.get_type_name() == "array" && contains(const_each_keys, "count:12"), const_each_keys);
|
||||
|
||||
DTree ordered;
|
||||
for(u32 i = 0; i < 12; i++)
|
||||
{
|
||||
DTree ordered_entry;
|
||||
ordered_entry = "v" + std::to_string(i);
|
||||
ordered.push(ordered_entry);
|
||||
}
|
||||
String ordered_keys = "";
|
||||
ordered.each([&](const DTree& item, String key) { ordered_keys += key + ","; });
|
||||
check("DTree list iteration is numeric", ordered_keys == "0,1,2,3,4,5,6,7,8,9,10,11," && dtree_values(ordered)["10"].to_string() == "v10" && dtree_map(ordered, [](const DTree& item, String key) { return(item); })["11"].to_string() == "v11", ordered_keys);
|
||||
|
||||
String binary_payload = "core";
|
||||
binary_payload.push_back((char)0x00);
|
||||
binary_payload.push_back((char)0xff);
|
||||
|
||||
Reference in New Issue
Block a user