packages feed

pa-error-tree (empty) → 0.1.0.0

raw patch · 5 files changed

+211/−0 lines, 5 filesdep +basedep +containersdep +pa-prelude

Dependencies added: base, containers, pa-prelude

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for pa-error-tree++## 0.1.0.0 -- 2023-05-19++- First release
+ LICENSE view
@@ -0,0 +1,11 @@+Copyright 2023 Possehl Analytics GmbH++Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,9 @@+# pa-error-tree++Oftentimes you want to validate a bunch of things, recursively.++In that case, stopping at the first error is not the best thing to do, instead you’d want to accumulate errors and print the whole tree of problems in one go (e.g. when validating some input).++This library provides a simple wrapper around `Data.Tree` and `Error` which can be combined with `Validation (NonEmpty Error)` and `Validation (NonEmpty ErrorTree)` to elegantly collect and annotate errors recursively.++Note that it only deals with errors that should be displayed to other programmers, and only error strings. If you need to validate and display (structured) user errors, this is not it.
+ pa-error-tree.cabal view
@@ -0,0 +1,77 @@+cabal-version:      3.0+-- !!! ATTN: file autogenerated from pa-template.cabal.mustache on toplevel !!!+name:               pa-error-tree+version:            0.1.0.0+synopsis:           Collect a tree of errors and pretty-print+description:        +license:            BSD-3-Clause+license-file:       LICENSE+-- author:+maintainer:         Philip Patsch <philip.patsch@possehl-analytics.com>+copyright:          2023 Possehl Analytics GmbH+homepage:           https://github.com/possehl-analytics/pa-hackage+category:           Data, Possehl-Analytics+build-type:         Simple+extra-doc-files:    CHANGELOG.md+                    README.md+-- extra-source-files:+++common common-options+  ghc-options:+      -Wall+      -Wno-type-defaults+      -Wunused-packages+      -Wredundant-constraints+      -fwarn-missing-deriving-strategies++  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html+  -- for a description of all these extensions+  default-extensions:+      -- Infer Applicative instead of Monad where possible+    ApplicativeDo++    -- Allow literal strings to be Text+    OverloadedStrings++    -- Syntactic sugar improvements+    LambdaCase+    MultiWayIf++    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error+    NoStarIsType++    -- Convenient and crucial to deal with ambiguous field names, commonly+    -- known as RecordDotSyntax+    OverloadedRecordDot++    -- does not export record fields as functions, use OverloadedRecordDot to access instead+    NoFieldSelectors++    -- Record punning+    RecordWildCards++    -- Improved Deriving+    DerivingStrategies+    DerivingVia++    -- Type-level strings+    DataKinds++    -- to enable the `type` keyword in import lists (ormolu uses this automatically)+    ExplicitNamespaces++  default-language: GHC2021++library+    import:           common-options+    exposed-modules:+      Data.Error.Tree,+    -- other-modules:+    -- other-extensions:+    hs-source-dirs:   src+    build-depends:+      base <5,+      pa-prelude,+      containers,+
+ src/Data/Error/Tree.hs view
@@ -0,0 +1,109 @@+module Data.Error.Tree where++import Data.String (IsString (..))+import Data.Tree qualified as Tree+import PossehlAnalyticsPrelude++-- | A tree of 'Error's, with a single root 'Error' and 0..n nested 'ErrorTree's.+--+-- @+-- top error+-- |+-- |-- error 1+-- | |+-- |  -- error 1.1+-- |+-- |-- error 2+-- @+newtype ErrorTree = ErrorTree {unErrorTree :: (Tree.Tree Error)}+  deriving stock (Show)++instance IsString ErrorTree where+  fromString = singleError . fromString++-- deriving newtype (Ord) -- TODO: Add this instance with containers-0.6.5++-- | Turn a single 'Error' into an 'ErrorTree', a leaf.+singleError :: Error -> ErrorTree+singleError e = ErrorTree $ Tree.Node e []++-- | Take a list of errors & create a new 'ErrorTree' with the given 'Error' as the root.+errorTree :: Error -> NonEmpty Error -> ErrorTree+errorTree topLevelErr nestedErrs =+  ErrorTree+    ( Tree.Node+        topLevelErr+        (nestedErrs <&> (\e -> Tree.Node e []) & toList)+    )++-- | Attach more context to the root 'Error' of the 'ErrorTree', via 'errorContext'.+errorTreeContext :: Text -> ErrorTree -> ErrorTree+errorTreeContext context (ErrorTree tree) =+  ErrorTree $+    tree+      { Tree.rootLabel = tree.rootLabel & errorContext context+      }++-- | Nest the given 'Error' around the ErrorTree+--+-- @+-- top level error+-- |+-- -- nestedError+--   |+--   -- error 1+--   |+--   -- error 2+-- @+nestedError ::+  Error -> -- top level+  ErrorTree -> -- nested+  ErrorTree+nestedError topLevelErr nestedErr =+  ErrorTree $+    Tree.Node+      { Tree.rootLabel = topLevelErr,+        Tree.subForest = [nestedErr.unErrorTree]+      }++-- | Nest the given 'Error' around the list of 'ErrorTree's.+--+-- @+-- top level error+-- |+-- |- nestedError1+-- | |+-- | -- error 1+-- | |+-- | -- error 2+-- |+-- |- nestedError 2+-- @+nestedMultiError ::+  Error -> -- top level+  NonEmpty ErrorTree -> -- nested+  ErrorTree+nestedMultiError topLevelErr nestedErrs =+  ErrorTree $+    Tree.Node+      { Tree.rootLabel = topLevelErr,+        Tree.subForest = nestedErrs & toList <&> (.unErrorTree)+      }++prettyErrorTree :: ErrorTree -> Text+prettyErrorTree (ErrorTree tree) =+  tree+    <&> prettyError+    <&> textToString+    & Tree.drawTree+    & stringToText++prettyErrorTrees :: NonEmpty ErrorTree -> Text+prettyErrorTrees forest =+  forest+    <&> (.unErrorTree)+    <&> fmap prettyError+    <&> fmap textToString+    & toList+    & Tree.drawForest+    & stringToText