mirror of https://github.com/antonmedv/fx
wip
parent
845fa0e25e
commit
3c88d21ea4
@ -0,0 +1,176 @@
|
||||
package dig
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type state int
|
||||
|
||||
const (
|
||||
start state = iota
|
||||
unknown
|
||||
propOrIndex
|
||||
prop
|
||||
index
|
||||
indexEnd
|
||||
number
|
||||
doubleQuote
|
||||
doubleQuoteEscape
|
||||
singleQuote
|
||||
singleQuoteEscape
|
||||
)
|
||||
|
||||
func SplitPath(p string) ([]any, bool) {
|
||||
path := make([]any, 0)
|
||||
s := ""
|
||||
state := start
|
||||
for _, ch := range p {
|
||||
switch state {
|
||||
|
||||
case start:
|
||||
switch {
|
||||
case ch == 'x':
|
||||
state = unknown
|
||||
case ch == '.':
|
||||
state = propOrIndex
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case unknown:
|
||||
switch {
|
||||
case ch == '.':
|
||||
state = prop
|
||||
s = ""
|
||||
case ch == '[':
|
||||
state = index
|
||||
s = ""
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case propOrIndex:
|
||||
switch {
|
||||
case isProp(ch):
|
||||
state = prop
|
||||
s = string(ch)
|
||||
case ch == '[':
|
||||
state = index
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case prop:
|
||||
switch {
|
||||
case isProp(ch):
|
||||
s += string(ch)
|
||||
case ch == '.':
|
||||
state = prop
|
||||
path = append(path, s)
|
||||
s = ""
|
||||
case ch == '[':
|
||||
state = index
|
||||
path = append(path, s)
|
||||
s = ""
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case index:
|
||||
switch {
|
||||
case unicode.IsDigit(ch):
|
||||
state = number
|
||||
s = string(ch)
|
||||
case ch == '"':
|
||||
state = doubleQuote
|
||||
s = ""
|
||||
case ch == '\'':
|
||||
state = singleQuote
|
||||
s = ""
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case indexEnd:
|
||||
switch {
|
||||
case ch == ']':
|
||||
state = unknown
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case number:
|
||||
switch {
|
||||
case unicode.IsDigit(ch):
|
||||
s += string(ch)
|
||||
case ch == ']':
|
||||
state = unknown
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return path, false
|
||||
}
|
||||
path = append(path, n)
|
||||
s = ""
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case doubleQuote:
|
||||
switch ch {
|
||||
case '"':
|
||||
state = indexEnd
|
||||
path = append(path, s)
|
||||
s = ""
|
||||
case '\\':
|
||||
state = doubleQuoteEscape
|
||||
default:
|
||||
s += string(ch)
|
||||
}
|
||||
|
||||
case doubleQuoteEscape:
|
||||
switch ch {
|
||||
case '"':
|
||||
state = doubleQuote
|
||||
s += string(ch)
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
|
||||
case singleQuote:
|
||||
switch ch {
|
||||
case '\'':
|
||||
state = indexEnd
|
||||
path = append(path, s)
|
||||
s = ""
|
||||
case '\\':
|
||||
state = singleQuoteEscape
|
||||
s += string(ch)
|
||||
default:
|
||||
s += string(ch)
|
||||
}
|
||||
|
||||
case singleQuoteEscape:
|
||||
switch ch {
|
||||
case '\'':
|
||||
state = singleQuote
|
||||
s += string(ch)
|
||||
default:
|
||||
return path, false
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(s) > 0 {
|
||||
if state == prop {
|
||||
path = append(path, s)
|
||||
} else {
|
||||
return path, false
|
||||
}
|
||||
|
||||
}
|
||||
return path, true
|
||||
}
|
||||
|
||||
func isProp(ch rune) bool {
|
||||
return unicode.IsLetter(ch) || unicode.IsDigit(ch) || ch == '_' || ch == '$'
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
package dig_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/antonmedv/fx/new/dig"
|
||||
)
|
||||
|
||||
func Test_SplitPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want []any
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
want: []any{},
|
||||
},
|
||||
{
|
||||
input: ".",
|
||||
want: []any{},
|
||||
},
|
||||
{
|
||||
input: "x",
|
||||
want: []any{},
|
||||
},
|
||||
{
|
||||
input: ".foo",
|
||||
want: []any{"foo"},
|
||||
},
|
||||
{
|
||||
input: "x.foo",
|
||||
want: []any{"foo"},
|
||||
},
|
||||
{
|
||||
input: "x[42]",
|
||||
want: []any{42},
|
||||
},
|
||||
{
|
||||
input: ".[42]",
|
||||
want: []any{42},
|
||||
},
|
||||
{
|
||||
input: ".42",
|
||||
want: []any{"42"},
|
||||
},
|
||||
{
|
||||
input: ".физ",
|
||||
want: []any{"физ"},
|
||||
},
|
||||
{
|
||||
input: ".foo.bar",
|
||||
want: []any{"foo", "bar"},
|
||||
},
|
||||
{
|
||||
input: ".foo[42]",
|
||||
want: []any{"foo", 42},
|
||||
},
|
||||
{
|
||||
input: ".foo[42].bar",
|
||||
want: []any{"foo", 42, "bar"},
|
||||
},
|
||||
{
|
||||
input: ".foo[1][2]",
|
||||
want: []any{"foo", 1, 2},
|
||||
},
|
||||
{
|
||||
input: ".foo[\"bar\"]",
|
||||
want: []any{"foo", "bar"},
|
||||
},
|
||||
{
|
||||
input: ".foo[\"bar\\\"\"]",
|
||||
want: []any{"foo", "bar\""},
|
||||
},
|
||||
{
|
||||
input: ".foo['bar']['baz\\'']",
|
||||
want: []any{"foo", "bar", "baz\\'"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
p, ok := dig.SplitPath(tt.input)
|
||||
require.Equal(t, tt.want, p)
|
||||
require.True(t, ok)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SplitPath_negative(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
}{
|
||||
{
|
||||
input: "./",
|
||||
},
|
||||
{
|
||||
input: "x/",
|
||||
},
|
||||
{
|
||||
input: "1+1",
|
||||
},
|
||||
{
|
||||
input: "x[42",
|
||||
},
|
||||
{
|
||||
input: ".i % 2",
|
||||
},
|
||||
{
|
||||
input: "x[for x]",
|
||||
},
|
||||
{
|
||||
input: "x['y'.",
|
||||
},
|
||||
{
|
||||
input: "x[0?",
|
||||
},
|
||||
{
|
||||
input: "x[\"\\u",
|
||||
},
|
||||
{
|
||||
input: "x['\\n",
|
||||
},
|
||||
{
|
||||
input: "x[9999999999999999999999999999999999999]",
|
||||
},
|
||||
{
|
||||
input: "x[]",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
p, ok := dig.SplitPath(tt.input)
|
||||
require.False(t, ok, p)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue