RequirementsParser.kt
package com.depanalyzer.parser.python
import com.depanalyzer.parser.DependencySection
import com.depanalyzer.parser.Ecosystem
import com.depanalyzer.parser.ParsedDependency
import java.io.File
class RequirementsParser {
fun parse(requirementsFile: File): List<ParsedDependency> {
require(requirementsFile.exists() && requirementsFile.isFile) {
"Invalid requirements.txt path: ${requirementsFile.absolutePath}"
}
return requirementsFile.readLines().mapNotNull { rawLine ->
val line = rawLine.substringBefore('#').trim()
if (line.isBlank()) return@mapNotNull null
if (line.startsWith("-r ") || line.startsWith("--requirement")) return@mapNotNull null
if (line.startsWith("-e ")) return@mapNotNull null
val cleaned = line.substringBefore(';').trim()
val nameMatch = Regex("""^([A-Za-z0-9_.-]+)""").find(cleaned) ?: return@mapNotNull null
val rawName = nameMatch.groupValues[1]
val name = rawName.lowercase().replace('_', '-')
val remainder = cleaned.removePrefix(rawName).trim().takeIf { it.isNotBlank() }
ParsedDependency(
groupId = "pypi",
artifactId = name,
version = remainder,
scope = "main",
section = DependencySection.DEPENDENCIES,
ecosystem = Ecosystem.PYPI
)
}.distinctBy { "${it.groupId}:${it.artifactId}" }
}
}