diff options
| -rw-r--r-- | engine.go | 3 | ||||
| -rw-r--r-- | parser.go | 55 | ||||
| -rw-r--r-- | parser_test.go | 79 | ||||
| -rw-r--r-- | router.go | 15 | ||||
| -rw-r--r-- | router_test.go | 35 | ||||
| -rw-r--r-- | types.go | 1 |
6 files changed, 154 insertions, 34 deletions
@@ -33,7 +33,7 @@ func (api *Engine) DELETE(path string, h Handler) { // serve and start func (a *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { - handler, params := a.router.Find(req.Method, req.URL.Path) + handler, params, query := a.router.Find(req.Method, req.URL.Path) if handler == nil { http.NotFound(w, req) return @@ -42,6 +42,7 @@ func (a *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { W: w, R: req, Params: params, + Querys: query, }) } diff --git a/parser.go b/parser.go new file mode 100644 index 0000000..87b06d0 --- /dev/null +++ b/parser.go @@ -0,0 +1,55 @@ +package growel + +import ( + "fmt" + "strings" +) + +func ParsePath(path string) ([]string, map[string]string) { + var query_str string + path, query_str = extractQueryStr(path) + + parts := splitPath(path) + + if len(parts) == 0 { + return parts, map[string]string{} + } + + query := parseQueryStr(query_str) + + return parts, query +} + +func splitPath(path string) []string { + return strings.Split(strings.Trim(path, "/"), "/") +} + +func extractQueryStr(str string) (path string, querystr string) { + str_slice := strings.Split(str, "?") + leng := len(str_slice) + if leng > 2 { + fmt.Println("Error parsing path: multiple '?' found.") + // return anyway + } else if leng == 1 { + // no query string found + return str_slice[0], "" + } + return str_slice[0], str_slice[1] +} + +func parseQueryStr(str string) map[string]string { + query := make(map[string]string) + if str == "" { + return query + } + pairs := strings.Split(str, "&") + for _, pair := range pairs { + kv := strings.Split(pair, "=") + if len(kv) != 2 { + fmt.Println("Error parsing query string: invalid key-value pair.") + continue + } + query[kv[0]] = kv[1] + } + return query +} diff --git a/parser_test.go b/parser_test.go new file mode 100644 index 0000000..c63f758 --- /dev/null +++ b/parser_test.go @@ -0,0 +1,79 @@ +package growel + +import ( + "fmt" + "reflect" + "testing" +) + +func TestRouterSplitPath(t *testing.T) { + tests := []struct { + input string + want []string + }{ + {"/", []string{""}}, + {"/hello", []string{"hello"}}, + {"/hello/world", []string{"hello", "world"}}, + {"//hello//world//", []string{"hello", "", "world"}}, + {"/hello?world=true", []string{"hello?world=true"}}, + } + + for _, tt := range tests { + got := splitPath(tt.input) + if len(got) != len(tt.want) { + t.Fatalf("splitPath(%q) length mismatch: got %v want %v", tt.input, got, tt.want) + } + for i := range got { + if got[i] != tt.want[i] { + t.Fatalf("splitPath(%q) mismatch: got %v want %v", tt.input, got, tt.want) + } + } + } +} + +func TestExtractQueryStr(t *testing.T) { + tests := []struct { + input string + want1 string + want2 string + }{ + {"", "", ""}, + {"hello", "hello", ""}, + {"user/23/", "user/23/", ""}, + {"api?str=123&yrs=23", "api", "str=123&yrs=23"}, + {"w?st?r", "w", "st"}, + {"w?str", "w", "str"}, + } + + for _, tt := range tests { + got1, got2 := extractQueryStr(tt.input) + if got1 != tt.want1 && got2 != tt.want2 { + fmt.Printf("want1: %s, got1: %s\n", tt.want1, got1) + fmt.Printf("want2: %s, got2: %s\n", tt.want2, got2) + t.Fail() + } + } +} + +func TestParseQueryStr(t *testing.T) { + tests := []struct { + input string + want map[string]string + }{ + {"str=123&yrs=23", map[string]string{ + "str": "123", + "yrs": "23", + }}, + {"str=abc", map[string]string{ + "str": "abc", + }}, + } + + for _, tt := range tests { + got := parseQueryStr(tt.input) + if !reflect.DeepEqual(got, tt.want) { + fmt.Printf("want: %s, got: %s\n", tt.want, got) + t.Fail() + } + } +} @@ -8,17 +8,14 @@ func NewRouter() *Router { return &Router{} } -func (r *Router) splitPath(path string) []string { - return strings.Split(strings.Trim(path, "/"), "/") -} - func (r *Router) Add(method string, path string, h Handler) { - parts := r.splitPath(path) + parts, _ := ParsePath(path) r.routes = append(r.routes, Route{method, parts, h}) } -func (r *Router) Find(method string, path string) (Handler, map[string]string) { - parts := r.splitPath(path) +func (r *Router) Find(method string, path string) ( + handler Handler, params map[string]string, query map[string]string) { + parts, query := ParsePath(path) for _, rt := range r.routes { // match HTTP method @@ -49,8 +46,8 @@ func (r *Router) Find(method string, path string) (Handler, map[string]string) { } } if matched { - return rt.handler, params + return rt.handler, params, query } } - return nil, nil + return nil, nil, nil } diff --git a/router_test.go b/router_test.go index 24a2faa..e03d16f 100644 --- a/router_test.go +++ b/router_test.go @@ -10,7 +10,7 @@ func TestRouterStaticMatch(t *testing.T) { r := NewRouter() r.Add("GET", "/hello", dummy) - h, params := r.Find("GET", "/hello") + h, params, _ := r.Find("GET", "/hello") if h == nil { t.Fatal("expected handler, got nil") } @@ -23,7 +23,7 @@ func TestRouterStaticNoMatch(t *testing.T) { r := NewRouter() r.Add("GET", "/hello", dummy) - h, _ := r.Find("GET", "/nothello") + h, _, _ := r.Find("GET", "/nothello") if h != nil { t.Fatal("expected nil handler, got non-nil") } @@ -33,7 +33,7 @@ func TestRouterParamMatch(t *testing.T) { r := NewRouter() r.Add("GET", "/user/:id", dummy) - h, params := r.Find("GET", "/user/42") + h, params, _ := r.Find("GET", "/user/42") if h == nil { t.Fatal("expected handler, got nil") } @@ -46,34 +46,21 @@ func TestRouterParamMismatch(t *testing.T) { r := NewRouter() r.Add("GET", "/user/:id", dummy) - h, _ := r.Find("GET", "/user") + h, _, _ := r.Find("GET", "/user") if h != nil { t.Fatal("expected nil handler, got non-nil") } } -func TestRouterSplitPath(t *testing.T) { +func TestRouterQueryMatch(t *testing.T) { r := NewRouter() + r.Add("GET", "/user", dummy) - tests := []struct { - input string - want []string - }{ - {"/", []string{""}}, - {"/hello", []string{"hello"}}, - {"/hello/world", []string{"hello", "world"}}, - {"//hello//world//", []string{"hello", "", "world"}}, + h, _, query := r.Find("GET", "/user?id=42") + if h == nil { + t.Fatal("expected handler, got nil") } - - for _, tt := range tests { - got := r.splitPath(tt.input) - if len(got) != len(tt.want) { - t.Fatalf("splitPath(%q) length mismatch: got %v want %v", tt.input, got, tt.want) - } - for i := range got { - if got[i] != tt.want[i] { - t.Fatalf("splitPath(%q) mismatch: got %v want %v", tt.input, got, tt.want) - } - } + if query["id"] != "42" { + t.Fatalf("expected id=42, got %v", query) } } @@ -6,6 +6,7 @@ type Context struct { W http.ResponseWriter R *http.Request Params map[string]string + Querys map[string]string } type Handler func(c *Context) |
