packages feed

htsn-import-0.0.6: src/TSN/XmlImport.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}

-- | Definition of the XmlImport class.
--
module TSN.XmlImport (
  XmlImport(..),
  XmlImportFk(..),
  XmlImportFkTeams(..) )
where

-- System imports.
import Database.Groundhog (
  AutoKey,
  DefaultKey,
  insert,
  insert_,
  insertByAll )
import Database.Groundhog.Core ( PersistBackend, PersistEntity )


-- Local imports.
import TSN.Team ( Team(..) )
import Xml (
  Child(..),
  FromXml(..),
  FromXmlFk(..),
  FromXmlFkTeams(..),
  ToDb(..) )


-- | In Groundhog, there is a typeclass of things you can insert into
--   the database. What we usually have, though, is an XML
--   representation of something that has a Groundhog analogue that we
--   could insert into the database. It would be real nice if we could
--   just insert the XML thing and not have to convert back and
--   forth. That's what the 'XmlImport' class lets you do.
--
--   Moreover, there is a contraint on the class that the type must
--   also be a member of the 'FromXml' class. This allows us to define
--   default implementations of \"insert me\" generically. Given any
--   XML thing that can be converted to a database thing, we just do
--   the conversion and then insert normally (however Groundhog would
--   do it).
--
class (FromXml a, PersistEntity (Db a)) => XmlImport a where
  -- | This is similar to the signature for Groundhog's 'insert'
  --   function, except the 'AutoKey' we return is for our 'Db'
  --   counterpart.
  insert_xml :: (PersistBackend m) => a ->  m (AutoKey (Db a))
  insert_xml = insert . from_xml

  -- | Identical to 'insert_xml', except it doesn't return anything.
  insert_xml_ :: (PersistBackend m) => a ->  m ()
  insert_xml_ = insert_ . from_xml

  -- | Same rationale as 'insert_xml', except it uses 'insertByAll'.
  insertByAll_xml :: (PersistBackend m)
                  => a
                  -> m ( Either (AutoKey (Db a)) (AutoKey (Db a)) )
  insertByAll_xml = insertByAll . from_xml


  -- | Try to insert the given object and get its primary key
  --   back. Or, if there's a unique constraint violation, get the
  --   primary key of the unique thing already present.
  --
  --   Note: we can switch to using fmap here as soon as Functor is a
  --   superclass of Monad (PersistBackend is a Monad).
  --
  insert_xml_or_select :: (PersistBackend m)
                       => a
                       -> m (AutoKey (Db a))
  insert_xml_or_select x = do
    tmp <- insertByAll_xml x
    return $ (either id id) tmp



-- | A total copy of 'XmlImport' for instances of 'FromXmlFk'.
--
class (Child a, FromXmlFk a, PersistEntity (Db a)) => XmlImportFk a where
  insert_xml_fk :: (PersistBackend m)
                => DefaultKey (Parent a)
                -> a
                ->  m (AutoKey (Db a))
  insert_xml_fk fk x = insert $ from_xml_fk fk x

  insert_xml_fk_ :: (PersistBackend m) => DefaultKey (Parent a) -> a -> m ()
  insert_xml_fk_ fk x = insert_ $ from_xml_fk fk x

  insertByAll_xml_fk :: (PersistBackend m)
                     => DefaultKey (Parent a)
                     -> a
                     -> m ( Either (AutoKey (Db a)) (AutoKey (Db a)) )
  insertByAll_xml_fk fk x = insertByAll $ from_xml_fk fk x

  insert_xml_or_select_fk :: (PersistBackend m)
                          => DefaultKey (Parent a)
                          -> a
                          -> m (AutoKey (Db a))
  insert_xml_or_select_fk fk x = do
    tmp <- insertByAll_xml_fk fk x
    return $ (either id id) tmp


-- | A total copy of 'XmlImport' for instances of 'FromXmlFkTeams'.
--   This is a lot of duplicated boilerplate, but you don't have to
--   think about it usually. What you're really worried about is that
--   the dbimport code is understandable, and having these convenience
--   classes makes the import much simpler since you don't have to do
--   these conversions on-the-fly.
--
class (Child a,
       FromXmlFkTeams a,
       PersistEntity (Db a))
       => XmlImportFkTeams a where
  insert_xml_fk_teams :: (PersistBackend m)
                      => DefaultKey (Parent a)
                      -> DefaultKey Team -- ^ Away team FK
                      -> DefaultKey Team -- ^ Home team FK
                      -> a
                      ->  m (AutoKey (Db a))
  insert_xml_fk_teams fk fk_away fk_home x =
    insert $ from_xml_fk_teams fk fk_away fk_home x

  insert_xml_fk_teams_ :: (PersistBackend m)
                       => DefaultKey (Parent a)
                       -> DefaultKey Team
                       -> DefaultKey Team
                       -> a
                       -> m ()
  insert_xml_fk_teams_ fk fk_away fk_home x =
    insert_ $ from_xml_fk_teams fk fk_away fk_home x

  insertByAll_xml_fk_teams :: (PersistBackend m)
                           => DefaultKey (Parent a)
                           -> DefaultKey Team
                           -> DefaultKey Team
                           -> a
                           -> m ( Either (AutoKey (Db a)) (AutoKey (Db a)) )
  insertByAll_xml_fk_teams fk fk_away fk_home x =
    insertByAll $ from_xml_fk_teams fk fk_away fk_home x

  insert_xml_or_select_fk_teams :: (PersistBackend m)
                                => DefaultKey (Parent a)
                                -> DefaultKey Team
                                -> DefaultKey Team
                                -> a
                                -> m (AutoKey (Db a))
  insert_xml_or_select_fk_teams fk fk_away fk_home x = do
    tmp <- insertByAll_xml_fk_teams fk fk_away fk_home x
    return $ (either id id) tmp