pinboard-0.2: src/Pinboard/ApiTypes.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module : Pinboard.ApiTypes
-- Copyright : (c) Jon Schoning, 2015
-- Maintainer : jonschoning@gmail.com
-- Stability : experimental
-- Portability : POSIX
module Pinboard.ApiTypes where
import Prelude hiding (words)
import Control.Applicative ((<$>), (<*>), (<|>))
import Data.Aeson (FromJSON (parseJSON), Value (String, Object), ( .:))
import Data.Aeson.Types (Parser)
import Data.HashMap.Strict (HashMap, member, toList)
import Data.Text (Text, words, unpack)
import Data.Time (UTCTime)
import Data.Time.Calendar (Day)
import Data.Time.Format (readTime)
import System.Locale (defaultTimeLocale)
import qualified Data.HashMap.Strict as HM
-- * Posts
data Posts = Posts {
postsDate :: UTCTime
, postsUser :: Text
, posts :: [Post]
} deriving (Show, Eq)
instance FromJSON Posts where
parseJSON (Object o) =
Posts <$> o .: "date"
<*> o .: "user"
<*> o .: "posts"
parseJSON _ = error "bad parse"
data Post = Post {
postHref :: Text
, postDescription :: Text
, postExtended :: Text
, postMeta :: Text
, postHash :: Text
, postTime :: UTCTime
, postShared :: Bool
, postToread :: Bool
, postTags :: [Text]
} deriving (Show, Eq)
instance FromJSON Post where
parseJSON (Object o) =
Post <$> o .: "href"
<*> o .: "description"
<*> o .: "extended"
<*> o .: "meta"
<*> o .: "hash"
<*> o .: "time"
<*> (boolFromYesNo <$> o .: "shared")
<*> (boolFromYesNo <$> o .: "toread")
<*> (words <$> o .: "tags")
parseJSON _ = error "bad parse"
boolFromYesNo :: Text -> Bool
boolFromYesNo "yes" = True
boolFromYesNo _ = False
data PostDates = PostDates {
postDatesUser :: Text
, postDatesTag :: Text
, postDatesCount :: [DateCount]
} deriving (Show, Eq)
instance FromJSON PostDates where
parseJSON (Object o) =
PostDates <$> o .: "user"
<*> o .: "tag"
<*> (parseDates <$> o .: "dates")
where
parseDates :: Value -> [DateCount]
parseDates (Object o')= do
(dateStr, String countStr) <- toList o'
return (read (unpack dateStr), read (unpack countStr))
parseDates _ = []
parseJSON _ = error "bad parse"
type DateCount = (Day, Int)
-- * Notes
data NoteList = NoteList {
noteListCount :: Int
, noteListItems :: [NoteListItem]
} deriving (Show, Eq)
instance FromJSON NoteList where
parseJSON (Object o) =
NoteList <$> o .: "count"
<*> o .: "notes"
parseJSON _ = error "bad parse"
data NoteListItem = NoteListItem {
noteListItemId :: Text
, noteListItemHash :: Text
, noteListItemTitle :: Text
, noteListItemLength :: Int
, noteListItemCreatedAt :: UTCTime
, noteListItemUpdatedAt :: UTCTime
} deriving (Show, Eq)
instance FromJSON NoteListItem where
parseJSON (Object o) =
NoteListItem <$> o .: "id"
<*> o .: "hash"
<*> o .: "title"
<*> (read <$> (o .: "length"))
<*> (readNoteTime <$> o .: "created_at")
<*> (readNoteTime <$> o .: "updated_at")
parseJSON _ = error "bad parse"
data Note = Note {
noteId :: Text
, noteHash :: Text
, noteTitle :: Text
, noteText :: Text
, noteLength :: Int
, noteCreatedAt :: UTCTime
, noteUpdatedAt :: UTCTime
} deriving (Show, Eq)
instance FromJSON Note where
parseJSON (Object o) =
Note <$> o .: "id"
<*> o .: "hash"
<*> o .: "title"
<*> o .: "text"
<*> o .: "length"
<*> (readNoteTime <$> o .: "created_at")
<*> (readNoteTime <$> o .: "updated_at")
parseJSON _ = error "bad parse"
readNoteTime :: String -> UTCTime
readNoteTime = readTime defaultTimeLocale "%F %T"
-- * Tags
type TagMap = HashMap Text Int
newtype JsonTagMap = ToJsonTagMap {fromJsonTagMap :: TagMap}
deriving (Show, Eq)
instance FromJSON JsonTagMap where
parseJSON = return . toTags
where toTags (Object o) = ToJsonTagMap $ HM.map (\(String s)-> read (unpack s)) o
toTags _ = error "bad parse"
data Suggested = Popular [Text]
| Recommended [Text]
deriving (Show, Eq)
instance FromJSON Suggested where
parseJSON (Object o)
| member "popular" o = Popular <$> (o .: "popular")
| member "recommended" o = Recommended <$> (o .: "recommended")
| otherwise = error "bad parse"
parseJSON _ = error "bad parse"
-- * Scalars
newtype DoneResult = ToDoneResult {fromDoneResult :: ()}
deriving (Show, Eq)
instance FromJSON DoneResult where
parseJSON (Object o) = parseDone =<< (o .: "result" <|> o .: "result_code")
where
parseDone :: Text -> Parser DoneResult
parseDone "done" = return $ ToDoneResult ()
parseDone msg = ( fail . unpack ) msg
parseJSON _ = error "bad parse"
newtype TextResult = ToTextResult {fromTextResult :: Text}
deriving (Show, Eq)
instance FromJSON TextResult where
parseJSON (Object o) = ToTextResult <$> (o .: "result")
parseJSON _ = error "bad parse"
newtype UpdateTime = ToUpdateTime {fromUpdateTime :: UTCTime}
deriving (Show, Eq)
instance FromJSON UpdateTime where
parseJSON (Object o) = ToUpdateTime <$> (o .: "update_time")
parseJSON _ = error "bad parse"
-- * Aliases
-- | as defined by RFC 3986. Allowed schemes are http, https, javascript, mailto, ftp and file. The Safari-specific feed scheme is allowed but will be treated as a synonym for http.
type Url = Text
-- | up to 255 characters long
type Description = Text
-- | up to 65536 characters long. Any URLs will be auto-linkified when displayed.
type Extended = Text
-- | up to 255 characters. May not contain commas or whitespace.
type Tag = Text
type Old = Tag
type New = Tag
type Count = Int
type NumResults = Int
type StartOffset = Int
type Shared = Bool
type Replace = Bool
type ToRead = Bool
-- | UTC date in this format: 2010-12-11. Same range as datetime above
type Date = Day
-- | UTC timestamp in this format: 2010-12-11T19:48:02Z. Valid date range is Jan 1, 1 AD to January 1, 2100 (but see note below about future timestamps).
type DateTime = UTCTime
type FromDateTime = DateTime
type ToDateTime = DateTime
type Meta = Int
type NoteId = Text