first pass at MetNo user agent

This commit is contained in:
Emil Lerch 2026-01-05 12:18:49 -08:00
parent ea17e18c6f
commit 24903e4beb
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 33 additions and 5 deletions

View file

@ -10,7 +10,7 @@ const Airports = @import("location/Airports.zig");
const Resolver = @import("location/resolver.zig").Resolver;
const GeoLite2 = @import("location/GeoLite2.zig");
pub fn main() !void {
pub fn main() !u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
@ -67,7 +67,10 @@ pub fn main() !void {
});
defer rate_limiter.deinit();
var metno = try MetNo.init(allocator);
var metno = MetNo.init(allocator, null) catch |err| {
if (err == MetNo.MissingIdentificationError) return 1;
return err;
};
defer metno.deinit();
var server = try Server.init(allocator, cfg.listen_host, cfg.listen_port, .{
@ -77,6 +80,7 @@ pub fn main() !void {
}, &rate_limiter);
try server.listen();
return 0;
}
test {

View file

@ -7,6 +7,8 @@ const zeit = @import("zeit");
const MetNo = @This();
pub const MissingIdentificationError = error.MetNoIdentificationRequired;
const MetNoOpenWeatherEntry = struct { []const u8, types.WeatherCode };
// symbol codes: https://github.com/metno/weathericons/tree/main/weather
// they also have _day, _night and _polartwilight variants
@ -63,10 +65,24 @@ const WeatherCodeMap = std.StaticStringMap(types.WeatherCode);
const weather_code_map = WeatherCodeMap.initComptime(weather_code_entries);
allocator: std.mem.Allocator,
identifying_email: []const u8,
pub fn init(allocator: std.mem.Allocator, identifying_email: ?[]const u8) !MetNo {
const email = identifying_email orelse blk: {
const env_email = std.process.getEnvVarOwned(allocator, "METNO_TOS_IDENTIFYING_EMAIL") catch |err| {
if (err == error.EnvironmentVariableNotFound) {
std.log.err("Met.no Terms of Service require identification. Set METNO_TOS_IDENTIFYING_EMAIL environment variable. See https://api.met.no/doc/TermsOfService", .{});
std.log.err("See \x1b]8;;https://api.met.no/doc/TermsOfService\x1b\\https://api.met.no/doc/TermsOfService\x1b]8;;\x1b\\ for more information", .{});
return MissingIdentificationError;
}
return err;
};
break :blk env_email;
};
pub fn init(allocator: std.mem.Allocator) !MetNo {
return MetNo{
.allocator = allocator,
.identifying_email = email,
};
}
@ -100,12 +116,20 @@ fn fetchRaw(ptr: *anyopaque, allocator: std.mem.Allocator, coords: Coordinates)
var response_buf: [1024 * 1024]u8 = undefined;
var writer = std.Io.Writer.fixed(&response_buf);
const user_agent = try std.fmt.allocPrint(
self.allocator,
"wttr/1.0 git.lerch.org/lobo/wttr {s}",
.{self.identifying_email},
);
defer self.allocator.free(user_agent);
const result = try client.fetch(.{
.location = .{ .uri = uri },
.method = .GET,
.response_writer = &writer,
.extra_headers = &.{
.{ .name = "User-Agent", .value = "wttr.in-zig/1.0 github.com/chubin/wttr.in" },
.{ .name = "User-Agent", .value = user_agent },
},
});
@ -145,7 +169,7 @@ fn deinitProvider(ptr: *anyopaque) void {
}
pub fn deinit(self: *MetNo) void {
_ = self;
self.allocator.free(self.identifying_email);
}
fn parseMetNoResponse(allocator: std.mem.Allocator, coords: Coordinates, json: std.json.Value) !types.WeatherData {