| package reference |
| |
| import "regexp" |
| |
| var ( |
| // alphaNumericRegexp defines the alpha numeric atom, typically a |
| // component of names. This only allows lower case characters and digits. |
| alphaNumericRegexp = match(`[a-z0-9]+`) |
| |
| // separatorRegexp defines the separators allowed to be embedded in name |
| // components. This allow one period, one or two underscore and multiple |
| // dashes. |
| separatorRegexp = match(`(?:[._]|__|[-]*)`) |
| |
| // nameComponentRegexp restricts registry path component names to start |
| // with at least one letter or number, with following parts able to be |
| // separated by one period, one or two underscore and multiple dashes. |
| nameComponentRegexp = expression( |
| alphaNumericRegexp, |
| optional(repeated(separatorRegexp, alphaNumericRegexp))) |
| |
| // domainComponentRegexp restricts the registry domain component of a |
| // repository name to start with a component as defined by DomainRegexp |
| // and followed by an optional port. |
| domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) |
| |
| // DomainRegexp defines the structure of potential domain components |
| // that may be part of image names. This is purposely a subset of what is |
| // allowed by DNS to ensure backwards compatibility with Docker image |
| // names. |
| DomainRegexp = expression( |
| domainComponentRegexp, |
| optional(repeated(literal(`.`), domainComponentRegexp)), |
| optional(literal(`:`), match(`[0-9]+`))) |
| |
| // TagRegexp matches valid tag names. From docker/docker:graph/tags.go. |
| TagRegexp = match(`[\w][\w.-]{0,127}`) |
| |
| // anchoredTagRegexp matches valid tag names, anchored at the start and |
| // end of the matched string. |
| anchoredTagRegexp = anchored(TagRegexp) |
| |
| // DigestRegexp matches valid digests. |
| DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) |
| |
| // anchoredDigestRegexp matches valid digests, anchored at the start and |
| // end of the matched string. |
| anchoredDigestRegexp = anchored(DigestRegexp) |
| |
| // NameRegexp is the format for the name component of references. The |
| // regexp has capturing groups for the domain and name part omitting |
| // the separating forward slash from either. |
| NameRegexp = expression( |
| optional(DomainRegexp, literal(`/`)), |
| nameComponentRegexp, |
| optional(repeated(literal(`/`), nameComponentRegexp))) |
| |
| // anchoredNameRegexp is used to parse a name value, capturing the |
| // domain and trailing components. |
| anchoredNameRegexp = anchored( |
| optional(capture(DomainRegexp), literal(`/`)), |
| capture(nameComponentRegexp, |
| optional(repeated(literal(`/`), nameComponentRegexp)))) |
| |
| // ReferenceRegexp is the full supported format of a reference. The regexp |
| // is anchored and has capturing groups for name, tag, and digest |
| // components. |
| ReferenceRegexp = anchored(capture(NameRegexp), |
| optional(literal(":"), capture(TagRegexp)), |
| optional(literal("@"), capture(DigestRegexp))) |
| |
| // IdentifierRegexp is the format for string identifier used as a |
| // content addressable identifier using sha256. These identifiers |
| // are like digests without the algorithm, since sha256 is used. |
| IdentifierRegexp = match(`([a-f0-9]{64})`) |
| |
| // ShortIdentifierRegexp is the format used to represent a prefix |
| // of an identifier. A prefix may be used to match a sha256 identifier |
| // within a list of trusted identifiers. |
| ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`) |
| |
| // anchoredIdentifierRegexp is used to check or match an |
| // identifier value, anchored at start and end of string. |
| anchoredIdentifierRegexp = anchored(IdentifierRegexp) |
| |
| // anchoredShortIdentifierRegexp is used to check if a value |
| // is a possible identifier prefix, anchored at start and end |
| // of string. |
| anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp) |
| ) |
| |
| // match compiles the string to a regular expression. |
| var match = regexp.MustCompile |
| |
| // literal compiles s into a literal regular expression, escaping any regexp |
| // reserved characters. |
| func literal(s string) *regexp.Regexp { |
| re := match(regexp.QuoteMeta(s)) |
| |
| if _, complete := re.LiteralPrefix(); !complete { |
| panic("must be a literal") |
| } |
| |
| return re |
| } |
| |
| // expression defines a full expression, where each regular expression must |
| // follow the previous. |
| func expression(res ...*regexp.Regexp) *regexp.Regexp { |
| var s string |
| for _, re := range res { |
| s += re.String() |
| } |
| |
| return match(s) |
| } |
| |
| // optional wraps the expression in a non-capturing group and makes the |
| // production optional. |
| func optional(res ...*regexp.Regexp) *regexp.Regexp { |
| return match(group(expression(res...)).String() + `?`) |
| } |
| |
| // repeated wraps the regexp in a non-capturing group to get one or more |
| // matches. |
| func repeated(res ...*regexp.Regexp) *regexp.Regexp { |
| return match(group(expression(res...)).String() + `+`) |
| } |
| |
| // group wraps the regexp in a non-capturing group. |
| func group(res ...*regexp.Regexp) *regexp.Regexp { |
| return match(`(?:` + expression(res...).String() + `)`) |
| } |
| |
| // capture wraps the expression in a capturing group. |
| func capture(res ...*regexp.Regexp) *regexp.Regexp { |
| return match(`(` + expression(res...).String() + `)`) |
| } |
| |
| // anchored anchors the regular expression by adding start and end delimiters. |
| func anchored(res ...*regexp.Regexp) *regexp.Regexp { |
| return match(`^` + expression(res...).String() + `$`) |
| } |