module Data.Hourglass.FuzzyParsing.Internal where
import Data.Char (digitToInt, isDigit, toUpper)
import Data.Hourglass (Month)
import Data.List (isPrefixOf)
import Text.Parsec
tryRead :: (Read a, Stream s m Char) => String -> ParsecT s st m a
tryRead str =
case reads str of
[(res, "")] -> return res
_ -> fail $ "Cannot read: " ++ str
tryReadInt :: (Stream s m Char, Num a) => String -> ParsecT s st m a
tryReadInt str =
if all isDigit str
then return $ fromIntegral $ foldl (\a b -> 10*a+b) 0 $ map digitToInt str
else fail $ "Cannot read: " ++ str
times :: (Stream s m Char)
=> Int
-> ParsecT s st m t
-> ParsecT s st m [t]
times 0 _ = return []
times n p = do
ts <- times (n 1) p
t <- optionMaybe p
case t of
Just t' -> return (ts ++ [t'])
Nothing -> return ts
number :: (Stream s m Char, Num a, Ord a)
=> Int
-> a
-> ParsecT s st m a
number n m = do
t <- tryReadInt =<< (n `times` digit)
if t > m
then fail "number too large"
else return t
pYear :: Stream s m Char => ParsecT s st m Int
pYear = do
y <- number 4 10000
if y < 2000
then return (y + 2000)
else return y
pMonth :: Stream s m Char => ParsecT s st m Month
pMonth = toEnum . pred <$> number 2 12
pDay :: Stream s m Char => ParsecT s st m Int
pDay = number 2 31
uppercase :: String -> String
uppercase = map toUpper
isPrefixOfI :: String -> String -> Bool
p `isPrefixOfI` s = uppercase p `isPrefixOf` uppercase s
uniqFuzzyMatch :: (Bounded a, Enum a, Show a)
=> String
-> Either [a] a
uniqFuzzyMatch n = if length matches == 1
then Right (head matches)
else Left matches
where
possibilities = [minBound..maxBound]
matches = filter (isPrefixOfI n . show) possibilities