packages feed

validationt (empty) → 0.1.0.0

raw patch · 5 files changed

+218/−0 lines, 5 filesdep +aesondep +basedep +lenssetup-changed

Dependencies added: aeson, base, lens, transformers

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for validationt++## 0.1.0.0  -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, Denis Redozubov++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * 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.++    * Neither the name of Denis Redozubov nor the names of other+      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+OWNER 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Control/Monad/Validation.hs view
@@ -0,0 +1,153 @@++module Common.Validation where++import Control.Lens hiding ((.=))+import Control.Monad.Base+import Control.Monad.Catch+import Control.Monad.Except+import Control.Monad.State.Strict+import Control.Monad.Trans.Lift.Local+import Data.Aeson+import Data.Foldable as F+import Data.List as L+import Data.Map.Strict as M+import Data.Monoid+import Data.Text as T+import Data.Vector as V+import Test.QuickCheck++-- | Collects all throwed "warnings" throwed through StateT and "errors" throwed+-- through ExceptT to single value using Monoid+--  FIXME: give more instances like HReaderT and MonadBaseControl/MonadMask+newtype ValidationT e m a = ValidationT+  { unValidationT :: ExceptT e (StateT e m) a+  } deriving ( Functor, Applicative, Monad, MonadThrow, MonadCatch+             , MonadBase b )++instance MonadTrans (ValidationT e) where+  lift = ValidationT . lift . lift++instance LiftLocal (ValidationT e) where+  liftLocal _ l f = ValidationT . mapExceptT (mapStateT $ l f) . unValidationT++-- | Map with 'Monoid' instance which 'mappend' its values+newtype MonoidMap k v = MonoidMap (Map k v)+  deriving (Eq, Ord, Show, Arbitrary)++makePrisms ''MonoidMap++type instance IxValue (MonoidMap k v) = v+type instance Index (MonoidMap k v) = k+instance (Ord k) => Ixed (MonoidMap k v) where+  ix key = _MonoidMap . ix key+instance (Ord k) => At (MonoidMap k v) where+  at key = _MonoidMap . at key++instance (Ord k, Monoid v) => Monoid (MonoidMap k v) where+  mempty = MonoidMap M.empty+  mappend (MonoidMap a) (MonoidMap b) =+    MonoidMap $ M.unionWith (<>) a b++instance (ToJSON k, ToJSON v) => ToJSON (MonoidMap k v) where+  toJSON (MonoidMap m) = toJSON $ L.map toObj $ M.toList m+    where+      toObj (k, v) = object+        [ "id" .= k+        , "value" .= v ]++instance (Ord k, FromJSON k, FromJSON v) => FromJSON (MonoidMap k v) where+  parseJSON v = withArray "MonoidMap" go v+    where+      go arr = do+        keyvals <- traverse fromObj arr+        return $ MonoidMap $ M.fromList $ V.toList keyvals+      fromObj objV = flip (withObject "element of MonoidMap") objV $ \obj -> do+        key <- obj .: "id"+        val <- obj .: "value"+        return (key, val)++-- | Convenient for 'vZoom' as first artument. Will prevent generation+-- of map with 'mempty' values+mmSingleton :: (Eq v, Monoid v, Ord k) => k -> v -> MonoidMap k v+mmSingleton k = memptyWrap mempty $ MonoidMap . M.singleton k++-- | Set given value to 'mempty'+setMempty :: (Monoid s) => ASetter' s a -> a -> s+setMempty setter a = set setter a mempty++memptyWrap :: (Eq a, Monoid a) => b -> (a -> b) -> a -> b+memptyWrap b f a+  | a == mempty = b+  | otherwise = f a++-- | If given container is not 'mempty', then use given function to+-- append all its elements and return 'Just' result+neConcat+  :: (Foldable f, Eq (f a), Monoid a, Monoid (f a))+  => (a -> a -> a)+  -> f a+  -> Maybe a+neConcat f = memptyWrap Nothing (Just . F.foldl' f mempty)++textErrors :: [Text] -> Maybe Text+textErrors = neConcat (\a b -> a <> ", " <> b)++-- | Returns `mempty` instead of error if no warnings was occured. So, your+-- error should have `Eq` instance to detect that any error was occured. Returns+-- Nothing for second element of tuple if compuration was interruped by 'vError'+runValidationT :: (Monoid e, Monad m) => ValidationT e m a -> m (e, Maybe a)+runValidationT (ValidationT m) = do+  (res, warnings) <- runStateT (runExceptT m) mempty+  return $ case res of+    Left err -> (err <> warnings, Nothing)+    Right a  -> (warnings, Just a)++runValidationTEither+  :: (Monoid e, Eq e, Monad m)+  => ValidationT e m a+  -> m (Either e a)+runValidationTEither action = do+  (err, res) <- runValidationT action+  return $ case res of+    Just a | err == mempty -> Right a+    _                      -> Left err++handleValidationT+  :: (Monoid e, Monad m, Eq e)+  => (e -> m a)+  -> ValidationT e m a+  -> m a+handleValidationT handler action = do+  runValidationTEither action >>= either handler return++-- | Stops further execution of validation+vError :: (Monad m) => e -> ValidationT e m a+vError e = ValidationT $ throwError e++-- | Does not stop further execution, append warning to+vWarning :: (Monad m, Monoid e) => e -> ValidationT e m ()+vWarning e = ValidationT $ modify' (<> e)++vErrorL :: (Monad m, Monoid e) => ASetter' e a -> a -> ValidationT e m x+vErrorL l a = vError $ setMempty l a++vWarningL :: (Monad m, Monoid e) => ASetter' e a -> a -> ValidationT e m ()+vWarningL l a = vWarning $ setMempty l a++vZoom+  :: (Monad m, Monoid a, Monoid b)+  => (a -> b)+  -> ValidationT a m x+  -> ValidationT b m x+vZoom up action = do+  (err, res) <- lift $ runValidationT action+  case res of+    Nothing -> vError $ up err+    Just a  -> vWarning (up err) *> return a++vZoomL+  :: (Monad m, Monoid a, Monoid b)+  => ASetter' b a+  -> ValidationT a m x+  -> ValidationT b m x+vZoomL l action = vZoom (setMempty l) action
+ validationt.cabal view
@@ -0,0 +1,28 @@+-- Initial validationt.cabal generated by cabal init.  For further+-- documentation, see http://haskell.org/cabal/users-guide/++name:                validationt+version:             0.1.0.0+synopsis:            Straightforward validation monad. Convenient solution for validating web forms and APIs.+-- description:+homepage:            typeable.io+license:             BSD3+license-file:        LICENSE+author:              Typeable.io contributors+maintainer:          makeit@typeable.io+-- copyright:+category:            Control+build-type:          Simple+extra-source-files:  ChangeLog.md+cabal-version:       >=1.10++library+  exposed-modules:    Control.Monad.Validation+  -- other-modules:+  -- other-extensions:+  build-depends:       base >=4.8 && < 5+                     , aeson >= 1.0+                     , transformers+                     , lens+  hs-source-dirs:      src+  default-language:    Haskell2010