# Cargo.toml Diff Analyzer Library # Provides functions to compare Cargo.toml files and categorize changes # Parse a Cargo.toml file and extract dependency information export def parse_cargo_toml [file_path: string] { if not ($file_path | path exists) { error make {msg: $"File not found: ($file_path)"} } let content = open $file_path let dependencies = try { $content | get dependencies? | default {} } catch { {} } let dev_dependencies = try { $content | get dev-dependencies? | default {} } catch { {} } let build_dependencies = try { $content | get build-dependencies? | default {} } catch { {} } { package: ($content | get package? | default {}), dependencies: $dependencies, dev_dependencies: $dev_dependencies, build_dependencies: $build_dependencies, raw_content: $content } } # Extract nu-* dependencies from parsed Cargo.toml export def extract_nu_dependencies [parsed_cargo: record] { let all_deps = ($parsed_cargo.dependencies | transpose key value) $all_deps | where $it.key =~ "^nu-" | reduce -f {} {|dep, acc| $acc | upsert $dep.key $dep.value } } # Compare two Cargo.toml files and categorize differences export def compare_cargo_tomls [local_path: string, upstream_path: string] { let local = parse_cargo_toml $local_path let upstream = parse_cargo_toml $upstream_path let local_nu_deps = extract_nu_dependencies $local let upstream_nu_deps = extract_nu_dependencies $upstream # Find all dependency differences let all_local_deps = $local.dependencies | transpose key value let all_upstream_deps = $upstream.dependencies | transpose key value # Dependencies only in local let local_only = $all_local_deps | where {|dep| not ($all_upstream_deps | any {|up| $up.key == $dep.key}) } # Dependencies only in upstream let upstream_only = $all_upstream_deps | where {|dep| not ($all_local_deps | any {|loc| $loc.key == $dep.key}) } # Dependencies with different versions let version_diffs = $all_local_deps | where {|local_dep| let upstream_dep = $all_upstream_deps | where key == $local_dep.key | first if ($upstream_dep | is-empty) { false } else { $local_dep.value != $upstream_dep.value } } | each {|dep| let upstream_ver = $all_upstream_deps | where key == $dep.key | first | get value { dependency: $dep.key, local_version: $dep.value, upstream_version: $upstream_ver, is_nu_dependency: ($dep.key =~ "^nu-") } } # Check if only nu-* dependencies differ let non_nu_changes = $version_diffs | where not $it.is_nu_dependency let only_nu_deps_changed = (($local_only | length) == 0) and (($upstream_only | length) == 0) and (($non_nu_changes | length) == 0) # Package version comparison let package_diff = if ($local.package.version? != null) and ($upstream.package.version? != null) { if $local.package.version != $upstream.package.version { { local_version: $local.package.version, upstream_version: $upstream.package.version } } else { null } } else { null } { dependencies: { local_only: $local_only, upstream_only: $upstream_only, version_differences: $version_diffs }, package: { version_diff: $package_diff }, analysis: { only_nu_deps_changed: $only_nu_deps_changed, nu_deps_changed: ($version_diffs | where $it.is_nu_dependency), non_nu_deps_changed: $non_nu_changes, has_changes: ((($version_diffs | length) > 0) or (($local_only | length) > 0) or (($upstream_only | length) > 0) or ($package_diff != null)) } } } # Check if changes are safe for auto-OK based on nu dependencies only export def is_safe_for_auto_ok [diff_result: record, nu_managed_deps: list] { let analysis = $diff_result.analysis # Must have changes to be relevant if not $analysis.has_changes { return false } # Check if all changed dependencies are in the managed list let all_changed_deps = $analysis.nu_deps_changed | get dependency let all_managed = $all_changed_deps | all {|dep| $dep in $nu_managed_deps} # Safe if only nu deps changed AND all changed deps are managed $analysis.only_nu_deps_changed and $all_managed } # Generate a human-readable diff summary export def generate_diff_summary [diff_result: record] { let analysis = $diff_result.analysis if not $analysis.has_changes { return "No changes detected" } mut summary = [] # Package version changes if ($diff_result.package.version_diff != null) { let pkg_diff = $diff_result.package.version_diff $summary = ($summary | append $"Package version: ($pkg_diff.local_version) → ($pkg_diff.upstream_version)") } # Nu dependency changes if ($analysis.nu_deps_changed | length) > 0 { let nu_changes = $analysis.nu_deps_changed | each {|dep| $" ($dep.dependency): ($dep.local_version) → ($dep.upstream_version)"} | str join "\n" $summary = ($summary | append $"Nu dependencies:\n($nu_changes)") } # Non-nu dependency changes if ($analysis.non_nu_deps_changed | length) > 0 { let other_changes = $analysis.non_nu_deps_changed | each {|dep| $" ($dep.dependency): ($dep.local_version) → ($dep.upstream_version)"} | str join "\n" $summary = ($summary | append $"Other dependencies:\n($other_changes)") } # New dependencies if ($diff_result.dependencies.upstream_only | length) > 0 { let new_deps = $diff_result.dependencies.upstream_only | each {|dep| $" ($dep.key): ($dep.value)"} | str join "\n" $summary = ($summary | append $"New dependencies:\n($new_deps)") } # Removed dependencies if ($diff_result.dependencies.local_only | length) > 0 { let removed_deps = $diff_result.dependencies.local_only | each {|dep| $" ($dep.key): ($dep.value)"} | str join "\n" $summary = ($summary | append $"Removed dependencies:\n($removed_deps)") } $summary | str join "\n\n" }