module Propellor.Property.Network where

import Propellor.Base
import Propellor.Property.File

import Data.Char

type Interface = String

-- | Options to put in a stanza of an ifupdown interfaces file.
type InterfaceOptions = [(String, String)]

-- | Stanza of an ifupdown interfaces file, with header lines and options.
type InterfaceStanza = ([String], InterfaceOptions)

ifUp :: Interface -> Property DebianLike
ifUp :: Interface -> Property DebianLike
ifUp Interface
iface = Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
forall (p :: * -> *) (untightened :: [MetaType])
       (tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
 SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets (Property
   (MetaTypes
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
 -> Property DebianLike)
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
forall a b. (a -> b) -> a -> b
$ Interface
-> [Interface]
-> UncheckedProperty
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
cmdProperty Interface
"ifup" [Interface
iface]
	UncheckedProperty
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Result
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall (p :: * -> *) i.
Checkable p i =>
p i -> Result -> Property i
`assume` Result
MadeChange

-- | Resets /etc/network/interfaces to a clean and empty state,
-- containing just the standard loopback interface, and with
-- interfacesD enabled.
--
-- This can be used as a starting point to defining other interfaces.
--
-- No interfaces are brought up or down by this property.
cleanInterfacesFile :: Property DebianLike
cleanInterfacesFile :: Property DebianLike
cleanInterfacesFile = Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
interfaceFileContains Interface
interfacesFile
	[ Interface
"source-directory interfaces.d"
	, Interface
""
	, Interface
"# The loopback network interface"
	, Interface
"auto lo"
	, Interface
"iface lo inet loopback"
	]
	[]
	Property DebianLike -> Interface -> Property DebianLike
forall p. IsProp p => p -> Interface -> p
`describe` (Interface
"clean " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
interfacesFile)

-- | Configures an interface to get its address via dhcp.
dhcp :: Interface -> Property DebianLike
dhcp :: Interface -> Property DebianLike
dhcp Interface
iface = Interface -> InterfaceOptions -> Property DebianLike
dhcp' Interface
iface InterfaceOptions
forall a. Monoid a => a
mempty

dhcp' :: Interface -> InterfaceOptions -> Property DebianLike
dhcp' :: Interface -> InterfaceOptions -> Property DebianLike
dhcp' Interface
iface InterfaceOptions
options = Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
interfaceFileContains (Interface -> Interface
interfaceDFile Interface
iface)
	[ Interface
"auto " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface
	, Interface
"iface " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
" inet dhcp"
	] InterfaceOptions
options
	Property DebianLike -> Interface -> Property DebianLike
forall p. IsProp p => p -> Interface -> p
`describe` (Interface
"dhcp " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface)
	Property DebianLike
-> Property DebianLike
-> CombinedType (Property DebianLike) (Property DebianLike)
forall x y. Combines x y => x -> y -> CombinedType x y
`requires` Property DebianLike
interfacesDEnabled

newtype Gateway = Gateway IPAddr

-- | Configures an interface with a static address and gateway.
static :: Interface -> IPAddr -> Maybe Gateway -> Property DebianLike
static :: Interface -> IPAddr -> Maybe Gateway -> Property DebianLike
static Interface
iface IPAddr
addr Maybe Gateway
gateway = Interface
-> IPAddr
-> Maybe Gateway
-> InterfaceOptions
-> Property DebianLike
static' Interface
iface IPAddr
addr Maybe Gateway
gateway InterfaceOptions
forall a. Monoid a => a
mempty

static' :: Interface -> IPAddr -> Maybe Gateway -> InterfaceOptions -> Property DebianLike
static' :: Interface
-> IPAddr
-> Maybe Gateway
-> InterfaceOptions
-> Property DebianLike
static' Interface
iface IPAddr
addr Maybe Gateway
gateway InterfaceOptions
options =
	Interface
-> [(IPAddr, Maybe Gateway, InterfaceOptions)]
-> Property DebianLike
static'' Interface
iface [(IPAddr
addr, Maybe Gateway
gateway, InterfaceOptions
options)]

-- | Configures an interface with several stanzas (IPv4 and IPv6 for example).
static'' :: Interface -> [(IPAddr, Maybe Gateway, InterfaceOptions)] -> Property DebianLike
static'' :: Interface
-> [(IPAddr, Maybe Gateway, InterfaceOptions)]
-> Property DebianLike
static'' Interface
iface [(IPAddr, Maybe Gateway, InterfaceOptions)]
confs =
	Interface -> [InterfaceStanza] -> Property DebianLike
interfaceFileContains' (Interface -> Interface
interfaceDFile Interface
iface) [InterfaceStanza]
stanzas
	Property DebianLike -> Interface -> Property DebianLike
forall p. IsProp p => p -> Interface -> p
`describe` (Interface
"static IP address for " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface)
	Property DebianLike
-> Property DebianLike
-> CombinedType (Property DebianLike) (Property DebianLike)
forall x y. Combines x y => x -> y -> CombinedType x y
`requires` Property DebianLike
interfacesDEnabled
  where
	stanzas :: [InterfaceStanza]
stanzas = ((IPAddr, Maybe Gateway, InterfaceOptions) -> InterfaceStanza)
-> [(IPAddr, Maybe Gateway, InterfaceOptions)] -> [InterfaceStanza]
forall a b. (a -> b) -> [a] -> [b]
map (IPAddr, Maybe Gateway, InterfaceOptions) -> InterfaceStanza
stanza [(IPAddr, Maybe Gateway, InterfaceOptions)]
confs
	stanza :: (IPAddr, Maybe Gateway, InterfaceOptions) -> InterfaceStanza
stanza (IPAddr
addr, Maybe Gateway
gateway, InterfaceOptions
options) = (IPAddr -> [Interface]
headerlines IPAddr
addr, IPAddr -> Maybe Gateway -> InterfaceOptions -> InterfaceOptions
forall t.
ConfigurableValue t =>
t -> Maybe Gateway -> InterfaceOptions -> InterfaceOptions
options' IPAddr
addr Maybe Gateway
gateway InterfaceOptions
options)
	headerlines :: IPAddr -> [Interface]
headerlines IPAddr
addr =
		[ Interface
"auto " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface
		, Interface
"iface " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
" " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ (IPAddr -> Interface
inet IPAddr
addr) Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
" static"
		]
	options' :: t -> Maybe Gateway -> InterfaceOptions -> InterfaceOptions
options' t
addr Maybe Gateway
gateway InterfaceOptions
options = [Maybe (Interface, Interface)] -> InterfaceOptions
forall a. [Maybe a] -> [a]
catMaybes
		[ (Interface, Interface) -> Maybe (Interface, Interface)
forall a. a -> Maybe a
Just ((Interface, Interface) -> Maybe (Interface, Interface))
-> (Interface, Interface) -> Maybe (Interface, Interface)
forall a b. (a -> b) -> a -> b
$ (Interface
"address", t -> Interface
forall t. ConfigurableValue t => t -> Interface
val t
addr)
		, case Maybe Gateway
gateway of
			Just (Gateway IPAddr
gaddr) ->
				(Interface, Interface) -> Maybe (Interface, Interface)
forall a. a -> Maybe a
Just (Interface
"gateway", IPAddr -> Interface
forall t. ConfigurableValue t => t -> Interface
val IPAddr
gaddr)
			Maybe Gateway
Nothing -> Maybe (Interface, Interface)
forall a. Maybe a
Nothing
		] InterfaceOptions -> InterfaceOptions -> InterfaceOptions
forall a. [a] -> [a] -> [a]
++ InterfaceOptions
options
	inet :: IPAddr -> Interface
inet IPAddr
addr = case IPAddr
addr of
		IPv4 Interface
_ -> Interface
"inet"
		IPv6 Interface
_ -> Interface
"inet6"

-- | Writes a static interface file for the specified interface
-- to preserve its current configuration.
--
-- The interface has to be up already. It could have been brought up by
-- DHCP, or by other means. The current ipv4 addresses
-- and routing configuration of the interface are written into the file.
--
-- If the interface file already exists, this property does nothing,
-- no matter its content.
--
-- (ipv6 addresses are not included because it's assumed they come up
-- automatically in most situations.)
preserveStatic :: Interface -> Property DebianLike
preserveStatic :: Interface -> Property DebianLike
preserveStatic Interface
iface = Property DebianLike -> Property DebianLike
forall (p :: * -> *) (untightened :: [MetaType])
       (tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
 SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets (Property DebianLike -> Property DebianLike)
-> Property DebianLike -> Property DebianLike
forall a b. (a -> b) -> a -> b
$ 
	IO Bool -> Property DebianLike -> Property DebianLike
forall (p :: * -> *) i (m :: * -> *).
(Checkable p i, LiftPropellor m) =>
m Bool -> p i -> Property i
check (Bool -> Bool
not (Bool -> Bool) -> IO Bool -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Interface -> IO Bool
doesFileExist Interface
f) Property DebianLike
setup
		Property DebianLike -> Interface -> Property DebianLike
forall p. IsProp p => p -> Interface -> p
`describe` Interface
desc
		Property DebianLike
-> Property DebianLike
-> CombinedType (Property DebianLike) (Property DebianLike)
forall x y. Combines x y => x -> y -> CombinedType x y
`requires` Property DebianLike
interfacesDEnabled
  where
	f :: Interface
f = Interface -> Interface
interfaceDFile Interface
iface
	desc :: Interface
desc = Interface
"static " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface
	setup :: Property DebianLike
	setup :: Property DebianLike
setup = Interface
-> (OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    -> Propellor Result)
-> Property DebianLike
forall k (metatypes :: k).
SingI metatypes =>
Interface
-> (OuterMetaTypesWitness metatypes -> Propellor Result)
-> Property (MetaTypes metatypes)
property' Interface
desc ((OuterMetaTypesWitness
    '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
  -> Propellor Result)
 -> Property DebianLike)
-> (OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    -> Propellor Result)
-> Property DebianLike
forall a b. (a -> b) -> a -> b
$ \OuterMetaTypesWitness
  '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
o -> do
		[Interface]
ls <- IO [Interface] -> Propellor [Interface]
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [Interface] -> Propellor [Interface])
-> IO [Interface] -> Propellor [Interface]
forall a b. (a -> b) -> a -> b
$ Interface -> [Interface]
lines (Interface -> [Interface]) -> IO Interface -> IO [Interface]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Interface -> [Interface] -> IO Interface
readProcess Interface
"ip"
			[Interface
"-o", Interface
"addr", Interface
"show", Interface
iface, Interface
"scope", Interface
"global"]
		[Interface]
stanzas <- IO [Interface] -> Propellor [Interface]
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [Interface] -> Propellor [Interface])
-> IO [Interface] -> Propellor [Interface]
forall a b. (a -> b) -> a -> b
$ [[Interface]] -> [Interface]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Interface]] -> [Interface])
-> IO [[Interface]] -> IO [Interface]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Interface -> IO [Interface]) -> [Interface] -> IO [[Interface]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Interface -> IO [Interface]
mkstanza [Interface]
ls
		OuterMetaTypesWitness
  '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Propellor Result
forall (inner :: [MetaType]) (outer :: [MetaType]).
EnsurePropertyAllowed inner outer =>
OuterMetaTypesWitness outer
-> Property (MetaTypes inner) -> Propellor Result
ensureProperty OuterMetaTypesWitness
  '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
o (Property
   (MetaTypes
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
 -> Propellor Result)
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Propellor Result
forall a b. (a -> b) -> a -> b
$ Interface
-> [Interface]
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
hasContent Interface
f ([Interface]
 -> Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
-> [Interface]
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall a b. (a -> b) -> a -> b
$ (Interface
"auto " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface) Interface -> [Interface] -> [Interface]
forall a. a -> [a] -> [a]
: [Interface]
stanzas
	mkstanza :: Interface -> IO [Interface]
mkstanza Interface
ipline = case Interface -> [Interface]
words Interface
ipline of
		-- Note that the IP address is written CIDR style, so
		-- the netmask does not need to be specified separately.
		(Interface
_:Interface
iface':Interface
"inet":Interface
addr:[Interface]
_) | Interface
iface' Interface -> Interface -> Bool
forall a. Eq a => a -> a -> Bool
== Interface
iface -> do
			Maybe Interface
gw <- IO (Maybe Interface)
getgateway
			[Interface] -> IO [Interface]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Interface] -> IO [Interface]) -> [Interface] -> IO [Interface]
forall a b. (a -> b) -> a -> b
$ [Maybe Interface] -> [Interface]
forall a. [Maybe a] -> [a]
catMaybes
				[ Interface -> Maybe Interface
forall a. a -> Maybe a
Just (Interface -> Maybe Interface) -> Interface -> Maybe Interface
forall a b. (a -> b) -> a -> b
$ Interface
"iface " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
iface Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
" inet static"
				, Interface -> Maybe Interface
forall a. a -> Maybe a
Just (Interface -> Maybe Interface) -> Interface -> Maybe Interface
forall a b. (a -> b) -> a -> b
$ Interface
"\taddress " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
addr
				, (Interface
"\tgateway " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++) (Interface -> Interface) -> Maybe Interface -> Maybe Interface
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Interface
gw
				]
		[Interface]
_ -> [Interface] -> IO [Interface]
forall (m :: * -> *) a. Monad m => a -> m a
return []
	getgateway :: IO (Maybe Interface)
getgateway = do
		[Interface]
rs <- Interface -> [Interface]
lines (Interface -> [Interface]) -> IO Interface -> IO [Interface]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Interface -> [Interface] -> IO Interface
readProcess Interface
"ip"
			[Interface
"route", Interface
"show", Interface
"scope", Interface
"global", Interface
"dev", Interface
iface]
		Maybe Interface -> IO (Maybe Interface)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Interface -> IO (Maybe Interface))
