import { parse } from '@iarna/toml' ; import { CrateDatasource } from '../../datasource/crate' ; import { logger } from '../../logger' ; import { SkipReason } from '../../types' ; import { findLocalSiblingOrParent , readLocalFile } from '../../util/fs' ; import type { ExtractConfig , PackageDependency , PackageFile } from '../types' ; import type { CargoConfig , CargoManifest , CargoRegistries , CargoSection , } from './types' ; function extractFromSection ( parsedContent , section , cargoRegistries , target ? ) { const deps = [ ] ; const sectionContent = parsedContent [ section ] ; if ( ! sectionContent ) { return [ ] ; } Object . keys ( sectionContent ) . forEach ( ( depName ) => { let skipReason ; let currentValue = sectionContent [ depName ] ; let nestedVersion = false ; let registryUrls ; let lookupName ; if ( typeof currentValue !== 'string' ) { const version = currentValue . version ; const path = currentValue . path ; const git = currentValue . git ; const registryName = currentValue . registry ; lookupName = currentValue . package ; if ( version ) { currentValue = version ; nestedVersion = true ; if ( registryName ) { const registryUrl = cargoRegistries [ registryName ] ; if ( registryUrl ) { registryUrls = [ registryUrl ] ; } else { skipReason = SkipReason . UnknownRegistry ; } } if ( path ) { skipReason = SkipReason . PathDependency ; } if ( git ) { skipReason = SkipReason . GitDependency ; } } else if ( path ) { currentValue = '' ; skipReason = SkipReason . PathDependency ; } else if ( git ) { currentValue = '' ; skipReason = SkipReason . GitDependency ; } else { currentValue = '' ; skipReason = SkipReason . InvalidDependencySpecification ; } } const dep = { depName , depType : section , currentValue : currentValue as any , managerData : { nestedVersion } , datasource : CrateDatasource . id , } ; if ( registryUrls ) { dep . registryUrls = registryUrls ; } if ( skipReason ) { dep . skipReason = skipReason ; } if ( target ) { dep . target = target ; } if ( lookupName ) { dep . lookupName = lookupName ; } deps . push ( dep ) ; } ) ; return deps ; } /** Reads `.cargo/config.toml`, or, if not found, `.cargo/config` */ async function readCargoConfig ( ) { for ( const configName of [ 'config.toml' , 'config' ] ) { const path = `.cargo/${ configName }` ; const payload = await readLocalFile ( path , 'utf8' ) ; if ( payload ) { try { return parse ( payload ) as CargoConfig ; } catch ( err ) { logger . debug ( { err } , `Error parsing ${ path }` ) ; } break ; } } logger . debug ( 'Neither .cargo/config nor .cargo/config.toml found' ) ; return null ; } /** Extracts a map of cargo registries from a CargoConfig */ function extractCargoRegistries ( config ) { const result = { } ; if ( ! config ?. registries ) { return result ; } const { registries } = config ; for ( const registryName of Object . keys ( registries ) ) { const registry = registries [ registryName ] ; if ( registry . index ) { result [ registryName ] = registry . index ; } else { logger . debug ( { registryName } , 'cargo registry is missing index' ) ; } } return result ; } export async function extractPackageFile ( content , fileName , _config ? ) { logger . trace ( `cargo.extractPackageFile(${ fileName })` ) ; const cargoConfig = await readCargoConfig ( ) ; const cargoRegistries = extractCargoRegistries ( cargoConfig ) ; let cargoManifest ; try { cargoManifest = parse ( content ) ; } catch ( err ) { logger . debug ( { err } , 'Error parsing Cargo.toml file' ) ; return null ; } const targetSection = cargoManifest . target ; let targetDeps = [ ] ; if ( targetSection ) { const targets = Object . keys ( targetSection ) ; targets . forEach ( ( target ) => { const targetContent = targetSection [ target ] ; const deps = [ ... extractFromSection ( targetContent , 'dependencies' , cargoRegistries , target ) , ... extractFromSection ( targetContent , 'dev-dependencies' , cargoRegistries , target ) , ... extractFromSection ( targetContent , 'build-dependencies' , cargoRegistries , target ) , ] ; targetDeps = targetDeps . concat ( deps ) ; } ) ; } const deps = [ ... extractFromSection ( cargoManifest , 'dependencies' , cargoRegistries ) , ... extractFromSection ( cargoManifest , 'dev-dependencies' , cargoRegistries ) , ... extractFromSection ( cargoManifest , 'build-dependencies' , cargoRegistries ) , ... targetDeps , ] ; if ( ! deps . length ) { return null ; } const lockFileName = await findLocalSiblingOrParent ( fileName , 'Cargo.lock' ) ; const res = { deps } ; if ( lockFileName ) { res . lockFiles = [ lockFileName ] ; } return res ; }