Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
106 lines
2.4 KiB
Go
106 lines
2.4 KiB
Go
// Package protoanalysis provides a toolset for analyzing proto files and packages.
|
|
package protoanalysis
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/ignite/cli/v29/ignite/pkg/errors"
|
|
)
|
|
|
|
// ErrImportNotFound returned when proto file import cannot be found.
|
|
var ErrImportNotFound = errors.New("proto import not found")
|
|
|
|
const (
|
|
protoFilePattern = "*.proto"
|
|
internalPath = "/internal"
|
|
)
|
|
|
|
// Parse parses proto packages by finding them with given glob pattern.
|
|
func Parse(ctx context.Context, cache *Cache, path string) (Packages, error) {
|
|
if cache != nil {
|
|
if packages, ok := cache.Get(path); ok {
|
|
return packages, nil
|
|
}
|
|
}
|
|
|
|
parsed, err := parse(ctx, path, protoFilePattern)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var packages Packages
|
|
|
|
for _, pp := range parsed {
|
|
if strings.Contains(pp.dir, internalPath) { // skip internal protos (mainly testing protos, etc.)
|
|
continue
|
|
}
|
|
|
|
packages = append(packages, build(*pp))
|
|
}
|
|
|
|
if cache != nil {
|
|
cache.Add(path, packages)
|
|
}
|
|
|
|
return packages, nil
|
|
}
|
|
|
|
// ParseFile parses a proto file at path.
|
|
func ParseFile(path string) (File, error) {
|
|
packages, err := Parse(context.Background(), nil, path)
|
|
if err != nil {
|
|
return File{}, err
|
|
}
|
|
files := packages.Files()
|
|
if len(files) != 1 {
|
|
return File{}, errors.New("path does not point to single file or it cannot be found")
|
|
}
|
|
return files[0], nil
|
|
}
|
|
|
|
// HasMessages checks if the proto package under path contains messages with given names.
|
|
func HasMessages(ctx context.Context, path string, names ...string) error {
|
|
pkgs, err := Parse(ctx, NewCache(), path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hasName := func(name string) error {
|
|
for _, pkg := range pkgs {
|
|
for _, msg := range pkg.Messages {
|
|
if msg.Name == name {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
return errors.Errorf("invalid proto message name %s", name)
|
|
}
|
|
|
|
for _, name := range names {
|
|
if err := hasName(name); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsImported checks if the proto package under path imports list of dependencies.
|
|
func IsImported(path string, dependencies ...string) error {
|
|
f, err := ParseFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, wantDep := range dependencies {
|
|
found := slices.Contains(f.Dependencies, wantDep)
|
|
if !found {
|
|
return errors.Wrap(ErrImportNotFound, fmt.Sprintf(
|
|
"invalid proto dependency %s for file %s", wantDep, path),
|
|
)
|
|
}
|
|
}
|
|
return nil
|
|
}
|