Function innerParseFromValue [src]
Alias for std.json.static.innerParseFromValue
This is an internal function called recursively
during the implementation of parseFromValueLeaky.
It is exposed primarily to enable custom jsonParseFromValue() methods to call back into the parseFromValue* system,
such as if you're implementing a custom container of type T;
you can call innerParseFromValue(T, ...) for each of the container's items.
Prototype
pub fn innerParseFromValue( comptime T: type, allocator: Allocator, source: Value, options: ParseOptions, ) ParseFromValueError!T
Parameters
T: type
allocator: Allocator
source: Value
options: ParseOptions
Possible Errors
The input was empty or contained an invalid character
The result cannot fit in the type specified
Source
pub fn innerParseFromValue(
comptime T: type,
allocator: Allocator,
source: Value,
options: ParseOptions,
) ParseFromValueError!T {
switch (@typeInfo(T)) {
.bool => {
switch (source) {
.bool => |b| return b,
else => return error.UnexpectedToken,
}
},
.float, .comptime_float => {
switch (source) {
.float => |f| return @as(T, @floatCast(f)),
.integer => |i| return @as(T, @floatFromInt(i)),
.number_string, .string => |s| return std.fmt.parseFloat(T, s),
else => return error.UnexpectedToken,
}
},
.int, .comptime_int => {
switch (source) {
.float => |f| {
if (@round(f) != f) return error.InvalidNumber;
if (f > std.math.maxInt(T)) return error.Overflow;
if (f < std.math.minInt(T)) return error.Overflow;
return @as(T, @intFromFloat(f));
},
.integer => |i| {
if (i > std.math.maxInt(T)) return error.Overflow;
if (i < std.math.minInt(T)) return error.Overflow;
return @as(T, @intCast(i));
},
.number_string, .string => |s| {
return sliceToInt(T, s);
},
else => return error.UnexpectedToken,
}
},
.optional => |optionalInfo| {
switch (source) {
.null => return null,
else => return try innerParseFromValue(optionalInfo.child, allocator, source, options),
}
},
.@"enum" => {
if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options);
}
switch (source) {
.float => return error.InvalidEnumTag,
.integer => |i| return std.meta.intToEnum(T, i),
.number_string, .string => |s| return sliceToEnum(T, s),
else => return error.UnexpectedToken,
}
},
.@"union" => |unionInfo| {
if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options);
}
if (unionInfo.tag_type == null) @compileError("Unable to parse into untagged union '" ++ @typeName(T) ++ "'");
if (source != .object) return error.UnexpectedToken;
if (source.object.count() != 1) return error.UnexpectedToken;
var it = source.object.iterator();
const kv = it.next().?;
const field_name = kv.key_ptr.*;
inline for (unionInfo.fields) |u_field| {
if (std.mem.eql(u8, u_field.name, field_name)) {
if (u_field.type == void) {
// void isn't really a json type, but we can support void payload union tags with {} as a value.
if (kv.value_ptr.* != .object) return error.UnexpectedToken;
if (kv.value_ptr.*.object.count() != 0) return error.UnexpectedToken;
return @unionInit(T, u_field.name, {});
}
// Recurse.
return @unionInit(T, u_field.name, try innerParseFromValue(u_field.type, allocator, kv.value_ptr.*, options));
}
}
// Didn't match anything.
return error.UnknownField;
},
.@"struct" => |structInfo| {
if (structInfo.is_tuple) {
if (source != .array) return error.UnexpectedToken;
if (source.array.items.len != structInfo.fields.len) return error.UnexpectedToken;
var r: T = undefined;
inline for (0..structInfo.fields.len, source.array.items) |i, item| {
r[i] = try innerParseFromValue(structInfo.fields[i].type, allocator, item, options);
}
return r;
}
if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options);
}
if (source != .object) return error.UnexpectedToken;
var r: T = undefined;
var fields_seen = [_]bool{false} ** structInfo.fields.len;
var it = source.object.iterator();
while (it.next()) |kv| {
const field_name = kv.key_ptr.*;
inline for (structInfo.fields, 0..) |field, i| {
if (field.is_comptime) @compileError("comptime fields are not supported: " ++ @typeName(T) ++ "." ++ field.name);
if (std.mem.eql(u8, field.name, field_name)) {
assert(!fields_seen[i]); // Can't have duplicate keys in a Value.object.
@field(r, field.name) = try innerParseFromValue(field.type, allocator, kv.value_ptr.*, options);
fields_seen[i] = true;
break;
}
} else {
// Didn't match anything.
if (!options.ignore_unknown_fields) return error.UnknownField;
}
}
try fillDefaultStructValues(T, &r, &fields_seen);
return r;
},
.array => |arrayInfo| {
switch (source) {
.array => |array| {
// Typical array.
return innerParseArrayFromArrayValue(T, arrayInfo.child, arrayInfo.len, allocator, array, options);
},
.string => |s| {
if (arrayInfo.child != u8) return error.UnexpectedToken;
// Fixed-length string.
if (s.len != arrayInfo.len) return error.LengthMismatch;
var r: T = undefined;
@memcpy(r[0..], s);
return r;
},
else => return error.UnexpectedToken,
}
},
.vector => |vecInfo| {
switch (source) {
.array => |array| {
return innerParseArrayFromArrayValue(T, vecInfo.child, vecInfo.len, allocator, array, options);
},
else => return error.UnexpectedToken,
}
},
.pointer => |ptrInfo| {
switch (ptrInfo.size) {
.one => {
const r: *ptrInfo.child = try allocator.create(ptrInfo.child);
r.* = try innerParseFromValue(ptrInfo.child, allocator, source, options);
return r;
},
.slice => {
switch (source) {
.array => |array| {
const r = if (ptrInfo.sentinel()) |sentinel|
try allocator.allocSentinel(ptrInfo.child, array.items.len, sentinel)
else
try allocator.alloc(ptrInfo.child, array.items.len);
for (array.items, r) |item, *dest| {
dest.* = try innerParseFromValue(ptrInfo.child, allocator, item, options);
}
return r;
},
.string => |s| {
if (ptrInfo.child != u8) return error.UnexpectedToken;
// Dynamic length string.
const r = if (ptrInfo.sentinel()) |sentinel|
try allocator.allocSentinel(ptrInfo.child, s.len, sentinel)
else
try allocator.alloc(ptrInfo.child, s.len);
@memcpy(r[0..], s);
return r;
},
else => return error.UnexpectedToken,
}
},
else => @compileError("Unable to parse into type '" ++ @typeName(T) ++ "'"),
}
},
else => @compileError("Unable to parse into type '" ++ @typeName(T) ++ "'"),
}
}