-> Maybe Interface -> IO (Maybe Interface)
forall a b. (a -> b) -> a -> b
$ case Interface -> [Interface]
words (Interface -> [Interface]) -> Maybe Interface -> Maybe [Interface]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Interface] -> Maybe Interface
forall a. [a] -> Maybe a
headMaybe [Interface]
rs of
			Just (Interface
"default":Interface
"via":Interface
gw:[Interface]
_) -> Interface -> Maybe Interface
forall a. a -> Maybe a
Just Interface
gw
			Maybe [Interface]
_ -> Maybe Interface
forall a. Maybe a
Nothing

-- | 6to4 ipv6 connection, should work anywhere
ipv6to4 :: Property DebianLike
ipv6to4 :: Property DebianLike
ipv6to4 = Property DebianLike -> Property DebianLike
forall (p :: * -> *) (untightened :: [MetaType])
       (tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
 SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets (Property DebianLike -> Property DebianLike)
-> Property DebianLike -> Property DebianLike
forall a b. (a -> b) -> a -> b
$ Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
interfaceFileContains (Interface -> Interface
interfaceDFile Interface
"sit0")
	[ Interface
"auto sit0"
	, Interface
"iface sit0 inet6 static"
	]
	[ (Interface
"address", Interface
"2002:5044:5531::1")
	, (Interface
"netmask", Interface
"64")
	, (Interface
"gateway", Interface
"::192.88.99.1")
	]
	Property DebianLike -> Interface -> Property DebianLike
forall p. IsProp p => p -> Interface -> p
`describe` Interface
"ipv6to4"
	Property DebianLike
-> Property DebianLike
-> CombinedType (Property DebianLike) (Property DebianLike)
forall x y. Combines x y => x -> y -> CombinedType x y
`requires` Property DebianLike
interfacesDEnabled
	Property DebianLike
-> Property DebianLike
-> CombinedType (Property DebianLike) (Property DebianLike)
forall x y. Combines x y => x -> y -> CombinedType x y
`onChange` Interface -> Property DebianLike
ifUp Interface
"sit0"

interfacesFile :: FilePath
interfacesFile :: Interface
interfacesFile = Interface
"/etc/network/interfaces"

-- | A file in the interfaces.d directory.
interfaceDFile :: Interface -> FilePath
interfaceDFile :: Interface -> Interface
interfaceDFile Interface
i = Interface
"/etc/network/interfaces.d" Interface -> Interface -> Interface
</> Interface -> Interface
escapeInterfaceDName Interface
i

-- | /etc/network/interfaces.d/ files have to match -- ^[a-zA-Z0-9_-]+$
-- see "man 5 interfaces"
escapeInterfaceDName :: Interface -> FilePath
escapeInterfaceDName :: Interface -> Interface
escapeInterfaceDName = (Char -> Bool) -> Interface -> Interface
forall a. (a -> Bool) -> [a] -> [a]
filter (\Char
c -> Char -> Bool
isAscii Char
c Bool -> Bool -> Bool
&& (Char -> Bool
isAlphaNum Char
c Bool -> Bool -> Bool
|| Char
c Char -> Interface -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Interface
"_-"))

-- | Ensures that files in the the interfaces.d directory are used.
-- interfacesDEnabled :: Property DebianLike
interfacesDEnabled :: Property DebianLike
interfacesDEnabled :: Property DebianLike
interfacesDEnabled = Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
forall (p :: * -> *) (untightened :: [MetaType])
       (tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
 SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets (Property
   (MetaTypes
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
 -> Property DebianLike)
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
forall a b. (a -> b) -> a -> b
$
	Interface
-> Interface
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
containsLine Interface
interfacesFile Interface
"source-directory interfaces.d"
		Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Interface
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall p. IsProp p => p -> Interface -> p
`describe` Interface
"interfaces.d directory enabled"

interfaceFileContains :: FilePath -> [String] -> InterfaceOptions -> Property DebianLike
interfaceFileContains :: Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
interfaceFileContains Interface
f [Interface]
headerlines InterfaceOptions
options =
	Interface -> [InterfaceStanza] -> Property DebianLike
interfaceFileContains' Interface
f [([Interface]
headerlines, InterfaceOptions
options)]

interfaceFileContains' :: FilePath -> [InterfaceStanza] -> Property DebianLike
interfaceFileContains' :: Interface -> [InterfaceStanza] -> Property DebianLike
interfaceFileContains' Interface
f [InterfaceStanza]
stanzas = Property
  (MetaTypes
     '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
        'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
forall (p :: * -> *) (untightened :: [MetaType])
       (tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
 SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets (Property
   (MetaTypes
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
         'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
 -> Property DebianLike)
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
-> Property DebianLike
forall a b. (a -> b) -> a -> b
$ Interface
-> [Interface]
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
hasContent Interface
f ([Interface]
 -> Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
-> [Interface]
-> Property
     (MetaTypes
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
           'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
forall a b. (a -> b) -> a -> b
$
	Interface
warning Interface -> [Interface] -> [Interface]
forall a. a -> [a] -> [a]
: (InterfaceStanza -> [Interface])
-> [InterfaceStanza] -> [Interface]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap InterfaceStanza -> [Interface]
stanza [InterfaceStanza]
stanzas
  where
	stanza :: InterfaceStanza -> [Interface]
stanza ([Interface]
headerlines, InterfaceOptions
options) = [Interface]
headerlines [Interface] -> [Interface] -> [Interface]
forall a. [a] -> [a] -> [a]
++ ((Interface, Interface) -> Interface)
-> InterfaceOptions -> [Interface]
forall a b. (a -> b) -> [a] -> [b]
map (Interface, Interface) -> Interface
fmt InterfaceOptions
options
	fmt :: (Interface, Interface) -> Interface
fmt (Interface
k, Interface
v) = Interface
"\t" Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
k Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
" " Interface -> Interface -> Interface
forall a. [a] -> [a] -> [a]
++ Interface
v
	warning :: Interface
warning = Interface
"# Deployed by propellor, do not edit."