-- SPDX-FileCopyrightText: 2026 Alexandra de Wit
--
-- SPDX-License-Identifier: MIT

{- | PyPI's entry in the ecosystem adapter registry, assembled from the
@Ecluse.Core.Registry.PyPI.*@ modules with no protocol logic of its own.

'adapterPublish' is 'Nothing', so the upload route answers its documented @405@ and the
composition root refuses a write destination on the mount. 'AdapterMaintenance' carries neither
verb, because PyPI spells no public wire endpoint for listing a store's projects or deleting a
release, so @ecluse dredger@ refuses such a store and names the missing verb. The alphabet is
declared regardless: a store with a control plane of its own is still walked in buckets.
-}
module Ecluse.Core.Registry.PyPI.Adapter (
    pypiAdapter,
) where

import Ecluse.Core.Ecosystem (Ecosystem (PyPI))
import Ecluse.Core.Registry.Adapter.Capability (
    AdapterArtifact (..),
    AdapterMaintenance (..),
    AdapterMetadata (..),
 )
import Ecluse.Core.Registry.Adapter.Types (AdapterServe (..), RegistryAdapter (..))
import Ecluse.Core.Registry.Maintenance.NameSpace (mkNameAlphabet)
import Ecluse.Core.Registry.Origin (OriginClient (ocToken), originBaseUrl)
import Ecluse.Core.Registry.PyPI.Credential (pypiCredential)
import Ecluse.Core.Registry.PyPI.Filter (assembleSimpleDocument, serialiseSimpleDocument)
import Ecluse.Core.Registry.PyPI.Metadata (fetchPyPIManifest, newPyPIMetadataReads, pypiChargeFactors)
import Ecluse.Core.Registry.PyPI.Project (projectName, pypiNameLeadChars)
import Ecluse.Core.Registry.PyPI.Request qualified as PyPIRequest
import Ecluse.Core.Registry.PyPI.Route qualified as PyPIRoute

-- | PyPI's capability record.
pypiAdapter :: RegistryAdapter
pypiAdapter :: RegistryAdapter
pypiAdapter =
    RegistryAdapter
        { adapterEcosystem :: Ecosystem
adapterEcosystem = Ecosystem
PyPI
        , adapterServe :: AdapterServe
adapterServe =
            AdapterServe
                { serveRouter :: MountRouter
serveRouter = MountRouter
PyPIRoute.pypiRouter
                , serveRoutes :: NonEmpty RouteSpec
serveRoutes = NonEmpty RouteSpec
PyPIRoute.pypiRouteSpecs
                , serveCredential :: CredentialMapping
serveCredential = CredentialMapping
pypiCredential
                }
        , adapterMetadata :: AdapterMetadata
adapterMetadata =
            AdapterMetadata
                { metadataNewReads :: forall posture.
TracingPort
-> MetricsPort
-> (PackageName -> MetadataError -> IO ())
-> (PackageName -> [InvalidEntry] -> IO ())
-> (PackageName -> IO ())
-> OriginFor posture
-> MetadataReads posture
metadataNewReads = TracingPort
-> MetricsPort
-> (PackageName -> MetadataError -> IO ())
-> (PackageName -> [InvalidEntry] -> IO ())
-> (PackageName -> IO ())
-> OriginFor posture
-> MetadataReads posture
forall posture.
TracingPort
-> MetricsPort
-> (PackageName -> MetadataError -> IO ())
-> (PackageName -> [InvalidEntry] -> IO ())
-> (PackageName -> IO ())
-> OriginFor posture
-> MetadataReads posture
newPyPIMetadataReads
                , metadataAssemble :: Text
-> Map SourceId (Snapshot CachedDoc)
-> MergePlan
-> Maybe CachedDoc
-> CachedDoc
metadataAssemble = Text
-> Map SourceId (Snapshot CachedDoc)
-> MergePlan
-> Maybe CachedDoc
-> CachedDoc
assembleSimpleDocument
                , metadataSerialise :: CachedDoc -> Either RenderRefused LByteString
metadataSerialise = CachedDoc -> Either RenderRefused LByteString
serialiseSimpleDocument
                , metadataFetchManifest :: ManifestFetch
metadataFetchManifest = ManifestFetch
fetchPyPIManifest
                , metadataChargeFactors :: ChargeFactors
metadataChargeFactors = ChargeFactors
pypiChargeFactors
                }
        , adapterArtifact :: AdapterArtifact
adapterArtifact =
            AdapterArtifact
                { artifactByFile :: OriginClient
-> PackageName -> Text -> Either UrlFormationError Request
artifactByFile = \OriginClient
origin -> Text
-> Maybe ClientCredential
-> PackageName
-> Text
-> Either UrlFormationError Request
PyPIRequest.artifactRequestByFile (OriginClient -> Text
originBaseUrl OriginClient
origin) (OriginClient -> Maybe ClientCredential
ocToken OriginClient
origin)
                , artifactByUrl :: Maybe ClientCredential -> Text -> Either UrlFormationError Request
artifactByUrl = Maybe ClientCredential -> Text -> Either UrlFormationError Request
PyPIRequest.artifactRequestByUrl
                , artifactHosts :: [Text]
artifactHosts = [Text]
PyPIRequest.pypiArtifactHosts
                }
        , adapterProjectName :: ProjectName
adapterProjectName = Either ParseError PackageName -> Maybe PackageName
forall l r. Either l r -> Maybe r
rightToMaybe (Either ParseError PackageName -> Maybe PackageName)
-> (Text -> Either ParseError PackageName) -> ProjectName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either ParseError PackageName
projectName
        , adapterPublish :: Maybe AdapterPublish
adapterPublish = Maybe AdapterPublish
forall a. Maybe a
Nothing
        , adapterMaintenance :: AdapterMaintenance
adapterMaintenance =
            AdapterMaintenance
                { maintenanceListing :: Maybe StoreListing
maintenanceListing = Maybe StoreListing
forall a. Maybe a
Nothing
                , maintenanceVersionDelete :: Maybe VersionDelete
maintenanceVersionDelete = Maybe VersionDelete
forall a. Maybe a
Nothing
                , maintenanceAlphabet :: NameAlphabet
maintenanceAlphabet = [Char] -> NameAlphabet
mkNameAlphabet [Char]
pypiNameLeadChars
                }
        }