Skip to content

Commit

Permalink
Optimize cargo dependencies parsing
Browse files Browse the repository at this point in the history
Signed-off-by: Lizhe Lv <[email protected]>
  • Loading branch information
Code-Agitator committed Feb 5, 2025
1 parent da4ca30 commit 6ba6cb6
Showing 1 changed file with 55 additions and 51 deletions.
106 changes: 55 additions & 51 deletions lib/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7381,7 +7381,7 @@ function fileListToComponents(fileList) {
}

/**
* Parse dependency from the `[package]` node in cargo.toml or cargo.lock
* Parse dependency info from the `[package]` node in `cargo.toml` or `cargo.lock`
* @param {Object} packageNode
* @returns {Object} dependency info
* @throws {Error} if dependency name or version is invalid
Expand All @@ -7401,37 +7401,43 @@ function parseCargoDependencyFromPackageNode(packageNode) {
const pkgLicense = packageNode["license"];
const pkgDependencies = packageNode["dependencies"];

// the value of attributes like:
// - `version = 1.0.0`
// - `version.workspace = true`
const isExtendFromWorkspace = (attribute) => {
return attribute?.workspace;
};
if (!pkgName || !pkgVersion) {
throw new Error("name or version is not defined");
}

if (pkgChecksum) {
if (!isExtendFromWorkspace(pkgChecksum) && pkgChecksum) {
pkg._integrity = `${pkgChecksum}`;
}
if (pkgName) {
if (!isExtendFromWorkspace(pkgName) && pkgName) {
pkg.group = group;
pkg.name = basename(pkgName.toString());
}
if (pkgVersion) {
if (!isExtendFromWorkspace(pkgVersion) && pkgVersion) {
pkg.version = pkgVersion;
}
if (pkgAuthors) {
if (Object.prototype.toString.call(pkgAuthors) === "[object Array]") {
if (!isExtendFromWorkspace(pkgAuthors) && pkgAuthors) {
if (Array.isArray(pkgAuthors)) {
pkg.author = pkgAuthors.join(",");
} else {
pkg.author = Object.prototype.toString.call(pkgAuthors);
}
}
if (pkgHomepage) {
if (!isExtendFromWorkspace(pkgHomepage) && pkgHomepage) {
pkg.homepage = { url: pkgHomepage };
}
if (pkgRepository) {
if (!isExtendFromWorkspace(pkgRepository) && pkgRepository) {
pkg.repository = { url: pkgRepository };
}
if (pkgLicense) {
if (!isExtendFromWorkspace(pkgLicense) && pkgLicense) {
pkg.license = pkgLicense;
}
if (pkgDependencies) {
if (!isExtendFromWorkspace(pkgDependencies) && pkgDependencies) {
pkg.dependencies = pkgDependencies;
}
return pkg;
Expand Down Expand Up @@ -7525,16 +7531,15 @@ export async function parseCargoTomlData(
}
const packageNode = cargoData["package"];
// parse `[package]`
if (
packageNode &&
Object.prototype.toString.call(packageNode) === "[object Object]"
) {
if (packageNode instanceof Object && !Array.isArray(packageNode)) {
/** @type {Object} */
const packageObjNode = packageNode;
try {
const pkg = parseCargoDependencyFromPackageNode(packageNode);
addPackageToList(pkgList, pkg, { packageMode: true, simple });
} catch (e) {
console.warn(
`Failed to parse package: ${packageNode["name"]}@${packageNode["version"]},fail with:${e.message}`,
`Failed to parse package: ${packageObjNode?.name}@${packageObjNode?.version},fail with:${e.message}`,
);
}
}
Expand All @@ -7544,18 +7549,16 @@ export async function parseCargoTomlData(
for (const dependencyName in dependenciesNode) {
const dependencyNode = dependenciesNode[dependencyName];
let version = "";

if (
Object.prototype.toString.call(dependencyNode) === "[object String]"
typeof dependencyNode === "string" ||
dependencyNode instanceof String
) {
// like `libc = 0.2.79`
version = dependencyNode;
} else if (
Object.prototype.toString.call(dependencyNode) === "[object Object]"
) {
} else if (Object.keys(dependencyNode).length > 0) {
// like `libc = { version = "0.2.79", features = ['rustc-dep-of-std'], default-features = false }`
version = dependencyNode["version"];
const git = dependencyNode["git"];
version = dependencyNode?.version;
const git = dependencyNode?.git;
if (!version && git) {
version = `git+${git}`;
}
Expand Down Expand Up @@ -7682,11 +7685,8 @@ export async function parseCargoData(

const packageNode = cargoData["package"];
// parse `[[package]]`
if (
packageNode &&
Object.prototype.toString.call(packageNode) === "[object Array]"
) {
for (const packageItem of packageNode) {
if (Array.isArray(packageNode)) {
packageNode.forEach((packageItem) => {
try {
const pkg = parseCargoDependencyFromPackageNode(packageItem);
addPackageToList(pkgList, pkg, { simple });
Expand All @@ -7695,7 +7695,7 @@ export async function parseCargoData(
`Failed to parse package: ${packageItem["name"]}@${packageItem["version"]},fail with:${e.message}`,
);
}
}
});
}
if (shouldFetchLicense() && !simple) {
return await getCratesMetadata(pkgList);
Expand All @@ -7709,15 +7709,15 @@ export function parseCargoDependencyData(cargoLockData) {
new PackageURL("cargo", "", pkg.name, pkg.version, null, null).toString(),
);
const cargoData = toml.parse(cargoLockData);
const packageNode = cargoData.package;
if (
!packageNode ||
Object.prototype.toString.call(packageNode) !== "[object Array]"
) {
const packageNode = cargoData?.package;
if (!packageNode || !Array.isArray(packageNode)) {
return [];
}
/** @type {Array<Object>} */
const packageArrayNode = packageNode;
/** @type {Array<{ name: string, version: string, dependencies: Array<string>}>} */
const pkgList = [];
for (const packageItem of packageNode) {
packageArrayNode.forEach((packageItem) => {
try {
const pkg = parseCargoDependencyFromPackageNode(packageItem);
pkgList.push(pkg);
Expand All @@ -7726,30 +7726,34 @@ export function parseCargoDependencyData(cargoLockData) {
`Failed to parse package: ${packageItem["name"]}@${packageItem["version"]},fail with:${e.message}`,
);
}
}
});
// Create a map of package names to package objects
const pkgMap = pkgList.reduce((acc, item) => {
acc[item.name] = item;
return acc;
}, {});
const result = [];
// parse dependency version
Object.values(pkgMap).forEach((pkg) => {
const dependsOn = new Set();
if (pkg.dependencies) {
pkg.dependencies.forEach((dep) => {
if (dep.indexOf(" ") !== -1) {
const depSplit = dep.split(" ");
dependsOn.add(
purlFromPackageInfo({ name: depSplit[0], version: depSplit[1] }),
);
} else if (pkgMap[dep]) {
dependsOn.add(purlFromPackageInfo(pkgMap[dep]));
} else if (DEBUG_MODE) {
console.warn(
`The package "${dep.name}" appears as a dependency to "${pkg.name}" but is not itself listed in the Cargo.lock file. The Cargo.lock file is invalid! The produced SBOM will not list ${dep.name} as a dependency.`,
);
}
});
}
pkg.dependencies?.forEach((dep) => {
if (dep.indexOf(" ") !== -1) {
// fill version in dependency definition like `libc 0.2.79`
const depSplit = dep.split(" ");
dependsOn.add(
purlFromPackageInfo({
name: depSplit[0].trim(),
version: depSplit[1].trim(),
}),
);
} else if (pkgMap[dep]) {
dependsOn.add(purlFromPackageInfo(pkgMap[dep]));
} else if (DEBUG_MODE) {
console.warn(
`The package "${dep.name}" appears as a dependency to "${pkg.name}" but is not itself listed in the Cargo.lock file. The Cargo.lock file is invalid! The produced SBOM will not list ${dep.name} as a dependency.`,
);
}
});
result.push({
ref: purlFromPackageInfo(pkg),
dependsOn: [...dependsOn],
Expand Down

0 comments on commit 6ba6cb6

Please sign in to comment.