packages feed

horizon-spec-pretty-0.0.1: src/Horizon/Spec/Pretty.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns      #-}

module Horizon.Spec.Pretty where

import qualified Data.ByteString    as B
import           Data.List.NonEmpty (NonEmpty ((:|)))
import qualified Data.Map           as Map
import           Data.Text          (Text)
import qualified Data.Text          as T
import qualified Data.Text.Encoding as T
import qualified Dhall
import           Dhall.Core         (Binding, Chunks (Chunks),
                                     Directory (Directory),
                                     Expr (App, Embed, Field, Let, ListLit, None, RecordLit, Some, TextLit, ToMap, With),
                                     File (File), Import (Import),
                                     ImportHashed (ImportHashed),
                                     ImportMode (Code), ImportType (Remote),
                                     Scheme (HTTPS), URL (URL),
                                     WithComponent (WithLabel), makeBinding,
                                     makeFieldSelection, makeRecordField,
                                     pretty)
import qualified Dhall.Map          as DMap
import           GHC.Exts           (fromList)
import           Horizon.Spec       (CabalFlag (MkCabalFlag),
                                     Compiler (MkCompiler),
                                     Flag (Disable, Enable),
                                     GitSource (MkGitSource),
                                     HackageSource (MkHackageSource),
                                     HaskellPackage (MkHaskellPackage),
                                     HaskellSource (FromGit, FromHackage, FromLocal, FromTarball),
                                     HorizonExport (MakeOverlay, MakePackageSet),
                                     LocalSource (MkLocalSource), Name (MkName),
                                     Overlay (MkOverlay),
                                     OverlayExportSettings (MkOverlayExportSettings),
                                     OverlayFile (MkOverlayFile),
                                     PackageList (MkPackageList),
                                     PackageSet (MkPackageSet),
                                     PackageSetExportSettings (MkPackageSetExportSettings),
                                     PackageSetFile (MkPackageSetFile),
                                     PackagesDir (MkPackagesDir), Repo (MkRepo),
                                     Revision (MkRevision), Subdir (MkSubdir),
                                     TarballSource (MkTarballSource),
                                     Url (MkUrl), Version (MkVersion))
import           Path               (Path, toFilePath)

horizonField :: Text -> Expr s a
horizonField = Field horizonSpecIdentifier . makeFieldSelection

callHackageLit :: Expr s a
callHackageLit = horizonField "callHackage"

callGitLit :: Expr s a
callGitLit = horizonField "callGit"

callTarballLit :: Expr s a
callTarballLit = horizonField "callTarball"

callLocalLit :: Expr s a
callLocalLit = horizonField "callLocal"

callHackageApp :: HackageSource -> Expr s a
callHackageApp (MkHackageSource (MkName x) (MkVersion v)) = App (App callHackageLit (TextLit $ Chunks [] x)) (TextLit $ Chunks [] v)

callGitApp :: GitSource -> Expr s a
callGitApp (MkGitSource (MkRepo (MkUrl x)) (MkRevision v) d) =
  let z = case d of
           Nothing -> App None $ horizonField "Subdir"
           Just (MkSubdir k) -> Some $ TextLit $ Chunks [] $ T.pack $ toFilePath k
  in App (App (App callGitLit (TextLit $ Chunks [] x)) (TextLit $ Chunks [] v)) z

callTarballApp :: TarballSource -> Expr s a
callTarballApp (MkTarballSource (MkUrl x)) = App callTarballLit $ TextLit $ Chunks [] x

callLocalApp :: LocalSource -> Expr s a
callLocalApp (MkLocalSource (MkSubdir x)) = App callLocalLit $ TextLit $ Chunks [] $ T.pack . toFilePath $ x

haskellSourceToExpr :: HaskellSource -> Expr s a
haskellSourceToExpr k = case k of
  FromHackage x -> callHackageApp x
  FromGit x     -> callGitApp x
  FromTarball x -> callTarballApp x
  FromLocal x   -> callLocalApp x

cabalFlagToExpr :: CabalFlag -> Expr s a
cabalFlagToExpr (MkCabalFlag x) =
  let (z, t) = case x of
                Disable a -> (makeFieldSelection "Disable", a)
                Enable a  -> (makeFieldSelection "Enable", a)
  in App (Field (horizonField "CabalFlag") z) (TextLit $ Chunks [] t)

haskellPackageToExpr :: HaskellPackage -> Expr s a
haskellPackageToExpr (MkHaskellPackage s _ ys) =
  let t = haskellSourceToExpr s
      applyFlagsExpr = if not . null $ ys then \x -> With x (WithLabel "flags" :| []) (ListLit Nothing $ GHC.Exts.fromList $ map cabalFlagToExpr ys) else id
  in applyFlagsExpr t

packageListToExpr :: PackageList -> Expr s a
packageListToExpr (MkPackageList (Map.toList -> ys)) = RecordLit . DMap.fromList . map (\(MkName x, y) -> (x, makeRecordField $ haskellPackageToExpr y)) $ ys

horizonExportToExpr :: HorizonExport -> Expr s Import
horizonExportToExpr (MakePackageSet x) = packageSetExportSettingsToExpr x
horizonExportToExpr (MakeOverlay x)    = overlayExportSettingsToExpr x

packageSetToExpr :: PackageSet -> Expr s a -> Expr s a
packageSetToExpr (MkPackageSet (MkCompiler c) _) xs = RecordLit $ DMap.fromList
  [ ("compiler", makeRecordField $ TextLit $ Chunks [] c)
  , ("packages", makeRecordField xs)
  ]

compilerToExpr :: Compiler -> Expr s a
compilerToExpr (MkCompiler c) = TextLit $ Chunks [] c

pathToExpr :: Path b t -> Expr s a
pathToExpr = TextLit . Chunks [] . T.pack . toFilePath

packagesDirToExpr :: PackagesDir -> Expr s a
packagesDirToExpr (MkPackagesDir d) = pathToExpr d

packageSetFileToExpr :: PackageSetFile -> Expr s a
packageSetFileToExpr (MkPackageSetFile d) = pathToExpr d

overlayFileToExpr :: OverlayFile -> Expr s a
overlayFileToExpr (MkOverlayFile d) = pathToExpr d

packageSetExportSettingsToExpr :: PackageSetExportSettings -> Expr s Import
packageSetExportSettingsToExpr (MkPackageSetExportSettings d f ys@(MkPackageSet _ xs)) = letHorizonSpecIn $ letPackagesBindingIn xs $ App (Field (horizonField "HorizonExport") (makeFieldSelection "MakePackageSet")) $ RecordLit . DMap.fromList $ [
    ("packageSetFile", makeRecordField $ packageSetFileToExpr f),
    ("packagesDir", makeRecordField $ packagesDirToExpr d),
    ("packageSet", makeRecordField $ packageSetToExpr ys $ ToMap "packages" Nothing)
    ]


overlayExportSettingsToExpr :: OverlayExportSettings -> Expr s Import
overlayExportSettingsToExpr (MkOverlayExportSettings d f (MkOverlay ys@(MkPackageSet _ xs))) = letHorizonSpecIn $ letPackagesBindingIn xs $ App (Field (horizonField "HorizonExport") (makeFieldSelection "MakeOverlay")) $ RecordLit . DMap.fromList $ [
    ("packageSetFile", makeRecordField $ overlayFileToExpr f),
    ("packagesDir", makeRecordField $ packagesDirToExpr d),
    ("packageSet", makeRecordField $ packageSetToExpr ys $ ToMap "packages" Nothing)
    ]


prettyHorizonExport :: HorizonExport -> Text
prettyHorizonExport = Dhall.Core.pretty . horizonExportToExpr

writeHorizonFile :: HorizonExport -> IO ()
writeHorizonFile = B.writeFile "horizon.dhall" . T.encodeUtf8 . Dhall.Core.pretty . horizonExportToExpr

loadHorizon :: IO HorizonExport
loadHorizon = Dhall.inputFile @HorizonExport Dhall.auto "horizon.dhall"

horizonSpecUrl :: Dhall.Core.URL
horizonSpecUrl = Dhall.Core.URL HTTPS "gitlab.homotopic.tech" (Dhall.Core.File (Dhall.Core.Directory ["dhall", "0.6", "raw", "-", "horizon-spec", "horizon"]) "package.dhall") Nothing Nothing

horizonSpecImportHashed :: ImportHashed
horizonSpecImportHashed = ImportHashed Nothing (Remote horizonSpecUrl)

horizonSpecImport :: Import
horizonSpecImport = Import horizonSpecImportHashed Code

horizonSpecIdentifier :: Expr s a
horizonSpecIdentifier = "H"

packagesIdentifier :: Text
packagesIdentifier = "packages"

horizonSpecBinding :: Binding s Import
horizonSpecBinding = makeBinding "H" (Dhall.Core.Embed horizonSpecImport)

letHorizonSpecIn :: Expr s Import -> Expr s Import
letHorizonSpecIn = Let horizonSpecBinding

packagesBinding :: PackageList -> Binding s a
packagesBinding = makeBinding packagesIdentifier . packageListToExpr

letPackagesBindingIn :: PackageList -> Expr s a -> Expr s a
letPackagesBindingIn xs = Let (packagesBinding xs)