Filter.hs: Implement tryGetFilters
[hdata.git] / src / Tools / Filter.hs
blob45d927d4c5dc05672a31b803f1b9511e3fc72e3d
1 {-
2 Tools/Filter.hs
4 Copyright 2013 Louis-Guillaume Gagnon <louis.guillaume.gagnon@gmail.com>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
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 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 module Tools.Filter (
21 tryGetFilters
22 ) where
24 import Data.Char (isDigit)
25 import Data.List (intersperse)
26 import System.Directory (doesFileExist)
28 data Filter = File String
29 | Title String
30 | Authors String
31 | Keywords String
32 | Journal String
33 | Volume String
34 | Year String
35 | Pages String
36 | Bookmarked String
37 deriving (Show)
39 tryGetFilters :: [String] -> IO (Either String ([String],[String]));
40 tryGetFilters argv = case getFilters argv of
41 Left msg -> return $ Left msg
42 Right pairs -> if anyDuplicates pairs
43 then return $ Left "duplicated arguments"
44 else do
45 s <- checkFile pairs
46 case s of
47 Right _ -> return $ Right $ pairFilters pairs
48 Left msg -> return $ Left msg
50 areFiltersEqual :: Filter -> Filter -> Bool
51 areFiltersEqual f1 f2 = f1' == f2'
52 where (f1',_) = break (==' ') $ show f1
53 (f2',_) = break (==' ') $ show f2
55 anyDuplicates :: [Filter] -> Bool
56 anyDuplicates (f:[]) = False
57 anyDuplicates (f:fs) = if or $ map (areFiltersEqual f) fs
58 then True
59 else do anyDuplicates fs
61 checkFile :: [Filter] -> IO (Either String ())
62 checkFile fs = case filter isPathFilter fs of
63 [] -> return $ Right ()
64 ((File p):_) -> do exists <- doesFileExist p
65 if exists
66 then do return $ Right ()
67 else do return $ Left $ "File does not exists: " ++ p
69 isFilter :: String -> Bool
70 isFilter f = f `elem` ["-f","-t","-a","-k","-j","-v","-y","-p","-b"]
72 isLikeFilter :: String -> Bool
73 isLikeFilter f = (length f == 2) && (head f == '-')
75 isPathFilter :: Filter -> Bool
76 isPathFilter f = case f of
77 File _ -> True
78 _ -> False
80 isYear :: String -> Bool
81 isYear s = (length s == 4) && (and $ map isDigit s)
83 getFilters :: [String] -> Either String [Filter]
84 getFilters strs = case getFilterPairs strs of
85 Left msg -> Left msg
86 Right pairs -> mapToFilter pairs
88 getFilterPairs :: [String] -> Either String [(String,[String])]
89 getFilterPairs strs = worker [] strs
90 where worker fs [] = Right fs
91 worker fs (x:xs) | not (isFilter x) = Left $ "Invalid argument: " ++ x
92 | otherwise = worker ((x, values):fs) rest
93 where (values,rest) = break isLikeFilter xs
95 pairFilters :: [Filter] -> ([String],[String])
96 pairFilters fs = worker [] [] fs
97 where worker ks vs [] = (ks,vs)
98 worker ks vs (f:fs) = worker (k:ks) (v:vs) fs
99 where (k,v') = break (==' ') $ show f
100 v = filter (/='\"') (tail v')
102 toFilter :: (String,[String]) -> Either String Filter
103 toFilter (f,vs) | null vs = if f == "-b"
104 then Right $ Bookmarked "true"
105 else Left "too few arguments"
106 | otherwise = case f of
107 "-f" -> Right $ File $ concat $ intersperse " " vs
108 "-t" -> Right $ Title $ concat $ intersperse " " vs
109 "-a" -> Right $ Authors $ concat $ intersperse " | " vs
110 "-k" -> Right $ Keywords $ concat $ intersperse " | " vs
111 "-j" -> Right $ Journal $ concat $ intersperse " " vs
112 "-v" -> if length vs == 1
113 then if and $ map isDigit vs0
114 then Right $ Volume vs0
115 else Left $ "Invalid Volume: " ++ vs0
116 else Left "too many arguments to -v"
117 "-y" -> if length vs == 1
118 then if isYear vs0
119 then Right $ Year vs0
120 else Left $ "Invalid year: " ++ vs0
121 else Left "too many arguments to -y"
122 "-p" -> case length vs of
123 1 -> if and $ map isDigit vs0
124 then Right $ Pages vs0
125 else Left $ "Invalid pages: " ++ vs0
126 2 -> if and $ map isDigit (vs0 ++ vs1)
127 then Right $ Pages (vs0 ++ " " ++ vs1)
128 else Left $ "Invalid pages: " ++ (vs0 ++ " " ++ vs1)
129 _ -> Left "too many arguments to -p"
130 "-b" -> if null vs
131 then Right $ Bookmarked "true"
132 else Left "too many arguments to -b"
133 where vs0 = vs!!0
134 vs1 = vs!!1
136 mapToFilter :: [(String,[String])] -> Either String [Filter]
137 mapToFilter strs = worker [] strs
138 where worker fs [] = Right fs
139 worker fs (x:xs) = let f = toFilter x
140 in case f of
141 Left msg -> Left msg
142 Right f -> worker (f:fs) xs
144 usageFilters :: String
145 usageFilters = "filters:\n\
146 \ -f <file>\n\
147 \ -t <title>\n\
148 \ -a <authors>\n\
149 \ -k <keywords>\n\
150 \ -j <journal>\n\
151 \ -y <year> : <yyyy>\n\
152 \ -p <page> [page]"