forked from ucsd-cse230/01-trees
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Directory.hs
230 lines (197 loc) · 7.68 KB
/
Directory.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
module CSE230.Directory where
import CSE230.Doc
import CSE230.List hiding (Dir)
import qualified Data.List as L
import System.FilePath (takeDirectory, takeFileName, (</>))
import System.Directory (doesFileExist, listDirectory)
-------------------------------------------------------------------------------
-- | Top-level "main" function
-------------------------------------------------------------------------------
main :: [String] -> IO ()
main ["-ls", p] = showDirectory (normalize p)
main ["-find", p, sub] = showMatchingFiles (normalize p) sub
main args = putStrLn (errorMsg args)
normalize :: FilePath -> FilePath
normalize p = takeDirectory (p </> ".")
errorMsg :: [String] -> String
errorMsg args = unlines
[ "Sorry, don't understand: " ++ unwords args ++ ". Try one of"
, " * 'htree -ls <path> ' to render the directory of all files under <path>"
, " * 'htree -find <path> sub' to find all files matching 'sub' under <path>"
]
-------------------------------------------------------------------------------
-- | 'showDirectory path' builds and displays the 'Dir' rooted at 'path'
-------------------------------------------------------------------------------
-- >>> showDirectory "src"
-- src
-- ├── CSE230
-- │ ├── Directory.hs
-- │ ├── Doc.hs
-- │ ├── Graphics.hs
-- │ ├── List.hs
-- │ └── Shapes.hs
-- └── Main.hs
-- <BLANKLINE>
--
showDirectory :: FilePath -> IO ()
showDirectory path = do
dir <- build path
print (dirDoc dir)
-------------------------------------------------------------------------------
-- | 'showMatchingFiles path sub' finds the files matching 'sub' (deep) in the
-- the directory rooted at 'path' and prints them out.
-------------------------------------------------------------------------------
-- >>> showMatchingFiles "src" ".hs"
-- src/CSE230/Directory.hs
-- src/CSE230/Doc.hs
-- src/CSE230/Graphics.hs
-- src/CSE230/List.hs
-- src/CSE230/Shapes.hs
-- src/Main.hs
showMatchingFiles :: FilePath -> String -> IO ()
showMatchingFiles path sub = do
dir <- build path
mapM_ putStrLn (findFiles sub dir)
-------------------------------------------------------------------------------
-- | The 'Directory' data type
-------------------------------------------------------------------------------
data Dir a
= Fil a -- ^ A single file named `a`
| Sub a [Dir a] -- ^ A sub-directory name `a` with contents `[Dir a]`
deriving (Eq, Show)
example :: Dir FilePath
example = Sub "."
[ Fil "LICENSE"
, Fil "README.md"
, Fil "cse230-tree.cabal"
, Sub "out" [ Fil "carpet.png"
, Fil "chess1.png"
, Fil "chess2.png"
, Fil "rainbow.png"
, Fil "triangle1.png"
, Fil "triangle2.png"
]
, Sub "src" [ Sub "CSE230" [ Fil "Directory.hs"
, Fil "Doc.hs"
, Fil "Graphics.hs"
, Fil "List.hs"
, Fil "Shapes.hs"
]
, Fil "Main.hs"
]
, Fil "stack.yaml"
]
srcDir :: Dir FilePath
srcDir = Sub "src"
[ Sub "CSE230" [ Fil "Directory.hs"
, Fil "Doc.hs"
, Fil "Graphics.hs"
, Fil "List.hs"
, Fil "Shapes.hs"
]
, Fil "Main.hs"
]
-------------------------------------------------------------------------------
-- | Printing Directories
-------------------------------------------------------------------------------
-- >>> dirDoc example
-- .
-- ├── LICENSE
-- ├── README.md
-- ├── cse230-tree.cabal
-- ├── out
-- │ ├── carpet.png
-- │ ├── chess1.png
-- │ ├── chess2.png
-- │ ├── rainbow.png
-- │ ├── triangle1.png
-- │ └── triangle2.png
-- ├── src
-- │ ├── CSE230
-- │ │ ├── Directory.hs
-- │ │ ├── Doc.hs
-- │ │ ├── Graphics.hs
-- │ │ ├── List.hs
-- │ │ └── Shapes.hs
-- │ └── Main.hs
-- └── stack.yaml
-- <BLANKLINE>
-- >>> dirDoc srcDir
-- src
-- ├── CSE230
-- │ ├── Directory.hs
-- │ ├── Doc.hs
-- │ ├── Graphics.hs
-- │ ├── List.hs
-- │ └── Shapes.hs
-- └── Main.hs
-- <BLANKLINE>
--
dirDoc :: Dir FilePath -> Doc
dirDoc (Fil f) = error "fill this in"
dirDoc (Sub f ds) = error "fill this in"
-------------------------------------------------------------------------------
-- | Some useful 'Doc's--------------------------------------------------------
-------------------------------------------------------------------------------
dash :: Doc
dash = doc "── "
stile :: Doc
stile = doc "├"
angle :: Doc
angle = doc "└"
bar :: Doc
bar = doc "│"
-------------------------------------------------------------------------------
-- | A 'fold' for directories
-------------------------------------------------------------------------------
data DirElem a = SubDir a | File a
foldDir :: ([a] -> r -> DirElem a -> r) -> r -> Dir a -> r
foldDir f = go []
where
go stk r (Fil a) = f stk r (File a)
go stk r (Sub a ds) = L.foldl' (go stk') r' ds
where
r' = f stk r (SubDir a)
stk' = a:stk
-------------------------------------------------------------------------------
-- | 'allFiles dir' returns a list of all the Files in 'dir'
-------------------------------------------------------------------------------
-- >>> allFiles example
-- ["LICENSE","README.md","cse230-tree.cabal","carpet.png","chess1.png","chess2.png","rainbow.png","triangle1.png","triangle2.png","Main.hs","Directory.hs","Doc.hs","Graphics.hs","List.hs","Shapes.hs","stack.yaml"]
allFiles :: Dir FilePath -> [FilePath]
allFiles dir = reverse (foldDir f [] dir)
where
f = error "fill this in"
-------------------------------------------------------------------------------
-- | 'allDirs dir' returns a list of all the sub-directories in 'dir'
-------------------------------------------------------------------------------
--
-- >>> allDirs example
-- [".","out","src","CSE230"]
allDirs :: Dir FilePath -> [FilePath]
allDirs dir = reverse (foldDir f [] dir)
where
f = error "fill this in"
-------------------------------------------------------------------------------
-- | 'findFiles sub dir' returns a list of all the Files-with-paths in 'dir'
-- such that 'sub' is a substring of the Files' names.
-------------------------------------------------------------------------------
--
-- >>> findFiles ".hs" example
-- ["./src/CSE230/Directory.hs","./src/CSE230/Doc.hs","./src/CSE230/Graphics.hs","./src/CSE230/List.hs","./src/CSE230/Shapes.hs", "./src/Main.hs"]
--
findFiles :: String -> Dir FilePath -> [FilePath]
findFiles sub dir = reverse (foldDir f [] dir)
where
f = error "fill this in"
-------------------------------------------------------------------------------
-- | 'build path' constructing the Directory on the filesystem rooted at 'path'
-------------------------------------------------------------------------------
--
-- >>> build "src"
-- Sub "src" [Sub "CSE230" [Fil "Directory.hs", Fil "Doc.hs", Fil "Graphics.hs", Fil "List.hs", Fil "Shapes.hs"],Fil "Main.hs"]
--
build :: FilePath -> IO (Dir FilePath)
build path = error "fill this in"
lshow :: Doc -> [String]
lshow = lines . show