    {-# LANGUAGE CPP                 #-}
    {-# LANGUAGE DeriveAnyClass      #-}
    {-# LANGUAGE DeriveDataTypeable  #-}
    {-# LANGUAGE NamedFieldPuns      #-}
    {-# LANGUAGE OverloadedStrings   #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE RankNTypes          #-}
    {-# LANGUAGE RecordWildCards     #-}
    {-# OPTIONS_GHC -Wall #-}

    {-| Dhall lets you import external expressions located either in local files or
        hosted on network endpoints.

        To import a local file as an expression, just insert the path to the file,
        prepending a @./@ if the path is relative to the current directory.  For
        example, if you create a file named @id@ with the following contents:

        > $ cat id
        > λ(a : Type) → λ(x : a) → x

        Then you can use the file directly within a @dhall@ program just by
        referencing the file's path:

        > $ dhall
        > ./id Bool True
        > <Ctrl-D>
        > Bool
        >
        > True

        Imported expressions may contain imports of their own, too, which will
        continue to be resolved.  However, Dhall will prevent cyclic imports.  For
        example, if you had these two files:

        > $ cat foo
        > ./bar

        > $ cat bar
        > ./foo

        ... Dhall would throw the following exception if you tried to import @foo@:

        > $ dhall
        > ./foo
        > ^D
        > ↳ ./foo
        >   ↳ ./bar
        >
        > Cyclic import: ./foo

        You can also import expressions hosted on network endpoints.  Just use the
        URL

        > http://host[:port]/path

        The compiler expects the downloaded expressions to be in the same format
        as local files, specifically UTF8-encoded source code text.

        For example, if our @id@ expression were hosted at @http://example.com/id@,
        then we would embed the expression within our code using:

        > http://example.com/id

        You can also import expressions stored within environment variables using
        @env:NAME@, where @NAME@ is the name of the environment variable.  For
        example:

        > $ export FOO=1
        > $ export BAR='"Hi"'
        > $ export BAZ='λ(x : Bool) → x == False'
        > $ dhall <<< "{ foo = env:FOO , bar = env:BAR , baz = env:BAZ }"
        > { bar : Text, baz : ∀(x : Bool) → Bool, foo : Integer }
        >
        > { bar = "Hi", baz = λ(x : Bool) → x == False, foo = 1 }

        If you wish to import the raw contents of an impoert as @Text@ then add
        @as Text@ to the end of the import:

        > $ dhall <<< "http://example.com as Text"
        > Text
        >
        > "<!doctype html>\n<html>\n<head>\n    <title>Example Domain</title>\n\n    <meta
        >  charset=\"utf-8\" />\n    <meta http-equiv=\"Content-type\" content=\"text/html
        > ; charset=utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width,
        > initial-scale=1\" />\n    <style type=\"text/css\">\n    body {\n        backgro
        > und-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-famil
        > y: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n        \n
        >    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        paddi
        > ng: 50px;\n        background-color: #fff;\n        border-radius: 1em;\n    }\n
        >     a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;
        > \n    }\n    @media (max-width: 700px) {\n        body {\n            background
        > -color: #fff;\n        }\n        div {\n            width: auto;\n            m
        > argin: 0 auto;\n            border-radius: 0;\n            padding: 1em;\n
        >   }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>Example Domain</
        > h1>\n    <p>This domain is established to be used for illustrative examples in d
        > ocuments. You may use this\n    domain in examples without prior coordination or
        >  asking for permission.</p>\n    <p><a href=\"http://www.iana.org/domains/exampl
        > e\">More information...</a></p>\n</div>\n</body>\n</html>\n"
    -}

    module Dhall.Import (
        -- * Import
          load
        , loadRelativeTo
        , loadWith
        , localToPath
        , hashExpression
        , hashExpressionToCode
        , writeExpressionToSemanticCache
        , warnAboutMissingCaches
        , assertNoImports
        , Status(..)
        , SemanticCacheMode(..)
        , Chained
        , chainedImport
        , chainedFromLocalHere
        , chainedChangeMode
        , emptyStatus
        , stack
        , cache
        , Depends(..)
        , graph
        , remote
        , toHeaders
        , substitutions
        , normalizer
        , startingContext
        , chainImport
        , ImportSemantics
        , Cycle(..)
        , ReferentiallyOpaque(..)
        , Imported(..)
        , ImportResolutionDisabled(..)
        , PrettyHttpException(..)
        , MissingFile(..)
        , MissingEnvironmentVariable(..)
        , MissingImports(..)
        , HashMismatch(..)
        ) where

    import Control.Applicative (Alternative(..), liftA2)
    import Control.Exception (Exception, SomeException, IOException, toException)
    import Control.Monad (when)
    import Control.Monad.Catch (throwM, MonadCatch(catch), handle)
    import Control.Monad.IO.Class (MonadIO(..))
    import Control.Monad.Trans.Class (lift)
    import Control.Monad.Trans.State.Strict (StateT)
    import Data.ByteString (ByteString)
    import Data.CaseInsensitive (CI)
    import Data.List.NonEmpty (NonEmpty(..))
    import Data.Semigroup (Semigroup(..))
    import Data.Text (Text)
    import Data.Void (Void, absurd)
    import Data.Typeable (Typeable)
    import System.FilePath ((</>))
    import Dhall.Binary (StandardVersion(..))
    import Dhall.Syntax
        ( Expr(..)
        , Chunks(..)
        , Directory(..)
        , File(..)
        , FilePrefix(..)
        , ImportHashed(..)
        , ImportType(..)
        , ImportMode(..)
        , Import(..)
        , URL(..)
        , bindingExprs
        )
    #ifdef WITH_HTTP
    import Dhall.Import.HTTP
    #endif
    import Dhall.Import.Types

    import Dhall.Parser (Parser(..), ParseError(..), Src(..), SourcedException(..))
    import Lens.Family.State.Strict (zoom)

    import qualified Codec.CBOR.Encoding                         as Encoding
    import qualified Codec.CBOR.Write                            as Write
    import qualified Codec.Serialise
    import qualified Control.Monad.Trans.Maybe                   as Maybe
    import qualified Control.Monad.Trans.State.Strict            as State
    import qualified Data.ByteString
    import qualified Data.ByteString.Lazy
    import qualified Data.CaseInsensitive
    import qualified Data.Foldable
    import qualified Data.List.NonEmpty                          as NonEmpty
    import qualified Data.Text.Encoding
    import qualified Data.Text                                   as Text
    import qualified Data.Text.IO
    import qualified Dhall.Binary
    import qualified Dhall.Core
    import qualified Dhall.Crypto
    import qualified Dhall.Map
    import qualified Dhall.Parser
    import qualified Dhall.Pretty.Internal
    import qualified Dhall.Substitution
    import qualified Dhall.Syntax                                as Syntax
    import qualified Dhall.TypeCheck
    import qualified System.AtomicWrite.Writer.ByteString.Binary as AtomicWrite.Binary
    import qualified System.Environment
    import qualified System.Info
    import qualified System.IO
    import qualified System.Directory                            as Directory
    import qualified System.FilePath                             as FilePath
    import qualified Text.Megaparsec
    import qualified Text.Parser.Combinators
    import qualified Text.Parser.Token

    -- | An import failed because of a cycle in the import graph
    newtype Cycle = Cycle
        { Cycle -> Import
    cyclicImport :: Import  -- ^ The offending cyclic import
        }
      deriving (Typeable)

    instance Exception Cycle

    instance Show Cycle where
        show :: Cycle -> String
    show (Cycle import_ :: Import
    import_) =
            "\nCyclic import: " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Import -> String
    forall a. Pretty a => a -> String
    Dhall.Pretty.Internal.prettyToString Import
    import_

    {-| Dhall tries to ensure that all expressions hosted on network endpoints are
        weakly referentially transparent, meaning roughly that any two clients will
        compile the exact same result given the same URL.

        To be precise, a strong interpretaton of referential transparency means that
        if you compiled a URL you could replace the expression hosted at that URL
        with the compiled result.  Let's call this \"static linking\".  Dhall (very
        intentionally) does not satisfy this stronger interpretation of referential
        transparency since \"statically linking\" an expression (i.e. permanently
        resolving all imports) means that the expression will no longer update if
        its dependencies change.

        In general, either interpretation of referential transparency is not
        enforceable in a networked context since one can easily violate referential
        transparency with a custom DNS, but Dhall can still try to guard against
        common unintentional violations.  To do this, Dhall enforces that a
        non-local import may not reference a local import.

        Local imports are defined as:

        * A file

        * A URL with a host of @localhost@ or @127.0.0.1@

        All other imports are defined to be non-local
    -}
    newtype ReferentiallyOpaque = ReferentiallyOpaque
        { ReferentiallyOpaque -> Import
    opaqueImport :: Import  -- ^ The offending opaque import
        } deriving (Typeable)

    instance Exception ReferentiallyOpaque

    instance Show ReferentiallyOpaque where
        show :: ReferentiallyOpaque -> String
    show (ReferentiallyOpaque import_ :: Import
    import_) =
            "\nReferentially opaque import: " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Import -> String
    forall a. Pretty a => a -> String
    Dhall.Pretty.Internal.prettyToString Import
    import_

    -- | Extend another exception with the current import stack
    data Imported e = Imported
        { Imported e -> NonEmpty Chained
    importStack :: NonEmpty Chained  -- ^ Imports resolved so far, in reverse order
        , Imported e -> e
    nested      :: e                 -- ^ The nested exception
        } deriving (Typeable)

    instance Exception e => Exception (Imported e)

    instance Show e => Show (Imported e) where
        show :: Imported e -> String
    show (Imported canonicalizedImports :: NonEmpty Chained
    canonicalizedImports e :: e
    e) =
               [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat ((Int -> Chained -> String) -> [Int] -> [Chained] -> [String]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith Int -> Chained -> String
    forall a. Pretty a => Int -> a -> String
    indent [0..] [Chained]
    toDisplay)
            String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ "\n"
            String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ e -> String
    forall a. Show a => a -> String
    show e
    e
          where
            indent :: Int -> a -> String
    indent n :: Int
    n import_ :: a
    import_ =
                "\n" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ Int -> Char -> String
    forall a. Int -> a -> [a]
    replicate (2 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    n) ' ' String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ "↳ " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ a -> String
    forall a. Pretty a => a -> String
    Dhall.Pretty.Internal.prettyToString a
    import_

            canonical :: [Chained]
    canonical = NonEmpty Chained -> [Chained]
    forall a. NonEmpty a -> [a]
    NonEmpty.toList NonEmpty Chained
    canonicalizedImports

            -- Tthe final (outermost) import is fake to establish the base
            -- directory. Also, we need outermost-first.
            toDisplay :: [Chained]
    toDisplay = Int -> [Chained] -> [Chained]
    forall a. Int -> [a] -> [a]
    drop 1 ([Chained] -> [Chained]
    forall a. [a] -> [a]
    reverse [Chained]
    canonical)

    -- | Exception thrown when an imported file is missing
    data MissingFile = MissingFile FilePath
        deriving (Typeable)

    instance Exception MissingFile

    instance Show MissingFile where
        show :: MissingFile -> String
    show (MissingFile path :: String
    path) =
                "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\ESC[1;31mError\ESC[0m: Missing file "
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  String
    path

    -- | Exception thrown when an environment variable is missing
    newtype MissingEnvironmentVariable = MissingEnvironmentVariable { MissingEnvironmentVariable -> Text
    name :: Text }
        deriving (Typeable)

    instance Exception MissingEnvironmentVariable

    instance Show MissingEnvironmentVariable where
        show :: MissingEnvironmentVariable -> String
    show (MissingEnvironmentVariable {..}) =
                "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\ESC[1;31mError\ESC[0m: Missing environment variable\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> Text -> String
    Text.unpack Text
    name

    -- | List of Exceptions we encounter while resolving Import Alternatives
    newtype MissingImports = MissingImports [SomeException]

    instance Exception MissingImports

    instance Show MissingImports where
        show :: MissingImports -> String
    show (MissingImports []) =
                "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\ESC[1;31mError\ESC[0m: No valid imports"
        show (MissingImports [e :: SomeException
    e]) = SomeException -> String
    forall a. Show a => a -> String
    show SomeException
    e
        show (MissingImports es :: [SomeException]
    es) =
                "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\ESC[1;31mError\ESC[0m: Failed to resolve imports. Error list:"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  (SomeException -> String) -> [SomeException] -> String
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap (\e :: SomeException
    e -> "\n" String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> SomeException -> String
    forall a. Show a => a -> String
    show SomeException
    e String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n") [SomeException]
    es

    throwMissingImport :: (MonadCatch m, Exception e) => e -> m a
    throwMissingImport :: e -> m a
    throwMissingImport e :: e
    e = MissingImports -> m a
    forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
    throwM ([SomeException] -> MissingImports
    MissingImports [e -> SomeException
    forall e. Exception e => e -> SomeException
    toException e
    e])

    type HTTPHeader = (CI ByteString, ByteString)

    -- | Exception thrown when a HTTP url is imported but dhall was built without
    -- the @with-http@ Cabal flag.
    data CannotImportHTTPURL =
        CannotImportHTTPURL
            String
            (Maybe [HTTPHeader])
        deriving (Typeable)

    instance Exception CannotImportHTTPURL

    instance Show CannotImportHTTPURL where
        show :: CannotImportHTTPURL -> String
    show (CannotImportHTTPURL url :: String
    url _mheaders :: Maybe [HTTPHeader]
    _mheaders) =
                "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\ESC[1;31mError\ESC[0m: Cannot import HTTP URL.\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "Dhall was compiled without the 'with-http' flag.\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "The requested URL was: "
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  String
    url
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"

    {-|
    > canonicalize . canonicalize = canonicalize

    > canonicalize (a <> b) = canonicalize (canonicalize a <> canonicalize b)
    -}
    class Semigroup path => Canonicalize path where
        canonicalize :: path -> path

    -- |
    -- >>> canonicalize (Directory {components = ["..",".."]})
    -- Directory {components = ["..",".."]}
    instance Canonicalize Directory where
        canonicalize :: Directory -> Directory
    canonicalize (Directory []) = [Text] -> Directory
    Directory []

        canonicalize (Directory ("." : components₀ :: [Text]
    components₀)) =
            Directory -> Directory
    forall path. Canonicalize path => path -> path
    canonicalize ([Text] -> Directory
    Directory [Text]
    components₀)

        canonicalize (Directory (".." : components₀ :: [Text]
    components₀)) =
            case Directory -> Directory
    forall path. Canonicalize path => path -> path
    canonicalize ([Text] -> Directory
    Directory [Text]
    components₀) of
                Directory [] ->
                    [Text] -> Directory
    Directory [ ".." ]
                Directory (".." : components₁ :: [Text]
    components₁) ->
                    [Text] -> Directory
    Directory (".." Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : ".." Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : [Text]
    components₁)
                Directory (_    : components₁ :: [Text]
    components₁) ->
                    [Text] -> Directory
    Directory [Text]
    components₁

        canonicalize (Directory (component :: Text
    component : components₀ :: [Text]
    components₀)) =
            [Text] -> Directory
    Directory (Text
    component Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : [Text]
    components₁)
          where
            Directory components₁ :: [Text]
    components₁ = Directory -> Directory
    forall path. Canonicalize path => path -> path
    canonicalize ([Text] -> Directory
    Directory [Text]
    components₀)

    instance Canonicalize File where
        canonicalize :: File -> File
    canonicalize (File { Directory
    directory :: File -> Directory
    directory :: Directory
    directory, .. }) =
            File :: Directory -> Text -> File
    File { directory :: Directory
    directory = Directory -> Directory
    forall path. Canonicalize path => path -> path
    canonicalize Directory
    directory, .. }

    instance Canonicalize ImportType where
        canonicalize :: ImportType -> ImportType
    canonicalize (Local prefix :: FilePrefix
    prefix file :: File
    file) =
            FilePrefix -> File -> ImportType
    Local FilePrefix
    prefix (File -> File
    forall path. Canonicalize path => path -> path
    canonicalize File
    file)

        canonicalize (Remote (URL {..})) =
            URL -> ImportType
    Remote (URL :: Scheme
    -> Text -> File -> Maybe Text -> Maybe (Expr Src Import) -> URL
    URL { path :: File
    path = File -> File
    forall path. Canonicalize path => path -> path
    canonicalize File
    path, headers :: Maybe (Expr Src Import)
    headers = (Expr Src Import -> Expr Src Import)
    -> Maybe (Expr Src Import) -> Maybe (Expr Src Import)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((Import -> Import) -> Expr Src Import -> Expr Src Import
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Import -> Import
    forall path. Canonicalize path => path -> path
    canonicalize) Maybe (Expr Src Import)
    headers, ..})

        canonicalize (Env name :: Text
    name) =
            Text -> ImportType
    Env Text
    name

        canonicalize Missing =
            ImportType
    Missing

    instance Canonicalize ImportHashed where
        canonicalize :: ImportHashed -> ImportHashed
    canonicalize (ImportHashed hash :: Maybe SHA256Digest
    hash importType :: ImportType
    importType) =
            Maybe SHA256Digest -> ImportType -> ImportHashed
    ImportHashed Maybe SHA256Digest
    hash (ImportType -> ImportType
    forall path. Canonicalize path => path -> path
    canonicalize ImportType
    importType)

    instance Canonicalize Import where
        canonicalize :: Import -> Import
    canonicalize (Import importHashed :: ImportHashed
    importHashed importMode :: ImportMode
    importMode) =
            ImportHashed -> ImportMode -> Import
    Import (ImportHashed -> ImportHashed
    forall path. Canonicalize path => path -> path
    canonicalize ImportHashed
    importHashed) ImportMode
    importMode

    -- | Exception thrown when an integrity check fails
    data HashMismatch = HashMismatch
        { HashMismatch -> SHA256Digest
    expectedHash :: Dhall.Crypto.SHA256Digest
        , HashMismatch -> SHA256Digest
    actualHash   :: Dhall.Crypto.SHA256Digest
        } deriving (Typeable)

    instance Exception HashMismatch

    instance Show HashMismatch where
        show :: HashMismatch -> String
    show (HashMismatch {..}) =
                "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\ESC[1;31mError\ESC[0m: Import integrity check failed\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "Expected hash:\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> SHA256Digest -> String
    forall a. Show a => a -> String
    show SHA256Digest
    expectedHash String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "Actual hash:\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "\n"
            String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <>  "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> SHA256Digest -> String
    forall a. Show a => a -> String
    show SHA256Digest
    actualHash String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"

    -- | Construct the file path corresponding to a local import. If the import is
    --   _relative_ then the resulting path is also relative.
    localToPath :: MonadIO io => FilePrefix -> File -> io FilePath
    localToPath :: FilePrefix -> File -> io String
    localToPath prefix :: FilePrefix
    prefix file_ :: File
    file_ = IO String -> io String
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO String -> io String) -> IO String -> io String
    forall a b. (a -> b) -> a -> b
    $ do
        let File {..} = File
    file_

        let Directory {..} = Directory
    directory

        String
    prefixPath <- case FilePrefix
    prefix of
            Home -> do
                IO String
    Directory.getHomeDirectory

            Absolute -> do
                String -> IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return "/"

            Parent -> do
                String -> IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return ".."

            Here -> do
                String -> IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return "."

        let cs :: [String]
    cs = (Text -> String) -> [Text] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> String
    Text.unpack (Text
    file Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : [Text]
    components)

        let cons :: String -> ShowS
    cons component :: String
    component dir :: String
    dir = String
    dir String -> ShowS
    </> String
    component

        String -> IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return ((String -> ShowS) -> String -> [String] -> String
    forall (t :: * -> *) a b.
    Foldable t =>
    (a -> b -> b) -> b -> t a -> b
    foldr String -> ShowS
    cons String
    prefixPath [String]
    cs)

    -- | Given a `Local` import construct the corresponding unhashed `Chained`
    --   import (interpreting relative path as relative to the current directory).
    chainedFromLocalHere :: FilePrefix -> File -> ImportMode -> Chained
    chainedFromLocalHere :: FilePrefix -> File -> ImportMode -> Chained
    chainedFromLocalHere prefix :: FilePrefix
    prefix file :: File
    file mode :: ImportMode
    mode = Import -> Chained
    Chained (Import -> Chained) -> Import -> Chained
    forall a b. (a -> b) -> a -> b
    $
         ImportHashed -> ImportMode -> Import
    Import (Maybe SHA256Digest -> ImportType -> ImportHashed
    ImportHashed Maybe SHA256Digest
    forall a. Maybe a
    Nothing (FilePrefix -> File -> ImportType
    Local FilePrefix
    prefix (File -> File
    forall path. Canonicalize path => path -> path
    canonicalize File
    file))) ImportMode
    mode

    -- | Adjust the import mode of a chained import
    chainedChangeMode :: ImportMode -> Chained -> Chained
    chainedChangeMode :: ImportMode -> Chained -> Chained
    chainedChangeMode mode :: ImportMode
    mode (Chained (Import importHashed :: ImportHashed
    importHashed _)) =
        Import -> Chained
    Chained (ImportHashed -> ImportMode -> Import
    Import ImportHashed
    importHashed ImportMode
    mode)

    -- | Chain imports, also typecheck and normalize headers if applicable.
    chainImport :: Chained -> Import -> StateT Status IO Chained
    chainImport :: Chained -> Import -> StateT Status IO Chained
    chainImport (Chained parent :: Import
    parent) child :: Import
    child@(Import importHashed :: ImportHashed
    importHashed@(ImportHashed _ (Remote url :: URL
    url)) _) = do
        URL
    url' <- URL -> StateT Status IO URL
    normalizeHeaders URL
    url
        let child' :: Import
    child' = Import
    child { importHashed :: ImportHashed
    importHashed = ImportHashed
    importHashed { importType :: ImportType
    importType = URL -> ImportType
    Remote URL
    url' } }
        Chained -> StateT Status IO Chained
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Import -> Chained
    Chained (Import -> Import
    forall path. Canonicalize path => path -> path
    canonicalize (Import
    parent Import -> Import -> Import
    forall a. Semigroup a => a -> a -> a
    <> Import
    child')))

    chainImport (Chained parent :: Import
    parent) child :: Import
    child =
        Chained -> StateT Status IO Chained
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Import -> Chained
    Chained (Import -> Import
    forall path. Canonicalize path => path -> path
    canonicalize (Import
    parent Import -> Import -> Import
    forall a. Semigroup a => a -> a -> a
    <> Import
    child)))

    -- | Load an import, resulting in a fully resolved, type-checked and normalised
    --   expression. @loadImport@ handles the 'hot' cache in @Status@ and defers to
    --   `loadImportWithSemanticCache` for imports that aren't in the @Status@ cache
    --   already.
    loadImport :: Chained -> StateT Status IO ImportSemantics
    loadImport :: Chained -> StateT Status IO ImportSemantics
    loadImport import_ :: Chained
    import_ = do
        Status {..} <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get

        case Chained -> Map Chained ImportSemantics -> Maybe ImportSemantics
    forall k v. Ord k => k -> Map k v -> Maybe v
    Dhall.Map.lookup Chained
    import_ Map Chained ImportSemantics
    _cache of
            Just importSemantics :: ImportSemantics
    importSemantics -> ImportSemantics -> StateT Status IO ImportSemantics
    forall (m :: * -> *) a. Monad m => a -> m a
    return ImportSemantics
    importSemantics
            Nothing -> do
                ImportSemantics
    importSemantics <- Chained -> StateT Status IO ImportSemantics
    loadImportWithSemanticCache Chained
    import_
                LensLike' (Zooming IO ()) Status (Map Chained ImportSemantics)
    -> StateT (Map Chained ImportSemantics) IO ()
    -> StateT Status IO ()
    forall (m :: * -> *) c s a.
    Monad m =>
    LensLike' (Zooming m c) s a -> StateT a m c -> StateT s m c
    zoom LensLike' (Zooming IO ()) Status (Map Chained ImportSemantics)
    forall (f :: * -> *).
    Functor f =>
    LensLike' f Status (Map Chained ImportSemantics)
    cache ((Map Chained ImportSemantics -> Map Chained ImportSemantics)
    -> StateT (Map Chained ImportSemantics) IO ()
    forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
    State.modify (Chained
    -> ImportSemantics
    -> Map Chained ImportSemantics
    -> Map Chained ImportSemantics
    forall k v. Ord k => k -> v -> Map k v -> Map k v
    Dhall.Map.insert Chained
    import_ ImportSemantics
    importSemantics))
                ImportSemantics -> StateT Status IO ImportSemantics
    forall (m :: * -> *) a. Monad m => a -> m a
    return ImportSemantics
    importSemantics

    -- | Load an import from the 'semantic cache'. Defers to
    --   `loadImportWithSemisemanticCache` for imports that aren't frozen (and
    --   therefore not cached semantically), as well as those that aren't cached yet.
    loadImportWithSemanticCache :: Chained -> StateT Status IO ImportSemantics
    loadImportWithSemanticCache :: Chained -> StateT Status IO ImportSemantics
    loadImportWithSemanticCache
      import_ :: Chained
    import_@(Chained (Import (ImportHashed Nothing _) _)) = do
        Chained -> StateT Status IO ImportSemantics
    loadImportWithSemisemanticCache Chained
    import_

    loadImportWithSemanticCache
      import_ :: Chained
    import_@(Chained (Import (ImportHashed (Just semanticHash :: SHA256Digest
    semanticHash) _) _)) = do
        Status { .. } <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get
        Maybe ByteString
    mCached <-
            case SemanticCacheMode
    _semanticCacheMode of
                UseSemanticCache -> IO (Maybe ByteString) -> StateT Status IO (Maybe ByteString)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Maybe ByteString) -> StateT Status IO (Maybe ByteString))
    -> IO (Maybe ByteString) -> StateT Status IO (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ SHA256Digest -> IO (Maybe ByteString)
    fetchFromSemanticCache SHA256Digest
    semanticHash
                IgnoreSemanticCache -> Maybe ByteString -> StateT Status IO (Maybe ByteString)
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure Maybe ByteString
    forall a. Maybe a
    Nothing

        case Maybe ByteString
    mCached of
            Just bytesStrict :: ByteString
    bytesStrict -> do
                let actualHash :: SHA256Digest
    actualHash = ByteString -> SHA256Digest
    Dhall.Crypto.sha256Hash ByteString
    bytesStrict

                if SHA256Digest
    semanticHash SHA256Digest -> SHA256Digest -> Bool
    forall a. Eq a => a -> a -> Bool
    == SHA256Digest
    actualHash
                    then () -> StateT Status IO ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
                    else do
                        Status { NonEmpty Chained
    _stack :: NonEmpty Chained
    _stack :: Status -> NonEmpty Chained
    _stack } <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get
                        Imported HashMismatch -> StateT Status IO ()
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> HashMismatch -> Imported HashMismatch
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack (HashMismatch :: SHA256Digest -> SHA256Digest -> HashMismatch
    HashMismatch {expectedHash :: SHA256Digest
    expectedHash = SHA256Digest
    semanticHash, ..}))

                let bytesLazy :: ByteString
    bytesLazy = ByteString -> ByteString
    Data.ByteString.Lazy.fromStrict ByteString
    bytesStrict

                Expr Void Void
    importSemantics <- case ByteString -> Either DecodingFailure (Expr Void Void)
    forall s a.
    Serialise (Expr s a) =>
    ByteString -> Either DecodingFailure (Expr s a)
    Dhall.Binary.decodeExpression ByteString
    bytesLazy of
                    Left  err :: DecodingFailure
    err -> Imported DecodingFailure -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> DecodingFailure -> Imported DecodingFailure
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack DecodingFailure
    err)
                    Right e :: Expr Void Void
    e   -> Expr Void Void -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Expr Void Void
    e

                ImportSemantics -> StateT Status IO ImportSemantics
    forall (m :: * -> *) a. Monad m => a -> m a
    return (ImportSemantics :: Expr Void Void -> ImportSemantics
    ImportSemantics {..})

            Nothing -> do
                ImportSemantics { Expr Void Void
    importSemantics :: Expr Void Void
    importSemantics :: ImportSemantics -> Expr Void Void
    importSemantics } <- Chained -> StateT Status IO ImportSemantics
    loadImportWithSemisemanticCache Chained
    import_

                let variants :: [ByteString]
    variants = (StandardVersion -> ByteString)
    -> [StandardVersion] -> [ByteString]
    forall a b. (a -> b) -> [a] -> [b]
    map (\version :: StandardVersion
    version -> StandardVersion -> Expr Void Void -> ByteString
    encodeExpression StandardVersion
    version (Expr Void Void -> Expr Void Void
    forall s a. Expr s a -> Expr s a
    Dhall.Core.alphaNormalize Expr Void Void
    importSemantics))
                                    [ StandardVersion
    forall a. Bounded a => a
    minBound .. StandardVersion
    forall a. Bounded a => a
    maxBound ]
                case (ByteString -> Bool) -> [ByteString] -> Maybe ByteString
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
    Data.Foldable.find ((SHA256Digest -> SHA256Digest -> Bool
    forall a. Eq a => a -> a -> Bool
    == SHA256Digest
    semanticHash)(SHA256Digest -> Bool)
    -> (ByteString -> SHA256Digest) -> ByteString -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> SHA256Digest
    Dhall.Crypto.sha256Hash) [ByteString]
    variants of
                    Just bytes :: ByteString
    bytes -> IO () -> StateT Status IO ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> StateT Status IO ()) -> IO () -> StateT Status IO ()
    forall a b. (a -> b) -> a -> b
    $ SHA256Digest -> ByteString -> IO ()
    writeToSemanticCache SHA256Digest
    semanticHash ByteString
    bytes
                    Nothing -> do
                        let expectedHash :: SHA256Digest
    expectedHash = SHA256Digest
    semanticHash
                        Status { NonEmpty Chained
    _stack :: NonEmpty Chained
    _stack :: Status -> NonEmpty Chained
    _stack } <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get
                        let actualHash :: SHA256Digest
    actualHash = Expr Void Void -> SHA256Digest
    hashExpression (Expr Void Void -> Expr Void Void
    forall s a. Expr s a -> Expr s a
    Dhall.Core.alphaNormalize Expr Void Void
    importSemantics)
                        Imported HashMismatch -> StateT Status IO ()
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> HashMismatch -> Imported HashMismatch
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack (HashMismatch :: SHA256Digest -> SHA256Digest -> HashMismatch
    HashMismatch {..}))

                ImportSemantics -> StateT Status IO ImportSemantics
    forall (m :: * -> *) a. Monad m => a -> m a
    return (ImportSemantics :: Expr Void Void -> ImportSemantics
    ImportSemantics {..})

    -- Fetch encoded normal form from "semantic cache"
    fetchFromSemanticCache :: Dhall.Crypto.SHA256Digest -> IO (Maybe Data.ByteString.ByteString)
    fetchFromSemanticCache :: SHA256Digest -> IO (Maybe ByteString)
    fetchFromSemanticCache expectedHash :: SHA256Digest
    expectedHash = MaybeT IO ByteString -> IO (Maybe ByteString)
    forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
    Maybe.runMaybeT (MaybeT IO ByteString -> IO (Maybe ByteString))
    -> MaybeT IO ByteString -> IO (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ do
        String
    cacheFile <- String -> SHA256Digest -> MaybeT IO String
    forall (m :: * -> *).
    (MonadCatch m, Alternative m, MonadIO m) =>
    String -> SHA256Digest -> m String
    getCacheFile "dhall" SHA256Digest
    expectedHash
        Bool
    True <- IO Bool -> MaybeT IO Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO Bool
    Directory.doesFileExist String
    cacheFile)
        IO ByteString -> MaybeT IO ByteString
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO ByteString
    Data.ByteString.readFile String
    cacheFile)

    -- | Ensure that the given expression is present in the semantic cache. The
    --   given expression should be alpha-beta-normal.
    writeExpressionToSemanticCache :: Expr Void Void -> IO ()
    writeExpressionToSemanticCache :: Expr Void Void -> IO ()
    writeExpressionToSemanticCache expression :: Expr Void Void
    expression = SHA256Digest -> ByteString -> IO ()
    writeToSemanticCache SHA256Digest
    hash ByteString
    bytes
      where
        bytes :: ByteString
    bytes = StandardVersion -> Expr Void Void -> ByteString
    encodeExpression StandardVersion
    NoVersion Expr Void Void
    expression
        hash :: SHA256Digest
    hash = ByteString -> SHA256Digest
    Dhall.Crypto.sha256Hash ByteString
    bytes

    writeToSemanticCache :: Dhall.Crypto.SHA256Digest -> Data.ByteString.ByteString -> IO ()
    writeToSemanticCache :: SHA256Digest -> ByteString -> IO ()
    writeToSemanticCache hash :: SHA256Digest
    hash bytes :: ByteString
    bytes = do
        Maybe ()
    _ <- MaybeT IO () -> IO (Maybe ())
    forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
    Maybe.runMaybeT (MaybeT IO () -> IO (Maybe ())) -> MaybeT IO () -> IO (Maybe ())
    forall a b. (a -> b) -> a -> b
    $ do
            String
    cacheFile <- String -> SHA256Digest -> MaybeT IO String
    forall (m :: * -> *).
    (MonadCatch m, Alternative m, MonadIO m) =>
    String -> SHA256Digest -> m String
    getCacheFile "dhall" SHA256Digest
    hash
            IO () -> MaybeT IO ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> ByteString -> IO ()
    AtomicWrite.Binary.atomicWriteFile String
    cacheFile ByteString
    bytes)
        () -> IO ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

    -- Check the "semi-semantic" disk cache, otherwise typecheck and normalise from
    -- scratch.
    loadImportWithSemisemanticCache
      :: Chained -> StateT Status IO ImportSemantics
    loadImportWithSemisemanticCache :: Chained -> StateT Status IO ImportSemantics
    loadImportWithSemisemanticCache (Chained (Import (ImportHashed _ importType :: ImportType
    importType) Code)) = do
        Text
    text <- ImportType -> StateT Status IO Text
    fetchFresh ImportType
    importType
        Status {..} <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get

        String
    path <- case ImportType
    importType of
            Local prefix :: FilePrefix
    prefix file :: File
    file -> IO String -> StateT Status IO String
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO String -> StateT Status IO String)
    -> IO String -> StateT Status IO String
    forall a b. (a -> b) -> a -> b
    $ do
                String
    path <- FilePrefix -> File -> IO String
    forall (io :: * -> *).
    MonadIO io =>
    FilePrefix -> File -> io String
    localToPath FilePrefix
    prefix File
    file
                String
    absolutePath <- String -> IO String
    Directory.makeAbsolute String
    path
                String -> IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    absolutePath
            Remote url :: URL
    url -> do
                let urlText :: Text
    urlText = URL -> Text
    forall a. Pretty a => a -> Text
    Dhall.Core.pretty (URL
    url { headers :: Maybe (Expr Src Import)
    headers = Maybe (Expr Src Import)
    forall a. Maybe a
    Nothing })
                String -> StateT Status IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text -> String
    Text.unpack Text
    urlText)
            Env env :: Text
    env -> String -> StateT Status IO String
    forall (m :: * -> *) a. Monad m => a -> m a
    return (String -> StateT Status IO String)
    -> String -> StateT Status IO String
    forall a b. (a -> b) -> a -> b
    $ Text -> String
    Text.unpack Text
    env
            Missing -> MissingImports -> StateT Status IO String
    forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
    throwM ([SomeException] -> MissingImports
    MissingImports [])

        let parser :: Parsec Void Text (Expr Src Import)
    parser = Parser (Expr Src Import) -> Parsec Void Text (Expr Src Import)
    forall a. Parser a -> Parsec Void Text a
    unParser (Parser (Expr Src Import) -> Parsec Void Text (Expr Src Import))
    -> Parser (Expr Src Import) -> Parsec Void Text (Expr Src Import)
    forall a b. (a -> b) -> a -> b
    $ do
                Parser ()
    forall (m :: * -> *). TokenParsing m => m ()
    Text.Parser.Token.whiteSpace
                Expr Src Import
    r <- Parser (Expr Src Import)
    Dhall.Parser.expr
                Parser ()
    forall (m :: * -> *). Parsing m => m ()
    Text.Parser.Combinators.eof
                Expr Src Import -> Parser (Expr Src Import)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Expr Src Import
    r

        Expr Src Import
    parsedImport <- case Parsec Void Text (Expr Src Import)
    -> String
    -> Text
    -> Either (ParseErrorBundle Text Void) (Expr Src Import)
    forall e s a.
    Parsec e s a -> String -> s -> Either (ParseErrorBundle s e) a
    Text.Megaparsec.parse Parsec Void Text (Expr Src Import)
    parser String
    path Text
    text of
            Left  errInfo :: ParseErrorBundle Text Void
    errInfo -> do
                Imported ParseError -> StateT Status IO (Expr Src Import)
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> ParseError -> Imported ParseError
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack (ParseErrorBundle Text Void -> Text -> ParseError
    ParseError ParseErrorBundle Text Void
    errInfo Text
    text))
            Right expr :: Expr Src Import
    expr    -> Expr Src Import -> StateT Status IO (Expr Src Import)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Expr Src Import
    expr

        Expr Src Void
    resolvedExpr <- Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    parsedImport  -- we load imports recursively here

        -- Check the semi-semantic cache. See
        -- https://github.com/dhall-lang/dhall-haskell/issues/1098 for the reasoning
        -- behind semi-semantic caching.
        let semisemanticHash :: SHA256Digest
    semisemanticHash = Expr Void Void -> SHA256Digest
    computeSemisemanticHash (Expr Src Void -> Expr Void Void
    forall s a t. Expr s a -> Expr t a
    Dhall.Core.denote Expr Src Void
    resolvedExpr)
        Maybe ByteString
    mCached <- IO (Maybe ByteString) -> StateT Status IO (Maybe ByteString)
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (IO (Maybe ByteString) -> StateT Status IO (Maybe ByteString))
    -> IO (Maybe ByteString) -> StateT Status IO (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ SHA256Digest -> IO (Maybe ByteString)
    fetchFromSemisemanticCache SHA256Digest
    semisemanticHash

        Expr Void Void
    importSemantics <- case Maybe ByteString
    mCached of
            Just bytesStrict :: ByteString
    bytesStrict -> do
                let bytesLazy :: ByteString
    bytesLazy = ByteString -> ByteString
    Data.ByteString.Lazy.fromStrict ByteString
    bytesStrict

                Expr Void Void
    importSemantics <- case ByteString -> Either DecodingFailure (Expr Void Void)
    forall s a.
    Serialise (Expr s a) =>
    ByteString -> Either DecodingFailure (Expr s a)
    Dhall.Binary.decodeExpression ByteString
    bytesLazy of
                    Left err :: DecodingFailure
    err -> Imported DecodingFailure -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> DecodingFailure -> Imported DecodingFailure
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack DecodingFailure
    err)
                    Right sem :: Expr Void Void
    sem -> Expr Void Void -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Expr Void Void
    sem

                Expr Void Void -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Expr Void Void
    importSemantics

            Nothing -> do
                Expr Void Void
    betaNormal <- do
                    let substitutedExpr :: Expr Src Void
    substitutedExpr = Expr Src Void -> Substitutions Src Void -> Expr Src Void
    forall s a. Expr s a -> Substitutions s a -> Expr s a
    Dhall.Substitution.substitute Expr Src Void
    resolvedExpr Substitutions Src Void
    _substitutions
                    case Context (Expr Src Void)
    -> Expr Src Void -> Either (TypeError Src Void) (Expr Src Void)
    forall s.
    Context (Expr s Void)
    -> Expr s Void -> Either (TypeError s Void) (Expr s Void)
    Dhall.TypeCheck.typeWith Context (Expr Src Void)
    _startingContext Expr Src Void
    substitutedExpr of
                        Left  err :: TypeError Src Void
    err -> Imported (TypeError Src Void) -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained
    -> TypeError Src Void -> Imported (TypeError Src Void)
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack TypeError Src Void
    err)
                        Right _ -> Expr Void Void -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Maybe (ReifiedNormalizer Void) -> Expr Src Void -> Expr Void Void
    forall a s t.
    Eq a =>
    Maybe (ReifiedNormalizer a) -> Expr s a -> Expr t a
    Dhall.Core.normalizeWith Maybe (ReifiedNormalizer Void)
    _normalizer Expr Src Void
    resolvedExpr)

                let bytes :: ByteString
    bytes = StandardVersion -> Expr Void Void -> ByteString
    encodeExpression StandardVersion
    NoVersion Expr Void Void
    betaNormal
                IO () -> StateT Status IO ()
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (IO () -> StateT Status IO ()) -> IO () -> StateT Status IO ()
    forall a b. (a -> b) -> a -> b
    $ SHA256Digest -> ByteString -> IO ()
    writeToSemisemanticCache SHA256Digest
    semisemanticHash ByteString
    bytes

                Expr Void Void -> StateT Status IO (Expr Void Void)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Expr Void Void
    betaNormal

        ImportSemantics -> StateT Status IO ImportSemantics
    forall (m :: * -> *) a. Monad m => a -> m a
    return (ImportSemantics :: Expr Void Void -> ImportSemantics
    ImportSemantics {..})

    -- `as Text` imports aren't cached since they are well-typed and normal by
    -- construction
    loadImportWithSemisemanticCache (Chained (Import (ImportHashed _ importType :: ImportType
    importType) RawText)) = do
        Text
    text <- ImportType -> StateT Status IO Text
    fetchFresh ImportType
    importType

        -- importSemantics is alpha-beta-normal by construction!
        let importSemantics :: Expr s a
    importSemantics = Chunks s a -> Expr s a
    forall s a. Chunks s a -> Expr s a
    TextLit ([(Text, Expr s a)] -> Text -> Chunks s a
    forall s a. [(Text, Expr s a)] -> Text -> Chunks s a
    Chunks [] Text
    text)
        ImportSemantics -> StateT Status IO ImportSemantics
    forall (m :: * -> *) a. Monad m => a -> m a
    return (ImportSemantics :: Expr Void Void -> ImportSemantics
    ImportSemantics {..})

    -- `as Location` imports aren't cached since they are well-typed and normal by
    -- construction
    loadImportWithSemisemanticCache (Chained (Import (ImportHashed _ importType :: ImportType
    importType) Location)) = do
        let locationType :: Expr s a
    locationType = Map Text (Maybe (Expr s a)) -> Expr s a
    forall s a. Map Text (Maybe (Expr s a)) -> Expr s a
    Union (Map Text (Maybe (Expr s a)) -> Expr s a)
    -> Map Text (Maybe (Expr s a)) -> Expr s a
    forall a b. (a -> b) -> a -> b
    $ [(Text, Maybe (Expr s a))] -> Map Text (Maybe (Expr s a))
    forall k v. Ord k => [(k, v)] -> Map k v
    Dhall.Map.fromList
                [ ("Environment", Expr s a -> Maybe (Expr s a)
    forall a. a -> Maybe a
    Just Expr s a
    forall s a. Expr s a
    Text)
                , ("Remote", Expr s a -> Maybe (Expr s a)
    forall a. a -> Maybe a
    Just Expr s a
    forall s a. Expr s a
    Text)
                , ("Local", Expr s a -> Maybe (Expr s a)
    forall a. a -> Maybe a
    Just Expr s a
    forall s a. Expr s a
    Text)
                , ("Missing", Maybe (Expr s a)
    forall a. Maybe a
    Nothing)
                ]

        -- importSemantics is alpha-beta-normal by construction!
        let importSemantics :: Expr s a
    importSemantics = case ImportType
    importType of
                Missing -> Expr s a -> Text -> Expr s a
    forall s a. Expr s a -> Text -> Expr s a
    Field Expr s a
    forall s a. Expr s a
    locationType "Missing"
                local :: ImportType
    local@(Local _ _) ->
                    Expr s a -> Expr s a -> Expr s a
    forall s a. Expr s a -> Expr s a -> Expr s a
    App (Expr s a -> Text -> Expr s a
    forall s a. Expr s a -> Text -> Expr s a
    Field Expr s a
    forall s a. Expr s a
    locationType "Local")
                      (Chunks s a -> Expr s a
    forall s a. Chunks s a -> Expr s a
    TextLit ([(Text, Expr s a)] -> Text -> Chunks s a
    forall s a. [(Text, Expr s a)] -> Text -> Chunks s a
    Chunks [] (ImportType -> Text
    forall a. Pretty a => a -> Text
    Dhall.Core.pretty ImportType
    local)))
                remote_ :: ImportType
    remote_@(Remote _) ->
                    Expr s a -> Expr s a -> Expr s a
    forall s a. Expr s a -> Expr s a -> Expr s a
    App (Expr s a -> Text -> Expr s a
    forall s a. Expr s a -> Text -> Expr s a
    Field Expr s a
    forall s a. Expr s a
    locationType "Remote")
                      (Chunks s a -> Expr s a
    forall s a. Chunks s a -> Expr s a
    TextLit ([(Text, Expr s a)] -> Text -> Chunks s a
    forall s a. [(Text, Expr s a)] -> Text -> Chunks s a
    Chunks [] (ImportType -> Text
    forall a. Pretty a => a -> Text
    Dhall.Core.pretty ImportType
    remote_)))
                Env env :: Text
    env ->
                    Expr s a -> Expr s a -> Expr s a
    forall s a. Expr s a -> Expr s a -> Expr s a
    App (Expr s a -> Text -> Expr s a
    forall s a. Expr s a -> Text -> Expr s a
    Field Expr s a
    forall s a. Expr s a
    locationType "Environment")
                      (Chunks s a -> Expr s a
    forall s a. Chunks s a -> Expr s a
    TextLit ([(Text, Expr s a)] -> Text -> Chunks s a
    forall s a. [(Text, Expr s a)] -> Text -> Chunks s a
    Chunks [] (Text -> Text
    forall a. Pretty a => a -> Text
    Dhall.Core.pretty Text
    env)))

        ImportSemantics -> StateT Status IO ImportSemantics
    forall (m :: * -> *) a. Monad m => a -> m a
    return (ImportSemantics :: Expr Void Void -> ImportSemantics
    ImportSemantics {..})

    -- The semi-semantic hash of an expression is computed from the fully resolved
    -- AST (without normalising or type-checking it first). See
    -- https://github.com/dhall-lang/dhall-haskell/issues/1098 for further
    -- discussion.
    computeSemisemanticHash :: Expr Void Void -> Dhall.Crypto.SHA256Digest
    computeSemisemanticHash :: Expr Void Void -> SHA256Digest
    computeSemisemanticHash resolvedExpr :: Expr Void Void
    resolvedExpr = Expr Void Void -> SHA256Digest
    hashExpression Expr Void Void
    resolvedExpr

    -- Fetch encoded normal form from "semi-semantic cache"
    fetchFromSemisemanticCache :: Dhall.Crypto.SHA256Digest -> IO (Maybe Data.ByteString.ByteString)
    fetchFromSemisemanticCache :: SHA256Digest -> IO (Maybe ByteString)
    fetchFromSemisemanticCache semisemanticHash :: SHA256Digest
    semisemanticHash = MaybeT IO ByteString -> IO (Maybe ByteString)
    forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
    Maybe.runMaybeT (MaybeT IO ByteString -> IO (Maybe ByteString))
    -> MaybeT IO ByteString -> IO (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ do
        String
    cacheFile <- String -> SHA256Digest -> MaybeT IO String
    forall (m :: * -> *).
    (MonadCatch m, Alternative m, MonadIO m) =>
    String -> SHA256Digest -> m String
    getCacheFile "dhall-haskell" SHA256Digest
    semisemanticHash
        Bool
    True <- IO Bool -> MaybeT IO Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO Bool
    Directory.doesFileExist String
    cacheFile)
        IO ByteString -> MaybeT IO ByteString
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO ByteString
    Data.ByteString.readFile String
    cacheFile)

    writeToSemisemanticCache :: Dhall.Crypto.SHA256Digest -> Data.ByteString.ByteString -> IO ()
    writeToSemisemanticCache :: SHA256Digest -> ByteString -> IO ()
    writeToSemisemanticCache semisemanticHash :: SHA256Digest
    semisemanticHash bytes :: ByteString
    bytes = do
        Maybe ()
    _ <- MaybeT IO () -> IO (Maybe ())
    forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
    Maybe.runMaybeT (MaybeT IO () -> IO (Maybe ())) -> MaybeT IO () -> IO (Maybe ())
    forall a b. (a -> b) -> a -> b
    $ do
            String
    cacheFile <- String -> SHA256Digest -> MaybeT IO String
    forall (m :: * -> *).
    (MonadCatch m, Alternative m, MonadIO m) =>
    String -> SHA256Digest -> m String
    getCacheFile "dhall-haskell" SHA256Digest
    semisemanticHash
            IO () -> MaybeT IO ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> ByteString -> IO ()
    AtomicWrite.Binary.atomicWriteFile String
    cacheFile ByteString
    bytes)
        () -> IO ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

    -- Fetch source code directly from disk/network
    fetchFresh :: ImportType -> StateT Status IO Text
    fetchFresh :: ImportType -> StateT Status IO Text
    fetchFresh (Local prefix :: FilePrefix
    prefix file :: File
    file) = do
        Status { NonEmpty Chained
    _stack :: NonEmpty Chained
    _stack :: Status -> NonEmpty Chained
    _stack } <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get
        String
    path <- IO String -> StateT Status IO String
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO String -> StateT Status IO String)
    -> IO String -> StateT Status IO String
    forall a b. (a -> b) -> a -> b
    $ FilePrefix -> File -> IO String
    forall (io :: * -> *).
    MonadIO io =>
    FilePrefix -> File -> io String
    localToPath FilePrefix
    prefix File
    file
        Bool
    exists <- IO Bool -> StateT Status IO Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Bool -> StateT Status IO Bool)
    -> IO Bool -> StateT Status IO Bool
    forall a b. (a -> b) -> a -> b
    $ String -> IO Bool
    Directory.doesFileExist String
    path
        if Bool
    exists
            then IO Text -> StateT Status IO Text
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Text -> StateT Status IO Text)
    -> IO Text -> StateT Status IO Text
    forall a b. (a -> b) -> a -> b
    $ String -> IO Text
    Data.Text.IO.readFile String
    path
            else Imported MissingFile -> StateT Status IO Text
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> MissingFile -> Imported MissingFile
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack (String -> MissingFile
    MissingFile String
    path))

    fetchFresh (Remote url :: URL
    url) = do
        Status { URL -> StateT Status IO Text
    _remote :: URL -> StateT Status IO Text
    _remote :: Status -> URL -> StateT Status IO Text
    _remote } <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get
        URL -> StateT Status IO Text
    _remote URL
    url

    fetchFresh (Env env :: Text
    env) = do
        Status { NonEmpty Chained
    _stack :: NonEmpty Chained
    _stack :: Status -> NonEmpty Chained
    _stack } <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get
        Maybe String
    x <- IO (Maybe String) -> StateT Status IO (Maybe String)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Maybe String) -> StateT Status IO (Maybe String))
    -> IO (Maybe String) -> StateT Status IO (Maybe String)
    forall a b. (a -> b) -> a -> b
    $ String -> IO (Maybe String)
    System.Environment.lookupEnv (Text -> String
    Text.unpack Text
    env)
        case Maybe String
    x of
            Just string :: String
    string -> do
                Text -> StateT Status IO Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return (String -> Text
    Text.pack String
    string)
            Nothing -> do
                    Imported MissingEnvironmentVariable -> StateT Status IO Text
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained
    -> MissingEnvironmentVariable
    -> Imported MissingEnvironmentVariable
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack (Text -> MissingEnvironmentVariable
    MissingEnvironmentVariable Text
    env))

    fetchFresh Missing = MissingImports -> StateT Status IO Text
    forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
    throwM ([SomeException] -> MissingImports
    MissingImports [])

    fetchRemote :: URL -> StateT Status IO Data.Text.Text
    #ifndef WITH_HTTP
    fetchRemote (url@URL { headers = maybeHeadersExpression }) = do
        let maybeHeaders = fmap toHeaders maybeHeadersExpression
        let urlString = Text.unpack (Dhall.Core.pretty url)
        Status { _stack } <- State.get
        throwMissingImport (Imported _stack (CannotImportHTTPURL urlString maybeHeaders))
    #else
    fetchRemote :: URL -> StateT Status IO Text
    fetchRemote url :: URL
    url = do
        LensLike' (Zooming IO ()) Status (URL -> StateT Status IO Text)
    -> StateT (URL -> StateT Status IO Text) IO ()
    -> StateT Status IO ()
    forall (m :: * -> *) c s a.
    Monad m =>
    LensLike' (Zooming m c) s a -> StateT a m c -> StateT s m c
    zoom LensLike' (Zooming IO ()) Status (URL -> StateT Status IO Text)
    forall (f :: * -> *).
    Functor f =>
    LensLike' f Status (URL -> StateT Status IO Text)
    remote ((URL -> StateT Status IO Text)
    -> StateT (URL -> StateT Status IO Text) IO ()
    forall (m :: * -> *) s. Monad m => s -> StateT s m ()
    State.put URL -> StateT Status IO Text
    fetchFromHTTP)
        URL -> StateT Status IO Text
    fetchFromHTTP URL
    url
      where
        fetchFromHTTP :: URL -> StateT Status IO Data.Text.Text
        fetchFromHTTP :: URL -> StateT Status IO Text
    fetchFromHTTP (url' :: URL
    url'@URL { headers :: URL -> Maybe (Expr Src Import)
    headers = Maybe (Expr Src Import)
    maybeHeadersExpression }) = do
            let maybeHeaders :: Maybe [HTTPHeader]
    maybeHeaders = (Expr Src Import -> [HTTPHeader])
    -> Maybe (Expr Src Import) -> Maybe [HTTPHeader]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Expr Src Import -> [HTTPHeader]
    forall s a. Expr s a -> [HTTPHeader]
    toHeaders Maybe (Expr Src Import)
    maybeHeadersExpression
            URL -> Maybe [HTTPHeader] -> StateT Status IO Text
    fetchFromHttpUrl URL
    url' Maybe [HTTPHeader]
    maybeHeaders
    #endif

    -- | Given a well-typed (of type `List { header : Text, value Text }` or
    -- `List { mapKey : Text, mapValue Text }`) headers expressions in normal form
    -- construct the corresponding binary http headers; otherwise return the empty
    -- list.
    toHeaders :: Expr s a -> [HTTPHeader]
    toHeaders :: Expr s a -> [HTTPHeader]
    toHeaders (ListLit _ hs :: Seq (Expr s a)
    hs) = Seq HTTPHeader -> [HTTPHeader]
    forall (t :: * -> *) a. Foldable t => t a -> [a]
    Data.Foldable.toList (Maybe (Seq HTTPHeader) -> Seq HTTPHeader
    forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
    Data.Foldable.fold Maybe (Seq HTTPHeader)
    maybeHeaders)
      where
          maybeHeaders :: Maybe (Seq HTTPHeader)
    maybeHeaders = (Expr s a -> Maybe HTTPHeader)
    -> Seq (Expr s a) -> Maybe (Seq HTTPHeader)
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM Expr s a -> Maybe HTTPHeader
    forall s a. Expr s a -> Maybe HTTPHeader
    toHeader Seq (Expr s a)
    hs
    toHeaders _ = []

    toHeader :: Expr s a -> Maybe HTTPHeader
    toHeader :: Expr s a -> Maybe HTTPHeader
    toHeader (RecordLit m :: Map Text (Expr s a)
    m) = do
        (TextLit (Chunks [] keyText :: Text
    keyText), TextLit (Chunks [] valueText :: Text
    valueText)) <- Maybe (Expr s a, Expr s a)
    lookupHeader Maybe (Expr s a, Expr s a)
    -> Maybe (Expr s a, Expr s a) -> Maybe (Expr s a, Expr s a)
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> Maybe (Expr s a, Expr s a)
    lookupMapKey
        let keyBytes :: ByteString
    keyBytes   = Text -> ByteString
    Data.Text.Encoding.encodeUtf8 Text
    keyText
        let valueBytes :: ByteString
    valueBytes = Text -> ByteString
    Data.Text.Encoding.encodeUtf8 Text
    valueText
        HTTPHeader -> Maybe HTTPHeader
    forall (m :: * -> *) a. Monad m => a -> m a
    return (ByteString -> CI ByteString
    forall s. FoldCase s => s -> CI s
    Data.CaseInsensitive.mk ByteString
    keyBytes, ByteString
    valueBytes)
          where
            lookupHeader :: Maybe (Expr s a, Expr s a)
    lookupHeader = (Expr s a -> Expr s a -> (Expr s a, Expr s a))
    -> Maybe (Expr s a)
    -> Maybe (Expr s a)
    -> Maybe (Expr s a, Expr s a)
    forall (f :: * -> *) a b c.
    Applicative f =>
    (a -> b -> c) -> f a -> f b -> f c
    liftA2 (,) (Text -> Map Text (Expr s a) -> Maybe (Expr s a)
    forall k v. Ord k => k -> Map k v -> Maybe v
    Dhall.Map.lookup "header" Map Text (Expr s a)
    m) (Text -> Map Text (Expr s a) -> Maybe (Expr s a)
    forall k v. Ord k => k -> Map k v -> Maybe v
    Dhall.Map.lookup "value" Map Text (Expr s a)
    m)
            lookupMapKey :: Maybe (Expr s a, Expr s a)
    lookupMapKey = (Expr s a -> Expr s a -> (Expr s a, Expr s a))
    -> Maybe (Expr s a)
    -> Maybe (Expr s a)
    -> Maybe (Expr s a, Expr s a)
    forall (f :: * -> *) a b c.
    Applicative f =>
    (a -> b -> c) -> f a -> f b -> f c
    liftA2 (,) (Text -> Map Text (Expr s a) -> Maybe (Expr s a)
    forall k v. Ord k => k -> Map k v -> Maybe v
    Dhall.Map.lookup "mapKey" Map Text (Expr s a)
    m) (Text -> Map Text (Expr s a) -> Maybe (Expr s a)
    forall k v. Ord k => k -> Map k v -> Maybe v
    Dhall.Map.lookup "mapValue" Map Text (Expr s a)
    m)
    toHeader _ = do
        Maybe HTTPHeader
    forall (f :: * -> *) a. Alternative f => f a
    empty

    getCacheFile
        :: (MonadCatch m, Alternative m, MonadIO m) => FilePath -> Dhall.Crypto.SHA256Digest -> m FilePath
    getCacheFile :: String -> SHA256Digest -> m String
    getCacheFile cacheName :: String
    cacheName hash :: SHA256Digest
    hash = do
        String
    cacheDirectory <- Bool -> String -> m String
    forall (m :: * -> *).
    (MonadCatch m, Alternative m, MonadIO m) =>
    Bool -> String -> m String
    getOrCreateCacheDirectory Bool
    False String
    cacheName

        let cacheFile :: String
    cacheFile = String
    cacheDirectory String -> ShowS
    </> ("1220" String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> SHA256Digest -> String
    forall a. Show a => a -> String
    show SHA256Digest
    hash)

        String -> m String
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    cacheFile

    -- | Warn if no cache directory is available
    warnAboutMissingCaches :: (MonadCatch m, Alternative m, MonadIO m) => m ()
    warnAboutMissingCaches :: m ()
    warnAboutMissingCaches = m ()
    warn m () -> m () -> m ()
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> () -> m ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
        where warn :: m ()
    warn = (String -> m String) -> [String] -> m ()
    forall (t :: * -> *) (f :: * -> *) a b.
    (Foldable t, Applicative f) =>
    (a -> f b) -> t a -> f ()
    Data.Foldable.traverse_ (Bool -> String -> m String
    forall (m :: * -> *).
    (MonadCatch m, Alternative m, MonadIO m) =>
    Bool -> String -> m String
    getOrCreateCacheDirectory Bool
    True) ["dhall", "dhall-haskell"]

    getOrCreateCacheDirectory :: (MonadCatch m, Alternative m, MonadIO m) => Bool -> FilePath -> m FilePath
    getOrCreateCacheDirectory :: Bool -> String -> m String
    getOrCreateCacheDirectory showWarning :: Bool
    showWarning cacheName :: String
    cacheName = do
        let warn :: String -> m b
    warn message :: String
    message = do
                let warning :: String
    warning =
                         "\n"
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\ESC[1;33mWarning\ESC[0m: "
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    message

                Bool -> m () -> m ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    when Bool
    showWarning (IO () -> m ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (Handle -> String -> IO ()
    System.IO.hPutStrLn Handle
    System.IO.stderr String
    warning))

                m b
    forall (f :: * -> *) a. Alternative f => f a
    empty

        let handler :: String -> String -> IOException -> m b
    handler action :: String
    action dir :: String
    dir (IOException
    ioex :: IOException) = do
                let ioExMsg :: String
    ioExMsg =
                         "When trying to " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    action String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> ":\n"
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    dir String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "... the following exception was thrown:\n"
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> IOException -> String
    forall a. Show a => a -> String
    show IOException
    ioex String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"

                String -> m b
    forall (m :: * -> *) b. (MonadIO m, Alternative m) => String -> m b
    warn String
    ioExMsg

        let setPermissions :: String -> m ()
    setPermissions dir :: String
    dir = do
                let private :: Permissions
    private = Permissions -> Permissions
    transform Permissions
    Directory.emptyPermissions
                        where
                            transform :: Permissions -> Permissions
    transform =
                                Bool -> Permissions -> Permissions
    Directory.setOwnerReadable   Bool
    True
                              (Permissions -> Permissions)
    -> (Permissions -> Permissions) -> Permissions -> Permissions
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Bool -> Permissions -> Permissions
    Directory.setOwnerWritable   Bool
    True
                              (Permissions -> Permissions)
    -> (Permissions -> Permissions) -> Permissions -> Permissions
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Bool -> Permissions -> Permissions
    Directory.setOwnerSearchable Bool
    True

                m () -> (IOException -> m ()) -> m ()
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    catch
                    (IO () -> m ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> Permissions -> IO ()
    Directory.setPermissions String
    dir Permissions
    private))
                    (String -> String -> IOException -> m ()
    forall (m :: * -> *) b.
    (MonadIO m, Alternative m) =>
    String -> String -> IOException -> m b
    handler "correct the permissions for" String
    dir)

        let assertPermissions :: String -> m ()
    assertPermissions dir :: String
    dir = do
                let accessible :: Permissions -> Bool
    accessible path :: Permissions
    path =
                        Permissions -> Bool
    Directory.readable   Permissions
    path
                     Bool -> Bool -> Bool
    && Permissions -> Bool
    Directory.writable   Permissions
    path
                     Bool -> Bool -> Bool
    && Permissions -> Bool
    Directory.searchable Permissions
    path

                Permissions
    permissions <-
                    m Permissions -> (IOException -> m Permissions) -> m Permissions
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    catch (IO Permissions -> m Permissions
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO Permissions
    Directory.getPermissions String
    dir))
                          (String -> String -> IOException -> m Permissions
    forall (m :: * -> *) b.
    (MonadIO m, Alternative m) =>
    String -> String -> IOException -> m b
    handler "get permissions of" String
    dir)

                if Permissions -> Bool
    accessible Permissions
    permissions
                    then
                        () -> m ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
                    else do
                        let message :: String
    message =
                                 "The directory:\n"
                              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    dir String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "... does not give you permission to read, write, or search files.\n\n"
                              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "The directory's current permissions are:\n"
                              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> Permissions -> String
    forall a. Show a => a -> String
    show Permissions
    permissions String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"

                        String -> m ()
    forall (m :: * -> *) b. (MonadIO m, Alternative m) => String -> m b
    warn String
    message

        let existsDirectory :: String -> m Bool
    existsDirectory dir :: String
    dir =
                m Bool -> (IOException -> m Bool) -> m Bool
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    catch (IO Bool -> m Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO Bool
    Directory.doesDirectoryExist String
    dir))
                      (String -> String -> IOException -> m Bool
    forall (m :: * -> *) b.
    (MonadIO m, Alternative m) =>
    String -> String -> IOException -> m b
    handler "check the existence of" String
    dir)

        let existsFile :: String -> m Bool
    existsFile path :: String
    path =
                m Bool -> (IOException -> m Bool) -> m Bool
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    catch (IO Bool -> m Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO Bool
    Directory.doesFileExist String
    path))
                      (String -> String -> IOException -> m Bool
    forall (m :: * -> *) b.
    (MonadIO m, Alternative m) =>
    String -> String -> IOException -> m b
    handler "check the existence of" String
    path)

        let createDirectory :: String -> m ()
    createDirectory dir :: String
    dir =
                m () -> (IOException -> m ()) -> m ()
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    catch (IO () -> m ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO ()
    Directory.createDirectory String
    dir))
                      (String -> String -> IOException -> m ()
    forall (m :: * -> *) b.
    (MonadIO m, Alternative m) =>
    String -> String -> IOException -> m b
    handler "create" String
    dir)

        let assertDirectory :: String -> m ()
    assertDirectory dir :: String
    dir = do
                Bool
    existsDir <- String -> m Bool
    forall (m :: * -> *).
    (MonadCatch m, MonadIO m, Alternative m) =>
    String -> m Bool
    existsDirectory String
    dir

                if Bool
    existsDir
                    then do
                        String -> m ()
    forall (m :: * -> *).
    (MonadCatch m, MonadIO m, Alternative m) =>
    String -> m ()
    assertPermissions String
    dir

                    else do
                        Bool
    existsFile' <- String -> m Bool
    forall (m :: * -> *).
    (MonadCatch m, MonadIO m, Alternative m) =>
    String -> m Bool
    existsFile String
    dir

                        if Bool
    existsFile'
                            then do
                                let message :: String
    message =
                                         "The given path:\n"
                                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    dir String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                                      String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "... already exists but is not a directory.\n"

                                String -> m ()
    forall (m :: * -> *) b. (MonadIO m, Alternative m) => String -> m b
    warn String
    message

                            else do
                                String -> m ()
    assertDirectory (ShowS
    FilePath.takeDirectory String
    dir)

                                String -> m ()
    forall (m :: * -> *).
    (MonadCatch m, MonadIO m, Alternative m) =>
    String -> m ()
    createDirectory String
    dir

                                String -> m ()
    forall (m :: * -> *).
    (MonadCatch m, MonadIO m, Alternative m) =>
    String -> m ()
    setPermissions String
    dir

        String
    cacheBaseDirectory <- Bool -> m String
    forall (m :: * -> *).
    (Alternative m, MonadIO m) =>
    Bool -> m String
    getCacheBaseDirectory Bool
    showWarning

        let directory :: String
    directory = String
    cacheBaseDirectory String -> ShowS
    </> String
    cacheName

        let message :: String
    message =
                 "Could not get or create the default cache directory:\n"
              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "↳ " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    directory String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "You can enable caching by creating it if needed and setting read,\n"
              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "write and search permissions on it or providing another cache base\n"
              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "directory by setting the $XDG_CACHE_HOME environment variable.\n"
              String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"

        String -> m ()
    forall (m :: * -> *).
    (MonadCatch m, MonadIO m, Alternative m) =>
    String -> m ()
    assertDirectory String
    directory m () -> m () -> m ()
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> String -> m ()
    forall (m :: * -> *) b. (MonadIO m, Alternative m) => String -> m b
    warn String
    message

        String -> m String
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    directory

    getCacheBaseDirectory :: (Alternative m, MonadIO m) => Bool -> m FilePath
    getCacheBaseDirectory :: Bool -> m String
    getCacheBaseDirectory showWarning :: Bool
    showWarning = m String
    alternative₀ m String -> m String -> m String
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> m String
    alternative₁ m String -> m String -> m String
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> m String
    forall b. m b
    alternative₂
      where
        alternative₀ :: m String
    alternative₀ = do
            Maybe String
    maybeXDGCacheHome <- do
              IO (Maybe String) -> m (Maybe String)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO (Maybe String)
    System.Environment.lookupEnv "XDG_CACHE_HOME")

            case Maybe String
    maybeXDGCacheHome of
                Just xdgCacheHome :: String
    xdgCacheHome -> String -> m String
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    xdgCacheHome
                Nothing           -> m String
    forall (f :: * -> *) a. Alternative f => f a
    empty

        alternative₁ :: m String
    alternative₁
            | Bool
    isWindows = do
                Maybe String
    maybeLocalAppDirectory <-
                  IO (Maybe String) -> m (Maybe String)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO (Maybe String)
    System.Environment.lookupEnv "LOCALAPPDATA")

                case Maybe String
    maybeLocalAppDirectory of
                    Just localAppDirectory :: String
    localAppDirectory -> String -> m String
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    localAppDirectory
                    Nothing                -> m String
    forall (f :: * -> *) a. Alternative f => f a
    empty

            | Bool
    otherwise = do
                Maybe String
    maybeHomeDirectory <-
                  IO (Maybe String) -> m (Maybe String)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (String -> IO (Maybe String)
    System.Environment.lookupEnv "HOME")

                case Maybe String
    maybeHomeDirectory of
                    Just homeDirectory :: String
    homeDirectory -> String -> m String
    forall (m :: * -> *) a. Monad m => a -> m a
    return (String
    homeDirectory String -> ShowS
    </> ".cache")
                    Nothing            -> m String
    forall (f :: * -> *) a. Alternative f => f a
    empty

            where isWindows :: Bool
    isWindows = String
    System.Info.os String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    == "mingw32"

        alternative₂ :: m b
    alternative₂ = do
            let message :: String
    message =
                    "\n"
                 String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\ESC[1;33mWarning\ESC[0m: "
                 String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "Could not locate a cache base directory from the environment.\n"
                 String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "\n"
                 String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "You can provide a cache base directory by pointing the $XDG_CACHE_HOME\n"
                 String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> "environment variable to a directory with read and write permissions.\n"

            Bool -> m () -> m ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    when Bool
    showWarning (IO () -> m ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (Handle -> String -> IO ()
    System.IO.hPutStrLn Handle
    System.IO.stderr String
    message))

            m b
    forall (f :: * -> *) a. Alternative f => f a
    empty

    -- If the URL contains headers typecheck them and replace them with their normal
    -- forms.
    normalizeHeaders :: URL -> StateT Status IO URL
    normalizeHeaders :: URL -> StateT Status IO URL
    normalizeHeaders url :: URL
    url@URL { headers :: URL -> Maybe (Expr Src Import)
    headers = Just headersExpression :: Expr Src Import
    headersExpression } = do
        Status { NonEmpty Chained
    _stack :: NonEmpty Chained
    _stack :: Status -> NonEmpty Chained
    _stack } <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get
        Expr Src Void
    loadedExpr <- Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    headersExpression

        let go :: Text -> Text -> m (Expr t Void)
    go key₀ :: Text
    key₀ key₁ :: Text
    key₁ = do
                let expected :: Expr Src Void
                    expected :: Expr Src Void
    expected =
                        Expr Src Void -> Expr Src Void -> Expr Src Void
    forall s a. Expr s a -> Expr s a -> Expr s a
    App Expr Src Void
    forall s a. Expr s a
    List
                            ( Substitutions Src Void -> Expr Src Void
    forall s a. Map Text (Expr s a) -> Expr s a
    Record
                                ( [(Text, Expr Src Void)] -> Substitutions Src Void
    forall k v. Ord k => [(k, v)] -> Map k v
    Dhall.Map.fromList
                                    [ (Text
    key₀, Expr Src Void
    forall s a. Expr s a
    Text), (Text
    key₁, Expr Src Void
    forall s a. Expr s a
    Text) ]
                                )
                            )

                let suffix_ :: Text
    suffix_ = Expr Src Void -> Text
    forall a. Pretty a => a -> Text
    Dhall.Pretty.Internal.prettyToStrictText Expr Src Void
    expected
                let annot :: Expr Src Void
    annot = case Expr Src Void
    loadedExpr of
                        Note (Src begin :: SourcePos
    begin end :: SourcePos
    end bytes :: Text
    bytes) _ ->
                            Src -> Expr Src Void -> Expr Src Void
    forall s a. s -> Expr s a -> Expr s a
    Note (SourcePos -> SourcePos -> Text -> Src
    Src SourcePos
    begin SourcePos
    end Text
    bytes') (Expr Src Void -> Expr Src Void -> Expr Src Void
    forall s a. Expr s a -> Expr s a -> Expr s a
    Annot Expr Src Void
    loadedExpr Expr Src Void
    expected)
                          where
                            bytes' :: Text
    bytes' = Text
    bytes Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> " : " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    suffix_
                        _ ->
                            Expr Src Void -> Expr Src Void -> Expr Src Void
    forall s a. Expr s a -> Expr s a -> Expr s a
    Annot Expr Src Void
    loadedExpr Expr Src Void
    expected

                ()
    _ <- case (Expr Src Void -> Either (TypeError Src Void) (Expr Src Void)
    forall s. Expr s Void -> Either (TypeError s Void) (Expr s Void)
    Dhall.TypeCheck.typeOf Expr Src Void
    annot) of
                    Left err :: TypeError Src Void
    err -> Imported (TypeError Src Void) -> m ()
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained
    -> TypeError Src Void -> Imported (TypeError Src Void)
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack TypeError Src Void
    err)
                    Right _ -> () -> m ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

                Expr t Void -> m (Expr t Void)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Expr Src Void -> Expr t Void
    forall a s t. Eq a => Expr s a -> Expr t a
    Dhall.Core.normalize Expr Src Void
    loadedExpr)

        let handler₀ :: SomeException -> m (Expr t Void)
    handler₀ (SomeException
    e :: SomeException) = do
                {- Try to typecheck using the preferred @mapKey@/@mapValue@ fields
                   and fall back to @header@/@value@ if that fails. However, if
                   @header@/@value@ still fails then re-throw the original exception
                   for @mapKey@ / @mapValue@. -}
                let handler₁ :: SomeException -> m a
    handler₁ (SomeException
    _ :: SomeException) =
                        Imported SomeException -> m a
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> SomeException -> Imported SomeException
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack SomeException
    e)

                (SomeException -> m (Expr t Void))
    -> m (Expr t Void) -> m (Expr t Void)
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    (e -> m a) -> m a -> m a
    handle SomeException -> m (Expr t Void)
    forall (m :: * -> *) a. MonadCatch m => SomeException -> m a
    handler₁ (Text -> Text -> m (Expr t Void)
    forall (m :: * -> *) t.
    MonadCatch m =>
    Text -> Text -> m (Expr t Void)
    go "header" "value")

        Expr Src Void
    headersExpression' <-
            (SomeException -> StateT Status IO (Expr Src Void))
    -> StateT Status IO (Expr Src Void)
    -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    (e -> m a) -> m a -> m a
    handle SomeException -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) t.
    MonadCatch m =>
    SomeException -> m (Expr t Void)
    handler₀ (Text -> Text -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) t.
    MonadCatch m =>
    Text -> Text -> m (Expr t Void)
    go "mapKey" "mapValue")

        URL -> StateT Status IO URL
    forall (m :: * -> *) a. Monad m => a -> m a
    return URL
    url { headers :: Maybe (Expr Src Import)
    headers = Expr Src Import -> Maybe (Expr Src Import)
    forall a. a -> Maybe a
    Just ((Void -> Import) -> Expr Src Void -> Expr Src Import
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Void -> Import
    forall a. Void -> a
    absurd Expr Src Void
    headersExpression') }

    normalizeHeaders url :: URL
    url = URL -> StateT Status IO URL
    forall (m :: * -> *) a. Monad m => a -> m a
    return URL
    url

    -- | Default starting `Status`, importing relative to the given directory.
    emptyStatus :: FilePath -> Status
    emptyStatus :: String -> Status
    emptyStatus = (URL -> StateT Status IO Text) -> String -> Status
    emptyStatusWith URL -> StateT Status IO Text
    fetchRemote

    {-| Generalized version of `load`

        You can configure the desired behavior through the initial `Status` that you
        supply
    -}
    loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith expr₀ :: Expr Src Import
    expr₀ = case Expr Src Import
    expr₀ of
      Embed import₀ :: Import
    import₀ -> do
        Status {..} <- StateT Status IO Status
    forall (m :: * -> *) s. Monad m => StateT s m s
    State.get

        let parent :: Chained
    parent = NonEmpty Chained -> Chained
    forall a. NonEmpty a -> a
    NonEmpty.head NonEmpty Chained
    _stack

        Chained
    child <- Chained -> Import -> StateT Status IO Chained
    chainImport Chained
    parent Import
    import₀

        let local :: Chained -> Bool
    local (Chained (Import (ImportHashed _ (Remote  {})) _)) = Bool
    False
            local (Chained (Import (ImportHashed _ (Local   {})) _)) = Bool
    True
            local (Chained (Import (ImportHashed _ (Env     {})) _)) = Bool
    True
            local (Chained (Import (ImportHashed _ (Missing {})) _)) = Bool
    False

        let referentiallySane :: Bool
    referentiallySane = Bool -> Bool
    not (Chained -> Bool
    local Chained
    child) Bool -> Bool -> Bool
    || Chained -> Bool
    local Chained
    parent

        if Import -> ImportMode
    importMode Import
    import₀ ImportMode -> ImportMode -> Bool
    forall a. Eq a => a -> a -> Bool
    == ImportMode
    Location Bool -> Bool -> Bool
    || Bool
    referentiallySane
            then () -> StateT Status IO ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
            else Imported ReferentiallyOpaque -> StateT Status IO ()
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained
    -> ReferentiallyOpaque -> Imported ReferentiallyOpaque
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack (Import -> ReferentiallyOpaque
    ReferentiallyOpaque Import
    import₀))

        let _stack' :: NonEmpty Chained
    _stack' = Chained -> NonEmpty Chained -> NonEmpty Chained
    forall a. a -> NonEmpty a -> NonEmpty a
    NonEmpty.cons Chained
    child NonEmpty Chained
    _stack

        if Chained
    child Chained -> NonEmpty Chained -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` NonEmpty Chained
    _stack
            then Imported Cycle -> StateT Status IO ()
    forall (m :: * -> *) e a. (MonadCatch m, Exception e) => e -> m a
    throwMissingImport (NonEmpty Chained -> Cycle -> Imported Cycle
    forall e. NonEmpty Chained -> e -> Imported e
    Imported NonEmpty Chained
    _stack (Import -> Cycle
    Cycle Import
    import₀))
            else () -> StateT Status IO ()
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

        LensLike' (Zooming IO ()) Status [Depends]
    -> StateT [Depends] IO () -> StateT Status IO ()
    forall (m :: * -> *) c s a.
    Monad m =>
    LensLike' (Zooming m c) s a -> StateT a m c -> StateT s m c
    zoom LensLike' (Zooming IO ()) Status [Depends]
    forall (f :: * -> *). Functor f => LensLike' f Status [Depends]
    graph (StateT [Depends] IO () -> StateT Status IO ())
    -> (([Depends] -> [Depends]) -> StateT [Depends] IO ())
    -> ([Depends] -> [Depends])
    -> StateT Status IO ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ([Depends] -> [Depends]) -> StateT [Depends] IO ()
    forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
    State.modify (([Depends] -> [Depends]) -> StateT Status IO ())
    -> ([Depends] -> [Depends]) -> StateT Status IO ()
    forall a b. (a -> b) -> a -> b
    $
            -- Add the edge `parent -> child` to the import graph
            \edges :: [Depends]
    edges -> Chained -> Chained -> Depends
    Depends Chained
    parent Chained
    child Depends -> [Depends] -> [Depends]
    forall a. a -> [a] -> [a]
    : [Depends]
    edges

        let stackWithChild :: NonEmpty Chained
    stackWithChild = Chained -> NonEmpty Chained -> NonEmpty Chained
    forall a. a -> NonEmpty a -> NonEmpty a
    NonEmpty.cons Chained
    child NonEmpty Chained
    _stack

        LensLike' (Zooming IO ()) Status (NonEmpty Chained)
    -> StateT (NonEmpty Chained) IO () -> StateT Status IO ()
    forall (m :: * -> *) c s a.
    Monad m =>
    LensLike' (Zooming m c) s a -> StateT a m c -> StateT s m c
    zoom LensLike' (Zooming IO ()) Status (NonEmpty Chained)
    forall (f :: * -> *).
    Functor f =>
    LensLike' f Status (NonEmpty Chained)
    stack (NonEmpty Chained -> StateT (NonEmpty Chained) IO ()
    forall (m :: * -> *) s. Monad m => s -> StateT s m ()
    State.put NonEmpty Chained
    stackWithChild)
        ImportSemantics {..} <- Chained -> StateT Status IO ImportSemantics
    loadImport Chained
    child
        LensLike' (Zooming IO ()) Status (NonEmpty Chained)
    -> StateT (NonEmpty Chained) IO () -> StateT Status IO ()
    forall (m :: * -> *) c s a.
    Monad m =>
    LensLike' (Zooming m c) s a -> StateT a m c -> StateT s m c
    zoom LensLike' (Zooming IO ()) Status (NonEmpty Chained)
    forall (f :: * -> *).
    Functor f =>
    LensLike' f Status (NonEmpty Chained)
    stack (NonEmpty Chained -> StateT (NonEmpty Chained) IO ()
    forall (m :: * -> *) s. Monad m => s -> StateT s m ()
    State.put NonEmpty Chained
    _stack)

        Expr Src Void -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Expr Void Void -> Expr Src Void
    forall a s. Expr Void a -> Expr s a
    Dhall.Core.renote Expr Void Void
    importSemantics)

      ImportAlt a :: Expr Src Import
    a b :: Expr Src Import
    b -> Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    a StateT Status IO (Expr Src Void)
    -> (SourcedException MissingImports
        -> StateT Status IO (Expr Src Void))
    -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    `catch` SourcedException MissingImports -> StateT Status IO (Expr Src Void)
    handler₀
        where
          handler₀ :: SourcedException MissingImports -> StateT Status IO (Expr Src Void)
    handler₀ (SourcedException (Src begin :: SourcePos
    begin _ text₀ :: Text
    text₀) (MissingImports es₀ :: [SomeException]
    es₀)) =
              Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    b StateT Status IO (Expr Src Void)
    -> (SourcedException MissingImports
        -> StateT Status IO (Expr Src Void))
    -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    `catch` SourcedException MissingImports -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) a.
    MonadThrow m =>
    SourcedException MissingImports -> m a
    handler₁
            where
              handler₁ :: SourcedException MissingImports -> m a
    handler₁ (SourcedException (Src _ end :: SourcePos
    end text₁ :: Text
    text₁) (MissingImports es₁ :: [SomeException]
    es₁)) =
                  SourcedException MissingImports -> m a
    forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
    throwM (Src -> MissingImports -> SourcedException MissingImports
    forall e. Src -> e -> SourcedException e
    SourcedException (SourcePos -> SourcePos -> Text -> Src
    Src SourcePos
    begin SourcePos
    end Text
    text₂) ([SomeException] -> MissingImports
    MissingImports ([SomeException]
    es₀ [SomeException] -> [SomeException] -> [SomeException]
    forall a. [a] -> [a] -> [a]
    ++ [SomeException]
    es₁)))
                where
                  text₂ :: Text
    text₂ = Text
    text₀ Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> " ? " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    text₁

      Note a :: Src
    a b :: Expr Src Import
    b             -> do
          let handler :: MissingImports -> m a
    handler e :: MissingImports
    e = SourcedException MissingImports -> m a
    forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
    throwM (Src -> MissingImports -> SourcedException MissingImports
    forall e. Src -> e -> SourcedException e
    SourcedException Src
    a (MissingImports
    e :: MissingImports))

          (Src -> Expr Src Void -> Expr Src Void
    forall s a. s -> Expr s a -> Expr s a
    Note (Src -> Expr Src Void -> Expr Src Void)
    -> StateT Status IO Src
    -> StateT Status IO (Expr Src Void -> Expr Src Void)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Src -> StateT Status IO Src
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure Src
    a StateT Status IO (Expr Src Void -> Expr Src Void)
    -> StateT Status IO (Expr Src Void)
    -> StateT Status IO (Expr Src Void)
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    b) StateT Status IO (Expr Src Void)
    -> (MissingImports -> StateT Status IO (Expr Src Void))
    -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) e a.
    (MonadCatch m, Exception e) =>
    m a -> (e -> m a) -> m a
    `catch` MissingImports -> StateT Status IO (Expr Src Void)
    forall (m :: * -> *) a. MonadThrow m => MissingImports -> m a
    handler
      Let a :: Binding Src Import
    a b :: Expr Src Import
    b              -> Binding Src Void -> Expr Src Void -> Expr Src Void
    forall s a. Binding s a -> Expr s a -> Expr s a
    Let (Binding Src Void -> Expr Src Void -> Expr Src Void)
    -> StateT Status IO (Binding Src Void)
    -> StateT Status IO (Expr Src Void -> Expr Src Void)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (Expr Src Import -> StateT Status IO (Expr Src Void))
    -> Binding Src Import -> StateT Status IO (Binding Src Void)
    forall (f :: * -> *) s a b.
    Applicative f =>
    (Expr s a -> f (Expr s b)) -> Binding s a -> f (Binding s b)
    bindingExprs Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Binding Src Import
    a StateT Status IO (Expr Src Void -> Expr Src Void)
    -> StateT Status IO (Expr Src Void)
    -> StateT Status IO (Expr Src Void)
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    b
      expression :: Expr Src Import
    expression           -> (Expr Src Import -> StateT Status IO (Expr Src Void))
    -> Expr Src Import -> StateT Status IO (Expr Src Void)
    forall (f :: * -> *) s a t b.
    Applicative f =>
    (Expr s a -> f (Expr t b)) -> Expr s a -> f (Expr t b)
    Syntax.unsafeSubExpressions Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    expression

    -- | Resolve all imports within an expression
    load :: Expr Src Import -> IO (Expr Src Void)
    load :: Expr Src Import -> IO (Expr Src Void)
    load = String
    -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src Void)
    loadRelativeTo "." SemanticCacheMode
    UseSemanticCache

    -- | Resolve all imports within an expression, importing relative to the given
    -- directory.
    loadRelativeTo :: FilePath -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src Void)
    loadRelativeTo :: String
    -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src Void)
    loadRelativeTo rootDirectory :: String
    rootDirectory semanticCacheMode :: SemanticCacheMode
    semanticCacheMode expression :: Expr Src Import
    expression =
        StateT Status IO (Expr Src Void) -> Status -> IO (Expr Src Void)
    forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
    State.evalStateT
            (Expr Src Import -> StateT Status IO (Expr Src Void)
    loadWith Expr Src Import
    expression)
            (String -> Status
    emptyStatus String
    rootDirectory) { _semanticCacheMode :: SemanticCacheMode
    _semanticCacheMode = SemanticCacheMode
    semanticCacheMode }

    encodeExpression
        :: StandardVersion
        -- ^ `NoVersion` means to encode without the version tag
        -> Expr Void Void
        -> Data.ByteString.ByteString
    encodeExpression :: StandardVersion -> Expr Void Void -> ByteString
    encodeExpression _standardVersion :: StandardVersion
    _standardVersion expression :: Expr Void Void
    expression = ByteString
    bytesStrict
      where
        intermediateExpression :: Expr Void Import
        intermediateExpression :: Expr Void Import
    intermediateExpression = (Void -> Import) -> Expr Void Void -> Expr Void Import
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Void -> Import
    forall a. Void -> a
    absurd Expr Void Void
    expression

        encoding :: Encoding
    encoding =
            case StandardVersion
    _standardVersion of
                NoVersion ->
                    Expr Void Import -> Encoding
    forall a. Serialise a => a -> Encoding
    Codec.Serialise.encode Expr Void Import
    intermediateExpression
                s :: StandardVersion
    s ->
                        Word -> Encoding
    Encoding.encodeListLen 2
                    Encoding -> Encoding -> Encoding
    forall a. Semigroup a => a -> a -> a
    <>  Text -> Encoding
    Encoding.encodeString Text
    v
                    Encoding -> Encoding -> Encoding
    forall a. Semigroup a => a -> a -> a
    <>  Expr Void Import -> Encoding
    forall a. Serialise a => a -> Encoding
    Codec.Serialise.encode Expr Void Import
    intermediateExpression
                  where
                    v :: Text
    v = StandardVersion -> Text
    Dhall.Binary.renderStandardVersion StandardVersion
    s

        bytesStrict :: ByteString
    bytesStrict = Encoding -> ByteString
    Write.toStrictByteString Encoding
    encoding

    -- | Hash a fully resolved expression
    hashExpression :: Expr Void Void -> Dhall.Crypto.SHA256Digest
    hashExpression :: Expr Void Void -> SHA256Digest
    hashExpression expression :: Expr Void Void
    expression =
        ByteString -> SHA256Digest
    Dhall.Crypto.sha256Hash (StandardVersion -> Expr Void Void -> ByteString
    encodeExpression StandardVersion
    NoVersion Expr Void Void
    expression)

    {-| Convenience utility to hash a fully resolved expression and return the
        base-16 encoded hash with the @sha256:@ prefix

        In other words, the output of this function can be pasted into Dhall
        source code to add an integrity check to an import
    -}
    hashExpressionToCode :: Expr Void Void -> Text
    hashExpressionToCode :: Expr Void Void -> Text
    hashExpressionToCode expr :: Expr Void Void
    expr =
        "sha256:" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> String -> Text
    Text.pack (SHA256Digest -> String
    forall a. Show a => a -> String
    show (Expr Void Void -> SHA256Digest
    hashExpression Expr Void Void
    expr))

    -- | A call to `assertNoImports` failed because there was at least one import
    data ImportResolutionDisabled = ImportResolutionDisabled deriving (Show ImportResolutionDisabled
    Typeable ImportResolutionDisabled
    (Typeable ImportResolutionDisabled,
     Show ImportResolutionDisabled) =>
    (ImportResolutionDisabled -> SomeException)
    -> (SomeException -> Maybe ImportResolutionDisabled)
    -> (ImportResolutionDisabled -> String)
    -> Exception ImportResolutionDisabled
    SomeException -> Maybe ImportResolutionDisabled
    ImportResolutionDisabled -> String
    ImportResolutionDisabled -> SomeException
    forall e.
    (Typeable e, Show e) =>
    (e -> SomeException)
    -> (SomeException -> Maybe e) -> (e -> String) -> Exception e
    displayException :: ImportResolutionDisabled -> String
    $cdisplayException :: ImportResolutionDisabled -> String
    fromException :: SomeException -> Maybe ImportResolutionDisabled
    $cfromException :: SomeException -> Maybe ImportResolutionDisabled
    toException :: ImportResolutionDisabled -> SomeException
    $ctoException :: ImportResolutionDisabled -> SomeException
    $cp2Exception :: Show ImportResolutionDisabled
    $cp1Exception :: Typeable ImportResolutionDisabled
    Exception)

    instance Show ImportResolutionDisabled where
        show :: ImportResolutionDisabled -> String
    show _ = "\nImport resolution is disabled"

    -- | Assert than an expression is import-free
    assertNoImports :: MonadIO io => Expr Src Import -> io (Expr Src Void)
    assertNoImports :: Expr Src Import -> io (Expr Src Void)
    assertNoImports expression :: Expr Src Import
    expression =
        Either ImportResolutionDisabled (Expr Src Void)
    -> io (Expr Src Void)
    forall e (io :: * -> *) a.
    (Exception e, MonadIO io) =>
    Either e a -> io a
    Dhall.Core.throws ((Import -> Either ImportResolutionDisabled Void)
    -> Expr Src Import
    -> Either ImportResolutionDisabled (Expr Src Void)
    forall (t :: * -> *) (f :: * -> *) a b.
    (Traversable t, Applicative f) =>
    (a -> f b) -> t a -> f (t b)
    traverse (\_ -> ImportResolutionDisabled -> Either ImportResolutionDisabled Void
    forall a b. a -> Either a b
    Left ImportResolutionDisabled
    ImportResolutionDisabled) Expr Src Import
    expression)