diff --git a/resolve_library.go b/resolve_library.go index dd96ab75..849940be 100644 --- a/resolve_library.go +++ b/resolve_library.go @@ -36,6 +36,7 @@ import ( "github.com/arduino/arduino-builder/constants" "github.com/arduino/arduino-builder/types" "github.com/arduino/arduino-builder/utils" + "github.com/schollz/closestmatch" ) func ResolveLibrary(ctx *types.Context, header string) *types.Library { @@ -211,6 +212,10 @@ func findBestLibraryWithHeader(header string, libraries []*types.Library) *types if library != nil { return library } + library = findLibWithNameBestDistance(headerName, libraries) + if library != nil { + return library + } } return nil @@ -252,6 +257,28 @@ func findLibWithNameContaining(name string, libraries []*types.Library) *types.L return nil } +func findLibWithNameBestDistance(name string, libraries []*types.Library) *types.Library { + // create closestmatch DB + var wordsToTest []string + + for _, library := range libraries { + wordsToTest = append(wordsToTest, simplifyName(library.Name)) + } + // Choose a set of bag sizes, more is more accurate but slower + bagSizes := []int{2} + // Create a closestmatch object + cm := closestmatch.New(wordsToTest, bagSizes) + closest_name := cm.Closest(name) + + for _, library := range libraries { + if (closest_name == simplifyName(library.Name)) { + return library + } + } + + return nil +} + func simplifyName(name string) string { return strings.ToLower(strings.Replace(name, "_", " ", -1)) } diff --git a/resolve_library_test.go b/resolve_library_test.go index 5d421216..1eff3c53 100644 --- a/resolve_library_test.go +++ b/resolve_library_test.go @@ -43,29 +43,38 @@ func TestFindBestLibraryWithHeader(t *testing.T) { l3 := &types.Library{Name: "Calculus Lib Improved"} l4 := &types.Library{Name: "Another Calculus Lib"} l5 := &types.Library{Name: "Yet Another Calculus Lib Improved"} - l6 := &types.Library{Name: "AnotherLib"} + l6 := &types.Library{Name: "Calculus Unified Lib"} + l7 := &types.Library{Name: "AnotherLib"} // Test exact name matching - res := findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3, l2, l1}) + res := findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4, l3, l2, l1}) require.Equal(t, l1.Name, res.Name) // Test exact name with "-master" postfix matching - res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3, l2}) + res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4, l3, l2}) require.Equal(t, l2.Name, res.Name) // Test prefix matching - res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3}) + res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4, l3}) require.Equal(t, l3.Name, res.Name) // Test postfix matching - res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4}) + res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4}) require.Equal(t, l4.Name, res.Name) // Test "contains"" matching - res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5}) + res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5}) require.Equal(t, l5.Name, res.Name) + // Test lexicographic similarity matching + res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6}) + require.Equal(t, l6.Name, res.Name) + + // Test lexicographic similarity matching (2) + res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l7}) + require.Equal(t, l6.Name, res.Name) + // Test none matching - res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6}) - require.Nil(t, res) + res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7}) + require.Equal(t, l7.Name, res.Name) }