diff --git a/internal/cargo/cargo.go b/internal/cargo/cargo.go index f7dc885..4738d07 100644 --- a/internal/cargo/cargo.go +++ b/internal/cargo/cargo.go @@ -109,6 +109,7 @@ func collectCargoDependencies(dependencies *[]core.Dependency, values map[string Version: extractCargoVersion(value), Scope: scope, Direct: true, + Source: extractCargoSource(value), }) } } @@ -156,6 +157,32 @@ func cargoRegistryDeclaration(name string, value any) (string, bool) { return name, true } +// extractCargoSource returns the explicit source override for a dependency +// table value, or the zero Source for plain and workspace-inherited entries. +func extractCargoSource(value any) core.Source { + properties, ok := value.(map[string]any) + if !ok { + return core.Source{} + } + if repo, ok := properties["git"].(string); ok { + source := core.Source{Kind: core.SourceGit, Value: repo} + if branch, ok := properties["branch"].(string); ok { + source.Branch = branch + } + if tag, ok := properties["tag"].(string); ok { + source.Tag = tag + } + if rev, ok := properties["rev"].(string); ok { + source.Ref = rev + } + return source + } + if registry, ok := properties["registry"].(string); ok { + return core.Source{Kind: core.SourceRegistry, Value: registry} + } + return core.Source{} +} + func extractCargoVersion(value any) string { switch v := value.(type) { case string: diff --git a/internal/cargo/cargo_test.go b/internal/cargo/cargo_test.go index 6c63322..9919068 100644 --- a/internal/cargo/cargo_test.go +++ b/internal/cargo/cargo_test.go @@ -177,6 +177,57 @@ license-file = "COPYING" } } +func TestCargoTomlDependencySource(t *testing.T) { + content := []byte(`[package] +name = "app" + +[dependencies] +plain = "1.0" +table = { version = "2.0" } +git-default = { git = "https://example.com/a.git" } +git-branch = { git = "https://example.com/b.git", branch = "next" } +git-tag = { git = "https://example.com/c.git", tag = "v1.0.0" } +git-rev = { git = "https://example.com/d.git", rev = "abc123" } +private = { registry = "internal", version = "3.0" } +inherited = { workspace = true } +local = { path = "../local" } +`) + + parser := &cargoTomlParser{} + result, err := parser.Parse("Cargo.toml", content) + if err != nil { + t.Fatalf("Parse failed: %v", err) + } + + got := make(map[string]core.Source) + for _, d := range result.Dependencies { + got[d.Name] = d.Source + } + + if _, ok := got["local"]; ok { + t.Error("path dependency should be filtered") + } + + want := map[string]core.Source{ + "plain": {}, + "table": {}, + "inherited": {}, + "git-default": {Kind: core.SourceGit, Value: "https://example.com/a.git"}, + "git-branch": {Kind: core.SourceGit, Value: "https://example.com/b.git", Branch: "next"}, + "git-tag": {Kind: core.SourceGit, Value: "https://example.com/c.git", Tag: "v1.0.0"}, + "git-rev": {Kind: core.SourceGit, Value: "https://example.com/d.git", Ref: "abc123"}, + "private": {Kind: core.SourceRegistry, Value: "internal"}, + } + if len(got) != len(want) { + t.Fatalf("got %d dependencies, want %d: %+v", len(got), len(want), got) + } + for name, source := range want { + if got[name] != source { + t.Errorf("%s Source = %+v, want %+v", name, got[name], source) + } + } +} + func TestCargoLock(t *testing.T) { content, err := os.ReadFile("../../testdata/cargo/Cargo.lock") if err != nil {