Luigit
repositories / termux-janitor

termux-janitor

Interactive cleanup assistant for Termux: transparent, safe, confirmed disk reclamation.

owned by admin

tools/janitor/model.zig

Raw
// Termux Janitor product registries layered onto the generic engine graph.
const std = @import("std");
const engine = @import("engine");
const profile = @import("profile");

pub const limit_count_max = 128;
pub const cli_option_count_max = 32;
pub const cli_exit_status_count_max = 16;
pub const android_package_count_max = 32;
pub const classification_value_count_max = 128;
pub const threshold_count_max = 16;

pub const limits_path = "spec/model/limits.zon";
pub const cli_path = "spec/model/cli.zon";
pub const android_packages_path = "spec/model/android_packages.zon";
pub const classification_path = "spec/model/classification.zon";
pub const thresholds_path = "spec/model/thresholds.zon";
pub const package_path = "build.zig.zon";

/// The complete repository model: the generic relationship graph plus the
/// product-domain registries owned by this repository alone.
pub const Model = struct {
    graph: engine.model.Graph,
    limits: []const Limit,
    cli: Cli,
    android_packages: []const AndroidPackage,
    classification: Classification,
    thresholds: []const Threshold,
    package: Package,

    pub fn load(allocator: std.mem.Allocator, io: std.Io) !Model {
        return .{
            .graph = try engine.model.Graph.load(allocator, io),
            .limits = try engine.model.parseFile(
                []const Limit,
                allocator,
                io,
                limits_path,
            ),
            .cli = try engine.model.parseFile(
                Cli,
                allocator,
                io,
                cli_path,
            ),
            .android_packages = try engine.model.parseFile(
                []const AndroidPackage,
                allocator,
                io,
                android_packages_path,
            ),
            .classification = try engine.model.parseFile(
                Classification,
                allocator,
                io,
                classification_path,
            ),
            .thresholds = try engine.model.parseFile(
                []const Threshold,
                allocator,
                io,
                thresholds_path,
            ),
            .package = try loadPackage(allocator, io),
        };
    }
};

/// The package manifest owns the one canonical version required by
/// `spec/ARTIFACT.md`. Unknown manifest fields belong to the Zig toolchain, so
/// this parse ignores them instead of duplicating the manifest schema.
pub const Package = struct {
    version: []const u8,
};

fn loadPackage(allocator: std.mem.Allocator, io: std.Io) !Package {
    const bytes = try std.Io.Dir.cwd().readFileAlloc(
        io,
        package_path,
        allocator,
        .limited(engine.model.file_size_max),
    );
    const source = try allocator.dupeZ(u8, bytes);
    return std.zon.parse.fromSliceAlloc(
        Package,
        allocator,
        source,
        null,
        .{ .ignore_unknown_fields = true, .free_on_error = true },
    );
}

comptime {
    std.debug.assert(profile.suiteIsConcrete(.u));
}

pub const AndroidPackage = struct {
    id: []const u8,
    revision: u32,
    identifier: []const u8,
    role: AndroidPackageRole,
    requirement: engine.model.RequirementReference,
};

pub const AndroidPackageRole = enum {
    base,
    add_on,
};

pub const Classification = struct {
    source_basenames: ClassificationSet,
    source_suffixes: ClassificationSet,
    document_roots: ClassificationSet,
};

pub const ClassificationSet = struct {
    revision: u32,
    values: []const []const u8,
    requirement: engine.model.RequirementReference,
};

pub const Threshold = struct {
    id: []const u8,
    revision: u32,
    key: []const u8,
    value: u64,
    unit: ThresholdUnit,
    requirement: engine.model.RequirementReference,
};

pub const ThresholdUnit = enum {
    bytes,
    seconds,
};

pub const Limit = struct {
    id: []const u8,
    category: LimitCategory,
    label: []const u8,
    value: LimitValue,
};

pub const LimitCategory = enum {
    scan,
    process,
    queue,
    terminal,
    logging,
};

pub const LimitValue = union(enum) {
    scalar: ScalarLimit,
    dimensions: DimensionsLimit,
    alternative: AlternativeLimit,
};

pub const ScalarLimit = struct {
    value: u64,
    unit: LimitUnit,
};

pub const DimensionsLimit = struct {
    columns: u16,
    rows: u16,
};

pub const AlternativeLimit = struct {
    items: u32,
    milliseconds: u32,
};

pub const LimitUnit = enum {
    bytes,
    cells,
    components,
    count,
    milliseconds,
    minutes,
    seconds,
};

pub const Cli = struct {
    executable: []const u8,
    description: []const u8,
    default_mode: []const u8,
    long_options_only: bool,
    option_terminator: bool,
    operands_allowed: bool,
    duplicate_options_allowed: bool,
    unattended_cleanup: bool,
    informational_stream: OutputStream,
    usage_stream: OutputStream,
    diagnostic_stream: OutputStream,
    options: []const CliOption,
    argument_rules: []const CliArgumentRule,
    exit_statuses: []const CliExitStatus,
};

pub const OutputStream = enum {
    stdout,
    stderr,
};

pub const CliOption = struct {
    id: []const u8,
    spelling: []const u8,
    argument: CliArgument,
    summary: []const u8,
    must_appear_alone: bool,
    bypasses_startup: []const StartupSubsystem,
    requires_terminal: bool,
};

pub const StartupSubsystem = enum {
    configuration,
    logging,
    terminal_initialization,
    scanning,
};

pub const CliArgument = enum {
    none,
    path,
};

pub const CliArgumentRule = struct {
    option: []const u8,
    argument: []const u8,
    arity: u8,
    syntax: ArgumentSyntax,
    nonempty: bool,
    relative_base: RelativeBase,
};

pub const ArgumentSyntax = enum {
    separate,
};

pub const RelativeBase = enum {
    initial_working_directory,
};

pub const CliExitStatus = struct {
    id: []const u8,
    code: u8,
    condition: []const u8,
};