Skip to content

Commit

Permalink
refactor(cargo): Migrate from toml4j to tomlkt
Browse files Browse the repository at this point in the history
tomlkt [1] brings TOML support to kotlinx-serialization. It is more feature
complete than ktoml [2], which was also considered to be used.

[1]: https://github.com/Peanuuutz/tomlkt
[2]: https://github.com/akuleshov7/ktoml

Signed-off-by: Sebastian Schuberth <[email protected]>
  • Loading branch information
sschuberth committed Oct 29, 2023
1 parent 8de8460 commit 44523e4
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 25 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ kotest = "5.7.2"
kotlinxCoroutines = "1.7.3"
kotlinxHtml = "0.9.1"
kotlinxSerialization = "1.6.0"
tomlkt = "0.3.7"
ktor = "2.3.5"
log4jApi = "2.21.1"
log4jApiKotlin = "1.3.0"
Expand Down Expand Up @@ -125,6 +126,7 @@ kotlinxCoroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core",
kotlinxHtml = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version.ref = "kotlinxHtml" }
kotlinxSerializationCore = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerialization" }
kotlinxSerializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
tomlkt = { module = "net.peanuuutz.tomlkt:tomlkt", version.ref = "tomlkt" }
ktorClientCore = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktorClientOkHttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
log4jApi = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4jApi" }
Expand Down
11 changes: 5 additions & 6 deletions plugins/package-managers/cargo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
plugins {
// Apply precompiled plugins.
id("ort-library-conventions")

// Apply third-party plugins.
alias(libs.plugins.kotlinSerialization)
}

