const std = @import("std"); const config = @import("config.zig"); const Cache = @import("cache/Cache.zig"); const MetNo = @import("weather/MetNo.zig"); const types = @import("weather/types.zig"); const Server = @import("http/Server.zig"); const RateLimiter = @import("http/RateLimiter.zig"); const GeoIP = @import("location/GeoIP.zig"); const GeoCache = @import("location/GeoCache.zig"); const Airports = @import("location/Airports.zig"); const Resolver = @import("location/resolver.zig").Resolver; const geolite_downloader = @import("location/geolite_downloader.zig"); pub const std_options: std.Options = .{ .log_level = .info, }; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var stdout_buffer: [4096]u8 = undefined; var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; const cfg = try config.Config.load(allocator); defer cfg.deinit(allocator); try stdout.print("wttr starting on {s}:{d}\n", .{ cfg.listen_host, cfg.listen_port }); try stdout.print("Cache size: {d}\n", .{cfg.cache_size}); try stdout.print("Cache dir: {s}\n", .{cfg.cache_dir}); try stdout.print("GeoLite2 path: {s}\n", .{cfg.geolite_path}); if (cfg.geocache_file) |f| { try stdout.print("Geocache file: {s}\n", .{f}); } else { try stdout.print("Geocache: in-memory only\n", .{}); } if (cfg.airports_dat_path) |f| { try stdout.print("Airports database: {s}\n", .{f}); } try stdout.flush(); // Ensure GeoLite2 database exists try geolite_downloader.ensureDatabase(allocator, cfg.geolite_path); // Initialize GeoIP database var geoip = GeoIP.init(cfg.geolite_path) catch |err| { std.log.warn("Failed to load GeoIP database: {}", .{err}); std.log.warn("IP-based location resolution will be unavailable", .{}); return err; }; defer geoip.deinit(); // Initialize geocoding cache var geocache = try GeoCache.init(allocator, cfg.geocache_file); defer geocache.deinit(); // Initialize airports database var airports_db: ?Airports = null; if (cfg.airports_dat_path) |path| { airports_db = Airports.initFromFile(allocator, path) catch |err| blk: { std.log.warn("Failed to load airports database: {}", .{err}); break :blk null; }; } if (airports_db) |*db| { defer db.deinit(); } // Initialize location resolver var resolver = Resolver.init(allocator, &geoip, &geocache, if (airports_db) |*db| db else null); var cache = try Cache.init(allocator, .{ .max_entries = cfg.cache_size, .cache_dir = cfg.cache_dir, }); defer cache.deinit(); var rate_limiter = try RateLimiter.init(allocator, .{ .capacity = 300, .refill_rate = 5, .refill_interval_ms = 200, }); defer rate_limiter.deinit(); var metno = try MetNo.init(allocator); defer metno.deinit(); var server = try Server.init(allocator, cfg.listen_host, cfg.listen_port, .{ .cache = &cache, .provider = metno.provider(), .resolver = &resolver, .geoip = &geoip, }, &rate_limiter); try server.listen(); } test { std.testing.refAllDecls(@This()); _ = @import("config.zig"); _ = @import("cache/LRU.zig"); _ = @import("weather/mock.zig"); _ = @import("http/RateLimiter.zig"); _ = @import("http/query.zig"); _ = @import("http/help.zig"); _ = @import("render/line.zig"); _ = @import("render/json.zig"); _ = @import("render/v2.zig"); _ = @import("render/custom.zig"); _ = @import("location/GeoIP.zig"); _ = @import("location/GeoCache.zig"); _ = @import("location/Airports.zig"); _ = @import("location/resolver.zig"); }