{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}

{-|
Module      : Headroom.Command
Description : Support for parsing command line arguments
Copyright   : (c) 2019-2020 Vaclav Svejcar
License     : BSD-3-Clause
Maintainer  : vaclav.svejcar@gmail.com
Stability   : experimental
Portability : POSIX

This module contains code responsible for parsing command line arguments, using
the /optparse-applicative/ library.
-}

module Headroom.Command
  ( commandParser
  )
where

import           Headroom.Command.Readers       ( licenseReader
                                                , licenseTypeReader
                                                , regexReader
                                                )
import           Headroom.Command.Types         ( Command(..) )
import           Headroom.Configuration.Types   ( LicenseType
                                                , RunMode(..)
                                                , TemplateSource(..)
                                                )
import           Headroom.Data.EnumExtra        ( EnumExtra(..) )
import           Headroom.Meta                  ( productDesc
                                                , productInfo
                                                )
import           Options.Applicative
import           RIO
import qualified RIO.Text                      as T


-- | Parses command line arguments.
commandParser :: ParserInfo Command
commandParser :: ParserInfo Command
commandParser = Parser Command -> InfoMod Command -> ParserInfo Command
forall a. Parser a -> InfoMod a -> ParserInfo a
info
  (Parser Command
commands Parser Command -> Parser (Command -> Command) -> Parser Command
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (Command -> Command)
forall a. Parser (a -> a)
helper)
  (InfoMod Command
forall a. InfoMod a
fullDesc InfoMod Command -> InfoMod Command -> InfoMod Command
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod Command
forall a. String -> InfoMod a
progDesc (Text -> String
T.unpack Text
productDesc) InfoMod Command -> InfoMod Command -> InfoMod Command
forall a. Semigroup a => a -> a -> a
<> String -> InfoMod Command
forall a. String -> InfoMod a
header (Text -> String
T.unpack Text
productInfo))
 where
  commands :: Parser Command
commands   = Mod CommandFields Command -> Parser Command
forall a. Mod CommandFields a -> Parser a
subparser (Mod CommandFields Command
runCommand Mod CommandFields Command
-> Mod CommandFields Command -> Mod CommandFields Command
forall a. Semigroup a => a -> a -> a
<> Mod CommandFields Command
genCommand Mod CommandFields Command
-> Mod CommandFields Command -> Mod CommandFields Command
forall a. Semigroup a => a -> a -> a
<> Mod CommandFields Command
initCommand)
  runCommand :: Mod CommandFields Command
runCommand = String -> ParserInfo Command -> Mod CommandFields Command
forall a. String -> ParserInfo a -> Mod CommandFields a
command
    String
"run"
    (Parser Command -> InfoMod Command -> ParserInfo Command
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command
runOptions Parser Command -> Parser (Command -> Command) -> Parser Command
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (Command -> Command)
forall a. Parser (a -> a)
helper)
          (String -> InfoMod Command
forall a. String -> InfoMod a
progDesc String
"add, replace, drop or check source code headers")
    )
  genCommand :: Mod CommandFields Command
genCommand = String -> ParserInfo Command -> Mod CommandFields Command
forall a. String -> ParserInfo a -> Mod CommandFields a
command
    String
"gen"
    (Parser Command -> InfoMod Command -> ParserInfo Command
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command
genOptions Parser Command -> Parser (Command -> Command) -> Parser Command
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (Command -> Command)
forall a. Parser (a -> a)
helper)
          (String -> InfoMod Command
forall a. String -> InfoMod a
progDesc String
"generate stub configuration and template files")
    )
  initCommand :: Mod CommandFields Command
initCommand = String -> ParserInfo Command -> Mod CommandFields Command
forall a. String -> ParserInfo a -> Mod CommandFields a
command
    String
"init"
    (Parser Command -> InfoMod Command -> ParserInfo Command
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command
initOptions Parser Command -> Parser (Command -> Command) -> Parser Command
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (Command -> Command)
forall a. Parser (a -> a)
helper)
          (String -> InfoMod Command
forall a. String -> InfoMod a
progDesc String
"initialize current project for Headroom")
    )

runOptions :: Parser Command
runOptions :: Parser Command
runOptions =
  [String]
-> [Regex]
-> Maybe TemplateSource
-> [Text]
-> Maybe RunMode
-> Bool
-> Bool
-> Command
Run
    ([String]
 -> [Regex]
 -> Maybe TemplateSource
 -> [Text]
 -> Maybe RunMode
 -> Bool
 -> Bool
 -> Command)
-> Parser [String]
-> Parser
     ([Regex]
      -> Maybe TemplateSource
      -> [Text]
      -> Maybe RunMode
      -> Bool
      -> Bool
      -> Command)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser String -> Parser [String]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many
          (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-path" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> Char -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
's' 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
"PATH" 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
"path to source code file/directory"
            )
          )
    Parser
  ([Regex]
   -> Maybe TemplateSource
   -> [Text]
   -> Maybe RunMode
   -> Bool
   -> Bool
   -> Command)
