{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE BlockArguments #-}

module Hercules.CLI.State (commandParser, getProjectAndClient) where

import Conduit (ConduitT, mapC, runConduitRes, sinkFile, sourceHandle, stdinC, stdoutC, (.|))
import Data.Has (Has)
import Hercules.API (ClientAuth, enterApiE)
import Hercules.API.Name (Name (Name))
import Hercules.API.State
import Hercules.CLI.Client
import Hercules.CLI.Common (runAuthenticated)
import Hercules.CLI.Options (mkCommand, subparser)
import Hercules.CLI.Project (ProjectPath (projectPathOwner, projectPathProject, projectPathSite), findProjectContextually, projectOption)
import Options.Applicative (auto, bashCompleter, completer, help, long, metavar, option, strOption)
import qualified Options.Applicative as Optparse
import Protolude hiding (option)
import RIO (RIO, runRIO, withBinaryFile)
import Servant.API (Headers (Headers), fromSourceIO, toSourceIO)
import Servant.Client.Generic (AsClientT)
import Servant.Client.Internal.HttpClient.Streaming (ClientM)
import Servant.Conduit ()

commandParser, getCommandParser, putCommandParser :: Optparse.Parser (IO ())
commandParser :: Parser (IO ())
commandParser =
  Mod CommandFields (IO ()) -> Parser (IO ())
forall a. Mod CommandFields a -> Parser a
subparser
    ( FilePath
-> InfoMod (IO ()) -> Parser (IO ()) -> Mod CommandFields (IO ())
forall a. FilePath -> InfoMod a -> Parser a -> Mod CommandFields a
mkCommand
        FilePath
"get"
        (FilePath -> InfoMod (IO ())
forall a. FilePath -> InfoMod a
Optparse.progDesc FilePath
"Download a state file")
        Parser (IO ())
getCommandParser
        Mod CommandFields (IO ())
-> Mod CommandFields (IO ()) -> Mod CommandFields (IO ())
forall a. Semigroup a => a -> a -> a
<> FilePath
-> InfoMod (IO ()) -> Parser (IO ()) -> Mod CommandFields (IO ())
forall a. FilePath -> InfoMod a -> Parser a -> Mod CommandFields a
mkCommand
          FilePath
"put"
          (FilePath -> InfoMod (IO ())
forall a. FilePath -> InfoMod a
Optparse.progDesc FilePath
"Upload a state file")
          Parser (IO ())
putCommandParser
    )
getCommandParser :: Parser (IO ())
getCommandParser = do
  Maybe ProjectPath
projectMaybe <- Parser ProjectPath -> Parser (Maybe ProjectPath)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional Parser ProjectPath
projectOption
  Text
name <- Parser Text
nameOption
  FilePath
file <- Parser FilePath
fileOption
  Maybe Int
versionMaybe <- Parser Int -> Parser (Maybe Int)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional Parser Int
versionOption
  pure do
    RIO (HerculesClientToken, HerculesClientEnv) () -> IO ()
forall b. RIO (HerculesClientToken, HerculesClientEnv) b -> IO b
runAuthenticated do
      ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
projectStateClient <- Maybe ProjectPath
-> RIO
     (HerculesClientToken, HerculesClientEnv)
     (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
forall r.
(Has HerculesClientToken r, Has HerculesClientEnv r) =>
Maybe ProjectPath
-> RIO r (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
getProjectAndClient Maybe ProjectPath
projectMaybe
      -- TODO: version
      (Token
 -> ClientM
      (Headers '[ContentLength, ContentDisposition] (SourceIO RawBytes)))
-> (Either
      ClientError
      (Headers '[ContentLength, ContentDisposition] (SourceIO RawBytes))
    -> IO ())
-> RIO (HerculesClientToken, HerculesClientEnv) ()
forall r a b.
(Has HerculesClientToken r, Has HerculesClientEnv r) =>
(Token -> ClientM a) -> (Either ClientError a -> IO b) -> RIO r b
runHerculesClientStream (ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
-> AsClientT ClientM
   :- (Summary "Download a state file"
       :> ("state"
           :> (Capture' '[Required, Strict] "stateName" Text
               :> ("data"
                   :> (QueryParam' '[Optional, Strict] "version" Int
                       :> (ClientAuth
                           :> StreamGet
                                NoFraming
                                OctetStream
                                (Headers
                                   '[ContentLength, ContentDisposition] (SourceIO RawBytes))))))))
forall auth f.
ProjectStateResourceGroup auth f
-> f
   :- (Summary "Download a state file"
       :> ("state"
           :> (Capture' '[Required, Strict] "stateName" Text
               :> ("data"
                   :> (QueryParam' '[Optional, Strict] "version" Int
                       :> (auth
                           :> StreamGet
                                NoFraming
                                OctetStream
                                (Headers
                                   '[ContentLength, ContentDisposition] (SourceIO RawBytes))))))))
getStateData ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
projectStateClient Text
name Maybe Int
versionMaybe) \case
        Left ClientError
e -> ClientError -> IO ()
forall a. ClientError -> IO a
dieWithHttpError ClientError
e
        Right (Headers SourceIO RawBytes
r HList '[ContentLength, ContentDisposition]
_) -> do
          ConduitT () Void (ResourceT IO) () -> IO ()
forall (m :: * -> *) r.
MonadUnliftIO m =>
ConduitT () Void (ResourceT m) r -> m r
runConduitRes (ConduitT () Void (ResourceT IO) () -> IO ())
-> ConduitT () Void (ResourceT IO) () -> IO ()
forall a b. (a -> b) -> a -> b
$
            SourceIO RawBytes -> ConduitT () RawBytes (ResourceT IO) ()
forall chunk a. FromSourceIO chunk a => SourceIO chunk -> a
fromSourceIO SourceIO RawBytes
r ConduitT () RawBytes (ResourceT IO) ()
-> ConduitT RawBytes Void (ResourceT IO) ()
-> ConduitT () Void (ResourceT IO) ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| (RawBytes -> ByteString)
-> ConduitT RawBytes ByteString (ResourceT IO) ()
forall (m :: * -> *) a b. Monad m => (a -> b) -> ConduitT a b m ()
mapC RawBytes -> ByteString
fromRawBytes ConduitT RawBytes ByteString (ResourceT IO) ()
-> ConduitT ByteString Void (ResourceT IO) ()
-> ConduitT RawBytes Void (ResourceT IO) ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| case FilePath
file of
              FilePath
"-" -> ConduitT ByteString Void (ResourceT IO) ()
forall (m :: * -> *) o. MonadIO m => ConduitT ByteString o m ()
stdoutC
              FilePath
_ -> FilePath -> ConduitT ByteString Void (ResourceT IO) ()
forall (m :: * -> *) o.
MonadResource m =>
FilePath -> ConduitT ByteString o m ()
sinkFile FilePath
file
putCommandParser :: Parser (IO ())
putCommandParser = do
  Maybe ProjectPath
projectMaybe <- Parser ProjectPath -> Parser (Maybe ProjectPath)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional Parser ProjectPath
projectOption
  Text
name <- Parser Text
nameOption
  FilePath
file <- Parser FilePath
fileOption
  pure do
    RIO (HerculesClientToken, HerculesClientEnv) () -> IO ()
forall b. RIO (HerculesClientToken, HerculesClientEnv) b -> IO b
runAuthenticated do
      ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
projectStateClient <- Maybe ProjectPath
-> RIO
     (HerculesClientToken, HerculesClientEnv)
     (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
forall r.
(Has HerculesClientToken r, Has HerculesClientEnv r) =>
Maybe ProjectPath
-> RIO r (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
getProjectAndClient Maybe ProjectPath
projectMaybe
      let withStream :: (ConduitT a RawBytes IO () -> RIO r b) -> RIO r b
          withStream :: forall a r b. (ConduitT a RawBytes IO () -> RIO r b) -> RIO r b
withStream = case FilePath
file of
            FilePath
"-" -> ((ConduitT a RawBytes IO () -> RIO r b)
-> ConduitT a RawBytes IO () -> RIO r b
forall a b. (a -> b) -> a -> b
$ (ConduitT a ByteString IO ()
forall (m :: * -> *) i. MonadIO m => ConduitT i ByteString m ()
stdinC ConduitT a ByteString IO ()
-> ConduitT ByteString RawBytes IO () -> ConduitT a RawBytes IO ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| (ByteString -> RawBytes) -> ConduitT ByteString RawBytes IO ()
forall (m :: * -> *) a b. Monad m => (a -> b) -> ConduitT a b m ()
mapC ByteString -> RawBytes
RawBytes))
            FilePath
_ -> \ConduitT a RawBytes IO () -> RIO r b
f -> do
              r
r <- RIO r r
forall r (m :: * -> *). MonadReader r m => m r
ask
              IO b -> RIO r b
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO b -> RIO r b) -> IO b -> RIO r b
forall a b. (a -> b) -> a -> b
$ FilePath -> IOMode -> (Handle -> IO b) -> IO b
forall (m :: * -> *) a.
MonadUnliftIO m =>
FilePath -> IOMode -> (Handle -> m a) -> m a
withBinaryFile FilePath
file IOMode
ReadMode \Handle
h ->
                r -> RIO r b -> IO b
forall (m :: * -> *) env a. MonadIO m => env -> RIO env a -> m a
runRIO r
r (RIO r b -> IO b) -> RIO r b -> IO b
forall a b. (a -> b) -> a -> b
$ ConduitT a RawBytes IO () -> RIO r b
f (Handle -> ConduitT a ByteString IO ()
forall (m :: * -> *) i.
MonadIO m =>
Handle -> ConduitT i ByteString m ()
sourceHandle Handle
h ConduitT a ByteString IO ()
-> ConduitT ByteString RawBytes IO () -> ConduitT a RawBytes IO ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| (ByteString -> RawBytes) -> ConduitT ByteString RawBytes IO ()
forall (m :: * -> *) a b. Monad m => (a -> b) -> ConduitT a b m ()
mapC ByteString -> RawBytes
RawBytes)
      (ConduitT Any RawBytes IO ()
 -> RIO (HerculesClientToken, HerculesClientEnv) ())
-> RIO (HerculesClientToken, HerculesClientEnv) ()
forall a r b. (ConduitT a RawBytes IO () -> RIO r b) -> RIO r b
withStream \ConduitT Any RawBytes IO ()
stream -> do
        NoContent
_noContent <- (Token -> ClientM NoContent)
-> RIO (HerculesClientToken, HerculesClientEnv) NoContent
forall a r.
(NFData a, Has HerculesClientToken r, Has HerculesClientEnv r) =>
(Token -> ClientM a) -> RIO r a
runHerculesClient (ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
-> AsClientT ClientM
   :- (Summary "Upload a state file"
       :> ("state"
           :> (Capture' '[Required, Strict] "stateName" Text
               :> ("data"
                   :> (StreamBody NoFraming OctetStream (SourceIO RawBytes)
                       :> (ClientAuth :> Put '[JSON] NoContent))))))
forall auth f.
ProjectStateResourceGroup auth f
-> f
   :- (Summary "Upload a state file"
       :> ("state"
           :> (Capture' '[Required, Strict] "stateName" Text
               :> ("data"
                   :> (StreamBody NoFraming OctetStream (SourceIO RawBytes)
                       :> (auth :> Put '[JSON] NoContent))))))
putStateData ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
projectStateClient Text
name (ConduitT Any RawBytes IO () -> SourceIO RawBytes
forall chunk a. ToSourceIO chunk a => a -> SourceIO chunk
toSourceIO ConduitT Any RawBytes IO ()
stream))
        RIO (HerculesClientToken, HerculesClientEnv) ()
forall (f :: * -> *). Applicative f => f ()
pass
    Text -> IO ()
forall (m :: * -> *). MonadIO m => Text -> m ()
putErrText (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"hci: State file upload successful for " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
name

nameOption :: Optparse.Parser Text
nameOption :: Parser Text
nameOption = Mod OptionFields Text -> Parser Text
forall s. IsString s => Mod OptionFields s -> Parser s
strOption (Mod OptionFields Text -> Parser Text)
-> Mod OptionFields Text -> Parser Text
forall a b. (a -> b) -> a -> b
$ FilePath -> Mod OptionFields Text
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"name" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields Text
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"NAME" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields Text
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Name of the state file"

fileOption :: Optparse.Parser FilePath
fileOption :: Parser FilePath
fileOption = Mod OptionFields FilePath -> Parser FilePath
forall s. IsString s => Mod OptionFields s -> Parser s
strOption (Mod OptionFields FilePath -> Parser FilePath)
-> Mod OptionFields FilePath -> Parser FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> Mod OptionFields FilePath
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"file" Mod OptionFields FilePath
-> Mod OptionFields FilePath -> Mod OptionFields FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields FilePath
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"FILE" Mod OptionFields FilePath
-> Mod OptionFields FilePath -> Mod OptionFields FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields FilePath
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Local path of the state file or - for stdio" Mod OptionFields FilePath
-> Mod OptionFields FilePath -> Mod OptionFields FilePath
forall a. Semigroup a => a -> a -> a
<> Completer -> Mod OptionFields FilePath
forall (f :: * -> *) a. HasCompleter f => Completer -> Mod f a
completer (FilePath -> Completer
bashCompleter FilePath
"file")

versionOption :: Optparse.Parser Int
versionOption :: Parser Int
versionOption = ReadM Int -> Mod OptionFields Int -> Parser Int
forall a. ReadM a -> Mod OptionFields a -> Parser a
option ReadM Int
forall a. Read a => ReadM a
auto (Mod OptionFields Int -> Parser Int)
-> Mod OptionFields Int -> Parser Int
forall a b. (a -> b) -> a -> b
$ FilePath -> Mod OptionFields Int
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"version" Mod OptionFields Int
-> Mod OptionFields Int -> Mod OptionFields Int
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields Int
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"INT" Mod OptionFields Int
-> Mod OptionFields Int -> Mod OptionFields Int
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields Int
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Version of the state file to retrieve"

getProjectAndClient :: (Has HerculesClientToken r, Has HerculesClientEnv r) => Maybe ProjectPath -> RIO r (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
getProjectAndClient :: forall r.
(Has HerculesClientToken r, Has HerculesClientEnv r) =>
Maybe ProjectPath
-> RIO r (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
getProjectAndClient Maybe ProjectPath
projectMaybe =
  case Maybe ProjectPath
projectMaybe of
    Just ProjectPath
projectPath ->
      ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
-> RIO r (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StateAPI ClientAuth (AsClientT ClientM)
stateClient StateAPI ClientAuth (AsClientT ClientM)
-> (StateAPI ClientAuth (AsClientT ClientM)
    -> ToServant
         (ProjectStateResourceGroup ClientAuth) (AsClientT ClientM))
-> ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
forall {k} (subapi :: k -> * -> *) (api :: k -> * -> *) mode
       (a :: k).
(GenericServant (api a) mode, GenericServant (subapi a) mode) =>
api a mode
-> (api a mode -> ToServant (subapi a) mode) -> subapi a mode
`enterApiE` \StateAPI ClientAuth (AsClientT ClientM)
api -> StateAPI ClientAuth (AsClientT ClientM)
-> AsClientT ClientM
   :- Substitute
        ("site"
         :> (Capture' '[Required, Strict] "site" (Name SourceHostingSite)
             :> ("account"
                 :> (Capture' '[Required, Strict] "account" (Name Account)
                     :> ("project"
                         :> (Capture' '[Required, Strict] "project" (Name Project)
                             :> Placeholder))))))
        (ToServantApi (ProjectStateResourceGroup ClientAuth))
forall auth f.
StateAPI auth f
-> f
   :- Substitute
        ("site"
         :> (Capture' '[Required, Strict] "site" (Name SourceHostingSite)
             :> ("account"
                 :> (Capture' '[Required, Strict] "account" (Name Account)
                     :> ("project"
                         :> (Capture' '[Required, Strict] "project" (Name Project)
                             :> Placeholder))))))
        (ToServantApi (ProjectStateResourceGroup auth))
byProjectName StateAPI ClientAuth (AsClientT ClientM)
api (Text -> Name SourceHostingSite
forall k (a :: k). Text -> Name a
Name (Text -> Name SourceHostingSite) -> Text -> Name SourceHostingSite
forall a b. (a -> b) -> a -> b
$ ProjectPath -> Text
projectPathSite ProjectPath
projectPath) (Text -> Name Account
forall k (a :: k). Text -> Name a
Name (Text -> Name Account) -> Text -> Name Account
forall a b. (a -> b) -> a -> b
$ ProjectPath -> Text
projectPathOwner ProjectPath
projectPath) (Text -> Name Project
forall k (a :: k). Text -> Name a
Name (Text -> Name Project) -> Text -> Name Project
forall a b. (a -> b) -> a -> b
$ ProjectPath -> Text
projectPathProject ProjectPath
projectPath))
    Maybe ProjectPath
Nothing -> do
      (Maybe (Id Project)
projectIdMaybe, ProjectPath
projectPath) <- RIO r (Maybe (Id Project), ProjectPath)
forall r.
(Has HerculesClientToken r, Has HerculesClientEnv r) =>
RIO r (Maybe (Id Project), ProjectPath)
findProjectContextually
      case Maybe (Id Project)
projectIdMaybe of
        Just Id Project
projectId ->
          ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
-> RIO r (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StateAPI ClientAuth (AsClientT ClientM)
stateClient StateAPI ClientAuth (AsClientT ClientM)
-> (StateAPI ClientAuth (AsClientT ClientM)
    -> ToServant
         (ProjectStateResourceGroup ClientAuth) (AsClientT ClientM))
-> ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
forall {k} (subapi :: k -> * -> *) (api :: k -> * -> *) mode
       (a :: k).
(GenericServant (api a) mode, GenericServant (subapi a) mode) =>
api a mode
-> (api a mode -> ToServant (subapi a) mode) -> subapi a mode
`enterApiE` \StateAPI ClientAuth (AsClientT ClientM)
api -> StateAPI ClientAuth (AsClientT ClientM)
-> AsClientT ClientM
   :- Substitute
        ("projects"
         :> (Capture' '[Required, Strict] "projectId" (Id Project)
             :> Placeholder))
        (ToServantApi (ProjectStateResourceGroup ClientAuth))
forall auth f.
StateAPI auth f
-> f
   :- Substitute
        ("projects"
         :> (Capture' '[Required, Strict] "projectId" (Id Project)
             :> Placeholder))
        (ToServantApi (ProjectStateResourceGroup auth))
byProjectId StateAPI ClientAuth (AsClientT ClientM)
api Id Project
projectId)
        Maybe (Id Project)
Nothing ->
          ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
-> RIO r (ProjectStateResourceGroup ClientAuth (AsClientT ClientM))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (StateAPI ClientAuth (AsClientT ClientM)
stateClient StateAPI ClientAuth (AsClientT ClientM)
-> (StateAPI ClientAuth (AsClientT ClientM)
    -> ToServant
         (ProjectStateResourceGroup ClientAuth) (AsClientT ClientM))
-> ProjectStateResourceGroup ClientAuth (AsClientT ClientM)
forall {k} (subapi :: k -> * -> *) (api :: k -> * -> *) mode
       (a :: k).
(GenericServant (api a) mode, GenericServant (subapi a) mode) =>
api a mode
-> (api a mode -> ToServant (subapi a) mode) -> subapi a mode
`enterApiE` \StateAPI ClientAuth (AsClientT ClientM)
api -> StateAPI ClientAuth (AsClientT ClientM)
-> AsClientT ClientM
   :- Substitute
        ("site"
         :> (Capture' '[Required, Strict] "site" (Name SourceHostingSite)
             :> ("account"
                 :> (Capture' '[Required, Strict] "account" (Name Account)
                     :> ("project"
                         :> (Capture' '[Required, Strict] "project" (Name Project)
                             :> Placeholder))))))
        (ToServantApi (ProjectStateResourceGroup ClientAuth))
forall auth f.
StateAPI auth f
-> f
   :- Substitute
        ("site"
         :> (Capture' '[Required, Strict] "site" (Name SourceHostingSite)
             :> ("account"
                 :> (Capture' '[Required, Strict] "account" (Name Account)
                     :> ("project"
                         :> (Capture' '[Required, Strict] "project" (Name Project)
                             :> Placeholder))))))
        (ToServantApi (ProjectStateResourceGroup auth))
byProjectName StateAPI ClientAuth (AsClientT ClientM)
api (Text -> Name SourceHostingSite
forall k (a :: k). Text -> Name a
Name (Text -> Name SourceHostingSite) -> Text -> Name SourceHostingSite
forall a b. (a -> b) -> a -> b
$ ProjectPath -> Text
projectPathSite ProjectPath
projectPath) (Text -> Name Account
forall k (a :: k). Text -> Name a
Name (Text -> Name Account) -> Text -> Name Account
forall a b. (a -> b) -> a -> b
$ ProjectPath -> Text
projectPathOwner ProjectPath
projectPath) (Text -> Name Project
forall k (a :: k). Text -> Name a
Name (Text -> Name Project) -> Text -> Name Project
forall a b. (a -> b) -> a -> b
$ ProjectPath -> Text
projectPathProject ProjectPath
projectPath))