packages feed

mahoro-0.1.2: Parsers/Post.hs

-- | Parsers/Post.hs
-- A module which contain functions for parse and show thread post.

module Parsers.Post (
    Post(..),
    PostSettings(..),
    parsePost,
    showPost
) where

import Config

import Text.HTML.TagSoup
import Data.List.Utils

import Data.List


-- | Post structure.
data Post = Post
    { postTitle  :: Maybe String
    , postAuthor :: Maybe String
    , postEmail  :: Maybe String
    , postTrip   :: Maybe String
    , postTime   :: String
    , postImg    :: Maybe Img
    , postBody   :: String
    } deriving Show

-- | Img structure.
data Img = Img
    { imgProperties :: String
    , imgURL        :: String
    , thumbURL      :: String
    } deriving Show

-- | Settings for post parser.
data PostSettings = PostSettings
    { sPostSplitter       :: String
    , sPostNumberTag      :: String
    , sTitleTag           :: (String, String)
    , sAuthorTag          :: (String, String)
    , sTripTag            :: String
    , sTimeCloseTag       :: String
    , sPropertiesTag      :: String
    , sPropertiesCloseTag :: String
    , sImgTag             :: String
    , sThumbTag           :: String
    , sBodyWalkAll        :: Bool
    , sBodyTag            :: String
    , sBodyCloseTag       :: String
    }


-- | Parse entire post.
parsePost :: PostSettings -> [Tag] -> Post
parsePost s tags = fst $
    parseTitle Post tags >==
    parseAuthorAndEmail  >==
    parseTrip            >==
    parseTime            >==
    parseImg             >==
    parseBody
  where
    (f, rest) >== f2 = f2 f rest
    -----------------------------------------------
    parseTitle f tags =
      let r = dropWhile (~/= (fst $ sTitleTag s)) tags
          rest = if null r
                 then dropWhile (~/= (snd $ sTitleTag s)) tags
                 else r
          (title, newRest) =
            if null rest
            then (Nothing, tags)
            else let tag = head $ tail rest
                 in if isTagText tag
                    then (Just $ fromTagText tag, rest)
                    else (Nothing, tags)
      in (f title, newRest)
    -----------------------------------------------
    parseAuthorAndEmail f tags =
      let r = dropWhile (~/= (fst $ sAuthorTag s)) tags
          rest = if null r
                 then dropWhile (~/= (snd $ sAuthorTag s)) tags
                 else r
          (author, email, newRest) =
            if null rest
            then (Nothing, Nothing, tags)
            else let tag = head $ tail rest
                     isHasEmail = tag ~== "<a href>"
                     email = if isHasEmail
                             then Just $ fromAttrib "href" tag
                             else Nothing
                     authorTag = if isHasEmail
                                 then last $ take 3 rest
                                 else tag
                     author = if isTagText authorTag
                              then Just $ fromTagText authorTag
                              else Nothing
                 in (author, email, rest)
      in (f author email, newRest)
    -----------------------------------------------
    parseTrip f tags =
      let rest = dropWhile (~/= sTripTag s) tags
          (trip, newRest) =
            if null rest
            then (Nothing, tags)
            else let tag = head $ tail rest
                 in if isTagText tag
                    then (Just $ fromTagText tag, rest)
                    else (Nothing, rest)
      in (f trip, newRest)
    -----------------------------------------------
    -- required
    parseTime f tags =
      let (t, rest) = break (~== sTimeCloseTag s) tags
          time = fromTagText $ last t
      in (f time, rest)
    -----------------------------------------------
    parseImg f t =
      let r = dropWhile (~/= sPropertiesTag s) t
          rest = if null r
                 then dropWhile (~/= sPropertiesTag s) tags
                 else r
          (img, newRest) =
            if null rest
            then (Nothing, t)
            else let (properties, rest') =
                       break (~== sPropertiesCloseTag s) rest
                     imgU = fromAttrib "href" $ head $
                              dropWhile (~/= sImgTag s) rest'
                     thumbU = fromAttrib "src" $ head $
                                dropWhile (~/= sThumbTag s) rest'
                 in (Just $ Img { imgProperties = innerText $ properties
                                , imgURL = imgU
                                , thumbURL = thumbU
                                }, rest')
      in (f img, newRest)
    -----------------------------------------------
    -- required
    parseBody f tags =
      let bodyTags =
            if sBodyWalkAll s
            then reverse $ dropWhile (~/= sBodyCloseTag s) $
                   reverse $ dropWhile (~/= sBodyTag s) tags
            else takeWhile (~/= sBodyCloseTag s) $
                   dropWhile (~/= sBodyTag s) tags
          body = innerText $ map parseWakabaMark bodyTags
      in (f  body, [])
      where
        -- FIXME: parse it correctly!!!
        parseWakabaMark tag | tag ~== "<br>" = TagText "\n"
        parseWakabaMark tag | tag ~== "<p>"  = TagText "\n"
        parseWakabaMark tag | tag ~== "</p>" = TagText "\n"
        parseWakabaMark tag = tag


-- | Show post.
showPost :: Chan -> (Integer, Post) -> String
showPost chan (n, p) =
    let title = maybe "" id $ postTitle p
        author = maybe "" id $ postAuthor p
        email = maybe "" id $ postEmail p
        trip = maybe "" id $ postTrip p
        time = postTime p
        number = show n
        (properties, imgU, thumbU) =
          maybe ("", "", "")
            (\i -> (imgProperties i
                  ,fixURL (imgURL i)
                  ,fixURL (thumbURL i))) $ postImg p
        body = postBody p

        -- formating
        format = "<title>_<author>_<trip>_<email>_<time>_#<number>|\
                 \<img>|\
                 \<body>"
        formating = [ ("<body>",   body)
                    , ("<title>",  title)
                    , ("<author>", author)
                    , ("<email>",  email)
                    , ("<trip>",   trip)
                    , ("<time>",   time)
                    , ("<number>", number)
                    , ("<size>",   properties)
                    , ("<img>",    imgU)
                    , ("<thumb>",  thumbU)
                    , ("|",        "\n")
                    , ("_",        " ")
                    ]
    in foldr (\(f, r) s -> replace f r s) format formating
  where
    fixURL u = if "http://" `isPrefixOf` u
                 then u
                 else (curl chan)++u