...

Source file src/github.com/ory/x/templatex/regex.go

Documentation: github.com/ory/x/templatex

     1  // Package compiler offers a regexp compiler which compiles regex templates to regexp.Regexp
     2  //
     3  //  reg, err := compiler.CompileRegex("foo:bar.baz:<[0-9]{2,10}>", '<', '>')
     4  //  // if err != nil ...
     5  //  reg.MatchString("foo:bar.baz:123")
     6  //
     7  //  reg, err := compiler.CompileRegex("/foo/bar/url/{[a-z]+}", '{', '}')
     8  //  // if err != nil ...
     9  //  reg.MatchString("/foo/bar/url/abz")
    10  //
    11  // This package is adapts github.com/gorilla/mux/regexp.go
    12  
    13  package templatex
    14  
    15  // Copyright 2012 The Gorilla Authors. All rights reserved.
    16  // Use of this source code is governed by a BSD-style
    17  // license as follows:
    18  
    19  //Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
    20  //
    21  //Redistribution and use in source and binary forms, with or without
    22  //modification, are permitted provided that the following conditions are
    23  //met:
    24  //
    25  //* Redistributions of source code must retain the above copyright
    26  //notice, this list of conditions and the following disclaimer.
    27  //* Redistributions in binary form must reproduce the above
    28  //copyright notice, this list of conditions and the following disclaimer
    29  //in the documentation and/or other materials provided with the
    30  //distribution.
    31  //* Neither the name of Google Inc. nor the names of its
    32  //contributors may be used to endorse or promote products derived from
    33  //this software without specific prior written permission.
    34  //
    35  //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    36  //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    37  //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    38  //A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    39  //OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    40  //SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    41  //LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    42  //DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    43  //THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    44  //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    45  //OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    46  
    47  import (
    48  	"bytes"
    49  	"fmt"
    50  	"regexp"
    51  
    52  	"github.com/pkg/errors"
    53  )
    54  
    55  // delimiterIndices returns the first level delimiter indices from a string.
    56  // It returns an error in case of unbalanced delimiters.
    57  func delimiterIndices(s string, delimiterStart, delimiterEnd byte) ([]int, error) {
    58  	var level, idx int
    59  	idxs := make([]int, 0)
    60  	for i := 0; i < len(s); i++ {
    61  		switch s[i] {
    62  		case delimiterStart:
    63  			if level++; level == 1 {
    64  				idx = i
    65  			}
    66  		case delimiterEnd:
    67  			if level--; level == 0 {
    68  				idxs = append(idxs, idx, i+1)
    69  			} else if level < 0 {
    70  				return nil, errors.Errorf("unbalanced braces in: %s", s)
    71  			}
    72  		}
    73  	}
    74  
    75  	if level != 0 {
    76  		return nil, errors.Errorf("unbalanced braces in: %s", s)
    77  	}
    78  
    79  	return idxs, nil
    80  }
    81  
    82  // CompileRegex parses a template and returns a Regexp.
    83  //
    84  // You can define your own delimiters. It is e.g. common to use curly braces {} but I recommend using characters
    85  // which have no special meaning in Regex, e.g.: <, >
    86  //
    87  //  reg, err := templatex.CompileRegex("foo:bar.baz:<[0-9]{2,10}>", '<', '>')
    88  //  // if err != nil ...
    89  //  reg.MatchString("foo:bar.baz:123")
    90  func CompileRegex(tpl string, delimiterStart, delimiterEnd byte) (*regexp.Regexp, error) {
    91  	// Check if it is well-formed.
    92  	idxs, errBraces := delimiterIndices(tpl, delimiterStart, delimiterEnd)
    93  	if errBraces != nil {
    94  		return nil, errBraces
    95  	}
    96  	varsR := make([]*regexp.Regexp, len(idxs)/2)
    97  	pattern := bytes.NewBufferString("")
    98  	if err := pattern.WriteByte('^'); err != nil {
    99  		return nil, errors.WithStack(err)
   100  	}
   101  
   102  	var end int
   103  	var err error
   104  	for i := 0; i < len(idxs); i += 2 {
   105  		// Set all values we are interested in.
   106  		raw := tpl[end:idxs[i]]
   107  		end = idxs[i+1]
   108  		patt := tpl[idxs[i]+1 : end-1]
   109  		// Build the regexp pattern.
   110  		varIdx := i / 2
   111  		fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
   112  		varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
   113  		if err != nil {
   114  			return nil, errors.WithStack(err)
   115  		}
   116  	}
   117  
   118  	// Add the remaining.
   119  	raw := tpl[end:]
   120  	if _, err := pattern.WriteString(regexp.QuoteMeta(raw)); err != nil {
   121  		return nil, errors.WithStack(err)
   122  	}
   123  	if err := pattern.WriteByte('$'); err != nil {
   124  		return nil, errors.WithStack(err)
   125  	}
   126  
   127  	// Compile full regexp.
   128  	reg, errCompile := regexp.Compile(pattern.String())
   129  	if errCompile != nil {
   130  		return nil, errors.WithStack(errCompile)
   131  	}
   132  
   133  	return reg, nil
   134  }
   135  

View as plain text