    {-# Language OverloadedStrings #-}

    {-|
    Module      : Irc.UserInfo
    Description : User hostmasks
    Copyright   : (c) Eric Mertens, 2016
    License     : ISC
    Maintainer  : emertens@gmail.com

    Information identifying users on IRC. This information includes
    a nickname and optionally a username and hostname.

    -}

    module Irc.UserInfo
      (
      -- * Type
        UserInfo(..)

      -- * Parser and printer
      , renderUserInfo
      , parseUserInfo

      -- * Lenses
      , uiNick
      , uiName
      , uiHost
      ) where

    import           Data.Text (Text)
    import qualified Data.Text as Text

    import           Irc.Identifier

    -- | 'UserInfo' packages a nickname along with the username and hostname
    -- if they are known in the current context.
    data UserInfo = UserInfo
      { UserInfo -> Identifier
    userNick :: {-# UNPACK #-} !Identifier   -- ^ nickname
      , UserInfo -> Text
    userName :: {-# UNPACK #-} !Text -- ^ username, empty when missing
      , UserInfo -> Text
    userHost :: {-# UNPACK #-} !Text -- ^ hostname, empty when missing
      }
      deriving (UserInfo -> UserInfo -> Bool
    (UserInfo -> UserInfo -> Bool)
    -> (UserInfo -> UserInfo -> Bool) -> Eq UserInfo
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: UserInfo -> UserInfo -> Bool
    == :: UserInfo -> UserInfo -> Bool
    $c/= :: UserInfo -> UserInfo -> Bool
    /= :: UserInfo -> UserInfo -> Bool
    Eq, Eq UserInfo
    Eq UserInfo =>
    (UserInfo -> UserInfo -> Ordering)
    -> (UserInfo -> UserInfo -> Bool)
    -> (UserInfo -> UserInfo -> Bool)
    -> (UserInfo -> UserInfo -> Bool)
    -> (UserInfo -> UserInfo -> Bool)
    -> (UserInfo -> UserInfo -> UserInfo)
    -> (UserInfo -> UserInfo -> UserInfo)
    -> Ord UserInfo
    UserInfo -> UserInfo -> Bool
    UserInfo -> UserInfo -> Ordering
    UserInfo -> UserInfo -> UserInfo
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    $ccompare :: UserInfo -> UserInfo -> Ordering
    compare :: UserInfo -> UserInfo -> Ordering
    $c< :: UserInfo -> UserInfo -> Bool
    < :: UserInfo -> UserInfo -> Bool
    $c<= :: UserInfo -> UserInfo -> Bool
    <= :: UserInfo -> UserInfo -> Bool
    $c> :: UserInfo -> UserInfo -> Bool
    > :: UserInfo -> UserInfo -> Bool
    $c>= :: UserInfo -> UserInfo -> Bool
    >= :: UserInfo -> UserInfo -> Bool
    $cmax :: UserInfo -> UserInfo -> UserInfo
    max :: UserInfo -> UserInfo -> UserInfo
    $cmin :: UserInfo -> UserInfo -> UserInfo
    min :: UserInfo -> UserInfo -> UserInfo
    Ord, ReadPrec [UserInfo]
    ReadPrec UserInfo
    Int -> ReadS UserInfo
    ReadS [UserInfo]
    (Int -> ReadS UserInfo)
    -> ReadS [UserInfo]
    -> ReadPrec UserInfo
    -> ReadPrec [UserInfo]
    -> Read UserInfo
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS UserInfo
    readsPrec :: Int -> ReadS UserInfo
    $creadList :: ReadS [UserInfo]
    readList :: ReadS [UserInfo]
    $creadPrec :: ReadPrec UserInfo
    readPrec :: ReadPrec UserInfo
    $creadListPrec :: ReadPrec [UserInfo]
    readListPrec :: ReadPrec [UserInfo]
    Read, Int -> UserInfo -> ShowS
    [UserInfo] -> ShowS
    UserInfo -> String
    (Int -> UserInfo -> ShowS)
    -> (UserInfo -> String) -> ([UserInfo] -> ShowS) -> Show UserInfo
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> UserInfo -> ShowS
    showsPrec :: Int -> UserInfo -> ShowS
    $cshow :: UserInfo -> String
    show :: UserInfo -> String
    $cshowList :: [UserInfo] -> ShowS
    showList :: [UserInfo] -> ShowS
    Show)

    -- | Lens into 'userNick' field.
    uiNick :: Functor f => (Identifier -> f Identifier) -> UserInfo -> f UserInfo
    uiNick :: forall (f :: * -> *).
    Functor f =>
    (Identifier -> f Identifier) -> UserInfo -> f UserInfo
    uiNick Identifier -> f Identifier
    f ui :: UserInfo
    ui@UserInfo{userNick :: UserInfo -> Identifier
    userNick = Identifier
    n} = (\Identifier
    n' -> UserInfo
    ui{userNick = n'}) (Identifier -> UserInfo) -> f Identifier -> f UserInfo
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Identifier -> f Identifier
    f Identifier
    n

    -- | Lens into 'userName' field.
    uiName :: Functor f => (Text -> f Text) -> UserInfo -> f UserInfo
    uiName :: forall (f :: * -> *).
    Functor f =>
    (Text -> f Text) -> UserInfo -> f UserInfo
    uiName Text -> f Text
    f ui :: UserInfo
    ui@UserInfo{userName :: UserInfo -> Text
    userName = Text
    n} = (\Text
    n' -> UserInfo
    ui{userName = n'}) (Text -> UserInfo) -> f Text -> f UserInfo
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Text -> f Text
    f Text
    n

    -- | Lens into 'userHost' field.
    uiHost :: Functor f => (Text -> f Text) -> UserInfo -> f UserInfo
    uiHost :: forall (f :: * -> *).
    Functor f =>
    (Text -> f Text) -> UserInfo -> f UserInfo
    uiHost Text -> f Text
    f ui :: UserInfo
    ui@UserInfo{userHost :: UserInfo -> Text
    userHost = Text
    n} = (\Text
    n' -> UserInfo
    ui{userHost = n'}) (Text -> UserInfo) -> f Text -> f UserInfo
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Text -> f Text
    f Text
    n

    -- | Render 'UserInfo' as @nick!username\@hostname@
    renderUserInfo :: UserInfo -> Text
    renderUserInfo :: UserInfo -> Text
    renderUserInfo (UserInfo Identifier
    a Text
    b Text
    c)
        = Identifier -> Text
    idText Identifier
    a
       Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (if Text -> Bool
    Text.null Text
    b then Text
    "" else Text
    "!" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    b)
       Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (if Text -> Bool
    Text.null Text
    c then Text
    "" else Text
    "@" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    c)

    -- | Split up a hostmask into a nickname, username, and hostname.
    -- The username and hostname might not be defined but are delimited by
    -- a @!@ and @\@@ respectively.
    parseUserInfo :: Text -> UserInfo
    parseUserInfo :: Text -> UserInfo
    parseUserInfo Text
    x = UserInfo
      { userNick :: Identifier
    userNick = Text -> Identifier
    mkId Text
    nick
      , userName :: Text
    userName = Int -> Text -> Text
    Text.drop Int
    1 Text
    user
      , userHost :: Text
    userHost = Int -> Text -> Text
    Text.drop Int
    1 Text
    host
      }
      where
      (Text
    nickuser,Text
    host) = (Char -> Bool) -> Text -> (Text, Text)
    Text.break (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    ==Char
    '@') Text
    x
      (Text
    nick,Text
    user) = (Char -> Bool) -> Text -> (Text, Text)
    Text.break (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    ==Char
    '!') Text
    nickuser