vlean up geocache
This commit is contained in:
parent
e8e31b25e8
commit
7dc4336474
1 changed files with 129 additions and 30 deletions
|
|
@ -88,6 +88,34 @@ pub fn saveIfNeeded(self: *GeoCache) void {
|
|||
self.last_save = now;
|
||||
}
|
||||
|
||||
fn load(allocator: std.mem.Allocator, cache: *std.StringHashMap(CachedLocation), content: []const u8) !void {
|
||||
const CacheData = struct {
|
||||
name: []const u8,
|
||||
latitude: f64,
|
||||
longitude: f64,
|
||||
};
|
||||
|
||||
const parsed = try std.json.parseFromSlice(
|
||||
std.json.ArrayHashMap(CacheData),
|
||||
allocator,
|
||||
content,
|
||||
.{},
|
||||
);
|
||||
defer parsed.deinit();
|
||||
|
||||
for (parsed.value.map.keys(), parsed.value.map.values()) |key, value| {
|
||||
const cache_key = try allocator.dupe(u8, key);
|
||||
const cache_value = CachedLocation{
|
||||
.name = try allocator.dupe(u8, value.name),
|
||||
.coords = .{
|
||||
.latitude = value.latitude,
|
||||
.longitude = value.longitude,
|
||||
},
|
||||
};
|
||||
try cache.put(cache_key, cache_value);
|
||||
}
|
||||
}
|
||||
|
||||
fn loadFromFile(allocator: std.mem.Allocator, cache: *std.StringHashMap(CachedLocation), file_path: []const u8) !void {
|
||||
const file = try std.fs.cwd().openFile(file_path, .{});
|
||||
defer file.close();
|
||||
|
|
@ -95,37 +123,10 @@ fn loadFromFile(allocator: std.mem.Allocator, cache: *std.StringHashMap(CachedLo
|
|||
const content = try file.readToEndAlloc(allocator, 10 * 1024 * 1024); // 10MB max
|
||||
defer allocator.free(content);
|
||||
|
||||
const parsed = try std.json.parseFromSlice(
|
||||
std.json.Value,
|
||||
allocator,
|
||||
content,
|
||||
.{},
|
||||
);
|
||||
defer parsed.deinit();
|
||||
|
||||
var it = parsed.value.object.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const obj = entry.value_ptr.object;
|
||||
const key = try allocator.dupe(u8, entry.key_ptr.*);
|
||||
const value = CachedLocation{
|
||||
.name = try allocator.dupe(u8, obj.get("name").?.string),
|
||||
.coords = .{
|
||||
.latitude = obj.get("latitude").?.float,
|
||||
.longitude = obj.get("longitude").?.float,
|
||||
},
|
||||
};
|
||||
try cache.put(key, value);
|
||||
}
|
||||
try load(allocator, cache, content);
|
||||
}
|
||||
|
||||
fn saveToFile(self: *GeoCache, file_path: []const u8) !void {
|
||||
const file = try std.fs.cwd().createFile(file_path, .{});
|
||||
defer file.close();
|
||||
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var file_writer = file.writer(&buffer);
|
||||
const writer = &file_writer.interface;
|
||||
|
||||
fn save(self: *GeoCache, writer: *std.Io.Writer) !void {
|
||||
try writer.writeAll("{\n");
|
||||
|
||||
var it = self.cache.iterator();
|
||||
|
|
@ -134,7 +135,7 @@ fn saveToFile(self: *GeoCache, file_path: []const u8) !void {
|
|||
if (!first) try writer.writeAll(",\n");
|
||||
first = false;
|
||||
|
||||
try writer.print(" {any}: {any}", .{
|
||||
try writer.print(" {f}: {f}", .{
|
||||
std.json.fmt(entry.key_ptr.*, .{}),
|
||||
std.json.fmt(.{
|
||||
.name = entry.value_ptr.name,
|
||||
|
|
@ -145,6 +146,17 @@ fn saveToFile(self: *GeoCache, file_path: []const u8) !void {
|
|||
}
|
||||
|
||||
try writer.writeAll("\n}\n");
|
||||
}
|
||||
|
||||
fn saveToFile(self: *GeoCache, file_path: []const u8) !void {
|
||||
const file = try std.fs.cwd().createFile(file_path, .{});
|
||||
defer file.close();
|
||||
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var file_writer = file.writer(&buffer);
|
||||
const writer = &file_writer.interface;
|
||||
|
||||
try self.save(writer);
|
||||
try writer.flush();
|
||||
}
|
||||
|
||||
|
|
@ -176,3 +188,90 @@ test "GeoCache miss returns null" {
|
|||
const result = cache.get("NonExistent");
|
||||
try std.testing.expect(result == null);
|
||||
}
|
||||
|
||||
test "save produces valid JSON" {
|
||||
const allocator = std.testing.allocator;
|
||||
var cache = try GeoCache.init(allocator, null);
|
||||
defer cache.deinit();
|
||||
|
||||
try cache.put("London", .{
|
||||
.name = "London, UK",
|
||||
.coords = .{ .latitude = 51.5074, .longitude = -0.1278 },
|
||||
});
|
||||
try cache.put("Paris", .{
|
||||
.name = "Paris, France",
|
||||
.coords = .{ .latitude = 48.8566, .longitude = 2.3522 },
|
||||
});
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var writer = std.Io.Writer.fixed(&buffer);
|
||||
try cache.save(&writer);
|
||||
|
||||
const output = buffer[0..writer.end];
|
||||
try std.testing.expect(std.mem.indexOf(u8, output, "London") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, output, "Paris") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, output, "51.5074") != null);
|
||||
}
|
||||
|
||||
test "load parses valid JSON" {
|
||||
const allocator = std.testing.allocator;
|
||||
var cache_map = std.StringHashMap(CachedLocation).init(allocator);
|
||||
defer {
|
||||
var it = cache_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
allocator.free(entry.key_ptr.*);
|
||||
allocator.free(entry.value_ptr.name);
|
||||
}
|
||||
cache_map.deinit();
|
||||
}
|
||||
|
||||
const json =
|
||||
\\{
|
||||
\\ "London": {"name": "London, UK", "latitude": 51.5074, "longitude": -0.1278},
|
||||
\\ "Paris": {"name": "Paris, France", "latitude": 48.8566, "longitude": 2.3522}
|
||||
\\}
|
||||
;
|
||||
|
||||
try load(allocator, &cache_map, json);
|
||||
|
||||
const london = cache_map.get("London");
|
||||
try std.testing.expect(london != null);
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 51.5074), london.?.coords.latitude, 0.0001);
|
||||
|
||||
const paris = cache_map.get("Paris");
|
||||
try std.testing.expect(paris != null);
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 48.8566), paris.?.coords.latitude, 0.0001);
|
||||
}
|
||||
|
||||
test "save and load round-trip" {
|
||||
const allocator = std.testing.allocator;
|
||||
var cache1 = try GeoCache.init(allocator, null);
|
||||
defer cache1.deinit();
|
||||
|
||||
try cache1.put("Berlin", .{
|
||||
.name = "Berlin, Germany",
|
||||
.coords = .{ .latitude = 52.5200, .longitude = 13.4050 },
|
||||
});
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var writer = std.Io.Writer.fixed(&buffer);
|
||||
try cache1.save(&writer);
|
||||
|
||||
var cache2 = std.StringHashMap(CachedLocation).init(allocator);
|
||||
defer {
|
||||
var it = cache2.iterator();
|
||||
while (it.next()) |entry| {
|
||||
allocator.free(entry.key_ptr.*);
|
||||
allocator.free(entry.value_ptr.name);
|
||||
}
|
||||
cache2.deinit();
|
||||
}
|
||||
|
||||
try load(allocator, &cache2, buffer[0..writer.end]);
|
||||
|
||||
const berlin = cache2.get("Berlin");
|
||||
try std.testing.expect(berlin != null);
|
||||
try std.testing.expectEqualStrings("Berlin, Germany", berlin.?.name);
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 52.5200), berlin.?.coords.latitude, 0.0001);
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 13.4050), berlin.?.coords.longitude, 0.0001);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue