1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
{- Copyright 2017 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
{-# LANGUAGE OverloadedStrings, FlexibleContexts, FlexibleInstances, TypeSynonymInstances #-}
module Graphviz (graphviz) where
import Types
import CmdLine
import Log
import Data.Char hiding (Control)
import Data.GraphViz
import Data.GraphViz.Attributes.Complete
import Data.GraphViz.Types.Generalised as G
import Data.GraphViz.Types.Monadic
import Control.Monad
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Encoding as T
import Data.Text.Encoding.Error
import Data.Monoid
import Prelude
graphviz :: GraphvizOpts -> IO ()
graphviz opts = do
l <- streamLog (graphvizLogFile opts)
let g = genGraph opts l
f <- createImage (graphvizLogFile opts) Png g
putStrLn ("Generated " ++ f)
createImage :: PrintDotRepr dg n => FilePath -> GraphvizOutput -> dg n -> IO FilePath
createImage f o g = Data.GraphViz.addExtension (runGraphvizCommand Dot g) o f
genGraph :: GraphvizOpts -> [Either String Log] -> G.DotGraph T.Text
genGraph opts ls = digraph (Str "debug-me") $ do
nodeAttrs [style filled]
forM_ ls $
showlog [ xcolor Green ]
where
showlog s (Right l) = showactivity s l
showlog _ (Left l) = node (display l) [xcolor Red, shape DiamondShape]
showactivity s l = case (loggedMessage l, loggedHash l) of
(User (ActivityMessage a), Just h) -> do
node (display h) $ s ++
[ textLabel $ prettyDisplay $ activity a
, shape BoxShape
]
linkprev s a h
(Developer (ActivityMessage a), Just h) -> do
node (display h) $ s ++
[ textLabel $ prettyDisplay $ activity a
, shape Circle
]
linkprev s a h
(User (ControlMessage c), Nothing) -> showcontrol c
(Developer (ControlMessage c), Nothing) -> showcontrol c
_ -> return ()
showcontrol (Control (EnteredRejected hr _) _) = do
let rejstyle =
[ xcolor Red
, Style [dashed, filled]
]
let nodename = display $ "Rejected: " <> display hr
node nodename $ rejstyle ++
[ textLabel "Rejected"
, shape BoxShape
]
edge nodename (display hr) rejstyle
showcontrol _ = return ()
linkprev s a h = do
case prevActivity a of
Nothing -> return ()
Just p -> link p h s
case prevEntered a of
Nothing -> return ()
Just p -> link p h (s ++ enteredpointerstyle)
link a b s = edge (display a) (display b) $ s ++
if graphvizShowHashes opts
then [ textLabel (prettyDisplay a) ]
else []
enteredpointerstyle = [ xcolor Gray ]
xcolor :: X11Color -> Attribute
xcolor c = Color [toWC $ X11Color c]
class Display t where
-- Display more or less as-is, for graphviz.
display :: t -> T.Text
-- Prettified display for user-visible labels etc.
prettyDisplay :: t -> T.Text
prettyDisplay = prettyDisplay . display
instance Display T.Text where
display = id
prettyDisplay t
| all visible s = t
| all isPrint s && not (leadingws s) && not (leadingws (reverse s)) = t
| otherwise = T.pack (show s)
where
s = T.unpack t
visible c = isPrint c && not (isSpace c)
leadingws (c:_) = isSpace c
leadingws _ = False
instance Display String where
display = display . T.pack
instance Display Val where
display (Val b) = T.decodeUtf8With lenientDecode (L.fromStrict b)
instance Display Hash where
display (Hash m h) = T.pack (show m) <> display h
-- Use short hash for pretty display.
-- The "h:" prefix is to work around this bug:
-- https://github.com/ivan-m/graphviz/issues/16
prettyDisplay h = display $ Val $ "h:" <> (B.take 5 $ val $ hashValue h)
instance Display Seen where
display = display . seenData
instance Display Entered where
display v
| B.null (val $ echoData v) = display $ enteredData v
| otherwise = "[" <> display (echoData v) <> "] " <> display (enteredData v)
instance Display Control where
display = display . control
instance Display ControlAction where
display = T.pack . show
|