From abac34f14a9fa95929ef59463a88a98e900980e2 Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Thu, 17 Aug 2023 19:05:32 +0200 Subject: [PATCH] Add regex (#10) --- regex.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ regex_test.go | 31 +++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 regex.go create mode 100644 regex_test.go diff --git a/regex.go b/regex.go new file mode 100644 index 0000000..c84a250 --- /dev/null +++ b/regex.go @@ -0,0 +1,70 @@ +package bson + +import ( + "bytes" + "errors" + "fmt" + "regexp" +) + +// Regex represents BSON regular expression. +type Regex struct { + Pattern string + Options string +} + +// String returns a hex string representation of the id. +func (re Regex) String() string { + return fmt.Sprintf(`Regex('%s', '%s')`, re.Pattern, re.Options) +} + +// MarshalBSON implements [bson.Marshaler]. +func (re *Regex) MarshalBSON() ([]byte, error) { + b := make([]byte, 0, len(re.Pattern)+1+len(re.Options)+1) + b = append(b, re.Pattern...) + b = append(b, 0) + b = append(b, re.Options...) + b = append(b, 0) + return b, nil +} + +// UnmarshalBSON implements [bson.Unmarshaler]. +func (re *Regex) UnmarshalBSON(b []byte) error { + idx := bytes.IndexByte(b, 0) + if idx == -1 { + return errors.New("malformed regex") + } + re.Pattern = string(b[0:idx]) + b = b[idx+1:] + + idx = bytes.IndexByte(b, 0) + if idx == -1 { + return errors.New("malformed regex") + } + re.Options = string(b[:idx]) + + return nil +} + +// Compile returns [regexp.Regexp]. +func (r Regex) Compile() (*regexp.Regexp, error) { + var opts string + for _, o := range r.Options { + switch o { + case 'i', 'm', 's': + opts += string(o) + default: + } + } + + expr := r.Pattern + if opts != "" { + expr = "(?" + opts + ")" + expr + } + + re, err := regexp.Compile(expr) + if err != nil { + return nil, err + } + return re, nil +} diff --git a/regex_test.go b/regex_test.go new file mode 100644 index 0000000..fedcbe9 --- /dev/null +++ b/regex_test.go @@ -0,0 +1,31 @@ +package bson + +import "testing" + +func TestRegex(t *testing.T) { + re := Regex{ + Pattern: "h.ll?ow(or)ld", + Options: "i", + } + + b, err := re.MarshalBSON() + mustOk(t, err) + wantBytes(t, b, "682e6c6c3f6f77286f72296c64006900") + + var re2 Regex + err = re2.UnmarshalBSON(b) + mustOk(t, err) + mustEqual(t, re2.Pattern, re.Pattern) + mustEqual(t, re2.Options, re.Options) +} + +func TestRegexCompile(t *testing.T) { + re := Regex{ + Pattern: "h.ll?ow(or)ld", + Options: "i", + } + + r, err := re.Compile() + mustOk(t, err) + mustEqual(t, r.String(), `(?i)h.ll?ow(or)ld`) +}