dependencies {
Expand All @@ -39,12 +42,8 @@ dependencies {

implementation(libs.jacksonDatabind)
implementation(libs.jacksonModuleKotlin)
implementation(libs.toml4j)
constraints {
implementation("com.google.code.gson:gson:2.10.1") {
because("Earlier versions have vulnerabilities.")
}
}
implementation(libs.kotlinxSerializationCore)
implementation(libs.tomlkt)

funTestImplementation(testFixtures(project(":analyzer")))
}
40 changes: 21 additions & 19 deletions plugins/package-managers/cargo/src/main/kotlin/Cargo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ package org.ossreviewtoolkit.plugins.packagemanagers.cargo

import com.fasterxml.jackson.module.kotlin.readValue

import com.moandjiezana.toml.Toml

import java.io.File

import net.peanuuutz.tomlkt.Toml
import net.peanuuutz.tomlkt.decodeFromNativeReader

import org.apache.logging.log4j.kotlin.logger

import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory
Expand Down Expand Up @@ -55,6 +56,8 @@ import org.ossreviewtoolkit.utils.ort.ProcessedDeclaredLicense
import org.ossreviewtoolkit.utils.spdx.SpdxConstants
import org.ossreviewtoolkit.utils.spdx.SpdxOperator

private val toml = Toml { ignoreUnknownKeys = true }

/**
* The [Cargo](https://doc.rust-lang.org/cargo/) package manager for Rust.
*/
Expand Down Expand Up @@ -100,20 +103,19 @@ class Cargo(
return emptyMap()
}

val contents = Toml().read(lockfile)
return when (contents.getLong("version")) {
3L -> {
contents.getTables("package").orEmpty().mapNotNull { pkg ->
pkg.getString("checksum")?.let { checksum ->
val key = "${pkg.getString("name")} ${pkg.getString("version")} (${pkg.getString("source")})"
val contents = lockfile.reader().use { toml.decodeFromNativeReader<CargoLockFile>(it) }
return when (contents.version) {
3 -> {
contents.packages.mapNotNull { pkg ->
pkg.checksum?.let { checksum ->
val key = "${pkg.name} ${pkg.version} (${pkg.source})"
key to checksum
}
}
}

else -> {
val metadata = contents.getTable("metadata")?.toMap().orEmpty()
metadata.mapNotNull { (k, v) ->
contents.metadata.mapNotNull { (k, v) ->
(v as? String)?.let { k.unquote().removePrefix("checksum ") to v }
}
}
Expand Down Expand Up @@ -158,9 +160,7 @@ class Cargo(
override fun resolveDependencies(definitionFile: File, labels: Map<String, String>): List<ProjectAnalyzerResult> {
// Get the project name and version. If one of them is missing return null, because this is a workspace
// definition file that does not contain a project.
val pkgDefinition = Toml().read(definitionFile)
val projectName = pkgDefinition.getString("package.name") ?: return emptyList()
val projectVersion = pkgDefinition.getString("package.version") ?: return emptyList()
val pkgDefinition = definitionFile.reader().use { toml.decodeFromNativeReader<CargoManifest>(it) }

val workingDir = definitionFile.parentFile
val metadataProcess = run(workingDir, "metadata", "--format-version=1")
Expand All @@ -172,7 +172,9 @@ class Cargo(
{ parsePackage(it, hashes) }
)

val projectId = metadata.workspaceMembers.single { it.startsWith("$projectName $projectVersion") }
val projectId = metadata.workspaceMembers.single {
it.startsWith("${pkgDefinition.pkg.name} ${pkgDefinition.pkg.version}")
}

val projectNode = metadata.packages.single { it.id == projectId }
val groupedDependencies = projectNode.dependencies.groupBy { it.kind.orEmpty() }
Expand All @@ -182,7 +184,8 @@ class Cargo(

val transitiveDependencies = directDependencies
.mapNotNull { dependency ->
val version = getResolvedVersion(projectName, projectVersion, dependency.name, metadata)
val version =
getResolvedVersion(pkgDefinition.pkg.name, pkgDefinition.pkg.version, dependency.name, metadata)
version?.let { Pair(dependency.name, it) }
}
.mapTo(mutableSetOf()) {
Expand All @@ -199,12 +202,11 @@ class Cargo(
)

val projectPkg = packages.values.single { pkg ->
pkg.id.name == projectName && pkg.id.version == projectVersion
pkg.id.name == pkgDefinition.pkg.name && pkg.id.version == pkgDefinition.pkg.version
}.let { it.copy(id = it.id.copy(type = managerName)) }

val homepageUrl = pkgDefinition.getString("package.homepage").orEmpty()
val authors = pkgDefinition.getList("package.authors", emptyList<String>())
.mapNotNullTo(mutableSetOf(), ::parseAuthorString)
val homepageUrl = pkgDefinition.pkg.homepage.orEmpty()
val authors = pkgDefinition.pkg.authors.mapNotNullTo(mutableSetOf(), ::parseAuthorString)

val project = Project(
id = projectPkg.id,
Expand Down
49 changes: 49 additions & 0 deletions plugins/package-managers/cargo/src/main/kotlin/CargoLockFile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

package org.ossreviewtoolkit.plugins.packagemanagers.cargo

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
* See https://docs.rs/cargo-lock/latest/cargo_lock/struct.Lockfile.html.
*/
@Serializable
internal data class CargoLockFile(
val version: Int = 1,

@SerialName("package")
val packages: List<Package>,

val metadata: Map<String, String>
) {
/**
* See https://docs.rs/cargo-lock/latest/cargo_lock/package/struct.Package.html.
*/
@Serializable
data class Package(
val name: String,
val version: String,
val source: String? = null,
val checksum: String? = null,
val dependencies: List<String> = emptyList(),
val replace: String? = null
)
}
43 changes: 43 additions & 0 deletions plugins/package-managers/cargo/src/main/kotlin/CargoManifest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

package org.ossreviewtoolkit.plugins.packagemanagers.cargo

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
* See https://doc.rust-lang.org/cargo/reference/manifest.html.
*/
@Serializable
internal data class CargoManifest(
@SerialName("package")
val pkg: Package
) {
/**
* See https://doc.rust-lang.org/cargo/reference/manifest.html#the-package-section.
*/
@Serializable
data class Package(
val name: String,
val version: String,
val authors: List<String> = emptyList(),
val homepage: String? = null
)
}

0 comments on commit 44523e4

Please sign in to comment.