Date, clean and errors

Added the datatype date, time and datetime

Moved all custom erros to a single file

Cleaned code to be more readable and created some small utils

Other stuffs that I dont recall. Basically preparing for the
relationship implementation
This commit is contained in:
2024-10-19 14:42:13 +02:00
parent 673148ba77
commit 6bcb369cbc
20 changed files with 1655 additions and 602 deletions
+5
View File
@@ -1 +1,6 @@
pub const BUFFER_SIZE = 1024 * 50; // Line limit when parsing file
pub const MAX_FILE_SIZE = 5e+4; // 50kb TODO: Put in config file
// Testing
pub const TEST_DATA_DIR = "test_data/v0.1.1"; // Maybe put that directly in the build
+366 -274
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -23,6 +23,7 @@ const State = enum {
end,
};
// TODO: If an argument is given when starting the binary, it is the db path
pub fn main() !void {
var state: State = .expect_main_command;
@@ -44,10 +45,10 @@ pub fn main() !void {
_ = std.fs.cwd().openDir(path, .{}) catch {
std.debug.print("Error opening ZipponDB path using environment variable, please select the database using 'db use' or create a new one with 'db new'\n", .{});
file_engine = FileEngine.init(allocator, try allocator.dupe(u8, ""));
to_init = true;
to_init = false;
};
if (to_init) {
file_engine = FileEngine.init(allocator, path_env_variable.?);
file_engine = FileEngine.init(allocator, path);
try file_engine.checkAndCreateDirectories();
}
} else {
@@ -73,7 +74,7 @@ pub fn main() !void {
while ((state != .end) and (state != .quit)) : (token = toker.next()) switch (state) {
.expect_main_command => switch (token.tag) {
.keyword_run => {
if (!file_engine.usable) {
if (!file_engine.usable()) {
send("Error: No database selected. Please use db new or db use.", .{});
state = .end;
continue;
@@ -82,7 +83,7 @@ pub fn main() !void {
},
.keyword_db => state = .expect_db_command,
.keyword_schema => {
if (!file_engine.usable) {
if (!file_engine.usable()) {
send("Error: No database selected. Please use db new or db use.", .{});
state = .end;
continue;
@@ -116,7 +117,7 @@ pub fn main() !void {
.keyword_new => state = .expect_path_to_new_db,
.keyword_use => state = .expect_path_to_db,
.keyword_metrics => {
if (!file_engine.usable) {
if (!file_engine.usable()) {
send("Error: No database selected. Please use db new or db use.", .{});
state = .end;
continue;
+98 -30
View File
@@ -6,10 +6,7 @@ const Token = @import("tokenizers/schema.zig").Token;
const send = @import("stuffs/utils.zig").send;
const printError = @import("stuffs/utils.zig").printError;
const SchemaParserError = error{
SynthaxError,
FeatureMissing,
};
const SchemaParserError = @import("stuffs/errors.zig").SchemaParserError;
const State = enum {
end,
@@ -35,10 +32,6 @@ pub const Parser = struct {
};
}
// Maybe I the name and member can be Loc, with a start and end, and use the buffer to get back the value
// This is how Token works
// From my understanding this is the same here. I put slices, that can just a len and a pointer, put I con't save the value itself.
// Or maybe I do actually, and an array of pointer would be *[]u8
pub const SchemaStruct = struct {
allocator: Allocator,
name: Token.Loc,
@@ -46,7 +39,12 @@ pub const Parser = struct {
types: std.ArrayList(DataType),
pub fn init(allocator: Allocator, name: Token.Loc) SchemaStruct {
return SchemaStruct{ .allocator = allocator, .name = name, .members = std.ArrayList(Token.Loc).init(allocator), .types = std.ArrayList(DataType).init(allocator) };
return SchemaStruct{
.allocator = allocator,
.name = name,
.members = std.ArrayList(Token.Loc).init(allocator),
.types = std.ArrayList(DataType).init(allocator),
};
}
pub fn deinit(self: *SchemaStruct) void {
@@ -78,15 +76,27 @@ pub const Parser = struct {
.expect_struct_name_OR_end => switch (token.tag) {
.identifier => {
state = .expect_l_paren;
struct_array.append(SchemaStruct.init(self.allocator, token.loc)) catch @panic("Error appending a struct name.");
struct_array.append(SchemaStruct.init(self.allocator, token.loc)) catch return SchemaParserError.MemoryError;
},
.eof => state = .end,
else => return printError("Error parsing schema: Expected a struct name", SchemaParserError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end),
else => return printError(
"Error parsing schema: Expected a struct name",
SchemaParserError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expect_l_paren => switch (token.tag) {
.l_paren => state = .expect_member_name,
else => return printError("Error parsing schema: Expected (", SchemaParserError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end),
else => return printError(
"Error parsing schema: Expected (",
SchemaParserError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expect_member_name_OR_r_paren => switch (token.tag) {
@@ -98,67 +108,125 @@ pub const Parser = struct {
state = .expect_struct_name_OR_end;
index += 1;
},
else => return printError("Error parsing schema: Expected member name or )", SchemaParserError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end),
else => return printError(
"Error parsing schema: Expected member name or )",
SchemaParserError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expect_member_name => {
state = .expect_two_dot;
struct_array.items[index].members.append(token.loc) catch @panic("Error appending a member name.");
struct_array.items[index].members.append(token.loc) catch return SchemaParserError.MemoryError;
},
.expect_two_dot => switch (token.tag) {
.two_dot => state = .expect_value_type,
else => return printError("Error parsing schema: Expected :", SchemaParserError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end),
else => return printError(
"Error parsing schema: Expected :",
SchemaParserError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expect_value_type => switch (token.tag) {
.type_int => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.int) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.int) catch return SchemaParserError.MemoryError;
},
.type_str => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.str) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.str) catch return SchemaParserError.MemoryError;
},
.type_float => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.float) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.float) catch return SchemaParserError.MemoryError;
},
.type_bool => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.bool) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.bool) catch return SchemaParserError.MemoryError;
},
.type_date => @panic("Date not yet implemented"),
.identifier => @panic("Link not yet implemented"),
.type_date => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.date) catch return SchemaParserError.MemoryError;
},
.type_time => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.time) catch return SchemaParserError.MemoryError;
},
.type_datetime => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.datetime) catch return SchemaParserError.MemoryError;
},
.identifier => return SchemaParserError.FeatureMissing,
.lr_bracket => state = .expext_array_type,
else => return printError("Error parsing schema: Expected data type", SchemaParserError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end),
else => return printError(
"Error parsing schema: Expected data type",
SchemaParserError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expext_array_type => switch (token.tag) {
.type_int => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.int_array) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.int_array) catch return SchemaParserError.MemoryError;
},
.type_str => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.str_array) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.str_array) catch return SchemaParserError.MemoryError;
},
.type_float => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.float_array) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.float_array) catch return SchemaParserError.MemoryError;
},
.type_bool => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.bool_array) catch @panic("Error appending a type.");
struct_array.items[index].types.append(DataType.bool_array) catch return SchemaParserError.MemoryError;
},
.type_date => return printError("Error parsing schema: Data not yet implemented", SchemaParserError.FeatureMissing, self.toker.buffer, token.loc.start, token.loc.end),
.identifier => return printError("Error parsing schema: Relationship not yet implemented", SchemaParserError.FeatureMissing, self.toker.buffer, token.loc.start, token.loc.end),
else => return printError("Error parsing schema: Expected data type", SchemaParserError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end),
.type_date => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.date_array) catch return SchemaParserError.MemoryError;
},
.type_time => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.time_array) catch return SchemaParserError.MemoryError;
},
.type_datetime => {
state = .expect_comma;
struct_array.items[index].types.append(DataType.datetime_array) catch return SchemaParserError.MemoryError;
},
.identifier => return printError(
"Error parsing schema: Relationship not yet implemented",
SchemaParserError.FeatureMissing,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
else => return printError(
"Error parsing schema: Expected data type",
SchemaParserError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expect_comma => switch (token.tag) {
.comma => state = .expect_member_name_OR_r_paren,
else => return printError("Error parsing schema: Expected ,", SchemaParserError.SynthaxError, self.toker.buffer, token.loc.start, token.loc.end),
else => return printError(
"Error parsing schema: Expected ,",
SchemaParserError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
else => unreachable,
+42
View File
@@ -0,0 +1,42 @@
pub const ZiQlParserError = error{
SynthaxError,
MemberNotFound,
MemberMissing,
StructNotFound,
FeatureMissing,
ParsingValueError,
ConditionError,
};
pub const SchemaParserError = error{
SynthaxError,
FeatureMissing,
ValueParsingError,
MemoryError,
};
pub const FileEngineError = error{
SchemaFileNotFound,
SchemaNotConform,
DATAFolderNotFound,
StructFolderNotFound,
CantMakeDir,
CantMakeFile,
CantOpenDir,
CantOpenFile,
MemoryError,
StreamError,
ReadError, // TODO: Only use stream
InvalidUUID,
InvalidDate,
InvalidFileIndex,
DirIterError,
WriteError,
FileStatError,
DeleteFileError,
RenameFileError,
StructNotFound,
MemberNotFound,
};
pub const ZipponError = ZiQlParserError || FileEngineError || SchemaParserError;
+3 -2
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const ZipponError = @import("errors.zig").ZipponError;
const stdout = std.io.getStdOut().writer();
@@ -43,7 +44,7 @@ pub fn send(comptime format: []const u8, args: anytype) void {
}
/// Print an error and send it to the user pointing to the token
pub fn printError(message: []const u8, err: anyerror, query: ?[]const u8, start: ?usize, end: ?usize) anyerror {
pub fn printError(message: []const u8, err: ZipponError, query: ?[]const u8, start: ?usize, end: ?usize) ZipponError {
const allocator = std.heap.page_allocator;
var buffer = std.ArrayList(u8).init(allocator);
defer buffer.deinit();
@@ -54,7 +55,7 @@ pub fn printError(message: []const u8, err: anyerror, query: ?[]const u8, start:
writer.print("{s}\n", .{message}) catch {};
if ((start != null) and (end != null) and (query != null)) {
const buffer_query = try allocator.dupe(u8, query.?);
const buffer_query = allocator.dupe(u8, query.?) catch return ZipponError.MemoryError;
defer allocator.free(buffer_query);
std.mem.replaceScalar(u8, buffer_query, '\n', ' ');
+1 -1
View File
@@ -116,7 +116,7 @@ pub const Tokenizer = struct {
},
.identifier => switch (c) {
'a'...'z', 'A'...'Z', '_', '0'...'9', '.' => continue,
'a'...'z', 'A'...'Z', '_', '0'...'9', '.', '/' => continue,
else => {
if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
result.tag = tag;
+45 -1
View File
@@ -13,6 +13,10 @@ pub const Token = struct {
string_literal,
int_literal,
float_literal,
uuid_literal,
date_literal,
time_literal,
datetime_literal,
l_bracket, // [
r_bracket, // ]
};
@@ -36,6 +40,9 @@ pub const Tokenizer = struct {
string_literal,
float,
int,
uuid_literal,
date_literal,
time_literal,
};
pub fn getTokenSlice(self: *Tokenizer, token: Token) []const u8 {
@@ -65,6 +72,11 @@ pub const Tokenizer = struct {
state = .string_literal;
result.tag = .string_literal;
},
'a'...'z' => {
state = .uuid_literal;
result.tag = .uuid_literal;
},
'0'...'9', '-' => {
state = .int;
result.tag = .int_literal;
@@ -95,9 +107,22 @@ pub const Tokenizer = struct {
state = .float;
result.tag = .float_literal;
},
'0'...'9' => continue,
'a'...'z', '-' => {
state = .uuid_literal;
result.tag = .uuid_literal;
},
'/' => {
state = .date_literal;
result.tag = .date_literal;
},
':' => {
state = .time_literal;
result.tag = .time_literal;
},
'_', '0'...'9' => continue,
else => break,
},
.float => switch (c) {
'0'...'9' => {
continue;
@@ -106,6 +131,25 @@ pub const Tokenizer = struct {
break;
},
},
.date_literal => switch (c) {
'-' => {
state = .time_literal;
result.tag = .datetime_literal;
},
'0'...'9', '/' => continue,
else => break,
},
.time_literal => switch (c) {
'0'...'9', ':', '.' => continue,
else => break,
},
.uuid_literal => switch (c) {
'0'...'9', 'a'...'z', '-' => continue,
else => break,
},
}
}
+15 -48
View File
@@ -95,7 +95,6 @@ pub const Tokenizer = struct {
string_literal,
date_literal,
time_literal,
datetime_literal,
uuid_literal,
identifier,
equal,
@@ -103,9 +102,7 @@ pub const Tokenizer = struct {
angle_bracket_left,
angle_bracket_right,
string_literal_backslash,
int_exponent,
float,
float_exponent,
int,
};
@@ -204,7 +201,7 @@ pub const Tokenizer = struct {
state = .float;
result.tag = .float_literal;
},
'0'...'9' => {
'0'...'9', '-' => {
state = .int;
result.tag = .int_literal;
},
@@ -324,7 +321,7 @@ pub const Tokenizer = struct {
state = .float;
result.tag = .float_literal;
},
'a'...'d', 'f'...'z' => {
'a'...'z', '-' => {
state = .uuid_literal;
result.tag = .uuid_literal;
},
@@ -332,35 +329,15 @@ pub const Tokenizer = struct {
state = .date_literal;
result.tag = .date_literal;
},
'-' => {
if ((self.index - result.loc.start) == 2) {
state = .time_literal;
result.tag = .time_literal;
} else { // Just in case a uuid have only number as fist part of its UUID
state = .uuid_literal;
result.tag = .uuid_literal;
}
},
'e', 'E' => {
state = .int_exponent;
result.tag = .float_literal;
':' => {
state = .time_literal;
result.tag = .time_literal;
},
'_', '0'...'9' => continue,
else => break,
},
.int_exponent => switch (c) {
'+', '-', '0'...'9' => {
state = .float;
},
else => {
self.index -= 1;
break;
},
},
.float => switch (c) {
'e', 'E' => {
state = .float_exponent;
},
'_', '0'...'9' => {
continue;
},
@@ -368,31 +345,21 @@ pub const Tokenizer = struct {
break;
},
},
.float_exponent => switch (c) {
'+', '-', '0'...'9' => {
continue;
},
else => {
self.index -= 1;
break;
},
},
.date_literal => switch (c) {
'|' => {
state = .datetime_literal;
'-' => {
state = .time_literal;
result.tag = .datetime_literal;
},
'0'...'9', '/' => continue,
else => break,
},
.time_literal => switch (c) {
'0'...'9', '-', '.' => continue,
else => break,
},
.datetime_literal => switch (c) {
'0'...'9', '-', '.' => continue,
'0'...'9', ':', '.' => continue,
else => break,
},
.uuid_literal => switch (c) {
'0'...'9', 'a'...'z', '-' => continue,
else => break,
@@ -421,9 +388,9 @@ test "basic query" {
test "basic date" {
try testTokenize("1a5527af-88fb-48c1-8d5c-49c9b73c2379", &.{.uuid_literal});
try testTokenize("21/01/1998", &.{.date_literal});
try testTokenize("17-55-31.0000", &.{.time_literal});
try testTokenize("21/01/1998|17-55-31.0000", &.{.datetime_literal});
try testTokenize("1998/01/21", &.{.date_literal});
try testTokenize("17:55:31.0000", &.{.time_literal});
try testTokenize("1998/01/21-17:55:31.0000", &.{.datetime_literal});
}
fn testTokenize(source: [:0]const u8, expected_token_tags: []const Token.Tag) !void {
+7
View File
@@ -1,13 +1,20 @@
/// Suported dataType for the DB
/// Maybe start using a unionenum
pub const DataType = enum {
int,
float,
str,
bool,
id,
date,
time,
datetime,
int_array,
float_array,
str_array,
bool_array,
id_array,
date_array,
time_array,
datetime_array,
};
+29 -61
View File
@@ -2,7 +2,6 @@
const std = @import("std");
const string = []const u8;
const extras = @import("extras");
const time = @This();
pub const DateTime = struct {
@@ -13,9 +12,8 @@ pub const DateTime = struct {
days: u8,
months: u8,
years: u16,
timezone: TimeZone,
const Self = @This();
const Self = @This(); // Pas mal ca
pub fn initUnixMs(unix: u64) Self {
return epoch_unix.addMs(unix);
@@ -26,14 +24,15 @@ pub const DateTime = struct {
}
/// Caller asserts that this is > epoch
pub fn init(year: u16, month: u16, day: u16, hr: u16, min: u16, sec: u16) Self {
pub fn init(year: u16, month: u16, day: u16, hr: u16, min: u16, sec: u16, ms: u16) Self {
return epoch_unix
.addYears(year - epoch_unix.years)
.addMonths(month)
.addDays(day)
.addHours(hr)
.addMins(min)
.addSecs(sec);
.addSecs(sec)
.addMs(ms);
}
pub fn now() Self {
@@ -47,8 +46,7 @@ pub const DateTime = struct {
.hours = 0,
.days = 0,
.months = 0,
.years = 1970, // Why ?
.timezone = .UTC,
.years = 0,
};
pub fn eql(self: Self, other: Self) bool {
@@ -58,9 +56,30 @@ pub const DateTime = struct {
self.hours == other.hours and
self.days == other.days and
self.months == other.months and
self.years == other.years and
self.timezone == other.timezone and
self.weekday == other.weekday;
self.years == other.years;
}
pub fn compareDate(self: Self, other: Self) bool {
return self.days == other.days and
self.months == other.months and
self.years == other.years;
}
pub fn compareTime(self: Self, other: Self) bool {
return self.ms == other.ms and
self.seconds == other.seconds and
self.minutes == other.minutes and
self.hours == other.hours;
}
pub fn compareDatetime(self: Self, other: Self) bool {
return self.ms == other.ms and
self.seconds == other.seconds and
self.minutes == other.minutes and
self.hours == other.hours and
self.days == other.days and
self.months == other.months and
self.years == other.years;
}
// So as long as the count / unit, it continue adding the next unit, smart
@@ -390,20 +409,6 @@ pub const DateTime = struct {
.ms = self.toUnixMilli() - other_in_the_past.toUnixMilli(),
};
}
pub fn era(self: Self) Era {
if (self.years >= 0) return .AD;
@compileError("TODO");
}
pub fn weekday(self: Self) WeekDay {
var i = self.daysSinceEpoch() % 7;
var result = WeekDay.Thu; // weekday of epoch_unix
while (i > 0) : (i -= 1) {
result = result.next();
}
return result;
}
};
pub const format = struct {
@@ -419,43 +424,6 @@ pub const format = struct {
pub const llll = "ddd, " ++ lll;
};
pub const TimeZone = enum {
UTC,
usingnamespace extras.TagNameJsonStringifyMixin(@This());
};
pub const WeekDay = enum {
Sun,
Mon,
Tue,
Wed,
Thu,
Fri,
Sat,
pub fn next(self: WeekDay) WeekDay {
return switch (self) {
.Sun => .Mon,
.Mon => .Tue,
.Tue => .Wed,
.Wed => .Thu,
.Thu => .Fri,
.Fri => .Sat,
.Sat => .Sun,
};
}
usingnamespace extras.TagNameJsonStringifyMixin(@This());
};
pub const Era = enum {
// BC,
AD,
usingnamespace extras.TagNameJsonStringifyMixin(@This());
};
pub fn isLeapYear(year: u16) bool {
var ret = false;
if (year % 4 == 0) ret = true;
+208 -33
View File
@@ -1,5 +1,6 @@
const std = @import("std");
const UUID = @import("uuid.zig").UUID;
const DateTime = @import("date.zig").DateTime;
// TODO: Put those functions somewhere else
pub fn parseInt(value_str: []const u8) i64 {
@@ -36,6 +37,69 @@ pub fn parseBool(value_str: []const u8) bool {
return (value_str[0] != '0');
}
// TODO: Optimize all date parsing
pub fn parseDate(value_str: []const u8) DateTime {
const year: u16 = std.fmt.parseInt(u16, value_str[0..4], 10) catch 0;
const month: u16 = std.fmt.parseInt(u16, value_str[5..7], 10) catch 0;
const day: u16 = std.fmt.parseInt(u16, value_str[8..10], 10) catch 0;
return DateTime.init(year, month, day, 0, 0, 0, 0);
}
pub fn parseArrayDate(allocator: std.mem.Allocator, array_str: []const u8) std.ArrayList(DateTime) {
var array = std.ArrayList(DateTime).init(allocator);
var it = std.mem.splitAny(u8, array_str[1 .. array_str.len - 1], " ");
while (it.next()) |x| {
array.append(parseDate(x)) catch {};
}
return array;
}
pub fn parseTime(value_str: []const u8) DateTime {
const hours: u16 = std.fmt.parseInt(u16, value_str[0..2], 10) catch 0;
const minutes: u16 = std.fmt.parseInt(u16, value_str[3..5], 10) catch 0;
const seconds: u16 = if (value_str.len > 6) std.fmt.parseInt(u16, value_str[6..8], 10) catch 0 else 0;
const milliseconds: u16 = if (value_str.len > 9) std.fmt.parseInt(u16, value_str[9..13], 10) catch 0 else 0;
return DateTime.init(0, 0, 0, hours, minutes, seconds, milliseconds);
}
pub fn parseArrayTime(allocator: std.mem.Allocator, array_str: []const u8) std.ArrayList(DateTime) {
var array = std.ArrayList(DateTime).init(allocator);
var it = std.mem.splitAny(u8, array_str[1 .. array_str.len - 1], " ");
while (it.next()) |x| {
array.append(parseTime(x)) catch {};
}
return array;
}
pub fn parseDatetime(value_str: []const u8) DateTime {
const year: u16 = std.fmt.parseInt(u16, value_str[0..4], 10) catch 0;
const month: u16 = std.fmt.parseInt(u16, value_str[5..7], 10) catch 0;
const day: u16 = std.fmt.parseInt(u16, value_str[8..10], 10) catch 0;
const hours: u16 = std.fmt.parseInt(u16, value_str[11..13], 10) catch 0;
const minutes: u16 = std.fmt.parseInt(u16, value_str[14..16], 10) catch 0;
const seconds: u16 = if (value_str.len > 17) std.fmt.parseInt(u16, value_str[17..19], 10) catch 0 else 0;
const milliseconds: u16 = if (value_str.len > 20) std.fmt.parseInt(u16, value_str[20..24], 10) catch 0 else 0;
return DateTime.init(year, month, day, hours, minutes, seconds, milliseconds);
}
pub fn parseArrayDatetime(allocator: std.mem.Allocator, array_str: []const u8) std.ArrayList(DateTime) {
var array = std.ArrayList(DateTime).init(allocator);
var it = std.mem.splitAny(u8, array_str[1 .. array_str.len - 1], " ");
while (it.next()) |x| {
array.append(parseDatetime(x)) catch {};
}
return array;
}
pub fn parseArrayBool(allocator: std.mem.Allocator, array_str: []const u8) std.ArrayList(bool) {
var array = std.ArrayList(bool).init(allocator);
@@ -59,65 +123,176 @@ pub fn parseArrayUUID(allocator: std.mem.Allocator, array_str: []const u8) std.A
return array;
}
// FIXME: I think it will not work if there is a ' inside the string
// FIXME: I think it will not work if there is a ' inside the string, even \', need to fix that
pub fn parseArrayStr(allocator: std.mem.Allocator, array_str: []const u8) std.ArrayList([]const u8) {
var array = std.ArrayList([]const u8).init(allocator);
var it = std.mem.splitAny(u8, array_str[1 .. array_str.len - 1], "'");
_ = it.next(); // SSkip first token that is empty
while (it.next()) |x| {
if (std.mem.eql(u8, " ", x)) continue;
const x_copy = allocator.dupe(u8, x) catch @panic("=(");
// FIXME: I think I need to add the '' on each side again
const x_copy = std.fmt.allocPrint(allocator, "'{s}'", .{x}) catch @panic("=(");
array.append(x_copy) catch {};
}
allocator.free(array.pop()); // Remove the last because empty like the first one
return array;
}
test "Data parsing" {
test "Value parsing: Int" {
const allocator = std.testing.allocator;
// Int
const in1: [3][]const u8 = .{ "1", "42", "Hello" };
const expected_out1: [3]i64 = .{ 1, 42, 0 };
for (in1, 0..) |value, i| {
try std.testing.expect(parseInt(value) == expected_out1[i]);
const values: [3][]const u8 = .{ "1", "42", "Hello" };
const expected_values: [3]i64 = .{ 1, 42, 0 };
for (values, 0..) |value, i| {
try std.testing.expect(parseInt(value) == expected_values[i]);
}
// Int array
const in2 = "[1 14 44 42 hello]";
const out2 = parseArrayInt(allocator, in2);
defer out2.deinit();
const expected_out2: [5]i64 = .{ 1, 14, 44, 42, 0 };
try std.testing.expect(std.mem.eql(i64, out2.items, &expected_out2));
const array_str = "[1 14 44 42 hello]";
const array = parseArrayInt(allocator, array_str);
defer array.deinit();
const expected_array: [5]i64 = .{ 1, 14, 44, 42, 0 };
try std.testing.expect(std.mem.eql(i64, array.items, &expected_array));
}
test "Value parsing: Float" {
const allocator = std.testing.allocator;
// Float
const in3: [3][]const u8 = .{ "1.3", "65.991", "Hello" };
const expected_out3: [3]f64 = .{ 1.3, 65.991, 0 };
for (in3, 0..) |value, i| {
try std.testing.expect(parseFloat(value) == expected_out3[i]);
const values: [3][]const u8 = .{ "1.3", "65.991", "Hello" };
const expected_values: [3]f64 = .{ 1.3, 65.991, 0 };
for (values, 0..) |value, i| {
try std.testing.expect(parseFloat(value) == expected_values[i]);
}
// Float array
const in4 = "[1.5 14.3 44.9999 42 hello]";
const out4 = parseArrayFloat(allocator, in4);
defer out4.deinit();
const expected_out4: [5]f64 = .{ 1.5, 14.3, 44.9999, 42, 0 };
try std.testing.expect(std.mem.eql(f64, out4.items, &expected_out4));
const array_str = "[1.5 14.3 44.9999 42 hello]";
const array = parseArrayFloat(allocator, array_str);
defer array.deinit();
const expected_array: [5]f64 = .{ 1.5, 14.3, 44.9999, 42, 0 };
try std.testing.expect(std.mem.eql(f64, array.items, &expected_array));
}
// Bool
const in5: [3][]const u8 = .{ "1", "Hello", "0" };
const expected_out5: [3]bool = .{ true, true, false };
for (in5, 0..) |value, i| {
try std.testing.expect(parseBool(value) == expected_out5[i]);
test "Value parsing: String" {
// Note that I dont parse string because I dont need to, a string is a string
const allocator = std.testing.allocator;
// string array
const array_str = "['Hello' 'How are you doing ?' '']";
const array = parseArrayStr(allocator, array_str);
defer {
for (array.items) |parsed| {
allocator.free(parsed);
}
array.deinit();
}
const expected_array: [3][]const u8 = .{ "'Hello'", "'How are you doing ?'", "''" };
for (array.items, expected_array) |parsed, expected| {
try std.testing.expect(std.mem.eql(u8, parsed, expected));
}
}
test "Value parsing: Bool array" {
const allocator = std.testing.allocator;
const values: [3][]const u8 = .{ "1", "Hello", "0" };
const expected_values: [3]bool = .{ true, true, false };
for (values, 0..) |value, i| {
try std.testing.expect(parseBool(value) == expected_values[i]);
}
// Bool array
const in6 = "[1 0 0 1 1]";
const out6 = parseArrayBool(allocator, in6);
defer out6.deinit();
const expected_out6: [5]bool = .{ true, false, false, true, true };
try std.testing.expect(std.mem.eql(bool, out6.items, &expected_out6));
const array_str = "[1 0 0 1 1]";
const array = parseArrayBool(allocator, array_str);
defer array.deinit();
const expected_array: [5]bool = .{ true, false, false, true, true };
try std.testing.expect(std.mem.eql(bool, array.items, &expected_array));
}
// TODO: Test the string array
test "Value parsing: Date" {
const allocator = std.testing.allocator;
// Date
const values: [3][]const u8 = .{ "1920/01/01", "1998/01/21", "2024/12/31" };
const expected_values: [3]DateTime = .{
DateTime.init(1920, 1, 1, 0, 0, 0, 0),
DateTime.init(1998, 1, 21, 0, 0, 0, 0),
DateTime.init(2024, 12, 31, 0, 0, 0, 0),
};
for (values, 0..) |value, i| {
try std.testing.expect(expected_values[i].compareDate(parseDate(value)));
}
// Date array
const array_str = "[1920/01/01 1998/01/21 2024/12/31]";
const array = parseArrayDate(allocator, array_str);
defer array.deinit();
const expected_array: [3]DateTime = .{
DateTime.init(1920, 1, 1, 0, 0, 0, 0),
DateTime.init(1998, 1, 21, 0, 0, 0, 0),
DateTime.init(2024, 12, 31, 0, 0, 0, 0),
};
for (array.items, expected_array) |parsed, expected| {
try std.testing.expect(expected.compareDate(parsed));
}
}
test "Value parsing: Time" {
const allocator = std.testing.allocator;
const values: [4][]const u8 = .{ "12:45:00.0000", "18:12:53.7491", "02:30:10", "12:30" };
const expected_values: [4]DateTime = .{
DateTime.init(0, 0, 0, 12, 45, 0, 0),
DateTime.init(0, 0, 0, 18, 12, 53, 7491),
DateTime.init(0, 0, 0, 2, 30, 10, 0),
DateTime.init(0, 0, 0, 12, 30, 0, 0),
};
for (values, 0..) |value, i| {
try std.testing.expect(expected_values[i].compareTime(parseTime(value)));
}
// Time array
const array_str = "[12:45:00.0000 18:12:53.7491 02:30:10 12:30]";
const array = parseArrayTime(allocator, array_str);
defer array.deinit();
const expected_array: [4]DateTime = .{
DateTime.init(0, 0, 0, 12, 45, 0, 0),
DateTime.init(0, 0, 0, 18, 12, 53, 7491),
DateTime.init(0, 0, 0, 2, 30, 10, 0),
DateTime.init(0, 0, 0, 12, 30, 0, 0),
};
for (array.items, expected_array) |parsed, expected| {
try std.testing.expect(expected.compareTime(parsed));
}
}
test "Value parsing: Datetime" {
const allocator = std.testing.allocator;
const values: [4][]const u8 = .{ "1920/01/01-12:45:00.0000", "1920/01/01-18:12:53.7491", "1920/01/01-02:30:10", "1920/01/01-12:30" };
const expected_values: [4]DateTime = .{
DateTime.init(1920, 1, 1, 12, 45, 0, 0),
DateTime.init(1920, 1, 1, 18, 12, 53, 7491),
DateTime.init(1920, 1, 1, 2, 30, 10, 0),
DateTime.init(1920, 1, 1, 12, 30, 0, 0),
};
for (values, 0..) |value, i| {
try std.testing.expect(expected_values[i].compareDatetime(parseDatetime(value)));
}
// Time array
const array_str = "[1920/01/01-12:45:00.0000 1920/01/01-18:12:53.7491 1920/01/01-02:30:10 1920/01/01-12:30]";
const array = parseArrayDatetime(allocator, array_str);
defer array.deinit();
const expected_array: [4]DateTime = .{
DateTime.init(1920, 1, 1, 12, 45, 0, 0),
DateTime.init(1920, 1, 1, 18, 12, 53, 7491),
DateTime.init(1920, 1, 1, 2, 30, 10, 0),
DateTime.init(1920, 1, 1, 12, 30, 0, 0),
};
for (array.items, expected_array) |parsed, expected| {
try std.testing.expect(expected.compareDatetime(parsed));
}
}
+615 -141
View File
File diff suppressed because it is too large Load Diff