const std = @import("std"); ///Units: /// /// m # metric (SI) (used by default everywhere except US) /// u # USCS (used by default in US) /// M # show wind speed in m/s /// ///View options: /// /// 0 # only current weather /// 1 # current weather + today's forecast /// 2 # current weather + today's + tomorrow's forecast /// A # ignore User-Agent and force ANSI output format (terminal) /// d # restrict output to standard console font glyphs /// F # do not show the "Follow" line /// n # narrow version (only day and night) /// q # quiet version (no "Weather report" text) /// Q # superquiet version (no "Weather report", no city name) /// T # switch terminal sequences off (no colors) pub const QueryParams = struct { format: ?[]const u8 = null, lang: ?[]const u8 = null, location: ?[]const u8 = null, units: ?Units = null, transparency: ?u8 = null, /// A: Ignore user agent and force ansi mode ansi: bool = false, /// T: Avoid terminal sequences and just output plain text text_only: bool = false, pub const Units = enum { metric, uscs, }; pub fn parse(allocator: std.mem.Allocator, query_string: []const u8) !QueryParams { var params = QueryParams{}; var iter = std.mem.splitScalar(u8, query_string, '&'); while (iter.next()) |pair| { if (pair.len == 0) continue; var kv = std.mem.splitScalar(u8, pair, '='); const key = kv.next() orelse continue; const value = kv.next(); if (key.len == 1) { switch (key[0]) { 'u' => params.units = .uscs, 'm' => params.units = .metric, 'A' => params.ansi = true, 'T' => params.text_only = true, 't' => params.transparency = 150, else => continue, } } if (std.mem.eql(u8, key, "format")) { params.format = if (value) |v| try allocator.dupe(u8, v) else null; } else if (std.mem.eql(u8, key, "lang")) { params.lang = if (value) |v| try allocator.dupe(u8, v) else null; } else if (std.mem.eql(u8, key, "location")) { params.location = if (value) |v| try allocator.dupe(u8, v) else null; } else if (std.mem.eql(u8, key, "use_imperial")) { params.units = .uscs; } else if (std.mem.eql(u8, key, "use_metric")) { params.units = .metric; } else if (std.mem.eql(u8, key, "transparency")) { if (value) |v| { params.transparency = try std.fmt.parseInt(u8, v, 10); } } } return params; } }; test "parse empty query" { const allocator = std.testing.allocator; const params = try QueryParams.parse(allocator, ""); try std.testing.expect(params.format == null); try std.testing.expect(params.lang == null); try std.testing.expect(params.units == null); } test "parse format parameter" { const allocator = std.testing.allocator; const params = try QueryParams.parse(allocator, "format=j1"); defer if (params.format) |f| allocator.free(f); try std.testing.expect(params.format != null); try std.testing.expectEqualStrings("j1", params.format.?); } test "parse units with question mark" { const allocator = std.testing.allocator; // Test with just "u" (no question mark in query string) const params1 = try QueryParams.parse(allocator, "u"); try std.testing.expectEqual(QueryParams.Units.uscs, params1.units.?); // Test with "u=" (empty value) const params2 = try QueryParams.parse(allocator, "u="); try std.testing.expectEqual(QueryParams.Units.uscs, params2.units.?); // Test combined with other params const params3 = try QueryParams.parse(allocator, "format=3&u"); defer if (params3.format) |f| allocator.free(f); try std.testing.expectEqual(QueryParams.Units.uscs, params3.units.?); } test "parse units parameters" { const allocator = std.testing.allocator; const params_m = try QueryParams.parse(allocator, "m"); try std.testing.expectEqual(QueryParams.Units.metric, params_m.units.?); const params_u = try QueryParams.parse(allocator, "u"); try std.testing.expectEqual(QueryParams.Units.uscs, params_u.units.?); const params_u_query = try QueryParams.parse(allocator, "u="); try std.testing.expectEqual(QueryParams.Units.uscs, params_u_query.units.?); } test "parse multiple parameters" { const allocator = std.testing.allocator; const params = try QueryParams.parse(allocator, "format=3&lang=de&m"); defer if (params.format) |f| allocator.free(f); defer if (params.lang) |l| allocator.free(l); try std.testing.expectEqualStrings("3", params.format.?); try std.testing.expectEqualStrings("de", params.lang.?); try std.testing.expectEqual(QueryParams.Units.metric, params.units.?); } test "parse transparency" { const allocator = std.testing.allocator; const params_t = try QueryParams.parse(allocator, "t"); try std.testing.expect(params_t.transparency != null); try std.testing.expectEqual(@as(u8, 150), params_t.transparency.?); const params_custom = try QueryParams.parse(allocator, "transparency=200"); try std.testing.expectEqual(@as(u8, 200), params_custom.transparency.?); }