diff --git a/src/calanonsync/settings.go b/src/calanonsync/settings.go index ce371f8..63b5eaf 100644 --- a/src/calanonsync/settings.go +++ b/src/calanonsync/settings.go @@ -12,6 +12,7 @@ import ( "io/ioutil" "log" "os" + "sort" "strings" "syscall" ) @@ -224,10 +225,59 @@ func runSettingsDecryption(cmd *cobra.Command, args []string) { log.Println("Settings decrypted") } +// Apply the anonymization rule to the given string, returning the +// anonymized version. +// If the anonymization is nil or empty, the original string will +// be returned (since no anonymization is wanted, apparently). +// If no whitelist is given or nothing within the whitelist is +// found inside the string, the ReplaceWith string is returned. +// +// If a whitelist is used, ALL entries that were found in the original +// string will be concatenated and returned as the result. The order +// as found in the original string is kept. +// +// The whitelist is considered case insensitive! +// func (settings *StringAnonSettings) Apply(s string) string { if settings == nil || settings.ReplaceWith == "" { return s } + if settings.Whitelist != nil { + // We have a whitelist. Try to find all matches in appropriate order. + type match struct { + index int + entry int + } + + lowerString := strings.ToLower(s) + + var matches []match + for i := range settings.Whitelist { + m := match{entry: i} + m.index = strings.Index(lowerString, strings.ToLower(settings.Whitelist[i])) + if m.index > -1 { + matches = append(matches, m) + } + } + if matches != nil { + // Oh, we have matches. Good. Sort them by original + // index within the string so we have a chance of a + // meaningful title. + sort.SliceStable(matches, func(i, j int) bool { + return matches[i].index < matches[j].index + }) + + sb := &strings.Builder{} + for i := range matches { + if i > 0 { + sb.WriteString(" ") + } + sb.WriteString(settings.Whitelist[matches[i].entry]) + } + return sb.String() + } + } + return settings.ReplaceWith } diff --git a/src/calanonsync/settings_test.go b/src/calanonsync/settings_test.go index 59547e9..ba89bf8 100644 --- a/src/calanonsync/settings_test.go +++ b/src/calanonsync/settings_test.go @@ -14,10 +14,57 @@ func TestStringAnonSettings_Apply(t *testing.T) { t.Run("Not replaced when empty", func(t *testing.T) { title := "Test" - var anonTitle *StringAnonSettings = &StringAnonSettings{ReplaceWith: ""} + anonTitle := &StringAnonSettings{ReplaceWith: ""} if anonTitle.Apply(title) != title { t.Fatal("The title should be unchanged.") } }) + + t.Run("With whitelist", func(t *testing.T) { + anonTitle := &StringAnonSettings{ + ReplaceWith: "#Replaced", + Whitelist: []string{ + "lower", + "Test", + "With Space", + }, + } + + t.Run("No match", func(t *testing.T) { + if anonTitle.Apply("unrelated title") != "#Replaced" { + t.Fatal("The title should have been replaced.") + } + }) + + t.Run("Word found", func(t *testing.T) { + if anonTitle.Apply("This is a Test title.") != "Test" { + t.Fatal("One word should have matched.") + } + }) + + t.Run("Case ignored", func(t *testing.T) { + if anonTitle.Apply("Something with LOWER case.") != "lower" { + t.Fatal("The lower case variant should have matched.") + } + }) + + t.Run("Match with space", func(t *testing.T) { + if anonTitle.Apply("A title With Space") != "With Space" { + t.Fatal("A match with space should be found.") + } + }) + + t.Run("Multiple matches", func(t *testing.T) { + if anonTitle.Apply("Some lower title Test") != "lower Test" { + t.Fatal("Multiple matches should be concatenated.") + } + }) + + t.Run("Multiple matches keep their order", func(t *testing.T) { + if anonTitle.Apply("Test title with lower order") != "Test lower" { + t.Fatal("The original order should be kept") + } + }) + }) }