warlock-0.1.0.0: test/Warlock/DisassembleFieldsSpec.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Warlock.DisassembleFieldsSpec (spec) where
import Test.Hspec
import Warlock
import Language.Haskell.TH (varE)
import qualified Witch as W
-- V1: combined field
data PersonV1 = PersonV1
{ fullName :: String
, age :: Int
} deriving (Show, Eq)
-- V2: separate fields
data PersonV2 = PersonV2
{ firstName :: String
, lastName :: String
, personAge :: Int
} deriving (Show, Eq)
-- Split fullName into firstName and lastName
deriveAutomap
( ByName $
defaultConfig
`withRules`
( disassembleFields 'fullName
[ 'firstName .= do
src <- getSource
pure [| case words $src of
(f:_:_) -> f
[single] -> single
_ -> ""
|]
, 'lastName .= do
src <- getSource
pure [| case words $src of
(_:l:_) -> l
_ -> ""
|]
]
++
[ rename 'personAge 'age
]
)
)
''PersonV1
''PersonV2
-- Another example: address parsing
data AddressV1 = AddressV1
{ fullAddress :: String
, city :: String
} deriving (Show, Eq)
data AddressV2 = AddressV2
{ street :: String
, zipCode :: String
, addressCity :: String
} deriving (Show, Eq)
-- Split fullAddress into street and zipCode
deriveAutomap
( ByName $
defaultConfig
`withRules`
( disassembleFields 'fullAddress
[ 'street .= do
src <- getSource
pure [| let parts = reverse $ words $src
in case parts of
(_:rest) -> unwords (reverse rest)
_ -> $src
|]
, 'zipCode .= do
src <- getSource
pure [| let parts = reverse $ words $src
in case parts of
(zip:_) -> zip
_ -> ""
|]
]
++
[ rename 'addressCity 'city
]
)
)
''AddressV1
''AddressV2
spec :: Spec
spec = do
describe "disassembleFields" $ do
it "splits fullName into firstName and lastName" $ do
let person = PersonV1 "John Doe" 30
let result = W.from @PersonV1 @PersonV2 person
result `shouldBe` PersonV2 "John" "Doe" 30
it "handles single-word names" $ do
let person = PersonV1 "Madonna" 65
let result = W.from @PersonV1 @PersonV2 person
result `shouldBe` PersonV2 "Madonna" "" 65
it "splits address into street and zipCode" $ do
let addr = AddressV1 "123 Main St 12345" "Springfield"
let result = W.from @AddressV1 @AddressV2 addr
result `shouldBe` AddressV2 "123 Main St" "12345" "Springfield"
it "handles address without zipCode" $ do
let addr = AddressV1 "Downtown" "Chicago"
let result = W.from @AddressV1 @AddressV2 addr
-- "Downtown" is treated as zipCode when there's only one word
result `shouldBe` AddressV2 "" "Downtown" "Chicago"