From 2b686439cd29dd08310ed13224e25035032ff961 Mon Sep 17 00:00:00 2001 From: deoxxa Date: Sat, 25 Mar 2017 18:54:48 +1100 Subject: [PATCH 1/2] add a "flag" syntax, similar to regexp flags --- extract.go | 28 +++++++++++++++++++++++++++- extract_test.go | 17 +++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/extract.go b/extract.go index 80cfa48..9ce9cf1 100644 --- a/extract.go +++ b/extract.go @@ -10,6 +10,7 @@ import ( type ( path struct { + flags []string selector string acc string parsers []parser @@ -17,6 +18,16 @@ type ( } ) +func (p *path) hasFlag(flag string) bool { + for _, v := range p.flags { + if v == flag { + return true + } + } + + return false +} + const ( accessorAttr = "attr" accessorHTML = "html" @@ -72,7 +83,22 @@ func parseFunctionSignature(s string) (string, string) { func parseTag(tag reflect.StructTag) (*path, error) { p := &path{} - for i, part := range strings.Split(tag.Get("sq"), " | ") { + + s := tag.Get("sq") + + if len(s) > 0 && s[0] == '(' { + if end := strings.Index(s, ")"); end != -1 { + for _, e := range strings.Split(s[1:end], ",") { + if e = strings.TrimSpace(e); e != "" { + p.flags = append(p.flags, e) + } + } + + s = strings.TrimLeft(s[end:], ") ") + } + } + + for i, part := range strings.Split(s, " | ") { part = strings.TrimSpace(part) switch i { case 0: diff --git a/extract_test.go b/extract_test.go index 207134d..79cade2 100644 --- a/extract_test.go +++ b/extract_test.go @@ -120,6 +120,14 @@ func TestParseTag(t *testing.T) { }, nil, }, + {`sq:"() p"`, &path{flags: []string{}, selector: "p"}, nil}, + {`sq:"( ) p"`, &path{flags: []string{}, selector: "p"}, nil}, + {`sq:"(opt1) p"`, &path{flags: []string{"opt1"}, selector: "p"}, nil}, + {`sq:"( opt1 ) p"`, &path{flags: []string{"opt1"}, selector: "p"}, nil}, + {`sq:"(opt1,opt2) p"`, &path{flags: []string{"opt1", "opt2"}, selector: "p"}, nil}, + {`sq:"( opt1 , opt2 ) p"`, &path{flags: []string{"opt1", "opt2"}, selector: "p"}, nil}, + {`sq:"(opt1,opt2,opt3) p"`, &path{flags: []string{"opt1", "opt2", "opt3"}, selector: "p"}, nil}, + {`sq:"( opt1 ,opt2, opt3 ) p"`, &path{flags: []string{"opt1", "opt2", "opt3"}, selector: "p"}, nil}, // bad {`sq:"p.last | fuzzy"`, nil, fmt.Errorf("Bad accessor: %q", `fuzzy`)}, @@ -136,6 +144,15 @@ func TestParseTag(t *testing.T) { } continue } + if len(p.flags) != len(test.p.flags) { + t.Errorf("Expected %#v, got %#v", test.p.flags, p.flags) + } else { + for i := 0; i < len(p.flags); i++ { + if p.flags[i] != test.p.flags[i] { + t.Errorf("Expected %#v, got %#v", test.p.flags, p.flags) + } + } + } if p.selector != test.p.selector { t.Errorf("Expected %q, got %q", test.p.selector, p.selector) } From 84d61a74df7615d43f24e54e771e184ab3df1f89 Mon Sep 17 00:00:00 2001 From: deoxxa Date: Sat, 25 Mar 2017 18:55:12 +1100 Subject: [PATCH 2/2] implement "optional" flag for selectors that might not exist --- sq.go | 4 ++++ test/types.go | 1 + 2 files changed, 5 insertions(+) diff --git a/sq.go b/sq.go index 5292dc9..4c92f42 100644 --- a/sq.go +++ b/sq.go @@ -62,6 +62,10 @@ func hydrateValue(v *reflect.Value, sel *goquery.Selection, p *path) []error { if p != nil && len(p.selector) > 0 && p.selector != "." && !sel.Is(p.selector) { sel = sel.Find(p.selector) if sel.Size() == 0 { + if p.hasFlag("optional") { + return nil + } + return []error{fmt.Errorf("%q did not match", p.selector)} } } diff --git a/test/types.go b/test/types.go index c0bdb95..0c77721 100644 --- a/test/types.go +++ b/test/types.go @@ -57,6 +57,7 @@ type ( Stylesheet *css.Stylesheet `sq:"style:first-of-type"` Stylesheets []*css.Stylesheet `sq:"style"` CustomType CustomType `sq:"p.string"` + Optional string `sq:"(optional) blink"` // errs Map map[string]interface{} `sq:"div"`