add config
This commit is contained in:
		
							parent
							
								
									f13471de46
								
							
						
					
					
						commit
						c393792cd1
					
				
					 2 changed files with 111 additions and 1 deletions
				
			
		
							
								
								
									
										107
									
								
								src/config.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/config.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | |||
| const Self = @This(); | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| allocator: std.mem.Allocator, | ||||
| 
 | ||||
| pub const ParsedConfig = struct { | ||||
|     key_value_map: *std.StringArrayHashMap([]u8), | ||||
|     value_key_map: *std.StringArrayHashMap(*std.ArrayList([]u8)), | ||||
| 
 | ||||
|     config_allocator: std.mem.Allocator, | ||||
|     const SelfConfig = @This(); | ||||
| 
 | ||||
|     var by_keys: std.StringArrayHashMap([]u8) = undefined; | ||||
|     var by_values: std.StringArrayHashMap(*std.ArrayList([]u8)) = undefined; | ||||
| 
 | ||||
|     pub fn init(config_allocator: std.mem.Allocator) SelfConfig { | ||||
|         by_keys = std.StringArrayHashMap([]u8).init(config_allocator); | ||||
|         by_values = std.StringArrayHashMap(*std.ArrayList([]u8)).init(config_allocator); | ||||
|         return SelfConfig{ | ||||
|             .config_allocator = config_allocator, | ||||
|             .key_value_map = &by_keys, | ||||
|             .value_key_map = &by_values, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     pub fn deinit(self: *SelfConfig) void { | ||||
|         for (self.key_value_map.keys(), self.key_value_map.values()) |k, v| { | ||||
|             self.config_allocator.free(k); // this is also the key in value_key_map | ||||
|             self.config_allocator.free(v); | ||||
|         } | ||||
| 
 | ||||
|         self.key_value_map.deinit(); | ||||
|         for (self.value_key_map.values()) |v| { | ||||
|             // The string in the value array is the same as the one in the key | ||||
|             // no need to free | ||||
|             // for (v.items) |item| { | ||||
|             //     self.config_allocator.free(item); | ||||
|             // } | ||||
|             v.deinit(); | ||||
|             self.config_allocator.destroy(v); | ||||
|         } | ||||
|         // These were already freed above | ||||
|         // for (self.value_key_map.keys()) |*k| { | ||||
|         //     self.config_allocator.free(k.*); | ||||
|         // } | ||||
|         self.value_key_map.deinit(); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| pub fn init(allocator: std.mem.Allocator) Self { | ||||
|     return Self{ | ||||
|         .allocator = allocator, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// Based on a reader, the return will be an ordered list of entries. The | ||||
| /// key is a path prefix, while the value is the library to use | ||||
| /// caller owns the allocated memory | ||||
| pub fn parse(self: Self, reader: anytype) !ParsedConfig { | ||||
|     const ws = " \t"; | ||||
|     var rc = ParsedConfig.init(self.allocator); | ||||
|     errdefer rc.deinit(); | ||||
|     while (try reader.readUntilDelimiterOrEofAlloc(self.allocator, '\n', std.math.maxInt(usize))) |line| { | ||||
|         defer self.allocator.free(line); | ||||
|         const nocomments = std.mem.trim(u8, @constCast(&std.mem.split(u8, line, "#")).first(), ws); | ||||
|         var data_iterator = std.mem.split(u8, nocomments, "="); | ||||
|         var key = std.mem.trim(u8, data_iterator.first(), ws); // first never fails | ||||
|         if (key.len == 0) continue; | ||||
|         var value = std.mem.trim(u8, data_iterator.next() orelse return error.NoValueForKey, ws); | ||||
|         // keys should be putNoClobber, but values can be put. | ||||
|         // Because we have to dup the memory here though, we want to | ||||
|         // manage duplicate values seperately | ||||
|         var dup_key = try self.allocator.dupe(u8, key); | ||||
|         var dup_value = try self.allocator.dupe(u8, value); | ||||
|         try rc.key_value_map.putNoClobber(dup_key, dup_value); | ||||
|         if (!rc.value_key_map.contains(value)) { | ||||
|             var keys = try self.allocator.create(std.ArrayList([]u8)); | ||||
|             keys.* = std.ArrayList([]u8).init(self.allocator); | ||||
|             try rc.value_key_map.put(dup_value, keys); | ||||
|         } | ||||
|         try rc.value_key_map.get(value).?.append(dup_key); | ||||
|     } | ||||
|     return rc; | ||||
| } | ||||
| 
 | ||||
| test "gets config from a stream" { | ||||
|     var allocator = std.testing.allocator; | ||||
|     var stream = std.io.fixedBufferStream( | ||||
|         \\# This is a simple "path prefix" = dynamic library path mapping | ||||
|         \\  # no reordering will be done, so you must do things most -> least specific | ||||
|         \\    | ||||
|         \\ | ||||
|         \\foo =     bar | ||||
|         \\ | ||||
|         \\baz= qux# what *is* this? | ||||
|         \\ | ||||
|         \\ | ||||
|         \\bar =foo | ||||
|         \\qaz=foo | ||||
|     ); | ||||
| 
 | ||||
|     var config = try Self.init(allocator).parse(stream.reader()); | ||||
|     defer config.deinit(); | ||||
|     try std.testing.expectEqual(@as(usize, 4), config.key_value_map.keys().len); | ||||
|     try std.testing.expectEqual(@as(usize, 3), config.value_key_map.keys().len); | ||||
|     try std.testing.expectEqual(@as(usize, 2), config.value_key_map.get("foo").?.items.len); | ||||
| } | ||||
|  | @ -2,10 +2,11 @@ const std = @import("std"); | |||
| const builtin = @import("builtin"); | ||||
| const interface = @import("interface.zig"); | ||||
| const Watch = @import("Watch.zig"); | ||||
| const config = @import("config.zig"); | ||||
| 
 | ||||
| const serveFn = *const fn (*interface.Request) ?*interface.Response; | ||||
| const zigInitFn = *const fn (*anyopaque) void; | ||||
| const requestDeinitFn = *const fn () void; | ||||
| 
 | ||||
| const timeout = 250; | ||||
| 
 | ||||
| const FullReturn = struct { | ||||
|  | @ -414,6 +415,8 @@ test { | |||
|     // `@This()` is a builtin function that returns the innermost container it is called from. | ||||
|     // In this example, the innermost container is this file (implicitly a struct). | ||||
|     std.testing.refAllDecls(@This()); | ||||
|     std.testing.refAllDecls(config); | ||||
|     std.testing.refAllDecls(interface); | ||||
| } | ||||
| var test_resp_buf: [1024]u8 = undefined; | ||||
| var test_resp_buf_len: usize = undefined; | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue