github.com/npaton/distribution@v2.3.1-rc.0+incompatible/reference/regexp.go (about)

     1  package reference
     2  
     3  import "regexp"
     4  
     5  var (
     6  	// alphaNumericRegexp defines the alpha numeric atom, typically a
     7  	// component of names. This only allows lower case characters and digits.
     8  	alphaNumericRegexp = match(`[a-z0-9]+`)
     9  
    10  	// separatorRegexp defines the separators allowed to be embedded in name
    11  	// components. This allow one period, one or two underscore and multiple
    12  	// dashes.
    13  	separatorRegexp = match(`(?:[._]|__|[-]*)`)
    14  
    15  	// nameComponentRegexp restricts registry path component names to start
    16  	// with at least one letter or number, with following parts able to be
    17  	// separated by one period, one or two underscore and multiple dashes.
    18  	nameComponentRegexp = expression(
    19  		alphaNumericRegexp,
    20  		optional(repeated(separatorRegexp, alphaNumericRegexp)))
    21  
    22  	// hostnameComponentRegexp restricts the registry hostname component of a
    23  	// repository name to start with a component as defined by hostnameRegexp
    24  	// and followed by an optional port.
    25  	hostnameComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
    26  
    27  	// hostnameRegexp defines the structure of potential hostname components
    28  	// that may be part of image names. This is purposely a subset of what is
    29  	// allowed by DNS to ensure backwards compatibility with Docker image
    30  	// names.
    31  	hostnameRegexp = expression(
    32  		hostnameComponentRegexp,
    33  		optional(repeated(literal(`.`), hostnameComponentRegexp)),
    34  		optional(literal(`:`), match(`[0-9]+`)))
    35  
    36  	// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
    37  	TagRegexp = match(`[\w][\w.-]{0,127}`)
    38  
    39  	// anchoredTagRegexp matches valid tag names, anchored at the start and
    40  	// end of the matched string.
    41  	anchoredTagRegexp = anchored(TagRegexp)
    42  
    43  	// DigestRegexp matches valid digests.
    44  	DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
    45  
    46  	// anchoredDigestRegexp matches valid digests, anchored at the start and
    47  	// end of the matched string.
    48  	anchoredDigestRegexp = anchored(DigestRegexp)
    49  
    50  	// NameRegexp is the format for the name component of references. The
    51  	// regexp has capturing groups for the hostname and name part omitting
    52  	// the seperating forward slash from either.
    53  	NameRegexp = expression(
    54  		optional(hostnameRegexp, literal(`/`)),
    55  		nameComponentRegexp,
    56  		optional(repeated(literal(`/`), nameComponentRegexp)))
    57  
    58  	// anchoredNameRegexp is used to parse a name value, capturing the
    59  	// hostname and trailing components.
    60  	anchoredNameRegexp = anchored(
    61  		optional(capture(hostnameRegexp), literal(`/`)),
    62  		capture(nameComponentRegexp,
    63  			optional(repeated(literal(`/`), nameComponentRegexp))))
    64  
    65  	// ReferenceRegexp is the full supported format of a reference. The regexp
    66  	// is anchored and has capturing groups for name, tag, and digest
    67  	// components.
    68  	ReferenceRegexp = anchored(capture(NameRegexp),
    69  		optional(literal(":"), capture(TagRegexp)),
    70  		optional(literal("@"), capture(DigestRegexp)))
    71  )
    72  
    73  // match compiles the string to a regular expression.
    74  var match = regexp.MustCompile
    75  
    76  // literal compiles s into a literal regular expression, escaping any regexp
    77  // reserved characters.
    78  func literal(s string) *regexp.Regexp {
    79  	re := match(regexp.QuoteMeta(s))
    80  
    81  	if _, complete := re.LiteralPrefix(); !complete {
    82  		panic("must be a literal")
    83  	}
    84  
    85  	return re
    86  }
    87  
    88  // expression defines a full expression, where each regular expression must
    89  // follow the previous.
    90  func expression(res ...*regexp.Regexp) *regexp.Regexp {
    91  	var s string
    92  	for _, re := range res {
    93  		s += re.String()
    94  	}
    95  
    96  	return match(s)
    97  }
    98  
    99  // optional wraps the expression in a non-capturing group and makes the
   100  // production optional.
   101  func optional(res ...*regexp.Regexp) *regexp.Regexp {
   102  	return match(group(expression(res...)).String() + `?`)
   103  }
   104  
   105  // repeated wraps the regexp in a non-capturing group to get one or more
   106  // matches.
   107  func repeated(res ...*regexp.Regexp) *regexp.Regexp {
   108  	return match(group(expression(res...)).String() + `+`)
   109  }
   110  
   111  // group wraps the regexp in a non-capturing group.
   112  func group(res ...*regexp.Regexp) *regexp.Regexp {
   113  	return match(`(?:` + expression(res...).String() + `)`)
   114  }
   115  
   116  // capture wraps the expression in a capturing group.
   117  func capture(res ...*regexp.Regexp) *regexp.Regexp {
   118  	return match(`(` + expression(res...).String() + `)`)
   119  }
   120  
   121  // anchored anchors the regular expression by adding start and end delimiters.
   122  func anchored(res ...*regexp.Regexp) *regexp.Regexp {
   123  	return match(`^` + expression(res...).String() + `$`)
   124  }