summaryrefslogtreecommitdiffhomepage
path: root/src/Xmobar/Plugins/PacmanUpdates.hs
blob: ad6d2c9cd19afe3d2741c7f0e384bc007c92853e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE PatternSynonyms #-}

-----------------------------------------------------------------------------

-----------------------------------------------------------------------------

{- |
Module      :  Plugins.Monitors.PacmanUpdates
Copyright   :  (c) 2024 Enrico Maria De Angelis
            ,  (c) 2025 Alexander Pankoff
License     :  BSD-style (see LICENSE)

Maintainer  :  Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com>
Stability   :  unstable
Portability :  unportable

A Pacman updates availablility plugin for Xmobar.
-}
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

-- | 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 PacmanUpdatesDeprecated where
  alias = const "pacman"
  rate (PacmanUpdates _ r) = r
  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."