diff --git a/README.md b/README.md index ea9623b..73d152e 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ For more usage and examples see the [Godoc](http://godoc.org/github.com/ln80/str - `email` - `ipv4_addr` - `credit_card` +- `fullname` ## Limitations 1. Only fields of types convertible to `string` or `*string` are supported, although nesting structs directly or through collections (slices and maps) is also supported. diff --git a/mask/fullname.go b/mask/fullname.go new file mode 100644 index 0000000..c7e920d --- /dev/null +++ b/mask/fullname.go @@ -0,0 +1,39 @@ +package mask + +import ( + "strings" + + "github.com/ln80/struct-sensitive/internal/option" +) + +type FullNameConfig struct { +} + +// FullName masks a full name in the format "J*** ***** ***** D**". +func FullName(name string, opts ...func(*Config[FullNameConfig])) (string, error) { + cfg := DefaultConfig(FullNameConfig{}) + option.Apply(&cfg, opts) + + // Split name into words + words := strings.Fields(name) + + // Mask all words except the first and last + var builder strings.Builder + for i, word := range words { + if i == 0 || i == len(words)-1 { + builder.WriteRune([]rune(word)[0]) + builder.WriteString(strings.Repeat(string(cfg.Symbol), len(word)-1)) + } else { // Middle words + builder.WriteString(strings.Repeat(string(cfg.Symbol), len(word))) + } + if i != len(words)-1 { + builder.WriteRune(' ') + } + } + + return builder.String(), nil +} + +func init() { + Register("fullname", DefaultMasker(FullName)) +} diff --git a/mask/fullname_test.go b/mask/fullname_test.go new file mode 100644 index 0000000..0f2e3b3 --- /dev/null +++ b/mask/fullname_test.go @@ -0,0 +1,37 @@ +package mask_test + +import ( + "testing" + + "github.com/ln80/struct-sensitive/mask" + "github.com/ln80/struct-sensitive/masktest" +) + +func TestFullName(t *testing.T) { + masktest.Run(t, mask.FullName, []masktest.Tc[mask.FullNameConfig]{ + { + Value: "John Doe", + Want: "J*** D**", + OK: true, + }, + { + Value: "Emily Anne Doe", + Want: "E**** **** D**", + OK: true, + }, + { + Value: "X Æ A-12 Musk", + Want: "X ** **** M***", + OK: true, + }, + }) +} + +func BenchmarkFullname(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := mask.FullName("Emily Anne Doe", func(mc *mask.Config[mask.FullNameConfig]) { + }); err != nil { + b.Fatal(err) + } + } +}