0 | module Control.RIO.Mock.File
2 | import Control.RIO.App
3 | import Control.RIO.File
17 | record Ctxt k v where
19 | start : SnocList (k,v)
24 | unFocus : Ctxt k v -> v -> List (k,v)
25 | unFocus (MkCtxt sp n ps) x = sp <>> ((n,x) :: ps)
28 | focus : Eq k => k -> List (k,v) -> Maybe (Ctxt k v, v)
32 | go : SnocList (k,v) -> List (k,v) -> Maybe (Ctxt k v, v)
34 | go sx (x :: xs) = case fst x == key of
35 | True => Just (MkCtxt sx (fst x) xs, snd x)
36 | False => go (sx :< x) xs
44 | data MockFile : Type where
45 | Regular : String -> MockFile
46 | Stream : Stream String -> MockFile
49 | record MockDir where
51 | content : List (Body, Either MockFile MockDir)
55 | AFile = Either MockFile MockDir
58 | data Focus : Type where
62 | -> SnocList (Ctxt Body AFile)
66 | -> SnocList (Ctxt Body AFile)
70 | data PCFocus : Type where
73 | -> (parendDir : MockDir)
74 | -> (context : SnocList (Ctxt Body AFile))
76 | Exists : Focus -> PCFocus
78 | selfOrParent : Path t -> Path t
79 | selfOrParent fp = maybe fp fst $
split fp
82 | mkdirFocus : MockDir -> Path Abs -> Maybe Focus
83 | mkdirFocus (MkMD ps) (PAbs u sx) = go [<] ps (sx <>> [])
87 | SnocList (Ctxt Body AFile)
88 | -> List (Body,AFile)
91 | go sx ps [] = Just $
DirF (MkMD ps) sx
92 | go sx ps (x :: xs) = case focus x ps of
94 | let c := MkCtxt Lin x Nil
95 | in go (sx :< c) [] xs
96 | Just (c, Left f) => case xs of
97 | Nil => Just $
FileF f c sx
99 | Just (c, Right $
MkMD ps2) => go (sx :< c) ps2 xs
102 | dirFocus : MockDir -> Path Abs -> Maybe Focus
103 | dirFocus (MkMD ps) (PAbs u sx) = go [<] ps (sx <>> [])
107 | SnocList (Ctxt Body AFile)
108 | -> List (Body,AFile)
111 | go sx ps [] = Just $
DirF (MkMD ps) sx
112 | go sx ps (x :: xs) = case focus x ps of
114 | Just (c, Left f) => case xs of
115 | Nil => Just $
FileF f c sx
117 | Just (c, Right $
MkMD ps2) => go (sx :< c) ps2 xs
121 | -> SnocList (Ctxt Body AFile)
123 | unDirFocus' xs [<] = MkMD xs
124 | unDirFocus' xs (sx :< x) = unDirFocus' (unFocus x . Right $
MkMD xs) sx
127 | unDirFocus : Focus -> MockDir
128 | unDirFocus (FileF x c sx) = unDirFocus' (unFocus c (Left x)) sx
129 | unDirFocus (DirF x sx) = unDirFocus' x.content sx
133 | record MockFS where
134 | constructor MkMockFS
138 | absPath : MockFS -> Path t -> Path Abs
139 | absPath fs (PAbs u sx) = PAbs u sx
140 | absPath fs (PRel sx) = fs.curDir </> PRel sx
143 | fsFocus : MockFS -> Path t -> Maybe Focus
144 | fsFocus fs = dirFocus fs.root . absPath fs
146 | pcFocus : MockFS -> Path t -> Maybe PCFocus
147 | pcFocus fs fp = case fsFocus fs fp of
148 | Just f => Just (Exists f)
149 | Nothing => case split fp of
150 | Just (p,c) => case fsFocus fs p of
151 | Just (DirF d sx) => Just $
Parent c d sx
156 | exists : Path t -> MockFS -> Bool
157 | exists fp fs = isJust $
fsFocus fs fp
160 | mkDir : Path t -> MockFS -> Either FileErr MockFS
161 | mkDir fp fs = case pcFocus fs fp of
162 | Just (Parent c d sx) =>
163 | let empty := (c, Right $
MkMD [])
164 | dir := unDirFocus' (empty :: d.content) sx
165 | in Right $
{root := dir} fs
166 | Just (Exists _) => Left (MkDir fp FileExists)
167 | Nothing => Left (MkDir (selfOrParent fp) FileNotFound)
170 | mkDirP : Path t -> MockFS -> Either FileErr MockFS
171 | mkDirP fp fs = case mkdirFocus fs.root (absPath fs fp) of
172 | Just (DirF x sx) => Right $
{root := unDirFocus' x.content sx} fs
173 | Just (FileF {}) => Left (MkDir fp FileExists)
174 | Nothing => Left (MkDir fp FileExists)
179 | -> (String -> String)
180 | -> MockFS -> Either FileErr MockFS
181 | writeImpl fp s f fs = case pcFocus fs $
toPath fp of
182 | Just (Parent c d sx) =>
183 | let empty := (c, Left $
Regular s)
184 | dir := unDirFocus' (empty :: d.content) sx
185 | in Right $
{root := dir} fs
186 | Just (Exists (FileF (Regular x) d sx)) =>
187 | let dir := unDirFocus $
FileF (Regular $
f x) d sx
188 | in Right $
{root := dir} fs
189 | Just (Exists _) => Left (WriteErr fp FileExists)
190 | Nothing => Left (WriteErr fp FileNotFound)
193 | write : File t -> String -> MockFS -> Either FileErr MockFS
194 | write fp s = writeImpl fp s (const s)
197 | append : File t -> String -> MockFS -> Either FileErr MockFS
198 | append fp s = writeImpl fp s (++ s)
201 | removeFile : File t -> MockFS -> Either FileErr MockFS
202 | removeFile fp fs = case fsFocus fs $
toPath fp of
203 | Just (FileF _ (MkCtxt start _ end) sx) =>
204 | Right $
{root := unDirFocus' (start <>> end) sx} fs
205 | Nothing => Left $
DeleteErr (toPath fp) FileNotFound
206 | Just (DirF {}) => Left $
DeleteErr (toPath fp) FileNotFound
209 | removeDir : Path t -> MockFS -> Either FileErr MockFS
210 | removeDir fp fs = case fsFocus fs fp of
211 | Just (DirF (MkMD []) (sx :< (MkCtxt start _ end))) =>
212 | Right $
{root := unDirFocus' (start <>> end) sx} fs
213 | Just (DirF {}) => Right fs
214 | Just (FileF {}) => Left $
DeleteErr fp FileNotFound
215 | Nothing => Left $
DeleteErr fp FileNotFound
218 | read : File t -> Bits32 -> MockFS -> Either FileErr String
219 | read fp ms fs = case fsFocus fs $
toPath fp of
220 | Just (FileF (Regular x) _ _) => case length x <= cast ms of
222 | False => Left (LimitExceeded fp ms)
223 | Just (FileF (Stream _) _ _) => Right ""
224 | Just (DirF _ _) => Left (ReadErr fp FileReadError)
225 | Nothing => Left (ReadErr fp FileNotFound)
228 | changeDir : Path t -> MockFS -> Either FileErr MockFS
229 | changeDir fp fs = case fsFocus fs fp of
230 | Just (DirF x sx) => Right $
{curDir := absPath fs fp} fs
231 | Just (FileF x y sx) => Left (ChangeDir fp)
232 | Nothing => Left (ChangeDir fp)
235 | listDir : Path t -> MockFS -> (Either FileErr $
List Body)
236 | listDir fp fs = case fsFocus fs fp of
237 | Just (DirF x _) => Right $
map fst x.content
238 | Just (FileF _ _ _) => Left (ListDir fp FileReadError)
239 | Nothing => Left (ListDir fp FileNotFound)
241 | mock : IORef MockFS
242 | -> (MockFS -> Either FileErr MockFS)
243 | -> IO (Either FileErr ())
245 | Right fs <- f <$> readIORef ref
246 | | Left err => pure (Left err)
247 | Right <$> writeIORef ref fs
251 | fs : IORef MockFS -> FS
254 | { write_ = \_,fp,s => mock ref (write fp s)
255 | , append_ = \_,fp,s => mock ref (append fp s)
256 | , removeFile_ = \_,fp => mock ref (removeFile fp)
257 | , removeDir_ = \_,fp => mock ref (removeDir fp)
258 | , exists_ = \_,fp => exists fp <$> readIORef ref
259 | , read_ = \_,fp,l => read fp l <$> readIORef ref
260 | , curDir_ = Right . curDir <$> readIORef ref
261 | , changeDir_ = \_,fp => mock ref (changeDir fp)
262 | , listDir_ = \_,fp => listDir fp <$> readIORef ref
263 | , mkDir_ = \_,fp => mock ref (mkDir fp)