Skip to content

Commit

Permalink
feat: server connection configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
sectasy0 committed Dec 12, 2023
1 parent 3c7e263 commit fdc7ad9
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ echo "*2\r\n\$3\r\nGET\r\n\$9\r\nmycounter\r\n" | netcat -N localhost 7556
- [ ] Client side library.

## Release History
* unreleased
* Ability to configure server listen address and port from `zcached.conf` file.
* 0.0.1
* Initial release

Expand Down
8 changes: 7 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
const std = @import("std");

const server = @import("server/listener.zig");
const Config = @import("server/config.zig").Config;
const storage = @import("storage.zig");

pub fn main() void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var allocator = gpa.allocator();

const config = Config.load(allocator) catch |err| {
std.log.err("failed to load config: {}", .{err});
return;
};

var mem_storage = storage.MemoryStorage.init(allocator);
defer mem_storage.deinit();

Expand All @@ -17,7 +23,7 @@ pub fn main() void {
};
defer thread_pool.deinit();

const listen_addr = std.net.Address.parseIp("127.0.0.1", 7556) catch |err| {
const listen_addr = std.net.Address.parseIp(config.address, config.port) catch |err| {
std.log.err("failed to parse address: {}", .{err});
return;
};
Expand Down
103 changes: 103 additions & 0 deletions src/server/config.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
const std = @import("std");

const FILENAME = "zcached.conf";

pub const Config = struct {
address: []const u8 = @constCast("127.0.0.1"),
port: u16 = 7556,

_arena: std.heap.ArenaAllocator,

pub fn deinit(config: *Config) void {
config._arena.deinit();
}

pub fn load(allocator: std.mem.Allocator) !Config {
var config = Config{ ._arena = std.heap.ArenaAllocator.init(allocator) };

const file = std.fs.cwd().openFile(FILENAME, .{ .mode = .read_only }) catch |err| {
// if the file doesn't exist, just return the default config
if (err == error.FileNotFound) return config;
return err;
};
defer file.close();

const file_size = (try file.stat()).size;
var buffer = try config._arena.allocator().alloc(u8, file_size);
defer config._arena.allocator().free(buffer);

const readed_size = try file.read(buffer);
if (readed_size != file_size) return error.InvalidInput;

var iter = std.mem.split(u8, buffer, "\n");
while (iter.next()) |line| {
// # is comment, _ is for internal use, like _allocator
if (line.len == 0 or line[0] == '#' or line[0] == '_') continue;

const key_value = try process_line(config._arena.allocator(), line);
defer key_value.deinit();

try assign_field_value(&config, key_value);
}

return config;
}

fn process_line(allocator: std.mem.Allocator, line: []const u8) !std.ArrayList([]const u8) {
var result = std.ArrayList([]const u8).init(allocator);

var iter = std.mem.split(u8, line, "=");
const key = iter.next();
const value = iter.next();
if (key == null or value == null) return error.InvalidInput;

try result.append(key.?);
try result.append(value.?);

return result;
}

fn assign_field_value(config: *Config, key_value: std.ArrayList([]const u8)) !void {
// I don't like how many nested things are here, but there is no other way
inline for (std.meta.fields(Config)) |field| {
if (std.mem.eql(u8, field.name, key_value.items[0])) {
var value = try config._arena.allocator().alloc(u8, key_value.items[1].len);
std.mem.copy(u8, value, key_value.items[1]);

switch (field.type) {
u16 => {
const parsed = try std.fmt.parseInt(u16, value, 10);
@field(config, field.name) = parsed;
},
[]const u8 => @field(config, field.name) = value,
else => unreachable,
}
}
}
}
};

test "config default values" {
var config = try Config.load(std.testing.allocator);
defer config.deinit();

try std.testing.expectEqualStrings(config.address, "127.0.0.1");
try std.testing.expectEqual(config.port, 7556);
}

test "config load" {
const file_content = "address=192.16.0.1\nport=1234\n";
// create file
const file = try std.fs.cwd().createFile(FILENAME, .{});
try file.writeAll(file_content);
defer file.close();

var config = try Config.load(std.testing.allocator);
defer config.deinit();

try std.testing.expectEqualStrings(config.address, "192.16.0.1");
try std.testing.expectEqual(config.port, 1234);

// delete file
try std.fs.cwd().deleteFile(FILENAME);
}
1 change: 1 addition & 0 deletions tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ comptime {
_ = @import("src/server/cmd_handler.zig");
_ = @import("src/server/err_handler.zig");
_ = @import("src/server/listener.zig");
_ = @import("src/server/config.zig");
}
2 changes: 2 additions & 0 deletions zcached.conf.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
address=192.168.0.1
port=5555

0 comments on commit fdc7ad9

Please sign in to comment.