diff --git a/internal/cargo/cargo.go b/internal/cargo/cargo.go index 89d3e00..f7dc885 100644 --- a/internal/cargo/cargo.go +++ b/internal/cargo/cargo.go @@ -19,13 +19,26 @@ func init() { // cargoTomlParser parses Cargo.toml files. type cargoTomlParser struct{} +// inheritableString decodes a Cargo [package] field that may be a literal +// string or the workspace-inheritance table form {workspace = true}. The +// inherited value is not resolvable from a single file, so the table form +// yields the zero value instead of failing the whole decode. +type inheritableString string + +func (s *inheritableString) UnmarshalTOML(v any) error { + if str, ok := v.(string); ok { + *s = inheritableString(str) + } + return nil +} + func (p *cargoTomlParser) Parse(filename string, content []byte) (*core.Result, error) { var cargo struct { Package struct { - Name string `toml:"name"` - Version string `toml:"version"` - License string `toml:"license"` - LicenseFile string `toml:"license-file"` + Name string `toml:"name"` + Version inheritableString `toml:"version"` + License inheritableString `toml:"license"` + LicenseFile inheritableString `toml:"license-file"` } `toml:"package"` Dependencies map[string]any `toml:"dependencies"` DevDependencies map[string]any `toml:"dev-dependencies"` @@ -72,13 +85,13 @@ func (p *cargoTomlParser) Parse(filename string, content []byte) (*core.Result, var licenses []string if cargo.Package.License != "" { - licenses = []string{cargo.Package.License} + licenses = []string{string(cargo.Package.License)} } return &core.Result{ Name: pkgName, - Version: cargo.Package.Version, + Version: string(cargo.Package.Version), Licenses: licenses, - LicenseFile: cargo.Package.LicenseFile, + LicenseFile: string(cargo.Package.LicenseFile), Dependencies: filtered, Declarations: declarations, }, nil diff --git a/internal/cargo/cargo_test.go b/internal/cargo/cargo_test.go index 5dd4d0e..6c63322 100644 --- a/internal/cargo/cargo_test.go +++ b/internal/cargo/cargo_test.go @@ -123,6 +123,60 @@ anyhow = "=1.0.0" } } +func TestCargoTomlWorkspaceInheritedPackageFields(t *testing.T) { + content := []byte(`[package] +name = "member" +version.workspace = true +license.workspace = true +license-file = { workspace = true } +edition.workspace = true + +[dependencies] +serde = "1.0" +`) + + parser := &cargoTomlParser{} + result, err := parser.Parse("Cargo.toml", content) + if err != nil { + t.Fatalf("Parse failed: %v", err) + } + if result.Name != "member" { + t.Errorf("Name = %q, want %q", result.Name, "member") + } + if result.Version != "" { + t.Errorf("Version = %q, want empty for workspace-inherited", result.Version) + } + if result.Licenses != nil { + t.Errorf("Licenses = %v, want nil for workspace-inherited", result.Licenses) + } + if result.LicenseFile != "" { + t.Errorf("LicenseFile = %q, want empty for workspace-inherited", result.LicenseFile) + } + if len(result.Dependencies) != 1 || result.Dependencies[0].Name != "serde" { + t.Fatalf("Dependencies = %+v, want [serde]", result.Dependencies) + } + + literal := []byte(`[package] +name = "root" +version = "1.2.3" +license = "MIT OR Apache-2.0" +license-file = "COPYING" +`) + result, err = parser.Parse("Cargo.toml", literal) + if err != nil { + t.Fatalf("Parse literal failed: %v", err) + } + if result.Version != "1.2.3" { + t.Errorf("literal Version = %q, want %q", result.Version, "1.2.3") + } + if len(result.Licenses) != 1 || result.Licenses[0] != "MIT OR Apache-2.0" { + t.Errorf("literal Licenses = %v, want [MIT OR Apache-2.0]", result.Licenses) + } + if result.LicenseFile != "COPYING" { + t.Errorf("literal LicenseFile = %q, want %q", result.LicenseFile, "COPYING") + } +} + func TestCargoLock(t *testing.T) { content, err := os.ReadFile("../../testdata/cargo/Cargo.lock") if err != nil {