module Ecluse.CLI (
    AppCommand (..),
    commandParser,
    execCLI,
) where

import Data.Version (showVersion)
import Options.Applicative
import Paths_ecluse (version)

import Ecluse.Pilot (PilotCompileOptions (..))

data AppCommand
    = RunProxy
    | RunPilot
    | RunPilotCompile PilotCompileOptions
    | RunDredger
    deriving stock (AppCommand -> AppCommand -> Bool
(AppCommand -> AppCommand -> Bool)
-> (AppCommand -> AppCommand -> Bool) -> Eq AppCommand
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: AppCommand -> AppCommand -> Bool
== :: AppCommand -> AppCommand -> Bool
$c/= :: AppCommand -> AppCommand -> Bool
/= :: AppCommand -> AppCommand -> Bool
Eq, Int -> AppCommand -> ShowS
[AppCommand] -> ShowS
AppCommand -> String
(Int -> AppCommand -> ShowS)
-> (AppCommand -> String)
-> ([AppCommand] -> ShowS)
-> Show AppCommand
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppCommand -> ShowS
showsPrec :: Int -> AppCommand -> ShowS
$cshow :: AppCommand -> String
show :: AppCommand -> String
$cshowList :: [AppCommand] -> ShowS
showList :: [AppCommand] -> ShowS
Show)

commandParser :: Parser AppCommand
commandParser :: Parser AppCommand
commandParser =
    Mod CommandFields AppCommand -> Parser AppCommand
forall a. Mod CommandFields a -> Parser a
hsubparser
        ( String -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. String -> ParserInfo a -> Mod CommandFields a
command String
"proxy" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AppCommand
RunProxy) (String -> InfoMod AppCommand
forall a. String -> InfoMod a
progDesc String
"Run the Écluse proxy server"))
            Mod CommandFields AppCommand
-> Mod CommandFields AppCommand -> Mod CommandFields AppCommand
forall a. Semigroup a => a -> a -> a
<> String -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. String -> ParserInfo a -> Mod CommandFields a
command String
"pilot" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info Parser AppCommand
pilotCommandParser (String -> InfoMod AppCommand
forall a. String -> InfoMod a
progDesc String
"Run the Écluse Pilot (OSV ingestion pipeline)"))
            Mod CommandFields AppCommand
-> Mod CommandFields AppCommand -> Mod CommandFields AppCommand
forall a. Semigroup a => a -> a -> a
<> String -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. String -> ParserInfo a -> Mod CommandFields a
command String
"dredger" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AppCommand
RunDredger) (String -> InfoMod AppCommand
forall a. String -> InfoMod a
progDesc String
"Run the Écluse Dredger (mirror pruning worker)"))
        )
        Parser AppCommand -> Parser AppCommand -> Parser AppCommand
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AppCommand
RunProxy

-- A bare @pilot@ keeps its serve-and-export meaning; @pilot compile@ selects
-- the one-shot mode.
pilotCommandParser :: Parser AppCommand
pilotCommandParser :: Parser AppCommand
pilotCommandParser =
    Mod CommandFields AppCommand -> Parser AppCommand
forall a. Mod CommandFields a -> Parser a
hsubparser
        ( String -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. String -> ParserInfo a -> Mod CommandFields a
command
            String
"compile"
            (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (PilotCompileOptions -> AppCommand
RunPilotCompile (PilotCompileOptions -> AppCommand)
-> Parser PilotCompileOptions -> Parser AppCommand
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser PilotCompileOptions
pilotCompileOptionsParser) (String -> InfoMod AppCommand
forall a. String -> InfoMod a
progDesc String
"Compile one ecosystem's OSV export into a local osv.db artifact, then exit"))
        )
        Parser AppCommand -> Parser AppCommand -> Parser AppCommand
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AppCommand
RunPilot

pilotCompileOptionsParser :: Parser PilotCompileOptions
pilotCompileOptionsParser :: Parser PilotCompileOptions
pilotCompileOptionsParser =
    Text -> Maybe String -> String -> Bool -> PilotCompileOptions
