package router import ( "github.com/franckcuny/web-request" "regexp" "strings" ) type fn func(*request.Request, *request.Response) error type fns map[string]fn type Route struct { Method string Path string Code fn Validations map[string]*regexp.Regexp Defaults map[string]string components []string requiredNamedComponents map[string]bool optionalNamedComponents map[string]bool length int lengthWithoutOptional int } type Match struct { path string Route Route mapping map[string]string method string } var componentIsVariable = regexp.MustCompile("^{[^}]+}$") var componentsIsOptional = regexp.MustCompile("^\\?{.*}$") var namedComponentsRegex = regexp.MustCompile("^{(.*)}$") var convertComponent = regexp.MustCompile("^\\??{(.*)}$") func (self *Match) RouteParams() map[string]string { return self.mapping } func (self *Match) GetPath() string { return self.path } func (self *Match) GetMethod() string { return self.method } // 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 { return nil } if len(components) < self.lengthWithoutOptional || len(components) > self.length { return nil } var mapping map[string]string if self.Defaults != nil { mapping = self.Defaults } else { mapping = map[string]string{} } currentComponentsLength := len(components) 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 } } } return &Match{ path: strings.Join(components, "/"), Route: *self, method: method, mapping: mapping, } } func (self *Route) Execute(request *request.Request, response *request.Response) error { return self.Code(request, response) } func (self *Route) init() { self.components = []string{} for _, c := range strings.Split(self.Path, "/") { self.components = append(self.components, c) } 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() { self.requiredNamedComponents = map[string]bool{} self.optionalNamedComponents = map[string]bool{} for _, c := range self.components { if namedComponentsRegex.MatchString(c) == true { self.requiredNamedComponents[self.convertComponentName(c)] = true } else if componentsIsOptional.MatchString(c) == true { self.optionalNamedComponents[self.convertComponentName(c)] = true } } self.lengthWithoutOptional = self.length - len(self.optionalNamedComponents) }