{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Network.Beeminder ( -- * API calls user , goal, allGoals , createGoal , points , createPoint , createPointNotify , createPoints, createPointsNotify , updatePoint , deletePoint -- * The Beeminder monad , Beeminder , Token , runBeeminder -- * Foo bar , UserGoals(..) , User(..) , Point(..) , Goal(..) -- UserGoals stuff , Burner(..), LevelOfGoalDetail(..) -- Goal stuff , TimeFrame(..), Aggregate(..), Direction(..), Behavior(..), Target(..), GoalType(..) -- * Lenses , HasID(..), HasUpdatedAt(..), HasName(..), HasTimezone(..), HasUsername(..), HasGoals(..), HasGoalsFilter(..), HasLevelOfDetail(..) , HasPointCount(..), HasTimestamp(..), HasValue(..), HasComment(..), HasRequestID(..), HasGoal(..), HasPointRequest(..), HasPointRequests(..) , HasGetPoints(..), HasTitle(..), HasType(..), HasTarget(..), HasBehavior(..), HasPanic(..) -- * Utilities , now , gType ) where import Control.Applicative import Control.Monad.Base import Control.Monad.Reader import Control.Monad.Trans.Control import Control.Monad.Trans.Maybe import Control.Monad.Trans.Resource import Data.Aeson import Data.Conduit import Data.Default.Class import Network.Beeminder.Internal hiding (allGoals, createGoal, createPoint, createPointNotify, createPoints, createPointsNotify, deletePoint, goal, points, updatePoint, user) import qualified Network.Beeminder.Internal as Internal import Network.HTTP.Conduit data BeeminderEnvironment = BeeminderEnvironment { token :: Token , manager :: Manager } type Beeminder_ = MaybeT (ReaderT BeeminderEnvironment (ResourceT IO)) newtype Beeminder a = Beeminder { unBeeminder :: Beeminder_ a } deriving (Functor, Applicative, Monad, MonadIO, MonadReader BeeminderEnvironment, MonadThrow, MonadResource, MonadBase IO) -- The following instance (and the "deriving" clause for MonadThrow, -- MonadUnsafeIO, MonadResource, and MonadBase IO) were copied basically -- verbatim from the "dgs" package, and even there they were written just "by -- typechecking" rather than with some deep understanding of what's happening. -- So it wouldn't surprise me if there's bugs here. instance MonadBaseControl IO Beeminder where type StM Beeminder a = StM Beeminder_ a liftBaseWith f = Beeminder (liftBaseWith (\g -> f (\(Beeminder m) -> g m))) restoreM v = Beeminder (restoreM v) -- | Run a beeminder computation with the given authentication token, -- possibly returning a result. runBeeminder :: Token -> Beeminder a -> IO (Maybe a) runBeeminder t m = do man <- newManager tlsManagerSettings runResourceT (runReaderT (runMaybeT (unBeeminder m)) BeeminderEnvironment { token = t, manager = man }) -- | Turn a raw operation taking a token and returning a 'Request' -- into a nicely encapsulated action in the 'Beeminder' monad. externalize :: FromJSON a => (Token -> params -> Request) -> params -> Beeminder a externalize f p = do BeeminderEnvironment { token = t, manager = m } <- ask r <- httpLbs (f t p) {responseTimeout = Nothing} m Beeminder . MaybeT . return . decode . responseBody $ r user :: UserParameters -> Beeminder User goal :: GoalParameters -> Beeminder Goal allGoals :: AllGoalsParameters -> Beeminder [Goal] createGoal :: CreateGoalParameters -> Beeminder Goal points :: PointsParameters -> Beeminder [Point] createPoint , createPointNotify :: CreatePointParameters -> Beeminder Point createPoints, createPointsNotify :: CreatePointsParameters -> Beeminder [Point] updatePoint :: UpdatePointParameters -> Beeminder Point deletePoint :: DeletePointParameters -> Beeminder Point user = externalize Internal.user goal = externalize Internal.goal allGoals = externalize Internal.allGoals createGoal = externalize Internal.createGoal points = externalize Internal.points createPoint = externalize Internal.createPoint createPointNotify = externalize Internal.createPointNotify createPoints = externalize Internal.createPoints createPointsNotify = externalize Internal.createPointsNotify updatePoint = externalize Internal.updatePoint deletePoint = externalize Internal.deletePoint