-- |
-- Stability   :  Ultra-Violence
-- Portability :  I'm too young to die
-- Higher-level file patterns. Don't support read/write operations at offsets and handling stat changes as for now.

{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, OverloadedStrings #-}

module Network.NineP.File
	( isDir
	, simpleFile
	, simpleFileBy
	, simpleDirectory
	, rwFile
	, memoryFile
	, memoryDirectory
	) where

import Control.Concurrent.Chan
import Control.Exception
import Control.Monad
import Control.Monad.EmbedIO
import Control.Monad.Trans
import Data.ByteString.Lazy.Char8 (ByteString)
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Bits
import Data.Convertible.Base
import Data.IORef
import Data.StateRef
import Data.Word
import Prelude hiding (read)

import Network.NineP.Error
import Network.NineP.Internal.File

-- |Tests if the file is a directory
isDir :: Word32	-- ^Permissions
	-> Bool
isDir :: Word32 -> Bool
isDir Word32
perms = forall a. Bits a => a -> Int -> Bool
testBit Word32
perms Int
31

simpleRead :: (Monad m, EmbedIO m) => m ByteString -> Word64 -> Word32 -> m ByteString
simpleRead :: forall (m :: * -> *).
(Monad m, EmbedIO m) =>
m ByteString -> Word64 -> Word32 -> m ByteString
simpleRead m ByteString
get Word64
offset Word32
count = do
		ByteString
r <- m ByteString
get
		(forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> ByteString -> ByteString
B.take (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
count) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> ByteString -> ByteString
B.drop (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
offset)) ByteString
r

-- TODO make it offset-aware
simpleWrite :: (Monad m, EmbedIO m) => (ByteString -> m ()) -> Word64 -> ByteString -> m Word32
simpleWrite :: forall (m :: * -> *).
(Monad m, EmbedIO m) =>
(ByteString -> m ()) -> Word64 -> ByteString -> m Word32
simpleWrite ByteString -> m ()
put Word64
offset ByteString
d = case Word64
offset of
	Word64
_ -> do
		()
r <- ByteString -> m ()
put ByteString
d
		(forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ ByteString -> Int64
B.length ByteString
d) ()
r
	--_ -> throwError $ OtherError "can't write at offset"


-- TODO remove
-- |A file that reads and writes using simple user-specified callbacks.
rwFile :: forall m. (EmbedIO m)
		=> String	-- ^File name
		-> Maybe (m ByteString)	-- ^Read handler
		-> Maybe (ByteString -> m ())	-- ^Write handler
		-> NineFile m
rwFile :: forall (m :: * -> *).
EmbedIO m =>
String
-> Maybe (m ByteString) -> Maybe (ByteString -> m ()) -> NineFile m
rwFile String
name Maybe (m ByteString)
rc Maybe (ByteString -> m ())
wc = forall a b (m :: * -> *).
(Monad m, EmbedIO m) =>
String
-> (m a, b -> m ())
-> (a -> ByteString, ByteString -> b)
-> NineFile m
simpleFileBy String
name (forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a b. (a, b) -> a
fst forall (m :: * -> *) a. MonadIO m => (m a, a -> m ())
nulls) forall a. a -> a
id Maybe (m ByteString)
rc, forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a b. (a, b) -> b
snd forall (m :: * -> *) a. MonadIO m => (m a, a -> m ())
nulls) forall a. a -> a
id Maybe (ByteString -> m ())
wc) (forall a. a -> a
id, forall a. a -> a
id)

-- FIXME sane errors
-- |Placeholder source and sink
nulls :: MonadIO m => (m a, a -> m ())
nulls :: forall (m :: * -> *) a. MonadIO m => (m a, a -> m ())
nulls = (forall a e. Exception e => e -> a
throw forall a b. (a -> b) -> a -> b
$ ArithException
Underflow, forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ())

-- |A file that reads from and writes to the supplied 'Ref' instances, with converstion to the appropriate types. See 'Network.NineP.File.Instances', 'Data.Convertible.Instances' and 'Data.StateRef.Instances'. Use '()', if the file is meant to be read-only/write-only.
simpleFile :: forall a b m rr wr. (Monad m, EmbedIO m, ReadRef rr m a, Convertible a ByteString, WriteRef wr m b, Convertible ByteString b)
		=> String	-- ^File name
		-> rr	-- ^Reading function
		-> wr	-- ^Writing function
		-> NineFile m
simpleFile :: forall a b (m :: * -> *) rr wr.
(Monad m, EmbedIO m, ReadRef rr m a, Convertible a ByteString,
 WriteRef wr m b, Convertible ByteString b) =>
String -> rr -> wr -> NineFile m
simpleFile String
name rr
rr wr
wr = forall a b (m :: * -> *).
(Monad m, EmbedIO m) =>
String
-> (m a, b -> m ())
-> (a -> ByteString, ByteString -> b)
-> NineFile m
simpleFileBy String
name (forall sr (m :: * -> *) a. ReadRef sr m a => sr -> m a
readReference rr
rr, forall sr (m :: * -> *) a. WriteRef sr m a => sr -> a -> m ()
writeReference wr
wr) (forall a b. Convertible a b => a -> b
convert, forall a b. Convertible a b => a -> b
convert)

