{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -Wno-unused-top-binds #-} module Main (main) where import Chart.Examples import Chart.Markup import Chart.Markup.Parser import qualified Data.ByteString as BS import Data.Foldable import Data.Maybe import Data.TreeDiff import qualified Data.TreeDiff.OMap as O import FlatParse.Basic import GHC.Exts import Test.Tasty (TestTree, defaultMain, testGroup) import Test.Tasty.Golden.Advanced (goldenTest) import Prelude main :: IO () main = defaultMain $ testGroup "tests" [ goldenTests ] goldenTests :: TestTree goldenTests = testGroup "examples" (testExample <$> pathChartOptions) testExample :: (FilePath, ChartOptions) -> TestTree testExample (fp, co) = goldenTest fp (getMarkupFile fp) (pure (markupChartOptions co)) (\expected actual -> pure (show . ansiWlEditExpr <$> patch expected actual)) (\_ -> pure ()) getMarkupFile :: FilePath -> IO Markup getMarkupFile fp = do bs <- BS.readFile fp case runParser markupP bs of OK m _ -> pure m Err e -> print e >> fail "parse error" Fail -> print ("uncaught parse error" :: BS.ByteString) >> fail "parse error" isUnchangedList :: [Edit EditExpr] -> Bool isUnchangedList xs = all isCpy xs && all isUnchangedExpr (mapMaybe cpy xs) isCpy :: Edit a -> Bool isCpy (Cpy _) = True isCpy _ = False cpy :: Edit a -> Maybe a cpy (Cpy a) = Just a cpy _ = Nothing isUnchangedEdit :: Edit EditExpr -> Bool isUnchangedEdit (Cpy e) = isUnchangedExpr e isUnchangedEdit _ = False isUnchangedExpr :: EditExpr -> Bool isUnchangedExpr e = isUnchangedList $ getList e getList :: EditExpr -> [Edit EditExpr] getList (EditApp _ xs) = xs getList (EditRec _ m) = snd <$> O.toList m getList (EditLst xs) = xs getList (EditExp _) = [] filterChangedExprs :: EditExpr -> Maybe EditExpr filterChangedExprs (EditApp n xs) = case filter (not . isUnchangedEdit) (filterChangedEdits xs) of [] -> Nothing xs' -> Just $ EditApp n xs' filterChangedExprs (EditRec n m) = case filterChangedEditMap (O.fromList $ filter (not . isUnchangedEdit . snd) (O.toList m)) of Nothing -> Nothing Just m' -> Just (EditRec n m') filterChangedExprs (EditLst xs) = case filter (not . isUnchangedEdit) (filterChangedEdits xs) of [] -> Nothing xs' -> Just (EditLst xs') filterChangedExprs (EditExp _) = Nothing filterChangedEdit :: Edit EditExpr -> Maybe (Edit EditExpr) filterChangedEdit (Cpy a) = Cpy <$> filterChangedExprs a filterChangedEdit x = Just x filterChangedEdit' :: (f, Edit EditExpr) -> Maybe (f, Edit EditExpr) filterChangedEdit' (f, e) = (f,) <$> filterChangedEdit e filterChangedEdits :: [Edit EditExpr] -> [Edit EditExpr] filterChangedEdits xs = mapMaybe filterChangedEdit xs filterChangedEditMap :: O.OMap FieldName (Edit EditExpr) -> Maybe (O.OMap FieldName (Edit EditExpr)) filterChangedEditMap m = case xs' of [] -> Nothing xs'' -> Just $ O.fromList xs'' where xs = O.toList m xs' = mapMaybe filterChangedEdit' xs patch :: Markup -> Markup -> Maybe (Edit EditExpr) patch m m' = filterChangedEdit $ ediff m m' -- patch testing printPatchExamples :: IO () printPatchExamples = traverse_ (printPatchExample m0) patchExamples printPatchExample :: Markup -> (String, Markup) -> IO () printPatchExample m (s, m') = do print s case show . ansiWlEditExpr <$> patch m m' of Nothing -> putStrLn ("no changes" :: String) Just x -> putStrLn x patchExamples :: [(String, Markup)] patchExamples = [ ("class change", m1), ("delete an attribute", m2), ("insert an attribute", m3), ("change a tag", m4), ("change a markup leaf", m5), ("delete a leaf", m6), ("insert a leaf", m7), ("insert attribute", m8), ("modify content", m9), ("deep leaf insertion", m10) ] m0 :: Markup m0 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c")])) [MarkupLeaf (Markup "leaf" mempty mempty), Content "text"] -- Changing class m1 :: Markup m1 = Markup "top" (Attributes (fromList [("class", "b"), ("b", "c")])) [MarkupLeaf (Markup "leaf" mempty mempty), Content "text"] -- deleting an attribute m2 :: Markup m2 = Markup "top" (Attributes (fromList [("class", "a")])) [MarkupLeaf (Markup "leaf" mempty mempty), Content "text"] -- inserting an attribute m3 :: Markup m3 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c"), ("d", "e")])) [MarkupLeaf (Markup "leaf" mempty mempty), Content "text"] -- changing a tag m4 :: Markup m4 = Markup "newtop" (Attributes (fromList [("class", "a"), ("b", "c")])) [MarkupLeaf (Markup "leaf" mempty mempty), Content "text"] -- changing a leaf m5 :: Markup m5 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c")])) [MarkupLeaf (Markup "newleaf" mempty mempty), Content "text"] -- deleting a leaf m6 :: Markup m6 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c")])) [Content "text"] -- inserting a leaf m7 :: Markup m7 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c")])) [MarkupLeaf (Markup "newleaf" mempty mempty), MarkupLeaf (Markup "leaf" mempty mempty), Content "text"] -- inserting Attributes m8 :: Markup m8 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c")])) [MarkupLeaf (Markup "leaf" (Attributes (fromList [("class", "a"), ("b", "c")])) mempty), Content "text"] -- modifying content m9 :: Markup m9 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c")])) [MarkupLeaf (Markup "leaf" mempty mempty), Content "textual content"] -- inserting a leaf deeper down m10 :: Markup m10 = Markup "top" (Attributes (fromList [("class", "a"), ("b", "c")])) [MarkupLeaf (Markup "leaf" mempty [MarkupLeaf (Markup "newdeepleaf" mempty [Content "deeper content"])]), Content "textual content"]