const std = @import("std"); const Allocator = std.mem.Allocator; const net = std.net; const expect = std.testing.expect; const http = @import("./http.zig"); pub const Location = struct { lat: f64, lon: f64, }; pub const DailyDataSchema = struct { latitude: f64, longitude: f64, generationtime_ms: f64, utc_offset_seconds: i64, timezone: []u8, timezone_abbreviation: []u8, elevation: f64, hourly_units: struct{ time: []u8, temperature_2m: []u8, }, hourly: struct { time: [][]u8, temperature_2m: []f64, }, }; pub const DailyData = struct{ data: DailyDataSchema, allocator: std.mem.Allocator, const Self = @This(); pub fn parse(allocator: Allocator, body: []u8) !Self{ var stream = std.json.TokenStream.init(body); return Self{ .data = try std.json.parse(DailyDataSchema, &stream, .{ .allocator = allocator }), .allocator = allocator, }; } pub fn deinit(self: *Self) void{ std.json.parseFree(DailyDataSchema, self.data, .{ .allocator = self.allocator, }); } }; /// Gets Daily Data. Allocates and returns an DailyData struct that must be /// .deinit() later pub fn getDailyData(allocator: std.mem.Allocator, location: Location) !DailyData { const stream = try net.tcpConnectToHost(allocator, "api.open-meteo.com", 80); defer stream.close(); const writer = stream.writer(); const request = try std.fmt.allocPrint(allocator, "GET /v1/forecast" ++ "?latitude={d}" ++ "&longitude={d}" ++ "&hourly=temperature_2m" ++ " HTTP/1.1\r\n" ++ "Host: api.open-meteo.com\r\n" ++ "Connection: keep-alive\r\n" ++ "Accept-Encoding: identity\r\n" ++ "\r\n", .{location.lat, location.lon}); defer allocator.free(request); try writer.writeAll(request); const reader = stream.reader(); var response = try http.HttpResponseParser.init(allocator); defer response.deinit(); try response.parseReader(reader); return try DailyData.parse(allocator, response.body()); } test "Parse a daily data block correctly with DailyDataSchema" { const allocator = std.testing.allocator; const body = \\ {"latitude":52.52,"longitude":13.419998,"generationtime_ms":0.4259347915649414, \\ "utc_offset_seconds":0,"timezone":"GMT","timezone_abbreviation":"GMT", \\ "elevation":38.0,"hourly_units": \\ {"time":"iso8601","temperature_2m":"°C"}, \\ "hourly":{"time":["2023-04-09T00:00","2023-04-09T01:00"],"temperature_2m":[4.3,3.6]}} ; var stream = std.json.TokenStream.init(body); const parsedData = try std.json.parse(DailyDataSchema, &stream, .{ .allocator = allocator }); defer std.json.parseFree(DailyDataSchema, parsedData, .{ .allocator = allocator }); try expect( std.mem.eql(u8, parsedData.timezone_abbreviation, "GMT") ); try expect( std.mem.eql(u8, parsedData.hourly.time[0], "2023-04-09T00:00") ); } test "Get data properly and parse it (requires access to open-meteo)" { const allocator = std.testing.allocator; var daily = try getDailyData(allocator); defer daily.deinit(); try expect( std.mem.eql(u8, daily.data.hourly_units.time, "iso8601") ); }