{- | The compiled advisory artifact's schema contract.

Écluse Pilot compiles OSV advisory data into a read-only SQLite artifact
(@osv.db@) and publishes it to object storage; the proxy downloads it and
queries it locally on the request path. This module is the one place the
writer and the reader agree on what that artifact looks like: the table-schema
epoch that names and stamps it, and the keys of its @meta@ table.

The artifact is immutable and rebuilt from scratch on every compilation, so
there are no migrations, only a read-compatibility contract between whoever
wrote a file and whoever reads it. The epoch expresses exactly that contract:
it moves only when the shape of the data breaks, so the key stays findable
and the stamp stays checkable across releases of either side.
-}
module Ecluse.Core.Osv.Schema (
    -- * The table-schema epoch
    osvSchemaEpoch,
    osvDbFileName,

    -- * The @meta@ table
    MetaKey (..),
    renderMetaKey,
) where

{- | The table-schema epoch: the version of the artifact's shape, shared by
the Pilot writer and the proxy reader.

Bump it only for a breaking change to the existing shape (a column rename, a
semantic change, a key change). Additive changes (a new column, a new table)
must not bump it: readers select explicit columns, so additions are invisible
to them. A column exists exactly when the build populates it, so a reader
learns what data an artifact offers from the schema itself.

The epoch names the published artifact ('osvDbFileName') and is stamped into
it as SQLite's @user_version@; a reader must reject an artifact whose stamp
does not match its own compiled-in epoch and keep its last known-good
database.
-}
osvSchemaEpoch :: Int
osvSchemaEpoch :: Int
osvSchemaEpoch = Int
1

{- | The artifact's file name, and object-storage key, for an ecosystem.

The key is stable per ecosystem, so a reader can poll one known key by ETag,
and embeds only the epoch, so the key changes exactly when a reader could no
longer use the file.

>>> osvDbFileName "npm"
"npm-osv-schema1.db"
-}
osvDbFileName :: Text -> FilePath
osvDbFileName :: Text -> FilePath
osvDbFileName Text
ecosystem =
    Text -> FilePath
forall a. ToString a => a -> FilePath
toString Text
ecosystem FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
"-osv-schema" FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> Int -> FilePath
forall b a. (Show a, IsString b) => a -> b
show Int
osvSchemaEpoch FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
".db"

{- | A key of the artifact's @meta@ table (one @TEXT@ key\/value row per key).

The table carries the artifact's provenance: which build produced it, from
what source, and when.
-}
data MetaKey
    = -- | The Pilot application version that produced the artifact.
      MetaPilotVersion
    | -- | The ecosystem the artifact was compiled for (e.g. @npm@).
      MetaEcosystem
    | -- | When the compilation finished, as an ISO-8601 UTC timestamp.
      MetaBuiltAt
    | -- | The advisory-dump URL the artifact was compiled from.
      MetaSourceUrl
    | -- | The number of advisory ranges the artifact holds.
      MetaRowCount
    deriving stock (MetaKey
MetaKey -> MetaKey -> Bounded MetaKey
forall a. a -> a -> Bounded a
$cminBound :: MetaKey
minBound :: MetaKey
$cmaxBound :: MetaKey
maxBound :: MetaKey
Bounded, Int -> MetaKey
MetaKey -> Int
MetaKey -> [MetaKey]
MetaKey -> MetaKey
MetaKey -> MetaKey -> [MetaKey]
MetaKey -> MetaKey -> MetaKey -> [MetaKey]
(MetaKey -> MetaKey)
-> (MetaKey -> MetaKey)
-> (Int -> MetaKey)
-> (MetaKey -> Int)
-> (MetaKey -> [MetaKey])
-> (MetaKey -> MetaKey -> [MetaKey])
-> (MetaKey -> MetaKey -> [MetaKey])
-> (MetaKey -> MetaKey -> MetaKey -> [MetaKey])
-> Enum MetaKey
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: MetaKey -> MetaKey
succ :: MetaKey -> MetaKey
$cpred :: MetaKey -> MetaKey
pred :: MetaKey -> MetaKey
$ctoEnum :: Int -> MetaKey
toEnum :: Int -> MetaKey
$cfromEnum :: MetaKey -> Int
fromEnum :: MetaKey -> Int
$cenumFrom :: MetaKey -> [MetaKey]
enumFrom :: MetaKey -> [MetaKey]
$cenumFromThen :: MetaKey -> MetaKey -> [MetaKey]
enumFromThen :: MetaKey -> MetaKey -> [MetaKey]
$cenumFromTo :: MetaKey -> MetaKey -> [MetaKey]
enumFromTo :: MetaKey -> MetaKey -> [MetaKey]
$cenumFromThenTo :: MetaKey -> MetaKey -> MetaKey -> [MetaKey]
enumFromThenTo :: MetaKey -> MetaKey -> MetaKey -> [MetaKey]
Enum, MetaKey -> MetaKey -> Bool
(MetaKey -> MetaKey -> Bool)
-> (MetaKey -> MetaKey -> Bool) -> Eq MetaKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MetaKey -> MetaKey -> Bool
== :: MetaKey -> MetaKey -> Bool
$c/= :: MetaKey -> MetaKey -> Bool
/= :: MetaKey -> MetaKey -> Bool
Eq, Int -> MetaKey -> FilePath -> FilePath
[MetaKey] -> FilePath -> FilePath
MetaKey -> FilePath
(Int -> MetaKey -> FilePath -> FilePath)
-> (MetaKey -> FilePath)
-> ([MetaKey] -> FilePath -> FilePath)
-> Show MetaKey
forall a.
(Int -> a -> FilePath -> FilePath)
-> (a -> FilePath) -> ([a] -> FilePath -> FilePath) -> Show a
$cshowsPrec :: Int -> MetaKey -> FilePath -> FilePath
showsPrec :: Int -> MetaKey -> FilePath -> FilePath
$cshow :: MetaKey -> FilePath
show :: MetaKey -> FilePath
$cshowList :: [MetaKey] -> FilePath -> FilePath
showList :: [MetaKey] -> FilePath -> FilePath
Show)

-- | The key's stored form in the @meta@ table.
renderMetaKey :: MetaKey -> Text
renderMetaKey :: MetaKey -> Text
renderMetaKey = \case
    MetaKey
MetaPilotVersion -> Text
"pilot_version"
    MetaKey
MetaEcosystem -> Text
"ecosystem"
    MetaKey
MetaBuiltAt -> Text
"built_at"
    MetaKey
MetaSourceUrl -> Text
"source_url"
    MetaKey
MetaRowCount -> Text
"row_count"