licensor/src/Licensor.hs
2019-02-05 15:22:10 -05:00

212 lines
4.2 KiB
Haskell

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
----------------------------------------------------------------------
-- |
-- Module: Licensor
-- Description:
--
--
--
----------------------------------------------------------------------
module Licensor
( LiLicense(..)
, LiPackage(..)
, getDependencies
, getPackage
, orderPackagesByLicense
, version
)
where
-- base
import qualified Control.Exception as Exception
import Control.Monad (unless)
import Data.Monoid ((<>))
import Data.Version (Version)
import System.IO
-- bytestring
import qualified Data.ByteString.Lazy as ByteString
-- Cabal
import Distribution.License
import Distribution.Package
import Distribution.PackageDescription
import Distribution.PackageDescription.Parse
import Distribution.Simple.Utils
import Distribution.Text
import Distribution.Verbosity
-- containers
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Set (Set)
import qualified Data.Set as Set
-- directory
import System.Directory
-- http-conduit
import Network.HTTP.Simple (HttpException, getResponseBody, httpLBS, parseRequest)
-- licensor
import qualified Paths_licensor
-- process
import System.Process
-- |
--
--
newtype LiLicense = LiLicense { getLicense :: License }
deriving (Eq, Read, Show, Text)
-- |
--
--
instance Ord LiLicense where
compare =
comparing display
-- |
--
--
data LiPackage =
LiPackage
{ liPackageId :: PackageIdentifier
, liPackageDependencies :: Set LiPackage
, liPackageLicense :: License
}
-- |
--
--
getPackage :: IO (Maybe PackageDescription)
getPackage = do
currentDirectory <- getCurrentDirectory
fmap getPackageDescription <$> findPackageDesc currentDirectory
>>= either (const (return Nothing)) (fmap Just)
-- |
--
--
getPackageDescription :: FilePath -> IO PackageDescription
getPackageDescription =
fmap packageDescription . readPackageDescription silent
-- |
--
--
getDependencies :: IO (Maybe (Set PackageIdentifier))
getDependencies = do
eitherDeps <-
Exception.try $ readProcess "stack" ["ls", "dependencies", "--separator", "-"] ""
case eitherDeps of
Left (_ :: IOError) ->
return Nothing
Right deps ->
return $ fmap Set.fromList $ sequence $ fmap simpleParse (lines deps)
-- |
--
--
getPackageLicense :: Bool -> PackageIdentifier -> IO (Maybe LiLicense)
getPackageLicense quiet p@PackageIdentifier{..} = do
unless quiet (putStr $ display p ++ "...")
let
url =
"GET https://hackage.haskell.org/package/"
<> display p
<> "/"
<> unPackageName pkgName
<> ".cabal"
req <- parseRequest url
eitherPd <- Exception.try $ fmap getResponseBody (httpLBS req)
case eitherPd of
Left (_ :: HttpException) ->
return Nothing
Right pd -> do
(file, handle) <- openTempFile "/tmp" "licensor"
hClose handle
ByteString.writeFile file pd
PackageDescription{license} <- getPackageDescription file
hClose handle
removeFile file
unless quiet (putStrLn $ display license)
return $ Just (LiLicense license)
-- |
--
--
orderPackagesByLicense
:: Bool
-> Maybe PackageIdentifier
-> Set PackageIdentifier
-> IO (Map LiLicense (Set PackageIdentifier), Set PackageIdentifier)
orderPackagesByLicense quiet maybeP =
let
cond =
maybe (const False) (==) maybeP
insertPackage package orderedPackages' = do
maybeLicense <- getPackageLicense quiet package
(orderedPackages, failed) <- orderedPackages'
return $
if cond package
then
(orderedPackages, failed)
else
case maybeLicense of
Nothing ->
( orderedPackages, Set.insert package failed
)
Just license ->
( Map.insertWith
Set.union
license
(Set.singleton package)
orderedPackages
, failed
)
in
foldr insertPackage (pure (mempty, mempty))
-- |
--
--
version :: Version
version =
Paths_licensor.version