diff options
| author | Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com> | 2026-03-06 17:10:10 +0100 |
|---|---|---|
| committer | Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com> | 2026-03-08 18:56:49 +0100 |
| commit | 508feb2abf27ea991bbcc0ba163e3f64cd60b67b (patch) | |
| tree | 7997409de94305702029f8601663a806ac1ea3e0 /src/Xmobar/Plugins | |
| parent | 39fd70308c3aef5402abe7152ade76ff7bb331bb (diff) | |
| download | xmobar-508feb2abf27ea991bbcc0ba163e3f64cd60b67b.tar.gz xmobar-508feb2abf27ea991bbcc0ba163e3f64cd60b67b.tar.bz2 | |
Improve `PacmanUpdates`
Recently, I've seen the usefulness in knowning whether one of the pending updates is a kernel update (which means we should most likely reboot after next system update), and also whether the running kernel is older than the installed kernel package (which means we've probably not rebooted yet), so I thought I should update this plugin.
However, the feature of knowing whether a kernel update is contained in the available updates requires that one knows the kernel package name, so that's to be a `String` input to the plugin. But one might not want to pass it, and be happy with just the detection of whether the running kernel is older than the installed one.
Since recently, I've been studying type families and also come across pattern synonyms, I thought I could leverage these, so I thought I could parametrize the plugin over kind `Bool`, and branch on it to provide the different APIs; then I could use type synonyms to lift the burden of having to use type application at the user code.
Furthermore, rather than still customizing the message by asking the user to provide `String`s for the various cases (0 updates, 1 update, 2+ updates), I thought it was better to ask the caller to provide a function that accepts the relevant inputs (`Int` number of updates, `Bool` telling whether such and such) and turns them into a `String`.
With this change, I'm
- renaming the previous type `PacmanUpdates` to
`PacmanUpdatesDeprecated`
- deprecating such type (with a pragma and by printing a clickable
note in the plugin text) with the intention of deleting it in a year
from now,
- preserving its data ctor's to avoid breaking existing code right
now,
- creating a new `PacmanUpdates` type that is parametrized over kind
`Bool`
- the `True` instance allows passing the name of the kernel package
that the plugin uses to detect whether there's a pending kernel
update
- the `False` instance doesn't,
- accordingly the two instances accept from the caller a printing
function with different signature (see haddock comments for
details),
- hiding (i.e. not exporting) the constructor of such new type,
- provided pattern synonyms to more conveniently create a
`PacmanUpdates False` (for `PacmanUpdates True` is just the same as
the ctor),
- changing the approach with which the final `String` is produced,
from asking the user to provide a bunch of some sort of template
`String`, to asking them for a function that produces a `String`
given the required inputs (e.g. the number of available updates).
Diffstat (limited to 'src/Xmobar/Plugins')
| -rw-r--r-- | src/Xmobar/Plugins/PacmanUpdates.hs | 139 |
1 files changed, 123 insertions, 16 deletions
diff --git a/src/Xmobar/Plugins/PacmanUpdates.hs b/src/Xmobar/Plugins/PacmanUpdates.hs index 1e8a8fc..ad6d2c9 100644 --- a/src/Xmobar/Plugins/PacmanUpdates.hs +++ b/src/Xmobar/Plugins/PacmanUpdates.hs @@ -1,4 +1,9 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE PatternSynonyms #-} ----------------------------------------------------------------------------- @@ -14,30 +19,132 @@ Maintainer : Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com> Stability : unstable Portability : unportable -A Pacman updates availablility plugin for Xmobar +A Pacman updates availablility plugin for Xmobar. -} -module Xmobar.Plugins.PacmanUpdates (PacmanUpdates (..)) where +module Xmobar.Plugins.PacmanUpdates ( + {-# DEPRECATED "These type and constructor are DEPRECATED;\ + please use `PacmanUpdates` type and `PacmanUpdatesK`\ + and `PacmanUpdatesNoK` constructors" #-} + PacmanUpdatesDeprecated(PacmanUpdates) + , PacmanUpdates () + , pattern PacmanUpdatesK + , pattern PacmanUpdatesNoK) where import System.Exit (ExitCode (..)) import System.Process (readProcessWithExitCode) import Xmobar.Plugins.Command (Rate) import Xmobar.Run.Exec +import Data.Tuple.Extra (fst3) +import Data.Kind (Type) +import Data.Functor ((<&>)) +import Data.Void (Void) +import Control.Arrow ((&&&)) +import qualified Data.Vector as V -data PacmanUpdates = PacmanUpdates (String, String, String, String) Rate +-- | Deprecated plugin type. +data PacmanUpdatesDeprecated + = PacmanUpdates (String, String, String, String) -- ^ strings to be shown for 0, 1, ≥ 2 updates, + -- and for error respectively (in the 3rd string, for + -- ≥ 2 updates, any occurrence of the '?' character + -- is a placeholder for the number of available updates). + Rate -- ^ rate of update deriving (Read, Show) -instance Exec PacmanUpdates where +instance Exec PacmanUpdatesDeprecated where alias = const "pacman" rate (PacmanUpdates _ r) = r - run (PacmanUpdates (z, o, m, e) _) = do - (exit, stdout, _) <- readProcessWithExitCode "checkupdates" [] "" - return $ case exit of - ExitFailure 2 -> z -- ero updates - ExitFailure 1 -> e - ExitSuccess -> case length $ lines stdout of - 0 -> impossible - 1 -> o - n -> m >>= \c -> if c == '?' then show n else pure c - _ -> impossible - where - impossible = error "This is impossible based on pacman manpage" + run (PacmanUpdates (z, o, m, e) r) + = Xmobar.Run.Exec.run (Make @False r undefined printer) + where printer = const + $ (++ deprecationNote) + . \case Left _ -> e + Right 0 -> z + Right 1 -> o + Right n -> m >>= \c -> if c == '?' + then show n + else pure c + deprecationNote = " <fc=#ff0000>(<action=`xdg-open https://codeberg.org/xmobar/xmobar/pulls/723`>" + ++ "deprecated plugin, click here</action>)</fc>" + +-- | PacmanUpdates plugin parametrized over `Bool` kind: if `True` the plugin +-- will detect if there's pending update(s) for the kernel package; if `False` +-- it wont. +data PacmanUpdates (b :: Bool) + = Make -- ^ Constructor + Rate -- ^ Rate of update (see [Xmobar doc](https://codeberg.org/xmobar/xmobar/src/commit/39fd70308c3aef5402abe7152ade76ff7bb331bb/src/Xmobar/Plugins/Command.hs#L34)). + (Arg b) -- ^ Optional further argument. See instances of `Updates`. + (Printer b) -- ^ Printer. See instances of `Updates` for its signature. + +instance Show (PacmanUpdates b) where + show = error "PacmanUpdates: Show instance is stub" + +instance Read (PacmanUpdates b) where + readsPrec = error "PacmanUpdates: Read instance is stub" + +instance Updates b => Exec (PacmanUpdates (b :: Bool)) where + alias = const "pacman" + rate (Make r _ _) = r + run = Xmobar.Plugins.PacmanUpdates.run' + +class Updates (b :: Bool) where + -- | See `Updates`'s instances. + type Arg b = (a :: Type) | a -> b + -- | See `Updates`'s instances. + type Printer b = (p :: Type) | p -> b + -- | This is the implementation of `Xmobar.Run.Exec.run`. + run' :: PacmanUpdates b -> IO String + +-- | No additional argument required for constructing the plugin; +-- the user-provided printer is fed with a `Bool` telling whether +-- the system is running an outdated kernel, and an `Int` telling +-- the number of available updates (or `Left` if an error occurred +-- when calling `checkupdates`). +instance Updates False where + type Arg False = Void + type Printer False = Bool -> Either String Int -> String + run' (Make _ _ printer) + = printer + <$> kernIsOld + <*> (fmap V.length <$> checkUpdates) + +-- | Constructing the plugin requires an additional `String` telling the name +-- name of the kernel package; the user-provided printer is fed with a `Bool` +-- telling whether the system is running an outdated kernel, and an `(Int, +-- Bool)` pair telling the number of available updates and whether one of these +-- is a kernel update (or `Left` if an error occurred when calling +-- `checkupdates`). +instance Updates True where + type Arg True = String + type Printer True = Bool -> Either String (Int, Bool) -> String + run' (Make _ kernName printer) + = printer + <$> kernIsOld + <*> (fmap (V.length &&& elem kernName) <$> checkUpdates) + +-- | Pattern synonym used to construct a `PacmanUpdates True`. +pattern PacmanUpdatesK :: Rate -> Arg True -> Printer True -> PacmanUpdates True +pattern PacmanUpdatesK r a p = Make r a p + +-- | Pattern synonym used to construct a `PacmanUpdates False`. +pattern PacmanUpdatesNoK :: Rate -> Printer False -> PacmanUpdates False +pattern PacmanUpdatesNoK r p <- Make r _ p + where PacmanUpdatesNoK r p = Make r undefined p + +checkUpdates :: IO (Either String (V.Vector String)) +checkUpdates = readProcessWithExitCode "checkupdates" [] "" + <&> \case (ExitFailure 2, "", "") -> Right V.empty + (ExitSuccess, stdout, "") + -> let pkgName = takeWhile (/= ' ') + pkgs = V.fromList $ fmap pkgName $ lines stdout + in case V.length pkgs of + 0 -> impossible + _ -> Right pkgs + (ExitFailure 1, _, _) -> Left "checkupdates: unknown cause of failure." + _ -> impossible + +kernIsOld :: IO Bool +kernIsOld = (/= ExitSuccess) . exitCode <$> readProcessWithExitCode "modinfo" ["-n", "i915"] "" + where exitCode = fst3 + +impossible :: a +impossible = error "This is impossible, according to my knowledge." |
