summaryrefslogtreecommitdiff
path: root/src/models_toml.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-06-23 09:38:23 -0600
committert <t@tjp.lol>2026-06-23 10:53:26 -0600
commit69a1ee138bb78ad4663fe2d9e58678f7b0d07b74 (patch)
tree453d3ad42a07536220e6ca5d91b439ff57793e32 /src/models_toml.zig
parent538a5d926fa626a00cc3dc12c555f11f82867efd (diff)
ponytail simplifications to panto cli and lua extensiosn
Diffstat (limited to 'src/models_toml.zig')
-rw-r--r--src/models_toml.zig97
1 files changed, 13 insertions, 84 deletions
diff --git a/src/models_toml.zig b/src/models_toml.zig
index 6527292..9b1b4f0 100644
--- a/src/models_toml.zig
+++ b/src/models_toml.zig
@@ -2,8 +2,7 @@
//!
//! Four files are read and merged, lowest precedence first:
//!
-//! 1. base — `$PANTO_HOME/models.toml`
-//! (or `${XDG_DATA_HOME:-$HOME/.local/share}/panto/models.toml`)
+//! 1. base — `${XDG_DATA_HOME:-$HOME/.local/share}/panto/models.toml`
//! 2. user — `${XDG_CONFIG_HOME:-$HOME/.config}/panto/models.toml`
//! 3. project — `./.panto/models.toml`
//! 4. local — `./.panto/models.local.toml`
@@ -79,6 +78,8 @@ const Io = std.Io;
const toml = @import("toml");
const panto = @import("panto");
const tvalue = toml.value_mod;
+const panto_home = @import("panto_home.zig");
+const toml_layer = @import("toml_layer.zig");
pub const Pricing = panto.Pricing;
pub const Registry = panto.PricingRegistry;
@@ -161,20 +162,13 @@ pub const Models = struct {
}
};
-/// Resolve the base-layer path to `models.toml`: `$PANTO_HOME/models.toml`,
-/// falling back to `${XDG_DATA_HOME:-$HOME/.local/share}/panto/models.toml`.
+/// Resolve the base-layer path to `models.toml`:
+/// `${XDG_DATA_HOME:-$HOME/.local/share}/panto/models.toml`.
/// Caller owns the returned slice.
pub fn basePath(allocator: Allocator, environ_map: *const std.process.Environ.Map) ![]u8 {
- if (environ_map.get("PANTO_HOME")) |home| {
- return try std.fs.path.join(allocator, &.{ home, "models.toml" });
- }
- if (environ_map.get("XDG_DATA_HOME")) |xdg| {
- return try std.fs.path.join(allocator, &.{ xdg, "panto", "models.toml" });
- }
- if (environ_map.get("HOME")) |home| {
- return try std.fs.path.join(allocator, &.{ home, ".local", "share", "panto", "models.toml" });
- }
- return error.NoHomeDirectory;
+ const home = try panto_home.homePath(allocator, environ_map);
+ defer allocator.free(home);
+ return try std.fs.path.join(allocator, &.{ home, "models.toml" });
}
/// Resolve the user-layer path to `models.toml`. Caller owns the returned
@@ -243,15 +237,17 @@ pub fn loadFromPaths(allocator: Allocator, io: Io, paths: []const []const u8) !M
defer merged.deinit();
for (paths) |path| {
- const bytes = readFileAlloc(allocator, io, path) catch |err| switch (err) {
+ const bytes = toml_layer.readFileAlloc(allocator, io, path) catch |err| switch (err) {
error.FileNotFound => continue,
- else => return err,
+ error.ReadFailed => return error.ModelsReadFailed,
+ error.Canceled => return error.Canceled,
+ error.OutOfMemory => return error.OutOfMemory,
};
defer allocator.free(bytes);
const doc = parseDoc(allocator, bytes) catch return error.InvalidModelsToml;
defer doc.deinit();
- try mergeTable(merged.allocator(), merged.root, doc.root);
+ try toml_layer.mergeTable(merged.allocator(), merged.root, doc.root);
}
var models: Models = .{
@@ -269,20 +265,6 @@ pub fn loadFromPath(allocator: Allocator, io: Io, path: []const u8) !Models {
return loadFromPaths(allocator, io, &.{path});
}
-fn readFileAlloc(allocator: Allocator, io: Io, path: []const u8) ![]u8 {
- const file = Io.Dir.cwd().openFile(io, path, .{ .mode = .read_only }) catch |err| switch (err) {
- error.FileNotFound => return error.FileNotFound,
- else => return error.ModelsReadFailed,
- };
- defer file.close(io);
-
- const len = try file.length(io);
- const bytes = try allocator.alloc(u8, @intCast(len));
- errdefer allocator.free(bytes);
- _ = try file.readPositionalAll(io, bytes, 0);
- return bytes;
-}
-
fn parseDoc(allocator: Allocator, source: []const u8) !*toml.Document {
const result = toml.parseWithError(allocator, source, .{});
return switch (result) {
@@ -299,48 +281,6 @@ fn parseDoc(allocator: Allocator, source: []const u8) !*toml.Document {
};
}
-fn mergeTable(alloc: Allocator, dst: *toml.Value, src: *const toml.Value) Allocator.Error!void {
- std.debug.assert(dst.* == .table);
- if (src.* != .table) return;
-
- var it = toml.tableIterator(src);
- while (it.next()) |entry| {
- const existing = dst.get(entry.key);
- if (existing != null and existing.?.* == .table and entry.value.* == .table) {
- try mergeTable(alloc, @constCast(existing.?), entry.value);
- } else {
- const copy = try cloneValue(alloc, entry.value);
- const key_copy = try alloc.dupe(u8, entry.key);
- try tvalue.tableSet(alloc, dst, key_copy, copy);
- }
- }
-}
-
-fn cloneValue(alloc: Allocator, src: *const toml.Value) Allocator.Error!*toml.Value {
- const out = try alloc.create(toml.Value);
- switch (src.*) {
- .table => {
- out.* = .{ .table = .{} };
- var it = toml.tableIterator(src);
- while (it.next()) |entry| {
- const child = try cloneValue(alloc, entry.value);
- const key_copy = try alloc.dupe(u8, entry.key);
- try tvalue.tableSet(alloc, out, key_copy, child);
- }
- },
- .array => |*a| {
- out.* = .{ .array = .{} };
- for (a.items.items) |*item| {
- const child = try cloneValue(alloc, item);
- try tvalue.arrayAppend(alloc, out, child.*);
- }
- },
- .string => |s| out.* = .{ .string = try alloc.dupe(u8, s) },
- else => out.* = src.*,
- }
- return out;
-}
-
/// Parse a TOML string into the given registries. Useful for tests.
pub fn parseInto(models: *Models, source: []const u8) !void {
const doc = try parseDoc(models.defs.allocator, source);
@@ -623,17 +563,6 @@ test "ModelRegistry.get: returns null for unknown alias" {
try testing.expect(models.defs.get("openai", "sonnet") == null);
}
-test "basePath: PANTO_HOME wins" {
- const a = testing.allocator;
- var env: std.process.Environ.Map = .init(a);
- defer env.deinit();
- try env.put("PANTO_HOME", "/tmp/panto-home");
- try env.put("XDG_DATA_HOME", "/ignored");
- const got = try basePath(a, &env);
- defer a.free(got);
- try testing.expectEqualStrings("/tmp/panto-home/models.toml", got);
-}
-
test "basePath: XDG_DATA_HOME falls back before HOME" {
const a = testing.allocator;
var env: std.process.Environ.Map = .init(a);