package mooh import ( "regexp" "strings" ) type fn func(*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("^\\??:(.*)$") // 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) (Response, error) { return self.Code(request) } 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) }