112 lines
4.3 KiB
Zig
112 lines
4.3 KiB
Zig
const std = @import("std");
|
|
|
|
const EnvMap = std.StringHashMap([]const u8);
|
|
|
|
pub const Config = struct {
|
|
twelvedata_key: ?[]const u8 = null,
|
|
polygon_key: ?[]const u8 = null,
|
|
finnhub_key: ?[]const u8 = null,
|
|
alphavantage_key: ?[]const u8 = null,
|
|
tiingo_key: ?[]const u8 = null,
|
|
openfigi_key: ?[]const u8 = null,
|
|
/// URL of a zfin-server instance for lazy cache sync (e.g. "https://zfin.lerch.org")
|
|
server_url: ?[]const u8 = null,
|
|
cache_dir: []const u8,
|
|
cache_dir_owned: bool = false, // true when cache_dir was allocated via path.join
|
|
allocator: ?std.mem.Allocator = null,
|
|
/// Raw .env file contents (keys/values in env_map point into this).
|
|
env_buf: ?[]const u8 = null,
|
|
/// Parsed KEY=VALUE pairs from .env file.
|
|
env_map: ?EnvMap = null,
|
|
/// Strings allocated by resolve() from process environment variables.
|
|
env_owned: std.ArrayList([]const u8) = .empty,
|
|
|
|
pub fn fromEnv(allocator: std.mem.Allocator) Config {
|
|
var self = Config{
|
|
.cache_dir = undefined,
|
|
.allocator = allocator,
|
|
};
|
|
|
|
// Try loading .env file from the current working directory
|
|
self.env_buf = std.fs.cwd().readFileAlloc(allocator, ".env", 4096) catch null;
|
|
if (self.env_buf) |buf| {
|
|
self.env_map = parseEnvFile(allocator, buf);
|
|
}
|
|
|
|
self.twelvedata_key = self.resolve("TWELVEDATA_API_KEY");
|
|
self.polygon_key = self.resolve("POLYGON_API_KEY");
|
|
self.finnhub_key = self.resolve("FINNHUB_API_KEY");
|
|
self.alphavantage_key = self.resolve("ALPHAVANTAGE_API_KEY");
|
|
self.tiingo_key = self.resolve("TIINGO_API_KEY");
|
|
self.openfigi_key = self.resolve("OPENFIGI_API_KEY");
|
|
self.server_url = self.resolve("ZFIN_SERVER");
|
|
|
|
const env_cache = self.resolve("ZFIN_CACHE_DIR");
|
|
self.cache_dir = env_cache orelse blk: {
|
|
self.cache_dir_owned = true;
|
|
// XDG Base Directory: $XDG_CACHE_HOME/zfin, falling back to $HOME/.cache/zfin
|
|
const xdg = self.resolve("XDG_CACHE_HOME");
|
|
const base = xdg orelse fallback: {
|
|
const home = self.resolve("HOME") orelse "/tmp";
|
|
break :fallback std.fs.path.join(allocator, &.{ home, ".cache" }) catch @panic("OOM");
|
|
};
|
|
const base_allocated = xdg == null;
|
|
defer if (base_allocated) allocator.free(base);
|
|
break :blk std.fs.path.join(allocator, &.{ base, "zfin" }) catch @panic("OOM");
|
|
};
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn deinit(self: *Config) void {
|
|
if (self.allocator) |a| {
|
|
if (self.env_map) |*m| {
|
|
var map = m.*;
|
|
map.deinit();
|
|
}
|
|
if (self.env_buf) |buf| a.free(buf);
|
|
for (self.env_owned.items) |s| a.free(s);
|
|
self.env_owned.deinit(a);
|
|
if (self.cache_dir_owned) {
|
|
a.free(self.cache_dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn hasAnyKey(self: Config) bool {
|
|
return self.twelvedata_key != null or
|
|
self.polygon_key != null or
|
|
self.finnhub_key != null or
|
|
self.alphavantage_key != null or
|
|
self.tiingo_key != null;
|
|
}
|
|
|
|
/// Look up a key: process environment first, then .env file fallback.
|
|
fn resolve(self: *Config, key: []const u8) ?[]const u8 {
|
|
if (self.allocator) |a| {
|
|
if (std.process.getEnvVarOwned(a, key)) |v| {
|
|
self.env_owned.append(a, v) catch {};
|
|
return v;
|
|
} else |_| {}
|
|
}
|
|
if (self.env_map) |m| return m.get(key);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/// Parse all KEY=VALUE pairs from .env content into a HashMap.
|
|
/// Values are slices into the original buffer (no extra allocations per entry).
|
|
fn parseEnvFile(allocator: std.mem.Allocator, data: []const u8) ?EnvMap {
|
|
var map = EnvMap.init(allocator);
|
|
var iter = std.mem.splitScalar(u8, data, '\n');
|
|
while (iter.next()) |line| {
|
|
const trimmed = std.mem.trim(u8, line, &std.ascii.whitespace);
|
|
if (trimmed.len == 0 or trimmed[0] == '#') continue;
|
|
if (std.mem.indexOfScalar(u8, trimmed, '=')) |eq| {
|
|
const k = std.mem.trim(u8, trimmed[0..eq], &std.ascii.whitespace);
|
|
const v = std.mem.trim(u8, trimmed[eq + 1 ..], &std.ascii.whitespace);
|
|
map.put(k, v) catch return null;
|
|
}
|
|
}
|
|
return map;
|
|
}
|