persist2er-0.1.0.0: src/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Database.Persist.Quasi
import Database.Persist.Types
import qualified Data.Text.IO as T
import qualified Data.Text as T
import Data.Text (Text)
import Types
import Data.Monoid
import Data.Maybe
import Options.Applicative
import Args
main :: IO ()
main = execParser opts >>= program
where
opts = info (helper <*> options)
( fullDesc
<> header "persist2er - Create a ER from a persist's model file"
)
program :: ProgramOpts -> IO ()
program opts = do
txt <- T.readFile (fileIn opts)
let con = conf opts
defs = parse lowerCaseSettings txt
(entities, relations) = unzip . map (convert con) . zip [0..] $ defs
entitiesTxt = T.unlines entities
relationsTxt = T.unlines relations
T.writeFile (fileOut opts) (genTitle con <> entitiesTxt <> "\n" <> relationsTxt)
genTitle :: Config -> Text
genTitle config = "title { label: \"" <> title config <> "\", size: \"" <> (T.pack . show . size $ config) <> "\"}"
-- | Generate a entity
-- This return a tuple of an entity and the relations
convert :: Config -> (Int, EntityDef) -> (Text, Text)
convert config (i, edef) = (entity, relations)
where
entity = T.unlines $
[ "[" <> genEntityName config edef <> "] { bgcolor: \"" <> selectColor <> "\"}"
, genIdField config edef
] ++ map (genEntityFields config) (entityFields edef)
relations = T.unlines . catMaybes $ map (genRelationsField config edef) (entityFields edef)
selectColor = unColor $ (colors config) !! (i `mod` length (colors config))
-- | Get the entity name acording to the config
genEntityName :: Config -> EntityDef -> Text
genEntityName config edef = entityName (useHaskellName config)
where
entityName True = unHaskellName . entityHaskell $ edef
entityName False = unDBName . entityDB $ edef
-- | Generate the Id of the Entity
genIdField :: Config -> EntityDef -> Text
genIdField config edef = "*" <> genEntityFields config (entityId edef)
genEntityFields :: Config -> FieldDef -> Text
genEntityFields config fdef = genFieldName (useHaskellName config) fdef <> " {label: \"" <> genFieldLabel fdef <> "\"}"
where
genFieldName True = unHaskellName . fieldHaskell
genFieldName False = unDBName . fieldDB
genFieldLabel fdef = T.intercalate ", " $
[ genFieldType (useHaskellName config) fdef
] ++ (maybeToList . genFieldNull (useHaskellName config) $ fdef)
genFieldType True = getType . fieldType
where
getType (FTTypeCon _ name) = name
genFieldType False = getTypeSql . fieldSqlType
where
genFieldNull True fdef = if fieldStrict fdef then Nothing else Just "Maybe"
genFieldNull False fdef = if fieldStrict fdef then Just "NOT NULL" else Just "NULL"
-- | Generate the relations.
genRelationsField :: Config -> EntityDef -> FieldDef -> Maybe Text
genRelationsField config edef fdef =
case (T.stripSuffix "Id" . getFieldType . fieldType $ fdef) of
Nothing -> Nothing
Just x -> Just $ x <> "\t1--*\t" <> genEntityName config edef
where
getFieldType (FTTypeCon _ typ) = typ
-- | Convert a SqlType to Tetx.
-- TODO: Currently this dosen't work because we need to know the backend
-- (postgres, mysql, etc) that is begin used by the user.
getTypeSql :: SqlType -> Text
getTypeSql SqlString = "TEXT"
getTypeSql SqlInt32 = "INT"
getTypeSql SqlInt64 = "INT64"
getTypeSql SqlReal = "FLOAT"
getTypeSql (SqlNumeric _ _) = "INT"
getTypeSql SqlBool = "BOOL"
getTypeSql SqlDay = "DAY"
getTypeSql SqlTime = "TIME"
getTypeSql SqlDayTime = "DATETIME"
getTypeSql SqlBlob = "BLOB"
getTypeSql (SqlOther other) = other