module Issue.Sort ( Sort, Order, SortBy, applySorts, sortArg, ) where import Data.List (sort, sortBy) import Data.Maybe (fromMaybe, mapMaybe) import Data.Text (Text) import Data.Text qualified as T import Issue (Issue (..)) import Issue.Tag (Tag (..)) import Issue.TypedValue (cast) import Options.Applicative qualified as O data Sort = Sort Order SortBy deriving (Show) data Order = Asc | Desc deriving (Show, Eq) data SortBy = SortByTag Text deriving (Show) defaultSort :: Sort defaultSort = Sort Desc (SortByTag "createdAt") sortArg :: O.Parser [Sort] sortArg = O.many ( O.option (O.maybeReader (parse . T.pack)) ( O.long "sort" <> O.short 's' <> O.metavar "SORT" <> O.help "Sort selected issues. (Defaults: `-@createdAt`)" ) ) where parse s | "@" `T.isPrefixOf` s = Just (Sort Asc (SortByTag (T.drop 1 s))) | "-@" `T.isPrefixOf` s = Just (Sort Desc (SortByTag (T.drop 2 s))) | otherwise = Nothing applySorts :: [Sort] -> [Issue] -> [Issue] applySorts cs = compose (defaultSort : cs) where compose :: [Sort] -> ([Issue] -> [Issue]) compose = foldr (flip (.)) id . map toSort toSort :: Sort -> ([Issue] -> [Issue]) toSort (Sort order sortBy') = (if order == Desc then reverse else id) . sortBy (sortByCompare sortBy') sortByCompare :: SortBy -> (Issue -> Issue -> Ordering) sortByCompare (SortByTag k) i1 i2 = compareList (incomparableFirst (cast compare)) (sort $ tagValues k (i1.tags ++ i1.internalTags)) (sort $ tagValues k (i2.tags ++ i2.internalTags)) tagValues :: Text -> [Tag] -> [Text] tagValues k = mapMaybe (\(Tag k' v) -> if k' == k then v else Nothing) compareList :: (a -> a -> Ordering) -> ([a] -> [a] -> Ordering) compareList _ [] _ = LT compareList _ _ [] = GT compareList g (a : as) (b : bs) | g a b == EQ = compareList g as bs | otherwise = g a b incomparableFirst :: (a -> a -> Maybe Ordering) -> (a -> a -> Ordering) incomparableFirst cmp a b = fromMaybe LT (cmp a b)