{-# LANGUAGE UndecidableInstances #-}

-- | @since 0.1
module Kairos.Types.TimeReader
  ( TimeReader (..),
    defaultTimeReader,
  )
where

import Control.DeepSeq (NFData (rnf), deepseq)
import Data.List.NonEmpty (NonEmpty)
import Data.Text (Text)
import GHC.Generics (Generic)
import Kairos.Types.Date (Date)
import Kairos.Types.TZInput (TZInput)
import Kairos.Types.TimeFormat (TimeFormat, defaultTimeFormats)
import Optics.Core (A_Lens, LabelOptic (labelOptic), lensVL)

-- | Determines how to read a time string.
--
-- @since 0.1
data TimeReader = MkTimeReader
  { -- | Format(s) used when parsing the time string. This should __not__
    -- include timezone formatting e.g. @%Z@. Use 'srcTZ' instead. It should
    -- also not include date information. Use 'date' instead.
    --
    -- The formats are tried in order.
    --
    -- @since 0.1
    TimeReader -> NonEmpty TimeFormat
formats :: NonEmpty TimeFormat,
    -- | Timezone in which to read the string. 'Nothing' corresponds to
    -- local timezone.
    --
    -- @since 0.1
    TimeReader -> Maybe TZInput
srcTZ :: Maybe TZInput,
    -- | Date corresponding to the 'timeString'. If 'Nothing', uses the
    -- current date, determined by the source.
    --
    -- @since 0.1
    TimeReader -> Maybe Date
date :: Maybe Date,
    -- | The time string to parse. This should __not__ include a timezone
    -- e.g. EST. Use 'srcTZ' instead.
    --
    -- @since 0.1
    TimeReader -> Text
timeString :: Text
  }
  deriving stock
    ( -- | @since 0.1
      TimeReader -> TimeReader -> Bool
(TimeReader -> TimeReader -> Bool)
-> (TimeReader -> TimeReader -> Bool) -> Eq TimeReader
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TimeReader -> TimeReader -> Bool
== :: TimeReader -> TimeReader -> Bool
$c/= :: TimeReader -> TimeReader -> Bool
/= :: TimeReader -> TimeReader -> Bool
Eq,
      -- | @since 0.1
      (forall x. TimeReader -> Rep TimeReader x)
-> (forall x. Rep TimeReader x -> TimeReader) -> Generic TimeReader
forall x. Rep TimeReader x -> TimeReader
forall x. TimeReader -> Rep TimeReader x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. TimeReader -> Rep TimeReader x
from :: forall x. TimeReader -> Rep TimeReader x
$cto :: forall x. Rep TimeReader x -> TimeReader
to :: forall x. Rep TimeReader x -> TimeReader
Generic,
      -- | @since 0.1
      Int -> TimeReader -> ShowS
[TimeReader] -> ShowS
TimeReader -> String
(Int -> TimeReader -> ShowS)
-> (TimeReader -> String)
-> ([TimeReader] -> ShowS)
-> Show TimeReader
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TimeReader -> ShowS
showsPrec :: Int -> TimeReader -> ShowS
$cshow :: TimeReader -> String
show :: TimeReader -> String
$cshowList :: [TimeReader] -> ShowS
showList :: [TimeReader] -> ShowS
Show
    )

-- | @since 0.1
instance NFData TimeReader where
  rnf :: TimeReader -> ()
rnf (MkTimeReader NonEmpty TimeFormat
f Maybe TZInput
s Maybe Date
td Text
ts) =
    NonEmpty TimeFormat
f NonEmpty TimeFormat -> () -> ()
forall a b. NFData a => a -> b -> b
`deepseq` Maybe TZInput
s Maybe TZInput -> () -> ()
forall a b. NFData a => a -> b -> b
`deepseq` Maybe Date
td Maybe Date -> () -> ()
forall a b. NFData a => a -> b -> b
`deepseq` Text
ts Text -> () -> ()
forall a b. NFData a => a -> b -> b
`deepseq` ()

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ NonEmpty TimeFormat, b ~ NonEmpty TimeFormat) =>
  LabelOptic "formats" k TimeReader TimeReader a b
  where
  labelOptic :: Optic k NoIx TimeReader TimeReader a b
labelOptic = LensVL TimeReader TimeReader a b -> Lens TimeReader TimeReader a b
forall s t a b. LensVL s t a b -> Lens s t a b
lensVL (LensVL TimeReader TimeReader a b
 -> Lens TimeReader TimeReader a b)
-> LensVL TimeReader TimeReader a b
-> Lens TimeReader TimeReader a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkTimeReader NonEmpty TimeFormat
_format Maybe TZInput
_srcTZ Maybe Date
_date Text
_timeString) ->
    (NonEmpty TimeFormat -> TimeReader)
