desktop-portal 0.1.1.0 → 0.2.0.0
raw patch · 8 files changed
+60/−20 lines, 8 filesdep +modern-uriPVP ok
version bump matches the API change (PVP)
Dependencies added: modern-uri
API changes (from Hackage documentation)
- Desktop.Portal.Account: GetUserInformationResults :: Text -> Text -> Maybe Text -> GetUserInformationResults
+ Desktop.Portal.Account: GetUserInformationResults :: Text -> Text -> Maybe FilePath -> GetUserInformationResults
- Desktop.Portal.Account: [$sel:image:GetUserInformationResults] :: GetUserInformationResults -> Maybe Text
+ Desktop.Portal.Account: [$sel:image:GetUserInformationResults] :: GetUserInformationResults -> Maybe FilePath
- Desktop.Portal.FileChooser: OpenFileResults :: [Text] -> Maybe [ChoiceComboSelection] -> Maybe Filter -> OpenFileResults
+ Desktop.Portal.FileChooser: OpenFileResults :: [FilePath] -> Maybe [ChoiceComboSelection] -> Maybe Filter -> OpenFileResults
- Desktop.Portal.FileChooser: SaveFileOptions :: Maybe Text -> Maybe Text -> Maybe Text -> Maybe Bool -> Maybe [Filter] -> Maybe Filter -> Maybe [ChoiceCombo] -> Maybe Text -> Maybe Text -> Maybe Text -> SaveFileOptions
+ Desktop.Portal.FileChooser: SaveFileOptions :: Maybe Text -> Maybe Text -> Maybe Text -> Maybe Bool -> Maybe [Filter] -> Maybe Filter -> Maybe [ChoiceCombo] -> Maybe Text -> Maybe FilePath -> Maybe FilePath -> SaveFileOptions
- Desktop.Portal.FileChooser: SaveFileResults :: [Text] -> Maybe [ChoiceComboSelection] -> Maybe Filter -> SaveFileResults
+ Desktop.Portal.FileChooser: SaveFileResults :: [FilePath] -> Maybe [ChoiceComboSelection] -> Maybe Filter -> SaveFileResults
- Desktop.Portal.FileChooser: [$sel:currentFile:SaveFileOptions] :: SaveFileOptions -> Maybe Text
+ Desktop.Portal.FileChooser: [$sel:currentFile:SaveFileOptions] :: SaveFileOptions -> Maybe FilePath
- Desktop.Portal.FileChooser: [$sel:currentFolder:SaveFileOptions] :: SaveFileOptions -> Maybe Text
+ Desktop.Portal.FileChooser: [$sel:currentFolder:SaveFileOptions] :: SaveFileOptions -> Maybe FilePath
- Desktop.Portal.FileChooser: [$sel:uris:OpenFileResults] :: OpenFileResults -> [Text]
+ Desktop.Portal.FileChooser: [$sel:uris:OpenFileResults] :: OpenFileResults -> [FilePath]
- Desktop.Portal.FileChooser: [$sel:uris:SaveFileResults] :: SaveFileResults -> [Text]
+ Desktop.Portal.FileChooser: [$sel:uris:SaveFileResults] :: SaveFileResults -> [FilePath]
Files
- ChangeLog.md +4/−0
- README.md +1/−0
- desktop-portal.cabal +3/−1
- src/Desktop/Portal/Account.hs +3/−3
- src/Desktop/Portal/FileChooser.hs +9/−9
- src/Desktop/Portal/Util.hs +34/−1
- test/Desktop/Portal/AccountSpec.hs +2/−2
- test/Desktop/Portal/FileChooserSpec.hs +4/−4
ChangeLog.md view
@@ -1,3 +1,7 @@+## 0.2.0.0+### Changed+- Functions now return FilePath instead of Text, where the value is always a file URI.+ ## 0.1.1.0 ### Added - Add Settings portal support.
README.md view
@@ -25,6 +25,7 @@ - Functions should take records called `...Options` and return records called `...Results`. - `...Options` records should have a `Default` instance where all fields have a reasonable empty value. - Record fields should not have unique prefixes.+- API methods that return URIs that are known to always be `file:` URIs should be wrapped with functions returning [`Prelude.FilePath`](https://hackage.haskell.org/package/base-4.18.0.0/docs/Prelude.html#t:FilePath) (at some point this can be replaced with [`System.OsPath`](https://hackage.haskell.org/package/filepath-1.4.100.3/docs/System-OsPath.html#t:OsPath)). If they are not known to be file URIs, then [`Text.URI.URI`](https://hackage.haskell.org/package/modern-uri-0.3.6.0/docs/Text-URI.html#t:URI) should be returned. ### To format the source code ```bash
desktop-portal.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: desktop-portal-version: 0.1.1.0+version: 0.2.0.0 license: MIT license-file: LICENSE maintainer: garethdanielsmith@gmail.com@@ -50,6 +50,7 @@ containers <0.7, data-default-class <0.2, dbus >=1.2.28 && <1.3,+ modern-uri <0.4, random <1.3, text <1.3 @@ -88,6 +89,7 @@ desktop-portal, hspec >=2 && <3, hspec-expectations <0.9,+ modern-uri <0.4, process <1.7, random <1.3, text <1.3
src/Desktop/Portal/Account.hs view
@@ -15,7 +15,7 @@ import Data.Maybe (catMaybes, fromMaybe) import Data.Text (Text) import Desktop.Portal.Internal (Client, Request, sendRequest)-import Desktop.Portal.Util (optionalFromVariant, toVariantPair)+import Desktop.Portal.Util (decodeFileUri, mapJust, optionalFromVariant, toVariantPair) data GetUserInformationOptions = GetUserInformationOptions { window :: Maybe Text,@@ -33,7 +33,7 @@ data GetUserInformationResults = GetUserInformationResults { id :: Text, name :: Text,- image :: Maybe Text+ image :: Maybe FilePath } deriving (Eq, Show) @@ -54,7 +54,7 @@ resMap | Just id' <- DBus.fromVariant =<< Map.lookup "id" resMap, Just name <- DBus.fromVariant =<< Map.lookup "name" resMap,- Just image <- optionalFromVariant "image" resMap ->+ Just image <- mapJust decodeFileUri =<< optionalFromVariant "image" resMap -> do pure GetUserInformationResults {id = id', name, image} resMap -> throwIO . DBus.clientError $ "getUserInformation: could not parse response: " <> show resMap
src/Desktop/Portal/FileChooser.hs view
@@ -26,10 +26,10 @@ import Data.Map (Map) import Data.Map.Strict qualified as Map import Data.Maybe (catMaybes, fromMaybe)-import Data.Text (Text)+import Data.Text (Text, pack) import Data.Word (Word32) import Desktop.Portal.Internal (Client, Request, sendRequest)-import Desktop.Portal.Util (encodeNullTerminatedUtf8, mapJust, optionalFromVariant, toVariantPair, toVariantPair')+import Desktop.Portal.Util (decodeFileUris, encodeNullTerminatedUtf8, mapJust, optionalFromVariant, toVariantPair, toVariantPair') data Filter = Filter { name :: Text,@@ -90,7 +90,7 @@ } data OpenFileResults = OpenFileResults- { uris :: [Text],+ { uris :: [FilePath], choices :: Maybe [ChoiceComboSelection], currentFilter :: Maybe Filter }@@ -105,8 +105,8 @@ currentFilter :: Maybe Filter, choices :: Maybe [ChoiceCombo], currentName :: Maybe Text,- currentFolder :: Maybe Text,- currentFile :: Maybe Text+ currentFolder :: Maybe FilePath,+ currentFile :: Maybe FilePath } deriving (Eq, Show) @@ -126,7 +126,7 @@ } data SaveFileResults = SaveFileResults- { uris :: [Text],+ { uris :: [FilePath], choices :: Maybe [ChoiceComboSelection], currentFilter :: Maybe Filter }@@ -168,8 +168,8 @@ toVariantPair' encodeFilter "current_filter" options.currentFilter, toVariantPair' (fmap encodeCombo) "choices" options.choices, toVariantPair "current_name" options.currentName,- toVariantPair "current_folder" (encodeNullTerminatedUtf8 <$> options.currentFolder),- toVariantPair "current_file" (encodeNullTerminatedUtf8 <$> options.currentFile)+ toVariantPair "current_folder" (encodeNullTerminatedUtf8 . pack <$> options.currentFolder),+ toVariantPair "current_file" (encodeNullTerminatedUtf8 . pack <$> options.currentFile) ] parseResponse resMap = do@@ -179,7 +179,7 @@ parseOpenFileResponse :: Map Text Variant -> IO OpenFileResults parseOpenFileResponse = \case resMap- | Just uris <- DBus.fromVariant =<< Map.lookup "uris" resMap,+ | Just uris <- decodeFileUris =<< DBus.fromVariant =<< Map.lookup "uris" resMap, Just choicesRaw <- optionalFromVariant "choices" resMap, choices <- fmap decodeChoiceComboSelection <$> choicesRaw, Just currentFilterRaw <- optionalFromVariant "current_filter" resMap,
src/Desktop/Portal/Util.hs view
@@ -1,9 +1,13 @@+{-# LANGUAGE ViewPatterns #-}+ module Desktop.Portal.Util ( optionalFromVariant, mapJust, toVariantPair, toVariantPair', encodeNullTerminatedUtf8,+ decodeFileUri,+ decodeFileUris, ) where @@ -13,8 +17,10 @@ import Data.ByteString.Lazy (ByteString) import Data.Map (Map) import Data.Map qualified as Map-import Data.Text (Text)+import Data.Text (Text, unpack) import Data.Text.Encoding qualified as Encoding+import Text.URI (Authority (..), URI (..))+import Text.URI qualified as URI -- | Returns @Just Nothing@ if the field does not exist, @Just (Just x)@ if it does exist and -- can be turned into the expected type, or @Nothing@ if the field exists with the wrong type.@@ -38,3 +44,30 @@ encodeNullTerminatedUtf8 :: Text -> ByteString encodeNullTerminatedUtf8 txt = Binary.toLazyByteString (Encoding.encodeUtf8Builder txt <> Binary.singleton 0)++decodeFileUri :: Text -> Maybe FilePath+decodeFileUri uri =+ case URI.mkURI uri of+ Just+ URI+ { uriScheme = Just (URI.unRText -> "file"),+ uriAuthority = (validAuthority -> True),+ uriPath = Just (_trailingSlash, parts),+ uriQuery = [],+ uriFragment = Nothing+ } -> Just . unpack $ foldMap (("/" <>) . URI.unRText) parts+ _ ->+ Nothing+ where+ validAuthority = \case+ Left True -> True+ Right+ Authority+ { authUserInfo = Nothing,+ authHost = (URI.unRText -> ""),+ authPort = Nothing+ } -> True+ _ -> False++decodeFileUris :: [Text] -> Maybe [FilePath]+decodeFileUris = traverse decodeFileUri
test/Desktop/Portal/AccountSpec.hs view
@@ -36,11 +36,11 @@ successResponse [ ("id", toVariantText "_id"), ("name", toVariantText "_name"),- ("image", toVariantText "_img")+ ("image", toVariantText "file:///some/path") ] withRequestResponse handle accountInterface "GetUserInformation" responseBody $ do (Portal.getUserInformation (client handle) def >>= Portal.await)- `shouldReturn` Just (GetUserInformationResults "_id" "_name" (Just "_img"))+ `shouldReturn` Just (GetUserInformationResults "_id" "_name" (Just "/some/path")) it "should decode response without image" $ \handle -> do let responseBody =
test/Desktop/Portal/FileChooserSpec.hs view
@@ -103,7 +103,7 @@ withRequestResponse handle fileChooserInterface "OpenFile" responseBody $ do (Portal.openFile (client handle) def >>= Portal.await) `shouldReturn` Just- (OpenFileResults {uris = ["file:///a/b/c"], choices = Nothing, currentFilter = Nothing})+ (OpenFileResults {uris = ["/a/b/c"], choices = Nothing, currentFilter = Nothing}) it "should decode response with all Justs" $ \handle -> do let responseBody =@@ -116,7 +116,7 @@ (Portal.openFile (client handle) def >>= Portal.await) `shouldReturn` Just ( OpenFileResults- { uris = ["file:///a/b/c"],+ { uris = ["/a/b/c"], choices = Just [ChoiceComboSelection {comboId = "_comboId", optionId = "_optionId"}], currentFilter = Just Filter {name = "_filterId", fileTypes = [GlobFilter "*.md"]} }@@ -214,7 +214,7 @@ withRequestResponse handle fileChooserInterface "SaveFile" responseBody $ do (Portal.saveFile (client handle) def >>= Portal.await) `shouldReturn` Just- (SaveFileResults {uris = ["file:///a/b/c"], choices = Nothing, currentFilter = Nothing})+ (SaveFileResults {uris = ["/a/b/c"], choices = Nothing, currentFilter = Nothing}) it "should decode response with all Justs" $ \handle -> do let responseBody =@@ -227,7 +227,7 @@ (Portal.saveFile (client handle) def >>= Portal.await) `shouldReturn` Just ( SaveFileResults- { uris = ["file:///a/b/c"],+ { uris = ["/a/b/c"], choices = Just [ChoiceComboSelection {comboId = "_comboId", optionId = "_optionId"}], currentFilter = Just Filter {name = "_filterId", fileTypes = [GlobFilter "*.md"]} }