packages feed

autodocodec-servant-multipart (empty) → 0.0.0.0

raw patch · 4 files changed

+321/−0 lines, 4 filesdep +aesondep +autodocodecdep +base

Dependencies added: aeson, autodocodec, base, bytestring, servant-multipart, servant-multipart-api, text, unordered-containers, vector

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Changelog++## [0.0.0.0] - 2022-07-27++First version
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2022 Tom Sydney Kerckhove++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ autodocodec-servant-multipart.cabal view
@@ -0,0 +1,43 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.7.+--+-- see: https://github.com/sol/hpack++name:           autodocodec-servant-multipart+version:        0.0.0.0+synopsis:       Autodocodec interpreters for Servant Multipart+homepage:       https://github.com/NorfairKing/autodocodec#readme+bug-reports:    https://github.com/NorfairKing/autodocodec/issues+author:         Tom Sydney Kerckhove+maintainer:     syd@cs-syd.eu+copyright:      2022 Tom Sydney Kerckhove+license:        MIT+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    LICENSE+    CHANGELOG.md++source-repository head+  type: git+  location: https://github.com/NorfairKing/autodocodec++library+  exposed-modules:+      Autodocodec.Multipart+  other-modules:+      Paths_autodocodec_servant_multipart+  hs-source-dirs:+      src+  build-depends:+      aeson+    , autodocodec >=0.2.0.1+    , base >=4.7 && <5+    , bytestring+    , servant-multipart+    , servant-multipart-api+    , text+    , unordered-containers+    , vector+  default-language: Haskell2010
+ src/Autodocodec/Multipart.hs view
@@ -0,0 +1,252 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Autodocodec.Multipart where++import Autodocodec+import Data.Aeson as JSON+import Data.Aeson.Types as JSON+import qualified Data.ByteString.Lazy as LB+import Data.Foldable+import qualified Data.HashMap.Strict as HashMap+import Data.Maybe+import Data.Text (Text)+import qualified Data.Text.Encoding as TE+import qualified Data.Vector as V+import Servant.Multipart as Servant+import Servant.Multipart.API as Servant++toMultipartViaCodec :: forall a tag. HasObjectCodec a => a -> MultipartData tag+toMultipartViaCodec = toMultipartVia (objectCodec @a)++toMultipartVia :: ObjectCodec a void -> a -> MultipartData tag+toMultipartVia = flip go+  where+    go :: a -> ObjectCodec a void -> MultipartData tag+    go a = \case+      BimapCodec _ to c -> go (to a) c+      EitherCodec _ c1 c2 -> case a of+        Left a1 -> go a1 c1+        Right a2 -> go a2 c2+      DiscriminatedUnionCodec discriminator encoding _ ->+        let (discriminatorValue, c) = encoding a+         in mappendMultipartData+              ( MultipartData+                  { inputs = [Input discriminator discriminatorValue],+                    files = []+                  }+              )+              (go a c)+      RequiredKeyCodec key vc _ ->+        MultipartData+          { inputs = map (Input key) (goValue a vc),+            files = []+          }+      OptionalKeyCodec key vc _ ->+        MultipartData+          { inputs = do+              a' <- maybeToList a+              v <- goValue a' vc+              pure $ Input key v,+            files = []+          }+      OptionalKeyWithDefaultCodec key vc _ _ ->+        MultipartData+          { inputs = map (Input key) (goValue a vc),+            files = []+          }+      OptionalKeyWithOmittedDefaultCodec key vc defaultValue _ ->+        MultipartData+          { inputs =+              if a == defaultValue+                then []+                else map (Input key) (goValue a vc),+            files = []+          }+      PureCodec _ -> memptyMultipartData+      ApCodec oc1 oc2 -> mappendMultipartData (go a oc1) (go a oc2)++    goValue :: a -> ValueCodec a void -> [Text]+    goValue a = \case+      BimapCodec _ to vc -> goValue (to a) vc+      EitherCodec _ c1 c2 -> case a of+        Left a1 -> goValue a1 c1+        Right a2 -> goValue a2 c2+      CommentCodec _ vc -> goValue a vc+      ArrayOfCodec _ vc -> map (`goSingleValue` vc) (toList a)+      vc -> [goSingleValue a vc]++    goSingleValue :: a -> ValueCodec a void -> Text+    goSingleValue a = \case+      BimapCodec _ to vc -> goSingleValue (to a) vc+      EitherCodec _ c1 c2 -> case a of+        Left a1 -> goSingleValue a1 c1+        Right a2 -> goSingleValue a2 c2+      CommentCodec _ vc -> goSingleValue a vc+      NullCodec -> "null"+      BoolCodec _ ->+        case a of+          True -> "True"+          False -> "False"+      StringCodec _ -> a+      vc ->+        let value = toJSONVia vc a+         in case value of+              JSON.String t -> t+              _ -> TE.decodeUtf8 (LB.toStrict (JSON.encode value))++memptyMultipartData :: MultipartData tag+memptyMultipartData =+  MultipartData+    { inputs = [],+      files = []+    }++mappendMultipartData :: MultipartData tag -> MultipartData tag -> MultipartData tag+mappendMultipartData mpd1 mpd2 =+  MultipartData+    { inputs = inputs mpd1 ++ inputs mpd2,+      files = files mpd1 ++ files mpd2+    }++instance HasObjectCodec a => Servant.ToMultipart tag (Autodocodec a) where+  toMultipart = toMultipartViaCodec . unAutodocodec++fromMultipartViaCodec :: forall a tag. HasObjectCodec a => MultipartData tag -> Either String a+fromMultipartViaCodec = fromMultipartVia (objectCodec @a)++fromMultipartVia :: ObjectCodec void a -> MultipartData tag -> Either String a+fromMultipartVia = flip go+  where+    go :: MultipartData tag -> ObjectCodec void a -> Either String a+    go mpd = \case+      BimapCodec from _ c -> go mpd c >>= from+      EitherCodec u c1 c2 -> case u of+        PossiblyJointUnion ->+          case go mpd c1 of+            Right l -> pure (Left l)+            Left err1 -> case go mpd c2 of+              Left err2 -> Left $ "  Previous branch failure: " <> err1 <> "\n" <> err2+              Right r -> pure (Right r)+        DisjointUnion ->+          case (go mpd c1, go mpd c2) of+            (Left _, Right r) -> pure (Right r)+            (Right l, Left _) -> pure (Left l)+            (Right _, Right _) -> Left "Both branches of a disjoint union succeeded."+            (Left lErr, Left rErr) ->+              Left $+                unlines+                  [ "Both branches of a disjoint union failed: ",+                    unwords ["Left:  ", lErr],+                    unwords ["Right: ", rErr]+                  ]+      DiscriminatedUnionCodec discriminator _ m -> do+        discriminatorValue <- lookupInput discriminator mpd+        case HashMap.lookup discriminatorValue m of+          Nothing -> Left $ "Unexpected discriminator value: " <> show discriminatorValue+          Just (_, c) -> go mpd c+      RequiredKeyCodec key vc _ -> do+        values <- lookupLInput key mpd+        goValue values vc+      OptionalKeyCodec key vc _ -> do+        values <- lookupLInput key mpd+        case values of+          [] -> pure Nothing+          _ -> Just <$> goValue values vc+      OptionalKeyWithDefaultCodec key vc defaultValue _ -> do+        values <- lookupLInput key mpd+        case values of+          [] -> pure defaultValue+          _ -> goValue values vc+      OptionalKeyWithOmittedDefaultCodec key vc defaultValue _ -> do+        values <- lookupLInput key mpd+        case values of+          [] -> pure defaultValue+          _ -> goValue values vc+      PureCodec v -> pure v+      ApCodec ocf oca -> go mpd ocf <*> go mpd oca++    goValue :: [Text] -> ValueCodec void a -> Either String a+    goValue ts = \case+      BimapCodec from _ c -> goValue ts c >>= from+      EitherCodec u c1 c2 -> case u of+        PossiblyJointUnion ->+          case goValue ts c1 of+            Right l -> pure (Left l)+            Left err1 -> case goValue ts c2 of+              Left err2 -> Left $ "  Previous branch failure: " <> err1 <> "\n" <> err2+              Right r -> pure (Right r)+        DisjointUnion ->+          case (goValue ts c1, goValue ts c2) of+            (Left _, Right r) -> pure (Right r)+            (Right l, Left _) -> pure (Left l)+            (Right _, Right _) -> Left "Both branches of a disjoint union succeeded."+            (Left lErr, Left rErr) ->+              Left $+                unlines+                  [ "Both branches of a disjoint union failed: ",+                    unwords ["Left:  ", lErr],+                    unwords ["Right: ", rErr]+                  ]+      ReferenceCodec _ vc -> goValue ts vc+      CommentCodec _ c -> goValue ts c+      ArrayOfCodec _ vc -> V.fromList <$> mapM (`goSingleValue` vc) (toList ts)+      vc -> case ts of+        [t] -> goSingleValue t vc+        _ -> Left "Expected exactly one value."++    goSingleValue :: Text -> ValueCodec void a -> Either String a+    goSingleValue t = \case+      BimapCodec from _ c -> goSingleValue t c >>= from+      EitherCodec u c1 c2 -> case u of+        PossiblyJointUnion ->+          case goSingleValue t c1 of+            Right l -> pure (Left l)+            Left err1 -> case goSingleValue t c2 of+              Left err2 -> Left $ "  Previous branch failure: " <> err1 <> "\n" <> err2+              Right r -> pure (Right r)+        DisjointUnion ->+          case (goSingleValue t c1, goSingleValue t c2) of+            (Left _, Right r) -> pure (Right r)+            (Right l, Left _) -> pure (Left l)+            (Right _, Right _) -> Left "Both branches of a disjoint union succeeded."+            (Left lErr, Left rErr) ->+              Left $+                unlines+                  [ "Both branches of a disjoint union failed: ",+                    unwords ["Left:  ", lErr],+                    unwords ["Right: ", rErr]+                  ]+      CommentCodec _ c -> goSingleValue t c+      ReferenceCodec _ vc -> goSingleValue t vc+      NullCodec -> case t of+        "null" -> Right ()+        _ -> Left $ "not 'null': " <> show t+      BoolCodec _ -> case t of+        "false" -> Right False+        "False" -> Right False+        "true" -> Right True+        "True" -> Right True+        _ -> Left $ "Unknown bool: " <> show t+      StringCodec _ -> Right t+      vc -> case JSON.parseEither (parseJSONVia vc) (JSON.String t) of+        Right a -> Right a+        Left _ -> do+          value <- JSON.eitherDecode (LB.fromStrict (TE.encodeUtf8 t))+          JSON.parseEither (parseJSONVia vc) value++lookupMInput :: Text -> MultipartData tag -> Either String (Maybe Text)+lookupMInput iname = Right . fmap iValue . find ((== iname) . iName) . inputs++lookupLInput :: Text -> MultipartData tag -> Either String [Text]+lookupLInput iname = Right . map iValue . filter ((== iname) . iName) . inputs++instance HasObjectCodec a => Servant.FromMultipart tag (Autodocodec a) where+  fromMultipart = fmap Autodocodec . fromMultipartViaCodec