digest.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package model
  2. import (
  3. "log/slog"
  4. "strings"
  5. "unicode"
  6. )
  7. // Digest represents a digest of a model Manifest. It is a comparable value
  8. // type and is immutable.
  9. //
  10. // The zero Digest is not a valid digest.
  11. type Digest struct {
  12. s string
  13. }
  14. // Type returns the digest type of the digest.
  15. //
  16. // Example:
  17. //
  18. // ParseDigest("sha256-1234").Type() // returns "sha256"
  19. func (d Digest) Type() string {
  20. typ, _, _ := strings.Cut(d.s, "-")
  21. return typ
  22. }
  23. // String returns the digest in the form of "<digest-type>-<digest>", or the
  24. // empty string if the digest is invalid.
  25. func (d Digest) String() string { return d.s }
  26. // IsValid returns true if the digest is valid (not zero).
  27. //
  28. // A valid digest may be created only by ParseDigest, or
  29. // ParseName(name).Digest().
  30. func (d Digest) IsValid() bool { return d.s != "" }
  31. // LogValue implements slog.Value.
  32. func (d Digest) LogValue() slog.Value {
  33. return slog.StringValue(d.String())
  34. }
  35. var (
  36. _ slog.LogValuer = Digest{}
  37. )
  38. // ParseDigest parses a string in the form of "<digest-type>-<digest>" into a
  39. // Digest.
  40. func ParseDigest(s string) Digest {
  41. typ, digest, ok := strings.Cut(s, "-")
  42. if ok && isValidDigestType(typ) && isValidHex(digest) {
  43. return Digest{s: s}
  44. }
  45. return Digest{}
  46. }
  47. func isValidDigestType(s string) bool {
  48. if len(s) == 0 {
  49. return false
  50. }
  51. for _, r := range s {
  52. if !unicode.IsLower(r) && !unicode.IsDigit(r) {
  53. return false
  54. }
  55. }
  56. return true
  57. }
  58. func isValidHex(s string) bool {
  59. if len(s) == 0 {
  60. return false
  61. }
  62. for i := range s {
  63. c := s[i]
  64. if c < '0' || c > '9' && c < 'a' || c > 'f' {
  65. return false
  66. }
  67. }
  68. return true
  69. }