| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| package selector |
|
|
| import ( |
| "fmt" |
| "strings" |
| ) |
|
|
| |
| type Kind string |
|
|
| const ( |
| KindNone Kind = "" |
| KindRef Kind = "ref" |
| KindCSS Kind = "css" |
| KindXPath Kind = "xpath" |
| KindText Kind = "text" |
| KindSemantic Kind = "semantic" |
| ) |
|
|
| |
| type Selector struct { |
| Kind Kind `json:"kind"` |
| Value string `json:"value"` |
| } |
|
|
| |
| func (s Selector) String() string { |
| switch s.Kind { |
| case KindRef: |
| return s.Value |
| case KindCSS: |
| return "css:" + s.Value |
| case KindXPath: |
| return "xpath:" + s.Value |
| case KindText: |
| return "text:" + s.Value |
| case KindSemantic: |
| return "find:" + s.Value |
| default: |
| return s.Value |
| } |
| } |
|
|
| |
| func (s Selector) IsEmpty() bool { |
| return s.Value == "" |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func Parse(s string) Selector { |
| s = strings.TrimSpace(s) |
| if s == "" { |
| return Selector{} |
| } |
|
|
| |
| if after, ok := cutPrefix(s, "css:"); ok { |
| return Selector{Kind: KindCSS, Value: after} |
| } |
| if after, ok := cutPrefix(s, "xpath:"); ok { |
| return Selector{Kind: KindXPath, Value: after} |
| } |
| if after, ok := cutPrefix(s, "text:"); ok { |
| return Selector{Kind: KindText, Value: after} |
| } |
| if after, ok := cutPrefix(s, "find:"); ok { |
| return Selector{Kind: KindSemantic, Value: after} |
| } |
| if after, ok := cutPrefix(s, "ref:"); ok { |
| return Selector{Kind: KindRef, Value: after} |
| } |
|
|
| |
| if strings.HasPrefix(s, "//") || strings.HasPrefix(s, "(//") { |
| return Selector{Kind: KindXPath, Value: s} |
| } |
|
|
| |
| if IsRef(s) { |
| return Selector{Kind: KindRef, Value: s} |
| } |
|
|
| |
| return Selector{Kind: KindCSS, Value: s} |
| } |
|
|
| |
| func IsRef(s string) bool { |
| if len(s) < 2 || s[0] != 'e' { |
| return false |
| } |
| for i := 1; i < len(s); i++ { |
| if s[i] < '0' || s[i] > '9' { |
| return false |
| } |
| } |
| return true |
| } |
|
|
| |
| func FromRef(ref string) Selector { |
| if ref == "" { |
| return Selector{} |
| } |
| return Selector{Kind: KindRef, Value: ref} |
| } |
|
|
| |
| func FromCSS(css string) Selector { |
| if css == "" { |
| return Selector{} |
| } |
| return Selector{Kind: KindCSS, Value: css} |
| } |
|
|
| |
| func FromXPath(xpath string) Selector { |
| if xpath == "" { |
| return Selector{} |
| } |
| return Selector{Kind: KindXPath, Value: xpath} |
| } |
|
|
| |
| func FromText(text string) Selector { |
| if text == "" { |
| return Selector{} |
| } |
| return Selector{Kind: KindText, Value: text} |
| } |
|
|
| |
| func FromSemantic(query string) Selector { |
| if query == "" { |
| return Selector{} |
| } |
| return Selector{Kind: KindSemantic, Value: query} |
| } |
|
|
| |
| func (s Selector) Validate() error { |
| if s.IsEmpty() { |
| return fmt.Errorf("empty selector") |
| } |
| switch s.Kind { |
| case KindRef, KindCSS, KindXPath, KindText, KindSemantic: |
| return nil |
| default: |
| return fmt.Errorf("unknown selector kind: %q", s.Kind) |
| } |
| } |
|
|
| |
| func cutPrefix(s, prefix string) (string, bool) { |
| if strings.HasPrefix(s, prefix) { |
| return s[len(prefix):], true |
| } |
| return s, false |
| } |
|
|