module Propellor.Property.Rsync where

import Propellor.Base
import qualified Propellor.Property.Apt as Apt
import qualified Propellor.Property.Pacman as Pacman

type Src = FilePath
type Dest = FilePath

class RsyncParam p where
        toRsync :: p -> String

-- | A pattern that matches all files under a directory, but does not
-- match the directory itself.
filesUnder :: FilePath -> Pattern
filesUnder d = Pattern (d ++ "/*")

-- | Ensures that the Dest directory exists and has identical contents as
-- the Src directory.
syncDir :: Src -> Dest -> Property (DebianLike + ArchLinux)
syncDir = syncDirFiltered []

data Filter
        = Include Pattern
        | Exclude Pattern
        | Protect Pattern

instance RsyncParam Filter where
        toRsync (Include (Pattern p)) = "--include=" ++ p
        toRsync (Exclude (Pattern p)) = "--exclude=" ++ p
        toRsync (Protect (Pattern p)) = "--filter=P " ++ p

-- | A pattern to match against files that rsync is going to transfer.
--
-- See "INCLUDE/EXCLUDE PATTERN RULES" in the rsync(1) man page.
--
-- For example, Pattern "/foo/*" matches all files under the "foo"
-- directory, relative to the 'Src' that rsync is acting on.
newtype Pattern = Pattern String

-- | Like syncDir, but avoids copying anything that the filter list
-- excludes. Anything that's filtered out will be deleted from Dest.
--
-- Rsync checks each name to be transferred against its list of Filter
-- rules, and the first matching one is acted on. If no matching rule
-- is found, the file is processed.
syncDirFiltered :: [Filter] -> Src -> Dest -> Property (DebianLike + ArchLinux)
syncDirFiltered filters src dest = rsync $
        [ "-a"
        -- Add trailing '/' to get rsync to sync the Dest directory,
        -- rather than a subdir inside it, which it will do without a
        -- trailing '/'.
        , addTrailingPathSeparator src
        , addTrailingPathSeparator dest
        , "--delete"
        , "--delete-excluded"
        , "--info=progress2"
        ] ++ map toRsync filters

rsync :: [String] -> Property (DebianLike + ArchLinux)
rsync ps = cmdProperty "rsync" ps
        `assume` MadeChange
        `requires` installed

installed :: Property (DebianLike + ArchLinux)
installed = Apt.installed ["rsync"] `pickOS` Pacman.installed ["rsync"]