diff --git a/src/fileEngine.zig b/src/fileEngine.zig index 008c146..ceb5b1a 100644 --- a/src/fileEngine.zig +++ b/src/fileEngine.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const utils = @import("stuffs/utils.zig"); const Allocator = std.mem.Allocator; const UUID = @import("types/uuid.zig").UUID; const DataType = @import("types/dataType.zig").DataType; @@ -31,23 +32,14 @@ pub const FileEngine = struct { null_terminated_schema_buff: [:0]u8, struct_array: std.ArrayList(SchemaStruct), - pub fn init(allocator: Allocator, path: []const u8) !FileEngine { + pub fn init(allocator: Allocator, path: []const u8) FileEngine { const path_to_ZipponDB_dir = path; - var schema_buf = std.ArrayList(u8).init(allocator); - defer schema_buf.deinit(); + var schema_buf = allocator.alloc(u8, 1024 * 50) catch @panic("Cant allocate the schema buffer"); + defer allocator.free(schema_buf); - const schema_path = try std.fmt.allocPrint(allocator, "{s}/schema.zipponschema", .{path_to_ZipponDB_dir}); - defer allocator.free(schema_path); - - const file = try std.fs.cwd().openFile(schema_path, .{}); - defer file.close(); - - const stat = try file.stat(); - const buff = try file.readToEndAlloc(allocator, stat.size); - - const null_terminated_schema_buff = try allocator.dupeZ(u8, buff); - allocator.free(buff); + const len: usize = FileEngine.readSchemaFile(allocator, path_to_ZipponDB_dir, schema_buf) catch 0; + const null_terminated_schema_buff = allocator.dupeZ(u8, schema_buf[0..len]) catch @panic("Cant allocate null term buffer for the schema"); var toker = SchemaTokenizer.init(null_terminated_schema_buff); var parser = SchemaParser.init(&toker, allocator); @@ -65,7 +57,9 @@ pub const FileEngine = struct { } pub fn deinit(self: *FileEngine) void { - for (self.struct_array.items) |*elem| elem.deinit(); + if (self.struct_array.items.len > 0) { + for (self.struct_array.items) |*elem| elem.deinit(); + } self.struct_array.deinit(); self.allocator.free(self.null_terminated_schema_buff); self.allocator.free(self.path_to_ZipponDB_dir); @@ -101,6 +95,17 @@ pub const FileEngine = struct { // --------------------Other-------------------- + pub fn readSchemaFile(allocator: Allocator, sub_path: []const u8, buffer: []u8) !usize { + const path = try std.fmt.allocPrint(allocator, "{s}/schema.zipponschema", .{sub_path}); + defer allocator.free(path); + + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + + const len = try file.readAll(buffer); + return len; + } + pub fn writeDbMetrics(self: *FileEngine, buffer: *std.ArrayList(u8)) !void { const path = try std.fmt.allocPrint(self.allocator, "{s}", .{self.path_to_ZipponDB_dir}); defer self.allocator.free(path); @@ -109,26 +114,26 @@ pub const FileEngine = struct { const writer = buffer.writer(); try writer.print("Database path: {s}\n", .{path}); - const main_size = try self.getDirTotalSize(main_dir); + const main_size = try utils.getDirTotalSize(main_dir); try writer.print("Total size: {d:.2}Mb\n", .{@as(f64, @floatFromInt(main_size)) / 1e6}); const log_dir = try main_dir.openDir("LOG", .{ .iterate = true }); - const log_size = try self.getDirTotalSize(log_dir); + const log_size = try utils.getDirTotalSize(log_dir); try writer.print("LOG: {d:.2}Mb\n", .{@as(f64, @floatFromInt(log_size)) / 1e6}); const backup_dir = try main_dir.openDir("BACKUP", .{ .iterate = true }); - const backup_size = try self.getDirTotalSize(backup_dir); + const backup_size = try utils.getDirTotalSize(backup_dir); try writer.print("BACKUP: {d:.2}Mb\n", .{@as(f64, @floatFromInt(backup_size)) / 1e6}); const data_dir = try main_dir.openDir("DATA", .{ .iterate = true }); - const data_size = try self.getDirTotalSize(data_dir); + const data_size = try utils.getDirTotalSize(data_dir); try writer.print("DATA: {d:.2}Mb\n", .{@as(f64, @floatFromInt(data_size)) / 1e6}); var iter = data_dir.iterate(); while (try iter.next()) |entry| { if (entry.kind != .directory) continue; const sub_dir = try data_dir.openDir(entry.name, .{ .iterate = true }); - const size = try self.getDirTotalSize(sub_dir); + const size = try utils.getDirTotalSize(sub_dir); try writer.print(" {s}: {d:.}Mb\n", .{ entry.name, @as(f64, @floatFromInt(size)) / 1e6 }); } } @@ -136,9 +141,9 @@ pub const FileEngine = struct { // --------------------Init folder and files-------------------- /// Create the main folder. Including DATA, LOG and BACKUP - pub fn checkAndCreateDirectories(sub_path: []const u8, allocator: Allocator) !void { - var path_buff = try std.fmt.allocPrint(allocator, "{s}", .{sub_path}); - defer allocator.free(path_buff); + pub fn checkAndCreateDirectories(self: *FileEngine) !void { + var path_buff = try std.fmt.allocPrint(self.allocator, "{s}", .{self.path_to_ZipponDB_dir}); + defer self.allocator.free(path_buff); const cwd = std.fs.cwd(); @@ -147,24 +152,24 @@ pub const FileEngine = struct { else => return err, }; - allocator.free(path_buff); - path_buff = try std.fmt.allocPrint(allocator, "{s}/DATA", .{sub_path}); + self.allocator.free(path_buff); + path_buff = try std.fmt.allocPrint(self.allocator, "{s}/DATA", .{self.path_to_ZipponDB_dir}); cwd.makeDir(path_buff) catch |err| switch (err) { error.PathAlreadyExists => {}, else => return err, }; - allocator.free(path_buff); - path_buff = try std.fmt.allocPrint(allocator, "{s}/BACKUP", .{sub_path}); + self.allocator.free(path_buff); + path_buff = try std.fmt.allocPrint(self.allocator, "{s}/BACKUP", .{self.path_to_ZipponDB_dir}); cwd.makeDir(path_buff) catch |err| switch (err) { error.PathAlreadyExists => {}, else => return err, }; - allocator.free(path_buff); - path_buff = try std.fmt.allocPrint(allocator, "{s}/LOG", .{sub_path}); + self.allocator.free(path_buff); + path_buff = try std.fmt.allocPrint(self.allocator, "{s}/LOG", .{self.path_to_ZipponDB_dir}); cwd.makeDir(path_buff) catch |err| switch (err) { error.PathAlreadyExists => {}, @@ -979,7 +984,7 @@ test "Get list of UUID using condition" { const allocator = std.testing.allocator; const path = try allocator.dupe(u8, "ZipponDB"); - var file_engine = try FileEngine.init(allocator, path); + var file_engine = FileEngine.init(allocator, path); defer file_engine.deinit(); var uuid_array = std.ArrayList(UUID).init(allocator); diff --git a/src/main.zig b/src/main.zig index 1396d08..3619451 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,7 +6,7 @@ const cliToken = @import("tokenizers/cli.zig").Token; const ziqlTokenizer = @import("tokenizers/ziql.zig").Tokenizer; const ziqlToken = @import("tokenizers/ziql.zig").Token; const ziqlParser = @import("ziqlParser.zig").Parser; -const utils = @import("utils.zig"); +const utils = @import("stuffs/utils.zig"); const send = @import("stuffs/utils.zig").send; const State = enum { @@ -41,202 +41,214 @@ pub fn main() !void { var to_init = true; _ = 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 = try FileEngine.init(allocator, try allocator.dupe(u8, "")); + file_engine = FileEngine.init(allocator, try allocator.dupe(u8, "")); to_init = true; }; if (to_init) { - file_engine = try FileEngine.init(allocator, path_env_variable.?); - try file_engine.checkAndCreateDirectories(path, allocator); + file_engine = FileEngine.init(allocator, path_env_variable.?); + try file_engine.checkAndCreateDirectories(); } } else { - file_engine = try FileEngine.init(allocator, try allocator.dupe(u8, "")); + file_engine = FileEngine.init(allocator, try allocator.dupe(u8, "")); std.debug.print("No ZIPONDB_PATH environment variable found, please use the command:\n db use path/to/db \nor\n db new /path/to/dir\n", .{}); } - const args_buffer = utils.getArgsString(allocator); - defer args_buffer.deinit(); + const line_buf = try allocator.alloc(u8, 1024 * 50); // TODO: Remove the size limitation + defer allocator.free(line_buf); - var toker = cliTokenizer.init(args_buffer.items); - var token = toker.next(); - state = .expect_main_command; + while (true) { + std.debug.print("> ", .{}); + const line = try std.io.getStdIn().reader().readUntilDelimiterOrEof(line_buf, '\n'); - while ((state != .end) and (state != .quit)) : (token = toker.next()) switch (state) { - .expect_main_command => switch (token.tag) { - .keyword_run => { - if (!file_engine.usable) { - send("Error: No database selected. Please use db new or db use.", .{}); - state = .end; - continue; - } - state = .expect_query; - }, - .keyword_db => state = .expect_db_command, - .keyword_schema => { - if (!file_engine.usable) { - send("Error: No database selected. Please use db new or db use.", .{}); - state = .end; - continue; - } - state = .expect_schema_command; - }, - .keyword_help => { - send("{s}", .{ - \\Welcome to ZipponDB v0.1.1! - \\ - \\Available commands: - \\run To run a query. - \\db Create or chose a database. - \\schema Initialize the database schema. - \\quit Stop the CLI with memory safety. - \\ - \\ For more informations: https://github.com/MrBounty/ZipponDB - \\ - }); - state = .end; - }, - .keyword_quit => state = .quit, - .eof => state = .end, - else => { - send("Command need to start with a keyword, including: run, db, schema, help and quit", .{}); - state = .end; - }, - }, + if (line) |line_str| { + const null_term_line_str = try allocator.dupeZ(u8, line_str[0..line_str.len]); + defer allocator.free(null_term_line_str); - .expect_db_command => switch (token.tag) { - .keyword_new => state = .expect_path_to_new_db, - .keyword_use => state = .expect_path_to_db, - .keyword_metrics => { - if (!file_engine.usable) { - send("Error: No database selected. Please use db new or db use.", .{}); - state = .end; - continue; - } + var toker = cliTokenizer.init(null_term_line_str); + var token = toker.next(); + state = .expect_main_command; - var buffer = std.ArrayList(u8).init(allocator); - defer buffer.deinit(); + while ((state != .end) and (state != .quit)) : (token = toker.next()) switch (state) { + .expect_main_command => switch (token.tag) { + .keyword_run => { + if (!file_engine.usable) { + send("Error: No database selected. Please use db new or db use.", .{}); + state = .end; + continue; + } + state = .expect_query; + }, + .keyword_db => state = .expect_db_command, + .keyword_schema => { + if (!file_engine.usable) { + send("Error: No database selected. Please use db new or db use.", .{}); + state = .end; + continue; + } + state = .expect_schema_command; + }, + .keyword_help => { + send("{s}", .{ + \\Welcome to ZipponDB v0.1.1! + \\ + \\Available commands: + \\run To run a query. + \\db Create or chose a database. + \\schema Initialize the database schema. + \\quit Stop the CLI with memory safety. + \\ + \\ For more informations: https://github.com/MrBounty/ZipponDB + \\ + }); + state = .end; + }, + .keyword_quit => state = .quit, + .eof => state = .end, + else => { + send("Command need to start with a keyword, including: run, db, schema, help and quit", .{}); + state = .end; + }, + }, - try file_engine.writeDbMetrics(&buffer); - send("{s}", .{buffer.items}); - state = .end; - }, - .keyword_help => { - send("{s}", .{ - \\Available commands: - \\new Create a new database using a path to a sub folder. - \\use Select another ZipponDB folder to use as database. - \\metrics Print some metrics of the current database. - \\ - \\ For more informations: https://github.com/MrBounty/ZipponDB - \\ - }); - state = .end; - }, - else => { - send("Error: db commands available: new, metrics, swap & help", .{}); - state = .end; - }, - }, + .expect_db_command => switch (token.tag) { + .keyword_new => state = .expect_path_to_new_db, + .keyword_use => state = .expect_path_to_db, + .keyword_metrics => { + if (!file_engine.usable) { + send("Error: No database selected. Please use db new or db use.", .{}); + state = .end; + continue; + } - .expect_path_to_db => switch (token.tag) { - .identifier => { - file_engine.deinit(); - file_engine = try FileEngine.init(allocator, try allocator.dupe(u8, toker.getTokenSlice(token))); - send("Successfully started using the database!", .{}); - state = .end; - }, - else => { - send("Error Expect a path to a ZipponDB folder.", .{}); - state = .end; - }, - }, + var buffer = std.ArrayList(u8).init(allocator); + defer buffer.deinit(); - .expect_path_to_new_db => switch (token.tag) { - .identifier => { - file_engine.checkAndCreateDirectories(toker.getTokenSlice(token), allocator) catch |err| { - send("Error: Coulnt create database directories: {any}", .{err}); - state = .end; - continue; - }; - file_engine.deinit(); - file_engine = FileEngine.init(allocator, try allocator.dupe(u8, toker.getTokenSlice(token))); - send("Successfully initialized the database!", .{}); - state = .end; - }, - else => { - send("Error Expect a path to a folder.", .{}); - state = .end; - }, - }, - - .expect_query => switch (token.tag) { - .string_literal => { - const null_term_query_str = try allocator.dupeZ(u8, toker.buffer[token.loc.start + 1 .. token.loc.end - 1]); - defer allocator.free(null_term_query_str); - runQuery(null_term_query_str, &file_engine); - state = .end; - }, - .keyword_help => { - send("The run command take a ZiQL query between \" and run it. eg: run \"GRAB User\"", .{}); - state = .end; - }, - else => { - send("Error: After command run, need a query, eg: \"GRAB User\"", .{}); - state = .end; - }, - }, - - .expect_schema_command => switch (token.tag) { - .keyword_describe => { - if (std.mem.eql(u8, file_engine.path_to_ZipponDB_dir, "")) send("Error: No database selected. Please use db bew or db use.", .{}); - - if (file_engine.null_terminated_schema_buff.len == 0) { - send("Need to init the schema first. Please use the schema init path/to/schema command to start.", .{}); - } else { - send("Schema:\n {s}", .{file_engine.null_terminated_schema_buff}); - } - state = .end; - }, - .keyword_init => state = .expect_path_to_schema, - .keyword_help => { - send("{s}", .{ - \\Available commands: - \\describe Print the schema use by the currently selected database. - \\init Take the path to a schema file and initialize the database. - \\ - \\ For more informations: https://github.com/MrBounty/ZipponDB - \\ - }); - state = .end; - }, - else => { - send("Error: schema commands available: describe, init & help", .{}); - state = .end; - }, - }, - - .expect_path_to_schema => switch (token.tag) { - .identifier => { - file_engine.initDataFolder(toker.getTokenSlice(token)) catch |err| switch (err) { - error.SchemaFileNotFound => { - send("Coulnt find the schema file at {s}", .{toker.getTokenSlice(token)}); + try file_engine.writeDbMetrics(&buffer); + send("{s}", .{buffer.items}); + state = .end; + }, + .keyword_help => { + send("{s}", .{ + \\Available commands: + \\new Create a new database using a path to a sub folder. + \\use Select another ZipponDB folder to use as database. + \\metrics Print some metrics of the current database. + \\ + \\ For more informations: https://github.com/MrBounty/ZipponDB + \\ + }); state = .end; }, else => { - send("Error initializing the schema", .{}); + send("Error: db commands available: new, metrics, swap & help", .{}); state = .end; }, - }; - send("Successfully initialized the database schema!", .{}); - state = .end; - }, - else => { - send("Error: Expect path to schema file.", .{}); - state = .end; - }, - }, + }, - .quit, .end => break, - }; + .expect_path_to_db => switch (token.tag) { + .identifier => { + file_engine.deinit(); + file_engine = FileEngine.init(allocator, try allocator.dupe(u8, toker.getTokenSlice(token))); + send("Successfully started using the database!", .{}); + state = .end; + }, + else => { + send("Error Expect a path to a ZipponDB folder.", .{}); + state = .end; + }, + }, + + .expect_path_to_new_db => switch (token.tag) { + .identifier => { + file_engine.deinit(); + file_engine = FileEngine.init(allocator, try allocator.dupe(u8, toker.getTokenSlice(token))); + file_engine.checkAndCreateDirectories() catch |err| { + send("Error: Coulnt create database directories: {any}", .{err}); + state = .end; + continue; + }; + send("Successfully initialized the database!", .{}); + state = .end; + }, + else => { + send("Error Expect a path to a folder.", .{}); + state = .end; + }, + }, + + .expect_query => switch (token.tag) { + .string_literal => { + const null_term_query_str = try allocator.dupeZ(u8, toker.buffer[token.loc.start + 1 .. token.loc.end - 1]); + defer allocator.free(null_term_query_str); + runQuery(null_term_query_str, &file_engine); + state = .end; + }, + .keyword_help => { + send("The run command take a ZiQL query between \" and run it. eg: run \"GRAB User\"", .{}); + state = .end; + }, + else => { + send("Error: After command run, need a query, eg: \"GRAB User\"", .{}); + state = .end; + }, + }, + + .expect_schema_command => switch (token.tag) { + .keyword_describe => { + if (std.mem.eql(u8, file_engine.path_to_ZipponDB_dir, "")) send("Error: No database selected. Please use db bew or db use.", .{}); + + if (file_engine.null_terminated_schema_buff.len == 0) { + send("Need to init the schema first. Please use the schema init path/to/schema command to start.", .{}); + } else { + send("Schema:\n {s}", .{file_engine.null_terminated_schema_buff}); + } + state = .end; + }, + .keyword_init => state = .expect_path_to_schema, + .keyword_help => { + send("{s}", .{ + \\Available commands: + \\describe Print the schema use by the currently selected database. + \\init Take the path to a schema file and initialize the database. + \\ + \\ For more informations: https://github.com/MrBounty/ZipponDB + \\ + }); + state = .end; + }, + else => { + send("Error: schema commands available: describe, init & help", .{}); + state = .end; + }, + }, + + .expect_path_to_schema => switch (token.tag) { + .identifier => { + file_engine.initDataFolder(toker.getTokenSlice(token)) catch |err| switch (err) { + error.SchemaFileNotFound => { + send("Coulnt find the schema file at {s}", .{toker.getTokenSlice(token)}); + state = .end; + }, + else => { + send("Error initializing the schema", .{}); + state = .end; + }, + }; + send("Successfully initialized the database schema!", .{}); + state = .end; + }, + else => { + send("Error: Expect path to schema file.", .{}); + state = .end; + }, + }, + + .quit, .end => unreachable, + }; + + if (state == .quit) break; + } + } } pub fn runQuery(null_term_query_str: [:0]const u8, file_engine: *FileEngine) void { diff --git a/src/ziqlParser.zig b/src/ziqlParser.zig index 366de3c..b9c4be2 100644 --- a/src/ziqlParser.zig +++ b/src/ziqlParser.zig @@ -783,7 +783,7 @@ fn testParsing(source: [:0]const u8) !void { const allocator = std.testing.allocator; const path = try allocator.dupe(u8, "ZipponDB"); - var file_engine = try FileEngine.init(allocator, path); + var file_engine = FileEngine.init(allocator, path); defer file_engine.deinit(); var tokenizer = Tokenizer.init(source); @@ -797,7 +797,7 @@ fn expectParsingError(source: [:0]const u8, err: ZiQlParserError) !void { const allocator = std.testing.allocator; const path = try allocator.dupe(u8, "ZipponDB"); - var file_engine = try FileEngine.init(allocator, path); + var file_engine = FileEngine.init(allocator, path); defer file_engine.deinit(); var tokenizer = Tokenizer.init(source);