pub enum Channel {
Installer,
Cargo,
Npm,
UvTool,
Pipx,
Pip,
WinGet,
Scoop,
Homebrew,
Unknown,
}Expand description
The package manager that owns the running binary.
One channel owns one binary. A copy installed through uv is upgraded through uv, never through npm, because two managers writing the same PATH entry would fight over it forever.
Variants§
Installer
install.sh / install.ps1 put it under the managed <config>/bin.
Cargo
cargo install / cargo binstall put it under ~/.cargo/bin.
Npm
npm install -g — the binary lives under a node_modules tree.
UvTool
uv tool install — under uv’s tool environments.
Pipx
pipx install — under a pipx venv.
Pip
pip install — a console script beside a Python interpreter, in the system
scripts directory or a virtualenv’s.
WinGet
winget install — under %LOCALAPPDATA%\Microsoft\WinGet\Packages.
Scoop
scoop install — under ~/scoop/apps, shimmed from ~/scoop/shims.
Homebrew
brew install — under the Cellar, symlinked into the prefix’s bin.
Unknown
Anywhere else: a dev build, a hand-copied binary, a distro package.
Implementations§
Source§impl Channel
impl Channel
Sourcepub fn detect_at(exe: &Path, managed: Option<&Path>) -> Self
pub fn detect_at(exe: &Path, managed: Option<&Path>) -> Self
Classify exe by the directories in its path.
Purely lexical: this must not touch the network or spawn anything, and each
channel’s layout is stable enough that its marker directory is a reliable
fingerprint. managed is passed in rather than resolved here so tests can probe
the classification without a config directory on disk.
The managed path is checked first and the three directory-owning managers next.
Order is load-bearing at least once already: a Scoop install of a Rust toolchain
can put .cargo inside ~/scoop, and misreading that as Cargo would send
devp update to run cargo install against a directory Scoop replaces wholesale.
Sourcepub fn label(self) -> &'static str
pub fn label(self) -> &'static str
How to name this channel in a sentence addressed to the user.
Sourcepub fn upgrade_command(self) -> Option<String>
pub fn upgrade_command(self) -> Option<String>
The command that upgrades through this channel, as the user would type it.
None for Channel::Unknown only: there is no command to name for a binary
somebody copied into place by hand.
Sourcepub fn uninstall_command(self) -> Option<String>
pub fn uninstall_command(self) -> Option<String>
The command that uninstalls through this channel.
None where there is no manager to tell: the installer’s own copy is deleted by
devp uninstall itself, and an unrecognised copy is just a file.
Sourcepub fn owns_its_files(self) -> bool
pub fn owns_its_files(self) -> bool
Whether a package manager keeps a record of this install that deleting the file would falsify.
When true, devp uninstall names the manager’s own command instead of quietly
removing the file: pip list still showing a package whose binary is gone, or
cargo install refusing to reinstall over its own bookkeeping, is worse than a
leftover binary the user was told about.
Sourcepub fn replaces_its_directory(self) -> bool
pub fn replaces_its_directory(self) -> bool
Whether this channel replaces its install directory wholesale on upgrade.
This is the distinction the old per-command classifiers did not have, and the one
that caused a real bug. WinGet, Scoop and Homebrew each version their package
directory and swap the whole thing — …\WinGet\Packages\<id>\, ~/scoop/apps/ <pkg>/<version>/, <prefix>/Cellar/<pkg>/<version>/. Anything dev-prune writes
beside its own executable there is gone at the next upgrade, and anything
pointing at it — a scheduled task, a git hook — is left aimed at a path that no
longer exists.
So nothing durable is ever written into one of these directories. The devp
twin goes to the managed <config>/bin instead, which this program owns and
which no package manager will replace underneath it.
Trait Implementations§
impl Copy for Channel
impl Eq for Channel
impl StructuralPartialEq for Channel
Auto Trait Implementations§
impl Freeze for Channel
impl RefUnwindSafe for Channel
impl Send for Channel
impl Sync for Channel
impl Unpin for Channel
impl UnsafeUnpin for Channel
impl UnwindSafe for Channel
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more