module Plat.Rendering.Errors ( ContextType(..), RenderErr(..), renderErr ) where import qualified Data.ByteString.UTF8 as B import Data.List import Plat.Context import Plat.Utils -- | This is an auxiliary type, which indicates what is the type we expected, -- or what is the actual type we've got. data ContextType = CtxString | CtxBool | CtxRecord | CtxArray instance Show ContextType where show CtxString = "string" show CtxBool = "boolean" show CtxRecord = "record" show CtxArray = "array" -- | This type represents a rendering error. Note that we provide a 'renderErr' function -- which can be used if you want to present an error to the user. -- -- This type internals are exposed for you to pattern-match on them, although they can -- change in the future versions. data RenderErr = ErrorType ContextType ContextType Pos [B.ByteString] | -- ^ The expression evaluates to something of the wrong type. -- -- Here, first type is the actual one, while the second is the expected one. ErrorField ContextType Pos [B.ByteString] | -- ^ The expression can't be evaluated, since there is no field with the given name -- in the record. ErrorOption (Pos, Pos) [RenderErr] | -- ^ There are several options in the template, but all of them failed for some reason. -- -- Error messages for all options are listed here. ErrorCheck Pos [B.ByteString] -- ^ Template asks to check for some boolean expression, which happened to be false. showBS :: [B.ByteString] -> String showBS bs = concat $ intersperse "." $ map B.toString bs instance Show RenderErr where show (ErrorType c1 c2 p bs) = show p ++ ": " ++ showBS bs ++ " - " ++ show c1 ++ " instead of " ++ show c2 show (ErrorField c p bs) = show p ++ ": " ++ showBS bs ++ " - no field in a " ++ show c show (ErrorOption (p1, p2) ems) = show p1 ++ "-" ++ show p2 ++ ": no valid option - " ++ show ems show (ErrorCheck p bs) = show p ++ ": " ++ showBS bs ++ " = false" -- | This function is similar to 'templateErr'. It allows you to show the rendering error -- to the user. The context it generates always has a value named \"@pos@\", indicating -- the position where an error occured; if multiple branches failed, there is also a value -- \"endpos\", which indicates the position where the list of branches ends. Positions are -- presented with the 'posContext' function. -- -- Other field depend on the error. For a type mismatch, there are values \"expected\" and -- \"actual\", which are the strings \'string\', \'boolean\', \'array\' or \'record\', -- depending on what type the value should have and what type it actually has; -- there is also the \"expr\" field, which presents the list of names in the failing -- expression with the function 'listValues'. -- -- If the required field wasn't found in the record, then there is the value \"field\", -- which is a list of names in the failing expression, and the value \"type\", which -- shows what type that expression should have. -- -- If there were multiple branches, all of them failing, then, apart from \"pos\" and -- \"endpos\" fields, there is a list of errors, one for each of the branches. These -- errors are presented using the 'renderErr' function recursively. -- -- Finally, if the boolean value was checked and happened to be false, there would be -- the field \"check\", which, again, is a list of names in the expression, which -- evaluated to false. renderErr :: RenderErr -> Context () renderErr (ErrorType c1 c2 p bs) = do "pos" =: posContext p "expected" =: show c1 "actual" =: show c2 "expr" =: listValues bs renderErr (ErrorField c p bs) = do "pos" =: posContext p "type" =: show c "field" =: listValues bs renderErr (ErrorOption (p1, p2) ems) = do "pos" =: posContext p1 "endpos" =: posContext p2 "options" =: listValues (map renderErr ems) renderErr (ErrorCheck p bs) = do "pos" =: posContext p "check" =: listValues bs