Improve D&D registry caching and retire the completed roadmap
This commit is contained in:
@@ -28,6 +28,13 @@ type Item struct {
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// validatedItem borrows its content from the supplied reference set. Callers
|
||||
// must copy content before passing it to a callback that may retain it.
|
||||
type validatedItem struct {
|
||||
mediaType string
|
||||
content []byte
|
||||
}
|
||||
|
||||
// Config supplies the domain-owned operations needed to prepare immutable
|
||||
// registry views. Absent and Load must return values whose mutable state is not
|
||||
// exposed to callers. Load receives owned bytes and may retain them. Errors
|
||||
@@ -111,7 +118,7 @@ func (r *Resolver[V]) Resolve(references contracts.ReferenceSet) (V, error) {
|
||||
return r.seeded.value, nil
|
||||
}
|
||||
|
||||
item, _, err := resolveOptionalSingleItem(references, r.config.Reference, r.mediaType)
|
||||
item, _, err := validateOptionalSingleItem(references, r.config.Reference, r.mediaType)
|
||||
if err != nil {
|
||||
var zero V
|
||||
return zero, err
|
||||
@@ -124,7 +131,7 @@ func (r *Resolver[V]) Resolve(references contracts.ReferenceSet) (V, error) {
|
||||
return cached.value, nil
|
||||
}
|
||||
|
||||
resolved, err := r.load(item.Content)
|
||||
resolved, err := r.load(append([]byte(nil), item.content...))
|
||||
if err != nil {
|
||||
var zero V
|
||||
return zero, err
|
||||
@@ -147,23 +154,30 @@ func (r *Resolver[V]) Resolve(references contracts.ReferenceSet) (V, error) {
|
||||
// ResolveOptionalSingleItem validates and copies one optional registry item.
|
||||
// A missing slot returns present=false. A present slot must contain exactly one
|
||||
// item, even when it represents an operation-time generated reference.
|
||||
func ResolveOptionalSingleItem(references contracts.ReferenceSet, spec ReferenceSpec) (item Item, present bool, err error) {
|
||||
func ResolveOptionalSingleItem(references contracts.ReferenceSet, spec ReferenceSpec) (Item, bool, error) {
|
||||
normalized, mediaType, err := normalizeReferenceSpec(spec)
|
||||
if err != nil {
|
||||
return Item{}, false, err
|
||||
}
|
||||
return resolveOptionalSingleItem(references, normalized, mediaType)
|
||||
item, present, err := validateOptionalSingleItem(references, normalized, mediaType)
|
||||
if err != nil || !present {
|
||||
return Item{}, present, err
|
||||
}
|
||||
return Item{
|
||||
MediaType: item.mediaType,
|
||||
Content: append([]byte(nil), item.content...),
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
func (r *Resolver[V]) resolveUncached(references contracts.ReferenceSet) (preparedView[V], error) {
|
||||
item, present, err := resolveOptionalSingleItem(references, r.config.Reference, r.mediaType)
|
||||
item, present, err := validateOptionalSingleItem(references, r.config.Reference, r.mediaType)
|
||||
if err != nil {
|
||||
return preparedView[V]{}, err
|
||||
}
|
||||
if !present {
|
||||
return r.absent()
|
||||
}
|
||||
return r.load(item.Content)
|
||||
return r.load(append([]byte(nil), item.content...))
|
||||
}
|
||||
|
||||
func (r *Resolver[V]) absent() (preparedView[V], error) {
|
||||
@@ -221,32 +235,32 @@ func normalizeReferenceSpec(spec ReferenceSpec) (ReferenceSpec, string, error) {
|
||||
return spec, mediaType, nil
|
||||
}
|
||||
|
||||
func resolveOptionalSingleItem(references contracts.ReferenceSet, spec ReferenceSpec, acceptedMediaType string) (Item, bool, error) {
|
||||
func validateOptionalSingleItem(references contracts.ReferenceSet, spec ReferenceSpec, acceptedMediaType string) (validatedItem, bool, error) {
|
||||
slot, present := references.Slots[spec.SlotName]
|
||||
if !present {
|
||||
return Item{}, false, nil
|
||||
return validatedItem{}, false, nil
|
||||
}
|
||||
if len(slot.Items) != 1 {
|
||||
return Item{}, true, fmt.Errorf("reference slot %q must contain exactly one item", spec.SlotName)
|
||||
return validatedItem{}, true, fmt.Errorf("reference slot %q must contain exactly one item", spec.SlotName)
|
||||
}
|
||||
item := slot.Items[0]
|
||||
mediaType, _, err := mime.ParseMediaType(item.MediaType)
|
||||
if err != nil {
|
||||
return Item{}, true, fmt.Errorf("reference slot %q item media type is invalid", spec.SlotName)
|
||||
return validatedItem{}, true, fmt.Errorf("reference slot %q item media type is invalid", spec.SlotName)
|
||||
}
|
||||
mediaType = strings.ToLower(mediaType)
|
||||
if !strings.EqualFold(mediaType, acceptedMediaType) {
|
||||
return Item{}, true, fmt.Errorf("reference slot %q item media type must be %s", spec.SlotName, acceptedMediaType)
|
||||
return validatedItem{}, true, fmt.Errorf("reference slot %q item media type must be %s", spec.SlotName, acceptedMediaType)
|
||||
}
|
||||
if int64(len(item.Content)) > spec.MaxBytes {
|
||||
return Item{}, true, fmt.Errorf("reference slot %q item is %d bytes, limit %d", spec.SlotName, len(item.Content), spec.MaxBytes)
|
||||
return validatedItem{}, true, fmt.Errorf("reference slot %q item is %d bytes, limit %d", spec.SlotName, len(item.Content), spec.MaxBytes)
|
||||
}
|
||||
return Item{MediaType: mediaType, Content: append([]byte(nil), item.Content...)}, true, nil
|
||||
return validatedItem{mediaType: mediaType, content: item.Content}, true, nil
|
||||
}
|
||||
|
||||
func rawReferenceKey(item Item) string {
|
||||
sum := sha256.Sum256(item.Content)
|
||||
return item.MediaType + "\x00sha256:" + hex.EncodeToString(sum[:])
|
||||
func rawReferenceKey(item validatedItem) string {
|
||||
sum := sha256.Sum256(item.content)
|
||||
return item.mediaType + "\x00sha256:" + hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func sameIdentity[V any](first, second preparedView[V]) bool {
|
||||
|
||||
Reference in New Issue
Block a user