{-| Module : MasterPlan.Backend.Identity Description : a backend that renders to a text that can be parsed Copyright : (c) Rodrigo Setti, 2017 License : MIT Maintainer : rodrigosetti@gmail.com Stability : experimental Portability : POSIX -} {-# LANGUAGE UnicodeSyntax #-} {-# LANGUAGE OverloadedStrings #-} module MasterPlan.Backend.Identity (render) where import Control.Monad (when, void) import Control.Monad.RWS (RWS, evalRWS, gets, tell, modify, asks) import Data.Generics import Data.List (nub) import qualified Data.List.NonEmpty as NE import qualified Data.Map as M import Data.Monoid ((<>)) import qualified Data.Text as T import Data.Maybe (fromMaybe) import MasterPlan.Data -- |Plain text renderer render ∷ ProjectSystem → [ProjProperty] -> T.Text render (ProjectSystem bs) whitelist = snd $ evalRWS (renderName "root" >> renderRest) whitelist bs where renderRest = gets M.keys >>= mapM_ renderName type RenderMonad = RWS [ProjProperty] T.Text (M.Map String Binding) renderLine ∷ T.Text → RenderMonad () renderLine s = tell $ s <> ";\n" renderName ∷ ProjectKey → RenderMonad () renderName projName = do mb <- gets $ M.lookup projName case mb of Nothing -> pure () Just b -> do rendered <- renderBinding projName b when rendered $ tell "\n" -- empty line to separate bindings modify $ M.delete projName mapM_ renderName $ dependencies b dependencies ∷ Binding → [ProjectKey] dependencies = nub . everything (++) ([] `mkQ` collectDep) where collectDep (Reference n) = [n] collectDep _ = [] renderProps ∷ String → ProjectProperties → RenderMonad Bool renderProps projName p = or <$> sequence [ renderProperty projName PTitle (title p) projName (T.pack . show) , renderProperty projName PDescription (description p) Nothing (T.pack . show . fromMaybe "") , renderProperty projName PUrl (url p) Nothing (T.pack . show . fromMaybe "") , renderProperty projName POwner (owner p) Nothing (T.pack . show . fromMaybe "") ] renderProperty ∷ Eq a ⇒ ProjectKey → ProjProperty → a → a → (a → T.Text) → RenderMonad Bool renderProperty projName prop val def toText | val == def = pure False | otherwise = do whitelisted <- asks (prop `elem`) when whitelisted $ renderLine $ T.pack (show prop) <> "(" <> T.pack projName <> ") = " <> toText val pure whitelisted renderBinding ∷ ProjectKey → Binding → RenderMonad Bool renderBinding projName (BindingPlaceholder p) = renderProps projName p renderBinding projName (BindingAtomic props c t p) = or <$> sequence [ renderProps projName props , renderProperty projName PCost c 0 (T.pack . show) , renderProperty projName PTrust t 1 percentage , renderProperty projName PProgress p 0 percentage ] where percentage n = T.pack $ show (n * 100) <> "%" renderBinding projName (BindingExpr pr e) = do void $ renderProps projName pr renderLine $ T.pack projName <> " = " <> expressionToStr False e pure True where combinedEToStr parens op ps = let sube = map (expressionToStr True) $ NE.toList ps s = T.intercalate (" " <> op <> " ") sube in if parens && length ps > 1 then "(" <> s <> ")" else s expressionToStr :: Bool -> ProjectExpr -> T.Text expressionToStr _ (Reference n) = T.pack n expressionToStr parens (Product ps) = combinedEToStr parens "*" ps expressionToStr parens (Sequence ps) = combinedEToStr parens "->" ps expressionToStr parens (Sum ps) = combinedEToStr parens "+" ps