packages feed

xdg-desktop-entry 0.1.1.5 → 0.1.1.6

raw patch · 4 files changed

+89/−15 lines, 4 filesdep +bytestringdep −inidep −unordered-containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: bytestring

Dependencies removed: ini, unordered-containers

API changes (from Hackage documentation)

- System.Environment.XDG.DesktopEntry: instance GHC.Internal.Read.Read System.Environment.XDG.DesktopEntry.DesktopEntry
- System.Environment.XDG.DesktopEntry: instance GHC.Internal.Read.Read System.Environment.XDG.DesktopEntry.DesktopEntryType
- System.Environment.XDG.DesktopEntry: instance GHC.Internal.Show.Show System.Environment.XDG.DesktopEntry.DesktopEntry
- System.Environment.XDG.DesktopEntry: instance GHC.Internal.Show.Show System.Environment.XDG.DesktopEntry.DesktopEntryType
+ System.Environment.XDG.DesktopEntry: instance GHC.Read.Read System.Environment.XDG.DesktopEntry.DesktopEntry
+ System.Environment.XDG.DesktopEntry: instance GHC.Read.Read System.Environment.XDG.DesktopEntry.DesktopEntryType
+ System.Environment.XDG.DesktopEntry: instance GHC.Show.Show System.Environment.XDG.DesktopEntry.DesktopEntry
+ System.Environment.XDG.DesktopEntry: instance GHC.Show.Show System.Environment.XDG.DesktopEntry.DesktopEntryType

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # Revision history for xdg-desktop-entry +## 0.1.1.6 -- 2026-09-04++* Replace the `ini` based parser with one that follows the desktop entry+  specification. Localised keys such as `Name[de]` and `GenericName[ar]` no+  longer make `readDesktopEntry` fail, so nearly every real-world desktop file+  now parses. Files are decoded as UTF-8 regardless of the process locale.+* Drop the `ini` and `unordered-containers` dependencies.+ ## 0.1.1.5 -- 2026-05-13  * Clean up parser and test warnings for newer GHC releases.
src/System/Environment/XDG/DesktopEntry.hs view
@@ -39,16 +39,16 @@ import Control.Monad import Control.Monad.IO.Class import Control.Monad.Trans.Except-import Data.Bifunctor (bimap)+import qualified Data.ByteString as BS import Data.Char import Data.Either import Data.Either.Combinators-import qualified Data.HashMap.Strict as HM-import qualified Data.Ini as Ini import Data.List import Data.Maybe import qualified Data.MultiMap as MM-import Data.Text (pack, unpack)+import Data.Text (unpack)+import Data.Text.Encoding (decodeUtf8With)+import Data.Text.Encoding.Error (lenientDecode) import Safe import System.Directory import System.FilePath.Posix@@ -193,19 +193,46 @@ -- | Read a desktop entry from a file. readDesktopEntry :: FilePath -> IO (Either String DesktopEntry) readDesktopEntry filePath = runExceptT $ do-  -- let foo1 = join . fmap except . liftIO $ Ini.readIniFile filePath-  -- let bar :: ExceptT String IO (HM.HashMap Text [(Text, Text)]) = map Ini.iniSections . liftIO $ Ini.readIniFile filePath-  -- sections <- fmap Ini.iniSections . join . fmap except . liftIO $ Ini.readIniFile filePath-  sections <- liftIO (Ini.readIniFile filePath) >>= fmap Ini.iniSections . except+  contents <- liftIO $ unpack . decodeUtf8With lenientDecode <$> BS.readFile filePath+  groups <- except $ parseDesktopEntryGroups contents   result <--    maybe (throwE "Section [Desktop Entry] not found") (pure . fmap (bimap unpack unpack)) $-      HM.lookup (pack "Desktop Entry") sections+    maybe (throwE "Section [Desktop Entry] not found") pure $+      lookup "Desktop Entry" groups   return     DesktopEntry       { deType = fromMaybe Application $ lookup "Type" result >>= readMaybe,         deFilename = filePath,         deAttributes = result       }++-- | Parse the groups of a desktop entry file into association lists, in file+-- order. Keys keep their locale suffix (e.g. @Name[de]@), whitespace around+-- @=@ is ignored, and blank and @#@ comment lines are skipped.+parseDesktopEntryGroups :: String -> Either String [(String, [(String, String)])]+parseDesktopEntryGroups = go Nothing [] . zip [1 :: Int ..] . lines+  where+    go current done [] = Right $ reverse $ finish current done+    go current done ((lineNo, rawLine) : rest)+      | null line || "#" `isPrefixOf` line = go current done rest+      | "[" `isPrefixOf` line && "]" `isSuffixOf` line =+          go (Just (takeWhile (/= ']') $ drop 1 line, [])) (finish current done) rest+      | otherwise =+          case break (== '=') line of+            (rawKey, '=' : rawValue)+              | not (null key) ->+                  case current of+                    Nothing ->+                      Left $ printf "line %d: entry before any group header" lineNo+                    Just (name, entries) ->+                      go (Just (name, (key, trim rawValue) : entries)) done rest+              where+                key = trim rawKey+            _ -> Left $ printf "line %d: expected a group header or key=value" lineNo+      where+        line = trim rawLine+    finish Nothing done = done+    finish (Just (name, entries)) done = (name, reverse entries) : done+    trim = dropWhileEnd isSpace . dropWhile isSpace  -- | Construct a 'MM.Multimap' where each 'DesktopEntry' in the provided -- foldable is indexed by the keys returned from the provided indexing function.
test/Main.hs view
@@ -12,7 +12,27 @@     "[Desktop Entry]\n\     \icon=2",     "[desktop entry]\n\-    \Icon=3"+    \Icon=3",+    "# Localised keys, comments, padding around '=' and extra groups.\n\+    \[Desktop Entry]\n\+    \Version=1.0\n\+    \Name=Google Chrome\n\+    \# Only KDE 4 seems to use GenericName.\n\+    \GenericName=Web Browser\n\+    \GenericName[ar]=متصفح الشبكة\n\+    \Name[de]=Google Chrome (de)\n\+    \Icon = google-chrome\n\+    \Exec=/usr/bin/google-chrome %U\n\+    \Actions=new-window;\n\+    \\n\+    \[Desktop Action new-window]\n\+    \Name=New Window\n\+    \Exec=/usr/bin/google-chrome",+    "Icon=before-any-group\n\+    \[Desktop Entry]\n\+    \Icon=4",+    "[Desktop Entry]\n\+    \this line has no equals sign"   ]  main :: IO ()@@ -39,4 +59,24 @@             deIcon deResult `shouldBe` Nothing       it "content2 should not work" $ do         deResultE <- readDesktopEntry $ filepath 2+        isLeft deResultE `shouldBe` True+    describe "readDesktopEntry" $ do+      it "parses localised keys, comments and extra groups" $ do+        deResultE <- readDesktopEntry $ filepath 3+        case deResultE of+          Left e -> expectationFailure $ show e+          Right deResult -> do+            deIcon deResult `shouldBe` Just "google-chrome"+            deName [] deResult `shouldBe` "Google Chrome"+            deName ["de"] deResult `shouldBe` "Google Chrome (de)"+            deName ["ar"] deResult `shouldBe` "Google Chrome"+            deCommand deResult `shouldBe` Just "/usr/bin/google-chrome"+            lookup "GenericName[ar]" (deAttributes deResult)+              `shouldBe` Just "متصفح الشبكة"+            lookup "Name" (deAttributes deResult) `shouldBe` Just "Google Chrome"+      it "rejects entries before any group header" $ do+        deResultE <- readDesktopEntry $ filepath 4+        isLeft deResultE `shouldBe` True+      it "rejects lines that are neither headers nor key=value" $ do+        deResultE <- readDesktopEntry $ filepath 5         isLeft deResultE `shouldBe` True
xdg-desktop-entry.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                xdg-desktop-entry-version:             0.1.1.5+version:             0.1.1.6 synopsis:            Parse files conforming to the xdg desktop entry spec description:         Parse files conforming to the xdg desktop entry spec. bug-reports:         https://github.com/taffybar/taffybar/issues@@ -22,16 +22,15 @@ library   exposed-modules:     System.Environment.XDG.DesktopEntry   build-depends:       base >=4.13 && < 5,+                       bytestring >= 0.10 && < 0.13,                        directory >= 1.3.6 && < 1.4,                        either >= 5.0.1.1 && < 5.1,                        filepath >= 1.4.2 && < 1.6,-                       ini >= 0.4.1 && < 0.6,                        multimap >= 1.2.1 && < 1.3,                        safe >= 0.3.19 && < 0.4,                        text >= 1.2.4 && < 2.2,                        transformers >= 0.5.6 && < 0.7,-                       unix >= 2.7.2 && < 2.9,-                       unordered-containers >= 0.2.10 && < 0.3+                       unix >= 2.7.2 && < 2.9   hs-source-dirs:      src   default-language:    Haskell2010   ghc-options:         -Wall