-- Shouldn't we make it simpler?
-- |Typeclass-free version of 'simpleFile'.
simpleFileBy :: forall a b m. (Monad m, EmbedIO m)
		=> String	-- ^File name
		-> (m a, b -> m ())	-- ^Reading and writing handle
		-> (a -> ByteString, ByteString -> b)	-- ^Type conversion handles
		-> NineFile m
simpleFileBy :: forall a b (m :: * -> *).
(Monad m, EmbedIO m) =>
String
-> (m a, b -> m ())
-> (a -> ByteString, ByteString -> b)
-> NineFile m
simpleFileBy String
name (m a
rd, b -> m ()
wr) (a -> ByteString
rdc, ByteString -> b
wrc) = (forall (m :: * -> *). (Monad m, EmbedIO m) => String -> NineFile m
boringFile String
name :: NineFile m) {
		read :: Word64 -> Word32 -> m ByteString
read = forall (m :: * -> *).
(Monad m, EmbedIO m) =>
m ByteString -> Word64 -> Word32 -> m ByteString
simpleRead forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM a -> ByteString
rdc forall a b. (a -> b) -> a -> b
$ m a
rd,
		write :: Word64 -> ByteString -> m Word32
write = forall (m :: * -> *).
(Monad m, EmbedIO m) =>
(ByteString -> m ()) -> Word64 -> ByteString -> m Word32
simpleWrite forall a b. (a -> b) -> a -> b
$ b -> m ()
wr forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> b
wrc 
	}

-- |A file that stores its contents in the form of 'IORef' 'ByteString'
memoryFile :: forall m. (Monad m, EmbedIO m)
	=> String	-- ^File name
	-> IO (NineFile m)
memoryFile :: forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String -> IO (NineFile m)
memoryFile String
name = do
	IORef ByteString
c <- forall a. a -> IO (IORef a)
newIORef ByteString
"" :: IO (IORef ByteString)
	forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b (m :: * -> *).
(Monad m, EmbedIO m) =>
String
-> (m a, b -> m ())
-> (a -> ByteString, ByteString -> b)
-> NineFile m
simpleFileBy String
name (
			forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef ByteString
c,
			forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. IORef a -> a -> IO ()
writeIORef IORef ByteString
c
		) (forall a. a -> a
id, forall a. a -> a
id)

-- |A directory that stores its contents in the form of 'IORef [(String, NineFile m)]'
simpleDirectory :: forall m. (Monad m, EmbedIO m)
			=> String	-- ^File name
			-> (String -> m (NineFile m))	-- ^A function for creating new files
			-> (String -> m (NineFile m))	-- ^A function for creating new directories
			-> IO (NineFile m, IORef [(String, NineFile m)])
simpleDirectory :: forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String
-> (String -> m (NineFile m))
-> (String -> m (NineFile m))
-> IO (NineFile m, IORef [(String, NineFile m)])
simpleDirectory String
name String -> m (NineFile m)
newfile String -> m (NineFile m)
newdir = do
	IORef [(String, NineFile m)]
files <- forall a. a -> IO (IORef a)
newIORef [] :: IO (IORef [(String, NineFile m)])
	forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ (\NineFile m
f -> (NineFile m
f, IORef [(String, NineFile m)]
files)) forall a b. (a -> b) -> a -> b
$ (forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String -> [(String, NineFile m)] -> NineFile m
boringDir String
name [] :: NineFile m) {
		create :: String -> Word32 -> m (NineFile m)
create = \String
name Word32
perms -> do
			NineFile m
nf <- (if Word32 -> Bool
isDir Word32
perms then String -> m (NineFile m)
newdir else String -> m (NineFile m)
newfile) String
name
			let nelem :: (String, NineFile m)
nelem = (String
name, NineFile m
nf)
			forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef [(String, NineFile m)]
files (\[(String, NineFile m)]
l -> ((String, NineFile m)
nelemforall a. a -> [a] -> [a]
:[(String, NineFile m)]
l, ()))
			forall (m :: * -> *) a. Monad m => a -> m a
return NineFile m
nf,
		getFiles :: m [NineFile m]
getFiles = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd) forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef [(String, NineFile m)]
files,
		descend :: String -> m (NineFile m)
descend = \String
name -> do
			[(String, NineFile m)]
d <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef [(String, NineFile m)]
files
			forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a e. Exception e => e -> a
throw forall a b. (a -> b) -> a -> b
$ String -> NineError
ENoFile String
name) (forall (m :: * -> *) a. Monad m => a -> m a
return) forall a b. (a -> b) -> a -> b
$ forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
name [(String, NineFile m)]
d
	}

-- |A composition of a 'simpleDirectory' and a 'memoryFile'
memoryDirectory :: forall m. (Monad m, EmbedIO m)
			=> String	-- ^File name
			-> IO (NineFile m)
memoryDirectory :: forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String -> IO (NineFile m)
memoryDirectory String
name = forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM forall a b. (a, b) -> a
fst forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String
-> (String -> m (NineFile m))
-> (String -> m (NineFile m))
-> IO (NineFile m, IORef [(String, NineFile m)])
simpleDirectory String
name (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String -> IO (NineFile m)
memoryFile) (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
(Monad m, EmbedIO m) =>
String -> IO (NineFile m)
memoryDirectory)