-> f (NonEmpty TimeFormat) -> f TimeReader
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\NonEmpty TimeFormat
format' -> NonEmpty TimeFormat
-> Maybe TZInput -> Maybe Date -> Text -> TimeReader
MkTimeReader NonEmpty TimeFormat
format' Maybe TZInput
_srcTZ Maybe Date
_date Text
_timeString) (a -> f b
f a
NonEmpty TimeFormat
_format)
  {-# INLINE labelOptic #-}

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ Maybe TZInput, b ~ Maybe TZInput) =>
  LabelOptic "srcTZ" k TimeReader TimeReader a b
  where
  labelOptic :: Optic k NoIx TimeReader TimeReader a b
labelOptic = LensVL TimeReader TimeReader a b -> Lens TimeReader TimeReader a b
forall s t a b. LensVL s t a b -> Lens s t a b
lensVL (LensVL TimeReader TimeReader a b
 -> Lens TimeReader TimeReader a b)
-> LensVL TimeReader TimeReader a b
-> Lens TimeReader TimeReader a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkTimeReader NonEmpty TimeFormat
_format Maybe TZInput
_srcTZ Maybe Date
_date Text
_timeString) ->
    (Maybe TZInput -> TimeReader) -> f (Maybe TZInput) -> f TimeReader
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Maybe TZInput
srcTZ' -> NonEmpty TimeFormat
-> Maybe TZInput -> Maybe Date -> Text -> TimeReader
MkTimeReader NonEmpty TimeFormat
_format Maybe TZInput
srcTZ' Maybe Date
_date Text
_timeString) (a -> f b
f a
Maybe TZInput
_srcTZ)
  {-# INLINE labelOptic #-}

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ Maybe Date, b ~ Maybe Date) =>
  LabelOptic "date" k TimeReader TimeReader a b
  where
  labelOptic :: Optic k NoIx TimeReader TimeReader a b
labelOptic = LensVL TimeReader TimeReader a b -> Lens TimeReader TimeReader a b
forall s t a b. LensVL s t a b -> Lens s t a b
lensVL (LensVL TimeReader TimeReader a b
 -> Lens TimeReader TimeReader a b)
-> LensVL TimeReader TimeReader a b
-> Lens TimeReader TimeReader a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkTimeReader NonEmpty TimeFormat
_format Maybe TZInput
_srcTZ Maybe Date
_date Text
_timeString) ->
    (Maybe Date -> TimeReader) -> f (Maybe Date) -> f TimeReader
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Maybe Date
date' -> NonEmpty TimeFormat
-> Maybe TZInput -> Maybe Date -> Text -> TimeReader
MkTimeReader NonEmpty TimeFormat
_format Maybe TZInput
_srcTZ Maybe Date
date' Text
_timeString) (a -> f b
f a
Maybe Date
_date)
  {-# INLINE labelOptic #-}

-- | @since 0.1
instance
  (k ~ A_Lens, a ~ Text, b ~ Text) =>
  LabelOptic "timeString" k TimeReader TimeReader a b
  where
  labelOptic :: Optic k NoIx TimeReader TimeReader a b
labelOptic = LensVL TimeReader TimeReader a b -> Lens TimeReader TimeReader a b
forall s t a b. LensVL s t a b -> Lens s t a b
lensVL (LensVL TimeReader TimeReader a b
 -> Lens TimeReader TimeReader a b)
-> LensVL TimeReader TimeReader a b
-> Lens TimeReader TimeReader a b
forall a b. (a -> b) -> a -> b
$ \a -> f b
f (MkTimeReader NonEmpty TimeFormat
_format Maybe TZInput
_srcTZ Maybe Date
_date Text
_timeString) ->
    (Text -> TimeReader) -> f Text -> f TimeReader
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (NonEmpty TimeFormat
-> Maybe TZInput -> Maybe Date -> Text -> TimeReader
MkTimeReader NonEmpty TimeFormat
_format Maybe TZInput
_srcTZ Maybe Date
_date) (a -> f b
f a
Text
_timeString)
  {-# INLINE labelOptic #-}

-- | Given a time string, returns a default time reader.
--
-- @since 0.1
defaultTimeReader :: Text -> TimeReader
defaultTimeReader :: Text -> TimeReader
defaultTimeReader = NonEmpty TimeFormat
-> Maybe TZInput -> Maybe Date -> Text -> TimeReader
MkTimeReader NonEmpty TimeFormat
defaultTimeFormats Maybe TZInput
forall a. Maybe a
Nothing Maybe Date
forall a. Maybe a
Nothing