packages feed

jsonlogic-0.1.0.0: src/JsonLogic/Operation/Array.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLists #-}

-- |
-- Module      : JsonLogic.Operation.Array
-- Description : Internal JsonLogic operations on arrays
-- Copyright   : (c) Marien Matser, Gerard van Schie, Jelle Teeuwissen, 2022
-- License     : MIT
-- Maintainer  : jelleteeuwissen@hotmail.nl
-- Stability   : experimental
module JsonLogic.Operation.Array (arrayOperations, map, reduce, filter, all, none, some, merge, in') where

import Control.Monad
import qualified Data.List as L
import JsonLogic.Json
import JsonLogic.Operation.Primitive
import JsonLogic.Type
import Prelude hiding (all, filter, map)

arrayOperations :: Monad m => Operations m
arrayOperations = [map, reduce, filter, all, none, some, merge, in']

map, reduce, filter :: Monad m => Operation m
map = ("map", evaluateMap)
reduce = ("reduce", evaluateReduce)
filter = ("filter", evaluateFilter)

all, none, some :: Monad m => Operation m
all = ("all", evaluateArrayToBool (\case [] -> False; bools -> and bools))
none = ("none", evaluateArrayToBool (not . or))
some = ("some", evaluateArrayToBool or)

merge, in' :: Monad m => Operation m
merge = ("merge", evaluateMerge)
in' = ("in", evaluateIn)

-- Evaluation for map
evaluateMap :: Monad m => Function m Json
evaluateMap evaluator (JsonArray [xs, f]) vars = do
  xs' <- evaluateArray evaluator xs vars -- This is our data we evaluate
  JsonArray <$> mapM (evaluator f) xs'
evaluateMap _ _ _ = throw "Map received the wrong arguments"

evaluateReduce :: Monad m => Function m Json
evaluateReduce evaluator (JsonArray [arrayExp, reduceFunction, initalExp]) vars = do
  array <- evaluateArray evaluator arrayExp vars
  initial <- evaluator initalExp vars
  foldM (\acc cur -> evaluator reduceFunction (JsonObject [("current", cur), ("accumulator", acc)])) initial array
evaluateReduce _ _ _ = throw "Wrong number of arguments for reduce"

evaluateFilter :: Monad m => Function m Json
evaluateFilter evaluator (JsonArray [xs, f]) vars = do
  array <- evaluateArray evaluator xs vars
  filtered <- filterM (evaluateBool evaluator f) array
  return $ JsonArray filtered
evaluateFilter _ _ _ = throw "Wrong number of arguments for filter"

evaluateArrayToBool :: Monad m => ([Bool] -> Bool) -> Function m Json
evaluateArrayToBool operator evaluator (JsonArray [xs, f]) vars = do
  xs' <- evaluateArray evaluator xs vars -- This is our data we evaluate
  bools <- mapM (evaluateBool evaluator f) xs'
  return $ JsonBool $ operator bools
evaluateArrayToBool _ _ _ _ = throw "Map received the wrong arguments"

-- | Merge operations flattens the array in the top level
evaluateMerge :: Monad m => Function m Json
evaluateMerge evaluator params vars = do
  res <- evaluator params vars
  case res of
    (JsonArray js) -> return $ JsonArray $ foldr merge' [] js
    -- If we get a single item, it gets put in an array
    json -> return $ JsonArray [json]
  where
    merge' (JsonArray as) acc = as ++ acc
    merge' j acc = j : acc

evaluateIn :: Monad m => Function m Json
evaluateIn evaluator (JsonArray (sub : arr : _)) vars = do
  sub' <- evaluator sub vars
  arr' <- evaluator arr vars
  return $
    JsonBool $ case (sub', arr') of
      (el, JsonArray xs) -> el `elem` xs
      (JsonString substr, JsonString s) -> substr `L.isInfixOf` s
      _ -> False
evaluateIn _ _ _ = return $ JsonBool False