Skip to content

Commit e0a077a

Browse files
authored
Merge pull request #52 from github/ajhenry/extract-licenses
Add method for extracting licenses from expression
2 parents 304897a + 0f05021 commit e0a077a

File tree

4 files changed

+103
-0
lines changed

4 files changed

+103
-0
lines changed

README.md

+24
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,30 @@ assert.True(valid)
131131
assert.NotContains(invalidLicenses, "MIT AND APACHE-2.0")
132132
```
133133

134+
### ExtractLicenses
135+
136+
```go
137+
func ExtractLicenses(expression string) ([]string, error)
138+
```
139+
140+
Function `ExtractLicenses` is used to extract licenses from the given expression without duplicates.
141+
142+
**parameter: expression**
143+
144+
`expression` is an SPDX expression string.
145+
146+
**returns**
147+
148+
Function `ExtractLicenses` has 2 return values. First is `[]string` which contains all of the SPDX licenses without duplicates.
149+
150+
The second return value is an `error` which is not `nil` if the given expression is not a valid SPDX expression.
151+
152+
#### Example
153+
154+
```go
155+
licenses, err := ExtractLicenses("(MIT AND APACHE-2.0) OR (APACHE-2.0)")
156+
assert.Equal(licenses, []string{"MIT", "Apache-2.0"})
157+
```
134158

135159
## Background
136160

spdxexp/extracts.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package spdxexp
2+
3+
// ExtractLicenses extracts licenses from the given expression without duplicates.
4+
// Returns an array of licenses or error if error occurs during processing.
5+
func ExtractLicenses(expression string) ([]string, error) {
6+
node, err := parse(expression)
7+
if err != nil {
8+
return nil, err
9+
}
10+
11+
expanded := node.expand(true)
12+
licenses := make([]string, 0)
13+
allLicenses := flatten(expanded)
14+
for _, licenseNode := range allLicenses {
15+
licenses = append(licenses, *licenseNode.reconstructedLicenseString())
16+
}
17+
18+
licenses = removeDuplicateStrings(licenses)
19+
20+
return licenses, nil
21+
}

spdxexp/extracts_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package spdxexp
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestExtractLicenses(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
inputExpression string
13+
extractedLicenses []string
14+
}{
15+
{"Single license", "MIT", []string{"MIT"}},
16+
{"AND'ed licenses", "MIT AND Apache-2.0", []string{"MIT", "Apache-2.0"}},
17+
{"AND'ed & OR'ed licenses", "(MIT AND Apache-2.0) OR GPL-3.0", []string{"GPL-3.0", "MIT", "Apache-2.0"}},
18+
{"ONLY modifiers", "LGPL-2.1-only OR MIT OR BSD-3-Clause", []string{"MIT", "BSD-3-Clause", "LGPL-2.1-only"}},
19+
{"WITH modifiers", "GPL-2.0-or-later WITH Bison-exception-2.2", []string{"GPL-2.0-or-later+ WITH Bison-exception-2.2"}},
20+
{"Invalid SPDX expression", "MIT OR INVALID", nil},
21+
}
22+
23+
for _, test := range tests {
24+
t.Run(test.name, func(t *testing.T) {
25+
licenses, err := ExtractLicenses(test.inputExpression)
26+
assert.ElementsMatch(t, test.extractedLicenses, licenses)
27+
if test.extractedLicenses == nil {
28+
assert.Error(t, err)
29+
} else {
30+
assert.NoError(t, err)
31+
}
32+
})
33+
}
34+
}

spdxexp/helpers.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package spdxexp
2+
3+
// flatten will take an array of nested array and return
4+
// all nested elements in an array. e.g. [[1,2,[3]],4] -> [1,2,3,4]
5+
func flatten[T any](lists [][]T) []T {
6+
var res []T
7+
for _, list := range lists {
8+
res = append(res, list...)
9+
}
10+
return res
11+
}
12+
13+
// removeDuplicateStrings will remove all duplicates from a slice
14+
func removeDuplicateStrings(sliceList []string) []string {
15+
allKeys := make(map[string]bool)
16+
list := []string{}
17+
for _, item := range sliceList {
18+
if _, value := allKeys[item]; !value {
19+
allKeys[item] = true
20+
list = append(list, item)
21+
}
22+
}
23+
return list
24+
}

0 commit comments

Comments
 (0)