valiant-cli-0.1.0.0: src/Valiant/CLI/CustomTypes.hs
module Valiant.CLI.CustomTypes
( CustomTypeMap
, loadCustomTypes
, customTypesFile
) where
import Data.Aeson (FromJSON (..), eitherDecode, withObject, (.:))
import Data.ByteString.Lazy qualified as LBS
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Text (Text)
import Data.Word (Word32)
import PgWire.Protocol.Oid (Oid (..))
import Valiant.CLI.TypeMap (HaskellType (..))
import System.Directory (doesFileExist)
-- | User-defined OID → Haskell type mappings.
type CustomTypeMap = Map Oid HaskellType
-- | Default file name for custom type mappings.
customTypesFile :: FilePath
customTypesFile = "valiant-types.json"
-- | Load custom type mappings from a JSON file if it exists.
-- Returns an empty map if the file is absent.
--
-- Expected format:
--
-- > [
-- > { "oid": 600, "pg_type_name": "point", "haskell_type": "MyPoint", "haskell_module": "MyApp.Types" },
-- > { "oid": 16389, "pg_type_name": "mood", "haskell_type": "Text", "haskell_module": "Data.Text" }
-- > ]
loadCustomTypes :: FilePath -> IO CustomTypeMap
loadCustomTypes path = do
exists <- doesFileExist path
if not exists
then pure Map.empty
else do
bytes <- LBS.readFile path
case eitherDecode bytes of
Left _ -> pure Map.empty
Right entries -> pure (toMap entries)
toMap :: [CustomTypeEntry] -> CustomTypeMap
toMap = Map.fromList . map toPair
where
toPair CustomTypeEntry {..} =
(Oid cteOid, HaskellType cteHaskellType cteHaskellModule)
data CustomTypeEntry = CustomTypeEntry
{ cteOid :: Word32
, ctePgTypeName :: Text
, cteHaskellType :: Text
, cteHaskellModule :: Text
}
instance FromJSON CustomTypeEntry where
parseJSON = withObject "CustomTypeEntry" $ \o ->
CustomTypeEntry
<$> o .: "oid"
<*> o .: "pg_type_name"
<*> o .: "haskell_type"
<*> o .: "haskell_module"