upgrade to zig 0.16.0 and latest zfin lib, add /splits
This commit is contained in:
parent
675d8839a2
commit
536a427a08
5 changed files with 109 additions and 72 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
|||
.zig-cache
|
||||
zig-out/
|
||||
*.srf
|
||||
zig-pkg/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[tools]
|
||||
prek = "0.3.1"
|
||||
zig = "0.15.2"
|
||||
zls = "0.15.1"
|
||||
zig = "0.16.0"
|
||||
zls = "0.16.0"
|
||||
"ubi:DonIsaac/zlint" = "0.7.9"
|
||||
|
|
|
|||
|
|
@ -8,19 +8,21 @@ pub const Options = struct {
|
|||
dirty_flag: []const u8 = "*",
|
||||
};
|
||||
|
||||
/// Get git version information by reading .git files directly
|
||||
/// Get git version information by reading .git files directly.
|
||||
/// Runs at build-config time, so uses `b.graph.io` for I/O.
|
||||
pub fn getVersion(b: *Build, options: Options) []const u8 {
|
||||
const allocator = b.allocator;
|
||||
const io = b.graph.io;
|
||||
|
||||
// Find build root by looking for build.zig
|
||||
const build_root = findBuildRoot(allocator) catch return "unknown";
|
||||
const build_root = findBuildRoot(allocator, io) catch return "unknown";
|
||||
defer allocator.free(build_root);
|
||||
|
||||
// Read .git/HEAD relative to build root
|
||||
const head_path = std.fmt.allocPrint(allocator, "{s}/.git/HEAD", .{build_root}) catch return "unknown";
|
||||
defer allocator.free(head_path);
|
||||
|
||||
const head_data = std.fs.cwd().readFileAlloc(allocator, head_path, 1024) catch {
|
||||
const head_data = std.Io.Dir.cwd().readFileAlloc(io, head_path, allocator, .limited(1024)) catch {
|
||||
return "not under version control";
|
||||
};
|
||||
defer allocator.free(head_data);
|
||||
|
|
@ -29,16 +31,12 @@ pub fn getVersion(b: *Build, options: Options) []const u8 {
|
|||
|
||||
// Parse HEAD - either "ref: refs/heads/branch" or direct hash
|
||||
const hash_owned = if (std.mem.startsWith(u8, head_trimmed, "ref: ")) blk: {
|
||||
const ref_path_rel = std.mem.trimLeft(u8, head_trimmed[5..], &std.ascii.whitespace);
|
||||
const ref_path_rel = std.mem.trimStart(u8, head_trimmed[5..], &std.ascii.whitespace);
|
||||
const ref_file = std.fmt.allocPrint(allocator, "{s}/.git/{s}", .{ build_root, ref_path_rel }) catch return "unknown";
|
||||
defer allocator.free(ref_file);
|
||||
|
||||
const ref_fd = std.fs.openFileAbsolute(ref_file, .{}) catch return "unknown";
|
||||
defer ref_fd.close();
|
||||
|
||||
var ref_buf: [1024]u8 = undefined;
|
||||
const bytes_read = ref_fd.readAll(&ref_buf) catch return "unknown";
|
||||
const ref_data = ref_buf[0..bytes_read];
|
||||
const ref_data = std.Io.Dir.cwd().readFileAlloc(io, ref_file, allocator, .limited(1024)) catch return "unknown";
|
||||
defer allocator.free(ref_data);
|
||||
|
||||
const ref_trimmed = std.mem.trim(u8, ref_data, &std.ascii.whitespace);
|
||||
break :blk allocator.dupe(u8, ref_trimmed) catch return "unknown";
|
||||
|
|
@ -53,7 +51,7 @@ pub fn getVersion(b: *Build, options: Options) []const u8 {
|
|||
|
||||
// Check if dirty using simple heuristic:
|
||||
// If any .zig files are newer than .git/index, mark as dirty
|
||||
const is_dirty = isDirty(allocator, build_root) catch return "unknown";
|
||||
const is_dirty = isDirty(allocator, io, build_root) catch return "unknown";
|
||||
|
||||
if (is_dirty) {
|
||||
return std.fmt.allocPrint(allocator, "{s}{s}", .{ short_hash, options.dirty_flag }) catch return "unknown";
|
||||
|
|
@ -62,17 +60,17 @@ pub fn getVersion(b: *Build, options: Options) []const u8 {
|
|||
return allocator.dupe(u8, short_hash) catch return "unknown";
|
||||
}
|
||||
|
||||
fn findBuildRoot(allocator: std.mem.Allocator) ![]const u8 {
|
||||
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const start_cwd = try std.fs.cwd().realpath(".", &buf);
|
||||
fn findBuildRoot(allocator: std.mem.Allocator, io: std.Io) ![]const u8 {
|
||||
const start_cwd = try std.Io.Dir.cwd().realPathFileAlloc(io, ".", allocator);
|
||||
defer allocator.free(start_cwd);
|
||||
var cwd: []const u8 = start_cwd;
|
||||
|
||||
while (true) {
|
||||
// Check if build.zig exists in current directory
|
||||
var dir = std.fs.openDirAbsolute(cwd, .{}) catch break;
|
||||
defer dir.close();
|
||||
var dir = std.Io.Dir.cwd().openDir(io, cwd, .{}) catch break;
|
||||
defer dir.close(io);
|
||||
|
||||
dir.access("build.zig", .{}) catch {
|
||||
dir.access(io, "build.zig", .{}) catch {
|
||||
// build.zig not found, try parent
|
||||
const parent = std.fs.path.dirname(cwd) orelse break;
|
||||
if (std.mem.eql(u8, parent, cwd)) break; // Reached root
|
||||
|
|
@ -86,29 +84,29 @@ fn findBuildRoot(allocator: std.mem.Allocator) ![]const u8 {
|
|||
return error.BuildRootNotFound;
|
||||
}
|
||||
|
||||
fn isDirty(allocator: std.mem.Allocator, build_root: []const u8) !bool {
|
||||
fn isDirty(allocator: std.mem.Allocator, io: std.Io, build_root: []const u8) !bool {
|
||||
// Get .git/index mtime
|
||||
const index_path = try std.fs.path.join(allocator, &[_][]const u8{ build_root, ".git", "index" });
|
||||
defer allocator.free(index_path);
|
||||
|
||||
const index_stat = std.fs.cwd().statFile(index_path) catch return error.CannotDetermineDirty;
|
||||
const index_stat = std.Io.Dir.cwd().statFile(io, index_path, .{}) catch return error.CannotDetermineDirty;
|
||||
const index_mtime = index_stat.mtime;
|
||||
|
||||
// Read .gitignore
|
||||
const ignore_path = try std.fs.path.join(allocator, &[_][]const u8{ build_root, ".gitignore" });
|
||||
defer allocator.free(ignore_path);
|
||||
const ignore_data = std.fs.cwd().readFileAlloc(allocator, ignore_path, 1024 * 1024) catch
|
||||
const ignore_data = std.Io.Dir.cwd().readFileAlloc(io, ignore_path, allocator, .limited(1024 * 1024)) catch
|
||||
try allocator.dupe(u8, "");
|
||||
defer allocator.free(ignore_data);
|
||||
|
||||
// Walk source files in build root and check if any are newer
|
||||
var dir = std.fs.openDirAbsolute(build_root, .{ .iterate = true }) catch return error.CannotDetermineDirty;
|
||||
defer dir.close();
|
||||
var dir = std.Io.Dir.cwd().openDir(io, build_root, .{ .iterate = true }) catch return error.CannotDetermineDirty;
|
||||
defer dir.close(io);
|
||||
|
||||
var walker = dir.walk(allocator) catch return error.CannotDetermineDirty;
|
||||
defer walker.deinit();
|
||||
|
||||
while (walker.next() catch return error.CannotDetermineDirty) |entry| {
|
||||
while (walker.next(io) catch return error.CannotDetermineDirty) |entry| {
|
||||
if (entry.kind != .file) continue;
|
||||
|
||||
// Always ignore .git/
|
||||
|
|
@ -129,8 +127,8 @@ fn isDirty(allocator: std.mem.Allocator, build_root: []const u8) !bool {
|
|||
}
|
||||
if (ignored) continue;
|
||||
|
||||
const stat = entry.dir.statFile(entry.basename) catch continue;
|
||||
if (stat.mtime > index_mtime) {
|
||||
const stat = entry.dir.statFile(io, entry.basename, .{}) catch continue;
|
||||
if (stat.mtime.nanoseconds > index_mtime.nanoseconds) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
.name = .zfin_server,
|
||||
.version = "0.1.0",
|
||||
.fingerprint = 0xa060a7d1947dfe8f,
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
|
|
@ -10,12 +10,12 @@
|
|||
},
|
||||
.dependencies = .{
|
||||
.httpz = .{
|
||||
.url = "git+https://github.com/karlseguin/http.zig#844f8016e6616f00b05d4cc3c713307b0fe586c7",
|
||||
.hash = "httpz-0.0.0-PNVzrBtMBwAPcQx3mNEgat3Xbsynw-eIC9SmOX5M9XtP",
|
||||
.url = "git+https://github.com/karlseguin/http.zig?ref=master#03658c279937066201aba8c0a8f1936d821d0709",
|
||||
.hash = "httpz-0.0.0-PNVzrLjJCAD37S0CcrXpsjSqr86hVjK0rsALTDJ98AAJ",
|
||||
},
|
||||
.zfin = .{
|
||||
.url = "git+https://git.lerch.org/lobo/zfin#f9c7fa99e4d30ac613c45572bbe1c45cd3767b3d",
|
||||
.hash = "zfin-0.0.0-J-B21u7_IgD0BHz6KGXpZ4sWj7aIOHwcTKCOByLAUYKJ",
|
||||
.url = "git+https://git.lerch.org/lobo/zfin#fe2894975751ab2bf9ea733e89396bf43c53532f",
|
||||
.hash = "zfin-0.0.0-J-B21tQtMQC0QPMuhUwnofeKdo2MU5XtCtxpvZeZr2kc",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
118
src/main.zig
118
src/main.zig
|
|
@ -17,14 +17,22 @@ const log = std.log.scoped(.@"zfin-server");
|
|||
// ── App ──────────────────────────────────────────────────────
|
||||
|
||||
const App = struct {
|
||||
io: std.Io,
|
||||
environ: *const std.process.Environ.Map,
|
||||
allocator: std.mem.Allocator,
|
||||
config: zfin.Config,
|
||||
svc: zfin.DataService,
|
||||
|
||||
fn init(allocator: std.mem.Allocator) App {
|
||||
const config = zfin.Config.fromEnv(allocator);
|
||||
const svc = zfin.DataService.init(allocator, config);
|
||||
return .{ .allocator = allocator, .config = config, .svc = svc };
|
||||
fn init(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process.Environ.Map) App {
|
||||
const config = zfin.Config.fromEnv(io, allocator, environ);
|
||||
const svc = zfin.DataService.init(io, allocator, config);
|
||||
return .{
|
||||
.io = io,
|
||||
.environ = environ,
|
||||
.allocator = allocator,
|
||||
.config = config,
|
||||
.svc = svc,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *App) void {
|
||||
|
|
@ -60,6 +68,7 @@ fn handleHelp(_: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
|||
\\ GET /{SYMBOL}/candles Raw SRF cache file
|
||||
\\ GET /{SYMBOL}/candles_meta Candle freshness metadata (SRF)
|
||||
\\ GET /{SYMBOL}/dividends Raw SRF cache file
|
||||
\\ GET /{SYMBOL}/splits Raw SRF cache file
|
||||
\\ GET /{SYMBOL}/earnings Raw SRF cache file
|
||||
\\ GET /{SYMBOL}/options Raw SRF cache file
|
||||
\\ GET /symbols List of tracked symbols
|
||||
|
|
@ -78,11 +87,11 @@ fn handleHelp(_: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
|||
;
|
||||
}
|
||||
|
||||
fn handleSymbols(_: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
fn handleSymbols(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
|
||||
const arena = res.arena;
|
||||
const portfolio_path = std.posix.getenv("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
||||
const portfolio_path = app.environ.get("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
||||
|
||||
const file_data = std.fs.cwd().readFileAlloc(arena, portfolio_path, 10 * 1024 * 1024) catch {
|
||||
const file_data = std.Io.Dir.cwd().readFileAlloc(app.io, portfolio_path, arena, .limited(10 * 1024 * 1024)) catch {
|
||||
res.content_type = httpz.ContentType.JSON;
|
||||
res.body = "[]";
|
||||
return;
|
||||
|
|
@ -131,7 +140,7 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
|||
const q = try req.query();
|
||||
if (q.get("watch")) |w| {
|
||||
if (std.ascii.eqlIgnoreCase(w, "true")) {
|
||||
appendWatchSymbol(symbol) catch |err| {
|
||||
appendWatchSymbol(app, symbol) catch |err| {
|
||||
log.warn("failed to append watch symbol {s}: {}", .{ symbol, err });
|
||||
};
|
||||
}
|
||||
|
|
@ -156,15 +165,22 @@ fn handleReturns(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
|||
|
||||
const last_close = candles[candles.len - 1].close;
|
||||
var date_buf: [10]u8 = undefined;
|
||||
const date_str = candles[candles.len - 1].date.format(&date_buf);
|
||||
const date_str = try std.fmt.bufPrint(&date_buf, "{f}", .{candles[candles.len - 1].date});
|
||||
|
||||
// Price-only returns (from adjusted close)
|
||||
// Price-only returns (split-adjusted, NOT dividend-adjusted —
|
||||
// see analytics/performance.zig:trailingReturnsPriceOnly).
|
||||
// Matches the "price return" numbers public sources publish
|
||||
// (Yahoo chart-bar, FMP, Barchart, Fidelity stock pages).
|
||||
const p1y = if (result.asof_price.one_year) |r| r.annualized_return else null;
|
||||
const p3y = if (result.asof_price.three_year) |r| r.annualized_return else null;
|
||||
const p5y = if (result.asof_price.five_year) |r| r.annualized_return else null;
|
||||
const p10y = if (result.asof_price.ten_year) |r| r.annualized_return else null;
|
||||
|
||||
// Total returns (best of adj_close and dividend reinvestment)
|
||||
// Total returns (dividend reinvestment when dividends are
|
||||
// available; falls back to adj_close-based total return when
|
||||
// dividend records are missing). Matches Morningstar
|
||||
// "Trailing Returns" / Yahoo "Performance Overview" / Koyfin
|
||||
// "Total Return".
|
||||
const total = result.asof_total orelse result.asof_price;
|
||||
const t1y = if (total.one_year) |r| r.annualized_return else null;
|
||||
const t3y = if (total.three_year) |r| r.annualized_return else null;
|
||||
|
|
@ -285,7 +301,7 @@ fn handleSrfFile(app: *App, req: *httpz.Request, res: *httpz.Response, filename:
|
|||
const symbol = try upperDupe(arena, raw_symbol);
|
||||
|
||||
const path = try std.fs.path.join(arena, &.{ app.config.cache_dir, symbol, filename });
|
||||
const content = std.fs.cwd().readFileAlloc(arena, path, 10 * 1024 * 1024) catch {
|
||||
const content = std.Io.Dir.cwd().readFileAlloc(app.io, path, arena, .limited(10 * 1024 * 1024)) catch {
|
||||
res.status = 404;
|
||||
res.body = "Cache file not found";
|
||||
return;
|
||||
|
|
@ -323,6 +339,10 @@ fn handleDividends(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
|||
return handleSrfFile(app, req, res, "dividends.srf");
|
||||
}
|
||||
|
||||
fn handleSplits(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||
return handleSrfFile(app, req, res, "splits.srf");
|
||||
}
|
||||
|
||||
fn handleEarnings(app: *App, req: *httpz.Request, res: *httpz.Response) !void {
|
||||
return handleSrfFile(app, req, res, "earnings.srf");
|
||||
}
|
||||
|
|
@ -362,13 +382,14 @@ fn fmtInt(arena: std.mem.Allocator, value: ?u8) []const u8 {
|
|||
|
||||
/// Append a watch lot for the given symbol to the portfolio SRF file,
|
||||
/// unless it already exists. Best-effort — errors are logged, not fatal.
|
||||
fn appendWatchSymbol(symbol: []const u8) !void {
|
||||
const portfolio_path = std.posix.getenv("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
||||
const allocator = std.heap.page_allocator;
|
||||
fn appendWatchSymbol(app: *App, symbol: []const u8) !void {
|
||||
const portfolio_path = app.environ.get("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
||||
const allocator = app.allocator;
|
||||
const io = app.io;
|
||||
|
||||
// Read and deserialize existing portfolio (or start empty)
|
||||
const file_data = std.fs.cwd().readFileAlloc(allocator, portfolio_path, 10 * 1024 * 1024) catch |err| {
|
||||
if (err == error.FileNotFound) return writeNewPortfolio(allocator, portfolio_path, symbol);
|
||||
const file_data = std.Io.Dir.cwd().readFileAlloc(io, portfolio_path, allocator, .limited(10 * 1024 * 1024)) catch |err| {
|
||||
if (err == error.FileNotFound) return writeNewPortfolio(io, allocator, portfolio_path, symbol);
|
||||
return err;
|
||||
};
|
||||
defer allocator.free(file_data);
|
||||
|
|
@ -397,14 +418,17 @@ fn appendWatchSymbol(symbol: []const u8) !void {
|
|||
const output = try zfin.cache.serializePortfolio(allocator, new_lots);
|
||||
defer allocator.free(output);
|
||||
|
||||
const file = try std.fs.cwd().createFile(portfolio_path, .{});
|
||||
defer file.close();
|
||||
try file.writeAll(output);
|
||||
const file = try std.Io.Dir.cwd().createFile(io, portfolio_path, .{});
|
||||
defer file.close(io);
|
||||
var write_buf: [4096]u8 = undefined;
|
||||
var fw = file.writer(io, &write_buf);
|
||||
try fw.interface.writeAll(output);
|
||||
try fw.interface.flush();
|
||||
|
||||
log.info("added watch symbol {s} to {s}", .{ symbol, portfolio_path });
|
||||
}
|
||||
|
||||
fn writeNewPortfolio(allocator: std.mem.Allocator, path: []const u8, symbol: []const u8) !void {
|
||||
fn writeNewPortfolio(io: std.Io, allocator: std.mem.Allocator, path: []const u8, symbol: []const u8) !void {
|
||||
const lot = [_]zfin.Lot{.{
|
||||
.symbol = symbol,
|
||||
.shares = 0,
|
||||
|
|
@ -415,24 +439,27 @@ fn writeNewPortfolio(allocator: std.mem.Allocator, path: []const u8, symbol: []c
|
|||
const output = try zfin.cache.serializePortfolio(allocator, &lot);
|
||||
defer allocator.free(output);
|
||||
|
||||
const file = try std.fs.cwd().createFile(path, .{});
|
||||
defer file.close();
|
||||
try file.writeAll(output);
|
||||
const file = try std.Io.Dir.cwd().createFile(io, path, .{});
|
||||
defer file.close(io);
|
||||
var write_buf: [4096]u8 = undefined;
|
||||
var fw = file.writer(io, &write_buf);
|
||||
try fw.interface.writeAll(output);
|
||||
try fw.interface.flush();
|
||||
|
||||
log.info("created {s} with watch symbol {s}", .{ path, symbol });
|
||||
}
|
||||
|
||||
// ── Refresh command ──────────────────────────────────────────
|
||||
|
||||
fn refresh(allocator: std.mem.Allocator) !void {
|
||||
var config = zfin.Config.fromEnv(allocator);
|
||||
fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process.Environ.Map) !void {
|
||||
var config = zfin.Config.fromEnv(io, allocator, environ);
|
||||
defer config.deinit();
|
||||
var svc = zfin.DataService.init(allocator, config);
|
||||
var svc = zfin.DataService.init(io, allocator, config);
|
||||
defer svc.deinit();
|
||||
|
||||
const portfolio_path = std.posix.getenv("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
||||
const portfolio_path = environ.get("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
||||
|
||||
const data = std.fs.cwd().readFileAlloc(allocator, portfolio_path, 10 * 1024 * 1024) catch {
|
||||
const data = std.Io.Dir.cwd().readFileAlloc(io, portfolio_path, allocator, .limited(10 * 1024 * 1024)) catch {
|
||||
log.err("failed to read portfolio: {s}", .{portfolio_path});
|
||||
return error.ReadFailed;
|
||||
};
|
||||
|
|
@ -455,9 +482,9 @@ fn refresh(allocator: std.mem.Allocator) !void {
|
|||
}
|
||||
}
|
||||
|
||||
const stdout_file = std.fs.File.stdout();
|
||||
const stdout_file = std.Io.File.stdout();
|
||||
var buf: [4096]u8 = undefined;
|
||||
var writer = stdout_file.writer(&buf);
|
||||
var writer = stdout_file.writer(io, &buf);
|
||||
const stdout = &writer.interface;
|
||||
|
||||
try stdout.print("zfin-server {s}\n", .{version});
|
||||
|
|
@ -503,6 +530,16 @@ fn refresh(allocator: std.mem.Allocator) !void {
|
|||
sym_ok = false;
|
||||
}
|
||||
|
||||
// Splits
|
||||
try printRateLimitWait(&svc, stdout);
|
||||
if (svc.getSplits(sym)) |result| {
|
||||
allocator.free(result.data);
|
||||
try stdout.print(", splits ok", .{});
|
||||
} else |err| {
|
||||
try stdout.print(", splits FAILED ({s})", .{@errorName(err)});
|
||||
sym_ok = false;
|
||||
}
|
||||
|
||||
// Earnings
|
||||
try printRateLimitWait(&svc, stdout);
|
||||
if (svc.getEarnings(sym)) |result| {
|
||||
|
|
@ -527,13 +564,13 @@ fn refresh(allocator: std.mem.Allocator) !void {
|
|||
|
||||
// ── Main ─────────────────────────────────────────────────────
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
const allocator = init.gpa;
|
||||
const io = init.io;
|
||||
const environ = init.environ_map;
|
||||
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
defer std.process.argsFree(allocator, args);
|
||||
const args = try init.minimal.args.toSlice(allocator);
|
||||
defer allocator.free(args);
|
||||
|
||||
if (args.len < 2) {
|
||||
printUsage();
|
||||
|
|
@ -550,10 +587,10 @@ pub fn main() !void {
|
|||
}
|
||||
}
|
||||
|
||||
var app = App.init(allocator);
|
||||
var app = App.init(io, allocator, environ);
|
||||
defer app.deinit();
|
||||
|
||||
var server = try httpz.Server(*App).init(allocator, .{
|
||||
var server = try httpz.Server(*App).init(io, allocator, .{
|
||||
.address = .all(port),
|
||||
}, &app);
|
||||
defer {
|
||||
|
|
@ -574,6 +611,7 @@ pub fn main() !void {
|
|||
router.get("/:symbol/candles", handleCandles, .{});
|
||||
router.get("/:symbol/candles_meta", handleCandlesMeta, .{});
|
||||
router.get("/:symbol/dividends", handleDividends, .{});
|
||||
router.get("/:symbol/splits", handleSplits, .{});
|
||||
router.get("/:symbol/earnings", handleEarnings, .{});
|
||||
router.get("/:symbol/options", handleOptions, .{});
|
||||
|
||||
|
|
@ -581,7 +619,7 @@ pub fn main() !void {
|
|||
log.info("listening on port {d}", .{port});
|
||||
try server.listen();
|
||||
} else if (std.mem.eql(u8, command, "refresh")) {
|
||||
try refresh(allocator);
|
||||
try refresh(io, allocator, environ);
|
||||
} else {
|
||||
printUsage();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue