package source import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" ) // DigestDocument returns a deterministic digest of the canonical source // document content. The existing Digest field is excluded from its own digest. func DigestDocument(doc *SourceDocument) (string, error) { if doc == nil { return "", fmt.Errorf("source document must not be nil") } payload := struct { ID string `json:"id"` Kind string `json:"kind"` Format string `json:"format"` Units []SourceUnit `json:"units"` Metadata map[string]any `json:"metadata,omitempty"` }{ ID: doc.ID, Kind: doc.Kind, Format: doc.Format, Units: doc.Units, Metadata: doc.Metadata, } encoded, err := json.Marshal(payload) if err != nil { return "", fmt.Errorf("encode source document for digest: %w", err) } sum := sha256.Sum256(encoded) return "sha256:" + hex.EncodeToString(sum[:]), nil }