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  }