Skip to content

Commit 2a49149

Browse files
authored
Better functional option for setting redact hint (#33)
Better functional option for setting redact hint Instead of having multiple optional functions for setting redact hint, now we have just one takes any string as redact hint. We still provide common redact hints but now as simple constants. This approach is a lot cleaner than before. Adjust tests accordingly. Extras: Renamed receiver var to `tx` in all methods and functions for consistency.
1 parent 6891a61 commit 2a49149

File tree

2 files changed

+54
-58
lines changed

2 files changed

+54
-58
lines changed

example_test.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,31 @@ import (
1010
func ExampleText() {
1111
s := secret.Text{}
1212
fmt.Println(s, s.Value())
13+
1314
// Output: *****
1415
}
1516

1617
func ExampleNew() {
1718
s := secret.New("$ecre!")
1819
fmt.Println(s, s.Value())
20+
1921
// Output: ***** $ecre!
2022
}
2123

22-
func ExampleFiveXs() {
23-
s := secret.New("$ecre!", secret.FiveXs)
24+
func ExampleRedactHint() {
25+
s := secret.New("$ecre!", secret.RedactHint(secret.FiveX))
2426
fmt.Println(s, s.Value())
25-
// Output: XXXXX $ecre!
26-
}
2727

28-
func ExampleRedacted() {
29-
s := secret.New("$ecre!", secret.Redacted)
28+
s = secret.New("$ecre!", secret.RedactHint(secret.Redacted))
3029
fmt.Println(s, s.Value())
31-
// Output: [REDACTED] $ecre!
32-
}
3330

34-
func ExampleCustomRedact() {
35-
s := secret.New("$ecre!", secret.CustomRedact("HIDDEN"))
31+
s = secret.New("$ecre!", secret.RedactHint("my redact hint"))
3632
fmt.Println(s, s.Value())
37-
// Output: HIDDEN $ecre!
33+
34+
// Output:
35+
// XXXXX $ecre!
36+
// [REDACTED] $ecre!
37+
// my redact hint $ecre!
3838
}
3939

4040
func ExampleText_MarshalText() {
@@ -52,6 +52,7 @@ func ExampleText_MarshalText() {
5252
}
5353

5454
fmt.Println(string(bytes))
55+
5556
// Output: {"User":"John","Password":"*****"}
5657
}
5758

@@ -68,6 +69,7 @@ func ExampleText_UnmarshalText() {
6869

6970
fmt.Printf("%+v\n", login)
7071
fmt.Println(login.Password.Value())
72+
7173
// Output:
7274
// {User:John Password:*****}
7375
// $ecre!
@@ -79,6 +81,7 @@ func ExampleText_Equals() {
7981
s3 := secret.New("hi")
8082
fmt.Println(s1.Equals(s2))
8183
fmt.Println(s1.Equals(s3))
84+
8285
// Output:
8386
// true
8487
// false

secret.go

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,92 +5,85 @@
55
package secret
66

77
// Text provides a way to safely store your secret value and a corresponding redact hint. This
8-
// redact hint what is used in operations like printing and serializing. The default
9-
// value of Text is usable.
8+
// redact hint is what is used in operations like printing and serializing.
109
type Text struct {
1110
// v is the actual secret values.
1211
v *string
1312
// r is the redact hint to be used in place of secret.
1413
r *string
1514
}
1615

17-
// New returns [Text] for the secret with `*****` as the redact hint.
18-
// Multiple option functions can be passed to alter default behavior.
16+
// New returns [Text] for the secret with [FiveStar] as the default redact hint. Provide options
17+
// like [RedactHint] to modify default behavior.
1918
func New(secret string, options ...func(*Text)) Text {
20-
sec := Text{
19+
tx := Text{
2120
v: new(string),
2221
r: new(string),
2322
}
2423

25-
*sec.v = secret
26-
*sec.r = defaultRedact
24+
*tx.v = secret
25+
*tx.r = FiveStar
2726

28-
// Apply options to override defaults
29-
for _, opt := range options {
30-
opt(&sec)
27+
for _, o := range options {
28+
o(&tx)
3129
}
3230

33-
return sec
31+
return tx
3432
}
3533

36-
// defaultRedact is used if no other redact hint is given.
37-
const defaultRedact string = "*****"
34+
// Some common redact hints.
35+
const (
36+
FiveX string = "XXXXX"
37+
FiveStar string = "*****"
38+
Redacted string = "[REDACTED]"
39+
)
3840

39-
// String implements the fmt.Stringer interface and returns only the redact hint. This prevents the
41+
// RedactHint is a functional option to set r as the redact hint for the [Text]. You can use one of
42+
// the common redact hints provided with this package like [FiveX] or provide your own string.
43+
func RedactHint(r string) func(*Text) {
44+
return func(t *Text) {
45+
*t.r = r
46+
}
47+
}
48+
49+
// String implements the [fmt.Stringer] interface and returns only the redact hint. This prevents the
4050
// secret value from being printed to std*, logs etc.
41-
func (s Text) String() string {
42-
if s.r == nil {
43-
return defaultRedact
51+
func (tx Text) String() string {
52+
if tx.r == nil {
53+
return FiveStar
4454
}
45-
return *s.r
55+
return *tx.r
4656
}
4757

4858
// Value gives you access to the actual secret value stored inside Text.
49-
func (s Text) Value() string {
50-
if s.v == nil {
59+
func (tx Text) Value() string {
60+
if tx.v == nil {
5161
return ""
5262
}
53-
return *s.v
63+
return *tx.v
5464
}
5565

5666
// MarshalText implements [encoding.TextMarshaler]. It marshals redact string into bytes rather than
5767
// the actual secret value.
58-
func (s Text) MarshalText() ([]byte, error) {
59-
return []byte(*s.r), nil
68+
func (tx Text) MarshalText() ([]byte, error) {
69+
return []byte(tx.String()), nil
6070
}
6171

6272
// UnmarshalText implements [encoding.TextUnmarshaler]. It unmarshals b into receiver's new secret
6373
// value. If redact string is present then it is reused.
64-
func (s *Text) UnmarshalText(b []byte) error {
65-
v := string(b)
74+
func (tx *Text) UnmarshalText(b []byte) error {
75+
s := string(b)
6676

6777
// If the original redact is not nil then use it otherwise fallback to default.
68-
if s.r != nil {
69-
*s = New(v, CustomRedact(*s.r))
78+
if tx.r != nil {
79+
*tx = New(s, RedactHint(*tx.r))
7080
} else {
71-
*s = New(v)
81+
*tx = New(s)
7282
}
7383
return nil
7484
}
7585

7686
// Equals checks whether s2 has same secret string or not.
77-
func (s *Text) Equals(s2 Text) bool {
78-
return *s.v == *s2.v
79-
}
80-
81-
// Redacted option sets "[REDACTED]" as the redact hint.
82-
func Redacted(s *Text) {
83-
*s.r = "[REDACTED]"
84-
}
85-
86-
// FiveXs option sets "XXXXX" as the redact hint.
87-
func FiveXs(s *Text) {
88-
*s.r = "XXXXX"
89-
}
90-
91-
// CustomRedact option sets the value of r as the redact hint.
92-
func CustomRedact(r string) func(*Text) {
93-
return func(s *Text) {
94-
*s.r = r
95-
}
87+
func (tx *Text) Equals(s2 Text) bool {
88+
return *tx.v == *s2.v
9689
}

0 commit comments

Comments
 (0)