diff options
| -rw-r--r-- | edify/library/software/cargo.py | 43 | ||||
| -rw-r--r-- | edify/library/software/checksum.py | 16 | ||||
| -rw-r--r-- | edify/library/software/component.py | 59 | ||||
| -rw-r--r-- | edify/library/software/digest.py | 26 | ||||
| -rw-r--r-- | edify/library/software/docker.py | 94 | ||||
| -rw-r--r-- | edify/library/software/git.py | 15 | ||||
| -rw-r--r-- | edify/library/software/image.py | 94 | ||||
| -rw-r--r-- | edify/library/software/makefile.py | 42 | ||||
| -rw-r--r-- | edify/library/software/package.py | 37 | ||||
| -rw-r--r-- | edify/library/software/ref.py | 71 | ||||
| -rw-r--r-- | edify/library/software/semver.py | 90 | ||||
| -rw-r--r-- | edify/library/software/version.py | 32 |
12 files changed, 562 insertions, 57 deletions
diff --git a/edify/library/software/cargo.py b/edify/library/software/cargo.py index ebaa269..c64e587 100644 --- a/edify/library/software/cargo.py +++ b/edify/library/software/cargo.py @@ -1,10 +1,45 @@ -"""``cargo`` — Rust Cargo crate identifier shape (``name`` or ``name@version``).""" +"""``cargo`` — Cargo crate identifier shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -cargo = RegexBackedPattern( - r"^[a-zA-Z][a-zA-Z0-9_-]{0,63}(?:@\d+(?:\.\d+){0,3}(?:[-.+][a-zA-Z0-9.\-]+)?)?$" +cargo = ( + Pattern() + .start_of_input() + .letter() + .between(0, 63) + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char("_") + .char("-") + .end() + .optional() + .group() + .char("@") + .one_or_more() + .digit() + .between(0, 3) + .group() + .char(".") + .one_or_more() + .digit() + .end() + .optional() + .group() + .any_of_chars("-.+") + .one_or_more() + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char(".") + .char("-") + .end() + .end() + .end() + .end_of_input() ) """Callable :class:`Pattern` for a Cargo crate identifier.""" diff --git a/edify/library/software/checksum.py b/edify/library/software/checksum.py index ed0cfad..aaa6de2 100644 --- a/edify/library/software/checksum.py +++ b/edify/library/software/checksum.py @@ -1,8 +1,18 @@ -"""``checksum`` — hex checksum shape (CRC through SHA).""" +"""``checksum`` — hex checksum shape (any common hash width).""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -checksum = RegexBackedPattern(r"^[a-fA-F0-9]{8,128}$") +checksum = ( + Pattern() + .start_of_input() + .between(8, 128) + .any_of() + .range("a", "f") + .range("A", "F") + .range("0", "9") + .end() + .end_of_input() +) """Callable :class:`Pattern` for a hex checksum (any common hash width).""" diff --git a/edify/library/software/component.py b/edify/library/software/component.py index e1227b9..ffaff4e 100644 --- a/edify/library/software/component.py +++ b/edify/library/software/component.py @@ -1,11 +1,60 @@ -"""``component`` — versioned software-component identifier (``name@version``).""" +"""``component`` — versioned component identifier ``name@version`` shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -component = RegexBackedPattern( - r"^(?:@[a-z0-9][a-z0-9-]*/)?[a-z0-9][a-z0-9._-]{0,213}" - r"@\d+(?:\.\d+){0,3}(?:[-.+][a-zA-Z0-9.\-]+)?$" +component = ( + Pattern() + .start_of_input() + .optional() + .group() + .char("@") + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .zero_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .char("-") + .end() + .char("/") + .end() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .between(0, 213) + .any_of() + .range("a", "z") + .range("0", "9") + .char(".") + .char("_") + .char("-") + .end() + .char("@") + .one_or_more() + .digit() + .between(0, 3) + .group() + .char(".") + .one_or_more() + .digit() + .end() + .optional() + .group() + .any_of_chars("-.+") + .one_or_more() + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char(".") + .char("-") + .end() + .end() + .end_of_input() ) """Callable :class:`Pattern` for a versioned component identifier ``name@version``.""" diff --git a/edify/library/software/digest.py b/edify/library/software/digest.py index 66c895f..69f3d65 100644 --- a/edify/library/software/digest.py +++ b/edify/library/software/digest.py @@ -1,8 +1,28 @@ -"""``digest`` — content-addressable digest shape (``algorithm:hex``).""" +"""``digest`` — content-addressable digest shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -digest = RegexBackedPattern(r"^(?:sha256|sha512|sha1|md5|blake2[bs]?)(?::|-)[a-fA-F0-9]{32,128}$") +digest = ( + Pattern() + .start_of_input() + .group() + .any_of() + .string("sha256") + .string("sha512") + .string("sha1") + .string("md5") + .subexpression(Pattern().string("blake2").optional().any_of_chars("bs")) + .end() + .end() + .any_of_chars(":-") + .between(32, 128) + .any_of() + .range("a", "f") + .range("A", "F") + .range("0", "9") + .end() + .end_of_input() +) """Callable :class:`Pattern` for a content-addressable digest.""" diff --git a/edify/library/software/docker.py b/edify/library/software/docker.py index 2b9d6f3..f76ad59 100644 --- a/edify/library/software/docker.py +++ b/edify/library/software/docker.py @@ -1,13 +1,91 @@ -"""``docker`` — Docker image reference shape.""" +"""``docker`` — container image reference shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -docker = RegexBackedPattern( - r"^(?:(?:[a-z0-9.\-]+(?::\d+)?/)?[a-z0-9]+(?:[._\-][a-z0-9]+)*)" - r"(?:/[a-z0-9]+(?:[._\-][a-z0-9]+)*)*" - r"(?::[a-zA-Z0-9_][a-zA-Z0-9._\-]{0,127})?" - r"(?:@sha256:[a-f0-9]{64})?$" +docker = ( + Pattern() + .start_of_input() + .group() + .optional() + .group() + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .char(".") + .char("-") + .end() + .optional() + .group() + .char(":") + .one_or_more() + .digit() + .end() + .char("/") + .end() + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .zero_or_more() + .group() + .any_of_chars("._-") + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .end() + .end() + .zero_or_more() + .group() + .char("/") + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .zero_or_more() + .group() + .any_of_chars("._-") + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .end() + .end() + .optional() + .group() + .char(":") + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char("_") + .end() + .between(0, 127) + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char(".") + .char("_") + .char("-") + .end() + .end() + .optional() + .group() + .string("@sha256:") + .exactly(64) + .any_of() + .range("a", "f") + .range("0", "9") + .end() + .end() + .end_of_input() ) -"""Callable :class:`Pattern` for a Docker image reference.""" +"""Callable :class:`Pattern` for a docker image reference.""" diff --git a/edify/library/software/git.py b/edify/library/software/git.py index 17d75d3..67a580d 100644 --- a/edify/library/software/git.py +++ b/edify/library/software/git.py @@ -1,8 +1,17 @@ -"""``git`` — 40-character git SHA-1 hash shape.""" +"""``git`` — git commit SHA shape (7-40 hex characters).""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -git = RegexBackedPattern(r"^[a-f0-9]{7,40}$") +git = ( + Pattern() + .start_of_input() + .between(7, 40) + .any_of() + .range("a", "f") + .range("0", "9") + .end() + .end_of_input() +) """Callable :class:`Pattern` for a git commit SHA (7-40 hex characters).""" diff --git a/edify/library/software/image.py b/edify/library/software/image.py index 7708774..9ac7471 100644 --- a/edify/library/software/image.py +++ b/edify/library/software/image.py @@ -1,13 +1,91 @@ -"""``image`` — container-image reference (alias-friendly wrapper around docker).""" +"""``image`` — container image reference shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -image = RegexBackedPattern( - r"^(?:(?:[a-z0-9.\-]+(?::\d+)?/)?[a-z0-9]+(?:[._\-][a-z0-9]+)*)" - r"(?:/[a-z0-9]+(?:[._\-][a-z0-9]+)*)*" - r"(?::[a-zA-Z0-9_][a-zA-Z0-9._\-]{0,127})?" - r"(?:@sha256:[a-f0-9]{64})?$" +image = ( + Pattern() + .start_of_input() + .group() + .optional() + .group() + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .char(".") + .char("-") + .end() + .optional() + .group() + .char(":") + .one_or_more() + .digit() + .end() + .char("/") + .end() + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .zero_or_more() + .group() + .any_of_chars("._-") + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .end() + .end() + .zero_or_more() + .group() + .char("/") + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .zero_or_more() + .group() + .any_of_chars("._-") + .one_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .end() + .end() + .optional() + .group() + .char(":") + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char("_") + .end() + .between(0, 127) + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char(".") + .char("_") + .char("-") + .end() + .end() + .optional() + .group() + .string("@sha256:") + .exactly(64) + .any_of() + .range("a", "f") + .range("0", "9") + .end() + .end() + .end_of_input() ) -"""Callable :class:`Pattern` for a container image reference (same as ``docker``).""" +"""Callable :class:`Pattern` for a image image reference.""" diff --git a/edify/library/software/makefile.py b/edify/library/software/makefile.py index 20e8e06..eecf316 100644 --- a/edify/library/software/makefile.py +++ b/edify/library/software/makefile.py @@ -1,8 +1,44 @@ -"""``makefile`` — Makefile-target line shape (``target: [deps]``).""" +"""``makefile`` — Makefile-target declaration line shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -makefile = RegexBackedPattern(r"^\.?[a-zA-Z][a-zA-Z0-9._-]*(?:\s+[a-zA-Z][a-zA-Z0-9._-]*)*\s*:.*$") +makefile = ( + Pattern() + .start_of_input() + .optional() + .char(".") + .letter() + .zero_or_more() + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char(".") + .char("_") + .char("-") + .end() + .zero_or_more() + .group() + .one_or_more() + .whitespace_char() + .letter() + .zero_or_more() + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char(".") + .char("_") + .char("-") + .end() + .end() + .zero_or_more() + .whitespace_char() + .char(":") + .zero_or_more() + .any_char() + .end_of_input() +) """Callable :class:`Pattern` for a Makefile-target declaration line.""" diff --git a/edify/library/software/package.py b/edify/library/software/package.py index 002688a..ec69af4 100644 --- a/edify/library/software/package.py +++ b/edify/library/software/package.py @@ -1,8 +1,39 @@ -"""``package`` — package identifier shape (``@scope/name`` or ``name``).""" +"""``package`` — npm/pypi-style package identifier shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -package = RegexBackedPattern(r"^(?:@[a-z0-9][a-z0-9-]*/)?[a-z0-9][a-z0-9._-]{0,213}$") +package = ( + Pattern() + .start_of_input() + .optional() + .group() + .char("@") + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .zero_or_more() + .any_of() + .range("a", "z") + .range("0", "9") + .char("-") + .end() + .char("/") + .end() + .any_of() + .range("a", "z") + .range("0", "9") + .end() + .between(0, 213) + .any_of() + .range("a", "z") + .range("0", "9") + .char(".") + .char("_") + .char("-") + .end() + .end_of_input() +) """Callable :class:`Pattern` for an npm/pypi-style package identifier.""" diff --git a/edify/library/software/ref.py b/edify/library/software/ref.py index 7a78148..ba9d253 100644 --- a/edify/library/software/ref.py +++ b/edify/library/software/ref.py @@ -1,14 +1,69 @@ -"""``ref`` — git ref shape (branch, tag, or SHA).""" +"""``ref`` — git ref shape (SHA, ``refs/…``, or bare branch/tag name).""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern, any_of -ref = RegexBackedPattern( - r"^(?:" - r"[a-f0-9]{7,40}" - r"|refs/(?:heads|tags|remotes)/[^\s~^:?*[\\]+" - r"|[^\s~^:?*[\\/][^\s~^:?*[\\]{0,127}" - r")$" + +def _forbidden() -> Pattern: + return ( + Pattern() + .assert_not_ahead() + .any_of() + .whitespace_char() + .any_of_chars("~^:?*[\\") + .end() + .end() + .any_char() + ) + + +def _forbidden_incl_slash() -> Pattern: + return ( + Pattern() + .assert_not_ahead() + .any_of() + .whitespace_char() + .any_of_chars("~^:?*[\\/") + .end() + .end() + .any_char() + ) + + +_sha = ( + Pattern() + .between(7, 40) + .any_of() + .range("a", "f") + .range("0", "9") + .end() +) +_refs = ( + Pattern() + .string("refs/") + .group() + .any_of() + .string("heads") + .string("tags") + .string("remotes") + .end() + .end() + .char("/") + .one_or_more() + .subexpression(_forbidden()) +) +_bare = ( + Pattern() + .subexpression(_forbidden_incl_slash()) + .between(0, 127) + .subexpression(_forbidden()) +) + +ref = ( + Pattern() + .start_of_input() + .subexpression(any_of(_sha, _refs, _bare)) + .end_of_input() ) """Callable :class:`Pattern` for a git ref: SHA, ``refs/heads/…``, or bare branch/tag name.""" diff --git a/edify/library/software/semver.py b/edify/library/software/semver.py index 560c984..3f9aeb0 100644 --- a/edify/library/software/semver.py +++ b/edify/library/software/semver.py @@ -2,12 +2,90 @@ from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern, any_of -semver = RegexBackedPattern( - r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)" - r"(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)" - r"(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?" - r"(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" + +def _positive() -> Pattern: + return any_of( + Pattern().char("0"), + Pattern().range("1", "9").zero_or_more().digit(), + ) + + +def _pre_id() -> Pattern: + return any_of( + Pattern().char("0"), + Pattern().range("1", "9").zero_or_more().digit(), + ( + Pattern() + .zero_or_more() + .digit() + .any_of() + .range("a", "z") + .range("A", "Z") + .char("-") + .end() + .zero_or_more() + .any_of() + .range("0", "9") + .range("a", "z") + .range("A", "Z") + .char("-") + .end() + ), + ) + + +semver = ( + Pattern() + .start_of_input() + .named_capture("major") + .subexpression(_positive()) + .end() + .char(".") + .named_capture("minor") + .subexpression(_positive()) + .end() + .char(".") + .named_capture("patch") + .subexpression(_positive()) + .end() + .optional() + .group() + .char("-") + .named_capture("prerelease") + .subexpression(_pre_id()) + .zero_or_more() + .group() + .char(".") + .subexpression(_pre_id()) + .end() + .end() + .end() + .optional() + .group() + .char("+") + .named_capture("buildmetadata") + .one_or_more() + .any_of() + .range("0", "9") + .range("a", "z") + .range("A", "Z") + .char("-") + .end() + .zero_or_more() + .group() + .char(".") + .one_or_more() + .any_of() + .range("0", "9") + .range("a", "z") + .range("A", "Z") + .char("-") + .end() + .end() + .end() + .end() + .end_of_input() ) """Callable :class:`Pattern` for SemVer 2.0.0 versions.""" diff --git a/edify/library/software/version.py b/edify/library/software/version.py index 59ac6be..0f34438 100644 --- a/edify/library/software/version.py +++ b/edify/library/software/version.py @@ -1,8 +1,34 @@ -"""``version`` — permissive version-string shape.""" +"""``version`` — permissive dotted version string shape.""" from __future__ import annotations -from edify.library._support.regex import RegexBackedPattern +from edify import Pattern -version = RegexBackedPattern(r"^v?\d+(?:\.\d+){0,3}(?:[-.+][a-zA-Z0-9.\-]+)?$") +version = ( + Pattern() + .start_of_input() + .optional() + .char("v") + .one_or_more() + .digit() + .between(0, 3) + .group() + .char(".") + .one_or_more() + .digit() + .end() + .optional() + .group() + .any_of_chars("-.+") + .one_or_more() + .any_of() + .range("a", "z") + .range("A", "Z") + .range("0", "9") + .char(".") + .char("-") + .end() + .end() + .end_of_input() +) """Callable :class:`Pattern` for a permissive dotted version string.""" |
