packages feed

credentials-cli-0.0.2: src/Credentials/CLI/Format.hs

{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE LambdaCase           #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE TupleSections        #-}
{-# LANGUAGE UndecidableInstances #-}

{-# OPTIONS_GHC -fno-warn-type-defaults #-}

-- |
-- Module      : Credentials.CLI.Format
-- Copyright   : (c) 2015-2016 Brendan Hay
-- License     : Mozilla Public License, v. 2.0.
-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
--
module Credentials.CLI.Format where

import Credentials
import Credentials.CLI.Types

import Data.Aeson            (ToJSON (..), object, (.=))
import Data.Bifunctor
import Data.ByteString       (ByteString)
import Data.List             (foldl', intersperse)
import Data.List.NonEmpty    (NonEmpty (..))
import Data.Monoid

import Network.AWS.Data

import Options.Applicative.Help hiding (list, string)

import qualified Data.Text as Text

data Status
    = Deleted
    | Truncated

instance ToLog Status where
    build = build . toText

instance ToText Status where
    toText = \case
        Deleted   -> "deleted"
        Truncated -> "truncated"

data Emit = Emit { store' :: Store, result :: Result }

instance ToJSON Emit where
    toJSON (Emit s r) = object [toText s .= r]

instance Pretty Emit where
    pretty (Emit s r) = doc s <> char ':' .$. indent 2 (pretty r)

data Result
    = SetupR    Setup
    | TeardownR
    | InsertR   Name Revision
    | SelectR   Name Revision ByteString
    | DeleteR   Name Revision
    | TruncateR Name
    | ListR     [(Name, NonEmpty Revision)]

instance ToLog Result where
    build = \case
        SetupR        s -> build s
        TeardownR       -> build Deleted
        InsertR   _ r   -> build r
        SelectR   _ _ v -> build v
        DeleteR   {}    -> build Deleted
        TruncateR {}    -> build Truncated
        ListR        rs -> foldMap f rs
          where
            f (n, v :| vs) =
                build n % "," % mconcat (intersperse "," $ map build (v:vs)) % "\n"

instance ToJSON Result where
    toJSON = \case
        SetupR        s -> object ["status" =~ s]
        TeardownR       -> object ["status" =~ Deleted]
        InsertR   n r   -> object ["name"   =~ n, "revision" =~ r]
        SelectR   n r v -> object ["name"   =~ n, "revision" =~ r, "secret" =~ toBS v]
        DeleteR   n r   -> object ["name"   =~ n, "revision" =~ r, "status" =~ Deleted]
        TruncateR n     -> object ["name"   =~ n, "status"   =~ Truncated]
        ListR        rs -> object (map go rs)
      where
        k =~ v = k .= toText v

        go (n, v :| vs) = toText n .= map toText (v:vs)

instance Pretty Result where
    pretty = \case
        SetupR        s -> stat s
        TeardownR       -> stat Deleted
        InsertR   n r   -> name n .$. rev r
        SelectR   n r v -> name n .$. rev r .$. val v
        DeleteR   n r   -> name n .$. rev r .$. stat Deleted
        TruncateR n     -> name n .$. stat Truncated
        ListR        rs -> list rs
      where
        name n = "name:"     <+> doc n
        rev  r = "revision:" <+> doc r
        stat s = "status:"   <+> doc s
        val  v = "secret:"   <+> doc (toBS v)

        list []     = mempty
        list (r:rs) = foldl' (.$.) (f r) (map f rs)
          where
            f (n, v :| vs) = doc n <> ":" .$.
                indent 2 (extractChunk (revs v vs))

            revs v vs = table $ (v, "# latest") : map (,mempty) vs

        table [] = mempty
        table xs = pure $ vcat
            [indent 2 (fillBreak n (item k) <+> v) | (k, v) <- ys]
          where
            n  = maximum (map (Text.length . fst) ys) + 2
            ys = map (first toText) xs

        item x = "-" <+> doc x

doc :: ToText a => a -> Doc
doc = text . string