From 6cbfc96e3d5d91ac0821c9e00e6a10737add4aed Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Wed, 25 Feb 2026 20:32:16 +0000 Subject: [PATCH] feat: add Target type for per-package version/repo/source constraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Breaking change to Manager interface: Install/Remove/Purge now accept []Target instead of []string. Target supports Version pinning, FromRepo constraints, and Source paths — matching SaltStack's pkgs list semantics. Also adds WithRefresh, WithFromRepo, WithReinstall options. --- snack.go | 45 ++++++++++++++++++++++++++++++++++++++++++--- types.go | 20 ++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/snack.go b/snack.go index 63e5f18..37db2ad 100644 --- a/snack.go +++ b/snack.go @@ -7,16 +7,55 @@ package snack import "context" +// Target represents a package to install, remove, or otherwise act on. +// At minimum, Name must be set. Version and other fields constrain the action. +// +// Modeled after SaltStack's pkgs list, which accepts both plain names +// and name:version mappings: +// +// pkgs: +// - nginx +// - redis: ">=7.0" +// - curl: "8.5.0-1" +type Target struct { + // Name is the package name (required). + Name string + + // Version pins a specific version. If empty, the latest is used. + // Comparison operators are supported where the backend allows them + // (e.g. ">=1.2.3", "<2.0", "1.2.3-4"). + Version string + + // FromRepo constrains the install to a specific repository + // (e.g. "unstable", "community", "epel"). + FromRepo string + + // Source is a local file path or URL for package files + // (e.g. .deb, .rpm, .pkg.tar.zst). When set, Name is used only + // for display/logging. + Source string +} + +// Targets is a convenience constructor for a slice of [Target] from +// plain package names (no version constraint). +func Targets(names ...string) []Target { + targets := make([]Target, len(names)) + for i, name := range names { + targets[i] = Target{Name: name} + } + return targets +} + // Manager is the common interface implemented by all package manager wrappers. type Manager interface { // Install one or more packages. - Install(ctx context.Context, pkgs []string, opts ...Option) error + Install(ctx context.Context, pkgs []Target, opts ...Option) error // Remove one or more packages. - Remove(ctx context.Context, pkgs []string, opts ...Option) error + Remove(ctx context.Context, pkgs []Target, opts ...Option) error // Purge one or more packages (remove including config files). - Purge(ctx context.Context, pkgs []string, opts ...Option) error + Purge(ctx context.Context, pkgs []Target, opts ...Option) error // Upgrade all installed packages to their latest versions. Upgrade(ctx context.Context, opts ...Option) error diff --git a/types.go b/types.go index 45f4787..bf1a9c4 100644 --- a/types.go +++ b/types.go @@ -17,6 +17,9 @@ type Options struct { DryRun bool Root string // alternate root filesystem Verbose bool + Refresh bool // refresh package index before operation + FromRepo string // constrain operation to a specific repository + Reinstall bool // reinstall already-installed packages } // Option is a functional option for package manager operations. @@ -47,6 +50,23 @@ func WithVerbose() Option { return func(o *Options) { o.Verbose = true } } +// WithRefresh refreshes the package database before the operation. +// Equivalent to pacman -Sy, apt-get update, apk update, etc. +func WithRefresh() Option { + return func(o *Options) { o.Refresh = true } +} + +// WithFromRepo constrains the operation to a specific repository. +// e.g. "unstable" for apt, "community" for pacman. +func WithFromRepo(repo string) Option { + return func(o *Options) { o.FromRepo = repo } +} + +// WithReinstall forces reinstallation of already-installed packages. +func WithReinstall() Option { + return func(o *Options) { o.Reinstall = true } +} + // ApplyOptions processes functional options into an Options struct. func ApplyOptions(opts ...Option) Options { var o Options