packages feed

warlock-0.1.0.0: test/Warlock/ComputedFieldsSpec.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Warlock.ComputedFieldsSpec (spec) where

import Test.Hspec
import qualified Witch as W
import Warlock

-- Combining multiple source fields into one destination field
data PersonV1 = PersonV1
  { firstName :: String
  , lastName :: String
  , age :: Int
  } deriving (Show, Eq)

data PersonV2 = PersonV2
  { fullName :: String
  , personAge :: Int
  } deriving (Show, Eq)

deriveAutomap
  ( ByName $
    defaultConfig
    `withRules`
    [ combineFields 'fullName $ do
        firstName <- get 'firstName
        lastName <- get 'lastName
        pure [| $firstName ++ " " ++ $lastName |]
    , rename 'personAge 'age
    ]
  )
  ''PersonV1
  ''PersonV2

-- Multiple computed fields
data ProductV1 = ProductV1
  { itemName :: String
  , unitPrice :: Double
  , quantity :: Int
  } deriving (Show, Eq)

data ProductV2 = ProductV2
  { description :: String
  , totalCost :: Double
  } deriving (Show, Eq)

deriveAutomap
  ( ByName $
    defaultConfig
    { staticRules =
        [ combineFields 'description $ do
            itemName <- get 'itemName
            quantity <- get 'quantity
            pure [| $itemName ++ " (qty: " ++ show $quantity ++ ")" |]
        , combineFields 'totalCost $ do
            unitPrice <- get 'unitPrice
            quantity <- get 'quantity
            pure [| $unitPrice * fromIntegral $quantity |]
        ]
    }
  )
  ''ProductV1
  ''ProductV2

-- Splitting fields (reverse operation)
data PersonV3 = PersonV3
  { fullNameV3 :: String
  , ageV3 :: Int
  } deriving (Show, Eq)

data PersonV4 = PersonV4
  { firstNameV4 :: String
  , lastNameV4 :: String
  , ageV4 :: Int
  } deriving (Show, Eq)

splitFullName :: String -> (String, String)
splitFullName s = case words s of
  []  -> ("", "")
  [w] -> (w, "")
  (w:ws) -> (w, unwords ws)

deriveAutomap
  ( ByName $
    defaultConfig
    `withRules`
    [ combineFields 'firstNameV4 $ do
        fullNameV3 <- get 'fullNameV3
        pure [| fst (splitFullName $fullNameV3) |]
    , combineFields 'lastNameV4 $ do
        fullNameV3 <- get 'fullNameV3
        pure [| snd (splitFullName $fullNameV3) |]
    , rename 'ageV4 'ageV3
    ]
  )
  ''PersonV3
  ''PersonV4

spec :: Spec
spec = do
  describe "Computed destination fields" $ do
    it "combines multiple source fields into one destination field" $ do
      let personV1 = PersonV1 "Alice" "Smith" 30
      let personV2 = W.from personV1 :: PersonV2
      fullName personV2 `shouldBe` "Alice Smith"
      personAge personV2 `shouldBe` 30

    it "computes multiple destination fields from the same sources" $ do
      let productV1 = ProductV1 "Widget" 9.99 5
      let productV2 = W.from productV1 :: ProductV2
      description productV2 `shouldBe` "Widget (qty: 5)"
      totalCost productV2 `shouldBe` 49.95

    it "can split a field into multiple fields" $ do
      let personV3 = PersonV3 "Bob Jones" 25
      let personV4 = W.from personV3 :: PersonV4
      firstNameV4 personV4 `shouldBe` "Bob"
      lastNameV4 personV4 `shouldBe` "Jones"
      ageV4 personV4 `shouldBe` 25