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