uniform-fileio-0.1.5.2: Uniform/TypedFile.hs
----------------------------------------------------------------------
--
-- Module : uniform.TypedFile
-- Copyright : andrew u frank -
--
-- mapping data structures to files typed with an extension
-- write and read quasi type-checked
----------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE InstanceSigs #-}
module Uniform.TypedFile
( module Uniform.TypedFile,
GZip.compress,
GZip.decompress,
EpochTime,
)
where
import qualified Codec.Compression.GZip as GZip
import qualified Data.ByteString.Lazy as L
import qualified Path.IO (ensureDir)
import Uniform.FileIOalgebra (Handle)
import Uniform.FileStatus (EpochTime)
import Uniform.FileStrings
import Uniform.FilenameAlgebra
import Uniform.FilenamePath
import Uniform.Strings
import Uniform.Error
data TypedFile5 a b = TypedFile5 {tpext5 :: Extension}
rdfGraphDebug = False
-- | reads or writes a structured file with the specified parsers or writer
-- the first parameter is the type of file, it is the type of the
-- input data and the returned data
-- the second an arbitrary differentiation
-- to allow two file types with different extension and read
-- the b can be () if no differentiation is desired
class
(FileHandles a) =>
TypedFiles5 a b
where
append5 f = errorT ["TypedFiles - no implementation for append5", showT f]
read5 f = errorT ["TypedFiles - no implementation for read5", showT f]
read6 f = errorT ["TypedFiles - no implementation for read6", showT f]
append6 f = errorT ["TypedFiles - no implementation for append6", showT f]
openHandle6 f = errorT ["TypedFiles - no implementation for openHandle6", showT f]
writeHandle6 f = errorT ["TypedFiles - no implementation for writeHandle6", showT f]
closeHandle6 f = errorT ["TypedFiles - no implementation for closeHandle6", showT f]
-- write5 :: Path Abs Dir -> Path Rel File -> TypedFile5 a b -> a -> ErrIO ()
-- -- write a file, directory is created if not exist
-- -- file, if exist, is replaced
-- write5 fp fn tp ct = do
-- dirx <- Path.IO.ensureDir (unPath fp)
-- write6 (fp </> fn) tp ct
append5 :: Path Abs Dir -> Path Rel File -> TypedFile5 a b -> a -> ErrIO ()
read5 :: Path Abs Dir -> Path Rel File -> TypedFile5 a b -> ErrIO a
write6 :: Path Abs File -> TypedFile5 a b -> a -> ErrIO ()
-- write a file, directory is created if not exist
-- file, if exist, is replaced
write6 fp tp queryText = do
-- when rdfGraphDebug $
putIOwords ["write6", showT fp]
-- let fn2 = fp </> addExt lpX fn (tpext tp) -- :: LegalPathname
let fn2 = setExtension (tpext5 tp) fp
createDirIfMissing' (getParentDir fp) -- add everywhere?
when rdfGraphDebug $
putIOwords
[ "sparql Turtle createDIrIfMissing' ",
showT (getParentDir fp)
]
hand <- openFile2handle fn2 WriteMode
-- when rdfGraphDebug $ putIOwords ["write6", showT fn2]
write2handle hand queryText -- changed for Text not []
closeFile2 hand
openHandle6 :: Path Abs File -> TypedFile5 a b -> ErrIO Handle
-- | create the file and open the handle
-- should attache ".tmp" to extension and when closing
-- rename to correct filename - > transaction completed
writeHandle6 :: Handle -> TypedFile5 a b -> a -> ErrIO ()
closeHandle6 :: Path Abs File -> TypedFile5 a b -> Handle -> ErrIO ()
-- | close the handle - with transaction
append6 :: Path Abs File -> TypedFile5 a b -> a -> ErrIO ()
-- append to the file, with the same methods as in write6
read6 :: Path Abs File -> TypedFile5 a b -> ErrIO a
exist6 :: Path Abs File -> TypedFile5 a b -> ErrIO Bool
-- ^ check whether file exist
exist6 fp tp = do
let fn2 = setExtension (tpext5 tp) fp :: Path Abs File
doesFileExist' fn2
modificationTime6 :: Path Abs File -> TypedFile5 a b -> ErrIO EpochTime
modificationTime6 fp tp = do
let fn2 = setExtension (tpext5 tp) fp :: Path Abs File
t :: EpochTime <- getFileModificationTime fn2
return t
isTyped :: Path Abs File -> TypedFile5 a b -> Bool
-- ^ check if a given file is of the right type (extenions, not mime type)
isTyped fp tp = getExtension fp == typedExtension tp
typedExtension :: TypedFile5 a b -> Extension
-- ^ get the extension back
typedExtension tp = tpext5 tp
makeTyped :: Extension -> TypedFile5 a b
-- make a typed file type, needs type specification!
makeTyped ext = TypedFile5 {tpext5 = ext}
instance TypedFiles5 Text b where
-- file contains a list of lines (text)
-- write5 :: Path Abs Dir -> Path Rel File -> TypedFile5 Text b -> Text -> ErrIO ()
-- write5 :: Path Abs Dir -> Path Rel File -> TypedFile5 Text b -> Text -> ErrIO ()
-- write5 fp fn tp ct = do
-- dirx <- Path.IO.ensureDir (unPath fp)
-- let fn2 = fn <.> tpext5 tp :: Path Rel File
-- writeFile2 (fp </> fn2) ct
-- -- writeFile2 (fp </> (fn <.> (tpext tp) )) . unlines'
-- append5 fp fn tp ct = do
-- dirx <- Path.IO.ensureDir (unPath fp)
-- let fn2 = fn <.> tpext5 tp -- :: Path ar File
-- appendFile2 (fp </> fn2) ct
-- read5 fp fn tp = do
-- let fn2 = fn <.> tpext5 tp
-- readFile2 $ fp </> fn2
append6 fn tp ct = do
let fn2 = setExtension (tpext5 tp) fn
appendFile2 fn2 ct
write6 fn tp ct = do
let fn2 = setExtension (tpext5 tp) fn
hand <- openFile2handle fn2 WriteMode
write2handle hand ct
closeFile2 hand
exist6 fn tp = do
let fn2 = setExtension (tpext5 tp) fn
doesFileExist' fn2
read6 fn tp = do
let fn2 = setExtension (tpext5 tp) fn
readFile2 fn2
instance TypedFiles5 [Text] b where
-- file contains a list of lines (text)
-- mkTypedFile5 = TypedFile5 { tpext5 = Extension "txt"}
-- write5 fp fn tp ct = do
-- dirx <- Path.IO.ensureDir (unPath fp)
-- let fn2 = fn <.> tpext5 tp -- :: Path ar File
-- writeFile2 (fp </> fn2) (unlines' ct)
-- append5 fp fn tp ct = do
-- dirx <- Path.IO.ensureDir (unPath fp)
-- let fn2 = fn <.> tpext5 tp -- :: Path ar File
-- appendFile2 (fp </> fn2) (unlines' ct)
-- read5 fp fn tp = do
-- let fn2 = fn <.> tpext5 tp
-- fmap lines' . readFile2 $ fp </> fn2
append6 fn tp ct = do
let fn2 = setExtension (tpext5 tp) fn
appendFile2 fn2 (unlines' ct)
write6 fn tp ct = do
let fn2 = setExtension (tpext5 tp) fn
hand <- openFile2handle fn2 WriteMode
write2handle hand (unlines' ct)
closeFile2 hand
exist6 fn tp = do
let fn2 = setExtension (tpext5 tp) fn
doesFileExist' fn2
read6 fn tp = do
let fn2 = setExtension (tpext5 tp) fn
fmap lines' . readFile2 $ fn2
data GZip
-- | files with full triples stored as zip
instance TypedFiles5 LazyByteString GZip where
append6 fp tp jsonld = do
when rdfGraphDebug $ putIOwords ["triples append6", showT fp]
let fn2 = setExtension (tpext5 tp) fp
appendFile2 fn2 (GZip.compress jsonld)
openHandle6 fp tp = do
when rdfGraphDebug $ putIOwords ["openHandle6 jsonld"]
let ext = unExtension (tpext5 tp)
let tmpext = Extension (ext <.> "tmp")
let fn2 = setExtension tmpext fp
when rdfGraphDebug $ putIOwords ["openHandle6 jsonld", showT fn2]
createDirIfMissing' (getParentDir fn2) -- add everywhere?
hand <- openFile2handle fn2 WriteMode
-- should create or truncate the file, but not when the dir not exist
--https://hackage.haskell.org/package/base-4.10.0.0/docs/System-IO.html#g:5
when rdfGraphDebug $ putIOwords ["openHandle6 jsonld", showT fn2]
return hand
closeHandle6 fp tp hand = do
-- when rdfGraphDebug $
when rdfGraphDebug $ putIOwords ["closeHandle6 jsonld"]
let ext = unExtension (tpext5 tp)
let tmpext = Extension (ext <.> "tmp")
closeFile2 hand
let fn2 = setExtension tmpext fp
let fn1 = setExtension (tpext5 tp) fp
renameOneFile fn2 fn1
when rdfGraphDebug $ putIOwords ["closeHandle6 jsonld", showT fn2]
return ()
writeHandle6 hand tp jsonld = do
r <- write2handle hand (GZip.compress jsonld)
return r
read6 fp tp = error "read for jsonld is not easy and not required"
-- | the a is the base type
-- which is written on file, b is the type for input and output
class FileHandles a => TypedFiles7 a b where
wrap7 :: a -> b
unwrap7 :: b -> a
class FileHandles a => TypedFiles7a a b where
-- | the 7 have two arguments for path and file
read7 :: Path Abs Dir -> Path Rel File -> TypedFile5 a b -> ErrIO b
write7 :: Path Abs Dir -> Path Rel File -> TypedFile5 a b -> b -> ErrIO ()
-- | the 8 versions have a single argument for path and file
read8 :: Path Abs File -> TypedFile5 a b -> ErrIO b
write8 :: Path Abs File -> TypedFile5 a b -> b -> ErrIO ()
-- write9 :: (TypedFiles7a Text b) => Path Abs File -> b -> ErrIO ()
-- would be useful, but gives AllowAmbiguousTypes error
read9 :: Path Abs File -> TypedFile5 a b -> IO b
write9 :: Path Abs File -> TypedFile5 a b -> b -> IO ()
renameToBak8 :: Path Abs File -> TypedFile5 a b -> ErrIO ()
-- ^ the createDir if missingis implied in the write
instance (TypedFiles7 Text b) => TypedFiles7a Text b where
-- an instance for all what has text or bytestring as underlying rep
-- write7 fp fn tp ct = do
-- write8 (fp </> fn) tp ct
-- read7 fp fn tp = do
-- read8 (fp </> fn) tp
write8 fp tp ct = do
let fn2 = fp <.> tpext5 tp -- :: Path ar File
let parent = getParentDir fn2
createDirIfMissing' parent
writeFile2 fn2 (unwrap7 ct :: Text)
read8 fp tp = do
let fp2 = fp <.> tpext5 tp
ares :: Text <- readFile2 fp2
return . wrap7 $ ares
write9 fp tp ct = do
let fn2 = fp <.> tpext5 tp -- :: Path ar File
let parent = getParentDir fn2
createDirIfMissing parent
writeFile3 fn2 (unwrap7 ct :: Text)
read9 fp tp = do
let fp2 = fp <.> tpext5 tp
ares :: Text <- readFile3 fp2
return . wrap7 $ ares
renameToBak8 fp tp = do
let fp2 = fp <.> tpext5 tp
let fpBak = fp <.> (Extension "bak")
renameOneFile fp2 fpBak
return ()
instance (TypedFiles7 L.ByteString b) => TypedFiles7a L.ByteString b where
-- an instance for all what has text or bytestring as underlying rep
-- write7 fp fn tp ct = do
-- write8 (fp </> fn) tp ct
-- read7 fp fn tp = do
-- read8 (fp </> fn) tp
write8 fp tp ct = do
let fn2 = fp <.> tpext5 tp -- :: Path ar File
let parent = getParentDir fn2
createDirIfMissing' parent
writeFile2 fn2 (unwrap7 ct :: L.ByteString)
read8 fp tp = do
let fp2 = fp <.> tpext5 tp
ares :: L.ByteString <- readFile2 fp2
return . wrap7 $ ares
write9 fp tp ct = do
let fn2 = fp <.> tpext5 tp -- :: Path ar File
let parent = getParentDir fn2
createDirIfMissing parent
writeFile3 fn2 (unwrap7 ct :: L.ByteString)
read9 fp tp = do
let fp2 = fp <.> tpext5 tp
ares :: L.ByteString <- readFile3 fp2
return . wrap7 $ ares
renameToBak8 fp tp = do
let fp2 = fp <.> tpext5 tp
let fpBak = fp <.> (Extension "bak")
renameOneFile fp2 fpBak
return ()