gitea源码

parser.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package cargo
  4. import (
  5. "encoding/binary"
  6. "errors"
  7. "io"
  8. "regexp"
  9. "code.gitea.io/gitea/modules/json"
  10. "code.gitea.io/gitea/modules/validation"
  11. "github.com/hashicorp/go-version"
  12. )
  13. const PropertyYanked = "cargo.yanked"
  14. var (
  15. ErrInvalidName = errors.New("package name is invalid")
  16. ErrInvalidVersion = errors.New("package version is invalid")
  17. )
  18. // Package represents a Cargo package
  19. type Package struct {
  20. Name string
  21. Version string
  22. Metadata *Metadata
  23. Content io.Reader
  24. ContentSize int64
  25. }
  26. // Metadata represents the metadata of a Cargo package
  27. type Metadata struct {
  28. Dependencies []*Dependency `json:"dependencies,omitempty"`
  29. Features map[string][]string `json:"features,omitempty"`
  30. Authors []string `json:"authors,omitempty"`
  31. Description string `json:"description,omitempty"`
  32. DocumentationURL string `json:"documentation_url,omitempty"`
  33. ProjectURL string `json:"project_url,omitempty"`
  34. Readme string `json:"readme,omitempty"`
  35. Keywords []string `json:"keywords,omitempty"`
  36. Categories []string `json:"categories,omitempty"`
  37. License string `json:"license,omitempty"`
  38. RepositoryURL string `json:"repository_url,omitempty"`
  39. Links string `json:"links,omitempty"`
  40. }
  41. type Dependency struct {
  42. Name string `json:"name"`
  43. Req string `json:"req"`
  44. Features []string `json:"features"`
  45. Optional bool `json:"optional"`
  46. DefaultFeatures bool `json:"default_features"`
  47. Target *string `json:"target"`
  48. Kind string `json:"kind"`
  49. Registry *string `json:"registry"`
  50. Package *string `json:"package"`
  51. }
  52. var nameMatch = regexp.MustCompile(`\A[a-zA-Z][a-zA-Z0-9-_]{0,63}\z`)
  53. // ParsePackage reads the metadata and content of a package
  54. func ParsePackage(r io.Reader) (*Package, error) {
  55. var size uint32
  56. if err := binary.Read(r, binary.LittleEndian, &size); err != nil {
  57. return nil, err
  58. }
  59. p, err := parsePackage(io.LimitReader(r, int64(size)))
  60. if err != nil {
  61. return nil, err
  62. }
  63. if err := binary.Read(r, binary.LittleEndian, &size); err != nil {
  64. return nil, err
  65. }
  66. p.Content = io.LimitReader(r, int64(size))
  67. p.ContentSize = int64(size)
  68. return p, nil
  69. }
  70. func parsePackage(r io.Reader) (*Package, error) {
  71. var meta struct {
  72. Name string `json:"name"`
  73. Vers string `json:"vers"`
  74. Deps []struct {
  75. Name string `json:"name"`
  76. VersionReq string `json:"version_req"`
  77. Features []string `json:"features"`
  78. Optional bool `json:"optional"`
  79. DefaultFeatures bool `json:"default_features"`
  80. Target *string `json:"target"`
  81. Kind string `json:"kind"`
  82. Registry *string `json:"registry"`
  83. ExplicitNameInToml string `json:"explicit_name_in_toml"`
  84. } `json:"deps"`
  85. Features map[string][]string `json:"features"`
  86. Authors []string `json:"authors"`
  87. Description string `json:"description"`
  88. Documentation string `json:"documentation"`
  89. Homepage string `json:"homepage"`
  90. Readme string `json:"readme"`
  91. ReadmeFile string `json:"readme_file"`
  92. Keywords []string `json:"keywords"`
  93. Categories []string `json:"categories"`
  94. License string `json:"license"`
  95. LicenseFile string `json:"license_file"`
  96. Repository string `json:"repository"`
  97. Links string `json:"links"`
  98. }
  99. if err := json.NewDecoder(r).Decode(&meta); err != nil {
  100. return nil, err
  101. }
  102. if !nameMatch.MatchString(meta.Name) {
  103. return nil, ErrInvalidName
  104. }
  105. if _, err := version.NewSemver(meta.Vers); err != nil {
  106. return nil, ErrInvalidVersion
  107. }
  108. if !validation.IsValidURL(meta.Homepage) {
  109. meta.Homepage = ""
  110. }
  111. if !validation.IsValidURL(meta.Documentation) {
  112. meta.Documentation = ""
  113. }
  114. if !validation.IsValidURL(meta.Repository) {
  115. meta.Repository = ""
  116. }
  117. dependencies := make([]*Dependency, 0, len(meta.Deps))
  118. for _, dep := range meta.Deps {
  119. // https://doc.rust-lang.org/cargo/reference/registry-web-api.html#publish
  120. // It is a string of the new package name if the dependency is renamed, otherwise empty
  121. name := dep.ExplicitNameInToml
  122. pkg := &dep.Name
  123. if name == "" {
  124. name = dep.Name
  125. pkg = nil
  126. }
  127. dependencies = append(dependencies, &Dependency{
  128. Name: name,
  129. Req: dep.VersionReq,
  130. Features: dep.Features,
  131. Optional: dep.Optional,
  132. DefaultFeatures: dep.DefaultFeatures,
  133. Target: dep.Target,
  134. Kind: dep.Kind,
  135. Registry: dep.Registry,
  136. Package: pkg,
  137. })
  138. }
  139. return &Package{
  140. Name: meta.Name,
  141. Version: meta.Vers,
  142. Metadata: &Metadata{
  143. Dependencies: dependencies,
  144. Features: meta.Features,
  145. Authors: meta.Authors,
  146. Description: meta.Description,
  147. DocumentationURL: meta.Documentation,
  148. ProjectURL: meta.Homepage,
  149. Readme: meta.Readme,
  150. Keywords: meta.Keywords,
  151. Categories: meta.Categories,
  152. License: meta.License,
  153. RepositoryURL: meta.Repository,
  154. Links: meta.Links,
  155. },
  156. }, nil
  157. }