about summary refs log tree commit diff
path: root/route.go
diff options
context:
space:
mode:
authorFranck Cuny <franck@lumberjaph.net>2013-04-25 20:01:23 -0700
committerFranck Cuny <franck@lumberjaph.net>2013-04-26 07:41:24 -0700
commitf1d0b1f2ce7a90b80079c8e0e2ef4642d36ec941 (patch)
tree85dd4aaa279abc49d0f6268c4bfc099aa18dc967 /route.go
parentAdd build status (diff)
downloadpath-router-f1d0b1f2ce7a90b80079c8e0e2ef4642d36ec941.tar.gz
Add Validations for URL parameters.
By adding validations, it's possible to create without ambiguity routes
like "/user/:id" and "/user/:name", where the validation for the name
will check that it's an number, and name is a string.

A validation is not required for every key.  However, you can't specify
a validation for a key that does not exist in the URL. The validation
use Regexp for now, and the user has to create the regexp with the
function MustCompile.

Some utility functions are added to easily gain information about a
component, validator, etc.  This will be extended more soon.
Diffstat (limited to 'route.go')
-rw-r--r--route.go77
1 files changed, 67 insertions, 10 deletions
diff --git a/route.go b/route.go
index b724a04..9eab0ab 100644
--- a/route.go
+++ b/route.go
@@ -17,6 +17,7 @@ type Route struct {
 	optionalNamedComponents map[string]bool
 	length                  int
 	lengthWithoutOptional   int
+	Validations             map[string]*regexp.Regexp
 }
 
 type Match struct {
@@ -31,11 +32,19 @@ var componentsIsOptional = regexp.MustCompile("^\\?:")
 var namedComponentsRegex = regexp.MustCompile("^:(.*)$")
 var convertComponent = regexp.MustCompile("^\\??:(.*)$")
 
+// XXX explain this function
 func (self *Route) convertComponentName(name string) string {
 	newName := convertComponent.FindStringSubmatch(name)
+	if len(newName) == 0 {
+		return name
+	}
 	return newName[1]
 }
 
+func (self *Route) ValidateFor(key string, component string) bool {
+	return self.Validations[key].MatchString(component)
+}
+
 func (self *Route) Match(method string, components []string) *Match {
 
 	if self.Method != method {
@@ -48,15 +57,35 @@ func (self *Route) Match(method string, components []string) *Match {
 
 	mapping := map[string]string{}
 
-	for i, c := range self.components {
-		if componentsIsOptional.MatchString(c) {
-			break
-		}
-		p := components[i]
+	currentComponentsLength := len(components)
 
-		if componentIsVariable.MatchString(c) == true {
-			mapping[self.convertComponentName(c)] = p
-		} else {
+L:
+	for i, c := range self.components {
+		convertedName := self.convertComponentName(c)
+
+		switch {
+		case self.IsOptionalUrlParameter(convertedName) == true:
+			if i < currentComponentsLength && self.HasValidationFor(convertedName) {
+				p := components[i]
+				if self.ValidateFor(convertedName, p) == true {
+					break L
+				}
+			} else {
+				break L
+			}
+		case self.IsRequiredUrlParameter(convertedName) == true:
+			p := components[i]
+			if self.HasValidationFor(convertedName) {
+				if self.ValidateFor(convertedName, p) == true {
+					mapping[convertedName] = p
+				} else {
+					return nil
+				}
+			} else {
+				mapping[convertedName] = p
+			}
+		default:
+			p := components[i]
 			if p != c {
 				return nil
 			}
@@ -85,6 +114,34 @@ func (self *Route) init() {
 	self.length = len(self.components)
 
 	self.getNamedComponents()
+
+	for k, _ := range self.Validations {
+		if self.IsUrlParameter(k) == false {
+			panic(k + " is missing")
+		}
+	}
+}
+
+func (self *Route) IsUrlParameter(component string) bool {
+	if self.IsRequiredUrlParameter(component) == false && self.IsOptionalUrlParameter(component) == false {
+		return false
+	}
+	return true
+}
+
+func (self *Route) IsRequiredUrlParameter(component string) bool {
+	return self.requiredNamedComponents[component]
+}
+
+func (self *Route) IsOptionalUrlParameter(component string) bool {
+	return self.optionalNamedComponents[component]
+}
+
+func (self *Route) HasValidationFor(component string) bool {
+	if self.Validations[component] != nil {
+		return true
+	}
+	return false
 }
 
 func (self *Route) getNamedComponents() {
@@ -93,9 +150,9 @@ func (self *Route) getNamedComponents() {
 
 	for _, c := range self.components {
 		if namedComponentsRegex.MatchString(c) == true {
-			self.requiredNamedComponents[c] = true
+			self.requiredNamedComponents[self.convertComponentName(c)] = true
 		} else if componentsIsOptional.MatchString(c) == true {
-			self.optionalNamedComponents[c] = true
+			self.optionalNamedComponents[self.convertComponentName(c)] = true
 		}
 	}
 	self.lengthWithoutOptional = self.length - len(self.optionalNamedComponents)