github.com/google/osv-scalibr@v0.4.1/clients/resolution/override_client.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package resolution
    16  
    17  import (
    18  	"context"
    19  	"slices"
    20  
    21  	"deps.dev/util/resolve"
    22  )
    23  
    24  // OverrideClient wraps a resolve.Client, allowing for custom packages & versions to be added
    25  type OverrideClient struct {
    26  	resolve.Client
    27  
    28  	// Can't quite reuse resolve.LocalClient because it automatically creates dependencies
    29  	pkgVers map[resolve.PackageKey][]resolve.Version            // versions of a package
    30  	verDeps map[resolve.VersionKey][]resolve.RequirementVersion // dependencies of a version
    31  }
    32  
    33  // NewOverrideClient makes a new OverrideClient.
    34  func NewOverrideClient(c resolve.Client) *OverrideClient {
    35  	return &OverrideClient{
    36  		Client:  c,
    37  		pkgVers: make(map[resolve.PackageKey][]resolve.Version),
    38  		verDeps: make(map[resolve.VersionKey][]resolve.RequirementVersion),
    39  	}
    40  }
    41  
    42  // AddVersion adds the specified version and dependencies to the client.
    43  func (c *OverrideClient) AddVersion(v resolve.Version, deps []resolve.RequirementVersion) {
    44  	// TODO: Inserting multiple co-dependent requirements may not work, depending on order
    45  	versions := c.pkgVers[v.PackageKey]
    46  	sem := v.Semver()
    47  	// Only add it to the versions if not already there (and keep versions sorted)
    48  	idx, ok := slices.BinarySearchFunc(versions, v, func(a, b resolve.Version) int {
    49  		return sem.Compare(a.Version, b.Version)
    50  	})
    51  	if !ok {
    52  		versions = slices.Insert(versions, idx, v)
    53  	}
    54  	c.pkgVers[v.PackageKey] = versions
    55  	c.verDeps[v.VersionKey] = slices.Clone(deps) // overwrites dependencies if called multiple times with same version
    56  }
    57  
    58  // Version returns the version specified by the VersionKey.
    59  func (c *OverrideClient) Version(ctx context.Context, vk resolve.VersionKey) (resolve.Version, error) {
    60  	for _, v := range c.pkgVers[vk.PackageKey] {
    61  		if v.VersionKey == vk {
    62  			return v, nil
    63  		}
    64  	}
    65  
    66  	return c.Client.Version(ctx, vk)
    67  }
    68  
    69  // Versions returns the versions of a package specified by the PackageKey.
    70  func (c *OverrideClient) Versions(ctx context.Context, pk resolve.PackageKey) ([]resolve.Version, error) {
    71  	if vers, ok := c.pkgVers[pk]; ok {
    72  		return vers, nil
    73  	}
    74  
    75  	return c.Client.Versions(ctx, pk)
    76  }
    77  
    78  // Requirements returns the requirement versions of the version specified by the VersionKey.
    79  func (c *OverrideClient) Requirements(ctx context.Context, vk resolve.VersionKey) ([]resolve.RequirementVersion, error) {
    80  	if deps, ok := c.verDeps[vk]; ok {
    81  		return deps, nil
    82  	}
    83  
    84  	return c.Client.Requirements(ctx, vk)
    85  }
    86  
    87  // MatchingVersions returns the versions matching the requirement specified by the VersionKey.
    88  func (c *OverrideClient) MatchingVersions(ctx context.Context, vk resolve.VersionKey) ([]resolve.Version, error) {
    89  	if vs, ok := c.pkgVers[vk.PackageKey]; ok {
    90  		return resolve.MatchRequirement(vk, vs), nil
    91  	}
    92  
    93  	return c.Client.MatchingVersions(ctx, vk)
    94  }