regexp.go 5.1 KB

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