# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import annotations
from extra_platforms import WINDOWS
from ..capabilities import Delegate, search_capabilities
from ..manager import PackageManager
from .scoop import Scoop
TYPE_CHECKING = False
if TYPE_CHECKING:
from collections.abc import Iterator
from ..package import Package
[docs]
class SFSU(PackageManager):
"""sfsu (Scoop For Speed and Usability) is a Rust reimplementation of
Scoop's slower read paths, working against the same buckets and ``~/scoop``
install tree.
mpm reaches for sfsu only where it is both faster than Scoop and speaks
JSON: ``installed``, ``outdated`` and ``search`` all pass ``--json`` and are
parsed as structured objects instead of the whitespace tables Scoop prints.
.. note::
sfsu implements no mutating verbs, so ``install``, ``remove`` and both
upgrade commands are bound straight to
:class:`~meta_package_manager.managers.scoop.Scoop` through the
:class:`~meta_package_manager.capabilities.Delegate` descriptor: those
operations run the ``scoop`` binary, and a host with sfsu but no Scoop
cannot mutate anything.
"""
# Mutating operations delegate to the Scoop CLI.
_scoop = Delegate(Scoop)
name = "Scoop sfsu"
homepage_url = "https://github.com/winpax/sfsu"
platforms = WINDOWS
requirement = ">=1.16.0"
post_args = ("--no-color",)
version_regexes = (r"sfsu\s+(?P<version>\S+)",)
"""
.. code-block:: pwsh-session
> sfsu --version
sfsu 1.17.2
sprinkles 0.22.0 (crates.io published version)
"""
@property
def installed(self) -> Iterator[Package]:
"""Fetch installed packages.
.. code-block:: pwsh-session
> sfsu list --json
[
{
"name": "7zip",
"version": "26.00",
"source": "main",
"updated": "2026-03-18 17:54:32",
"notes": ""
},
{
"name": "git",
"version": "2.53.0.3",
"source": "main",
"updated": "2026-03-15 09:12:04",
"notes": ""
}
]
"""
output = self.run_cli("list", "--json")
data = self.parse_json(output)
if data:
for pkg in data:
yield self.package(
id=pkg["name"],
installed_version=pkg["version"],
)
@property
def outdated(self) -> Iterator[Package]:
"""Fetch outdated packages.
Uses ``sfsu status --only apps --json`` which returns packages with
available updates.
.. code-block:: pwsh-session
> sfsu status --only apps --json
{
"packages": [
{
"name": "git",
"current": "2.53.0.2",
"available": "2.53.0.3",
"missing_dependencies": [],
"info": null
}
]
}
"""
output = self.run_cli("status", "--only", "apps", "--json")
data = self.parse_json(output)
if data:
for pkg in data.get("packages", []):
yield self.package(
id=pkg["name"],
installed_version=pkg["current"],
latest_version=pkg["available"],
)
[docs]
@search_capabilities(extended_support=False, exact_support=False)
def search(self, query: str, extended: bool, exact: bool) -> Iterator[Package]:
"""Fetch matching packages.
.. caution::
Search does not support extended or exact matching. Results are
refiltered by
:py:meth:`meta_package_manager.manager.PackageManager.refiltered_search`.
.. code-block:: pwsh-session
> sfsu search --json git
{
"main": [
{
"name": "git",
"bucket": "main",
"version": "2.53.0.3",
"installed": true,
"bins": []
},
...
],
...
}
"""
output = self.run_cli("search", "--json", query)
data = self.parse_json(output)
if data:
# Results are grouped by bucket name.
for packages in data.values():
for pkg in packages:
yield self.package(
id=pkg["name"],
latest_version=pkg["version"],
)
install = _scoop.install
upgrade_all_cli = _scoop.upgrade_all_cli
upgrade_one_cli = _scoop.upgrade_one_cli
remove = _scoop.remove
[docs]
def sync(self) -> None:
"""Sync package metadata.
Uses sfsu's native ``update`` command which updates Scoop and all
buckets.
.. code-block:: pwsh-session
> sfsu update --no-color
"""
self.run_cli("update")
[docs]
def cleanup(self) -> None:
"""Removes old versions of all installed apps and clears the cache.
.. code-block:: pwsh-session
> sfsu cleanup --all --cache --no-color
"""
self.run_cli("cleanup", "--all", "--cache")