Compare commits
4 Commits
0f4a9fcaa7
...
ad73ea6508
Author | SHA1 | Date | |
---|---|---|---|
ad73ea6508 | |||
875b1b6344 | |||
ea52c99fee | |||
dbf2762982 |
23
build.zig
23
build.zig
@ -2,11 +2,26 @@ const std = @import("std");
|
|||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const nice = b.addModule("nice", .{
|
const nice = b.addModule("nice", .{
|
||||||
.source_file = .{ .path = "src/nice.zig" },
|
.root_source_file = .{ .path = "src/nice.zig" },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tests = b.addTest(.{
|
||||||
|
.name = "nice-unit-tests",
|
||||||
|
.root_source_file = .{ .path = "tests/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
tests.root_module.addImport("nice", nice);
|
||||||
|
|
||||||
|
const run_main_tests = b.addRunArtifact(tests);
|
||||||
|
const test_step = b.step("test", "Run tests");
|
||||||
|
test_step.dependOn(&b.addInstallArtifact(tests, .{}).step);
|
||||||
|
test_step.dependOn(&run_main_tests.step);
|
||||||
|
|
||||||
add_examples(b, .{
|
add_examples(b, .{
|
||||||
.target = target,
|
.target = target,
|
||||||
.nice_mod = nice,
|
.nice_mod = nice,
|
||||||
@ -14,7 +29,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ExampleOptions = struct {
|
const ExampleOptions = struct {
|
||||||
target: std.zig.CrossTarget,
|
target: std.Build.ResolvedTarget,
|
||||||
nice_mod: *std.Build.Module,
|
nice_mod: *std.Build.Module,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,7 +44,7 @@ const examples = [_]Example{
|
|||||||
.{ .name = "reify", .file = "examples/reify.zig" },
|
.{ .name = "reify", .file = "examples/reify.zig" },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn add_examples(b: *std.build, options: ExampleOptions) void {
|
pub fn add_examples(b: *std.Build, options: ExampleOptions) void {
|
||||||
const example_step = b.step("examples", "build examples");
|
const example_step = b.step("examples", "build examples");
|
||||||
|
|
||||||
inline for (examples) |example| {
|
inline for (examples) |example| {
|
||||||
@ -40,7 +55,7 @@ pub fn add_examples(b: *std.build, options: ExampleOptions) void {
|
|||||||
.optimize = .Debug,
|
.optimize = .Debug,
|
||||||
});
|
});
|
||||||
|
|
||||||
ex_exe.addModule("nice", options.nice_mod);
|
ex_exe.root_module.addImport("nice", options.nice_mod);
|
||||||
const install = b.addInstallArtifact(ex_exe, .{});
|
const install = b.addInstallArtifact(ex_exe, .{});
|
||||||
example_step.dependOn(&install.step);
|
example_step.dependOn(&install.step);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ pub const Options = struct {
|
|||||||
// If an empty document is parsed, this defines what value type should be the
|
// If an empty document is parsed, this defines what value type should be the
|
||||||
// resulting document root object. The default behavior is to emit an error if the
|
// resulting document root object. The default behavior is to emit an error if the
|
||||||
// document is empty.
|
// document is empty.
|
||||||
default_object: enum { string, list, map, fail } = .fail,
|
default_object: enum { scalar, list, map, fail } = .fail,
|
||||||
|
|
||||||
// Only used by the parseTo family of functions.
|
// Only used by the parseTo family of functions.
|
||||||
// If false, and a mapping contains additional keys that do not map to the fields of
|
// If false, and a mapping contains additional keys that do not map to the fields of
|
||||||
@ -80,10 +80,8 @@ pub const Options = struct {
|
|||||||
// an error if the destination is a boolean type. By default, these comparisons are
|
// an error if the destination is a boolean type. By default, these comparisons are
|
||||||
// case-sensitive. See the `case_insensitive_scalar_coersion` option to change
|
// case-sensitive. See the `case_insensitive_scalar_coersion` option to change
|
||||||
// this.
|
// this.
|
||||||
boolean_scalars: struct { truthy: []const []const u8, falsy: []const []const u8 } = .{
|
truthy_boolean_scalars: []const []const u8 = &.{ "true", "True", "yes", "on" },
|
||||||
.truthy = &.{ "true", "True", "yes", "on" },
|
falsy_boolean_scalars: []const []const u8 = &.{ "false", "False", "no", "off" },
|
||||||
.falsy = &.{ "false", "False", "no", "off" },
|
|
||||||
},
|
|
||||||
|
|
||||||
// Only used by the parseTo family of functions.
|
// Only used by the parseTo family of functions.
|
||||||
// A list of strings. Scalars in the doucment that match any of the values listed
|
// A list of strings. Scalars in the doucment that match any of the values listed
|
||||||
|
@ -59,7 +59,7 @@ pub const State = struct {
|
|||||||
|
|
||||||
switch (state.mode) {
|
switch (state.mode) {
|
||||||
.initial => switch (options.default_object) {
|
.initial => switch (options.default_object) {
|
||||||
.string => state.document.root = Value.emptyString(),
|
.scalar => state.document.root = Value.emptyScalar(),
|
||||||
.list => state.document.root = Value.newList(arena_alloc),
|
.list => state.document.root = Value.newList(arena_alloc),
|
||||||
.map => state.document.root = Value.newMap(arena_alloc),
|
.map => state.document.root = Value.newMap(arena_alloc),
|
||||||
.fail => {
|
.fail => {
|
||||||
|
@ -82,14 +82,14 @@ pub const Value = union(enum) {
|
|||||||
inline .scalar, .string => |str, tag| {
|
inline .scalar, .string => |str, tag| {
|
||||||
if (tag == .string and !options.coerce_strings) return error.BadValue;
|
if (tag == .string and !options.coerce_strings) return error.BadValue;
|
||||||
if (options.case_insensitive_scalar_coersion) {
|
if (options.case_insensitive_scalar_coersion) {
|
||||||
for (options.boolean_scalars.truthy) |check|
|
for (options.truthy_boolean_scalars) |check|
|
||||||
if (std.ascii.eqlIgnoreCase(str, check)) return true;
|
if (std.ascii.eqlIgnoreCase(str, check)) return true;
|
||||||
for (options.boolean_scalars.falsy) |check|
|
for (options.falsy_boolean_scalars) |check|
|
||||||
if (std.ascii.eqlIgnoreCase(str, check)) return false;
|
if (std.ascii.eqlIgnoreCase(str, check)) return false;
|
||||||
} else {
|
} else {
|
||||||
for (options.boolean_scalars.truthy) |check|
|
for (options.truthy_boolean_scalars) |check|
|
||||||
if (std.mem.eql(u8, str, check)) return true;
|
if (std.mem.eql(u8, str, check)) return true;
|
||||||
for (options.boolean_scalars.falsy) |check|
|
for (options.falsy_boolean_scalars) |check|
|
||||||
if (std.mem.eql(u8, str, check)) return false;
|
if (std.mem.eql(u8, str, check)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
tests/main.zig
Normal file
5
tests/main.zig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
comptime {
|
||||||
|
if (@import("builtin").is_test) {
|
||||||
|
_ = @import("./reify.zig");
|
||||||
|
}
|
||||||
|
}
|
144
tests/reify.zig
Normal file
144
tests/reify.zig
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const nice = @import("nice");
|
||||||
|
|
||||||
|
fn reifyScalar(comptime scalar: []const u8, expected: anytype) !void {
|
||||||
|
try reifyScalarWithOptions(scalar, expected, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reifyScalarWithOptions(comptime scalar: []const u8, expected: anytype, options: nice.parser.Options) !void {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var diagnostics = nice.Diagnostics{};
|
||||||
|
const parsed = try nice.parseBufferTo(
|
||||||
|
@TypeOf(expected),
|
||||||
|
allocator,
|
||||||
|
scalar ++ "\n",
|
||||||
|
&diagnostics,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
defer parsed.deinit();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(expected, parsed.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify integer" {
|
||||||
|
try reifyScalar("123", @as(u8, 123));
|
||||||
|
try reifyScalar("0123", @as(u8, 123));
|
||||||
|
try reifyScalar("1_23", @as(u8, 123));
|
||||||
|
try reifyScalar("-01_23", @as(i8, -123));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify hexadecimal" {
|
||||||
|
try reifyScalar("0x123", @as(i64, 0x123));
|
||||||
|
try reifyScalar("0x0123", @as(i64, 0x123));
|
||||||
|
try reifyScalar("0x01_23", @as(i64, 0x123));
|
||||||
|
try reifyScalar("-0x01_23", @as(i64, -0x123));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify octal" {
|
||||||
|
try reifyScalar("0o123", @as(i64, 0o123));
|
||||||
|
try reifyScalar("0o0123", @as(i64, 0o123));
|
||||||
|
try reifyScalar("0o01_23", @as(i64, 0o123));
|
||||||
|
try reifyScalar("-0o01_23", @as(i64, -0o123));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify binary" {
|
||||||
|
try reifyScalar("0b1011", @as(i5, 0b1011));
|
||||||
|
try reifyScalar("0b01011", @as(i5, 0b1011));
|
||||||
|
try reifyScalar("0b010_11", @as(i5, 0b1011));
|
||||||
|
try reifyScalar("-0b010_11", @as(i5, -0b1011));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify float" {
|
||||||
|
try reifyScalar("0.25", @as(f32, 0.25));
|
||||||
|
try reifyScalar("0.2_5", @as(f32, 0.25));
|
||||||
|
try reifyScalar("00.250", @as(f32, 0.25));
|
||||||
|
try reifyScalar("-0.25", @as(f32, -0.25));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify hexfloat" {
|
||||||
|
try reifyScalar("0x0.25", @as(f64, 0x0.25));
|
||||||
|
try reifyScalar("0x0.2_5", @as(f64, 0x0.25));
|
||||||
|
try reifyScalar("0x0.250p1", @as(f64, 0x0.25p1));
|
||||||
|
try reifyScalar("-0x0.25", @as(f64, -0x0.25));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify true" {
|
||||||
|
try reifyScalar("true", true);
|
||||||
|
try reifyScalar("True", true);
|
||||||
|
try reifyScalar("yes", true);
|
||||||
|
try reifyScalar("on", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify false" {
|
||||||
|
try reifyScalar("false", false);
|
||||||
|
try reifyScalar("False", false);
|
||||||
|
try reifyScalar("no", false);
|
||||||
|
try reifyScalar("off", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify custom true" {
|
||||||
|
const options = nice.parser.Options{ .truthy_boolean_scalars = &.{"correct"} };
|
||||||
|
try reifyScalarWithOptions("correct", true, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify true case insensitive" {
|
||||||
|
try std.testing.expectError(error.BadValue, reifyScalar("TRUE", true));
|
||||||
|
const options = nice.parser.Options{ .case_insensitive_scalar_coersion = true };
|
||||||
|
try reifyScalarWithOptions("TRUE", true, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify custom false" {
|
||||||
|
const options = nice.parser.Options{ .falsy_boolean_scalars = &.{"incorrect"} };
|
||||||
|
try reifyScalarWithOptions("incorrect", false, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify false case insensitive" {
|
||||||
|
try std.testing.expectError(error.BadValue, reifyScalar("FALSE", false));
|
||||||
|
const options = nice.parser.Options{ .case_insensitive_scalar_coersion = true };
|
||||||
|
try reifyScalarWithOptions("FALSE", false, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify null" {
|
||||||
|
try reifyScalar("null", @as(?u8, null));
|
||||||
|
try reifyScalar("nil", @as(?u8, null));
|
||||||
|
try reifyScalar("None", @as(?u8, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify custom null" {
|
||||||
|
const options = nice.parser.Options{ .null_scalars = &.{"nothing"} };
|
||||||
|
try reifyScalarWithOptions("nothing", @as(?u8, null), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify null case insensitive" {
|
||||||
|
// this is a little weird because when the null string mismatches, it will try to
|
||||||
|
// parse the child optional type and produce either a value or an error from that,
|
||||||
|
// so the error received depends on whether or not the optional child type fails to
|
||||||
|
// parse the given value.
|
||||||
|
try std.testing.expectError(error.InvalidCharacter, reifyScalar("NULL", @as(?u8, null)));
|
||||||
|
const options = nice.parser.Options{ .case_insensitive_scalar_coersion = true };
|
||||||
|
try reifyScalarWithOptions("NULL", @as(?u8, null), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify void" {
|
||||||
|
// A void scalar cannot exist on its own as it is not distinguishable from an empty
|
||||||
|
// document.
|
||||||
|
const Void = struct { void: void };
|
||||||
|
try reifyScalar("void:", Void{ .void = void{} });
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify void scalar" {
|
||||||
|
const options = nice.parser.Options{ .default_object = .scalar };
|
||||||
|
try reifyScalarWithOptions("", void{}, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify enum" {
|
||||||
|
const Enum = enum { one, two };
|
||||||
|
try reifyScalar(".one", Enum.one);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "reify enum no dot" {
|
||||||
|
const options = nice.parser.Options{ .expect_enum_dot = false };
|
||||||
|
const Enum = enum { one, two };
|
||||||
|
try reifyScalarWithOptions("two", Enum.two, options);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user