msg: { actual, expected ? { }, asString ? false, asJSON ? false , removeDunders ? false, safeToPrint ? true, throws ? false }: let preProcess = v: if removeDunders then undunder v else if asString then toString v else if asJSON then builtins.toJSON v else v; a = preProcess actual; e = preProcess expected; undunder = v: if builtins.isAttrs v then builtins.removeAttrs v [ "__toString" "__functor" ] else v; out = (if safeToPrint then builtins.toJSON (undunder (if throws then (builtins.tryEval actual).value else { inherit actual expected; })) else ''{"msg": "cannot be stringified ):"}''); success = if throws then (builtins.tryEval actual).success == false else (a == e); in if success then '' echo 'it ${msg}' '' else builtins.trace "FAILED ${msg}" (let file = builtins.toFile "value" out; in '' echo FAILED ${msg} echo '${file}' cat '${file}' | jq '.' '')