from std/path/z/functions import STANDARD_FUNCTIONS as Z_STANDARD_FUNCTIONS;
from std/path/zz/functions import Func, STANDARD_FUNCTIONS;
from std/path/zz import ZZPath;
from test/more import *;
ok( typeof Func eq "Class", "Func class is importable" );
ok( typeof STANDARD_FUNCTIONS eq "Array", "STANDARD_FUNCTIONS is importable" );
is( STANDARD_FUNCTIONS.length(), 59, "ZZPath standard function table is limited" );
function find_z ( spelling ) {
return Z_STANDARD_FUNCTIONS.first( fn f → f.has_name(spelling) );
}
function find_zz ( spelling ) {
return STANDARD_FUNCTIONS.first( fn f → f.has_name(spelling) );
}
for ( let spelling in [
"true",
"false",
"null",
"tag",
"url",
"local-name",
"key",
"value",
"index",
"count",
"is-first",
"is-last",
"next",
"prev",
] ) {
let z_func := find_z(spelling);
let zz_func := find_zz(spelling);
ok( zz_func ≢ null, `${spelling}() is included in ZZPath` );
ok( zz_func ≡ z_func, `${spelling}() reuses the ZPath definition` );
}
for ( let spelling in [
"abs",
"floor",
"ceil",
"round",
"int",
"sqrt",
"uc",
"lc",
"length",
"not",
"typeof",
"defined",
"empty",
"string",
"number",
"boolean",
"regexp",
"sum",
"min",
"max",
"clamp",
"substr",
"index-of",
"rindex",
"last-index-of",
"contains",
"chr",
"ord",
"replace",
"search",
"starts_with",
"ends_with",
"matches",
"pattern_to_regexp",
"quotemeta",
"sprint",
"split",
"join",
"trim",
"pad",
"chomp",
"title",
"snake",
"kebab",
"camel",
] ) {
let zz_func := find_zz(spelling);
ok( zz_func ≢ null, `${spelling}() is included in ZZPath` );
}
is(
find_zz("last-index-of"){f},
find_zz("rindex"){f},
"last-index-of() reuses the rindex() implementation callback",
);
for ( let spelling in [
"type",
"union",
"intersection",
"upper-case",
"lower-case",
"match",
] ) {
is( find_zz(spelling), null, `${spelling}() is not included yet` );
}
let data := {
neg: -4,
float: 2.6,
root: 16,
zero: 0,
word: "Alpha",
values: [ 1, 2, 3 ],
empty_arr: [],
empty_dict: {},
empty_set: << >>,
users: [
{ name: "Ada" },
{ name: "Bob" },
{ name: "Cara" },
],
meta: {
code: "x",
},
};
is(
( new ZZPath( path: "count(/users/*)" ) ).first(data),
3,
"count() works through ZZPath",
);
is(
( new ZZPath( path: "/meta/code[key() eq \"code\"]" ) ).first(data),
"x",
"key() works in ZZPath filters",
);
is(
( new ZZPath( path: "/users/*[index() = 1]/name" ) ).first(data),
"Bob",
"index() works in ZZPath filters",
);
is(
( new ZZPath( path: "/users/*[is-first()]/name" ) ).first(data),
"Ada",
"is-first() works in ZZPath filters",
);
is(
( new ZZPath( path: "/users/*[is-last()]/name" ) ).first(data),
"Cara",
"is-last() works in ZZPath filters",
);
is(
( new ZZPath( path: "value(/meta/code)" ) ).first(data),
"x",
"value() works through ZZPath",
);
is(
( new ZZPath( path: "/users/#0/next()/name" ) ).first(data),
"Bob",
"next() returns following sibling nodes",
);
is(
( new ZZPath( path: "/users/#2/prev()/name" ) ).first(data),
"Bob",
"prev() returns previous sibling nodes",
);
is(
( new ZZPath( path: "type(/meta/code)" ) ).first( data, "fallback" ),
"fallback",
"unsupported ZPath functions are not inherited by ZZPath",
);
is( ( new ZZPath( path: "abs(neg)" ) ).first(data), 4, "abs() matches prefix abs" );
is(
( new ZZPath( path: "floor(float)" ) ).first(data),
2,
"floor() matches prefix floor",
);
is(
( new ZZPath( path: "ceil(float)" ) ).first(data),
3,
"ceil() matches prefix ceil",
);
is(
( new ZZPath( path: "round(float)" ) ).first(data),
3,
"round() matches prefix round",
);
is( ( new ZZPath( path: "int(float)" ) ).first(data), 2, "int() matches prefix int" );
is( ( new ZZPath( path: "sqrt(root)" ) ).first(data), 4, "sqrt() matches prefix sqrt" );
is( ( new ZZPath( path: "uc(word)" ) ).first(data), "ALPHA", "uc() matches prefix uc" );
is( ( new ZZPath( path: "lc(word)" ) ).first(data), "alpha", "lc() matches prefix lc" );
is(
( new ZZPath( path: "length(word)" ) ).first(data),
5,
"length() matches prefix length",
);
is( ( new ZZPath( path: "not(zero)" ) ).first(data), true, "not() matches prefix not" );
is(
( new ZZPath( path: "typeof(word)" ) ).first(data),
"String",
"typeof() matches prefix typeof",
);
is(
( new ZZPath( path: "/word[length() = 5]" ) ).first(data),
"Alpha",
"word-like functions can use the current node",
);
is( ( new ZZPath( path: "true()" ) ).first(data), true, "true() returns true" );
is( ( new ZZPath( path: "false()" ) ).first(data), false, "false() returns false" );
is( ( new ZZPath( path: "null()" ) ).first(data), null, "null() returns null" );
is( ( new ZZPath( path: "defined(word)" ) ).first(data), true, "defined() finds values" );
is(
( new ZZPath( path: "defined(null())" ) ).first(data),
false,
"defined() rejects null",
);
is(
( new ZZPath( path: "empty(null())" ) ).first(data),
true,
"empty() treats null as empty",
);
is(
( new ZZPath( path: "empty(empty_arr)" ) ).first(data),
true,
"empty() accepts empty arrays",
);
is(
( new ZZPath( path: "empty(values)" ) ).first(data),
false,
"empty() accepts non-empty arrays",
);
is(
( new ZZPath( path: "empty(empty_dict)" ) ).first(data),
true,
"empty() accepts empty dicts",
);
is(
( new ZZPath( path: "empty(empty_set)" ) ).first(data),
true,
"empty() accepts empty sets",
);
like(
exception( function () {
( new ZZPath( path: "empty(word)" ) ).first(data);
} ),
/Collection/,
"empty() rejects scalar values",
);
is(
( new ZZPath( path: "string(float)" ) ).first(data),
"2.6",
"string() exposes std/internals to_String",
);
is(
( new ZZPath( path: "number(\"7\")" ) ).first(data),
7,
"number() exposes std/internals to_Number",
);
is(
( new ZZPath( path: "boolean(0)" ) ).first(data),
false,
"boolean() exposes std/internals to_Boolean",
);
is(
( new ZZPath( path: "typeof(regexp(\"^Al\"))" ) ).first(data),
"Regexp",
"regexp() exposes std/internals to_Regexp",
);
is( ( new ZZPath( path: "sum(1, 2, 3)" ) ).first(data), 6, "sum() exposes Math.sum" );
is( ( new ZZPath( path: "min(3, 1, 2)" ) ).first(data), 1, "min() exposes Math.min" );
is( ( new ZZPath( path: "max(3, 1, 2)" ) ).first(data), 3, "max() exposes Math.max" );
is(
( new ZZPath( path: "clamp(10, 1, 5)" ) ).first(data),
5,
"clamp() exposes Math.clamp",
);
is(
( new ZZPath( path: "sum(values/*)" ) ).first(data),
6,
"math functions accept path result sequences",
);
is(
( new ZZPath( path: "substr(word, 1, 3)" ) ).first(data),
"lph",
"substr() exposes std/string.substr",
);
is(
( new ZZPath( path: "index-of(\"bananas\", \"na\")" ) ).first(data),
2,
"index-of() exposes std/string.index",
);
is(
( new ZZPath( path: "rindex(\"bananas\", \"a\")" ) ).first(data),
5,
"rindex() exposes std/string.rindex",
);
is(
( new ZZPath( path: "last-index-of(\"bananas\", \"a\")" ) ).first(data),
5,
"last-index-of() aliases std/string.rindex",
);
is(
( new ZZPath( path: "contains(word, \"ph\")" ) ).first(data),
true,
"contains() exposes std/string.contains",
);
is(
( new ZZPath( path: "chr(65)" ) ).first(data),
"A",
"chr() exposes std/string.chr",
);
is(
( new ZZPath( path: "ord(word)" ) ).first(data),
65,
"ord() exposes std/string.ord",
);
is(
( new ZZPath( path: "replace(\"foo foo\", \"foo\", \"bar\", \"g\")" ) )
.first(data),
"bar bar",
"replace() exposes std/string.replace",
);
is(
( new ZZPath( path: "search(\"abc123\", \"[0-9]+\")" ) ).first(data),
"123",
"search() exposes std/string.search",
);
is(
( new ZZPath( path: "starts_with(word, \"Al\")" ) ).first(data),
true,
"starts_with() exposes std/string.starts_with",
);
is(
( new ZZPath( path: "ends_with(word, \"ha\")" ) ).first(data),
true,
"ends_with() exposes std/string.ends_with",
);
is(
( new ZZPath( path: "matches(\"abc123\", \"[0-9]+\")" ) ).first(data),
true,
"matches() exposes std/string.matches",
);
is(
( new ZZPath( path: "search(\"Alpha\", pattern_to_regexp(\"Al\"))" ) )
.first(data),
"Al",
"pattern_to_regexp() exposes std/string.pattern_to_regexp",
);
is(
( new ZZPath( path: "quotemeta(\"Zia.+\")" ) ).first(data),
```Zia\.\+```,
"quotemeta() exposes std/string.quotemeta",
);
is(
( new ZZPath( path: "sprint(\"%s = %d\", \"count\", 7)" ) ).first(data),
"count = 7",
"sprint() exposes std/string.sprint",
);
is(
( new ZZPath( path: "split(\"a,b,c\", \",\")" ) ).first(data).length(),
3,
"split() exposes std/string.split",
);
is(
( new ZZPath( path: "join(\"|\", values/*)" ) ).first(data),
"1|2|3",
"join() exposes std/string.join",
);
is(
( new ZZPath( path: "trim(\" hi \")" ) ).first(data),
"hi",
"trim() exposes std/string.trim",
);
is(
( new ZZPath( path: "pad(\"7\", 3, \"0\", \"left\")" ) ).first(data),
"007",
"pad() exposes std/string.pad",
);
is(
( new ZZPath( path: "chomp(\"line\n\")" ) ).first(data),
"line",
"chomp() exposes std/string.chomp",
);
is(
( new ZZPath( path: "title(\"hello_world-value\")" ) ).first(data),
"Hello World Value",
"title() exposes std/string.title",
);
is(
( new ZZPath( path: "snake(\"HelloWorld value\")" ) ).first(data),
"hello_world_value",
"snake() exposes std/string.snake",
);
is(
( new ZZPath( path: "kebab(\"HelloWorld value\")" ) ).first(data),
"hello-world-value",
"kebab() exposes std/string.kebab",
);
is(
( new ZZPath( path: "camel(\"hello_world value\")" ) ).first(data),
"helloWorldValue",
"camel() exposes std/string.camel",
);
done_testing();