packages feed

kit-0.2: Kit/Repository.hs

{-

  Exposes the repositories.
  Allows you to:
    * copy down kit packages (getKit)
    * read kit specs (getKitSpec)
-}

module Kit.Repository (
    getKit,
    getKitSpec,
    webRepo,
    fileRepo,
    KitRepository
  ) where
    
  import Kit.Spec
  import Kit.Kit
  import Kit.Util
  import Data.List
  import Network.HTTP
  import Network.URI
  import Network.BufferType
  import Data.Maybe
  import Data.Traversable
  import Control.Monad
  import Control.Monad.Trans
  import Debug.Trace
  import Text.JSON
  import Kit.JSON
  import qualified Data.ByteString as BS

  data KitRepository = KitRepository {
    repoSave :: String -> FilePath -> IO (Maybe ()),
    repoRead :: String -> IO (Maybe String)
  }
  
  getKit :: KitRepository -> Kit -> FilePath -> IO (Maybe ())
  getKit kr k = repoSave kr (kitPackagePath k)
  
  getKitSpec :: KitRepository -> Kit -> KitIO KitSpec
  getKitSpec kr k = do
    mbKitStuff <- liftIO $ repoRead kr (kitSpecPath k)
    maybe (kitError $ "Missing " ++ kitFileName k) f mbKitStuff
    where f contents = maybeToKitIO ("Invalid KitSpec file for " ++ kitFileName k) $ case (decode contents) of
                          Ok a -> Just a
                          Error _ -> Nothing
  
  webRepo :: String -> KitRepository
  webRepo baseUrl = KitRepository save read where
    save = download 
    read = getBody

  fileRepo :: String -> KitRepository
  fileRepo baseDir = KitRepository save read where
    save src destPath = Just <$> copyFile (baseDir </> src) destPath
    read path = let file = (baseDir </> path) in do
      exists <- doesFileExist file
      sequenceA $ justTrue exists $ readFile file
      
  -- private!
  baseKitPath :: Kit -> String
  baseKitPath k = joinS ["kits", kitName k, kitVersion k] "/"
    where joinS xs x = foldl1 (++) $ intersperse x xs 
  
  kitPackagePath k = baseKitPath k ++ "/" ++ kitFileName k ++ ".tar.gz"
  kitSpecPath k = baseKitPath k ++ "/" ++ "KitSpec"
  
  getBody :: BufferType a => HStream a => String -> IO (Maybe a)
  getBody path = let
      request = defaultGETRequest_ . fromJust . parseURI
      checkResponse r = justTrue (rspCode r == (2,0,0)) r
      leftMaybe = either (const Nothing) Just 
    in do
      rr <- Network.HTTP.simpleHTTP $ request path
      return $ fmap rspBody $ leftMaybe rr
  
  download :: String -> FilePath -> IO (Maybe ())
  download url destination = do
      body <- getBody url
      sequenceA $ fmap (BS.writeFile destination) body