-> Parser [Regex]
-> Parser
     (Maybe TemplateSource
      -> [Text] -> Maybe RunMode -> Bool -> Bool -> Command)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser Regex -> Parser [Regex]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many
          (ReadM Regex -> Mod OptionFields Regex -> Parser Regex
forall a. ReadM a -> Mod OptionFields a -> Parser a
option
            ReadM Regex
regexReader
            (String -> Mod OptionFields Regex
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"excluded-path" Mod OptionFields Regex
-> Mod OptionFields Regex -> Mod OptionFields Regex
forall a. Semigroup a => a -> a -> a
<> Char -> Mod OptionFields Regex
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'e' Mod OptionFields Regex
-> Mod OptionFields Regex -> Mod OptionFields Regex
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields Regex
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"REGEX" Mod OptionFields Regex
-> Mod OptionFields Regex -> Mod OptionFields Regex
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields Regex
forall (f :: * -> *) a. String -> Mod f a
help
              String
"path to exclude from source code file paths"
            )
          )
    Parser
  (Maybe TemplateSource
   -> [Text] -> Maybe RunMode -> Bool -> Bool -> Command)
-> Parser (Maybe TemplateSource)
-> Parser ([Text] -> Maybe RunMode -> Bool -> Bool -> Command)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser TemplateSource -> Parser (Maybe TemplateSource)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional
          (   LicenseType -> TemplateSource
BuiltInTemplates
          (LicenseType -> TemplateSource)
-> Parser LicenseType -> Parser TemplateSource
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadM LicenseType
-> Mod OptionFields LicenseType -> Parser LicenseType
forall a. ReadM a -> Mod OptionFields a -> Parser a
option
                ReadM LicenseType
licenseTypeReader
                (String -> Mod OptionFields LicenseType
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"builtin-templates" Mod OptionFields LicenseType
-> Mod OptionFields LicenseType -> Mod OptionFields LicenseType
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields LicenseType
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"TYPE" Mod OptionFields LicenseType
-> Mod OptionFields LicenseType -> Mod OptionFields LicenseType
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields LicenseType
forall (f :: * -> *) a. String -> Mod f a
help
                  (String
"use built-in templates for license type, available options: "
                  String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
T.unpack (Text -> Text
T.toLower (EnumExtra LicenseType => Text
forall a. EnumExtra a => Text
allValuesToText @LicenseType))
                  )
                )
          Parser TemplateSource
-> Parser TemplateSource -> Parser TemplateSource
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> [String] -> TemplateSource
TemplateFiles
          ([String] -> TemplateSource)
-> Parser [String] -> Parser TemplateSource
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser String -> Parser [String]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
some
                (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
"template-path" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> Char -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
't' 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
"PATH" 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
"path to license template file/directory"
                  )
                )
          )
    Parser ([Text] -> Maybe RunMode -> Bool -> Bool -> Command)
-> Parser [Text]
-> Parser (Maybe RunMode -> Bool -> Bool -> Command)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser Text -> Parser [Text]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many
          (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
"variable" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> Char -> Mod OptionFields Text
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'v' 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
"KEY=VALUE" 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
"value for template variable"
            )
          )
    Parser (Maybe RunMode -> Bool -> Bool -> Command)
-> Parser (Maybe RunMode) -> Parser (Bool -> Bool -> Command)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser RunMode -> Parser (Maybe RunMode)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional
          (   RunMode -> Mod FlagFields RunMode -> Parser RunMode
forall a. a -> Mod FlagFields a -> Parser a
flag'
              RunMode
Add
              (String -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"add-headers" Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> Char -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'a' Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> String -> Mod FlagFields RunMode
forall (f :: * -> *) a. String -> Mod f a
help
                String
"only adds missing license headers"
              )
          Parser RunMode -> Parser RunMode -> Parser RunMode
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> RunMode -> Mod FlagFields RunMode -> Parser RunMode
forall a. a -> Mod FlagFields a -> Parser a
flag'
                RunMode
Check
                (String -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"check-headers" Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> Char -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'c' Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> String -> Mod FlagFields RunMode
forall (f :: * -> *) a. String -> Mod f a
help
                  String
"check whether existing headers are up-to-date"
                )
          Parser RunMode -> Parser RunMode -> Parser RunMode
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> RunMode -> Mod FlagFields RunMode -> Parser RunMode
forall a. a -> Mod FlagFields a -> Parser a
flag'
                RunMode
Replace
                (String -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"replace-headers" Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> Char -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'r' Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> String -> Mod FlagFields RunMode
forall (f :: * -> *) a. String -> Mod f a
help
                  String
"force replace existing license headers"
                )
          Parser RunMode -> Parser RunMode -> Parser RunMode
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> RunMode -> Mod FlagFields RunMode -> Parser RunMode
forall a. a -> Mod FlagFields a -> Parser a
flag'
                RunMode
Drop
                (String -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"drop-headers" Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> Char -> Mod FlagFields RunMode
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'd' Mod FlagFields RunMode
-> Mod FlagFields RunMode -> Mod FlagFields RunMode
forall a. Semigroup a => a -> a -> a
<> String -> Mod FlagFields RunMode
forall (f :: * -> *) a. String -> Mod f a
help
                  String
"drop existing license headers only"
                )
          )
    Parser (Bool -> Bool -> Command)
