|
|
@@ -3,7 +3,6 @@ |
|
|
- Read all folders and sub-folders with structure like below |
|
|
- store all folder and file info in State monad, |
|
|
- at the end of the loop, print it. |
|
|
|
|
|
├── aaa |
|
|
│ ├── b |
|
|
│ │ ├── b1 |
|
|
@@ -17,27 +16,30 @@ |
|
|
│ └── c2 |
|
|
|
|
|
Goal: to practice using State and IO monad together by leveraging liftIO and unsafePerformIO |
|
|
|
|
|
Would appreciate any correction, better way to do it or generic advices |
|
|
|
|
|
-} |
|
|
module Main where |
|
|
|
|
|
import Control.Monad.State |
|
|
import Control.Monad.State |
|
|
import System.Directory |
|
|
import System.Exit |
|
|
import System.FilePath |
|
|
import Data.Time |
|
|
|
|
|
import Control.Monad.Extra (partitionM) |
|
|
|
|
|
main :: IO ([Dir], [File], Root) |
|
|
main :: IO ([Dir], [File]) |
|
|
main |
|
|
= do |
|
|
~(_, s@(d, f, r)) <- runStateT k ([], [], "aaa") |
|
|
putStrLn $ "Root: " ++ r |
|
|
let root = "/tmp/poulet" |
|
|
s@(d, f) <- k root |
|
|
putStrLn $ "Root: " ++ root |
|
|
|
|
|
pretty d True "DIR:" |
|
|
pretty f True "FILE:" |
|
|
return s |
|
|
|
|
|
-- | Order is arbitrary |
|
|
pretty :: Show a => [a] -> Bool -> String -> IO () |
|
|
pretty src rev prefix = do |
|
|
putStrLn $ "\n" ++ prefix ++ "\n" |
|
|
@@ -47,60 +49,48 @@ pretty src rev prefix = do |
|
|
|
|
|
data File = File |
|
|
{ name :: String |
|
|
, date :: String |
|
|
, date :: UTCTime |
|
|
, size :: Integer |
|
|
, content :: String |
|
|
} deriving (Show) |
|
|
|
|
|
data Dir = Dir |
|
|
{ dirName :: String |
|
|
, dirDate :: String |
|
|
, dirDate :: UTCTime |
|
|
, dirContent :: [FilePath] |
|
|
} deriving (Show) |
|
|
|
|
|
type Root = String |
|
|
k :: String -> IO ([Dir], [File]) |
|
|
k root = do |
|
|
isDir <- doesDirectoryExist root |
|
|
unless isDir $ die "not a dir" |
|
|
execStateT (list root) ([], []) |
|
|
|
|
|
list :: String -> StateT ([Dir], [File]) IO () |
|
|
list path = do |
|
|
-- walk current directory |
|
|
dir <- liftIO $ do |
|
|
children <- listDirectory path |
|
|
dt <- getModificationTime path |
|
|
return Dir {dirName = path, dirContent = children, dirDate = dt} |
|
|
modify (\(ds, fs) -> (dir : ds, fs)) |
|
|
|
|
|
-- remove unwanted files and prepend directory name |
|
|
let |
|
|
fullPaths = map (path</>) (dirContent dir) |
|
|
filteredFiles = filter (/=".DS_Store") fullPaths |
|
|
|
|
|
-- Split between sub directories and sub files |
|
|
(subDirs, subFiles) <- liftIO (partitionM doesDirectoryExist filteredFiles) |
|
|
|
|
|
-- recursivly walk sub directories |
|
|
forM_ subDirs list |
|
|
|
|
|
k :: StateT ([Dir], [File], Root) IO () |
|
|
k = do |
|
|
(_, _, root) <- get |
|
|
liftIO $ do |
|
|
isDir <- doesDirectoryExist root |
|
|
unless isDir $ die "not a dir" |
|
|
list root |
|
|
where |
|
|
list :: String -> StateT ([Dir], [File], Root) IO () |
|
|
list path = do |
|
|
dir <- |
|
|
liftIO $ |
|
|
do |
|
|
children <- withCurrentDirectory path $ listDirectory "." |
|
|
dt <- getModificationTime path |
|
|
return Dir {dirName = path, dirContent = children, dirDate = show dt} |
|
|
_ <- withStateT (\(ds, fs, root) -> (dir : ds, fs, root)) get |
|
|
n <- |
|
|
liftIO $ |
|
|
foldM |
|
|
(\acc@(dirs, files) f -> do |
|
|
let fullPath = path </> f |
|
|
if (f == ".DS_Store") |
|
|
then return acc |
|
|
else do |
|
|
bool <- doesDirectoryExist fullPath |
|
|
if bool |
|
|
then return (dirs ++ [fullPath], files) |
|
|
else return (dirs, files ++ [fullPath])) |
|
|
([], []) -- acc, ([dirs], [files]) |
|
|
$ |
|
|
dirContent dir |
|
|
forM_ (fst n) list |
|
|
files <- |
|
|
liftIO $ |
|
|
mapM |
|
|
(\f -> do |
|
|
s <- getFileSize f |
|
|
dt <- getModificationTime f |
|
|
c <- readFile f |
|
|
return File {name = f, date = show dt, size = s, content = c}) |
|
|
(snd n) |
|
|
_ <- withStateT (\(ds, fs, root) -> (ds, files ++ fs, root)) get |
|
|
return () |
|
|
-- handle files in the current directory |
|
|
forM_ subFiles $ \f -> do |
|
|
file <- liftIO $ do |
|
|
s <- getFileSize f |
|
|
dt <- getModificationTime f |
|
|
c <- readFile f |
|
|
return File {name = f, date = dt, size = s, content = c} |
|
|
modify (\(ds, fs) -> (ds, file:fs)) |