moduleDistribution.PackDeps(-- * Data typesNewest,CheckDepsRes(..),DescInfo-- * Read package database,loadNewest,loadNewestFrom,parseNewest-- * Check a package,checkDeps-- * Get a single package,getPackage,parsePackage,loadPackage-- * Get multiple packages,filterPackages,deepDeps-- * Reverse dependencies,Reverses,getReverses-- * Helpers,diName-- * Internal,PackInfo(..),DescInfo(..))whereimportSystem.Directory(getAppUserDataDirectory)importSystem.FilePath((</>))importqualifiedData.MapasMapimportData.List(foldl',group,sort,isInfixOf,isPrefixOf)importData.Time(UTCTime(UTCTime),addUTCTime)importData.Maybe(mapMaybe,catMaybes)importControl.Exception(throw)importDistribution.PackageimportDistribution.PackageDescriptionimportDistribution.PackageDescription.ParseimportDistribution.VersionimportDistribution.TextimportData.Char(toLower)importqualifiedData.Text.LazyasTimportqualifiedData.Text.Lazy.EncodingasTimportqualifiedData.Text.Encoding.ErrorasTimportqualifiedData.ByteString.LazyasLimportData.List.Split(splitOn)importqualifiedCodec.Archive.TarasTarimportqualifiedCodec.Archive.Tar.EntryasTarimportData.Function(on)importControl.Arrow((&&&))importData.List(groupBy,sortBy)importData.Ord(comparing)importqualifiedData.SetasSetloadNewest::IONewestloadNewest=doc<-getAppUserDataDirectory"cabal"cfg<-readFile(c</>"config")letrepos=reposFromConfigcfgtarNamerepo=c</>"packages"</>repo</>"00-index.tar"fmap(Map.unionsWithmaxVersion).mapM(loadNewestFrom.tarName)$reposreposFromConfig::String->[String]reposFromConfig=map(takeWhile(/=':')).catMaybes.map(dropPrefix"remote-repo: ").linesdropPrefix::(Eqa)=>[a]->[a]->Maybe[a]dropPrefixprefixs=ifprefix`isPrefixOf`sthenJust.drop(lengthprefix)$selseNothingloadNewestFrom::FilePath->IONewestloadNewestFrom=fmapparseNewest.L.readFileparseNewest::L.ByteString->NewestparseNewest=foldl'addPackageMap.empty.entriesToList.Tar.readentriesToList::Tar.EntriesTar.FormatError->[Tar.Entry]entriesToListTar.Done=[]entriesToList(Tar.Fails)=throwsentriesToList(Tar.Nextees)=e:entriesToListesaddPackage::Newest->Tar.Entry->NewestaddPackagementry=casesplitOn"/"$Tar.fromTarPathToPosixPath(Tar.entryTarPathentry)of[package',versionS,_]->casesimpleParseversionSofJustversion->caseMap.lookuppackage'mofNothing->gopackage'versionJustPackInfo{piVersion=oldv}->ifversion>oldvthengopackage'versionelsemNothing->m_->mwheregopackage'version=caseTar.entryContententryofTar.NormalFilebs_->Map.insertpackage'PackInfo{piVersion=version,piDesc=parsePackagebs,piEpoch=Tar.entryTimeentry}m_->mdataPackInfo=PackInfo{piVersion::Version,piDesc::MaybeDescInfo,piEpoch::Tar.EpochTime}deriving(Show,Read)maxVersion::PackInfo->PackInfo->PackInfomaxVersionpi1pi2=ifpiVersionpi1<=piVersionpi2thenpi2elsepi1-- | The newest version of every package.typeNewest=Map.MapStringPackInfotypeReverses=Map.MapString(Version,[(String,VersionRange)])getReverses::Newest->ReversesgetReversesnewest=Map.fromListwithVersionwhere-- dep = dependency, rel = relying packagetoTuples(_,PackInfo{piDesc=Nothing})=[]toTuples(rel,PackInfo{piDesc=JustDescInfo{diDeps=deps}})=map(toTuplerel)depstoTuplerel(Dependency(PackageNamedep)range)=(dep,(rel,range))hoist::Orda=>[(a,b)]->[(a,[b])]hoist=map((fst.head)&&&mapsnd).groupBy((==)`on`fst).sortBy(comparingfst)hoisted=hoist$concatMaptoTuples$Map.toListnewestwithVersion=mapMaybeaddVersionhoistedaddVersion(dep,rels)=caseMap.lookupdepnewestofNothing->NothingJustPackInfo{piVersion=v}->Just(dep,(v,rels))-- | Information on a single package.dataDescInfo=DescInfo{diHaystack::String,diDeps::[Dependency],diPackage::PackageIdentifier,diSynopsis::String}deriving(Show,Read)getDescInfo::GenericPackageDescription->DescInfogetDescInfogpd=DescInfo{diHaystack=maptoLower$authorp++maintainerp++name,diDeps=getDepsgpd,diPackage=pi',diSynopsis=synopsisp}wherep=packageDescriptiongpdpi'@(PackageIdentifier(PackageNamename)_)=packagepgetDeps::GenericPackageDescription->[Dependency]getDepsx=concat$maybeid((:).condTreeConstraints)(condLibraryx)$map(condTreeConstraints.snd)(condExecutablesx)checkDeps::Newest->DescInfo->(PackageName,Version,CheckDepsRes)checkDepsnewestdesc=casemapMaybe(notNewestnewest)$diDepsdescof[]->(name,version,AllNewest)x->lety=maphead$group$sort$mapfstxet=maximum$mapsndxin(name,version,WontAccepty$epochToTimeet)wherePackageIdentifiernameversion=diPackagedesc-- | Whether or not a package can accept all of the newest versions of its-- dependencies. If not, it returns a list of packages which are not accepted,-- and a timestamp of the most recently updated package.dataCheckDepsRes=AllNewest|WontAccept[(String,String)]UTCTimederivingShowepochToTime::Tar.EpochTime->UTCTimeepochToTimee=addUTCTime(fromIntegrale)$UTCTime(read"1970-01-01")0notNewest::Newest->Dependency->Maybe((String,String),Tar.EpochTime)notNewestnewest(Dependency(PackageNames)range)=caseMap.lookupsnewestof--Nothing -> Just ((s, " no version found"), 0)Nothing->NothingJustPackInfo{piVersion=version,piEpoch=e}->ifwithinRangeversionrangethenNothingelseJust((s,displayversion),e)-- | Loads up the newest version of a package from the 'Newest' list, if-- available.getPackage::String->Newest->MaybeDescInfogetPackagesn=Map.lookupsn>>=piDesc-- | Parse information on a package from the contents of a cabal file.parsePackage::L.ByteString->MaybeDescInfoparsePackagelbs=caseparsePackageDescription$T.unpack$T.decodeUtf8WithT.lenientDecodelbsofParseOk_x->Just$getDescInfox_->Nothing-- | Load a single package from a cabal file.loadPackage::FilePath->IO(MaybeDescInfo)loadPackage=fmapparsePackage.L.readFile-- | Find all of the packages matching a given search string.filterPackages::String->Newest->[DescInfo]filterPackagesneedle=mapMaybego.Map.elemswhereneedle'=maptoLowerneedlegoPackInfo{piDesc=Justdesc}=ifneedle'`isInfixOf`diHaystackdesc&&not("(deprecated)"`isInfixOf`diSynopsisdesc)thenJustdescelseNothinggo_=Nothing-- | Find all packages depended upon by the given list of packages.deepDeps::Newest->[DescInfo]->[DescInfo]deepDepsnewestdis0=goSet.emptydis0wherego_[]=[]goviewed(di:dis)|name`Set.member`viewed=govieweddis|otherwise=di:goviewed'(newDis++dis)wherePackageIdentifier(PackageNamename)_=diPackagediviewed'=Set.insertnameviewednewDis=mapMaybegetDI$diDepsdigetDI::Dependency->MaybeDescInfogetDI(Dependency(PackageNamename')_)=dopi'<-Map.lookupname'newestpiDescpi'diName::DescInfo->StringdiName=unPN.pkgName.diPackagewhereunPN(PackageNamepn)=pn