package holder import ( "encoding/json" "fmt" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshbuilds" ) // Reserved is the holder's check of a publish against the builder's reservation (novox/hq design 47 §381, §478, §504): it asks the // seat mesh-builds' verb reserved with the publish's name and version, a package's name in its ecosystem, and accepts the publish // only when that version is the one reserved and an artifact of it has the publish's digest. It reads no catalog and a publish gains // no field. It is the Builder the holder's Check asks. type Reserved struct { // Ask is the runtime's request and reply over the bus (mesh-sdk stdio.Ask). Ask func(address string, body any) (json.RawMessage, error) } // Address is the seat verb asked, as the manifest's `invokes` names it. const Address = "seat:" + meshbuilds.Seat + "." + meshbuilds.VerbReserved // Recorded answers whether name@version with digest is reserved, and why not when it is not. An answer that cannot be read is an // error, never a yes; a refusal by the builder (a name no entry carries, a version it did not reserve) is a no that names it. func (r Reserved) Recorded(name, version, digest string) (bool, string, error) { raw, err := r.Ask(Address, meshbuilds.ReservedInput{Name: name, Version: version}) if err != nil { return false, "", fmt.Errorf("%s did not answer for %s@%s: %w", Address, name, version, err) } var a meshbuilds.ReservedAnswer if err := json.Unmarshal(raw, &a); err != nil || a.Version == "" { return false, "", fmt.Errorf("%s answered no reservation for %s@%s: %s", Address, name, version, raw) } if a.Version != version { return false, fmt.Sprintf("%s reserved %s for %s, not %s", a.Module, a.Version, name, version), nil } for _, art := range a.Artifacts { if art.Digest == digest { return true, "", nil } } return false, fmt.Sprintf("%s reserved %s@%s for another digest than %s", a.Module, name, version, digest), nil }