Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace Microsoft.ComponentDetection.Detectors.Rust;

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Detectors.Rust.Contracts;

/// <summary>
/// Provides functionality to construct contextual metadata for Rust packages,
/// specifically mapping package (crate) names to the <c>Cargo.toml</c> manifests
/// that declare or reference them.
/// </summary>
public interface IRustMetadataContextBuilder
{
/// <summary>
/// Builds a mapping of package (crate) names to the set of <c>Cargo.toml</c> files
/// (supplied in dependency resolution order) that either declare or reference them,
/// and returns additional ownership metadata. Also returns a cache of raw Cargo metadata
/// per manifest so downstream detection code can avoid invoking <c>cargo metadata</c> again.
/// </summary>
/// <param name="orderedTomlPaths">
/// An ordered enumeration of paths to <c>Cargo.toml</c> manifest files. The order should
/// reflect dependency resolution (e.g. workspace root manifests first, followed by members).
/// </param>
/// <param name="cancellationToken">A token used to observe cancellation requests.</param>
/// <returns>An <see cref="OwnershipResult"/> with ownership and per-manifest metadata cache.</returns>
public Task<OwnershipResult> BuildPackageOwnershipMapAsync(
IEnumerable<string> orderedTomlPaths,
CancellationToken cancellationToken);

/// <summary>
/// Represents the result of building Rust package ownership metadata.
/// </summary>
public class OwnershipResult
{
/// <summary>
/// Mapping from a package (crate) id to all <c>Cargo.toml</c> manifest
/// paths that declare or reference that package.
/// </summary>
public Dictionary<string, HashSet<string>> PackageToTomls { get; set; }
= new(StringComparer.OrdinalIgnoreCase);

/// <summary>
/// Set of <c>Cargo.toml</c> manifest paths that declare local packages.
/// </summary>
public HashSet<string> LocalPackageManifests { get; set; }
= new(StringComparer.OrdinalIgnoreCase);

/// <summary>
/// Raw <c>cargo metadata</c> JSON (already parsed) per manifest path (normalized).
/// This enables downstream detectors to reuse metadata without issuing
/// another CLI call.
/// </summary>
public Dictionary<string, CargoMetadata> ManifestToMetadata { get; set; }
= new(StringComparer.OrdinalIgnoreCase);

// Manifests for which cargo metadata failed.
public HashSet<string> FailedManifests { get; set; } = new(StringComparer.OrdinalIgnoreCase);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Microsoft.ComponentDetection.Detectors.Rust;

using System.Threading;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts;

public interface IRustCargoLockParser
{
public Task<int?> ParseAsync(
IComponentStream componentStream,
ISingleFileComponentRecorder singleFileComponentRecorder,
CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
namespace Microsoft.ComponentDetection.Detectors.Rust;

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Detectors.Rust.Contracts;

/// <summary>
/// Provides methods to parse Rust <c>Cargo.toml</c> / workspace dependency information into the component
/// recording system. Implementations may choose to invoke the Rust CLI (e.g. <c>cargo metadata</c>) or
/// operate on already-supplied serialized metadata.
/// </summary>
/// <remarks>
/// There are three entry points:
/// 1. <see cref="ParseAsync"/> triggers a fresh acquisition (typically by invoking the Cargo CLI).
/// 2. <see cref="ParseFromMetadataAsync(IComponentStream, ISingleFileComponentRecorder, CargoMetadata, IComponentRecorder, IReadOnlyDictionary{string, HashSet{string}}, CancellationToken)"/> consumes a pre-fetched <see cref="CargoMetadata"/> blob.
/// 3. <see cref="ParseFromMetadataAsync(IComponentStream, ISingleFileComponentRecorder, CargoMetadata, IComponentRecorder, IReadOnlyDictionary{string, HashSet{string}}, CancellationToken)"/> adds support
/// for multi-file / workspace ownership resolution by leveraging a parent recorder and an ownership map.
/// </remarks>
public interface IRustCliParser
{
/// <summary>
/// Parses Rust dependency information for the supplied component stream (generally a <c>Cargo.toml</c>)
/// by invoking the Cargo CLI (e.g. running <c>cargo metadata</c>) and recording discovered components
/// and dependency edges into the provided <paramref name="recorder"/>.
/// </summary>
/// <param name="componentStream">The stream representing the manifest file being parsed.</param>
/// <param name="recorder">The per-file component recorder used to register detected components and graph edges.</param>
/// <param name="cancellationToken">A token that can be used to cancel the parse operation.</param>
/// <returns>
/// A <see cref="ParseResult"/> indicating success or failure (with failure reason and any relevant local
/// package directories that were resolved).
/// </returns>
public Task<ParseResult> ParseAsync(
IComponentStream componentStream,
ISingleFileComponentRecorder recorder,
CancellationToken cancellationToken);

/// <summary>
/// Parses Rust dependency information using a pre-obtained <see cref="CargoMetadata"/> object, with support
/// for attributing discovered packages to owning manifests in a multi-project / workspace scenario.
/// </summary>
/// <param name="componentStream">The manifest stream being processed (used primarily for location context).</param>
/// <param name="fallbackRecorder">
/// A single-file recorder used if ownership cannot be resolved to a more specific recorder via the
/// <paramref name="ownershipMap"/> or <paramref name="parentComponentRecorder"/>.
/// </param>
/// <param name="cachedMetadata">The pre-fetched Cargo metadata describing packages and their relationships.</param>
/// <param name="parentComponentRecorder">
/// The parent recorder that can produce (or correlate) other single-file recorders used to correctly
/// attribute dependencies to their originating manifest locations.
/// </param>
/// <param name="ownershipMap">
/// A mapping of package ID (or equivalent key) to a set of manifest file paths indicating ownership.
/// Used to decide which recorder should own which package entries.
/// </param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A <see cref="ParseResult"/> containing success state and any local package directories discovered.</returns>
public Task<ParseResult> ParseFromMetadataAsync(
IComponentStream componentStream,
ISingleFileComponentRecorder fallbackRecorder,
CargoMetadata cachedMetadata,
IComponentRecorder parentComponentRecorder,
IReadOnlyDictionary<string, HashSet<string>> ownershipMap,
CancellationToken cancellationToken);

/// <summary>
/// Result of parsing a Cargo.toml file.
/// </summary>
public class ParseResult
{
/// <summary>
/// Gets or sets a value indicating whether parsing was successful.
/// </summary>
public bool Success { get; set; }

/// <summary>
/// Gets or sets the error message if parsing failed.
/// </summary>
public string ErrorMessage { get; set; }

/// <summary>
/// Gets or sets the reason for failure if parsing failed.
/// </summary>
public string FailureReason { get; set; }

/// <summary>
/// Gets or sets the local package directories that should be marked as visited.
/// This allows upstream client to skip TOMLs that were already accounted for in this run.
/// </summary>
public HashSet<string> LocalPackageDirectories { get; set; } = [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Microsoft.ComponentDetection.Detectors.Rust;

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts;

public interface IRustSbomParser
{
public Task<int?> ParseAsync(
IComponentStream componentStream,
ISingleFileComponentRecorder recorder,
CancellationToken cancellationToken);

public Task<int?> ParseWithOwnershipAsync(
IComponentStream componentStream,
ISingleFileComponentRecorder sbomRecorder,
IComponentRecorder parentComponentRecorder,
IReadOnlyDictionary<string, HashSet<string>> ownershipMap,
CancellationToken cancellationToken);
}
Loading
Loading