{-# 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 hsotname
-- 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
/= :: UserInfo -> UserInfo -> Bool
$c/= :: UserInfo -> UserInfo -> Bool
== :: UserInfo -> UserInfo -> Bool
$c== :: UserInfo -> UserInfo -> Bool
Eq, 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
readListPrec :: ReadPrec [UserInfo]
$creadListPrec :: ReadPrec [UserInfo]
readPrec :: ReadPrec UserInfo
$creadPrec :: ReadPrec UserInfo
readList :: ReadS [UserInfo]
$creadList :: ReadS [UserInfo]
readsPrec :: Int -> ReadS UserInfo
$creadsPrec :: Int -> ReadS 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
showList :: [UserInfo] -> ShowS
$cshowList :: [UserInfo] -> ShowS
show :: UserInfo -> String
$cshow :: UserInfo -> String
showsPrec :: Int -> UserInfo -> ShowS
$cshowsPrec :: Int -> UserInfo -> ShowS
Show)

-- | Lens into 'userNick' field.
uiNick :: Functor f => (Identifier -> f Identifier) -> UserInfo -> f UserInfo
uiNick :: (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 :: Identifier
userNick = Identifier
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 :: (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 :: Text
userName = Text
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 :: (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 :: Text
userHost = Text
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 :: Identifier -> Text -> Text -> UserInfo
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