packages feed

cachix-0.3.7: src/Cachix/Client/OptionsParser.hs

module Cachix.Client.OptionsParser
  ( CachixCommand (..),
    CachixOptions (..),
    PushArguments (..),
    PushOptions (..),
    BinaryCacheName,
    getOpts,
  )
where

import qualified Cachix.Client.Config as Config
import qualified Cachix.Client.InstallationMode as InstallationMode
import Cachix.Client.URI (defaultCachixURI)
import Data.Bifunctor (first)
import Options.Applicative
import Protolude hiding (option)
import URI.ByteString
  ( Absolute,
    URIRef,
    parseURI,
    serializeURIRef',
    strictURIParserOptions,
  )

data CachixOptions
  = CachixOptions
      { host :: URIRef Absolute,
        configPath :: Config.ConfigPath,
        verbose :: Bool
      }
  deriving (Show)

parserCachixOptions :: Config.ConfigPath -> Parser CachixOptions
parserCachixOptions defaultConfigPath =
  CachixOptions
    <$> option
      uriOption
      ( long "host"
          <> value defaultCachixURI
          <> metavar "URI"
          <> showDefaultWith (toS . serializeURIRef')
          <> help "Host to connect to"
      )
    <*> strOption
      ( long "config"
          <> short 'c'
          <> value defaultConfigPath
          <> metavar "CONFIGPATH"
          <> showDefault
          <> help "Cachix configuration file"
      )
    <*> switch
      ( long "verbose"
          <> short 'v'
          <> help "Verbose mode"
      )

uriOption :: ReadM (URIRef Absolute)
uriOption = eitherReader $ \s ->
  first show $ parseURI strictURIParserOptions $ toS s

type BinaryCacheName = Text

data CachixCommand
  = AuthToken Text
  | Create BinaryCacheName
  | GenerateKeypair BinaryCacheName
  | Push PushArguments
  | Use BinaryCacheName InstallationMode.UseOptions
  | Version
  deriving (Show)

data PushArguments
  = PushPaths PushOptions Text [Text]
  | PushWatchStore PushOptions Text
  deriving (Show)

data PushOptions
  = PushOptions
      { compressionLevel :: Int,
        numJobs :: Int
      }
  deriving (Show)

parserCachixCommand :: Parser CachixCommand
parserCachixCommand =
  subparser $
    command "authtoken" (infoH authtoken (progDesc "Configure token for authentication to cachix.org"))
      <> command "create" (infoH create (progDesc "DEPRECATED: Go to https://cachix.org instead"))
      <> command "generate-keypair" (infoH generateKeypair (progDesc "Generate keypair for a binary cache"))
      <> command "push" (infoH push (progDesc "Upload Nix store paths to the binary cache"))
      <> command "use" (infoH use (progDesc "Configure nix.conf to enable binary cache during builds"))
  where
    nameArg = strArgument (metavar "NAME")
    authtoken = AuthToken <$> strArgument (metavar "TOKEN")
    create = Create <$> nameArg
    generateKeypair = GenerateKeypair <$> nameArg
    validatedLevel l =
      l <$ unless (l `elem` [0 .. 9]) (readerError $ "value " <> show l <> " not in expected range: [0..9]")
    pushOptions :: Parser PushOptions
    pushOptions =
      PushOptions
        <$> option
          (auto >>= validatedLevel)
          ( long "compression-level"
              <> short 'c'
              <> metavar "[0..9]"
              <> help
                "The compression level for XZ compression.\
                \ Take compressor *and* decompressor memory usage into account before using [7..9]!"
              <> showDefault
              <> value 2
          )
        <*> option
          auto
          ( long "jobs"
              <> short 'j'
              <> help "Number of threads used for pushing store paths."
              <> showDefault
              <> value 4
          )
    push = (\opts cache f -> Push $ f opts cache) <$> pushOptions <*> nameArg <*> (pushPaths <|> pushWatchStore)
    pushPaths =
      (\paths opts cache -> PushPaths opts cache paths)
        <$> many (strArgument (metavar "PATHS..."))
    pushWatchStore =
      (\() opts cache -> PushWatchStore opts cache)
        <$> flag'
          ()
          ( long "watch-store"
              <> short 'w'
              <> help "Run in daemon mode and push store paths as they are added to /nix/store"
          )
    use =
      Use <$> nameArg
        <*> ( InstallationMode.UseOptions
                <$> optional
                  ( option
                      (maybeReader InstallationMode.fromString)
                      ( long "mode"
                          <> short 'm'
                          <> help "Mode in which to configure binary caches for Nix. Supported values: nixos, root-nixconf, user-nixconf"
                      )
                  )
                <*> strOption
                  ( long "nixos-folder"
                      <> short 'd'
                      <> help "Base directory for NixOS configuration"
                      <> value "/etc/nixos/"
                      <> showDefault
                  )
            )

getOpts :: IO (CachixOptions, CachixCommand)
getOpts = do
  configpath <- Config.getDefaultFilename
  customExecParser (prefs showHelpOnEmpty) (optsInfo configpath)

optsInfo :: Config.ConfigPath -> ParserInfo (CachixOptions, CachixCommand)
optsInfo configpath = infoH parser desc
  where
    parser = (,) <$> parserCachixOptions configpath <*> (parserCachixCommand <|> versionParser)
    versionParser :: Parser CachixCommand
    versionParser =
      flag'
        Version
        ( long "version"
            <> short 'V'
            <> help "Show cachix version"
        )

desc :: InfoMod a
desc =
  fullDesc
    <> progDesc "Sign into https://cachix.org to get started."
    <> header "cachix.org command interface"

-- TODO: usage footer
infoH :: Parser a -> InfoMod a -> ParserInfo a
infoH a = info (helper <*> a)