mukan-ignite/ignite/pkg/protoanalysis/protoanalysis.go
Mukan Erkin Törük c32551b6f7
Some checks failed
Docs Deploy / build_and_deploy (push) Has been cancelled
Generate Docs / cli (push) Has been cancelled
Generate Config Doc / cli (push) Has been cancelled
Go formatting / go-formatting (push) Has been cancelled
Check links / markdown-link-check (push) Has been cancelled
Integration / pre-test (push) Has been cancelled
Integration / test on (push) Has been cancelled
Integration / status (push) Has been cancelled
Lint / Lint Go code (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:24 +03:00

106 lines
2.4 KiB
Go

// Package protoanalysis provides a toolset for analyzing proto files and packages.
package protoanalysis
import (
"context"
"fmt"
"slices"
"strings"
"git.cw.tr/mukan-network/mukan-ignite/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
}