gleamunison/publication_index

The store-local publication index: one name hash line per published subject, at the path cli_lifecycle.manifest_path hands out, terminated by a newline. Published manifests are bytes other tooling reads, so this module is the only parser, renderer and writer of that format — publish, unpublish and purge all go through it and none of them re-derive the line shape or the write dance.

Two rules each caller used to restate on its own:

Types

A failed stage plus the inspected filesystem error, so each command reports the failure in its own established wording.

pub type Failure {
  Failure(stage: Stage, detail: String)
}

Constructors

  • Failure(stage: Stage, detail: String)

One index line. Opaque is a non-empty line the format does not recognise; a rewrite keeps it verbatim rather than dropping bytes it cannot interpret. Blank lines carry no information and never survive parse.

pub type Line {
  Entry(name: String, hash: String)
  Opaque(text: String)
}

Constructors

  • Entry(name: String, hash: String)
  • Opaque(text: String)

Which stage of an index operation failed.

pub type Stage {
  Read
  Write
  Replace
}

Constructors

  • Read
  • Write
  • Replace

Values

pub fn describe(
  label: String,
  failure: Failure,
) -> #(Int, String)

Render a failure in the wording the index commands have always used: <label>: cannot <stage> the index: <detail>.

pub fn hash_of(
  lines: List(Line),
  name: String,
) -> option.Option(String)

The hash name is published under, if it is published at all.

pub fn parse(content: String) -> List(Line)

Parse index text into lines, dropping the blanks the trailing newline and any empty line produce.

pub fn read(path: String) -> Result(List(Line), Failure)

Read and parse the index. A missing file is an empty index; every other read failure is reported instead of being flattened into “nothing published yet”.

pub fn render(lines: List(Line)) -> String

Render lines back to the exact on-disk shape. An empty index renders as a lone newline, which is what every writer of this file has always produced.

pub fn replace(
  path: String,
  lines: List(Line),
) -> Result(Nil, Failure)

Write lines to the index atomically: content goes to <path>.tmp first and is renamed into place, so a crash mid-write leaves the previous index intact. A failed replace removes the temp file rather than leaving litter in a store.

pub fn rewrite(
  path: String,
  lines: List(Line),
  keep: fn(String, String) -> Bool,
) -> Result(Nil, Failure)

Rewrite the index keeping the entries keep accepts, by name and hash. Lines the format does not recognise are always kept.

pub fn upsert(
  path: String,
  lines: List(Line),
  entries: List(Line),
) -> Result(Nil, Failure)

Replace the index with entries taking the place of any existing line for the same name — publish’s upsert, which is how re-publishing a def moves its recorded hash without disturbing the rest of the index.

✨ Search Document