PilotCompileOptions
        (Text -> Maybe String -> String -> Bool -> PilotCompileOptions)
-> Parser Text
-> Parser (Maybe String -> String -> Bool -> PilotCompileOptions)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Mod OptionFields Text -> Parser Text
forall s. IsString s => Mod OptionFields s -> Parser s
strOption (String -> Mod OptionFields Text
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"ecosystem" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields Text
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"ECOSYSTEM" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> Text -> Mod OptionFields Text
forall (f :: * -> *) a. HasValue f => a -> Mod f a
value Text
"npm" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> Mod OptionFields Text
forall a (f :: * -> *). Show a => Mod f a
showDefault Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields Text
forall (f :: * -> *) a. String -> Mod f a
help String
"Ecosystem whose OSV export to compile")
        Parser (Maybe String -> String -> Bool -> PilotCompileOptions)
-> Parser (Maybe String)
-> Parser (String -> Bool -> PilotCompileOptions)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser String -> Parser (Maybe String)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional (Mod OptionFields String -> Parser String
forall s. IsString s => Mod OptionFields s -> Parser s
strOption (String -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"source" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"URL" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. String -> Mod f a
help String
"OSV export URL (defaults to the configured osvExportBaseUrl for ECOSYSTEM)"))
        Parser (String -> Bool -> PilotCompileOptions)
-> Parser String -> Parser (Bool -> PilotCompileOptions)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Mod OptionFields String -> Parser String
forall s. IsString s => Mod OptionFields s -> Parser s
strOption (String -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"out" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"DIR" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields String
forall (f :: * -> *) a. String -> Mod f a
help String
"Directory the artifact is written into")
        Parser (Bool -> PilotCompileOptions)
-> Parser Bool -> Parser PilotCompileOptions
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Mod FlagFields Bool -> Parser Bool
switch (String -> Mod FlagFields Bool
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"upload" Mod FlagFields Bool -> Mod FlagFields Bool -> Mod FlagFields Bool
forall a. Semigroup a => a -> a -> a
<> String -> Mod FlagFields Bool
forall (f :: * -> *) a. String -> Mod f a
help String
"After compiling, upload the artifact to the configured vulnerability-database bucket (one full sync cycle)")

execCLI :: IO AppCommand
execCLI :: IO AppCommand
execCLI =
    ParserInfo AppCommand -> IO AppCommand
forall a. ParserInfo a -> IO a
execParser (ParserInfo AppCommand -> IO AppCommand)
-> ParserInfo AppCommand -> IO AppCommand
forall a b. (a -> b) -> a -> b
$
        Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info
            (Parser AppCommand
commandParser Parser AppCommand
-> Parser (AppCommand -> AppCommand) -> Parser AppCommand
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (AppCommand -> AppCommand)
forall a. Parser (a -> a)
helper Parser AppCommand
-> Parser (AppCommand -> AppCommand) -> Parser AppCommand
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (AppCommand -> AppCommand)
forall a. Parser (a -> a)
versionOption)
            ( InfoMod AppCommand
forall a. InfoMod a
fullDesc
                InfoMod AppCommand -> InfoMod AppCommand -> InfoMod AppCommand
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod AppCommand
forall a. String -> InfoMod a
progDesc String
"Écluse - supply-chain resilience proxy"
                InfoMod AppCommand -> InfoMod AppCommand -> InfoMod AppCommand
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod AppCommand
forall a. String -> InfoMod a
header String
"ecluse - a configurable policy gate for package registries"
            )
  where
    versionOption :: Parser (a -> a)
versionOption = String -> Mod OptionFields (a -> a) -> Parser (a -> a)
forall a. String -> Mod OptionFields (a -> a) -> Parser (a -> a)
infoOption (Version -> String
showVersion Version
version) (String -> Mod OptionFields (a -> a)
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"version" Mod OptionFields (a -> a)
-> Mod OptionFields (a -> a) -> Mod OptionFields (a -> a)
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields (a -> a)
forall (f :: * -> *) a. String -> Mod f a
help String
"Show version")