module Propellor.Property.Firewall (
rule,
installed,
Chain(..),
Table(..),
Target(..),
Proto(..),
Rules(..),
ConnectionState(..),
ICMPTypeMatch(..),
TCPFlag(..),
Frequency(..),
IPWithMask(..),
) where
import qualified Data.Semigroup as Sem
import Data.Char
import Data.List
import Propellor.Base
import qualified Propellor.Property.Apt as Apt
import qualified Propellor.Property.Network as Network
installed :: Property DebianLike
installed :: Property DebianLike
installed = [String] -> Property DebianLike
Apt.installed [String
"iptables"]
rule :: Chain -> Table -> Target -> Rules -> Property Linux
rule :: Chain -> Table -> Target -> Rules -> Property Linux
rule Chain
c Table
tb Target
tg Rules
rs = String -> Propellor Result -> Property Linux
forall {k} (metatypes :: k).
SingI metatypes =>
String -> Propellor Result -> Property (MetaTypes metatypes)
property (String
"firewall rule: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Rule -> String
forall a. Show a => a -> String
show Rule
r) Propellor Result
addIpTable
where
r :: Rule
r = Chain -> Table -> Target -> Rules -> Rule
Rule Chain
c Table
tb Target
tg Rules
rs
addIpTable :: Propellor Result
addIpTable = IO Result -> Propellor Result
forall a. IO a -> Propellor a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Result -> Propellor Result) -> IO Result -> Propellor Result
forall a b. (a -> b) -> a -> b
$ do
let args :: [CommandParam]
args = Rule -> [CommandParam]
toIpTable Rule
r
Bool
exist <- String -> [CommandParam] -> IO Bool
boolSystem String
"iptables" ([CommandParam] -> [CommandParam]
chk [CommandParam]
args)
if Bool
exist
then Result -> IO Result
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Result
NoChange
else Bool -> Result
forall t. ToResult t => t -> Result
toResult (Bool -> Result) -> IO Bool -> IO Result
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [CommandParam] -> IO Bool
boolSystem String
"iptables" ([CommandParam] -> [CommandParam]
add [CommandParam]
args)
add :: [CommandParam] -> [CommandParam]
add [CommandParam]
params = String -> CommandParam
Param String
"-A" CommandParam -> [CommandParam] -> [CommandParam]
forall a. a -> [a] -> [a]
: [CommandParam]
params
chk :: [CommandParam] -> [CommandParam]
chk [CommandParam]
params = String -> CommandParam
Param String
"-C" CommandParam -> [CommandParam] -> [CommandParam]
forall a. a -> [a] -> [a]
: [CommandParam]
params
toIpTable :: Rule -> [CommandParam]
toIpTable :: Rule -> [CommandParam]
toIpTable Rule
r = (String -> CommandParam) -> [String] -> [CommandParam]
forall a b. (a -> b) -> [a] -> [b]
map String -> CommandParam
Param ([String] -> [CommandParam]) -> [String] -> [CommandParam]
forall a b. (a -> b) -> a -> b
$
Chain -> String
forall t. ConfigurableValue t => t -> String
val (Rule -> Chain
ruleChain Rule
r) String -> [String] -> [String]
forall a. a -> [a] -> [a]
:
[String
"-t", Table -> String
forall t. ConfigurableValue t => t -> String
val (Rule -> Table
ruleTable Rule
r), String
"-j", Target -> String
forall t. ConfigurableValue t => t -> String
val (Rule -> Target
ruleTarget Rule
r)] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
Rules -> [String]
toIpTableArg (Rule -> Rules
ruleRules Rule
r)
toIpTableArg :: Rules -> [String]
toIpTableArg :: Rules -> [String]
toIpTableArg Rules
Everything = []
toIpTableArg (Proto Proto
proto) = [String
"-p", (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Proto -> String
forall a. Show a => a -> String
show Proto
proto]
toIpTableArg (DPort Port
port) = [String
"--dport", Port -> String
forall t. ConfigurableValue t => t -> String
val Port
port]
toIpTableArg (DPortRange (Port
portf, Port
portt)) =
[String
"--dport", Port -> String
forall t. ConfigurableValue t => t -> String
val Port
portf String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Port -> String
forall t. ConfigurableValue t => t -> String
val Port
portt]
toIpTableArg (InIFace String
iface) = [String
"-i", String
iface]
toIpTableArg (OutIFace String
iface) = [String
"-o", String
iface]
toIpTableArg (Ctstate [ConnectionState]
states) =
[ String
"-m"
, String
"conntrack"
, String
"--ctstate", String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ((ConnectionState -> String) -> [ConnectionState] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ConnectionState -> String
forall a. Show a => a -> String
show [ConnectionState]
states)
]
toIpTableArg (ICMPType ICMPTypeMatch
i) =
[ String
"-m"
, String
"icmp"
, String
"--icmp-type", ICMPTypeMatch -> String
forall t. ConfigurableValue t => t -> String
val ICMPTypeMatch
i
]
toIpTableArg (RateLimit Frequency
f) =
[ String
"-m"
, String
"limit"
, String
"--limit", Frequency -> String
forall t. ConfigurableValue t => t -> String
val Frequency
f
]
toIpTableArg (TCPFlags TCPFlagMask
m TCPFlagMask
c) =
[ String
"-m"
, String
"tcp"
, String
"--tcp-flags"
, String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ((TCPFlag -> String) -> TCPFlagMask -> [String]
forall a b. (a -> b) -> [a] -> [b]
map TCPFlag -> String
forall a. Show a => a -> String
show TCPFlagMask
m)
, String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ((TCPFlag -> String) -> TCPFlagMask -> [String]
forall a b. (a -> b) -> [a] -> [b]
map TCPFlag -> String
forall a. Show a => a -> String
show TCPFlagMask
c)
]
toIpTableArg Rules
TCPSyn = [String
"--syn"]
toIpTableArg (GroupOwner (Group String
g)) =
[ String
"-m"
, String
"owner"
, String
"--gid-owner"
, String
g
]
toIpTableArg (Source [IPWithMask]
ipwm) =
[ String
"-s"
, String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ((IPWithMask -> String) -> [IPWithMask] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map IPWithMask -> String
forall t. ConfigurableValue t => t -> String
val [IPWithMask]
ipwm)
]
toIpTableArg (Destination [IPWithMask]
ipwm) =
[ String
"-d"
, String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ((IPWithMask -> String) -> [IPWithMask] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map IPWithMask -> String
forall t. ConfigurableValue t => t -> String
val [IPWithMask]
ipwm)
]
toIpTableArg (NotDestination [IPWithMask]
ipwm) =
[ String
"!"
, String
"-d"
, String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ((IPWithMask -> String) -> [IPWithMask] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map IPWithMask -> String
forall t. ConfigurableValue t => t -> String
val [IPWithMask]
ipwm)
]
toIpTableArg (NatDestination IPAddr
ip Maybe Port
mport) =
[ String
"--to-destination"
, IPAddr -> String
forall t. ConfigurableValue t => t -> String
val IPAddr
ip String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> (Port -> String) -> Maybe Port -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"" (\Port
p -> String
":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Port -> String
forall t. ConfigurableValue t => t -> String
val Port
p) Maybe Port
mport
]
toIpTableArg (Rules
r :- Rules
r') = Rules -> [String]
toIpTableArg Rules
r [String] -> [String] -> [String]
forall a. Semigroup a => a -> a -> a
<> Rules -> [String]
toIpTableArg Rules
r'
data IPWithMask = IPWithNoMask IPAddr | IPWithIPMask IPAddr IPAddr | IPWithNumMask IPAddr Int
deriving (IPWithMask -> IPWithMask -> Bool
(IPWithMask -> IPWithMask -> Bool)
-> (IPWithMask -> IPWithMask -> Bool) -> Eq IPWithMask
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: IPWithMask -> IPWithMask -> Bool
== :: IPWithMask -> IPWithMask -> Bool
$c/= :: IPWithMask -> IPWithMask -> Bool
/= :: IPWithMask -> IPWithMask -> Bool
Eq, Int -> IPWithMask -> String -> String
[IPWithMask] -> String -> String
IPWithMask -> String
(Int -> IPWithMask -> String -> String)
-> (IPWithMask -> String)
-> ([IPWithMask] -> String -> String)
-> Show IPWithMask
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> IPWithMask -> String -> String
showsPrec :: Int -> IPWithMask -> String -> String
$cshow :: IPWithMask -> String
show :: IPWithMask -> String
$cshowList :: [IPWithMask] -> String -> String
showList :: [IPWithMask] -> String -> String
Show)
instance ConfigurableValue IPWithMask where
val :: IPWithMask -> String
val (IPWithNoMask IPAddr
ip) = IPAddr -> String
forall t. ConfigurableValue t => t -> String
val IPAddr
ip
val (IPWithIPMask IPAddr
ip IPAddr
ipm) = IPAddr -> String
forall t. ConfigurableValue t => t -> String
val IPAddr
ip String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/" String -> String -> String
forall a. [a] -> [a] -> [a]
++ IPAddr -> String
forall t. ConfigurableValue t => t -> String
val IPAddr
ipm
val (IPWithNumMask IPAddr
ip Int
m) = IPAddr -> String
forall t. ConfigurableValue t => t -> String
val IPAddr
ip String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall t. ConfigurableValue t => t -> String
val Int
m
data Rule = Rule
{ Rule -> Chain
ruleChain :: Chain
, Rule -> Table
ruleTable :: Table
, Rule -> Target
ruleTarget :: Target
, Rule -> Rules
ruleRules :: Rules
} deriving (Rule -> Rule -> Bool
(Rule -> Rule -> Bool) -> (Rule -> Rule -> Bool) -> Eq Rule
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Rule -> Rule -> Bool
== :: Rule -> Rule -> Bool
$c/= :: Rule -> Rule -> Bool
/= :: Rule -> Rule -> Bool
Eq, Int -> Rule -> String -> String
[Rule] -> String -> String
Rule -> String
(Int -> Rule -> String -> String)
-> (Rule -> String) -> ([Rule] -> String -> String) -> Show Rule
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Rule -> String -> String
showsPrec :: Int -> Rule -> String -> String
$cshow :: Rule -> String
show :: Rule -> String
$cshowList :: [Rule] -> String -> String
showList :: [Rule] -> String -> String
Show)
data Table = Filter | Nat | Mangle | Raw | Security
deriving (Table -> Table -> Bool
(Table -> Table -> Bool) -> (Table -> Table -> Bool) -> Eq Table
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Table -> Table -> Bool
== :: Table -> Table -> Bool
$c/= :: Table -> Table -> Bool
/= :: Table -> Table -> Bool
Eq, Int -> Table -> String -> String
[Table] -> String -> String
Table -> String
(Int -> Table -> String -> String)
-> (Table -> String) -> ([Table] -> String -> String) -> Show Table
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Table -> String -> String
showsPrec :: Int -> Table -> String -> String
$cshow :: Table -> String
show :: Table -> String
$cshowList :: [Table] -> String -> String
showList :: [Table] -> String -> String
Show)
instance ConfigurableValue Table where
val :: Table -> String
val Table
Filter = String
"filter"
val Table
Nat = String
"nat"
val Table
Mangle = String
"mangle"
val Table
Raw = String
"raw"
val Table
Security = String
"security"
data Target = ACCEPT | REJECT | DROP | LOG | TargetCustom String
deriving (Target -> Target -> Bool
(Target -> Target -> Bool)
-> (Target -> Target -> Bool) -> Eq Target
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Target -> Target -> Bool
== :: Target -> Target -> Bool
$c/= :: Target -> Target -> Bool
/= :: Target -> Target -> Bool
Eq, Int -> Target -> String -> String
[Target] -> String -> String
Target -> String
(Int -> Target -> String -> String)
-> (Target -> String)
-> ([Target] -> String -> String)
-> Show Target
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Target -> String -> String
showsPrec :: Int -> Target -> String -> String
$cshow :: Target -> String
show :: Target -> String
$cshowList :: [Target] -> String -> String
showList :: [Target] -> String -> String
Show)
instance ConfigurableValue Target where
val :: Target -> String
val Target
ACCEPT = String
"ACCEPT"
val Target
REJECT = String
"REJECT"
val Target
DROP = String
"DROP"
val Target
LOG = String
"LOG"
val (TargetCustom String
t) = String
t
data Chain = INPUT | OUTPUT | FORWARD | PREROUTING | POSTROUTING | ChainCustom String
deriving (Chain -> Chain -> Bool
(Chain -> Chain -> Bool) -> (Chain -> Chain -> Bool) -> Eq Chain
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Chain -> Chain -> Bool
== :: Chain -> Chain -> Bool
$c/= :: Chain -> Chain -> Bool
/= :: Chain -> Chain -> Bool
Eq, Int -> Chain -> String -> String
[Chain] -> String -> String
Chain -> String
(Int -> Chain -> String -> String)
-> (Chain -> String) -> ([Chain] -> String -> String) -> Show Chain
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Chain -> String -> String
showsPrec :: Int -> Chain -> String -> String
$cshow :: Chain -> String
show :: Chain -> String
$cshowList :: [Chain] -> String -> String
showList :: [Chain] -> String -> String
Show)
instance ConfigurableValue Chain where
val :: Chain -> String
val Chain
INPUT = String
"INPUT"
val Chain
OUTPUT = String
"OUTPUT"
val Chain
FORWARD = String
"FORWARD"
val Chain
PREROUTING = String
"PREROUTING"
val Chain
POSTROUTING = String
"POSTROUTING"
val (ChainCustom String
c) = String
c
data Proto = TCP | UDP | ICMP
deriving (Proto -> Proto -> Bool
(Proto -> Proto -> Bool) -> (Proto -> Proto -> Bool) -> Eq Proto
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Proto -> Proto -> Bool
== :: Proto -> Proto -> Bool
$c/= :: Proto -> Proto -> Bool
/= :: Proto -> Proto -> Bool
Eq, Int -> Proto -> String -> String
[Proto] -> String -> String
Proto -> String
(Int -> Proto -> String -> String)
-> (Proto -> String) -> ([Proto] -> String -> String) -> Show Proto
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Proto -> String -> String
showsPrec :: Int -> Proto -> String -> String
$cshow :: Proto -> String
show :: Proto -> String
$cshowList :: [Proto] -> String -> String
showList :: [Proto] -> String -> String
Show)
data ConnectionState = ESTABLISHED | RELATED | NEW | INVALID
deriving (ConnectionState -> ConnectionState -> Bool
(ConnectionState -> ConnectionState -> Bool)
-> (ConnectionState -> ConnectionState -> Bool)
-> Eq ConnectionState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ConnectionState -> ConnectionState -> Bool
== :: ConnectionState -> ConnectionState -> Bool
$c/= :: ConnectionState -> ConnectionState -> Bool
/= :: ConnectionState -> ConnectionState -> Bool
Eq, Int -> ConnectionState -> String -> String
[ConnectionState] -> String -> String
ConnectionState -> String
(Int -> ConnectionState -> String -> String)
-> (ConnectionState -> String)
-> ([ConnectionState] -> String -> String)
-> Show ConnectionState
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> ConnectionState -> String -> String
showsPrec :: Int -> ConnectionState -> String -> String
$cshow :: ConnectionState -> String
show :: ConnectionState -> String
$cshowList :: [ConnectionState] -> String -> String
showList :: [ConnectionState] -> String -> String
Show)
data ICMPTypeMatch = ICMPTypeName String | ICMPTypeCode Int
deriving (ICMPTypeMatch -> ICMPTypeMatch -> Bool
(ICMPTypeMatch -> ICMPTypeMatch -> Bool)
-> (ICMPTypeMatch -> ICMPTypeMatch -> Bool) -> Eq ICMPTypeMatch
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ICMPTypeMatch -> ICMPTypeMatch -> Bool
== :: ICMPTypeMatch -> ICMPTypeMatch -> Bool
$c/= :: ICMPTypeMatch -> ICMPTypeMatch -> Bool
/= :: ICMPTypeMatch -> ICMPTypeMatch -> Bool
Eq, Int -> ICMPTypeMatch -> String -> String
[ICMPTypeMatch] -> String -> String
ICMPTypeMatch -> String
(Int -> ICMPTypeMatch -> String -> String)
-> (ICMPTypeMatch -> String)
-> ([ICMPTypeMatch] -> String -> String)
-> Show ICMPTypeMatch
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> ICMPTypeMatch -> String -> String
showsPrec :: Int -> ICMPTypeMatch -> String -> String
$cshow :: ICMPTypeMatch -> String
show :: ICMPTypeMatch -> String
$cshowList :: [ICMPTypeMatch] -> String -> String
showList :: [ICMPTypeMatch] -> String -> String
Show)
instance ConfigurableValue ICMPTypeMatch where
val :: ICMPTypeMatch -> String
val (ICMPTypeName String
t) = String
t
val (ICMPTypeCode Int
c) = Int -> String
forall t. ConfigurableValue t => t -> String
val Int
c
data Frequency = NumBySecond Int
deriving (Frequency -> Frequency -> Bool
(Frequency -> Frequency -> Bool)
-> (Frequency -> Frequency -> Bool) -> Eq Frequency
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Frequency -> Frequency -> Bool
== :: Frequency -> Frequency -> Bool
$c/= :: Frequency -> Frequency -> Bool
/= :: Frequency -> Frequency -> Bool
Eq, Int -> Frequency -> String -> String
[Frequency] -> String -> String
Frequency -> String
(Int -> Frequency -> String -> String)
-> (Frequency -> String)
-> ([Frequency] -> String -> String)
-> Show Frequency
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Frequency -> String -> String
showsPrec :: Int -> Frequency -> String -> String
$cshow :: Frequency -> String
show :: Frequency -> String
$cshowList :: [Frequency] -> String -> String
showList :: [Frequency] -> String -> String
Show)
instance ConfigurableValue Frequency where
val :: Frequency -> String
val (NumBySecond Int
n) = Int -> String
forall t. ConfigurableValue t => t -> String
val Int
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/second"
type TCPFlagMask = [TCPFlag]
type TCPFlagComp = [TCPFlag]
data TCPFlag = SYN | ACK | FIN | RST | URG | PSH | ALL | NONE
deriving (TCPFlag -> TCPFlag -> Bool
(TCPFlag -> TCPFlag -> Bool)
-> (TCPFlag -> TCPFlag -> Bool) -> Eq TCPFlag
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TCPFlag -> TCPFlag -> Bool
== :: TCPFlag -> TCPFlag -> Bool
$c/= :: TCPFlag -> TCPFlag -> Bool
/= :: TCPFlag -> TCPFlag -> Bool
Eq, Int -> TCPFlag -> String -> String
TCPFlagMask -> String -> String
TCPFlag -> String
(Int -> TCPFlag -> String -> String)
-> (TCPFlag -> String)
-> (TCPFlagMask -> String -> String)
-> Show TCPFlag
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> TCPFlag -> String -> String
showsPrec :: Int -> TCPFlag -> String -> String
$cshow :: TCPFlag -> String
show :: TCPFlag -> String
$cshowList :: TCPFlagMask -> String -> String
showList :: TCPFlagMask -> String -> String
Show)
data Rules
= Everything
| Proto Proto
| DPort Port
| DPortRange (Port, Port)
| InIFace Network.Interface
| OutIFace Network.Interface
| Ctstate [ ConnectionState ]
| ICMPType ICMPTypeMatch
| RateLimit Frequency
| TCPFlags TCPFlagMask TCPFlagComp
| TCPSyn
| GroupOwner Group
| Source [ IPWithMask ]
| Destination [ IPWithMask ]
| NotDestination [ IPWithMask ]
| NatDestination IPAddr (Maybe Port)
| Rules :- Rules
deriving (Rules -> Rules -> Bool
(Rules -> Rules -> Bool) -> (Rules -> Rules -> Bool) -> Eq Rules
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Rules -> Rules -> Bool
== :: Rules -> Rules -> Bool
$c/= :: Rules -> Rules -> Bool
/= :: Rules -> Rules -> Bool
Eq, Int -> Rules -> String -> String
[Rules] -> String -> String
Rules -> String
(Int -> Rules -> String -> String)
-> (Rules -> String) -> ([Rules] -> String -> String) -> Show Rules
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Rules -> String -> String
showsPrec :: Int -> Rules -> String -> String
$cshow :: Rules -> String
show :: Rules -> String
$cshowList :: [Rules] -> String -> String
showList :: [Rules] -> String -> String
Show)
infixl 0 :-
instance Sem.Semigroup Rules where
<> :: Rules -> Rules -> Rules
(<>) = Rules -> Rules -> Rules
(:-)
instance Monoid Rules where
mempty :: Rules
mempty = Rules
Everything
mappend :: Rules -> Rules -> Rules
mappend = Rules -> Rules -> Rules
forall a. Semigroup a => a -> a -> a
(Sem.<>)