{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} module Language.PureScript.Bridge ( bridgeSumType , defaultBridge , module Bridge , writePSTypes ) where import Control.Applicative import qualified Data.Map as M import qualified Data.Set as Set import qualified Data.Text.IO as T import Language.PureScript.Bridge.Builder as Bridge import Language.PureScript.Bridge.Primitives as Bridge import Language.PureScript.Bridge.Printer as Bridge import Language.PureScript.Bridge.SumType as Bridge import Language.PureScript.Bridge.Tuple as Bridge import Language.PureScript.Bridge.TypeInfo as Bridge -- | Your entry point to this library and quite likely all you will need. -- Make sure all your types derive `Generic` and `Typeable`. -- Typeable is not needed from ghc-7.10 on. -- -- Then list all your types you want to use in PureScript and call 'writePSTypes': -- -- > data Foo = Foo { ... } deriving (Eq, Generic) -- > data Bar = A | B | C deriving (Eq, Ord, Generic) -- > data Baz = ... deriving (Generic) -- > -- > -- | All types will have a `Generic` instance produced in Purescript. -- > myTypes :: [SumType 'Haskell] -- > myTypes = -- > [ let p = (Proxy :: Proxy Foo) in equal p (mkSumType p) -- Also produce a `Eq` instance. -- > , let p = (Proxy :: Proxy Bar) in order p (mkSumType p) -- Produce both `Eq` and `Ord`. -- > , mkSumType (Proxy :: Proxy Baz) -- Just produce a `Generic` instance. -- > ] -- > -- > writePSTypes "path/to/your/purescript/project" (buildBridge defaultBridge) myTypes -- -- You can define your own type bridges based on 'defaultBridge': -- -- -- > myBridge = defaultBridge <|> mySpecialTypeBridge -- -- and use it with 'writePSTypes': -- -- > writePSTypes "path/to/your/purescript/project" (buildBridge myBridge) myTypes -- -- Find examples for implementing your own bridges in: "Language.PureScript.Bridge.Primitives". -- -- == Result: -- 'writePSTypes' will write out PureScript modules to the given path, mirroring the hierarchy of the Haskell modules -- the types came from. In addition a list of needed PS packages is printed to the console. -- -- The list of needed packages is retrieved from the bridged 'TypeInfo' data, so make sure you set '_typePackage' correctly -- in your own bridges, in order for this feature to be useful. -- -- == Real world usage example (at time of this writing outdated, at time of reading hopefully fixed): -- A real world use case of this library can be found . -- -- With custom bridges defined and -- custom PS types defined . -- -- Parts of the generated output can be found . -- -- Note how 'Secret' and 'Key' -- get translated according to our custom rules, with correct imports and everything. -- Also the formatting is quite nice, would you have guessed that this code was generated? -- -- == /WARNING/: -- This function overwrites files - make backups or use version control! writePSTypes :: FilePath -> FullBridge -> [SumType 'Haskell] -> IO () writePSTypes root br sts = do let bridged = map (bridgeSumType br) sts let modules = M.elems $ sumTypesToModules M.empty bridged mapM_ (printModule root) modules T.putStrLn "The following purescript packages are needed by the generated code:\n" let packages = Set.insert "purescript-profunctor-lenses" $ sumTypesToNeededPackages bridged mapM_ (T.putStrLn . mappend " - ") packages T.putStrLn "\nSuccessfully created your PureScript modules!" -- | Translate all 'TypeInfo' values in a 'SumType' to PureScript types. -- -- Example usage, with defaultBridge: -- -- > data Foo = Foo | Bar Int | FooBar Int Text deriving (Generic, Typeable, Show) -- -- > bridgeSumType (buildBridge defaultBridge) (mkSumType (Proxy :: Proxy Foo)) bridgeSumType :: FullBridge -> SumType 'Haskell -> SumType 'PureScript bridgeSumType br (SumType t cs is) = SumType (br t) (map (bridgeConstructor br) cs) is -- | Default bridge for mapping primitive/common types: -- You can append your own bridges like this: -- -- > defaultBridge <|> myBridge1 <|> myBridge2 -- -- Find examples for bridge definitions in "Language.PureScript.Bridge.Primitives" and -- "Language.PureScript.Bridge.Tuple". defaultBridge :: BridgePart defaultBridge = textBridge <|> stringBridge <|> listBridge <|> maybeBridge <|> eitherBridge <|> boolBridge <|> intBridge <|> doubleBridge <|> tupleBridge <|> unitBridge <|> noContentBridge -- | Translate types in a constructor. bridgeConstructor :: FullBridge -> DataConstructor 'Haskell -> DataConstructor 'PureScript bridgeConstructor br (DataConstructor name (Left infos)) = DataConstructor name . Left $ map br infos bridgeConstructor br (DataConstructor name (Right record)) = DataConstructor name . Right $ map (bridgeRecordEntry br) record -- | Translate types in a record entry. bridgeRecordEntry :: FullBridge -> RecordEntry 'Haskell -> RecordEntry 'PureScript bridgeRecordEntry br (RecordEntry label value) = RecordEntry label $ br value