PranitInitial commit439f83c3 months agoHistory
1479 lines · 51.4 KB · Zig
RawDownload
const std = @import("std");const io_mod = @import("../shared/io.zig");const process_supervisor = @import("process_supervisor.zig");const session_child_store = @import("../session/session_child_store.zig"); const Allocator = std.mem.Allocator;const legacy_schema_version: i64 = 1;const schema_version: i64 = 2;const max_record_bytes: usize = 256 * 1024;pub const StableBackgroundRecordId =    process_supervisor.StableBackgroundRecordId; pub const LogStorage = union(enum) {    managed_session: struct {        managed_log_name: []u8,    },    external: struct {        path: []u8,    },     pub fn deinit(self: *LogStorage, alloc: Allocator) void {        switch (self.*) {            .managed_session => |value| alloc.free(value.managed_log_name),            .external => |value| alloc.free(value.path),        }        self.* = undefined;    }}; pub fn renderLogStorageJson(    alloc: Allocator,    storage: LogStorage,) ![]u8 {    var out: std.Io.Writer.Allocating = .init(alloc);    defer out.deinit();    switch (storage) {        .managed_session => |value| {            try session_child_store.SessionChildCapability.validateManagedName(                value.managed_log_name,            );            try out.writer.writeAll(                "{\"kind\":\"managed_session\",\"managed_log_name\":",            );            try std.json.Stringify.value(                value.managed_log_name,                .{},                &out.writer,            );        },        .external => |value| {            if (!std.fs.path.isAbsolute(value.path)) {                return error.InvalidBackgroundRecord;            }            try out.writer.writeAll("{\"kind\":\"external\",\"path\":");            try std.json.Stringify.value(value.path, .{}, &out.writer);        },    }    try out.writer.writeByte('}');    return out.toOwnedSlice();} pub fn parseLogStorageJson(    alloc: Allocator,    json_text: []const u8,) !LogStorage {    var parsed = std.json.parseFromSlice(        std.json.Value,        alloc,        json_text,        .{},    ) catch return error.InvalidBackgroundRecord;    defer parsed.deinit();    const object = try requireObject(parsed.value);    const kind = try requireString(object, "kind");    if (std.mem.eql(u8, kind, "managed_session")) {        const name = try requireString(object, "managed_log_name");        session_child_store.SessionChildCapability.validateManagedName(name) catch            return error.InvalidBackgroundRecord;        return .{ .managed_session = .{            .managed_log_name = try alloc.dupe(u8, name),        } };    }    if (std.mem.eql(u8, kind, "external")) {        const path = try requireString(object, "path");        if (!std.fs.path.isAbsolute(path)) {            return error.InvalidBackgroundRecord;        }        return .{ .external = .{            .path = try alloc.dupe(u8, path),        } };    }    return error.InvalidBackgroundRecord;} pub fn classifyLegacyLogStorage(    alloc: Allocator,    capability: *session_child_store.SessionChildCapability,    legacy_path: []const u8,    explicit_external_dir: ?[]const u8,) !?LogStorage {    if (!std.fs.path.isAbsolute(legacy_path)) return null;    const parent = std.fs.path.dirname(legacy_path) orelse return null;    const basename = std.fs.path.basename(legacy_path);    session_child_store.SessionChildCapability.validateManagedName(basename) catch        return null;     const managed_route = try capability.displayRoutePath(        alloc,        .background_logs,    );    defer alloc.free(managed_route);    if (std.mem.eql(u8, parent, managed_route)) {        return .{ .managed_session = .{            .managed_log_name = try alloc.dupe(u8, basename),        } };    }    const external_dir = explicit_external_dir orelse return null;    if (!std.mem.eql(u8, parent, external_dir)) return null;    return .{ .external = .{        .path = try alloc.dupe(u8, legacy_path),    } };} test "managed and external background log storage remain distinct" {    const alloc = std.testing.allocator;    var managed = LogStorage{        .managed_session = .{            .managed_log_name = try alloc.dupe(u8, "managed.log"),        },    };    defer managed.deinit(alloc);    var external = LogStorage{        .external = .{            .path = try alloc.dupe(u8, "/tmp/managed.log"),        },    };    defer external.deinit(alloc);     switch (managed) {        .managed_session => |value| try std.testing.expectEqualStrings(            "managed.log",            value.managed_log_name,        ),        .external => return error.ExpectedManagedSessionStorage,    }    switch (external) {        .external => |value| try std.testing.expectEqualStrings(            "/tmp/managed.log",            value.path,        ),        .managed_session => return error.ExpectedExternalStorage,    }} test "background log storage codec preserves managed and external identity" {    const alloc = std.testing.allocator;    const cases = [_]LogStorage{        .{ .managed_session = .{            .managed_log_name = @constCast("managed.log"),        } },        .{ .external = .{            .path = @constCast("/tmp/external.log"),        } },    };     for (cases) |storage| {        const encoded = try renderLogStorageJson(alloc, storage);        defer alloc.free(encoded);        var decoded = try parseLogStorageJson(alloc, encoded);        defer decoded.deinit(alloc);        switch (storage) {            .managed_session => |expected| switch (decoded) {                .managed_session => |actual| try std.testing.expectEqualStrings(                    expected.managed_log_name,                    actual.managed_log_name,                ),                .external => return error.ExpectedManagedSessionStorage,            },            .external => |expected| switch (decoded) {                .external => |actual| try std.testing.expectEqualStrings(                    expected.path,                    actual.path,                ),                .managed_session => return error.ExpectedExternalStorage,            },        }    }} test "legacy log path maps to managed only for exact display parent" {    const alloc = std.testing.allocator;    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    try tmp.dir.createDir(        io_mod.getIo(),        "session-logs",        std.Io.File.Permissions.fromMode(0o700),    );    try tmp.dir.createDir(        io_mod.getIo(),        "external-logs",        std.Io.File.Permissions.fromMode(0o700),    );    const managed_dir = try io_mod.dirRealpathAlloc(        alloc,        tmp.dir,        "session-logs",    );    defer alloc.free(managed_dir);    const external_dir = try io_mod.dirRealpathAlloc(        alloc,        tmp.dir,        "external-logs",    );    defer alloc.free(external_dir);    var capability = try session_child_store.SessionChildCapability.initLegacyRoute(        alloc,        managed_dir,        .background_logs,        .read_only,    );    defer capability.deinit();     const managed_path = try std.fs.path.join(        alloc,        &.{ managed_dir, "managed.log" },    );    defer alloc.free(managed_path);    var managed = (try classifyLegacyLogStorage(        alloc,        &capability,        managed_path,        external_dir,    )).?;    defer managed.deinit(alloc);    switch (managed) {        .managed_session => |value| try std.testing.expectEqualStrings(            "managed.log",            value.managed_log_name,        ),        .external => return error.ExpectedManagedSessionStorage,    }     const external_path = try std.fs.path.join(        alloc,        &.{ external_dir, "external.log" },    );    defer alloc.free(external_path);    var external = (try classifyLegacyLogStorage(        alloc,        &capability,        external_path,        external_dir,    )).?;    defer external.deinit(alloc);    switch (external) {        .external => |value| try std.testing.expectEqualStrings(            external_path,            value.path,        ),        .managed_session => return error.ExpectedExternalStorage,    }     const unauthorized = try std.fs.path.join(        alloc,        &.{ external_dir, "..", "other.log" },    );    defer alloc.free(unauthorized);    try std.testing.expect((try classifyLegacyLogStorage(        alloc,        &capability,        unauthorized,        external_dir,    )) == null);} test "managed background read only absence does not create route" {    const alloc = std.testing.allocator;    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    try tmp.dir.createDir(        io_mod.getIo(),        "session",        std.Io.File.Permissions.fromMode(0o700),    );    var session_dir = try tmp.dir.openDir(io_mod.getIo(), "session", .{        .iterate = true,        .follow_symlinks = false,    });    defer session_dir.close(io_mod.getIo());    const session_path = try io_mod.dirRealpathAlloc(        alloc,        tmp.dir,        "session",    );    defer alloc.free(session_path);    var capability = try session_child_store.SessionChildCapability.initForTesting(        alloc,        session_dir,        session_path,        .read_only,        .{},    );    defer capability.deinit();    const store = Store.initManaged(&capability);     try std.testing.expectError(        error.BackgroundRecordNotFound,        store.load(alloc, 1),    );    var records = try store.list(alloc);    defer records.deinit(alloc);    try std.testing.expectEqual(@as(usize, 0), records.items.len);    try std.testing.expectError(        error.FileNotFound,        session_dir.statFile(io_mod.getIo(), "background", .{}),    );} pub const TaskState = process_supervisor.TaskState; pub const Record = struct {    id: u64,    background_record_id: ?StableBackgroundRecordId = null,    process_token: ?[]u8 = null,    pid: []u8,    command: []u8,    cwd: []u8,    log_path: []u8,    log_storage: ?LogStorage = null,    expect_url: bool,    server_url: ?[]u8 = null,    started_at_ms: i64,    updated_at_ms: i64,    exit_code: ?i32 = null,    state: TaskState,    diagnostic: ?[]u8 = null,     pub fn deinit(self: *Record, alloc: Allocator) void {        alloc.free(self.pid);        if (self.process_token) |process_token| alloc.free(process_token);        alloc.free(self.command);        alloc.free(self.cwd);        alloc.free(self.log_path);        if (self.log_storage) |*storage| storage.deinit(alloc);        if (self.server_url) |url| alloc.free(url);        if (self.diagnostic) |diagnostic| alloc.free(diagnostic);        self.* = undefined;    }     pub fn fromTaskSnapshot(alloc: Allocator, snapshot: process_supervisor.TaskSnapshot, updated_at_ms: i64) !Record {        const pid = try alloc.dupe(u8, snapshot.pid);        errdefer alloc.free(pid);        const command = try alloc.dupe(u8, snapshot.command);        errdefer alloc.free(command);        const cwd = try alloc.dupe(u8, snapshot.cwd);        errdefer alloc.free(cwd);        const log_path = try alloc.dupe(u8, snapshot.log_path);        errdefer alloc.free(log_path);         var server_url: ?[]u8 = null;        errdefer if (server_url) |url| alloc.free(url);        if (snapshot.server_url) |url| {            server_url = try alloc.dupe(u8, url);        }         return .{            .id = snapshot.durable_record_id orelse snapshot.id,            .background_record_id = snapshot.background_record_id,            .process_token = if (snapshot.process_token) |token|                try alloc.dupe(u8, token.view())            else                null,            .pid = pid,            .command = command,            .cwd = cwd,            .log_path = log_path,            .log_storage = if (snapshot.managed_log_name) |name|                .{ .managed_session = .{                    .managed_log_name = try alloc.dupe(u8, name),                } }            else                .{ .external = .{                    .path = try alloc.dupe(u8, snapshot.log_path),                } },            .expect_url = snapshot.expect_url,            .server_url = server_url,            .started_at_ms = snapshot.started_at_ms,            .updated_at_ms = updated_at_ms,            .exit_code = snapshot.exit_code,            .state = snapshot.state,        };    }}; pub const Store = struct {    capability: *session_child_store.SessionChildCapability,    owned_capability: ?*session_child_store.SessionChildCapability = null,    display_route_path: ?[]u8 = null,     pub fn initManaged(        capability: *session_child_store.SessionChildCapability,    ) Store {        return .{ .capability = capability };    }     /// Transitional legacy-only constructor retained until adapter conversion.    pub fn initWithDir(alloc: Allocator, dir_path: []const u8) !Store {        const owned = try alloc.create(session_child_store.SessionChildCapability);        errdefer alloc.destroy(owned);        owned.* = try session_child_store.SessionChildCapability.initLegacyRoute(            alloc,            dir_path,            .background_records,            .writable,        );        const display_route_path = try alloc.dupe(u8, dir_path);        errdefer alloc.free(display_route_path);        return .{            .capability = owned,            .owned_capability = owned,            .display_route_path = display_route_path,        };    }     pub fn initReadOnlyWithDir(        alloc: Allocator,        dir_path: []const u8,    ) !Store {        const owned = try alloc.create(            session_child_store.SessionChildCapability,        );        errdefer alloc.destroy(owned);        owned.* = try session_child_store.SessionChildCapability.initLegacyRoute(            alloc,            dir_path,            .background_records,            .read_only,        );        const display_route_path = try alloc.dupe(u8, dir_path);        errdefer alloc.free(display_route_path);        return .{            .capability = owned,            .owned_capability = owned,            .display_route_path = display_route_path,        };    }     pub fn deinit(self: *Store, alloc: Allocator) void {        if (self.owned_capability) |owned| {            owned.deinit();            alloc.destroy(owned);        }        if (self.display_route_path) |path| alloc.free(path);        self.* = undefined;    }     pub fn sameBacking(self: Store, other: Store) bool {        if (self.display_route_path) |left| {            const right = other.display_route_path orelse return false;            return std.mem.eql(u8, left, right);        }        return other.display_route_path == null and            self.capability == other.capability;    }     pub fn nextId(self: Store) !u64 {        var max_id: u64 = 0;        var entries = try self.capability.iterate(            std.heap.c_allocator,            .background_records,        );        defer entries.deinit();        for (entries.names) |name| {            if (!std.mem.endsWith(u8, name, ".json")) continue;            const basename = name[0 .. name.len - ".json".len];            const id = std.fmt.parseUnsigned(u64, basename, 10) catch continue;            max_id = @max(max_id, id);        }         return max_id + 1;    }     pub fn saveTaskSnapshot(self: Store, alloc: Allocator, snapshot: process_supervisor.TaskSnapshot) !void {        var record = try Record.fromTaskSnapshot(alloc, snapshot, io_mod.milliTimestamp());        defer record.deinit(alloc);        try self.saveRecord(alloc, record);    }     pub fn saveRecord(self: Store, alloc: Allocator, record: Record) !void {        const name = try recordName(alloc, record.id);        defer alloc.free(name);        try self.validateIndeterminateCurrent(alloc);         const text = try renderRecordJson(alloc, record);        defer alloc.free(text);         var entry = try self.capability.atomicReplace(            alloc,            .background_records,

Lines 1–500 of 1479.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t