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:
@@ -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
File diff suppressed because it is too large
Load Diff
+6
-5
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -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', ' ');
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user