cuelabs.dev/go/oci/ociregistry@v0.0.0-20240906074133-82eb438dd565/ocimem/desciter.go (about)

     1  package ocimem
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  
     7  	"cuelabs.dev/go/oci/ociregistry"
     8  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
     9  )
    10  
    11  type refKind int
    12  
    13  const (
    14  	kindSubjectManifest refKind = iota
    15  	kindBlob
    16  	kindManifest
    17  )
    18  
    19  type descInfo struct {
    20  	name string
    21  	kind refKind
    22  	desc ociregistry.Descriptor
    23  }
    24  
    25  type descIter func(yield func(descInfo) bool)
    26  
    27  // TODO support other manifest types.
    28  var manifestIterators = map[string]func(data []byte) (descIter, error){
    29  	ocispec.MediaTypeImageManifest: descIterForType(imageDescIter),
    30  	ocispec.MediaTypeImageIndex:    descIterForType(indexDescIter),
    31  }
    32  
    33  // manifestReferences returns an iterator that iterates over all
    34  // direct references inside the given manifest described byx the
    35  // given descriptor that holds the given data.
    36  func manifestReferences(mediaType string, data []byte) (descIter, error) {
    37  	dataIter := manifestIterators[mediaType]
    38  	if dataIter == nil {
    39  		// TODO provide a configuration option to disallow unknown manifest types.
    40  		//return nil, fmt.Errorf("media type %q: %w", mediaType, errUnknownManifestMediaTypeForIteration)
    41  		return func(func(descInfo) bool) {}, nil
    42  	}
    43  	return dataIter(data)
    44  }
    45  
    46  // repoTagIter returns an iterator that iterates through
    47  // all the tags in the given repository.
    48  func repoTagIter(r *repository) descIter {
    49  	return func(yield func(descInfo) bool) {
    50  		for tag, desc := range r.tags {
    51  			if !yield(descInfo{
    52  				name: tag,
    53  				desc: desc,
    54  				kind: kindManifest,
    55  			}) {
    56  				break
    57  			}
    58  		}
    59  	}
    60  }
    61  
    62  func descIterForType[T any](newIter func(T) descIter) func(data []byte) (descIter, error) {
    63  	return func(data []byte) (descIter, error) {
    64  		var x T
    65  		if err := json.Unmarshal(data, &x); err != nil {
    66  			return nil, fmt.Errorf("cannot unmarshal into %T: %v", &x, err)
    67  		}
    68  		return newIter(x), nil
    69  	}
    70  }
    71  
    72  func imageDescIter(m ociregistry.Manifest) descIter {
    73  	return func(yield func(descInfo) bool) {
    74  		for i, layer := range m.Layers {
    75  			if !yield(descInfo{
    76  				name: fmt.Sprintf("layers[%d]", i),
    77  				desc: layer,
    78  				kind: kindBlob,
    79  			}) {
    80  				return
    81  			}
    82  		}
    83  		if !yield(descInfo{
    84  			name: "config",
    85  			desc: m.Config,
    86  			kind: kindBlob,
    87  		}) {
    88  			return
    89  		}
    90  		if m.Subject != nil {
    91  			if !yield(descInfo{
    92  				name: "subject",
    93  				kind: kindSubjectManifest,
    94  				desc: *m.Subject,
    95  			}) {
    96  				return
    97  			}
    98  		}
    99  	}
   100  }
   101  
   102  func indexDescIter(m ocispec.Index) descIter {
   103  	return func(yield func(descInfo) bool) {
   104  		for i, manifest := range m.Manifests {
   105  			if !yield(descInfo{
   106  				name: fmt.Sprintf("manifests[%d]", i),
   107  				kind: kindManifest,
   108  				desc: manifest,
   109  			}) {
   110  				return
   111  			}
   112  		}
   113  		if m.Subject != nil {
   114  			if !yield(descInfo{
   115  				name: "subject",
   116  				kind: kindSubjectManifest,
   117  				desc: *m.Subject,
   118  			}) {
   119  				return
   120  			}
   121  		}
   122  	}
   123  }