diff options
Diffstat (limited to 'compiler/src/Input.hs')
-rw-r--r-- | compiler/src/Input.hs | 75 |
1 files changed, 43 insertions, 32 deletions
diff --git a/compiler/src/Input.hs b/compiler/src/Input.hs index 6ed7471..7990571 100644 --- a/compiler/src/Input.hs +++ b/compiler/src/Input.hs | |||
@@ -1,7 +1,7 @@ | |||
1 | -- ldgallery - A static generator which turns a collection of tagged | 1 | -- ldgallery - A static generator which turns a collection of tagged |
2 | -- pictures into a searchable web gallery. | 2 | -- pictures into a searchable web gallery. |
3 | -- | 3 | -- |
4 | -- Copyright (C) 2019-2020 Pacien TRAN-GIRARD | 4 | -- Copyright (C) 2019-2022 Pacien TRAN-GIRARD |
5 | -- | 5 | -- |
6 | -- This program is free software: you can redistribute it and/or modify | 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 | 7 | -- it under the terms of the GNU Affero General Public License as |
@@ -27,13 +27,15 @@ import GHC.Generics (Generic) | |||
27 | import Control.Exception (Exception, AssertionFailed(..), throw, throwIO) | 27 | import Control.Exception (Exception, AssertionFailed(..), throw, throwIO) |
28 | import Control.Monad.IO.Class (MonadIO, liftIO) | 28 | import Control.Monad.IO.Class (MonadIO, liftIO) |
29 | import Data.Function ((&)) | 29 | import Data.Function ((&)) |
30 | import Data.Maybe (catMaybes) | 30 | import Data.Functor ((<&>)) |
31 | import Data.Maybe (catMaybes, fromMaybe) | ||
31 | import Data.Bool (bool) | 32 | import Data.Bool (bool) |
32 | import Data.List (find) | 33 | import Data.List (find, isSuffixOf) |
33 | import Data.Time.Clock (UTCTime) | 34 | import Data.Time.Clock (UTCTime) |
34 | import Data.Time.LocalTime (ZonedTime) | 35 | import Data.Time.LocalTime (ZonedTime) |
35 | import Data.Yaml (ParseException, decodeFileEither) | 36 | import Data.Yaml (ParseException, decodeFileEither) |
36 | import Data.Aeson (FromJSON) | 37 | import Data.Aeson (FromJSON) |
38 | import qualified Data.Map.Strict as Map | ||
37 | import System.FilePath (isExtensionOf, dropExtension) | 39 | import System.FilePath (isExtensionOf, dropExtension) |
38 | import System.Directory (doesFileExist, getModificationTime) | 40 | import System.Directory (doesFileExist, getModificationTime) |
39 | 41 | ||
@@ -54,12 +56,13 @@ data InputTree = | |||
54 | InputFile | 56 | InputFile |
55 | { path :: Path | 57 | { path :: Path |
56 | , modTime :: UTCTime | 58 | , modTime :: UTCTime |
57 | , sidecar :: Sidecar } | 59 | , sidecar :: Sidecar |
60 | , thumbnailPath :: Maybe Path } | ||
58 | | InputDir | 61 | | InputDir |
59 | { path :: Path | 62 | { path :: Path |
60 | , modTime :: UTCTime | 63 | , modTime :: UTCTime |
61 | , sidecar :: Sidecar | 64 | , sidecar :: Sidecar |
62 | , dirThumbnailPath :: Maybe Path | 65 | , thumbnailPath :: Maybe Path |
63 | , items :: [InputTree] } | 66 | , items :: [InputTree] } |
64 | deriving Show | 67 | deriving Show |
65 | 68 | ||
@@ -80,6 +83,9 @@ emptySidecar = Sidecar | |||
80 | sidecarExt :: String | 83 | sidecarExt :: String |
81 | sidecarExt = "yaml" | 84 | sidecarExt = "yaml" |
82 | 85 | ||
86 | thumbnailSuffix :: String | ||
87 | thumbnailSuffix = "_thumbnail" | ||
88 | |||
83 | dirPropFile :: String | 89 | dirPropFile :: String |
84 | dirPropFile = "_directory" | 90 | dirPropFile = "_directory" |
85 | 91 | ||
@@ -90,48 +96,55 @@ readSidecarFile :: FilePath -> IO Sidecar | |||
90 | readSidecarFile filepath = | 96 | readSidecarFile filepath = |
91 | doesFileExist filepath | 97 | doesFileExist filepath |
92 | >>= bool (return Nothing) (decodeYamlFile filepath) | 98 | >>= bool (return Nothing) (decodeYamlFile filepath) |
93 | >>= return . maybe emptySidecar id | 99 | <&> fromMaybe emptySidecar |
94 | 100 | ||
95 | 101 | ||
96 | readInputTree :: AnchoredFSNode -> IO InputTree | 102 | readInputTree :: AnchoredFSNode -> IO InputTree |
97 | readInputTree (AnchoredFSNode _ File{}) = | 103 | readInputTree (AnchoredFSNode anchor root) = mkDirNode root |
98 | throw $ AssertionFailed "Input directory is a file" | ||
99 | readInputTree (AnchoredFSNode anchor root@Dir{}) = mkDirNode root | ||
100 | where | 104 | where |
101 | mkInputNode :: FSNode -> IO (Maybe InputTree) | 105 | mkInputNode :: Map.Map FileName FSNode -> FSNode -> IO (Maybe InputTree) |
102 | mkInputNode file@File{path} | 106 | mkInputNode dir file@File{path} | not (isSidecar file) && not (isThumbnail file) = |
103 | | (not $ isSidecar file) && (not $ isThumbnail file) = | 107 | do |
104 | do | 108 | sidecar <- readSidecarFile $ localPath (anchor /> path <.> sidecarExt) |
105 | sidecar <- readSidecarFile $ localPath (anchor /> path <.> sidecarExt) | 109 | modTime <- getModificationTime $ localPath (anchor /> path) |
106 | modTime <- getModificationTime $ localPath (anchor /> path) | 110 | let thumbnail = findFileThumbnail (fromMaybe "" $ fileName path) dir |
107 | return $ Just $ InputFile path modTime sidecar | 111 | return $ Just $ InputFile path modTime sidecar thumbnail |
108 | mkInputNode File{} = return Nothing | 112 | mkInputNode _ File{} = return Nothing |
109 | mkInputNode dir@Dir{} = mkDirNode dir >>= return . Just | 113 | mkInputNode _ dir@Dir{} = Just <$> mkDirNode dir |
110 | 114 | ||
111 | mkDirNode :: FSNode -> IO InputTree | 115 | mkDirNode :: FSNode -> IO InputTree |
112 | mkDirNode File{} = throw $ AssertionFailed "Input directory is a file" | 116 | mkDirNode File{} = throw $ AssertionFailed "Input directory is a file" |
113 | mkDirNode Dir{path, items} = | 117 | mkDirNode Dir{path, items} = |
114 | do | 118 | do |
115 | dirItems <- mapM mkInputNode items | 119 | dirItems <- mapM (mkInputNode $ Map.fromList (map withBaseName items)) items |
116 | modTime <- getModificationTime $ localPath (anchor /> path) | 120 | modTime <- getModificationTime $ localPath (anchor /> path) |
117 | sidecar <- readSidecarFile $ localPath (anchor /> path </> dirSidecar) | 121 | sidecar <- readSidecarFile $ localPath (anchor /> path </> dirSidecar) |
118 | return $ InputDir path modTime sidecar (findThumbnail items) (catMaybes dirItems) | 122 | return $ InputDir path modTime sidecar (findDirThumbnail items) (catMaybes dirItems) |
123 | |||
124 | withBaseName :: FSNode -> (FileName, FSNode) | ||
125 | withBaseName node = (fromMaybe "" $ baseName $ Files.path node, node) | ||
126 | |||
127 | findFileThumbnail :: FileName -> Map.Map FileName FSNode -> Maybe Path | ||
128 | findFileThumbnail name dict = Files.path <$> Map.lookup (name ++ thumbnailSuffix) dict | ||
119 | 129 | ||
120 | isSidecar :: FSNode -> Bool | 130 | isSidecar :: FSNode -> Bool |
121 | isSidecar Dir{} = False | 131 | isSidecar Dir{} = False |
122 | isSidecar File{path} = | 132 | isSidecar File{path} = fileName path & maybe False (isExtensionOf sidecarExt) |
123 | fileName path | 133 | |
124 | & (maybe False $ isExtensionOf sidecarExt) | 134 | baseName :: Path -> Maybe FileName |
135 | baseName = fmap dropExtension . fileName | ||
125 | 136 | ||
126 | isThumbnail :: FSNode -> Bool | 137 | isThumbnail :: FSNode -> Bool |
127 | isThumbnail Dir{} = False | 138 | isThumbnail Dir{} = False |
128 | isThumbnail File{path} = | 139 | isThumbnail File{path} = baseName path & maybe False (thumbnailSuffix `isSuffixOf`) |
129 | fileName path | 140 | |
130 | & fmap dropExtension | 141 | isDirThumbnail :: FSNode -> Bool |
131 | & (maybe False (dirPropFile ==)) | 142 | isDirThumbnail Dir{} = False |
143 | isDirThumbnail File{path} = baseName path & (== Just thumbnailSuffix) | ||
144 | |||
145 | findDirThumbnail :: [FSNode] -> Maybe Path | ||
146 | findDirThumbnail = fmap Files.path . find isDirThumbnail | ||
132 | 147 | ||
133 | findThumbnail :: [FSNode] -> Maybe Path | ||
134 | findThumbnail = (fmap Files.path) . (find isThumbnail) | ||
135 | 148 | ||
136 | -- | Filters an InputTree. The root is always returned. | 149 | -- | Filters an InputTree. The root is always returned. |
137 | filterInputTree :: (InputTree -> Bool) -> InputTree -> InputTree | 150 | filterInputTree :: (InputTree -> Bool) -> InputTree -> InputTree |
@@ -140,6 +153,4 @@ filterInputTree cond = filterNode | |||
140 | filterNode :: InputTree -> InputTree | 153 | filterNode :: InputTree -> InputTree |
141 | filterNode inputFile@InputFile{} = inputFile | 154 | filterNode inputFile@InputFile{} = inputFile |
142 | filterNode inputDir@InputDir{items} = | 155 | filterNode inputDir@InputDir{items} = |
143 | filter cond items | 156 | inputDir { Input.items = filter cond items & map filterNode } |
144 | & map filterNode | ||
145 | & \curatedItems -> inputDir { items = curatedItems } :: InputTree | ||