{-|

Module      : Azubi.Core.Model
Description : Core low level Model of Azubi
Copyright   : (c) Ingolf Wagner, 2017
License     : GPL-3
Maintainer  : azubi@ingolf-wagner.de
Stability   : experimental
Portability : POSIX

This is the lowest level of Azubi.
It should deliver only the bare
minimum to create 'State' evaluation
and 'State' enforcement.

The 'State' object is the element
a user should use to formulate more complex
situations.

The idea here is to create a simple AST which
can be run on different setups.

* export to Script
* run on the Machine (Linux, OSX and (maybe) Windows)
* run over SSH on another computer

-}
module Azubi.Core.Model where


type Path = String
type Target = String
type Argument = String
type Comment = String

{-|

A command is something that will be run.
In a normal case you put a 'Check' before
using a 'State'

When a Command get executed,
it will create a 'CommandResult'.

@ command -> exit code -> 'CommandResult' @

-}
data Command = Run String [Argument] (Maybe Comment)
             | FileContent Path [String]
             | CreateSymlink Path Target
             | CreateFolder Path
             | Remove Path
             deriving (Show, Eq)


{-|

The Result of a 'Command' will be matched
to a 'CommandResult'. The basic rule is

@
0 -> 'Success'
_ -> 'Failure'
@

-}
data CommandResult = Success
                   | Failure
                   deriving (Show, Enum, Eq)


{-|

Gather information about a situation.

@
check -> success/failure
check -> exit code -> success/failure
@

-}
data Check =
  {-|

Check if command returns exit status

@
0 -> Yes
_ -> No
@

-}
  Check String [Argument] (Maybe Comment)
  | AlwaysYes
  -- | Opposite result of a 'Check'
  | Not Check
  -- | Check if 'Path' has content
  | HasFileContent Path [String]
  -- | Check if a Symbolic link exists to a specific target
  | SymlinkExists Path Target
  -- | Check if a folder exists
  | FolderExists Path
  -- | Check if something exists at path
  | DoesExist Path
           deriving (Show, Eq)

{-|

The result of a 'Check'.

-}
data CheckResult = Yes
                 | No
                 deriving (Show, Enum, Eq)

{-|

The low level element to formulate a state on a machine.
If the check returns 'No' the 'Command's will run.
So the author should make sure the 'Command' will result
in a 'Yes' the next time this state will run.

All 'Check's have to return 'Yes' to avoid the 'Command's
to be run.

If a 'Command' returns a 'Failure' the following commands
will not be called.
Same holdes for 'States' if one of the states fail,
the following 'State's will not be /run/.

-}
data State =

  -- | State contains checks and commands
  -- if one command failed
  -- the following commands will not be
  -- executed.
  State [Check] [Command] (Maybe Comment)

  -- | To create depended states
  -- which should stop being executed
  -- when a previous state fails
  | States [Check] [State] (Maybe Comment)
           deriving (Show, Eq)


{-|

When a 'State' is /run/ it returns one of the 'StateResult's.

* positive 'CheckResult's will result in 'Fulfilled'.
* negative 'CheckResult's with positive 'CommandResult's will result in 'Fulfilled'.
* negative 'CheckResult's with negative 'CommandResult's will result in 'Unfulfilled'.

-}
data StateResult = Fulfilled
                 | Unfulfilled
                 deriving (Show, Eq, Enum)