packages feed

hw-uri 0.1.1.4 → 0.1.1.5

raw patch · 12 files changed

+396/−38 lines, 12 filesdep +antiope-optparse-applicativedep +hw-primdep +optparse-applicativedep ~antiope-coredep ~antiope-s3new-component:exe:hw-uriPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

Dependencies added: antiope-optparse-applicative, hw-prim, optparse-applicative, unliftio-core

Dependency ranges changed: antiope-core, antiope-s3

API changes (from Hackage documentation)

+ HaskellWorks.Data.Uri.IO.File: dirname :: FilePath -> FilePath
+ HaskellWorks.Data.Uri.IO.File: listDirectory :: MonadIO m => FilePath -> m [FilePath]
+ HaskellWorks.Data.Uri.IO.File: listFilesRecursive :: (MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]
+ HaskellWorks.Data.Uri.IO.File: listFilesRecursiveWithPrefix :: (MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]
+ HaskellWorks.Data.Uri.IO.Lazy: listResourcePrefix :: (MonadUnliftIO m, MonadResource m) => Env -> Location -> ExceptT UriError m [Location]
+ HaskellWorks.Data.Uri.Internal.List: splitBy :: (a -> Bool) -> [a] -> [[a]]

Files

+ app/App/Aws/Logger.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE OverloadedStrings #-}++module App.Aws.Logger+  ( awsLogger+  ) where++import Antiope.Env        (LogLevel (..))+import App.Show+import Control.Concurrent (myThreadId)+import Control.Monad+import Data.Semigroup     ((<>))++import qualified Data.ByteString.Lazy       as LBS+import qualified Data.ByteString.Lazy.Char8 as LC8+import qualified Data.Text.Encoding         as T+import qualified Data.Text.IO               as TIO+import qualified System.IO                  as IO++awsLogger :: Maybe LogLevel -> LogLevel -> LC8.ByteString -> IO ()+awsLogger maybeConfigLogLevel msgLogLevel message =+  forM_ maybeConfigLogLevel $ \configLogLevel ->+    when (msgLogLevel <= configLogLevel) $ do+      threadId <- myThreadId+      TIO.hPutStrLn IO.stderr $ "[" <> tshow msgLogLevel <> "] [tid: " <> tshow threadId <> "]"  <> text+  where text = T.decodeUtf8 $ LBS.toStrict message
+ app/App/Commands.hs view
@@ -0,0 +1,15 @@+module App.Commands where++import App.Commands.Cp+import App.Commands.LsPrefix+import Data.Semigroup        ((<>))+import Options.Applicative++commands :: Parser (IO ())+commands = commandsGeneral++commandsGeneral :: Parser (IO ())+commandsGeneral = subparser $ mempty+  <>  commandGroup "Commands:"+  <>  cmdCp+  <>  cmdLsPrefix
+ app/App/Commands/Cp.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}++module App.Commands.Cp+  ( cmdCp+  ) where++import Antiope.Core                   (Region (..))+import Antiope.Env                    (mkEnv)+import Antiope.Options.Applicative+import Control.Lens+import Control.Monad.Except+import Control.Monad.Trans.Resource   (runResourceT)+import Data.Generics.Product.Any+import Data.Semigroup                 ((<>))+import HaskellWorks.Data.Uri.Location+import HaskellWorks.Data.Uri.UriError (displayUriError)+import Options.Applicative            hiding (columns)++import qualified App.Aws.Logger                as AWS+import qualified App.Commands.Types            as Z+import qualified Data.Text                     as T+import qualified Data.Text.IO                  as TIO+import qualified HaskellWorks.Data.Uri.IO.Lazy as URI+import qualified System.IO                     as IO+import qualified System.IO.Unsafe              as IO++runCp :: Z.CpOptions -> IO ()+runCp opts = do+  let input       = opts ^. the @"input"+  let output      = opts ^. the @"output"+  let region      = opts ^. the @"region"+  let awsLogLevel = opts ^. the @"awsLogLevel"++  envAws <- IO.unsafeInterleaveIO $ mkEnv region (AWS.awsLogger awsLogLevel)++  result <- runResourceT . runExceptT $ do+    lbsResult <- URI.readResource envAws input++    case lbsResult of+      Right lbs -> void $ URI.writeResource envAws output lbs+      Left msg  -> return ()++    return ()++  case result of+    Right _  -> return ()+    Left msg -> TIO.hPutStrLn IO.stderr (displayUriError msg)++optsCp :: Parser Z.CpOptions+optsCp = Z.CpOptions+  <$> option (maybeReader (toLocation . T.pack))+        (   long "input"+        <>  short 'i'+        <>  help "Input location"+        <>  metavar "LOCATION"+        )+  <*> option (maybeReader (toLocation . T.pack))+        (   long "output"+        <>  short 'o'+        <>  help "Output location"+        <>  metavar "LOCATION"+        )+  <*> option auto+      (  long "region"+      <> metavar "AWS_REGION"+      <> showDefault <> value Oregon+      <> help "The AWS region in which to operate"+      )+  <*> optional+      ( option autoText+        (   long "aws-log-level"+        <>  help "AWS Log Level.  One of (Error, Info, Debug, Trace)"+        <>  metavar "AWS_LOG_LEVEL"+        )+      )++cmdCp :: Mod CommandFields (IO ())+cmdCp = command "cp"  $ flip info idm $ runCp <$> optsCp
+ app/App/Commands/LsPrefix.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}++module App.Commands.LsPrefix+  ( cmdLsPrefix+  ) where++import Antiope.Core                   (Region (..))+import Antiope.Env                    (mkEnv)+import Antiope.Options.Applicative+import App.Show+import Control.Lens+import Control.Monad.Except+import Control.Monad.Trans.Resource   (runResourceT)+import Data.Generics.Product.Any+import Data.Semigroup                 ((<>))+import HaskellWorks.Data.Uri.Location+import HaskellWorks.Data.Uri.UriError (displayUriError)+import Options.Applicative            hiding (columns)++import qualified App.Aws.Logger                as AWS+import qualified App.Commands.Types            as Z+import qualified Data.Text                     as T+import qualified Data.Text.IO                  as TIO+import qualified HaskellWorks.Data.Uri.IO.Lazy as URI+import qualified System.IO                     as IO+import qualified System.IO.Unsafe              as IO++runLsPrefix :: Z.LsPrefixOptions -> IO ()+runLsPrefix opts = do+  let prefix       = opts ^. the @"prefix"+  let region      = opts ^. the @"region"+  let awsLogLevel = opts ^. the @"awsLogLevel"++  envAws <- IO.unsafeInterleaveIO $ mkEnv region (AWS.awsLogger awsLogLevel)++  result <- runResourceT . runExceptT $ do+    locations <- URI.listResourcePrefix envAws prefix+    forM_ locations $ \location -> liftIO $ TIO.putStrLn (tshow location)++    return ()++  case result of+    Right _  -> return ()+    Left msg -> TIO.hPutStrLn IO.stderr (displayUriError msg)++optsLsPrefix :: Parser Z.LsPrefixOptions+optsLsPrefix = Z.LsPrefixOptions+  <$> option (maybeReader (toLocation . T.pack))+        (   long "prefix"+        <>  short 'p'+        <>  help "Prefix location"+        <>  metavar "LOCATION"+        )+  <*> option auto+      (  long "region"+      <> metavar "AWS_REGION"+      <> showDefault <> value Oregon+      <> help "The AWS region in which to operate"+      )+  <*> optional+      ( option autoText+        (   long "aws-log-level"+        <>  help "AWS Log Level.  One of (Error, Info, Debug, Trace)"+        <>  metavar "AWS_LOG_LEVEL"+        )+      )++cmdLsPrefix :: Mod CommandFields (IO ())+cmdLsPrefix = command "ls-prefix"  $ flip info idm $ runLsPrefix <$> optsLsPrefix
+ app/App/Commands/Types.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE DuplicateRecordFields #-}++module App.Commands.Types+  ( CpOptions(..)+  , LsPrefixOptions(..)+  ) where++import Antiope.Env                    (Region)+import GHC.Generics+import HaskellWorks.Data.Uri.Location++import qualified Antiope.Env as AWS++data CpOptions = CpOptions+  { input       :: Location+  , output      :: Location+  , region      :: Region+  , awsLogLevel :: Maybe AWS.LogLevel+  } deriving (Eq, Show, Generic)++data LsPrefixOptions = LsPrefixOptions+  { prefix      :: Location+  , region      :: Region+  , awsLogLevel :: Maybe AWS.LogLevel+  } deriving (Eq, Show, Generic)
+ app/App/Show.hs view
@@ -0,0 +1,10 @@+module App.Show+  ( tshow+  ) where++import Data.Text (Text)++import qualified Data.Text as T++tshow :: Show a => a -> Text+tshow = T.pack . show
+ app/Main.hs view
@@ -0,0 +1,11 @@+module Main where++import App.Commands+import Control.Monad+import Data.Semigroup      ((<>))+import Options.Applicative++main :: IO ()+main = join $ customExecParser+  (prefs $ showHelpOnEmpty <> showHelpOnError)+  (info (commands <**> helper) idm)
hw-uri.cabal view
@@ -1,7 +1,7 @@-cabal-version:          2.2+cabal-version:          2.4  name:                   hw-uri-version:                0.1.1.4+version:                0.1.1.5 synopsis:               Supports IO on URIs description:            Supports IO on URIs. homepage:               https://github.com/haskell-works/hw-uri@@ -23,8 +23,9 @@ common amazonka                       { build-depends: amazonka                       >= 1.6.1      && < 1.7    } common amazonka-core                  { build-depends: amazonka-core                  >= 1.6.1      && < 1.7    } common amazonka-s3                    { build-depends: amazonka-s3                    >= 1.6.1      && < 1.7    }-common antiope-core                   { build-depends: antiope-core                   >= 7.3.3      && < 8      }-common antiope-s3                     { build-depends: antiope-s3                     >= 7.3.3      && < 8      }+common antiope-core                   { build-depends: antiope-core                   >= 7.4.2      && < 8      }+common antiope-s3                     { build-depends: antiope-s3                     >= 7.4.2      && < 8      }+common antiope-optparse-applicative   { build-depends: antiope-optparse-applicative   >= 7.0.1      && < 8      } common bytestring                     { build-depends: bytestring                     >= 0.10.8.2   && < 0.11   } common directory                      { build-depends: directory                      >= 1.3.3.0    && < 1.4    } common exceptions                     { build-depends: exceptions                     >= 0.10.1     && < 0.11   }@@ -35,8 +36,11 @@ common http-client                    { build-depends: http-client                    >= 0.5.14     && < 0.7    } common http-types                     { build-depends: http-types                     >= 0.12.3     && < 0.13   } common hw-hspec-hedgehog              { build-depends: hw-hspec-hedgehog              >= 0.1.0.4    && < 0.2    }+common hw-prim                        { build-depends: hw-prim                        >= 0.6.2.33   && < 0.7    } common lens                           { build-depends: lens                           >= 4.17       && < 5      } common mtl                            { build-depends: mtl                            >= 2.2.2      && < 2.3    }+common optparse-applicative           { build-depends: optparse-applicative           >= 0.14       && < 0.16   }+common unliftio-core                  { build-depends: unliftio-core                  >= 0.1.2.0    && < 0.2    } common resourcet                      { build-depends: resourcet                      >= 1.2.2      && < 1.3    } common text                           { build-depends: text                           >= 1.2.3.1    && < 1.3    } @@ -45,24 +49,26 @@   ghc-options:          -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates  library-  import:   base, config-          , aeson-          , amazonka-          , amazonka-core-          , amazonka-s3-          , antiope-core-          , antiope-s3-          , bytestring-          , directory-          , exceptions-          , filepath-          , generic-lens-          , http-client-          , http-types-          , lens-          , mtl-          , resourcet-          , text+  import:               base, config+                      , aeson+                      , amazonka+                      , amazonka-core+                      , amazonka-s3+                      , antiope-core+                      , antiope-s3+                      , bytestring+                      , directory+                      , exceptions+                      , filepath+                      , generic-lens+                      , http-client+                      , http-types+                      , hw-prim+                      , lens+                      , mtl+                      , resourcet+                      , text+                      , unliftio-core   other-modules:        Paths_hw_uri   autogen-modules:      Paths_hw_uri   hs-source-dirs:       src@@ -79,20 +85,41 @@                         HaskellWorks.Data.Uri.Text                         HaskellWorks.Data.Uri.UriError -test-suite hw-uri-test-  import:   base, config-          , antiope-core-          , antiope-s3-          , bytestring-          , aeson-          , filepath-          , hedgehog-          , hspec-          , http-types-          , hw-hspec-hedgehog-          , lens-          , text+executable hw-uri+  import:               base, config+                      , antiope-core+                      , antiope-optparse-applicative+                      , bytestring+                      , generic-lens+                      , lens+                      , mtl+                      , optparse-applicative+                      , resourcet+                      , text+  main-is:              Main.hs+  hs-source-dirs:       app+  ghc-options:          -threaded -rtsopts -with-rtsopts=-N+  build-depends:        hw-uri+  other-modules:        App.Aws.Logger+                        App.Commands+                        App.Commands.Cp+                        App.Commands.LsPrefix+                        App.Commands.Types+                        App.Show +test-suite hw-uri-test+  import:               base, config+                      , antiope-core+                      , antiope-s3+                      , bytestring+                      , aeson+                      , filepath+                      , hedgehog+                      , hspec+                      , http-types+                      , hw-hspec-hedgehog+                      , lens+                      , text   type:                 exitcode-stdio-1.0   main-is:              Spec.hs   build-depends:        hw-uri@@ -101,4 +128,5 @@   build-tool-depends:   hspec-discover:hspec-discover   other-modules:        HaskellWorks.Data.Uri.Gen                         HaskellWorks.Data.Uri.AwsSpec+                        HaskellWorks.Data.Uri.Internal.ListSpec                         HaskellWorks.Data.Uri.LocationSpec
src/HaskellWorks/Data/Uri/IO/File.hs view
@@ -1,16 +1,61 @@ {-# LANGUAGE OverloadedStrings #-}  module HaskellWorks.Data.Uri.IO.File-  ( listMaybeDirectory+  ( dirname+  , listMaybeDirectory+  , listFilesRecursiveWithPrefix+  , listDirectory+  , listFilesRecursive   ) where  import Control.Monad.Except+import Control.Monad.IO.Unlift+import Data.Semigroup          ((<>))+import System.FilePath -import qualified System.Directory as IO+import qualified Data.List                           as L+import qualified HaskellWorks.Control.Monad.Lazy     as IO+import qualified HaskellWorks.Data.Uri.Internal.List as L+import qualified System.Directory                    as IO +dirname :: FilePath -> FilePath+dirname filePath = case reverse (L.splitBy (== '/') ("$" <> filePath)) of+  []  -> filePath+  [_] -> filePath+  bs  -> drop 1 (L.intercalate "/" (reverse (drop 1 bs)))+ listMaybeDirectory :: MonadIO m => FilePath -> m [FilePath] listMaybeDirectory filepath = do   exists <- liftIO $ IO.doesDirectoryExist filepath   if exists-    then liftIO $ IO.listDirectory filepath+    then listDirectory filepath     else return []++listDirectory :: MonadIO m => FilePath -> m [FilePath]+listDirectory = liftIO . IO.listDirectory++listFilesRecursive :: (MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]+listFilesRecursive filePath = do+  ps <- listDirectory filePath+  qs <- fmap concat $ IO.interleaveSequenceM $ fmap (recurse filePath) ps+  let rs = if filePath /= "." then fmap (filePath </>) qs else qs+  return rs++recurse :: (MonadIO m, MonadUnliftIO m) => FilePath -> FilePath -> m [FilePath]+recurse filePath p = do+  isDirectory <- liftIO $ IO.doesDirectoryExist (filePath </> p)+  if isDirectory+    then case filePath </> p of+      subPath -> if "./" `L.isPrefixOf` subPath+        then listFilesRecursive (drop 2 subPath)+        else listFilesRecursive subPath+    else return [p]++listFilesRecursiveWithPrefix :: (MonadIO m, MonadUnliftIO m) => FilePath -> m [FilePath]+listFilesRecursiveWithPrefix prefix = if '/' `elem` prefix+  then do+    fs <- listFilesRecursive (dirname prefix)+    return (filter (prefix `L.isPrefixOf`) fs)+  else do+    fs <- listFilesRecursive "."+    return (filter (prefix `L.isPrefixOf`) fs)
src/HaskellWorks/Data/Uri/IO/Lazy.hs view
@@ -1,6 +1,8 @@+{-# LANGUAGE DataKinds           #-} {-# LANGUAGE LambdaCase          #-} {-# LANGUAGE OverloadedStrings   #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}  module HaskellWorks.Data.Uri.IO.Lazy   ( readResource@@ -15,6 +17,7 @@   , linkOrCopyResource   , readHttpUri   , removePathRecursive+  , listResourcePrefix   ) where  import Antiope.Core@@ -23,6 +26,7 @@ import Control.Monad.Except import Control.Monad.Trans.Resource import Data.Either                    (isRight)+import Data.Generics.Product.Any import Data.Semigroup                 ((<>)) import Data.Text                      (Text) import HaskellWorks.Data.Uri.Location (Location (..))@@ -35,6 +39,7 @@ import qualified Data.ByteString.Lazy             as LBS import qualified Data.Text                        as T import qualified HaskellWorks.Data.Uri.IO.Console as CIO+import qualified HaskellWorks.Data.Uri.IO.File    as FIO import qualified HaskellWorks.Data.Uri.Location   as URI import qualified Network.AWS                      as AWS import qualified Network.AWS.S3.CopyObject        as AWS@@ -228,3 +233,9 @@         handler e = do           CIO.hPutStrLn IO.stderr $ "Warning: Caught " <> tshow e           return (Left (GenericUriError (tshow e)))++listResourcePrefix :: (MonadUnliftIO m, MonadResource m) => AWS.Env -> Location -> ExceptT UriError m [Location]+listResourcePrefix envAws location = case location of+  S3 s3Uri   -> fmap S3 <$> runAws envAws (AWS.lsPrefix (s3Uri ^. the @"bucket") (s3Uri ^. the @"objectKey" . the @1))+  Local path -> fmap Local <$> lift (FIO.listFilesRecursiveWithPrefix path)+  HttpUri _  -> throwError "HTTP PUT method not supported"
src/HaskellWorks/Data/Uri/Internal/List.hs view
@@ -1,6 +1,7 @@ module HaskellWorks.Data.Uri.Internal.List   ( mapLast   , dropSave1+  , splitBy   ) where  mapLast :: (a -> a) -> [a] -> [a]@@ -12,3 +13,7 @@ dropSave1 [x]    = [x] dropSave1 (_:xs) = xs dropSave1 []     = []++splitBy :: (a -> Bool) -> [a] -> [[a]]+splitBy p as = if null as then [] else go as+  where go bs = let (cs, ds) = break p bs in cs:if null ds then [] else go (drop 1 ds)
+ test/HaskellWorks/Data/Uri/Internal/ListSpec.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}++module HaskellWorks.Data.Uri.Internal.ListSpec+  ( spec+  ) where++import Control.Monad+import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec++import qualified Data.List                           as L+import qualified HaskellWorks.Data.Uri.Internal.List as L+import qualified Hedgehog.Gen                        as G+import qualified Hedgehog.Range                      as R++{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}+{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}+{-# ANN module ("HLint: ignore Redundant bracket"   :: String) #-}++spec :: Spec+spec = describe "HaskellWorks.Data.Uri.Internal.ListSpec" $ do+  it "splitBy" $ requireProperty $ do+    as <- forAll $ G.list (R.linear 1 3) (G.string (R.linear 0 3) G.alpha)+    when (as /= [""]) $ do+      annotateShow $ L.intercalate "/" as+      annotateShow $ L.splitBy (== '/') $ L.intercalate "/" as+      L.splitBy (== '/') (L.intercalate "/" as) === as