packages feed

keuringsdienst-0.1.0.5: src/Keuringsdienst.hs

{-
  Copyright © 2023 Josep Bigorra

  This file is part of Keuringsdienst.
  Keuringsdienst is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation,
    either version 3 of the License, or (at your option) any later version.

  Keuringsdienst is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  See the GNU General Public License for more details.
  You should have received a copy of the GNU General Public License along with Keuringsdienst.
  If not, see <https://www.gnu.org/licenses/>.
-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module Keuringsdienst
  ( -- * Core types
    ValidationResult,
    ValidationRule (..),
    Validation (..),

    -- * Operators
    keuren,
    misschienKeuren,
    validate,
    maybeValidate,
    ofDitOfDat,
    orThisOrThat,
    (*||*),
    (|??|),
    (|?|),
  )
where

import Data.Aeson hiding (Success)
import Data.Text as T
import GHC.Generics

-- | Data type that represents the error messages. Currently is set to a simple Text,
--  futurely this could be expanded to contain more metadata.
type ErrMsg = Text

-- | Core data type representing the outcome of a validation, which can be a success,
--  or a failure with more information about the failure.
data Validation err
  = Success
  | Failure err
  deriving (Eq, Show, Generic, FromJSON, ToJSON)

-- | ValidationResult is the data type of choice for now, since we have chosen to implement
-- the library in a way that it will return a [Text] as context for validation failures.
type ValidationResult = Validation [ErrMsg]

instance Semigroup ValidationResult where
  (<>) a b = case a of
    Success -> case b of
      Success -> Success
      Failure errorsB -> Failure errorsB
    Failure errorsA -> case b of
      Success -> Failure errorsA
      Failure errorsB -> Failure (errorsA <> errorsB)

instance Monoid ValidationResult where
  mempty = Success

-- | Data type representing a composable validation rule to be applied on a certain data.
newtype ValidationRule a = ValidationRule
  { performValidation :: a -> ValidationResult
  }

instance Semigroup (ValidationRule x) where
  (<>) a b = do
    ValidationRule
      { performValidation = \value -> performValidation a value <> performValidation b value
      }

instance Monoid (ValidationRule a) where
  mempty = ValidationRule {performValidation = const Success}

-- | This function applies a validation rule to a value. Stands for validate/judge in Dutch.
keuren :: a -> ValidationRule a -> ValidationResult
keuren x rule = performValidation rule x

infixl 8 |?|

-- | keuren operator
(|?|) :: a -> ValidationRule a -> ValidationResult
(|?|) = keuren

-- | Alias to the keuren operator
validate :: a -> ValidationRule a -> ValidationResult
validate = keuren

-- | This function applies a validation rule to a value when it is a Just and
-- defaults to a Success in case of a Nothing. Stands for maybe validate/judge in Dutch.
misschienKeuren :: Maybe a -> ValidationRule a -> ValidationResult
misschienKeuren x rule = maybe Success (performValidation rule) x

infixl 8 |??|

-- | misschienKeuren operator
(|??|) :: Maybe a -> ValidationRule a -> ValidationResult
(|??|) = misschienKeuren

-- | Alias to the misschienKeuren operator
maybeValidate :: Maybe a -> ValidationRule a -> ValidationResult
maybeValidate = misschienKeuren

-- | If one of the validations has a successful result, then the validation is a success.
-- Stands for or this or that in Dutch.
ofDitOfDat :: ValidationRule a -> ValidationRule a -> ValidationRule a
ofDitOfDat rule1 rule2 = ValidationRule $ \actual ->
  case (performValidation rule1 actual, performValidation rule2 actual) of
    (Failure e1, Failure e2) -> Failure (e1 <> e2)
    (Success, _) -> Success
    (_, Success) -> Success

infixl 6 *||*

-- | ofDitOfDat operator
(*||*) :: ValidationRule a -> ValidationRule a -> ValidationRule a
(*||*) = ofDitOfDat

-- | Alias to the ofDitOfDat operator
orThisOrThat :: ValidationRule a -> ValidationRule a -> ValidationRule a
orThisOrThat = ofDitOfDat