-> Parser Bool -> Parser (Bool -> Command)
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
"debug" 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
"produce more verbose output")
    Parser (Bool -> Command) -> Parser Bool -> Parser Command
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
"dry-run" 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
"execute dry run (no changes to files)")

genOptions :: Parser Command
genOptions :: Parser Command
genOptions =
  Bool -> Maybe (LicenseType, FileType) -> Command
Gen
    (Bool -> Maybe (LicenseType, FileType) -> Command)
-> Parser Bool -> Parser (Maybe (LicenseType, FileType) -> Command)
forall (f :: * -> *) a b. Functor 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
"config-file" Mod FlagFields Bool -> Mod FlagFields Bool -> Mod FlagFields Bool
forall a. Semigroup a => a -> a -> a
<> Char -> Mod FlagFields Bool
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'c' 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
"generate stub YAML config file to stdout"
          )
    Parser (Maybe (LicenseType, FileType) -> Command)
-> Parser (Maybe (LicenseType, FileType)) -> Parser Command
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser (LicenseType, FileType)
-> Parser (Maybe (LicenseType, FileType))
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional
          (ReadM (LicenseType, FileType)
-> Mod OptionFields (LicenseType, FileType)
-> Parser (LicenseType, FileType)
forall a. ReadM a -> Mod OptionFields a -> Parser a
option
            ReadM (LicenseType, FileType)
licenseReader
            (  String -> Mod OptionFields (LicenseType, FileType)
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"license"
            Mod OptionFields (LicenseType, FileType)
-> Mod OptionFields (LicenseType, FileType)
-> Mod OptionFields (LicenseType, FileType)
forall a. Semigroup a => a -> a -> a
<> Char -> Mod OptionFields (LicenseType, FileType)
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'l'
            Mod OptionFields (LicenseType, FileType)
-> Mod OptionFields (LicenseType, FileType)
-> Mod OptionFields (LicenseType, FileType)
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields (LicenseType, FileType)
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"licenseType:fileType"
            Mod OptionFields (LicenseType, FileType)
-> Mod OptionFields (LicenseType, FileType)
-> Mod OptionFields (LicenseType, FileType)
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields (LicenseType, FileType)
forall (f :: * -> *) a. String -> Mod f a
help String
"generate template for license and file type"
            )
          )

initOptions :: Parser Command
initOptions :: Parser Command
initOptions =
  LicenseType -> [String] -> Command
Init
    (LicenseType -> [String] -> Command)
-> Parser LicenseType -> Parser ([String] -> Command)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadM LicenseType
-> Mod OptionFields LicenseType -> Parser LicenseType
forall a. ReadM a -> Mod OptionFields a -> Parser a
option
          ReadM LicenseType
licenseTypeReader
          (String -> Mod OptionFields LicenseType
forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"license-type" Mod OptionFields LicenseType
-> Mod OptionFields LicenseType -> Mod OptionFields LicenseType
forall a. Semigroup a => a -> a -> a
<> Char -> Mod OptionFields LicenseType
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'l' Mod OptionFields LicenseType
-> Mod OptionFields LicenseType -> Mod OptionFields LicenseType
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields LicenseType
forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
metavar String
"TYPE" Mod OptionFields LicenseType
-> Mod OptionFields LicenseType -> Mod OptionFields LicenseType
forall a. Semigroup a => a -> a -> a
<> String -> Mod OptionFields LicenseType
forall (f :: * -> *) a. String -> Mod f a
help
            (  String
"type of open source license, available options: "
            String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
T.unpack (Text -> Text
T.toLower (EnumExtra LicenseType => Text
forall a. EnumExtra a => Text
allValuesToText @LicenseType))
            )
          )
    Parser ([String] -> Command) -> Parser [String] -> Parser Command
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser String -> Parser [String]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
some
          (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-path" Mod OptionFields String
-> Mod OptionFields String -> Mod OptionFields String
forall a. Semigroup a => a -> a -> a
<> Char -> Mod OptionFields String
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
's' 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
"PATH" 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
"path to source code file/directory"
            )
          )