diff options
Diffstat (limited to 'compiler/src/Input.hs')
-rw-r--r-- | compiler/src/Input.hs | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/compiler/src/Input.hs b/compiler/src/Input.hs new file mode 100644 index 0000000..cb837e3 --- /dev/null +++ b/compiler/src/Input.hs | |||
@@ -0,0 +1,126 @@ | |||
1 | -- ldgallery - A static generator which turns a collection of tagged | ||
2 | -- pictures into a searchable web gallery. | ||
3 | -- | ||
4 | -- Copyright (C) 2019-2020 Pacien TRAN-GIRARD | ||
5 | -- | ||
6 | -- This program is free software: you can redistribute it and/or modify | ||
7 | -- it under the terms of the GNU Affero General Public License as | ||
8 | -- published by the Free Software Foundation, either version 3 of the | ||
9 | -- License, or (at your option) any later version. | ||
10 | -- | ||
11 | -- This program is distributed in the hope that it will be useful, | ||
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | -- GNU Affero General Public License for more details. | ||
15 | -- | ||
16 | -- You should have received a copy of the GNU Affero General Public License | ||
17 | -- along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
18 | |||
19 | module Input | ||
20 | ( decodeYamlFile | ||
21 | , Sidecar(..) | ||
22 | , InputTree(..), readInputTree | ||
23 | ) where | ||
24 | |||
25 | |||
26 | import GHC.Generics (Generic) | ||
27 | import Control.Exception (Exception, AssertionFailed(..), throw, throwIO) | ||
28 | import Control.Monad.IO.Class (MonadIO, liftIO) | ||
29 | import Data.Function ((&)) | ||
30 | import Data.Maybe (catMaybes) | ||
31 | import Data.Bool (bool) | ||
32 | import Data.List (find) | ||
33 | import Data.Time.Clock (UTCTime) | ||
34 | import Data.Time.LocalTime (ZonedTime) | ||
35 | import Data.Yaml (ParseException, decodeFileEither) | ||
36 | import Data.Aeson (FromJSON) | ||
37 | import System.FilePath (isExtensionOf, dropExtension) | ||
38 | import System.Directory (doesFileExist, getModificationTime) | ||
39 | |||
40 | import Files | ||
41 | |||
42 | |||
43 | data LoadException = LoadException String ParseException deriving Show | ||
44 | instance Exception LoadException | ||
45 | |||
46 | decodeYamlFile :: (MonadIO m, FromJSON a) => FileName -> m a | ||
47 | decodeYamlFile path = | ||
48 | liftIO $ Data.Yaml.decodeFileEither path | ||
49 | >>= either (throwIO . LoadException path) return | ||
50 | |||
51 | |||
52 | -- | Tree representing the input from the input directory. | ||
53 | data InputTree = | ||
54 | InputFile | ||
55 | { path :: Path | ||
56 | , modTime :: UTCTime | ||
57 | , sidecar :: Sidecar } | ||
58 | | InputDir | ||
59 | { path :: Path | ||
60 | , modTime :: UTCTime | ||
61 | , dirThumbnailPath :: Maybe Path | ||
62 | , items :: [InputTree] } | ||
63 | deriving Show | ||
64 | |||
65 | data Sidecar = Sidecar | ||
66 | { title :: Maybe String | ||
67 | , datetime :: Maybe ZonedTime | ||
68 | , description :: Maybe String | ||
69 | , tags :: Maybe [String] | ||
70 | } deriving (Generic, FromJSON, Show) | ||
71 | |||
72 | emptySidecar :: Sidecar | ||
73 | emptySidecar = Sidecar | ||
74 | { title = Nothing | ||
75 | , datetime = Nothing | ||
76 | , description = Nothing | ||
77 | , tags = Nothing } | ||
78 | |||
79 | sidecarExt :: String | ||
80 | sidecarExt = "yaml" | ||
81 | |||
82 | readSidecarFile :: FilePath -> IO Sidecar | ||
83 | readSidecarFile filepath = | ||
84 | doesFileExist filepath | ||
85 | >>= bool (return Nothing) (decodeYamlFile filepath) | ||
86 | >>= return . maybe emptySidecar id | ||
87 | |||
88 | |||
89 | readInputTree :: AnchoredFSNode -> IO InputTree | ||
90 | readInputTree (AnchoredFSNode _ File{}) = | ||
91 | throw $ AssertionFailed "Input directory is a file" | ||
92 | readInputTree (AnchoredFSNode anchor root@Dir{}) = mkDirNode root | ||
93 | where | ||
94 | mkInputNode :: FSNode -> IO (Maybe InputTree) | ||
95 | mkInputNode file@File{path} | ||
96 | | (not $ isSidecar file) && (not $ isThumbnail file) = | ||
97 | do | ||
98 | sidecar <- readSidecarFile $ localPath (anchor /> path <.> sidecarExt) | ||
99 | modTime <- getModificationTime $ localPath (anchor /> path) | ||
100 | return $ Just $ InputFile path modTime sidecar | ||
101 | mkInputNode File{} = return Nothing | ||
102 | mkInputNode dir@Dir{} = mkDirNode dir >>= return . Just | ||
103 | |||
104 | mkDirNode :: FSNode -> IO InputTree | ||
105 | mkDirNode File{} = throw $ AssertionFailed "Input directory is a file" | ||
106 | mkDirNode Dir{path, items} = | ||
107 | do | ||
108 | dirItems <- mapM mkInputNode items | ||
109 | modTime <- getModificationTime $ localPath (anchor /> path) | ||
110 | return $ InputDir path modTime (findThumbnail items) (catMaybes dirItems) | ||
111 | |||
112 | isSidecar :: FSNode -> Bool | ||
113 | isSidecar Dir{} = False | ||
114 | isSidecar File{path} = | ||
115 | fileName path | ||
116 | & (maybe False $ isExtensionOf sidecarExt) | ||
117 | |||
118 | isThumbnail :: FSNode -> Bool | ||
119 | isThumbnail Dir{} = False | ||
120 | isThumbnail File{path} = | ||
121 | fileName path | ||
122 | & fmap dropExtension | ||
123 | & (maybe False ("thumbnail" ==)) | ||
124 | |||
125 | findThumbnail :: [FSNode] -> Maybe Path | ||
126 | findThumbnail = (fmap Files.path) . (find isThumbnail) | ||