Skip to content

Instantly share code, notes, and snippets.

@guibou
Forked from coodoo/Interop.hs
Last active December 9, 2017 23:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save guibou/900da1e3740b7b3804f4d6b6778c4cb8 to your computer and use it in GitHub Desktop.
Save guibou/900da1e3740b7b3804f4d6b6778c4cb8 to your computer and use it in GitHub Desktop.
{-
Functionality:
- 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
│ │ │ ├── b1-1
│ │ │ └── b1-2
│ │ └── b2
│ │ └── 2-1
│ │ └── b2-1-1
│ └── c
│ ├── c1
│ └── 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 System.Directory
import System.Exit
import System.FilePath
import Data.Time
import Control.Monad.Extra (partitionM)
main :: IO ([Dir], [File])
main
= do
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"
if rev
then mapM_ print $ reverse src
else mapM_ print src
data File = File
{ name :: String
, date :: UTCTime
, size :: Integer
, content :: String
} deriving (Show)
data Dir = Dir
{ dirName :: String
, dirDate :: UTCTime
, dirContent :: [FilePath]
} deriving (Show)
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
-- 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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment