{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE OverloadedStrings #-} -- | -- Module : Linux.Arch.Aur -- Copyright : (c) Colin Woodbury, 2014 - 2020 -- License : GPL3 -- Maintainer: Colin Woodbury -- -- Access package metadata from the Arch Linux User Repository. module Linux.Arch.Aur ( -- * Types AurInfo(..) , AurError(..) -- * Queries , info, search ) where import Control.Applicative ((<|>)) import Data.Aeson import Data.ByteString (ByteString) import Data.Hashable (Hashable) import Data.Text (Text) import qualified Data.Text as T import GHC.Generics (Generic) import Network.HTTP.Client import Network.HTTP.Types.Status --- data RPCResp = RPCResp { _version :: Int , _type :: Text , _resultCount :: Int , _results :: [AurInfo] } deriving (Show) instance FromJSON RPCResp where parseJSON = withObject "RPCResp" $ \v -> RPCResp <$> v .: "version" <*> v .: "type" <*> v .: "resultcount" <*> v .: "results" -- | All relevant information about an AUR package. data AurInfo = AurInfo { aurIdOf :: Int , aurNameOf :: Text , pkgBaseIdOf :: Int , pkgBaseOf :: Text , aurVersionOf :: Text , aurDescriptionOf :: Maybe Text , urlOf :: Maybe Text , aurVotesOf :: Int , popularityOf :: Float , dateObsoleteOf :: Maybe Int , aurMaintainerOf :: Maybe Text , submissionDateOf :: Int , modifiedDateOf :: Int , urlPathOf :: Maybe Text , dependsOf :: [Text] , makeDepsOf :: [Text] , optDepsOf :: [Text] , conflictsOf :: [Text] , providesOf :: [Text] , licenseOf :: [Text] , keywordsOf :: [Text]} deriving (Eq, Ord, Show, Generic, Hashable) instance FromJSON AurInfo where parseJSON = withObject "AurInfo" $ \v -> AurInfo <$> v .: "ID" <*> v .: "Name" <*> v .: "PackageBaseID" <*> v .: "PackageBase" <*> v .: "Version" <*> v .:? "Description" <*> v .:? "URL" <*> v .: "NumVotes" <*> v .: "Popularity" <*> (v .: "OutOfDate" <|> pure Nothing) <*> (v .: "Maintainer" <|> pure Nothing) <*> v .: "FirstSubmitted" <*> v .: "LastModified" <*> v .:? "URLPath" <*> v .:? "Depends" .!= [] <*> v .:? "MakeDepends" .!= [] <*> v .:? "OptDepends" .!= [] <*> v .:? "Conflicts" .!= [] <*> v .:? "Provides" .!= [] <*> v .:? "License" .!= [] <*> v .:? "Keywords" .!= [] instance ToJSON AurInfo where toJSON ai = object [ "ID" .= aurIdOf ai , "Name" .= aurNameOf ai , "PackageBaseID" .= pkgBaseIdOf ai , "PackageBase" .= pkgBaseOf ai , "Version" .= aurVersionOf ai , "Description" .= aurDescriptionOf ai , "URL" .= urlOf ai , "NumVotes" .= aurVotesOf ai , "Popularity" .= popularityOf ai , "OutOfDate" .= dateObsoleteOf ai , "Maintainer" .= aurMaintainerOf ai , "FirstSubmitted" .= submissionDateOf ai , "LastModified" .= modifiedDateOf ai , "URLPath" .= urlPathOf ai , "Depends" .= dependsOf ai , "MakeDepends" .= makeDepsOf ai , "OptDepends" .= optDepsOf ai , "Conflicts" .= conflictsOf ai , "Provides" .= providesOf ai , "License" .= licenseOf ai , "Keywords" .= keywordsOf ai ] data AurError = NotFound ByteString | BadJSON | OtherAurError ByteString deriving stock (Eq, Ord, Show) -- | Perform an @info@ call on one or more package names. -- Will fail with a `Left` if there was a connection/decoding error. info :: Manager -> [Text] -> IO (Either AurError [AurInfo]) info m ps = work m url where url = "https://aur.archlinux.org/rpc?v=5&type=info&" <> as as = T.unpack . T.intercalate "&" $ map (\p -> "arg%5B%5D=" <> T.concatMap escape p) ps escape :: Char -> Text escape '+' = "%2B" escape c = T.singleton c -- | Perform a @search@ call on a package name or description text. -- Will fail with a `Left` if there was a connection/decoding error. search :: Manager -> Text -> IO (Either AurError [AurInfo]) search m p = work m url where url = "https://aur.archlinux.org/rpc?v=5&type=search&arg=" <> T.unpack p work :: Manager -> String -> IO (Either AurError [AurInfo]) work m url = do req <- parseRequest url res <- httpLbs req m case responseStatus res of Status 200 _ -> pure . maybe (Left BadJSON) (Right . _results) . decode' $ responseBody res Status 404 e -> pure . Left $ NotFound e Status _ e -> pure . Left $ OtherAurError e