content: {-# LANGUAGE LambdaCase, OverloadedStrings #-}
  -- | The precisely-typed version of Hawk's command-line arguments.
  module System.Console.Hawk.Args.Spec where
  
  import Data.ByteString (ByteString)
  
  
  data HawkSpec
      = Help
      | Version
      | Eval  ExprSpec           OutputSpec
      | Apply ExprSpec InputSpec OutputSpec
      | Map   ExprSpec InputSpec OutputSpec
    deriving (Show, Eq)
  
  
  data InputSpec = InputSpec
      { inputSource :: InputSource
      , inputFormat :: InputFormat
      }
    deriving (Show, Eq)
  
  data OutputSpec = OutputSpec
      { outputSink :: OutputSink
      , outputFormat :: OutputFormat
      }
    deriving (Show, Eq)
  
  
  data InputSource
      = NoInput
      | UseStdin
      | InputFile FilePath
    deriving (Show, Eq)
  
  data OutputSink
      = UseStdout
      -- OutputFile FilePath  -- we might want to implement --in-place
                              -- in the future
    deriving (Show, Eq)
  
  data InputFormat
      = RawStream
      | Records Separator RecordFormat
    deriving (Show, Eq)
  
  data RecordFormat
      = RawRecord
      | Fields Separator
    deriving (Show, Eq)
  
  -- We can't know ahead of time whether it's going to be a raw stream
  -- or raw records or fields, it depends on the type of the user expression.
  data OutputFormat = OutputFormat
      { recordDelimiter :: Delimiter
      , fieldDelimiter :: Delimiter
      }
    deriving (Show, Eq)
  
  
  -- A 'Processor' describes how to process a string; either by separating it
  -- into chunks or by leaving it as-is. When separating it into chunks, we can
  -- use whitespace as a delimiter (meaning one or more consecutive whitespace
  -- characters), or we can use a specific delimiter.
  type Delimiter = ByteString
  data Separator = Whitespace | Delimiter Delimiter
    deriving (Show, Eq)
  data Processor = DoNotSeparate | SeparateOn Separator
    deriving (Show, Eq)
  
  fromSeparator :: Delimiter -> Separator -> Delimiter
  fromSeparator def = \case
    Whitespace  -> def
    Delimiter d -> d
  
  fromProcessor :: Delimiter -> Processor -> Delimiter
  fromProcessor def = \case
    DoNotSeparate -> def
    SeparateOn s  -> fromSeparator def s
  
  
  newtype ContextSpec = ContextSpec
      { userContextDirectory :: FilePath
      }
    deriving (Show, Eq)
  
  type UntypedExpr = String
  
  data ExprSpec = ExprSpec
      { contextSpec :: ContextSpec
      , untypedExpr :: UntypedExpr
      }
    deriving (Show, Eq)
  
  defaultInputSpec, noInput :: InputSpec
  defaultInputSpec = InputSpec UseStdin defaultInputFormat
  noInput          = InputSpec NoInput  defaultInputFormat
  
  defaultOutputSpec :: OutputSpec
  defaultOutputSpec = OutputSpec UseStdout defaultOutputFormat
  
  
  defaultInputFormat :: InputFormat
  defaultInputFormat = Records defaultRecordSeparator
                     $ Fields defaultFieldSeparator
  
  defaultOutputFormat :: OutputFormat
  defaultOutputFormat = OutputFormat defaultRecordDelimiter defaultFieldDelimiter
  
  
  defaultRecordSeparator, defaultFieldSeparator :: Separator
  defaultRecordSeparator = Delimiter defaultRecordDelimiter
  defaultFieldSeparator = Whitespace
  
  defaultRecordDelimiter, defaultFieldDelimiter :: Delimiter
  defaultRecordDelimiter = "\n"
  defaultFieldDelimiter = " "
  