aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/Input.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/Input.hs')
-rw-r--r--compiler/src/Input.hs75
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)
27import Control.Exception (Exception, AssertionFailed(..), throw, throwIO) 27import Control.Exception (Exception, AssertionFailed(..), throw, throwIO)
28import Control.Monad.IO.Class (MonadIO, liftIO) 28import Control.Monad.IO.Class (MonadIO, liftIO)
29import Data.Function ((&)) 29import Data.Function ((&))
30import Data.Maybe (catMaybes) 30import Data.Functor ((<&>))
31import Data.Maybe (catMaybes, fromMaybe)
31import Data.Bool (bool) 32import Data.Bool (bool)
32import Data.List (find) 33import Data.List (find, isSuffixOf)
33import Data.Time.Clock (UTCTime) 34import Data.Time.Clock (UTCTime)
34import Data.Time.LocalTime (ZonedTime) 35import Data.Time.LocalTime (ZonedTime)
35import Data.Yaml (ParseException, decodeFileEither) 36import Data.Yaml (ParseException, decodeFileEither)
36import Data.Aeson (FromJSON) 37import Data.Aeson (FromJSON)
38import qualified Data.Map.Strict as Map
37import System.FilePath (isExtensionOf, dropExtension) 39import System.FilePath (isExtensionOf, dropExtension)
38import System.Directory (doesFileExist, getModificationTime) 40import 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
80sidecarExt :: String 83sidecarExt :: String
81sidecarExt = "yaml" 84sidecarExt = "yaml"
82 85
86thumbnailSuffix :: String
87thumbnailSuffix = "_thumbnail"
88
83dirPropFile :: String 89dirPropFile :: String
84dirPropFile = "_directory" 90dirPropFile = "_directory"
85 91
@@ -90,48 +96,55 @@ readSidecarFile :: FilePath -> IO Sidecar
90readSidecarFile filepath = 96readSidecarFile 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
96readInputTree :: AnchoredFSNode -> IO InputTree 102readInputTree :: AnchoredFSNode -> IO InputTree
97readInputTree (AnchoredFSNode _ File{}) = 103readInputTree (AnchoredFSNode anchor root) = mkDirNode root
98 throw $ AssertionFailed "Input directory is a file"
99readInputTree (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.
137filterInputTree :: (InputTree -> Bool) -> InputTree -> InputTree 150filterInputTree :: (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