{-# LANGUAGE NoImplicitPrelude #-}
-- |
-- Module:       $HEADER$
-- Description:  Type class for accessing Verbosity.
-- Copyright:    (c) 2015-2016 Peter Trško
-- License:      BSD3
--
-- Maintainer:   peter.trsko@gmail.com
-- Stability:    experimental
-- Portability:  NoImplicitPrelude
--
-- Type class for accessing 'Verbosity'.
module Data.Verbosity.Class
    (
    -- * Hand Written Instance Example
    --
    -- $basicUsageExample

    -- * TemplateHaskell Example
    --
    -- $thUsageExample

    -- * HasVerbosity Type Class
      HasVerbosity(..)
    , getVerbosity
    , setVerbosity
    , modifyVerbosity

    -- * Verbosity Re-export
    , module Data.Verbosity
    )
  where

import Control.Applicative (Const(..))
import Data.Function ((.), ($), const)
import Data.Functor (Functor)
import Data.Functor.Identity (Identity(..))

import Data.Verbosity


class HasVerbosity s where
    -- | Lens for accessing 'Verbosity' embedded in the type @s@.
    verbosity :: Functor f => (Verbosity -> f Verbosity) -> s -> f s

instance HasVerbosity Verbosity where
    verbosity = ($)

-- | Specialization of 'verbosity' lens in to getter function.
getVerbosity :: HasVerbosity s => s -> Verbosity
getVerbosity = getConst . verbosity Const

-- | Specialization of 'verbosity' lens in to setter function.
setVerbosity :: HasVerbosity s => Verbosity -> s -> s
setVerbosity v = runIdentity . verbosity (const (Identity v))

-- | Specialization of 'verbosity' lens in to modification function.
modifyVerbosity :: HasVerbosity s => (Verbosity -> Verbosity) -> s -> s
modifyVerbosity f = runIdentity . verbosity (Identity . f)

-- $basicUsageExample
--
-- Lets define simple data type that looks something like:
--
-- @
-- data Config = Config
--     { _appVerbosity :: 'Verbosity'
--     , ...
--     }
--   deriving (Show, ...)
-- @
--
-- Now we can define instance of 'HasVerbosity' by hand:
--
-- @
-- instance 'HasVerbosity' Config where
--     verbosity f c@Config{_appVerbosity = a} =
--         (\b -> c{_appVerbosity = b}) 'Data.Functor.<$>' f a
-- @

-- $thUsageExample
--
-- Package <https://hackage.haskell.org/package/lens lens> has TemplateHaskell
-- functions that can define lenses for you:
--
-- @
-- import Control.Lens.TH (makeLenses)
--
-- data Config = Config
--     { _appVerbosity :: 'Verbosity'
--     , ...
--     }
--   deriving (Show, ...)
--
-- makeLenses ''Config
-- @
--
-- Don't forget to to turn on TemplateHaskell by putting following pragma at
-- the beginning of your module:
--
-- @
-- {-\# LANGUAGE TemplateHaskell \#-}
-- @
--
-- Now definition of 'HasVerbosity' instance will look like:
--
-- @
-- instance 'HasVerbosity' Config where
--     'verbosity' = appVerbosity   -- Lens generated by makeLenses.
-- @