packages feed

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

{-# OPTIONS_GHC -Wno-unused-imports #-}

-- |
-- Module      : JsonLogic.Operation.Utils
-- Description : Internal JsonLogic operations utilities
-- Copyright   : (c) Marien Matser, Gerard van Schie, Jelle Teeuwissen, 2022
-- License     : MIT
-- Maintainer  : jelleteeuwissen@hotmail.nl
-- Stability   : experimental
module JsonLogic.Operation.Utils where

-- IMPORTANT!! Needs singleton import for doctests
import qualified Data.Map as M
import JsonLogic.Json
import JsonLogic.Type
import Text.Read

-- | Index a json object using a string seperated by periods.
--
-- >>> indexWithJson (JsonString "x.y") (JsonObject $ M.singleton "x" $ JsonObject $ M.singleton "y" JsonNull)
-- Just null
--
-- >>> indexWithJson (JsonString "x.y") (JsonObject $ M.singleton "x" JsonNull)
-- Nothing
--
-- >>> indexWithJson (JsonString "") (JsonNumber 1)
-- Just 1.0
--
-- >>> indexWithJson (JsonString "1") (JsonArray [JsonString "abc", JsonString "def"])
-- Just "def"
--
-- >>> indexWithJson (JsonString "1.0") (JsonArray [JsonString "abc", JsonString "def"])
-- Just "d"
--
-- >>> indexWithJson (JsonString "abs") (JsonArray [JsonString "abc", JsonString "def"])
-- Nothing
indexWithJson :: Rule -> Data -> Maybe Json
indexWithJson (JsonString indexString) = indexWithString (splitOnPeriod indexString)
indexWithJson (JsonNumber indexNumber) = indexWithString [show (floor indexNumber :: Int)]
indexWithJson _ = const Nothing

indexWithString :: [String] -> Data -> Maybe Json
indexWithString [] vars = Just vars
indexWithString [x] (JsonString s) =
  readMaybe x >>= (!?) s >>= Just . JsonString . singleton
indexWithString (x : xs) (JsonArray js) =
  readMaybe x >>= (!?) js >>= indexWithString xs
indexWithString (x : xs) (JsonObject o) = M.lookup x o >>= indexWithString xs
indexWithString _ _ = Nothing

-- | Splits string on periods
-- Same definition as words at: https://github.com/ghc/ghc/blob/master/libraries/base/Data/OldList.hs
--
-- >>> splitOnPeriod "foo.bar.tea"
-- ["foo","bar","tea"]
splitOnPeriod :: String -> [String]
splitOnPeriod "" = []
splitOnPeriod s = case dropWhile ('.' Prelude.==) s of
  "." -> []
  s' -> w : splitOnPeriod s''
    where
      (w, s'') = break ('.' Prelude.==) s'

-- Safe indexing of a list
(!?) :: [a] -> Int -> Maybe a
_ !? n | n < 0 = Nothing
[] !? _ = Nothing
(x : _) !? 0 = Just x
(_ : xs) !? n = xs !? (n - 1)

-- | Returns the single item in a list if the argument is an array, otherwise returns the argument
-- If you like, we support syntactic sugar to skip the array around single arguments
-- Should only be used for unary operations.
--
-- >>> evaluateUnaryArgument $ JsonArray [JsonString "abc"]
-- "abc"
--
-- >>> evaluateUnaryArgument $ JsonString "abc"
-- "abc"
evaluateUnaryArgument :: Data -> Data
evaluateUnaryArgument (JsonArray [json]) = json
evaluateUnaryArgument json = json

-- | Put a single item in a list
-- Included in base since: base-4.15.0.0
-- But currently on older version.
--
-- >>> singleton "single value"
-- ["single value"]
singleton :: a -> [a]
singleton x = [x]