packages feed

credentials-cli-0.0.1: 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.ByteString.Lazy as LBS
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