pax_global_header00006660000000000000000000000064151334212050014506gustar00rootroot0000000000000052 comment=7aec188df5c36f22160dbb3871756754e4cd05e3 aip-go-0.80.0/000077500000000000000000000000001513342120500127475ustar00rootroot00000000000000aip-go-0.80.0/.backstage/000077500000000000000000000000001513342120500147515ustar00rootroot00000000000000aip-go-0.80.0/.backstage/aip-go.yaml000066400000000000000000000005371513342120500170160ustar00rootroot00000000000000apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: aip-go description: Go SDK for implementing gRPC APIs according to the AIP standard. links: - url: https://pkg.go.dev/go.einride.tech/aip title: GoDoc icon: docs spec: type: go-library lifecycle: production owner: platform-engineering system: backend-sdk aip-go-0.80.0/.github/000077500000000000000000000000001513342120500143075ustar00rootroot00000000000000aip-go-0.80.0/.github/CODEOWNERS000066400000000000000000000000411513342120500156750ustar00rootroot00000000000000 * @einride/platform-engineering aip-go-0.80.0/.github/dependabot.yml000066400000000000000000000004211513342120500171340ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: monthly - package-ecosystem: gomod directories: [".", ".sage"] schedule: interval: monthly groups: go-dependencies: patterns: ["*"] aip-go-0.80.0/.github/workflows/000077500000000000000000000000001513342120500163445ustar00rootroot00000000000000aip-go-0.80.0/.github/workflows/go.yml000066400000000000000000000007211513342120500174740ustar00rootroot00000000000000name: Go on: pull_request: types: [opened, reopened, synchronize] jobs: build: runs-on: ubuntu-latest steps: - name: Setup Sage uses: einride/sage/actions/setup@master with: go-version-file: go.mod - name: Make run: make - name: Dry-run semantic-release run: make semantic-release repo=${{ github.repository }} dry=true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} aip-go-0.80.0/.github/workflows/release.yml000066400000000000000000000007271513342120500205150ustar00rootroot00000000000000name: Release on: push: branches: [master] permissions: write-all jobs: release: runs-on: ubuntu-latest steps: - name: Setup Sage uses: einride/sage/actions/setup@master with: go-version-file: .sage/go.mod - name: Make run: make - name: Run semantic-release run: make semantic-release repo=${{ github.repository }} dry=false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} aip-go-0.80.0/.github/workflows/stale.yml000066400000000000000000000012521513342120500201770ustar00rootroot00000000000000name: Stale permissions: issues: write pull-requests: write on: workflow_dispatch: schedule: # At 08:00 on every Monday. - cron: "0 8 * * 1" jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v10 with: days-before-pr-stale: 180 days-before-pr-close: 14 stale-pr-message: "This PR has been open for **180 days** with no activity. Remove the stale label or add a comment or it will be closed in **14 days**." days-before-issue-stale: 365 days-before-issue-close: -1 stale-issue-message: "This issue has been open for **365 days** with no activity. Marking as stale." aip-go-0.80.0/.gitignore000066400000000000000000000001001513342120500147260ustar00rootroot00000000000000.idea .vscode # Go workspace files go.work go.work.sum .semrel aip-go-0.80.0/.sage/000077500000000000000000000000001513342120500137445ustar00rootroot00000000000000aip-go-0.80.0/.sage/go.mod000066400000000000000000000001201513342120500150430ustar00rootroot00000000000000module sage go 1.22 toolchain go1.24.1 require go.einride.tech/sage v0.370.0 aip-go-0.80.0/.sage/go.sum000066400000000000000000000002431513342120500150760ustar00rootroot00000000000000go.einride.tech/sage v0.370.0 h1:BmQbf/Pp2GTwCr6MnRHZFADw1vokSfQ6Je21MJP0FKM= go.einride.tech/sage v0.370.0/go.mod h1:sy9YuK//XVwEZ2wD3f19xVSKEtN8CYtgtBZGpzC3p80= aip-go-0.80.0/.sage/main.go000066400000000000000000000055011513342120500152200ustar00rootroot00000000000000package main import ( "context" "go.einride.tech/sage/sg" "go.einride.tech/sage/sgtool" "go.einride.tech/sage/tools/sgconvco" "go.einride.tech/sage/tools/sggit" "go.einride.tech/sage/tools/sggo" "go.einride.tech/sage/tools/sggolangcilint" "go.einride.tech/sage/tools/sggolines" "go.einride.tech/sage/tools/sggosemanticrelease" "go.einride.tech/sage/tools/sgmdformat" "go.einride.tech/sage/tools/sgyamlfmt" ) func main() { sg.GenerateMakefiles( sg.Makefile{ Path: sg.FromGitRoot("Makefile"), DefaultTarget: All, }, sg.Makefile{ Path: sg.FromGitRoot("proto/Makefile"), DefaultTarget: Proto.All, Namespace: Proto{}, }, ) } func All(ctx context.Context) error { sg.Deps(ctx, ConvcoCheck, FormatMarkdown, FormatYAML, GoGenerate, Proto.All) sg.Deps(ctx, GoLint, GoTest) sg.SerialDeps(ctx, GoModTidy, GitVerifyNoDiff) return nil } func ConvcoCheck(ctx context.Context) error { sg.Logger(ctx).Println("checking git commits...") return sgconvco.Command(ctx, "check", "origin/master..HEAD").Run() } func FormatYAML(ctx context.Context) error { sg.Logger(ctx).Println("formatting YAML files...") return sgyamlfmt.Run(ctx) } func FormatMarkdown(ctx context.Context) error { sg.Logger(ctx).Println("formatting Markdown files...") return sgmdformat.Command(ctx).Run() } func GoLint(ctx context.Context) error { sg.Logger(ctx).Println("linting Go files...") return sggolangcilint.Run(ctx) } func GoLintFix(ctx context.Context) error { sg.Logger(ctx).Println("fixing Go files...") return sggolangcilint.Fix(ctx) } func GoLines(ctx context.Context) error { sg.Logger(ctx).Println("wrapping Go lines...") return sggolines.Run(ctx) } func GoModTidy(ctx context.Context) error { sg.Logger(ctx).Println("tidying Go module files...") return sg.Command(ctx, "go", "mod", "tidy", "-v").Run() } func GoTest(ctx context.Context) error { sg.Logger(ctx).Println("running Go tests...") return sggo.TestCommand(ctx).Run() } func GitVerifyNoDiff(ctx context.Context) error { sg.Logger(ctx).Println("verifying that git has no diff...") return sggit.VerifyNoDiff(ctx) } func Stringer(ctx context.Context) error { sg.Logger(ctx).Println("building...") _, err := sgtool.GoInstall(ctx, "golang.org/x/tools/cmd/stringer", "v0.31.0") return err } func GoGenerate(ctx context.Context) error { sg.Deps(ctx, Stringer) sg.Logger(ctx).Println("generating Go code...") return sg.Command(ctx, "go", "generate", "./...").Run() } func SemanticRelease(ctx context.Context, repo string, dry bool) error { sg.Logger(ctx).Println("triggering release...") args := []string{ "--allow-initial-development-versions", "--allow-no-changes", "--ci-condition=default", "--provider=github", "--provider-opt=slug=" + repo, } if dry { args = append(args, "--dry") } return sggosemanticrelease.Command(ctx, args...).Run() } aip-go-0.80.0/.sage/proto.go000066400000000000000000000042401513342120500154360ustar00rootroot00000000000000package main import ( "context" "go.einride.tech/sage/sg" "go.einride.tech/sage/sgtool" "go.einride.tech/sage/tools/sgapilinter" "go.einride.tech/sage/tools/sgbuf" ) type Proto sg.Namespace func (Proto) All(ctx context.Context) error { sg.Deps(ctx, Proto.BufFormat, Proto.BufLint) sg.Deps(ctx, Proto.BufGenerate) sg.Deps(ctx, Proto.BufGenerateTestdata) return nil } func (Proto) BufLint(ctx context.Context) error { sg.Logger(ctx).Println("linting proto files...") cmd := sgbuf.Command(ctx, "lint") cmd.Dir = sg.FromGitRoot("proto") return cmd.Run() } func (Proto) BufFormat(ctx context.Context) error { sg.Logger(ctx).Println("formatting proto files...") cmd := sgbuf.Command(ctx, "format", "--write") cmd.Dir = sg.FromGitRoot("proto") return cmd.Run() } func (Proto) ProtocGenGo(ctx context.Context) error { sg.Logger(ctx).Println("installing...") _, err := sgtool.GoInstallWithModfile(ctx, "google.golang.org/protobuf/cmd/protoc-gen-go", sg.FromGitRoot("go.mod")) return err } func (Proto) ProtocGenGoGRPC(ctx context.Context) error { sg.Logger(ctx).Println("installing...") _, err := sgtool.GoInstall(ctx, "google.golang.org/grpc/cmd/protoc-gen-go-grpc", "v1.2.0") return err } func (Proto) ProtocGenGoAIP(ctx context.Context) error { sg.Logger(ctx).Println("building binary...") return sg.Command(ctx, "go", "build", "-o", sg.FromBinDir("protoc-gen-go-aip"), "./cmd/protoc-gen-go-aip").Run() } func (Proto) BufGenerate(ctx context.Context) error { sg.Deps(ctx, Proto.ProtocGenGo, Proto.ProtocGenGoGRPC, Proto.ProtocGenGoAIP) sg.Logger(ctx).Println("generating proto stubs...") cmd := sgbuf.Command(ctx, "generate", "--template", "buf.gen.yaml", "--path", "einride") cmd.Dir = sg.FromGitRoot("proto") return cmd.Run() } func (Proto) APILinterLint(ctx context.Context) error { sg.Logger(ctx).Println("linting gRPC APIs...") return sgapilinter.Run(ctx) } func (Proto) BufGenerateTestdata(ctx context.Context) error { sg.Deps(ctx, Proto.ProtocGenGoAIP) sg.Logger(ctx).Println("generating testdata stubs...") cmd := sgbuf.Command(ctx, "generate", "--path", "test") cmd.Dir = sg.FromGitRoot("cmd/protoc-gen-go-aip/internal/genaip/testdata") return cmd.Run() } aip-go-0.80.0/CODE_OF_CONDUCT.md000066400000000000000000000121241513342120500155460ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at open-source@einride.tech. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. aip-go-0.80.0/LICENSE000066400000000000000000000020321513342120500137510ustar00rootroot00000000000000Copyright 2020 Einride AB Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. aip-go-0.80.0/Makefile000066400000000000000000000043341513342120500144130ustar00rootroot00000000000000# Code generated by go.einride.tech/sage. DO NOT EDIT. # To learn more, see .sage/main.go and https://github.com/einride/sage. .DEFAULT_GOAL := all cwd := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) sagefile := $(abspath $(cwd)/.sage/bin/sagefile) # Setup Go. go := $(shell command -v go 2>/dev/null) export GOWORK ?= off ifndef go SAGE_GO_VERSION ?= 1.23.4 export GOROOT := $(abspath $(cwd)/.sage/tools/go/$(SAGE_GO_VERSION)/go) export PATH := $(PATH):$(GOROOT)/bin go := $(GOROOT)/bin/go os := $(shell uname | tr '[:upper:]' '[:lower:]') arch := $(shell uname -m) ifeq ($(arch),x86_64) arch := amd64 endif $(go): $(info installing Go $(SAGE_GO_VERSION)...) @mkdir -p $(dir $(GOROOT)) @curl -sSL https://go.dev/dl/go$(SAGE_GO_VERSION).$(os)-$(arch).tar.gz | tar xz -C $(dir $(GOROOT)) @touch $(GOROOT)/go.mod @chmod +x $(go) endif .PHONY: $(sagefile) $(sagefile): $(go) @cd .sage && $(go) mod tidy && $(go) run . .PHONY: sage sage: @$(MAKE) $(sagefile) .PHONY: update-sage update-sage: $(go) @cd .sage && $(go) get go.einride.tech/sage@latest && $(go) mod tidy && $(go) run . .PHONY: clean-sage clean-sage: @git clean -fdx .sage/tools .sage/bin .sage/build .PHONY: all all: $(sagefile) @$(sagefile) All .PHONY: convco-check convco-check: $(sagefile) @$(sagefile) ConvcoCheck .PHONY: format-markdown format-markdown: $(sagefile) @$(sagefile) FormatMarkdown .PHONY: format-yaml format-yaml: $(sagefile) @$(sagefile) FormatYAML .PHONY: git-verify-no-diff git-verify-no-diff: $(sagefile) @$(sagefile) GitVerifyNoDiff .PHONY: go-generate go-generate: $(sagefile) @$(sagefile) GoGenerate .PHONY: go-lines go-lines: $(sagefile) @$(sagefile) GoLines .PHONY: go-lint go-lint: $(sagefile) @$(sagefile) GoLint .PHONY: go-lint-fix go-lint-fix: $(sagefile) @$(sagefile) GoLintFix .PHONY: go-mod-tidy go-mod-tidy: $(sagefile) @$(sagefile) GoModTidy .PHONY: go-test go-test: $(sagefile) @$(sagefile) GoTest .PHONY: semantic-release semantic-release: $(sagefile) ifndef repo $(error missing argument repo="...") endif ifndef dry $(error missing argument dry="...") endif @$(sagefile) SemanticRelease "$(repo)" "$(dry)" .PHONY: stringer stringer: $(sagefile) @$(sagefile) Stringer .PHONY: proto proto: $(MAKE) -C proto -f Makefile aip-go-0.80.0/README.md000066400000000000000000000044601513342120500142320ustar00rootroot00000000000000[![Go Reference](https://pkg.go.dev/badge/go.einride.tech/aip.svg)](https://pkg.go.dev/go.einride.tech/aip) # AIP Go Go SDK for implementing [Google API Improvement Proposals](https://aip.dev/) (AIP). ## Generate AIP support code from proto ```bash go install go.einride.tech/aip/cmd/protoc-gen-go-aip ``` Add to `buf.gen.yaml`: ```yaml version: v2 plugins: - local: protoc-gen-go-aip out: gen opt: - paths=source_relative ``` Run `buf build` to generate e.g. `your_service_aip.go`. ## Library usage examples ```bash go get -u go.einride.tech/aip ``` ### [AIP-132](https://google.aip.dev/132) (Standard method: List) - Use [`pagination.PageToken`](./pagination/pagetoken.go) to implement offset-based pagination. ```go package examplelibrary import ( "context" "go.einride.tech/aip/pagination" "google.golang.org/genproto/googleapis/example/library/v1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func (s *Server) ListShelves( ctx context.Context, request *library.ListShelvesRequest, ) (*library.ListShelvesResponse, error) { // Handle request constraints. const ( maxPageSize = 1000 defaultPageSize = 100 ) switch { case request.PageSize < 0: return nil, status.Errorf(codes.InvalidArgument, "page size is negative") case request.PageSize == 0: request.PageSize = defaultPageSize case request.PageSize > maxPageSize: request.PageSize = maxPageSize } // Use pagination.PageToken for offset-based page tokens. pageToken, err := pagination.ParsePageToken(request) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid page token") } // Query the storage. result, err := s.Storage.ListShelves(ctx, &ListShelvesQuery{ Offset: pageToken.Offset, PageSize: request.GetPageSize(), }) if err != nil { return nil, err } // Build the response. response := &library.ListShelvesResponse{ Shelves: result.Shelves, } // Set the next page token. if result.HasNextPage { response.NextPageToken = pageToken.Next(request).String() } // Respond. return response, nil } ``` aip-go-0.80.0/SECURITY.md000066400000000000000000000032211513342120500145360ustar00rootroot00000000000000# Security Policy Einride welcomes feedback from security researchers and the general public to help improve our security. If you believe you have discovered a vulnerability, privacy issue, exposed data, or other security issues in relation to this project, we want to hear from you. This policy outlines steps for reporting security issues to us, what we expect, and what you can expect from us. ## Supported versions We release patches for security issues according to semantic versioning. This project is currently unstable (v0.x) and only the latest version will receive security patches. ## Reporting a vulnerability Please do not report security vulnerabilities through public issues, discussions, or change requests. Please report security issues via [oss-security@einride.tech][email]. Provide all relevant information, including steps to reproduce the issue, any affected versions, and known mitigations. The more details you provide, the easier it will be for us to triage and fix the issue. You will receive a response from us within 2 business days. If the issue is confirmed, a patch will be released as soon as possible. For more information, or security issues not relating to open source code, please consult our [Vulnerability Disclosure Policy][vdp]. ## Preferred languages English is our preferred language of communication. ## Contributions and recognition We appreciate every contribution and will do our best to publicly [acknowledge][acknowledgments] your contributions. [acknowledgments]: https://einride.tech/security-acknowledgments.txt [email]: mailto:oss-security@einride.tech [vdp]: https://www.einride.tech/vulnerability-disclosure-policy aip-go-0.80.0/cmd/000077500000000000000000000000001513342120500135125ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/000077500000000000000000000000001513342120500167415ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/000077500000000000000000000000001513342120500205555ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/000077500000000000000000000000001513342120500220205ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/doc.go000066400000000000000000000001461513342120500231150ustar00rootroot00000000000000// Package genaip provides a protobuf compiler plugin for resource-oriented gRPC APIs. package genaip aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/plugin.go000066400000000000000000000052661513342120500236560ustar00rootroot00000000000000package genaip import ( "fmt" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" ) // PluginName is the name of the AIP Go protobuf compiler plugin. const PluginName = "protoc-gen-go-aip" const generatedFilenameSuffix = "_aip.go" type Config struct { IncludeResourceDefinitions bool } // Run the AIP Go protobuf compiler plugin. func Run(gen *protogen.Plugin, config Config) error { var files protoregistry.Files for _, file := range gen.Files { if err := files.RegisterFile(file.Desc); err != nil { return err } } for _, file := range gen.Files { if !file.Generate { continue } g := newGeneratedFile(gen, file) g.Skip() var rangeErr error rangeResourcesInFile( file.Desc, func(resource *annotations.ResourceDescriptor, extension protoreflect.ExtensionType) bool { if !config.IncludeResourceDefinitions && extension == annotations.E_ResourceDefinition { return true } g.Unskip() if err := (resourceNameCodeGenerator{ resource: resource, files: &files, file: file, }).GenerateCode(g); err != nil { rangeErr = err return false } return true }, ) if rangeErr != nil { return rangeErr } } return nil } func newGeneratedFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { g := gen.NewGeneratedFile(file.GeneratedFilenamePrefix+generatedFilenameSuffix, file.GoImportPath) g.P("// Code generated by ", PluginName, ". DO NOT EDIT.") g.P("//") g.P("// versions:") g.P("// \t", PluginName, " ", PluginVersion) g.P("// \tprotoc ", getProtocVersion(gen)) g.P("// source: ", file.Desc.Path()) g.P() g.P("package ", file.GoPackageName) return g } func getProtocVersion(gen *protogen.Plugin) string { if v := gen.Request.GetCompilerVersion(); v != nil { return fmt.Sprintf("v%v.%v.%v", v.GetMajor(), v.GetMinor(), v.GetPatch()) } return "(unknown)" } func rangeResourcesInFile( file protoreflect.FileDescriptor, fn func(resource *annotations.ResourceDescriptor, extension protoreflect.ExtensionType) bool, ) { for _, resource := range proto.GetExtension( file.Options(), annotations.E_ResourceDefinition, ).([]*annotations.ResourceDescriptor) { if !fn(resource, annotations.E_ResourceDefinition) { return } } for i := 0; i < file.Messages().Len(); i++ { resource := proto.GetExtension( file.Messages().Get(i).Options(), annotations.E_Resource, ).(*annotations.ResourceDescriptor) if resource == nil { continue } if !fn(resource, annotations.E_Resource) { return } } } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/resourcename.go000066400000000000000000000315461513342120500250500ustar00rootroot00000000000000package genaip import ( "strconv" "strings" "github.com/stoewer/go-strcase" "go.einride.tech/aip/reflect/aipreflect" "go.einride.tech/aip/resourcename" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/reflect/protoregistry" ) const ( fmtPackage = protogen.GoImportPath("fmt") resourcenamePackage = protogen.GoImportPath("go.einride.tech/aip/resourcename") stringsPackage = protogen.GoImportPath("strings") encodingPackage = protogen.GoImportPath("encoding") ) type resourceNameCodeGenerator struct { resource *annotations.ResourceDescriptor file *protogen.File files *protoregistry.Files } func (r resourceNameCodeGenerator) GenerateCode(g *protogen.GeneratedFile) error { if len(r.resource.GetPattern()) == 0 { return nil } hasMultiPattern := len(r.resource.GetPattern()) > 1 hasFutureMultiPattern := r.resource.GetHistory() == annotations.ResourceDescriptor_FUTURE_MULTI_PATTERN // Generate multi-pattern interface and parse methods if we have multiple patterns now or in the future. if hasMultiPattern || hasFutureMultiPattern { if err := r.generateMultiPatternInterface(g); err != nil { return err } if err := r.generateMultiPatternParseMethod(g); err != nil { return err } } // Generate the single-pattern struct unless we explicitly only want multi-patterns from the start. firstPattern := r.resource.GetPattern()[0] shouldGenerateSinglePatternStruct := !hasFutureMultiPattern firstSinglePatternStructName := r.SinglePatternStructName() if shouldGenerateSinglePatternStruct { if err := r.generatePatternStruct( g, firstPattern, firstSinglePatternStructName, ); err != nil { return err } } // Generate the multi-pattern variant of the single-pattern struct if we need multi-pattern support. // If we've already generated single-pattern structs above, ignore top-level resources here as the // multi-pattern variant of the single-pattern struct will be identical to the single-pattern struct. firstMultiPatternStructName := r.MultiPatternStructName(firstPattern) equalMultiSinglePatternStructName := firstMultiPatternStructName == firstSinglePatternStructName avoidGeneratingSameSingleStruct := shouldGenerateSinglePatternStruct && equalMultiSinglePatternStructName if (hasMultiPattern || hasFutureMultiPattern) && !avoidGeneratingSameSingleStruct { if err := r.generatePatternStruct( g, firstPattern, firstMultiPatternStructName, ); err != nil { return err } } // Generate multi-pattern structs for all but the first pattern. for _, pattern := range r.resource.GetPattern()[1:] { if err := r.generatePatternStruct(g, pattern, r.MultiPatternStructName(pattern)); err != nil { return err } } return nil } func (r resourceNameCodeGenerator) generatePatternStruct( g *protogen.GeneratedFile, pattern string, typeName string, ) error { g.P() g.P("type ", typeName, " struct {") var sc resourcename.Scanner sc.Init(pattern) for sc.Scan() { if sc.Segment().IsVariable() { g.P(strcase.UpperCamelCase(string(sc.Segment().Literal())), " string") } } g.P("}") var parentConstructorsErr error aipreflect.RangeParentResourcesInPackage( r.files, r.file.Desc.Package(), pattern, func(parent *annotations.ResourceDescriptor) bool { if err := r.generateParentConstructorMethod(g, pattern, typeName, parent); err != nil { parentConstructorsErr = err return false } return true }, ) if parentConstructorsErr != nil { return parentConstructorsErr } if err := r.generateValidateMethod(g, pattern, typeName); err != nil { return err } if err := r.generateContainsWildcardMethod(g, pattern, typeName); err != nil { return err } if err := r.generateStringMethod(g, pattern, typeName); err != nil { return err } if err := r.generateMarshalStringMethod(g, typeName); err != nil { return err } if err := r.generateMarshalTextMethod(g, typeName); err != nil { return err } if err := r.generateUnmarshalStringMethod(g, pattern, typeName); err != nil { return err } if err := r.generateUnmarshalTextMethod(g, typeName); err != nil { return err } if err := r.generateTypeMethod(g, typeName); err != nil { return err } var parentErr error aipreflect.RangeParentResourcesInPackage( r.files, r.file.Desc.Package(), pattern, func(parent *annotations.ResourceDescriptor) bool { if err := r.generateParentMethod(g, pattern, typeName, parent); err != nil { parentErr = err return false } return true }, ) return parentErr } func (r *resourceNameCodeGenerator) generateParentConstructorMethod( g *protogen.GeneratedFile, pattern string, typeName string, parent *annotations.ResourceDescriptor, ) error { var parentPattern string for _, parentCandidate := range parent.GetPattern() { if resourcename.HasParent(pattern, parentCandidate) { parentPattern = parentCandidate break } } if parentPattern == "" { return nil } pg := resourceNameCodeGenerator{resource: parent, file: r.file, files: r.files} parentStruct := pg.StructName(parentPattern) g.P() g.P("func (n ", parentStruct, ") ", typeName, "(") var sc resourcename.Scanner sc.Init(strings.TrimPrefix(pattern, parentPattern)) for sc.Scan() { if sc.Segment().IsVariable() { g.P(strcase.LowerCamelCase(string(sc.Segment().Literal())), " string,") } } g.P(") ", typeName, " {") g.P("return ", typeName, "{") sc.Init(parentPattern) for sc.Scan() { if sc.Segment().IsVariable() { g.P( strcase.UpperCamelCase(string(sc.Segment().Literal())), ": ", "n.", strcase.UpperCamelCase(string(sc.Segment().Literal())), ",", ) } } sc.Init(strings.TrimPrefix(pattern, parentPattern)) for sc.Scan() { if sc.Segment().IsVariable() { g.P( strcase.UpperCamelCase(string(sc.Segment().Literal())), ": ", strcase.LowerCamelCase(string(sc.Segment().Literal())), ",", ) } } g.P("}") g.P("}") return nil } func (r *resourceNameCodeGenerator) generateParentMethod( g *protogen.GeneratedFile, pattern string, typeName string, parent *annotations.ResourceDescriptor, ) error { var parentPattern string for _, parentCandidate := range parent.GetPattern() { if resourcename.HasParent(pattern, parentCandidate) { parentPattern = parentCandidate break } } if parentPattern == "" { return nil } pg := resourceNameCodeGenerator{resource: parent, file: r.file, files: r.files} parentStruct := pg.StructName(parentPattern) g.P() g.P("func (n ", typeName, ") ", parentStruct, "() ", parentStruct, " {") g.P("return ", parentStruct, "{") var sc resourcename.Scanner sc.Init(parentPattern) for sc.Scan() { if sc.Segment().IsVariable() { fieldName := strcase.UpperCamelCase(string(sc.Segment().Literal())) g.P(fieldName, ": n.", fieldName, ",") } } g.P("}") g.P("}") return nil } func (r resourceNameCodeGenerator) generateValidateMethod( g *protogen.GeneratedFile, pattern string, typeName string, ) error { stringsIndexByte := stringsPackage.Ident("IndexByte") fmtErrorf := fmtPackage.Ident("Errorf") g.P() g.P("func (n ", typeName, ") Validate() error {") var sc resourcename.Scanner sc.Init(pattern) for sc.Scan() { if !sc.Segment().IsVariable() { continue } value := string(sc.Segment().Literal()) g.P("if n.", strcase.UpperCamelCase(value), " == ", strconv.Quote(""), "{") g.P("return ", fmtErrorf, `("`, value, `: empty")`) g.P("}") g.P("if ", stringsIndexByte, "(n.", strcase.UpperCamelCase(value), ", '/') != - 1 {") g.P("return ", fmtErrorf, `("`, value, `: contains illegal character '/'")`) g.P("}") } g.P("return nil") g.P("}") return nil } func (r resourceNameCodeGenerator) generateContainsWildcardMethod( g *protogen.GeneratedFile, pattern string, typeName string, ) error { g.P() g.P("func (n ", typeName, ") ContainsWildcard() bool {") var returnStatement strings.Builder returnStatement.WriteString("return false") var sc resourcename.Scanner sc.Init(pattern) for sc.Scan() { if !sc.Segment().IsVariable() { continue } returnStatement.WriteString("|| n.") returnStatement.WriteString(strcase.UpperCamelCase(string(sc.Segment().Literal()))) returnStatement.WriteString(" == ") returnStatement.WriteString(strconv.Quote("-")) } g.P(returnStatement.String()) g.P("}") return nil } func (r *resourceNameCodeGenerator) generateStringMethod( g *protogen.GeneratedFile, pattern string, typeName string, ) error { resourcenameSprint := resourcenamePackage.Ident("Sprint") g.P() g.P("func (n ", typeName, ") String() string {") g.P("return ", resourcenameSprint, "(") g.P(strconv.Quote(pattern), ",") var sc resourcename.Scanner sc.Init(pattern) for sc.Scan() { if sc.Segment().IsVariable() { g.P("n.", strcase.UpperCamelCase(string(sc.Segment().Literal())), ",") } } g.P(")") g.P("}") return nil } func (r resourceNameCodeGenerator) generateMarshalStringMethod( g *protogen.GeneratedFile, typeName string, ) error { g.P() g.P("func (n ", typeName, ") MarshalString() (string, error) {") g.P("if err := n.Validate(); err != nil {") g.P("return ", strconv.Quote(""), ", err") g.P("}") g.P("return n.String(), nil") g.P("}") return nil } func (r resourceNameCodeGenerator) generateUnmarshalStringMethod( g *protogen.GeneratedFile, pattern string, typeName string, ) error { resourcenameSscan := resourcenamePackage.Ident("Sscan") g.P() g.P("func (n *", typeName, ") UnmarshalString(name string) error {") g.P("err := ", resourcenameSscan, "(") g.P("name,") g.P(strconv.Quote(pattern), ",") var sc resourcename.Scanner sc.Init(pattern) for sc.Scan() { if sc.Segment().IsVariable() { g.P("&n.", strcase.UpperCamelCase(string(sc.Segment().Literal())), ",") } } g.P(")") g.P("if err != nil {") g.P("return err") g.P("}") g.P("return n.Validate()") g.P("}") return nil } func (r resourceNameCodeGenerator) generateMarshalTextMethod( g *protogen.GeneratedFile, typeName string, ) error { g.P() g.P("// MarshalText implements the encoding.TextMarshaler interface.") g.P("func (n ", typeName, ") MarshalText() ([]byte, error) {") g.P("if err := n.Validate(); err != nil {") g.P("return nil, err") g.P("}") g.P("return []byte(n.String()), nil") g.P("}") return nil } func (r resourceNameCodeGenerator) generateUnmarshalTextMethod( g *protogen.GeneratedFile, typeName string, ) error { g.P() g.P("// UnmarshalText implements the encoding.TextUnmarshaler interface.") g.P("func (n *", typeName, ") UnmarshalText(text []byte) error {") g.P("return n.UnmarshalString(string(text))") g.P("}") return nil } func (r resourceNameCodeGenerator) generateTypeMethod( g *protogen.GeneratedFile, typeName string, ) error { g.P() g.P("func (n ", typeName, ") Type() string {") g.P("return ", strconv.Quote(r.resource.GetType())) g.P("}") return nil } func (r resourceNameCodeGenerator) generateMultiPatternInterface(g *protogen.GeneratedFile) error { fmtStringer := fmtPackage.Ident("Stringer") textMarshaler := encodingPackage.Ident("TextMarshaler") g.P() g.P("type ", r.MultiPatternInterfaceName(), " interface {") g.P(fmtStringer) g.P(textMarshaler) g.P("MarshalString() (string, error)") g.P("ContainsWildcard() bool") g.P("}") return nil } func (r *resourceNameCodeGenerator) generateMultiPatternParseMethod(g *protogen.GeneratedFile) error { resourcenameMatch := resourcenamePackage.Ident("Match") fmtErrorf := fmtPackage.Ident("Errorf") g.P() g.P("func Parse", r.MultiPatternInterfaceName(), "(name string) (", r.MultiPatternInterfaceName(), ", error) {") g.P("switch {") for _, pattern := range r.resource.GetPattern() { g.P("case ", resourcenameMatch, "(", strconv.Quote(pattern), ", name):") g.P("var result ", r.MultiPatternStructName(pattern)) g.P("return &result, result.UnmarshalString(name)") } g.P("default:") g.P("return nil, ", fmtErrorf, `("no matching pattern")`) g.P("}") g.P("}") return nil } func (r *resourceNameCodeGenerator) SinglePatternStructName() string { return aipreflect.ResourceType(r.resource.GetType()).Type() + "ResourceName" } func (r *resourceNameCodeGenerator) StructName(pattern string) string { if r.resource.GetHistory() == annotations.ResourceDescriptor_FUTURE_MULTI_PATTERN || len(r.resource.GetPattern()) > 1 { return r.MultiPatternStructName(pattern) } if r.resource.GetPattern()[0] == pattern { return r.SinglePatternStructName() } return r.MultiPatternStructName(pattern) } func (r *resourceNameCodeGenerator) MultiPatternStructName(pattern string) string { var result strings.Builder var sc resourcename.Scanner sc.Init(pattern) for sc.Scan() { if !sc.Segment().IsVariable() && string(sc.Segment().Literal()) != r.resource.GetPlural() { _, _ = result.WriteString(strcase.UpperCamelCase(string(sc.Segment().Literal()))) } } _, _ = result.WriteString(r.SinglePatternStructName()) return result.String() } func (r *resourceNameCodeGenerator) MultiPatternInterfaceName() string { return aipreflect.ResourceType(r.resource.GetType()).Type() + "MultiPatternResourceName" } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/000077500000000000000000000000001513342120500236315ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/api-linter.yaml000066400000000000000000000000641513342120500265610ustar00rootroot00000000000000- included_paths: ["**"] disabled_rules: ["core"] aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/buf.gen.yaml000066400000000000000000000005551513342120500260460ustar00rootroot00000000000000version: v1 managed: enabled: true go_package_prefix: default: go.einride.tech/aip/cmd/protoc-gen-go-aip/internal/genaip/testdata except: - buf.build/googleapis/googleapis plugins: - name: go-aip out: . opt: module=go.einride.tech/aip/cmd/protoc-gen-go-aip/internal/genaip/testdata path: ../../../../../.sage/bin/protoc-gen-go-aip aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/buf.lock000066400000000000000000000004751513342120500252650ustar00rootroot00000000000000# Generated by buf. DO NOT EDIT. version: v1 deps: - remote: buf.build owner: googleapis repository: googleapis commit: cc916c31859748a68fd229a3c8d7a2e8 digest: shake256:469b049d0eb04203d5272062636c078decefc96fec69739159c25d85349c50c34c7706918a8b216c5c27f76939df48452148cff8c5c3ae77fa6ba5c25c1b8bf8 aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/buf.yaml000066400000000000000000000000671513342120500252740ustar00rootroot00000000000000version: v1 deps: - buf.build/googleapis/googleapis aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/000077500000000000000000000000001513342120500246105ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/multipattern/000077500000000000000000000000001513342120500273405ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/multipattern/pattern_test.go000066400000000000000000000151601513342120500324060ustar00rootroot00000000000000package multipattern import ( "fmt" "testing" "gotest.tools/v3/assert" ) func TestParseBookMultiPatternResourceName(t *testing.T) { goodPatterns := []string{ "shelves/shelf/books/book", "publishers/publisher/books/book", } for _, pattern := range goodPatterns { pattern := pattern t.Run(pattern, func(t *testing.T) { name, err := ParseBookMultiPatternResourceName(pattern) assert.NilError(t, err) assert.Equal(t, name.String(), pattern) }) } badPatterns := []string{ "books/book", "others/other", "others/other/books/book", } for _, pattern := range badPatterns { pattern := pattern t.Run(pattern, func(t *testing.T) { _, err := ParseBookMultiPatternResourceName(pattern) assert.Error(t, err, "no matching pattern") }) } } func TestShelvesBookResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( shelf = "shelf" book = "book" ) pattern := fmt.Sprintf("shelves/%s/books/%s", shelf, book) var name ShelvesBookResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Shelf, shelf) assert.Equal(t, name.Book, book) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name ShelvesBookResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name ShelvesBookResourceName err := name.UnmarshalString("others/other/books/book") assert.Error( t, err, "parse resource name 'others/other/books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got others", ) }) } func TestPublishersBookResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( publisher = "publisher" book = "book" ) pattern := fmt.Sprintf("publishers/%s/books/%s", publisher, book) var name PublishersBookResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Publisher, publisher) assert.Equal(t, name.Book, book) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name PublishersBookResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'publishers/{publisher}/books/{book}': segment publishers: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name PublishersBookResourceName err := name.UnmarshalString("others/other/books/book") assert.Error( t, err, "parse resource name 'others/other/books/book' with pattern 'publishers/{publisher}/books/{book}': segment publishers: got others", ) }) } func TestParseShelfMultiPatternResourceName(t *testing.T) { goodPatterns := []string{ "shelves/shelf", "libraries/library/shelves/shelf", "rooms/room/shelves/shelf", } for _, pattern := range goodPatterns { pattern := pattern t.Run(pattern, func(t *testing.T) { name, err := ParseShelfMultiPatternResourceName(pattern) assert.NilError(t, err) assert.Equal(t, name.String(), pattern) }) } badPatterns := []string{ "others/other", "others/other/shelves/shelf", } for _, pattern := range badPatterns { pattern := pattern t.Run(pattern, func(t *testing.T) { _, err := ParseShelfMultiPatternResourceName(pattern) assert.Error(t, err, "no matching pattern") }) } } func TestShelfResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const shelf = "shelf" pattern := fmt.Sprintf("shelves/%s", shelf) var name ShelfResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Shelf, shelf) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("invalid", func(t *testing.T) { var name ShelfResourceName err := name.UnmarshalString("others/other") assert.Error(t, err, "parse resource name 'others/other' with pattern 'shelves/{shelf}': segment shelves: got others") }) t.Run("bad wrong parent", func(t *testing.T) { var name ShelfResourceName err := name.UnmarshalString("others/other/shelves/shelf") assert.Error( t, err, "parse resource name 'others/other/shelves/shelf' with pattern 'shelves/{shelf}': segment shelves: got others", ) }) } func TestLibrariesShelfResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( library = "library" shelf = "shelf" ) pattern := fmt.Sprintf("libraries/%s/shelves/%s", library, shelf) var name LibrariesShelfResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Library, library) assert.Equal(t, name.Shelf, shelf) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name LibrariesShelfResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'libraries/{library}/shelves/{shelf}': segment libraries: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name LibrariesShelfResourceName err := name.UnmarshalString("others/other/shelves/shelf") assert.Error( t, err, "parse resource name 'others/other/shelves/shelf' with pattern 'libraries/{library}/shelves/{shelf}': segment libraries: got others", ) }) } func TestRoomsShelfResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( room = "room" shelf = "shelf" ) pattern := fmt.Sprintf("rooms/%s/shelves/%s", room, shelf) var name RoomsShelfResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Room, room) assert.Equal(t, name.Shelf, shelf) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name RoomsShelfResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'rooms/{room}/shelves/{shelf}': segment rooms: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name RoomsShelfResourceName err := name.UnmarshalString("others/other/shelves/shelf") assert.Error( t, err, "parse resource name 'others/other/shelves/shelf' with pattern 'rooms/{room}/shelves/{shelf}': segment rooms: got others", ) }) } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/multipattern/testdata.proto000066400000000000000000000014511513342120500322370ustar00rootroot00000000000000syntax = "proto3"; package test.multipattern; import "google/api/resource.proto"; message Book { option (google.api.resource) = { type: "test1.testdata/Book" singular: "book" plural: "books" pattern: "shelves/{shelf}/books/{book}" pattern: "publishers/{publisher}/books/{book}" history: FUTURE_MULTI_PATTERN }; // The resource name of the book. string name = 1; } // Shelf can be either top-level, within a library or within a room. message Shelf { option (google.api.resource) = { type: "test1.testdata/Shelf" singular: "shelf" plural: "shelves" pattern: "shelves/{shelf}" pattern: "libraries/{library}/shelves/{shelf}" pattern: "rooms/{room}/shelves/{shelf}" history: FUTURE_MULTI_PATTERN }; // The resource name. string name = 1; } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/multipattern/testdata_aip.go000066400000000000000000000227361513342120500323430ustar00rootroot00000000000000// Code generated by protoc-gen-go-aip. DO NOT EDIT. // // versions: // protoc-gen-go-aip development // protoc (unknown) // source: test/multipattern/testdata.proto package multipattern import ( encoding "encoding" fmt "fmt" resourcename "go.einride.tech/aip/resourcename" strings "strings" ) type BookMultiPatternResourceName interface { fmt.Stringer encoding.TextMarshaler MarshalString() (string, error) ContainsWildcard() bool } func ParseBookMultiPatternResourceName(name string) (BookMultiPatternResourceName, error) { switch { case resourcename.Match("shelves/{shelf}/books/{book}", name): var result ShelvesBookResourceName return &result, result.UnmarshalString(name) case resourcename.Match("publishers/{publisher}/books/{book}", name): var result PublishersBookResourceName return &result, result.UnmarshalString(name) default: return nil, fmt.Errorf("no matching pattern") } } type ShelvesBookResourceName struct { Shelf string Book string } func (n ShelfResourceName) ShelvesBookResourceName( book string, ) ShelvesBookResourceName { return ShelvesBookResourceName{ Shelf: n.Shelf, Book: book, } } func (n ShelvesBookResourceName) Validate() error { if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } if n.Book == "" { return fmt.Errorf("book: empty") } if strings.IndexByte(n.Book, '/') != -1 { return fmt.Errorf("book: contains illegal character '/'") } return nil } func (n ShelvesBookResourceName) ContainsWildcard() bool { return false || n.Shelf == "-" || n.Book == "-" } func (n ShelvesBookResourceName) String() string { return resourcename.Sprint( "shelves/{shelf}/books/{book}", n.Shelf, n.Book, ) } func (n ShelvesBookResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ShelvesBookResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ShelvesBookResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shelves/{shelf}/books/{book}", &n.Shelf, &n.Book, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ShelvesBookResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ShelvesBookResourceName) Type() string { return "test1.testdata/Book" } func (n ShelvesBookResourceName) ShelfResourceName() ShelfResourceName { return ShelfResourceName{ Shelf: n.Shelf, } } type PublishersBookResourceName struct { Publisher string Book string } func (n PublishersBookResourceName) Validate() error { if n.Publisher == "" { return fmt.Errorf("publisher: empty") } if strings.IndexByte(n.Publisher, '/') != -1 { return fmt.Errorf("publisher: contains illegal character '/'") } if n.Book == "" { return fmt.Errorf("book: empty") } if strings.IndexByte(n.Book, '/') != -1 { return fmt.Errorf("book: contains illegal character '/'") } return nil } func (n PublishersBookResourceName) ContainsWildcard() bool { return false || n.Publisher == "-" || n.Book == "-" } func (n PublishersBookResourceName) String() string { return resourcename.Sprint( "publishers/{publisher}/books/{book}", n.Publisher, n.Book, ) } func (n PublishersBookResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n PublishersBookResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *PublishersBookResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "publishers/{publisher}/books/{book}", &n.Publisher, &n.Book, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *PublishersBookResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n PublishersBookResourceName) Type() string { return "test1.testdata/Book" } type ShelfMultiPatternResourceName interface { fmt.Stringer encoding.TextMarshaler MarshalString() (string, error) ContainsWildcard() bool } func ParseShelfMultiPatternResourceName(name string) (ShelfMultiPatternResourceName, error) { switch { case resourcename.Match("shelves/{shelf}", name): var result ShelfResourceName return &result, result.UnmarshalString(name) case resourcename.Match("libraries/{library}/shelves/{shelf}", name): var result LibrariesShelfResourceName return &result, result.UnmarshalString(name) case resourcename.Match("rooms/{room}/shelves/{shelf}", name): var result RoomsShelfResourceName return &result, result.UnmarshalString(name) default: return nil, fmt.Errorf("no matching pattern") } } type ShelfResourceName struct { Shelf string } func (n ShelfResourceName) Validate() error { if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } return nil } func (n ShelfResourceName) ContainsWildcard() bool { return false || n.Shelf == "-" } func (n ShelfResourceName) String() string { return resourcename.Sprint( "shelves/{shelf}", n.Shelf, ) } func (n ShelfResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ShelfResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ShelfResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shelves/{shelf}", &n.Shelf, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ShelfResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ShelfResourceName) Type() string { return "test1.testdata/Shelf" } type LibrariesShelfResourceName struct { Library string Shelf string } func (n LibrariesShelfResourceName) Validate() error { if n.Library == "" { return fmt.Errorf("library: empty") } if strings.IndexByte(n.Library, '/') != -1 { return fmt.Errorf("library: contains illegal character '/'") } if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } return nil } func (n LibrariesShelfResourceName) ContainsWildcard() bool { return false || n.Library == "-" || n.Shelf == "-" } func (n LibrariesShelfResourceName) String() string { return resourcename.Sprint( "libraries/{library}/shelves/{shelf}", n.Library, n.Shelf, ) } func (n LibrariesShelfResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n LibrariesShelfResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *LibrariesShelfResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "libraries/{library}/shelves/{shelf}", &n.Library, &n.Shelf, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *LibrariesShelfResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n LibrariesShelfResourceName) Type() string { return "test1.testdata/Shelf" } type RoomsShelfResourceName struct { Room string Shelf string } func (n RoomsShelfResourceName) Validate() error { if n.Room == "" { return fmt.Errorf("room: empty") } if strings.IndexByte(n.Room, '/') != -1 { return fmt.Errorf("room: contains illegal character '/'") } if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } return nil } func (n RoomsShelfResourceName) ContainsWildcard() bool { return false || n.Room == "-" || n.Shelf == "-" } func (n RoomsShelfResourceName) String() string { return resourcename.Sprint( "rooms/{room}/shelves/{shelf}", n.Room, n.Shelf, ) } func (n RoomsShelfResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n RoomsShelfResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *RoomsShelfResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "rooms/{room}/shelves/{shelf}", &n.Room, &n.Shelf, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *RoomsShelfResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n RoomsShelfResourceName) Type() string { return "test1.testdata/Shelf" } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/originallysinglepattern/000077500000000000000000000000001513342120500315615ustar00rootroot00000000000000pattern_test.go000066400000000000000000000164001513342120500345460ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/originallysinglepatternpackage originallysinglepattern import ( "fmt" "testing" "gotest.tools/v3/assert" ) func TestParseBookMultiPatternResourceName(t *testing.T) { goodPatterns := []string{ "shelves/shelf/books/book", "publishers/publisher/books/book", } for _, pattern := range goodPatterns { pattern := pattern t.Run(pattern, func(t *testing.T) { name, err := ParseBookMultiPatternResourceName(pattern) assert.NilError(t, err) assert.Equal(t, name.String(), pattern) }) } badPatterns := []string{ "books/book", "others/other", "others/other/books/book", } for _, pattern := range badPatterns { pattern := pattern t.Run(pattern, func(t *testing.T) { _, err := ParseBookMultiPatternResourceName(pattern) assert.Error(t, err, "no matching pattern") }) } } func TestBookResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( shelf = "shelf" book = "book" ) pattern := fmt.Sprintf("shelves/%s/books/%s", shelf, book) var name BookResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Shelf, shelf) assert.Equal(t, name.Book, book) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name BookResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name BookResourceName err := name.UnmarshalString("others/other/books/book") assert.Error( t, err, "parse resource name 'others/other/books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got others", ) }) t.Run("bad newer parent", func(t *testing.T) { var name BookResourceName err := name.UnmarshalString("publishers/publisher/books/book") assert.Error( t, err, "parse resource name 'publishers/publisher/books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got publishers", ) }) } func TestShelvesBookResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( shelf = "shelf" book = "book" ) pattern := fmt.Sprintf("shelves/%s/books/%s", shelf, book) var name ShelvesBookResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Shelf, shelf) assert.Equal(t, name.Book, book) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name ShelvesBookResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name ShelvesBookResourceName err := name.UnmarshalString("others/other/books/book") assert.Error( t, err, "parse resource name 'others/other/books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got others", ) }) } func TestPublishersBookResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( publisher = "publisher" book = "book" ) pattern := fmt.Sprintf("publishers/%s/books/%s", publisher, book) var name PublishersBookResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Publisher, publisher) assert.Equal(t, name.Book, book) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name PublishersBookResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'publishers/{publisher}/books/{book}': segment publishers: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name PublishersBookResourceName err := name.UnmarshalString("others/other/books/book") assert.Error( t, err, "parse resource name 'others/other/books/book' with pattern 'publishers/{publisher}/books/{book}': segment publishers: got others", ) }) } func TestShelfResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const shelf = "shelf" pattern := fmt.Sprintf("shelves/%s", shelf) var name ShelfResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Shelf, shelf) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("invalid", func(t *testing.T) { var name ShelfResourceName err := name.UnmarshalString("others/other") assert.Error(t, err, "parse resource name 'others/other' with pattern 'shelves/{shelf}': segment shelves: got others") }) t.Run("bad wrong parent", func(t *testing.T) { var name ShelfResourceName err := name.UnmarshalString("others/other/shelves/shelf") assert.Error( t, err, "parse resource name 'others/other/shelves/shelf' with pattern 'shelves/{shelf}': segment shelves: got others", ) }) } func TestLibrariesShelfResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( library = "library" shelf = "shelf" ) pattern := fmt.Sprintf("libraries/%s/shelves/%s", library, shelf) var name LibrariesShelfResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Library, library) assert.Equal(t, name.Shelf, shelf) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name LibrariesShelfResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'libraries/{library}/shelves/{shelf}': segment libraries: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name LibrariesShelfResourceName err := name.UnmarshalString("others/other/shelves/shelf") assert.Error( t, err, "parse resource name 'others/other/shelves/shelf' with pattern 'libraries/{library}/shelves/{shelf}': segment libraries: got others", ) }) } func TestRoomsShelfResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( room = "room" shelf = "shelf" ) pattern := fmt.Sprintf("rooms/%s/shelves/%s", room, shelf) var name RoomsShelfResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Room, room) assert.Equal(t, name.Shelf, shelf) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name RoomsShelfResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'rooms/{room}/shelves/{shelf}': segment rooms: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name RoomsShelfResourceName err := name.UnmarshalString("others/other/shelves/shelf") assert.Error( t, err, "parse resource name 'others/other/shelves/shelf' with pattern 'rooms/{room}/shelves/{shelf}': segment rooms: got others", ) }) } testdata.proto000066400000000000000000000014761513342120500344100ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/originallysinglepatternsyntax = "proto3"; package test.originallysinglepattern; import "google/api/resource.proto"; message Book { option (google.api.resource) = { type: "test1.testdata/Book" singular: "book" plural: "books" pattern: "shelves/{shelf}/books/{book}" pattern: "publishers/{publisher}/books/{book}" history: ORIGINALLY_SINGLE_PATTERN }; // The resource name of the book. string name = 1; } // Shelf can be either top-level, within a library or within a room. message Shelf { option (google.api.resource) = { type: "test1.testdata/Shelf" singular: "shelf" plural: "shelves" pattern: "shelves/{shelf}" pattern: "libraries/{library}/shelves/{shelf}" pattern: "rooms/{room}/shelves/{shelf}" history: ORIGINALLY_SINGLE_PATTERN }; // The resource name. string name = 1; } testdata_aip.go000066400000000000000000000263461513342120500345060ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/originallysinglepattern// Code generated by protoc-gen-go-aip. DO NOT EDIT. // // versions: // protoc-gen-go-aip development // protoc (unknown) // source: test/originallysinglepattern/testdata.proto package originallysinglepattern import ( encoding "encoding" fmt "fmt" resourcename "go.einride.tech/aip/resourcename" strings "strings" ) type BookMultiPatternResourceName interface { fmt.Stringer encoding.TextMarshaler MarshalString() (string, error) ContainsWildcard() bool } func ParseBookMultiPatternResourceName(name string) (BookMultiPatternResourceName, error) { switch { case resourcename.Match("shelves/{shelf}/books/{book}", name): var result ShelvesBookResourceName return &result, result.UnmarshalString(name) case resourcename.Match("publishers/{publisher}/books/{book}", name): var result PublishersBookResourceName return &result, result.UnmarshalString(name) default: return nil, fmt.Errorf("no matching pattern") } } type BookResourceName struct { Shelf string Book string } func (n ShelfResourceName) BookResourceName( book string, ) BookResourceName { return BookResourceName{ Shelf: n.Shelf, Book: book, } } func (n BookResourceName) Validate() error { if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } if n.Book == "" { return fmt.Errorf("book: empty") } if strings.IndexByte(n.Book, '/') != -1 { return fmt.Errorf("book: contains illegal character '/'") } return nil } func (n BookResourceName) ContainsWildcard() bool { return false || n.Shelf == "-" || n.Book == "-" } func (n BookResourceName) String() string { return resourcename.Sprint( "shelves/{shelf}/books/{book}", n.Shelf, n.Book, ) } func (n BookResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n BookResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *BookResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shelves/{shelf}/books/{book}", &n.Shelf, &n.Book, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *BookResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n BookResourceName) Type() string { return "test1.testdata/Book" } func (n BookResourceName) ShelfResourceName() ShelfResourceName { return ShelfResourceName{ Shelf: n.Shelf, } } type ShelvesBookResourceName struct { Shelf string Book string } func (n ShelfResourceName) ShelvesBookResourceName( book string, ) ShelvesBookResourceName { return ShelvesBookResourceName{ Shelf: n.Shelf, Book: book, } } func (n ShelvesBookResourceName) Validate() error { if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } if n.Book == "" { return fmt.Errorf("book: empty") } if strings.IndexByte(n.Book, '/') != -1 { return fmt.Errorf("book: contains illegal character '/'") } return nil } func (n ShelvesBookResourceName) ContainsWildcard() bool { return false || n.Shelf == "-" || n.Book == "-" } func (n ShelvesBookResourceName) String() string { return resourcename.Sprint( "shelves/{shelf}/books/{book}", n.Shelf, n.Book, ) } func (n ShelvesBookResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ShelvesBookResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ShelvesBookResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shelves/{shelf}/books/{book}", &n.Shelf, &n.Book, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ShelvesBookResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ShelvesBookResourceName) Type() string { return "test1.testdata/Book" } func (n ShelvesBookResourceName) ShelfResourceName() ShelfResourceName { return ShelfResourceName{ Shelf: n.Shelf, } } type PublishersBookResourceName struct { Publisher string Book string } func (n PublishersBookResourceName) Validate() error { if n.Publisher == "" { return fmt.Errorf("publisher: empty") } if strings.IndexByte(n.Publisher, '/') != -1 { return fmt.Errorf("publisher: contains illegal character '/'") } if n.Book == "" { return fmt.Errorf("book: empty") } if strings.IndexByte(n.Book, '/') != -1 { return fmt.Errorf("book: contains illegal character '/'") } return nil } func (n PublishersBookResourceName) ContainsWildcard() bool { return false || n.Publisher == "-" || n.Book == "-" } func (n PublishersBookResourceName) String() string { return resourcename.Sprint( "publishers/{publisher}/books/{book}", n.Publisher, n.Book, ) } func (n PublishersBookResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n PublishersBookResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *PublishersBookResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "publishers/{publisher}/books/{book}", &n.Publisher, &n.Book, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *PublishersBookResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n PublishersBookResourceName) Type() string { return "test1.testdata/Book" } type ShelfMultiPatternResourceName interface { fmt.Stringer encoding.TextMarshaler MarshalString() (string, error) ContainsWildcard() bool } func ParseShelfMultiPatternResourceName(name string) (ShelfMultiPatternResourceName, error) { switch { case resourcename.Match("shelves/{shelf}", name): var result ShelfResourceName return &result, result.UnmarshalString(name) case resourcename.Match("libraries/{library}/shelves/{shelf}", name): var result LibrariesShelfResourceName return &result, result.UnmarshalString(name) case resourcename.Match("rooms/{room}/shelves/{shelf}", name): var result RoomsShelfResourceName return &result, result.UnmarshalString(name) default: return nil, fmt.Errorf("no matching pattern") } } type ShelfResourceName struct { Shelf string } func (n ShelfResourceName) Validate() error { if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } return nil } func (n ShelfResourceName) ContainsWildcard() bool { return false || n.Shelf == "-" } func (n ShelfResourceName) String() string { return resourcename.Sprint( "shelves/{shelf}", n.Shelf, ) } func (n ShelfResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ShelfResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ShelfResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shelves/{shelf}", &n.Shelf, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ShelfResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ShelfResourceName) Type() string { return "test1.testdata/Shelf" } type LibrariesShelfResourceName struct { Library string Shelf string } func (n LibrariesShelfResourceName) Validate() error { if n.Library == "" { return fmt.Errorf("library: empty") } if strings.IndexByte(n.Library, '/') != -1 { return fmt.Errorf("library: contains illegal character '/'") } if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } return nil } func (n LibrariesShelfResourceName) ContainsWildcard() bool { return false || n.Library == "-" || n.Shelf == "-" } func (n LibrariesShelfResourceName) String() string { return resourcename.Sprint( "libraries/{library}/shelves/{shelf}", n.Library, n.Shelf, ) } func (n LibrariesShelfResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n LibrariesShelfResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *LibrariesShelfResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "libraries/{library}/shelves/{shelf}", &n.Library, &n.Shelf, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *LibrariesShelfResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n LibrariesShelfResourceName) Type() string { return "test1.testdata/Shelf" } type RoomsShelfResourceName struct { Room string Shelf string } func (n RoomsShelfResourceName) Validate() error { if n.Room == "" { return fmt.Errorf("room: empty") } if strings.IndexByte(n.Room, '/') != -1 { return fmt.Errorf("room: contains illegal character '/'") } if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } return nil } func (n RoomsShelfResourceName) ContainsWildcard() bool { return false || n.Room == "-" || n.Shelf == "-" } func (n RoomsShelfResourceName) String() string { return resourcename.Sprint( "rooms/{room}/shelves/{shelf}", n.Room, n.Shelf, ) } func (n RoomsShelfResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n RoomsShelfResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *RoomsShelfResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "rooms/{room}/shelves/{shelf}", &n.Room, &n.Shelf, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *RoomsShelfResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n RoomsShelfResourceName) Type() string { return "test1.testdata/Shelf" } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/single/000077500000000000000000000000001513342120500260715ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/single/pattern_test.go000066400000000000000000000037601513342120500311420ustar00rootroot00000000000000package single import ( "fmt" "testing" "gotest.tools/v3/assert" ) func TestBookResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const ( shelf = "shelf" book = "book" ) pattern := fmt.Sprintf("shelves/%s/books/%s", shelf, book) var name BookResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Shelf, shelf) assert.Equal(t, name.Book, book) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("bad top-level", func(t *testing.T) { var name BookResourceName err := name.UnmarshalString("books/book") assert.Error( t, err, "parse resource name 'books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got books", ) }) t.Run("bad wrong parent", func(t *testing.T) { var name BookResourceName err := name.UnmarshalString("others/other/books/book") assert.Error( t, err, "parse resource name 'others/other/books/book' with pattern 'shelves/{shelf}/books/{book}': segment shelves: got others", ) }) } func TestShelfResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const shelf = "shelf" pattern := fmt.Sprintf("shelves/%s", shelf) var name ShelfResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) assert.Equal(t, name.Shelf, shelf) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("invalid", func(t *testing.T) { var name ShelfResourceName err := name.UnmarshalString("others/other") assert.Error(t, err, "parse resource name 'others/other' with pattern 'shelves/{shelf}': segment shelves: got others") }) t.Run("bad wrong parent", func(t *testing.T) { var name ShelfResourceName err := name.UnmarshalString("others/other/shelves/shelf") assert.Error( t, err, "parse resource name 'others/other/shelves/shelf' with pattern 'shelves/{shelf}': segment shelves: got others", ) }) } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/single/testdata.proto000066400000000000000000000010251513342120500307650ustar00rootroot00000000000000syntax = "proto3"; package test.single; import "google/api/resource.proto"; message Shelf { option (google.api.resource) = { type: "test1.testdata/Shelf" singular: "shelf" plural: "shelves" pattern: "shelves/{shelf}" }; // The resource name of the shelf. string name = 1; } message Book { option (google.api.resource) = { type: "test1.testdata/Book" singular: "book" plural: "books" pattern: "shelves/{shelf}/books/{book}" }; // The resource name of the book. string name = 1; } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/single/testdata_aip.go000066400000000000000000000064041513342120500310660ustar00rootroot00000000000000// Code generated by protoc-gen-go-aip. DO NOT EDIT. // // versions: // protoc-gen-go-aip development // protoc (unknown) // source: test/single/testdata.proto package single import ( fmt "fmt" resourcename "go.einride.tech/aip/resourcename" strings "strings" ) type ShelfResourceName struct { Shelf string } func (n ShelfResourceName) Validate() error { if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } return nil } func (n ShelfResourceName) ContainsWildcard() bool { return false || n.Shelf == "-" } func (n ShelfResourceName) String() string { return resourcename.Sprint( "shelves/{shelf}", n.Shelf, ) } func (n ShelfResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ShelfResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ShelfResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shelves/{shelf}", &n.Shelf, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ShelfResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ShelfResourceName) Type() string { return "test1.testdata/Shelf" } type BookResourceName struct { Shelf string Book string } func (n ShelfResourceName) BookResourceName( book string, ) BookResourceName { return BookResourceName{ Shelf: n.Shelf, Book: book, } } func (n BookResourceName) Validate() error { if n.Shelf == "" { return fmt.Errorf("shelf: empty") } if strings.IndexByte(n.Shelf, '/') != -1 { return fmt.Errorf("shelf: contains illegal character '/'") } if n.Book == "" { return fmt.Errorf("book: empty") } if strings.IndexByte(n.Book, '/') != -1 { return fmt.Errorf("book: contains illegal character '/'") } return nil } func (n BookResourceName) ContainsWildcard() bool { return false || n.Shelf == "-" || n.Book == "-" } func (n BookResourceName) String() string { return resourcename.Sprint( "shelves/{shelf}/books/{book}", n.Shelf, n.Book, ) } func (n BookResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n BookResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *BookResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shelves/{shelf}/books/{book}", &n.Shelf, &n.Book, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *BookResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n BookResourceName) Type() string { return "test1.testdata/Book" } func (n BookResourceName) ShelfResourceName() ShelfResourceName { return ShelfResourceName{ Shelf: n.Shelf, } } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/toplevelsingleton/000077500000000000000000000000001513342120500303655ustar00rootroot00000000000000aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/toplevelsingleton/pattern_test.go000066400000000000000000000011561513342120500334330ustar00rootroot00000000000000package toplevelsingleton import ( "testing" "gotest.tools/v3/assert" ) func TestConfigResourceName(t *testing.T) { t.Run("good", func(t *testing.T) { const pattern = "config" var name ConfigResourceName err := name.UnmarshalString(pattern) assert.NilError(t, err) marshalled, err := name.MarshalString() assert.NilError(t, err) assert.Equal(t, marshalled, pattern) }) t.Run("invalid", func(t *testing.T) { var name ConfigResourceName err := name.UnmarshalString("other") t.Log(err) assert.Error(t, err, "parse resource name 'other' with pattern 'config': segment config: got other") }) } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/toplevelsingleton/testdata.proto000066400000000000000000000004641513342120500332670ustar00rootroot00000000000000syntax = "proto3"; package test.toplevelsingleton; import "google/api/resource.proto"; message Config { option (google.api.resource) = { type: "test1.testdata/Config" singular: "config" plural: "configs" pattern: "config" }; // The resource name of the config. string name = 1; } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/testdata/test/toplevelsingleton/testdata_aip.go000066400000000000000000000024441513342120500333620ustar00rootroot00000000000000// Code generated by protoc-gen-go-aip. DO NOT EDIT. // // versions: // protoc-gen-go-aip development // protoc (unknown) // source: test/toplevelsingleton/testdata.proto package toplevelsingleton import ( resourcename "go.einride.tech/aip/resourcename" ) type ConfigResourceName struct { } func (n ConfigResourceName) Validate() error { return nil } func (n ConfigResourceName) ContainsWildcard() bool { return false } func (n ConfigResourceName) String() string { return resourcename.Sprint( "config", ) } func (n ConfigResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ConfigResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ConfigResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "config", ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ConfigResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ConfigResourceName) Type() string { return "test1.testdata/Config" } aip-go-0.80.0/cmd/protoc-gen-go-aip/internal/genaip/version.go000066400000000000000000000002201513342120500240260ustar00rootroot00000000000000package genaip // PluginVersion is the version of the protoc-gen-go-aip plugin. // //nolint:gochecknoglobals var PluginVersion = "development" aip-go-0.80.0/cmd/protoc-gen-go-aip/main.go000066400000000000000000000016051513342120500202160ustar00rootroot00000000000000package main import ( "flag" "log" "os" "path/filepath" "go.einride.tech/aip/cmd/protoc-gen-go-aip/internal/genaip" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/types/pluginpb" ) func main() { log.SetFlags(0) if len(os.Args) == 2 && os.Args[1] == "--version" { log.Printf("%v %v\n", filepath.Base(os.Args[0]), genaip.PluginVersion) os.Exit(0) } var ( flags flag.FlagSet includeResourceDefinitions = flags.Bool( "include_resource_definitions", true, "set to false to exclude resource definitions from code generation", ) ) protogen.Options{ ParamFunc: flags.Set, }.Run(func(plugin *protogen.Plugin) error { plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) return genaip.Run(plugin, genaip.Config{ IncludeResourceDefinitions: *includeResourceDefinitions, }) }) } aip-go-0.80.0/doc.go000066400000000000000000000001411513342120500140370ustar00rootroot00000000000000// Package aip provides primitives for implementing API Improvement Proposals (AIP). package aip aip-go-0.80.0/examples/000077500000000000000000000000001513342120500145655ustar00rootroot00000000000000aip-go-0.80.0/examples/examplelibrary/000077500000000000000000000000001513342120500176055ustar00rootroot00000000000000aip-go-0.80.0/examples/examplelibrary/listshelves.go000066400000000000000000000024671513342120500225120ustar00rootroot00000000000000package examplelibrary import ( "context" "go.einride.tech/aip/pagination" "google.golang.org/genproto/googleapis/example/library/v1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func (s *Server) ListShelves( ctx context.Context, request *library.ListShelvesRequest, ) (*library.ListShelvesResponse, error) { // Handle request constraints. const ( maxPageSize = 1000 defaultPageSize = 100 ) switch { case request.PageSize < 0: return nil, status.Errorf(codes.InvalidArgument, "page size is negative") case request.PageSize == 0: request.PageSize = defaultPageSize case request.PageSize > maxPageSize: request.PageSize = maxPageSize } // Use pagination.PageToken for offset-based page tokens. pageToken, err := pagination.ParsePageToken(request) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid page token") } // Query the storage. result, err := s.Storage.ListShelves(ctx, &ListShelvesQuery{ Offset: pageToken.Offset, PageSize: request.GetPageSize(), }) if err != nil { return nil, err } // Build the response. response := &library.ListShelvesResponse{ Shelves: result.Shelves, } // Set the next page token. if result.HasNextPage { response.NextPageToken = pageToken.Next(request).String() } // Respond. return response, nil } aip-go-0.80.0/examples/examplelibrary/listshelves_test.go000066400000000000000000000022611513342120500235410ustar00rootroot00000000000000package examplelibrary import ( "context" "fmt" "google.golang.org/genproto/googleapis/example/library/v1" ) func ExampleServer_ListShelves() { ctx := context.Background() server := &Server{ Storage: &Storage{ Shelves: []*library.Shelf{ {Name: "shelves/0001", Theme: "Sci-Fi"}, {Name: "shelves/0002", Theme: "Horror"}, {Name: "shelves/0003", Theme: "Romance"}, }, }, } page1, err := server.ListShelves(ctx, &library.ListShelvesRequest{ PageSize: 2, }) if err != nil { panic(err) // TODO: Handle errors. } for _, shelf := range page1.Shelves { fmt.Println(shelf.Name, shelf.Theme) } fmt.Println("page1.NextPageToken non-empty:", page1.NextPageToken != "") page2, err := server.ListShelves(ctx, &library.ListShelvesRequest{ PageSize: 2, PageToken: page1.NextPageToken, }) if err != nil { panic(err) // TODO: Handle errors. } for _, shelf := range page2.Shelves { fmt.Println(shelf.Name, shelf.Theme) } fmt.Println("page2.NextPageToken non-empty:", page2.NextPageToken != "") // Output: // shelves/0001 Sci-Fi // shelves/0002 Horror // page1.NextPageToken non-empty: true // shelves/0003 Romance // page2.NextPageToken non-empty: false } aip-go-0.80.0/examples/examplelibrary/server.go000066400000000000000000000002571513342120500214460ustar00rootroot00000000000000package examplelibrary import "google.golang.org/genproto/googleapis/example/library/v1" type Server struct { library.UnimplementedLibraryServiceServer Storage *Storage } aip-go-0.80.0/examples/examplelibrary/storage.go000066400000000000000000000015441513342120500216040ustar00rootroot00000000000000package examplelibrary import ( "context" "google.golang.org/genproto/googleapis/example/library/v1" ) type Storage struct { Shelves []*library.Shelf } type ListShelvesQuery struct { Offset int64 PageSize int32 } type ListShelvesResult struct { Shelves []*library.Shelf HasNextPage bool } func (s *Storage) ListShelves(_ context.Context, query *ListShelvesQuery) (*ListShelvesResult, error) { pageStart := int(query.Offset) pageEnd := pageStart + int(query.PageSize) switch { case pageStart >= len(s.Shelves): return &ListShelvesResult{ Shelves: nil, HasNextPage: false, }, nil case pageEnd > len(s.Shelves): return &ListShelvesResult{ Shelves: s.Shelves[pageStart:], HasNextPage: false, }, nil default: return &ListShelvesResult{ Shelves: s.Shelves[pageStart:pageEnd], HasNextPage: true, }, nil } } aip-go-0.80.0/fieldbehavior/000077500000000000000000000000001513342120500155525ustar00rootroot00000000000000aip-go-0.80.0/fieldbehavior/doc.go000066400000000000000000000002701513342120500166450ustar00rootroot00000000000000// Package fieldbehavior provides primitives for implementing AIP fieldbehavior annotations. // // See: https://google.aip.dev/203 (Field behavior documentation) package fieldbehavior aip-go-0.80.0/fieldbehavior/fieldbehavior.go000066400000000000000000000122531513342120500207070ustar00rootroot00000000000000package fieldbehavior import ( "fmt" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" ) // Get returns the field behavior of the provided field descriptor. func Get(field protoreflect.FieldDescriptor) []annotations.FieldBehavior { if behaviors, ok := proto.GetExtension( field.Options(), annotations.E_FieldBehavior, ).([]annotations.FieldBehavior); ok { return behaviors } return nil } // Has returns true if the provided field descriptor has the wanted field behavior. func Has(field protoreflect.FieldDescriptor, want annotations.FieldBehavior) bool { for _, got := range Get(field) { if got == want { return true } } return false } // ClearFields clears all fields annotated with any of the provided behaviors. // This can be used to ignore fields provided as input that have field_behavior's // such as OUTPUT_ONLY and IMMUTABLE. // // See: https://google.aip.dev/161#output-only-fields func ClearFields(message proto.Message, behaviorsToClear ...annotations.FieldBehavior) { clearFieldsWithBehaviors(message, behaviorsToClear...) } // CopyFields copies all fields annotated with any of the provided behaviors from src to dst. func CopyFields(dst, src proto.Message, behaviorsToCopy ...annotations.FieldBehavior) { dstReflect := dst.ProtoReflect() srcReflect := src.ProtoReflect() if dstReflect.Descriptor() != srcReflect.Descriptor() { panic(fmt.Sprintf( "different types of dst (%s) and src (%s)", dstReflect.Type().Descriptor().FullName(), srcReflect.Type().Descriptor().FullName(), )) } for i := 0; i < dstReflect.Descriptor().Fields().Len(); i++ { dstField := dstReflect.Descriptor().Fields().Get(i) if hasAnyBehavior(Get(dstField), behaviorsToCopy) { srcField := srcReflect.Descriptor().Fields().Get(i) if isMessageFieldPresent(srcReflect, srcField) { dstReflect.Set(dstField, srcReflect.Get(srcField)) } else { dstReflect.Clear(dstField) } } } } func isMessageFieldPresent(m protoreflect.Message, f protoreflect.FieldDescriptor) bool { return isPresent(m.Get(f), f) } func isPresent(v protoreflect.Value, f protoreflect.FieldDescriptor) bool { if !v.IsValid() { return false } if f.IsList() { return v.List().Len() > 0 } if f.IsMap() { return v.Map().Len() > 0 } switch f.Kind() { case protoreflect.EnumKind: return v.Enum() != 0 case protoreflect.BoolKind: return v.Bool() case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind: return v.Int() != 0 case protoreflect.Uint32Kind, protoreflect.Uint64Kind, protoreflect.Fixed32Kind, protoreflect.Fixed64Kind: return v.Uint() != 0 case protoreflect.FloatKind, protoreflect.DoubleKind: return v.Float() != 0 case protoreflect.StringKind: return len(v.String()) > 0 case protoreflect.BytesKind: return len(v.Bytes()) > 0 case protoreflect.MessageKind: return v.Message().IsValid() case protoreflect.GroupKind: return v.IsValid() default: return v.IsValid() } } func clearFieldsWithBehaviors(m proto.Message, behaviorsToClear ...annotations.FieldBehavior) { rangeFieldsWithBehaviors( m.ProtoReflect(), func( m protoreflect.Message, f protoreflect.FieldDescriptor, _ protoreflect.Value, behaviors []annotations.FieldBehavior, ) bool { if hasAnyBehavior(behaviors, behaviorsToClear) { m.Clear(f) } return true }, ) } func rangeFieldsWithBehaviors( m protoreflect.Message, fn func( protoreflect.Message, protoreflect.FieldDescriptor, protoreflect.Value, []annotations.FieldBehavior, ) bool, ) { m.Range( func(f protoreflect.FieldDescriptor, v protoreflect.Value) bool { if behaviors, ok := proto.GetExtension( f.Options(), annotations.E_FieldBehavior, ).([]annotations.FieldBehavior); ok { fn(m, f, v, behaviors) } switch { // if field is repeated, traverse the nested message for field behaviors case f.IsList() && f.Kind() == protoreflect.MessageKind: for i := 0; i < v.List().Len(); i++ { rangeFieldsWithBehaviors( v.List().Get(i).Message(), fn, ) } return true // if field is map, traverse the nested message for field behaviors case f.IsMap() && f.MapValue().Kind() == protoreflect.MessageKind: v.Map().Range(func(_ protoreflect.MapKey, mv protoreflect.Value) bool { rangeFieldsWithBehaviors( mv.Message(), fn, ) return true }) return true // if field is message, traverse the message // maps are also treated as Kind message and should not be traversed as messages case f.Kind() == protoreflect.MessageKind && !f.IsMap(): rangeFieldsWithBehaviors( v.Message(), fn, ) return true default: return true } }) } func hasAnyBehavior(haystack, needles []annotations.FieldBehavior) bool { for _, needle := range needles { if hasBehavior(haystack, needle) { return true } } return false } func hasBehavior(haystack []annotations.FieldBehavior, needle annotations.FieldBehavior) bool { for _, straw := range haystack { if straw == needle { return true } } return false } aip-go-0.80.0/fieldbehavior/fieldbehavior_test.go000066400000000000000000000723231513342120500217520ustar00rootroot00000000000000package fieldbehavior import ( "testing" examplefreightv1 "go.einride.tech/aip/proto/gen/einride/example/freight/v1" syntaxv1 "go.einride.tech/aip/proto/gen/einride/example/syntax/v1" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/genproto/googleapis/example/library/v1" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/fieldmaskpb" "google.golang.org/protobuf/types/known/timestamppb" "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" ) func TestClearFields(t *testing.T) { t.Parallel() t.Run("clear fields with set field_behavior", func(t *testing.T) { t.Parallel() site := &examplefreightv1.Site{ Name: "site1", // has no field_behaviors; should not be cleared. CreateTime: timestamppb.Now(), // has OUTPUT_ONLY field_behavior; should be cleared. DisplayName: "site one", // has REQUIRED field_behavior; should not be cleared. } ClearFields(site, annotations.FieldBehavior_OUTPUT_ONLY) assert.Equal(t, site.GetCreateTime(), (*timestamppb.Timestamp)(nil)) assert.Equal(t, site.GetDisplayName(), "site one") assert.Equal(t, site.GetName(), "site1") }) t.Run("clear field with set field_behavior on nested message", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. }, } expected := &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", }, } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual(t, input, expected, protocmp.Transform()) }) t.Run("clear field with set field_behavior on multiple levels of nested messages", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. }, }, } expected := &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", }, }, } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual(t, input, expected, protocmp.Transform()) }) t.Run("clear fields with set field_behavior on repeated message", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. { Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. }, }, StringList: []string{ // not a message type, should not be traversed "string", }, } expected := &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ { Field: "field", OptionalField: "optional", }, }, StringList: []string{ "string", }, } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual(t, input, expected, protocmp.Transform()) }) t.Run("clear fields with set field_behavior on multiple levels of repeated messages", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. { Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. { Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. }, }, }, }, StringList: []string{ // not a message type, should not be traversed "string", }, } expected := &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ { Field: "field", OptionalField: "optional", RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ { Field: "field", OptionalField: "optional", }, }, }, }, StringList: []string{ "string", }, } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual(t, input, expected, protocmp.Transform()) }) t.Run("clear repeated field with set field_behavior", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. { Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. }, }, RepeatedOutputOnlyMessage: []*syntaxv1.FieldBehaviorMessage{ // has OUTPUT_ONLY field_behavior; should be cleared. { Field: "field", OptionalField: "optional", OutputOnlyField: "output_only", }, }, } expected := &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. RepeatedMessage: []*syntaxv1.FieldBehaviorMessage{ // has no field_behaviors; should not be cleared. { Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. }, }, } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual(t, input, expected, protocmp.Transform()) }) t.Run("clear fields with set field_behavior on message in map", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key_1": { OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. }, }, StringMap: map[string]string{ "string_key": "string", // not a message type, should not be traversed }, } expected := &syntaxv1.FieldBehaviorMessage{ OptionalField: "optional", MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key_1": { OptionalField: "optional", }, }, StringMap: map[string]string{ "string_key": "string", }, } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual( t, input, expected, protocmp.Transform(), ) }) t.Run("clear map field with set field_behavior", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ OptionalField: "optional", // has OUTPUT_ONLY field_behavior; should be cleared. MapOutputOnlyMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key_1": { OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; but should be cleared with parent message. }, }, } expected := &syntaxv1.FieldBehaviorMessage{ OptionalField: "optional", } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual( t, input, expected, protocmp.Transform(), ) }) t.Run("clear field with set field_behavior on oneof message", func(t *testing.T) { t.Parallel() input := &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. Oneof: &syntaxv1.FieldBehaviorMessage_FieldBehaviorMessage{ FieldBehaviorMessage: &syntaxv1.FieldBehaviorMessage{ Field: "field", // has no field_behaviors; should not be cleared. OptionalField: "optional", // has OPTIONAL field_behavior; should not be cleared. OutputOnlyField: "output_only", // has OUTPUT_ONLY field_behavior; should be cleared. }, }, } expected := &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", Oneof: &syntaxv1.FieldBehaviorMessage_FieldBehaviorMessage{ FieldBehaviorMessage: &syntaxv1.FieldBehaviorMessage{ Field: "field", OptionalField: "optional", }, }, } ClearFields(input, annotations.FieldBehavior_OUTPUT_ONLY) assert.DeepEqual(t, input, expected, protocmp.Transform()) }) } func TestCopyFields(t *testing.T) { t.Parallel() t.Run("different types", func(t *testing.T) { t.Parallel() assert.Assert(t, cmp.Panics(func() { CopyFields(&library.Book{}, &library.Shelf{}, annotations.FieldBehavior_REQUIRED) })) }) } func TestValidateRequiredFields(t *testing.T) { t.Parallel() assert.NilError(t, ValidateRequiredFields(&examplefreightv1.GetShipmentRequest{Name: "testbook"})) assert.Error(t, ValidateRequiredFields(&examplefreightv1.GetShipmentRequest{}), "missing required field: name") } func TestValidateRequiredFieldsWithMask(t *testing.T) { t.Parallel() t.Run("ok", func(t *testing.T) { t.Parallel() assert.NilError( t, ValidateRequiredFieldsWithMask( &library.Book{Name: "testbook"}, nil, ), ) }) t.Run("ok - empty mask", func(t *testing.T) { t.Parallel() assert.NilError( t, ValidateRequiredFieldsWithMask( &library.Book{}, nil, ), ) }) t.Run("missing field", func(t *testing.T) { t.Parallel() assert.Error( t, ValidateRequiredFieldsWithMask( &examplefreightv1.GetShipmentRequest{}, &fieldmaskpb.FieldMask{Paths: []string{"*"}}, ), "missing required field: name", ) }) t.Run("missing but not in mask", func(t *testing.T) { t.Parallel() assert.NilError( t, ValidateRequiredFieldsWithMask( &library.Book{}, &fieldmaskpb.FieldMask{Paths: []string{"author"}}, ), ) }) t.Run("missing nested", func(t *testing.T) { t.Parallel() assert.Error( t, ValidateRequiredFieldsWithMask( &examplefreightv1.UpdateShipmentRequest{ Shipment: &examplefreightv1.Shipment{}, }, &fieldmaskpb.FieldMask{Paths: []string{"shipment.origin_site"}}, ), "missing required field: shipment.origin_site", ) }) t.Run("missing nested not in mask", func(t *testing.T) { t.Parallel() assert.NilError( t, ValidateRequiredFieldsWithMask( &library.UpdateBookRequest{ Book: &library.Book{}, }, &fieldmaskpb.FieldMask{Paths: []string{"book.author"}}, ), ) }) t.Run("support maps", func(t *testing.T) { t.Parallel() assert.NilError( t, ValidateRequiredFieldsWithMask( &examplefreightv1.Shipment{ Annotations: map[string]string{ "x": "y", }, }, &fieldmaskpb.FieldMask{Paths: []string{"annotations"}}, ), ) }) } func TestValidateImmutableFieldsWithMask(t *testing.T) { t.Parallel() t.Run("no error when immutable field not set", func(t *testing.T) { t.Parallel() req := &examplefreightv1.UpdateShipmentRequest{ Shipment: &examplefreightv1.Shipment{}, UpdateMask: &fieldmaskpb.FieldMask{ Paths: []string{""}, }, } err := ValidateImmutableFieldsWithMask(req, req.GetUpdateMask()) assert.NilError(t, err) }) t.Run("no error when immutable field not part of fieldmask", func(t *testing.T) { t.Parallel() req := &examplefreightv1.UpdateShipmentRequest{ Shipment: &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site1", }, UpdateMask: &fieldmaskpb.FieldMask{ Paths: []string{"shipment.origin_site"}, }, } err := ValidateImmutableFieldsWithMask(req, req.GetUpdateMask()) assert.NilError(t, err) }) t.Run("errors when wildcard fieldmask used", func(t *testing.T) { t.Parallel() req := &examplefreightv1.UpdateShipmentRequest{ Shipment: &examplefreightv1.Shipment{}, UpdateMask: &fieldmaskpb.FieldMask{ Paths: []string{"*"}, }, } err := ValidateImmutableFieldsWithMask(req, req.GetUpdateMask()) assert.ErrorContains(t, err, "field is immutable") }) t.Run("errors when immutable field set in fieldmask", func(t *testing.T) { t.Parallel() req := &examplefreightv1.UpdateShipmentRequest{ Shipment: &examplefreightv1.Shipment{}, UpdateMask: &fieldmaskpb.FieldMask{ Paths: []string{"shipment.external_reference_id"}, }, } err := ValidateImmutableFieldsWithMask(req, req.GetUpdateMask()) assert.ErrorContains(t, err, "field is immutable") }) t.Run("errors when immutable field set in message", func(t *testing.T) { t.Parallel() req := &examplefreightv1.UpdateShipmentRequest{ Shipment: &examplefreightv1.Shipment{ ExternalReferenceId: "I am immutable!", }, UpdateMask: &fieldmaskpb.FieldMask{ Paths: []string{}, }, } err := ValidateImmutableFieldsWithMask(req, req.GetUpdateMask()) assert.ErrorContains(t, err, "field is immutable") }) t.Run("errors when immutable field set in nested field", func(t *testing.T) { t.Parallel() req := &examplefreightv1.UpdateShipmentRequest{ Shipment: &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { ExternalReferenceId: "I am immutable", }, }, }, UpdateMask: &fieldmaskpb.FieldMask{ Paths: []string{}, }, } err := ValidateImmutableFieldsWithMask(req, req.GetUpdateMask()) assert.ErrorContains(t, err, "field is immutable") }) } func TestValidateImmutableFieldsNotChanged(t *testing.T) { t.Parallel() t.Run("no error when immutable field not in mask", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site1", } updated := &examplefreightv1.Shipment{ ExternalReferenceId: "different-reference-id", OriginSite: "shippers/shipper1/sites/site2", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"origin_site"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when immutable field unchanged", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site1", } updated := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site2", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"external_reference_id", "origin_site"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("error when immutable field changed", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site1", } updated := &examplefreightv1.Shipment{ ExternalReferenceId: "different-reference-id", OriginSite: "shippers/shipper1/sites/site2", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"external_reference_id", "origin_site"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.ErrorContains(t, err, "immutable field cannot be changed") assert.ErrorContains(t, err, "external_reference_id") }) t.Run("error when wildcard used and immutable field changed", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site1", } updated := &examplefreightv1.Shipment{ ExternalReferenceId: "different-reference-id", OriginSite: "shippers/shipper1/sites/site2", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"*"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.ErrorContains(t, err, "immutable field cannot be changed") }) t.Run("no error when wildcard used but immutable field unchanged", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site1", } updated := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", OriginSite: "shippers/shipper1/sites/site2", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"*"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("error when different message types", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ ExternalReferenceId: "external-reference-id", } updated := &examplefreightv1.Site{ Name: "site1", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"*"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.ErrorContains(t, err, "different types") }) t.Run("error when nested immutable field in repeated message changed", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1", }, }, } updated := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1 Updated", ExternalReferenceId: "line-item-1-changed", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"line_items"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.ErrorContains(t, err, "immutable field cannot be changed") assert.ErrorContains(t, err, "external_reference_id") }) t.Run("no error when nested immutable field in repeated message unchanged", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1", }, }, } updated := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1 Updated", ExternalReferenceId: "line-item-1", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"line_items"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("error when new element added with immutable field", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1", }, }, } updated := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1", }, { Title: "Item 2", ExternalReferenceId: "line-item-2", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"line_items"}, } // This should succeed as we're adding new items, not changing existing ones err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("error when nested immutable field in map message changed", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1", ImmutableField: "immutable-1", }, }, } updated := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1-updated", ImmutableField: "immutable-1-changed", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"map_optional_message"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.ErrorContains(t, err, "immutable field cannot be changed") assert.ErrorContains(t, err, "immutable_field") }) t.Run("no error when nested immutable field in map message unchanged", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1", ImmutableField: "immutable-1", }, }, } updated := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1-updated", ImmutableField: "immutable-1", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"map_optional_message"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("error when new map key added with changed immutable field", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1", ImmutableField: "immutable-1", }, }, } updated := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1", ImmutableField: "immutable-1", }, "key2": { Field: "value2", ImmutableField: "immutable-2", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"map_optional_message"}, } // This should succeed as we're adding new entries, not changing existing ones err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when old list is empty and updated has elements with immutable fields", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{}, } updated := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"line_items"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when updated list is empty and old had elements", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1", }, }, } updated := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{}, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"line_items"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when both lists are empty", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{}, } updated := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{}, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"line_items"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when old map is empty and updated has entries with immutable fields", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{}, } updated := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1", ImmutableField: "immutable-1", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"map_optional_message"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when updated map is empty and old had entries", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{ "key1": { Field: "value1", ImmutableField: "immutable-1", }, }, } updated := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{}, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"map_optional_message"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when both maps are empty", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{}, } updated := &syntaxv1.FieldBehaviorMessage{ MapOptionalMessage: map[string]*syntaxv1.FieldBehaviorMessage{}, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"map_optional_message"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when old singular message field not set and updated has immutable field", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ Field: "field", } updated := &syntaxv1.FieldBehaviorMessage{ Field: "field", MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ ImmutableField: "immutable-value", }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"message_without_field_behavior"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("no error when updated singular message field not set and old had immutable field", func(t *testing.T) { t.Parallel() old := &syntaxv1.FieldBehaviorMessage{ Field: "field", MessageWithoutFieldBehavior: &syntaxv1.FieldBehaviorMessage{ ImmutableField: "immutable-value", }, } updated := &syntaxv1.FieldBehaviorMessage{ Field: "field", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"message_without_field_behavior"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.NilError(t, err) }) t.Run("error when trying to unset top-level immutable field", func(t *testing.T) { t.Parallel() old := &examplefreightv1.Shipment{ ExternalReferenceId: "reference-123", } updated := &examplefreightv1.Shipment{ ExternalReferenceId: "", } mask := &fieldmaskpb.FieldMask{ Paths: []string{"external_reference_id"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.ErrorContains(t, err, "immutable field cannot be changed") }) t.Run("error when nested field under immutable parent is in mask and changed", func(t *testing.T) { t.Parallel() // The line_items field itself is NOT immutable, but line_items[].external_reference_id IS immutable // If mask contains "line_items.external_reference_id", we should still catch changes old := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1", }, }, } updated := &examplefreightv1.Shipment{ LineItems: []*examplefreightv1.LineItem{ { Title: "Item 1", ExternalReferenceId: "line-item-1-changed", }, }, } mask := &fieldmaskpb.FieldMask{ Paths: []string{"line_items.external_reference_id"}, } err := ValidateImmutableFieldsNotChanged(old, updated, mask) assert.ErrorContains(t, err, "immutable field cannot be changed") assert.ErrorContains(t, err, "external_reference_id") }) } aip-go-0.80.0/fieldbehavior/immutable.go000066400000000000000000000170171513342120500200660ustar00rootroot00000000000000package fieldbehavior // This file provides functions for validating immutable fields according to AIP-203. // // For AIP-203 compliant validation, use ValidateImmutableFieldsNotChanged, which allows // immutable fields in the update mask as long as their values haven't changed. // // ValidateImmutableFieldsWithMask is deprecated as it's too strict and doesn't fully // comply with AIP-203. import ( "fmt" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/fieldmaskpb" ) // ValidateImmutableFieldsWithMask returns a validation error if the message // or field mask contains a field that is immutable. // // Deprecated: This function is too strict and does not fully comply with AIP-203. // It errors whenever an immutable field appears in the update mask, even if the value // hasn't changed. According to AIP-203, immutable fields MAY be included in an update // request as long as they are set to their existing value. // // Use ValidateImmutableFieldsNotChanged instead, which properly implements AIP-203 by // only returning an error when an immutable field's value is actually being changed. // // If you want to ignore immutable fields rather than error then use ClearFields(). // // See: https://aip.dev/203 func ValidateImmutableFieldsWithMask(m proto.Message, mask *fieldmaskpb.FieldMask) error { return validateImmutableFields(m.ProtoReflect(), mask, "") } func validateImmutableFields(m protoreflect.Message, mask *fieldmaskpb.FieldMask, path string) error { for i := 0; i < m.Descriptor().Fields().Len(); i++ { field := m.Descriptor().Fields().Get(i) currPath := path if len(currPath) > 0 { currPath += "." } currPath += string(field.Name()) if isImmutable(field) && hasPath(mask, currPath) { return fmt.Errorf("field is immutable: %s", currPath) } if field.Kind() == protoreflect.MessageKind { value := m.Get(field) switch { case field.IsList(): for i := 0; i < value.List().Len(); i++ { if err := validateImmutableFields(value.List().Get(i).Message(), mask, currPath); err != nil { return err } } case field.IsMap(): if field.MapValue().Kind() != protoreflect.MessageKind { continue } var mapErr error value.Map().Range(func(_ protoreflect.MapKey, value protoreflect.Value) bool { if err := validateImmutableFields(value.Message(), mask, currPath); err != nil { mapErr = err return false } return true }) if mapErr != nil { return mapErr } default: // Check if field is set to avoid infinite recursion on recursive type definitions if !m.Has(field) { continue } if err := validateImmutableFields(value.Message(), mask, currPath); err != nil { return err } } } } return nil } // ValidateImmutableFieldsNotChanged validates that immutable fields in the update mask // are not being changed, in compliance with AIP-203. // // This function compares the old (existing) and updated (requested) values for any immutable // fields present in the update mask. It returns a validation error only if an immutable // field's value is actually being changed. // // According to AIP-203, immutable fields MAY be included in an update request as long as // they are set to their existing value. This allows clients to use the same message for // both create and update operations without having to remove immutable fields. // // Use this function when validating update requests to return INVALID_ARGUMENT if a user // tries to change an immutable field's value. // // Example: // // err := fieldbehavior.ValidateImmutableFieldsNotChanged( // existingResource, // updateRequest.GetResource(), // updateRequest.GetUpdateMask(), // ) // if err != nil { // return status.Errorf(codes.InvalidArgument, "invalid request: %v", err) // } // // See: https://aip.dev/203 func ValidateImmutableFieldsNotChanged(old, updated proto.Message, mask *fieldmaskpb.FieldMask) error { return validateImmutableFieldsNotChanged(old.ProtoReflect(), updated.ProtoReflect(), mask, "") } func validateImmutableFieldsNotChanged( old, updated protoreflect.Message, mask *fieldmaskpb.FieldMask, path string, ) error { if old.Descriptor() != updated.Descriptor() { return fmt.Errorf("old and updated messages have different types") } for i := 0; i < updated.Descriptor().Fields().Len(); i++ { field := updated.Descriptor().Fields().Get(i) currPath := path if len(currPath) > 0 { currPath += "." } currPath += string(field.Name()) if isImmutable(field) && hasPathWithPrefix(mask, currPath) { // Check if the immutable field's value has changed oldValue := old.Get(field) updatedValue := updated.Get(field) if !oldValue.Equal(updatedValue) { return fmt.Errorf("immutable field cannot be changed: %s", currPath) } // Values are equal and entire field is in mask - no need to check nested fields continue } if field.Kind() == protoreflect.MessageKind { updatedValue := updated.Get(field) switch { case field.IsList(): updatedList := updatedValue.List() oldList := old.Get(field).List() // If old is empty: new elements are allowed to have immutable fields set // If updated is empty: deletion is allowed if oldList.Len() == 0 || updatedList.Len() == 0 { continue } // Compare old and new lists element-by-element to catch nested immutable field changes // New elements are allowed to have immutable fields set, so only validate existing ones minLen := min(oldList.Len(), updatedList.Len()) for i := range minLen { if err := validateImmutableFieldsNotChanged( oldList.Get(i).Message(), updatedList.Get(i).Message(), mask, currPath, ); err != nil { return err } } case field.IsMap(): if field.MapValue().Kind() != protoreflect.MessageKind { continue } updatedMap := updatedValue.Map() oldMap := old.Get(field).Map() // If old is empty: new map entries are allowed to have immutable fields set // If updated is empty: deletion is allowed if oldMap.Len() == 0 || updatedMap.Len() == 0 { continue } // Compare old and new maps key-by-key to catch nested immutable field changes var mapErr error updatedMap.Range(func(key protoreflect.MapKey, updatedVal protoreflect.Value) bool { if oldVal := oldMap.Get(key); oldVal.IsValid() { // Key exists in both maps: compare the values if err := validateImmutableFieldsNotChanged( oldVal.Message(), updatedVal.Message(), mask, currPath, ); err != nil { mapErr = err return false } } // New keys are allowed to have immutable fields set, so skip validation return true }) if mapErr != nil { return mapErr } default: // For singular message fields, check if set to avoid infinite recursion oldHas := old.Has(field) updatedHas := updated.Has(field) // If old is not set: new field is allowed to have immutable fields set // If updated is not set: deletion is allowed if !oldHas || !updatedHas { continue } // Both old and updated have this field set: compare nested messages if err := validateImmutableFieldsNotChanged( old.Get(field).Message(), updatedValue.Message(), mask, currPath, ); err != nil { return err } } } } return nil } func isImmutable(field protoreflect.FieldDescriptor) bool { return Has(field, annotations.FieldBehavior_IMMUTABLE) } aip-go-0.80.0/fieldbehavior/required.go000066400000000000000000000076361513342120500177350ustar00rootroot00000000000000package fieldbehavior import ( "fmt" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/fieldmaskpb" ) // ValidateRequiredFields returns a validation error if any field annotated as required does not have a value. // See: https://aip.dev/203 func ValidateRequiredFields(m proto.Message) error { return validateRequiredFields( m.ProtoReflect(), &fieldmaskpb.FieldMask{Paths: []string{"*"}}, "", ) } // ValidateRequiredFieldsWithMask returns a validation error if any field annotated as required // does not have a value, considering only fields specified in the update mask. // // Note: This function only validates fields that exactly match paths in the mask. It does not // support prefix matching. For example, if the mask contains "shipment", it will NOT validate // nested required fields like "shipment.origin_site" unless explicitly specified in the mask. // Callers should ensure the mask contains all nested field paths that need validation. // // See: https://aip.dev/203 func ValidateRequiredFieldsWithMask(m proto.Message, mask *fieldmaskpb.FieldMask) error { return validateRequiredFields(m.ProtoReflect(), mask, "") } func validateRequiredFields(reflectMessage protoreflect.Message, mask *fieldmaskpb.FieldMask, path string) error { // If no paths are provided, the field mask should be treated to be equivalent // to all fields set on the wire. This means that no required fields can be missing, // since if they were missing they're not set on the wire. if len(mask.GetPaths()) == 0 { return nil } for i := 0; i < reflectMessage.Descriptor().Fields().Len(); i++ { field := reflectMessage.Descriptor().Fields().Get(i) currPath := path if len(currPath) > 0 { currPath += "." } currPath += string(field.Name()) if !isMessageFieldPresent(reflectMessage, field) { if Has(field, annotations.FieldBehavior_REQUIRED) && hasPath(mask, currPath) { return fmt.Errorf("missing required field: %s", currPath) } } else if field.Kind() == protoreflect.MessageKind { value := reflectMessage.Get(field) switch { case field.IsList(): for i := 0; i < value.List().Len(); i++ { if err := validateRequiredFields(value.List().Get(i).Message(), mask, currPath); err != nil { return err } } case field.IsMap(): if field.MapValue().Kind() != protoreflect.MessageKind { continue } var mapErr error value.Map().Range(func(_ protoreflect.MapKey, value protoreflect.Value) bool { if err := validateRequiredFields(value.Message(), mask, currPath); err != nil { mapErr = err return false } return true }) if mapErr != nil { return mapErr } default: if err := validateRequiredFields(value.Message(), mask, currPath); err != nil { return err } } } } return nil } func isEmpty(mask *fieldmaskpb.FieldMask) bool { return mask == nil || len(mask.GetPaths()) == 0 } func hasPath(mask *fieldmaskpb.FieldMask, needle string) bool { if isEmpty(mask) { return true } for _, straw := range mask.GetPaths() { if straw == "*" || straw == needle { return true } } return false } // hasPathWithPrefix checks if a field path is covered by the mask, supporting prefix matching. // This enables nested field validation when the mask contains a parent field. // For example, if mask contains "line_items", it matches "line_items.external_reference_id". func hasPathWithPrefix(mask *fieldmaskpb.FieldMask, needle string) bool { if isEmpty(mask) { return true } for _, straw := range mask.GetPaths() { if straw == "*" || straw == needle { return true } // Support prefix matching: if mask contains "line_items", it should match "line_items.external_reference_id" if len(straw) < len(needle) && needle[:len(straw)] == straw && needle[len(straw)] == '.' { return true } } return false } aip-go-0.80.0/fieldmask/000077500000000000000000000000001513342120500147065ustar00rootroot00000000000000aip-go-0.80.0/fieldmask/doc.go000066400000000000000000000005511513342120500160030ustar00rootroot00000000000000// Package fieldmask provides primitives for implementing AIP field mask functionality. // // This package only provides primitives not already provided by the package // google.golang.org/protobuf/types/known/fieldmaskpb. // // See: https://google.aip.dev/134 (Standard methods: Update) // See: https://google.aip.dev/157 (Partial responses) package fieldmask aip-go-0.80.0/fieldmask/update.go000066400000000000000000000056451513342120500165310ustar00rootroot00000000000000package fieldmask import ( "fmt" "strings" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/fieldmaskpb" ) // Update updates fields in dst with values from src according to the provided field mask. // Nested messages are recursively updated in the same manner. // Repeated fields and maps are copied by reference from src to dst. // Field mask paths referring to Individual entries in maps or // repeated fields are ignored. // // If no update mask is provided, only non-zero values of src are copied to dst. // If the special value "*" is provided as the field mask, a full replacement of all fields in dst is done. // // See: https://google.aip.dev/134 (Standard methods: Update). func Update(mask *fieldmaskpb.FieldMask, dst, src proto.Message) { dstReflect := dst.ProtoReflect() srcReflect := src.ProtoReflect() if dstReflect.Descriptor() != srcReflect.Descriptor() { panic(fmt.Sprintf( "dst (%s) and src (%s) messages have different types", dstReflect.Descriptor().FullName(), srcReflect.Descriptor().FullName(), )) } switch { // Special-case: No update mask. // Update all fields of src that are set on the wire. case len(mask.GetPaths()) == 0: updateWireSetFields(dstReflect, srcReflect) // Special-case: Update mask is [*]. // Do a full replacement of all fields. case IsFullReplacement(mask): proto.Reset(dst) proto.Merge(dst, src) default: for _, path := range mask.GetPaths() { segments := strings.Split(path, ".") updateNamedField(dstReflect, srcReflect, segments) } } } func updateWireSetFields(dst, src protoreflect.Message) { src.Range(func(field protoreflect.FieldDescriptor, value protoreflect.Value) bool { switch { case field.IsList(): dst.Set(field, value) case field.IsMap(): dst.Set(field, value) case field.Message() != nil && !dst.Has(field): dst.Set(field, value) case field.Message() != nil: updateWireSetFields(dst.Get(field).Message(), value.Message()) default: dst.Set(field, value) } return true }) } func updateNamedField(dst, src protoreflect.Message, segments []string) { if len(segments) == 0 { return } field := src.Descriptor().Fields().ByName(protoreflect.Name(segments[0])) if field == nil { // no known field by that name return } // a named field in this message if len(segments) == 1 { if !src.Has(field) { dst.Clear(field) } else { dst.Set(field, src.Get(field)) } return } // a named field in a nested message switch { case field.IsList(), field.IsMap(): // nested fields in repeated or map not supported return case field.Message() != nil: // if message field is not set, allocate an empty value if !dst.Has(field) { dst.Set(field, dst.NewField(field)) } if !src.Has(field) { src.Set(field, src.NewField(field)) } updateNamedField(dst.Get(field).Message(), src.Get(field).Message(), segments[1:]) default: return } } aip-go-0.80.0/fieldmask/update_test.go000066400000000000000000000416161513342120500175660ustar00rootroot00000000000000package fieldmask import ( "testing" syntaxv1 "go.einride.tech/aip/proto/gen/einride/example/syntax/v1" "google.golang.org/genproto/googleapis/example/library/v1" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/fieldmaskpb" "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" ) func TestUpdate(t *testing.T) { t.Parallel() t.Run("should panic on different src and dst", func(t *testing.T) { t.Parallel() assert.Assert(t, cmp.Panics(func() { Update(&fieldmaskpb.FieldMask{}, &library.Book{}, &library.Shelf{}) })) }) t.Run("full replacement", func(t *testing.T) { t.Parallel() for _, tt := range []struct { name string src proto.Message dst proto.Message }{ { name: "scalars", src: &syntaxv1.Message{ Double: 111, Float: 111, Bool: true, String_: "111", Bytes: []byte{111}, }, dst: &syntaxv1.Message{ Double: 222, Float: 222, Bool: false, String_: "222", Bytes: []byte{222}, }, }, { name: "repeated", src: &syntaxv1.Message{ RepeatedDouble: []float64{111}, RepeatedFloat: []float32{111}, RepeatedBool: []bool{true}, RepeatedString: []string{"111"}, RepeatedBytes: [][]byte{{111}}, }, dst: &syntaxv1.Message{ RepeatedDouble: []float64{222}, RepeatedFloat: []float32{222}, RepeatedBool: []bool{false}, RepeatedString: []string{"222"}, RepeatedBytes: [][]byte{{222}}, }, }, { name: "nested", src: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, dst: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "dst", Int64: 222, }, }, }, { name: "maps", src: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "src-key": { String_: "src-value", }, }, }, dst: &syntaxv1.Message{ MapStringString: map[string]string{ "dst-key": "dst-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "dst-key": { String_: "dst-value", }, }, }, }, { name: "oneof: swap", src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofString{ OneofString: "src", }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, }, { name: "oneof: message swap", src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() srcClone := proto.Clone(tt.src) Update(&fieldmaskpb.FieldMask{Paths: []string{"*"}}, tt.dst, tt.src) assert.DeepEqual(t, srcClone, tt.dst, protocmp.Transform()) }) } }) t.Run("wire set fields", func(t *testing.T) { t.Parallel() for _, tt := range []struct { name string src proto.Message dst proto.Message expected proto.Message }{ { name: "scalars", src: &syntaxv1.Message{ Double: 111, Float: 111, }, dst: &syntaxv1.Message{ Double: 222, Float: 222, Bool: false, String_: "222", Bytes: []byte{222}, }, expected: &syntaxv1.Message{ Double: 111, Float: 111, Bool: false, String_: "222", Bytes: []byte{222}, }, }, { name: "repeated", src: &syntaxv1.Message{ RepeatedDouble: []float64{111}, }, dst: &syntaxv1.Message{ RepeatedDouble: []float64{222}, RepeatedFloat: []float32{222}, RepeatedBool: []bool{false}, RepeatedString: []string{"222"}, RepeatedBytes: [][]byte{{222}}, }, expected: &syntaxv1.Message{ RepeatedDouble: []float64{111}, RepeatedFloat: []float32{222}, RepeatedBool: []bool{false}, RepeatedString: []string{"222"}, RepeatedBytes: [][]byte{{222}}, }, }, { name: "nested", src: &syntaxv1.Message{ Message: &syntaxv1.Message{String_: "src"}, }, dst: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "dst", Int64: 222, }, }, expected: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", Int64: 222, }, }, }, { name: "nested: dst nil", src: &syntaxv1.Message{ Message: &syntaxv1.Message{String_: "src"}, }, dst: &syntaxv1.Message{ Message: nil, }, expected: &syntaxv1.Message{ Message: &syntaxv1.Message{String_: "src"}, }, }, { name: "maps", src: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "src-key": {String_: "src-value"}, }, }, dst: &syntaxv1.Message{ MapStringString: map[string]string{ "dst-key": "dst-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "dst-key": {String_: "dst-value"}, }, }, expected: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "src-key": {String_: "src-value"}, }, }, }, { name: "maps: dst nil", src: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "src-key": {String_: "src-value"}, }, }, dst: &syntaxv1.Message{ MapStringString: nil, MapStringMessage: nil, }, expected: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "src-key": {String_: "src-value"}, }, }, }, { name: "oneof", src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{String_: "src"}, }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "dst", Int64: 222, }, }, }, expected: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", Int64: 222, }, }, }, }, { name: "oneof: kind swap", src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofString{ OneofString: "src", }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, expected: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofString{ OneofString: "src", }, }, }, { name: "oneof: message swap", src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, expected: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() Update(nil, tt.dst, tt.src) assert.DeepEqual(t, tt.expected, tt.dst, protocmp.Transform()) }) } }) t.Run("paths", func(t *testing.T) { t.Parallel() for _, tt := range []struct { name string paths []string src proto.Message dst proto.Message expected proto.Message }{ { name: "scalars", paths: []string{ "double", "bytes", }, src: &syntaxv1.Message{ Double: 111, Float: 111, Bytes: []byte{111}, }, dst: &syntaxv1.Message{ Double: 222, Float: 222, Bool: false, String_: "222", Bytes: []byte{222}, }, expected: &syntaxv1.Message{ Double: 111, Float: 222, Bytes: []byte{111}, Bool: false, String_: "222", }, }, { name: "repeated scalar", paths: []string{ "repeated_double", "repeated_string", }, src: &syntaxv1.Message{ RepeatedDouble: []float64{111}, RepeatedFloat: []float32{111}, }, dst: &syntaxv1.Message{ RepeatedDouble: []float64{222}, RepeatedString: []string{"222"}, RepeatedBytes: [][]byte{{222}}, }, expected: &syntaxv1.Message{ RepeatedDouble: []float64{111}, RepeatedBytes: [][]byte{{222}}, }, }, { name: "repeated message", paths: []string{ "repeated_message", }, src: &syntaxv1.Message{ RepeatedMessage: []*syntaxv1.Message{ {String_: "src"}, {Int64: 111}, }, }, dst: &syntaxv1.Message{ RepeatedMessage: []*syntaxv1.Message{ {Int64: 222}, {String_: "dst"}, }, }, expected: &syntaxv1.Message{ RepeatedMessage: []*syntaxv1.Message{ {String_: "src"}, {Int64: 111}, }, }, }, { // can not update individual fields in a repeated message name: "repeated message: deep", paths: []string{ "repeated_message.string", }, src: &syntaxv1.Message{ RepeatedMessage: []*syntaxv1.Message{ {String_: "src"}, {Int64: 111}, }, }, dst: &syntaxv1.Message{ RepeatedMessage: []*syntaxv1.Message{ {Int64: 222}, {String_: "dst"}, }, }, expected: &syntaxv1.Message{ RepeatedMessage: []*syntaxv1.Message{ {Int64: 222}, {String_: "dst"}, }, }, }, { name: "nested", paths: []string{ "message", }, src: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, dst: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "dst", Int64: 222, }, }, expected: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, }, { name: "nested: deep", paths: []string{ "message.string", }, src: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, dst: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "dst", Int64: 222, }, }, expected: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", Int64: 222, }, }, }, { name: "nested: dst nil", paths: []string{ "message", }, src: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, dst: &syntaxv1.Message{ Message: nil, }, expected: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, }, { name: "nested: deep, dst nil", paths: []string{ "message.string", }, src: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, dst: &syntaxv1.Message{ Message: nil, }, expected: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, }, { name: "nested: deep, src nil", paths: []string{ "message.string", }, src: &syntaxv1.Message{ Message: nil, }, dst: &syntaxv1.Message{ Message: &syntaxv1.Message{ String_: "src", }, }, expected: &syntaxv1.Message{ Message: &syntaxv1.Message{}, }, }, { name: "maps", paths: []string{ "map_string_string", }, src: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "src-key": {String_: "src-value"}, }, }, dst: &syntaxv1.Message{ MapStringString: map[string]string{ "dst-key": "dst-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "dst-key": {String_: "dst-value"}, }, }, expected: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "dst-key": {String_: "dst-value"}, }, }, }, { // can not update individual entries in a map name: "maps: deep", paths: []string{ "map_string_string.src1", }, src: &syntaxv1.Message{ MapStringString: map[string]string{ "src1": "src1-value", "src2": "src2-value", }, }, dst: &syntaxv1.Message{ MapStringString: map[string]string{ "dst-key": "dst-value", }, }, expected: &syntaxv1.Message{ MapStringString: map[string]string{ "dst-key": "dst-value", }, }, }, { name: "maps: dst nil", paths: []string{ "map_string_string", }, src: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "src-key": {String_: "src-value"}, }, }, dst: &syntaxv1.Message{}, expected: &syntaxv1.Message{ MapStringString: map[string]string{ "src-key": "src-value", }, }, }, { name: "maps: src nil", paths: []string{ "map_string_string", }, src: &syntaxv1.Message{}, dst: &syntaxv1.Message{ MapStringString: map[string]string{ "dst-key": "dst-value", }, MapStringMessage: map[string]*syntaxv1.Message{ "dst-key": {String_: "dst-value"}, }, }, expected: &syntaxv1.Message{ MapStringMessage: map[string]*syntaxv1.Message{ "dst-key": {String_: "dst-value"}, }, }, }, { name: "oneof", paths: []string{ "oneof_message1", }, src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "dst", Int64: 222, }, }, }, expected: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, }, { name: "oneof: kind swap", paths: []string{ "oneof_string", }, src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofString{ OneofString: "src", }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, expected: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofString{ OneofString: "src", }, }, }, { name: "oneof: kind swap src nil", paths: []string{ "oneof_message2", }, src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofString{ OneofString: "src", }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, expected: &syntaxv1.Message{ Oneof: nil, }, }, { name: "oneof: deep", paths: []string{ "oneof_message1.string", }, src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, expected: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, }, { name: "oneof: deep src nil", paths: []string{ "oneof_message2.string", }, src: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage1{ OneofMessage1: &syntaxv1.Message{ String_: "src", }, }, }, dst: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{ String_: "dst", }, }, }, expected: &syntaxv1.Message{ Oneof: &syntaxv1.Message_OneofMessage2{ OneofMessage2: &syntaxv1.Message{}, }, }, }, { name: "message: src nil", paths: []string{ "message", }, src: &syntaxv1.Message{ Message: nil, }, dst: &syntaxv1.Message{ Message: &syntaxv1.Message{ Int32: 23, }, }, expected: &syntaxv1.Message{ Message: nil, }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() Update(&fieldmaskpb.FieldMask{Paths: tt.paths}, tt.dst, tt.src) assert.DeepEqual(t, tt.expected, tt.dst, protocmp.Transform()) }) } }) } aip-go-0.80.0/fieldmask/validate.go000066400000000000000000000040501513342120500170250ustar00rootroot00000000000000package fieldmask import ( "fmt" "strings" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/fieldmaskpb" ) // Validate validates that the paths in the provided field mask are syntactically valid and // refer to known fields in the specified message type. func Validate(fm *fieldmaskpb.FieldMask, m proto.Message) error { // special case for '*' if stringsContain(WildcardPath, fm.GetPaths()) { if len(fm.GetPaths()) != 1 { return fmt.Errorf("invalid field path: '*' must not be used with other paths") } return nil } md0 := m.ProtoReflect().Descriptor() for _, path := range fm.GetPaths() { md := md0 if !rangeFields(path, func(field string) bool { // Search the field within the message. if md == nil { return false // not within a message } fd := md.Fields().ByName(protoreflect.Name(field)) // The real field name of a group is the message name. if fd == nil { gd := md.Fields().ByName(protoreflect.Name(strings.ToLower(field))) if gd != nil && gd.Kind() == protoreflect.GroupKind && string(gd.Message().Name()) == field { fd = gd } } else if fd.Kind() == protoreflect.GroupKind && string(fd.Message().Name()) != field { fd = nil } if fd == nil { return false // message has does not have this field } // Identify the next message to search within. md = fd.Message() // may be nil if fd.IsMap() { md = fd.MapValue().Message() // may be nil } return true }) { return fmt.Errorf("invalid field path: %s", path) } } return nil } func stringsContain(str string, ss []string) bool { for _, s := range ss { if s == str { return true } } return false } func rangeFields(path string, f func(field string) bool) bool { for { var field string if i := strings.IndexByte(path, '.'); i >= 0 { field, path = path[:i], path[i:] } else { field, path = path, "" } if !f(field) { return false } if len(path) == 0 { return true } path = strings.TrimPrefix(path, ".") } } aip-go-0.80.0/fieldmask/validate_test.go000066400000000000000000000036131513342120500200700ustar00rootroot00000000000000package fieldmask import ( "testing" "google.golang.org/genproto/googleapis/example/library/v1" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/fieldmaskpb" "gotest.tools/v3/assert" ) func TestValidate(t *testing.T) { t.Parallel() for _, tt := range []struct { name string fieldMask *fieldmaskpb.FieldMask message proto.Message errorContains string }{ { name: "valid nil", message: &library.Book{}, }, { name: "valid *", fieldMask: &fieldmaskpb.FieldMask{Paths: []string{"*"}}, message: &library.Book{}, }, { name: "invalid *", fieldMask: &fieldmaskpb.FieldMask{Paths: []string{"*", "author"}}, message: &library.Book{}, errorContains: "invalid field path: '*' must not be used with other paths", }, { name: "valid empty", fieldMask: &fieldmaskpb.FieldMask{}, message: &library.Book{}, }, { name: "valid single", fieldMask: &fieldmaskpb.FieldMask{ Paths: []string{"name", "author"}, }, message: &library.Book{}, }, { name: "invalid single", fieldMask: &fieldmaskpb.FieldMask{ Paths: []string{"name", "foo"}, }, message: &library.Book{}, errorContains: "invalid field path: foo", }, { name: "valid nested", fieldMask: &fieldmaskpb.FieldMask{ Paths: []string{"parent", "book.name"}, }, message: &library.CreateBookRequest{}, }, { name: "invalid nested", fieldMask: &fieldmaskpb.FieldMask{ Paths: []string{"parent", "book.foo"}, }, message: &library.CreateBookRequest{}, errorContains: "invalid field path: book.foo", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() if tt.errorContains != "" { assert.ErrorContains(t, Validate(tt.fieldMask, tt.message), tt.errorContains) } else { assert.NilError(t, Validate(tt.fieldMask, tt.message)) } }) } } aip-go-0.80.0/fieldmask/wildcard.go000066400000000000000000000007161513342120500170320ustar00rootroot00000000000000package fieldmask import "google.golang.org/protobuf/types/known/fieldmaskpb" // WildcardPath is the path used for update masks that should perform a full replacement. const WildcardPath = "*" // IsFullReplacement reports whether a field mask contains the special wildcard path, // meaning full replacement (the equivalent of PUT). func IsFullReplacement(fm *fieldmaskpb.FieldMask) bool { return len(fm.GetPaths()) == 1 && fm.GetPaths()[0] == WildcardPath } aip-go-0.80.0/fieldmask/wildcard_test.go000066400000000000000000000007671513342120500200770ustar00rootroot00000000000000package fieldmask import ( "testing" "google.golang.org/protobuf/types/known/fieldmaskpb" "gotest.tools/v3/assert" ) func TestIsFullReplacement(t *testing.T) { t.Parallel() assert.Assert(t, IsFullReplacement(&fieldmaskpb.FieldMask{Paths: []string{WildcardPath}})) assert.Assert(t, !IsFullReplacement(&fieldmaskpb.FieldMask{Paths: []string{WildcardPath, "foo"}})) assert.Assert(t, !IsFullReplacement(&fieldmaskpb.FieldMask{Paths: []string{"foo"}})) assert.Assert(t, !IsFullReplacement(nil)) } aip-go-0.80.0/filtering/000077500000000000000000000000001513342120500147325ustar00rootroot00000000000000aip-go-0.80.0/filtering/checker.go000066400000000000000000000170521513342120500166720ustar00rootroot00000000000000package filtering import ( "fmt" "time" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/proto" ) type Checker struct { declarations *Declarations expr *expr.Expr sourceInfo *expr.SourceInfo typeMap map[int64]*expr.Type } func (c *Checker) Init(exp *expr.Expr, sourceInfo *expr.SourceInfo, declarations *Declarations) { *c = Checker{ expr: exp, declarations: declarations, sourceInfo: sourceInfo, typeMap: make(map[int64]*expr.Type, len(sourceInfo.GetPositions())), } } func (c *Checker) Check() (*expr.CheckedExpr, error) { if err := c.checkExpr(c.expr); err != nil { return nil, err } resultType, ok := c.getType(c.expr) if !ok { return nil, c.errorf(c.expr, "unknown result type") } if !proto.Equal(resultType, TypeBool) { return nil, c.errorf(c.expr, "non-bool result type") } return &expr.CheckedExpr{ TypeMap: c.typeMap, SourceInfo: c.sourceInfo, Expr: c.expr, }, nil } func (c *Checker) checkExpr(e *expr.Expr) error { if e == nil { return nil } switch e.GetExprKind().(type) { case *expr.Expr_ConstExpr: switch e.GetConstExpr().GetConstantKind().(type) { case *expr.Constant_BoolValue: return c.checkBoolLiteral(e) case *expr.Constant_DoubleValue: return c.checkDoubleLiteral(e) case *expr.Constant_Int64Value: return c.checkInt64Literal(e) case *expr.Constant_StringValue: return c.checkStringLiteral(e) default: return c.errorf(e, "unsupported constant kind") } case *expr.Expr_IdentExpr: return c.checkIdentExpr(e) case *expr.Expr_SelectExpr: return c.checkSelectExpr(e) case *expr.Expr_CallExpr: return c.checkCallExpr(e) default: return c.errorf(e, "unsupported expr kind") } } func (c *Checker) checkIdentExpr(e *expr.Expr) error { identExpr := e.GetIdentExpr() ident, ok := c.declarations.LookupIdent(identExpr.GetName()) if !ok { return c.errorf(e, "undeclared identifier '%s'", identExpr.GetName()) } if err := c.setType(e, ident.GetIdent().GetType()); err != nil { return c.wrapf(err, e, "identifier '%s'", identExpr.GetName()) } return nil } func (c *Checker) checkSelectExpr(e *expr.Expr) (err error) { defer func() { if err != nil { err = c.wrapf(err, e, "check select expr") } }() if qualifiedName, ok := toQualifiedName(e); ok { if ident, ok := c.declarations.LookupIdent(qualifiedName); ok { return c.setType(e, ident.GetIdent().GetType()) } } selectExpr := e.GetSelectExpr() if selectExpr.GetOperand() == nil { return c.errorf(e, "missing operand") } if err := c.checkExpr(selectExpr.GetOperand()); err != nil { return err } operandType, ok := c.getType(selectExpr.GetOperand()) if !ok { return c.errorf(e, "failed to get operand type") } switch operandType.GetTypeKind().(type) { case *expr.Type_MapType_: return c.setType(e, operandType.GetMapType().GetValueType()) default: return c.errorf(e, "unsupported operand type") } } func (c *Checker) checkCallExpr(e *expr.Expr) (err error) { defer func() { if err != nil { err = c.wrapf(err, e, "check call expr") } }() callExpr := e.GetCallExpr() for _, arg := range callExpr.GetArgs() { if err := c.checkExpr(arg); err != nil { return err } } functionDeclaration, ok := c.declarations.LookupFunction(callExpr.GetFunction()) if !ok { return c.errorf(e, "undeclared function '%s'", callExpr.GetFunction()) } functionOverload, err := c.resolveCallExprFunctionOverload(e, functionDeclaration) if err != nil { return err } if err := c.checkCallExprBuiltinFunctionOverloads(e, functionOverload); err != nil { return err } return c.setType(e, functionOverload.GetResultType()) } func (c *Checker) resolveCallExprFunctionOverload( e *expr.Expr, functionDeclaration *expr.Decl, ) (*expr.Decl_FunctionDecl_Overload, error) { callExpr := e.GetCallExpr() for _, overload := range functionDeclaration.GetFunction().GetOverloads() { if len(callExpr.GetArgs()) != len(overload.GetParams()) { continue } if len(overload.GetTypeParams()) == 0 { allTypesMatch := true for i, param := range overload.GetParams() { argType, ok := c.getType(callExpr.GetArgs()[i]) if !ok { return nil, c.errorf(callExpr.GetArgs()[i], "unknown type") } if !proto.Equal(argType, param) { allTypesMatch = false break } } if allTypesMatch { return overload, nil } } // TODO: Add support for type parameters. } var argTypes []string for _, arg := range callExpr.GetArgs() { t, ok := c.getType(arg) if !ok { argTypes = append(argTypes, "UNKNOWN") } else { argTypes = append(argTypes, t.String()) } } return nil, c.errorf(e, "no matching overload found for calling '%s' with %s", callExpr.GetFunction(), argTypes) } func (c *Checker) checkCallExprBuiltinFunctionOverloads( e *expr.Expr, functionOverload *expr.Decl_FunctionDecl_Overload, ) error { callExpr := e.GetCallExpr() switch functionOverload.GetOverloadId() { case FunctionOverloadTimestampString: if constExpr := callExpr.GetArgs()[0].GetConstExpr(); constExpr != nil { if _, err := time.Parse(time.RFC3339, constExpr.GetStringValue()); err != nil { return c.errorf(callExpr.GetArgs()[0], "invalid timestamp. Should be in RFC3339 format") } } case FunctionOverloadDurationString: if constExpr := callExpr.GetArgs()[0].GetConstExpr(); constExpr != nil { if _, err := time.ParseDuration(constExpr.GetStringValue()); err != nil { return c.errorf(callExpr.GetArgs()[0], "invalid duration") } } case FunctionOverloadLessThanTimestampString, FunctionOverloadGreaterThanTimestampString, FunctionOverloadLessEqualsTimestampString, FunctionOverloadGreaterEqualsTimestampString, FunctionOverloadEqualsTimestampString, FunctionOverloadNotEqualsTimestampString: if constExpr := callExpr.GetArgs()[1].GetConstExpr(); constExpr != nil { if _, err := time.Parse(time.RFC3339, constExpr.GetStringValue()); err != nil { return c.errorf(callExpr.GetArgs()[0], "invalid timestamp. Should be in RFC3339 format") } } } return nil } func (c *Checker) checkInt64Literal(e *expr.Expr) error { return c.setType(e, TypeInt) } func (c *Checker) checkStringLiteral(e *expr.Expr) error { return c.setType(e, TypeString) } func (c *Checker) checkDoubleLiteral(e *expr.Expr) error { return c.setType(e, TypeFloat) } func (c *Checker) checkBoolLiteral(e *expr.Expr) error { return c.setType(e, TypeBool) } func (c *Checker) errorf(_ *expr.Expr, format string, args ...interface{}) error { // TODO: Include the provided expr. return &typeError{ message: fmt.Sprintf(format, args...), } } func (c *Checker) wrapf(err error, _ *expr.Expr, format string, args ...interface{}) error { // TODO: Include the provided expr. return &typeError{ message: fmt.Sprintf(format, args...), err: err, } } func (c *Checker) setType(e *expr.Expr, t *expr.Type) error { if existingT, ok := c.typeMap[e.GetId()]; ok && !proto.Equal(t, existingT) { return c.errorf(e, "type conflict between %s and %s", t, existingT) } c.typeMap[e.GetId()] = t return nil } func (c *Checker) getType(e *expr.Expr) (*expr.Type, bool) { t, ok := c.typeMap[e.GetId()] if !ok { return nil, false } return t, true } func toQualifiedName(e *expr.Expr) (string, bool) { switch kind := e.GetExprKind().(type) { case *expr.Expr_IdentExpr: return kind.IdentExpr.GetName(), true case *expr.Expr_SelectExpr: if kind.SelectExpr.GetTestOnly() { return "", false } parent, ok := toQualifiedName(kind.SelectExpr.GetOperand()) if !ok { return "", false } return parent + "." + kind.SelectExpr.GetField(), true default: return "", false } } aip-go-0.80.0/filtering/checker_test.go000066400000000000000000000253511513342120500177320ustar00rootroot00000000000000package filtering import ( "testing" syntaxv1 "go.einride.tech/aip/proto/gen/einride/example/syntax/v1" "gotest.tools/v3/assert" ) func TestChecker(t *testing.T) { t.Parallel() for _, tt := range []struct { filter string declarations []DeclarationOption errorContains string }{ { filter: "New York Giants", declarations: []DeclarationOption{ DeclareIdent("New", TypeBool), DeclareIdent("York", TypeBool), DeclareIdent("Giants", TypeBool), }, errorContains: "undeclared function 'FUZZY'", }, { filter: "New York Giants OR Yankees", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("New", TypeBool), DeclareIdent("York", TypeBool), DeclareIdent("Giants", TypeBool), DeclareIdent("Yankees", TypeBool), }, errorContains: "undeclared function 'FUZZY'", }, { filter: "New York (Giants OR Yankees)", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("New", TypeBool), DeclareIdent("York", TypeBool), DeclareIdent("Giants", TypeBool), DeclareIdent("Yankees", TypeBool), }, errorContains: "undeclared function 'FUZZY'", }, { filter: "a b AND c AND d", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("a", TypeBool), DeclareIdent("b", TypeBool), DeclareIdent("c", TypeBool), DeclareIdent("d", TypeBool), }, errorContains: "undeclared function 'FUZZY'", }, { filter: "a", declarations: []DeclarationOption{ DeclareIdent("a", TypeBool), }, }, { filter: "(a b) AND c AND d", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("a", TypeBool), DeclareIdent("b", TypeBool), DeclareIdent("c", TypeBool), DeclareIdent("d", TypeBool), }, errorContains: "undeclared function 'FUZZY'", }, { filter: `author = "Karin Boye" AND NOT read`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("author", TypeString), DeclareIdent("read", TypeBool), }, }, { filter: "a < 10 OR a >= 100", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("a", TypeInt), }, }, { filter: "NOT (a OR b)", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("a", TypeBool), DeclareIdent("b", TypeBool), }, }, { filter: `-file:".java"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("file", TypeString), }, }, { filter: "-30", errorContains: "non-bool result type", }, { filter: "package=com.google", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("package", TypeString), DeclareIdent("com", TypeMap(TypeString, TypeString)), }, }, { filter: `msg != 'hello'`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("msg", TypeString), }, }, { filter: `1 > 0`, declarations: []DeclarationOption{ DeclareStandardFunctions(), }, }, { filter: `2.5 >= 2.4`, declarations: []DeclarationOption{ DeclareStandardFunctions(), }, }, { filter: `foo >= -2.4`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("foo", TypeFloat), }, }, { filter: `foo >= (-2.4)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("foo", TypeFloat), }, }, { filter: `-2.5 >= -2.4`, declarations: []DeclarationOption{ DeclareStandardFunctions(), }, }, { filter: `yesterday < request.time`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("yesterday", TypeTimestamp), }, // TODO: Add support for structs. errorContains: "undeclared identifier 'request'", }, { filter: `experiment.rollout <= cohort(request.user)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareFunction("cohort", NewFunctionOverload("cohort_string", TypeFloat, TypeString)), }, // TODO: Add support for structs. errorContains: "undeclared identifier 'experiment'", }, { filter: `prod`, declarations: []DeclarationOption{ DeclareIdent("prod", TypeBool), }, }, { filter: `expr.type_map.1.type`, // TODO: Add support for structs. errorContains: "undeclared identifier 'expr'", }, { filter: `regex(m.key, '^.*prod.*$')`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("m", TypeMap(TypeString, TypeString)), DeclareFunction("regex", NewFunctionOverload("regex_string", TypeBool, TypeString, TypeString)), }, }, { filter: `math.mem('30mb')`, declarations: []DeclarationOption{ DeclareFunction("math.mem", NewFunctionOverload("math.mem_string", TypeBool, TypeString)), }, }, { filter: `(msg.endsWith('world') AND retries < 10)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("retries", TypeInt), }, errorContains: "undeclared function 'msg.endsWith'", }, { filter: `(endsWith(msg, 'world') AND retries < 10)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareFunction("endsWith", NewFunctionOverload("endsWith_string", TypeBool, TypeString, TypeString)), DeclareIdent("retries", TypeInt), DeclareIdent("msg", TypeString), }, }, { filter: "expire_time > time.now()", declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareFunction("time.now", NewFunctionOverload("time.now", TypeTimestamp)), DeclareIdent("expire_time", TypeTimestamp), }, }, { filter: `time.now() > timestamp("2012-04-21T11:30:00-04:00")`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareFunction("time.now", NewFunctionOverload("time.now", TypeTimestamp)), }, }, { filter: `time.now() > timestamp("INVALID")`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareFunction("time.now", NewFunctionOverload("time.now", TypeTimestamp)), }, errorContains: "invalid timestamp", }, { filter: `ttl > duration("30s")`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("ttl", TypeDuration), }, }, { filter: `ttl > duration("INVALID")`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("ttl", TypeDuration), }, errorContains: "invalid duration", }, { filter: `ttl > duration(input_field)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("ttl", TypeDuration), DeclareIdent("input_field", TypeString), }, }, { filter: `create_time > timestamp("2006-01-02T15:04:05+07:00")`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: `create_time > timestamp("2006-01-02T15:04:05+07:00")`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: ` start_time > timestamp("2006-01-02T15:04:05+07:00") AND (driver = "driver1" OR start_driver = "driver1" OR end_driver = "driver1") `, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("start_time", TypeTimestamp), DeclareIdent("driver", TypeString), DeclareIdent("start_driver", TypeString), DeclareIdent("end_driver", TypeString), }, }, { filter: `annotations:schedule`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("annotations", TypeMap(TypeString, TypeString)), }, }, { filter: `annotations.schedule = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("annotations", TypeMap(TypeString, TypeString)), }, }, { filter: `enum = ENUM_ONE`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareEnumIdent("enum", syntaxv1.Enum(0).Type()), }, }, { filter: `enum = ENUM_ONE AND NOT enum2 = ENUM_TWO`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareEnumIdent("enum", syntaxv1.Enum(0).Type()), DeclareEnumIdent("enum2", syntaxv1.Enum(0).Type()), }, }, { filter: `create_time = "2022-08-12 22:22:22"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, errorContains: "invalid timestamp. Should be in RFC3339 format", }, { filter: `create_time = "2022-08-12T22:22:22+01:00"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: `create_time != "2022-08-12T22:22:22+01:00"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: `create_time < "2022-08-12T22:22:22+01:00"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: `create_time > "2022-08-12T22:22:22+01:00"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: `create_time <= "2022-08-12T22:22:22+01:00"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: `create_time >= "2022-08-12T22:22:22+01:00"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("create_time", TypeTimestamp), }, }, { filter: "<", errorContains: "unexpected token <", }, { filter: `(-2.5) >= -2.4`, errorContains: "unexpected token >=", }, { filter: `a = "foo`, errorContains: "unterminated string", }, { filter: "invalid = foo\xa0\x01bar", errorContains: "invalid UTF-8", }, } { t.Run(tt.filter, func(t *testing.T) { t.Parallel() var parser Parser parser.Init(tt.filter) parsedExpr, err := parser.Parse() if err != nil && tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) return } assert.NilError(t, err) declarations, err := NewDeclarations(tt.declarations...) if err != nil && tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) return } assert.NilError(t, err) var checker Checker checker.Init(parsedExpr.GetExpr(), parsedExpr.GetSourceInfo(), declarations) checkedExpr, err := checker.Check() if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) return } assert.NilError(t, err) assert.Assert(t, checkedExpr != nil) }) } } aip-go-0.80.0/filtering/declarations.go000066400000000000000000000157151513342120500177420ustar00rootroot00000000000000package filtering import ( "fmt" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" ) // NewStringConstant creates a new string constant. func NewStringConstant(value string) *expr.Constant { return &expr.Constant{ ConstantKind: &expr.Constant_StringValue{ StringValue: value, }, } } // NewFunctionDeclaration creates a new function declaration. func NewFunctionDeclaration(name string, overloads ...*expr.Decl_FunctionDecl_Overload) *expr.Decl { return &expr.Decl{ Name: name, DeclKind: &expr.Decl_Function{ Function: &expr.Decl_FunctionDecl{ Overloads: overloads, }, }, } } // NewFunctionOverload creates a new function overload. func NewFunctionOverload(id string, result *expr.Type, params ...*expr.Type) *expr.Decl_FunctionDecl_Overload { return &expr.Decl_FunctionDecl_Overload{ OverloadId: id, ResultType: result, Params: params, } } // NewIdentDeclaration creates a new ident declaration. func NewIdentDeclaration(name string, identType *expr.Type) *expr.Decl { return &expr.Decl{ Name: name, DeclKind: &expr.Decl_Ident{ Ident: &expr.Decl_IdentDecl{ Type: identType, }, }, } } // NewConstantDeclaration creates a new constant ident declaration. func NewConstantDeclaration(name string, constantType *expr.Type, constantValue *expr.Constant) *expr.Decl { return &expr.Decl{ Name: name, DeclKind: &expr.Decl_Ident{ Ident: &expr.Decl_IdentDecl{ Type: constantType, Value: constantValue, }, }, } } // Declarations contain declarations for type-checking filter expressions. type Declarations struct { idents map[string]*expr.Decl functions map[string]*expr.Decl enums map[string]protoreflect.EnumType } // DeclarationOption configures Declarations. type DeclarationOption func(*Declarations) error // DeclareStandardFunction is a DeclarationOption that declares all standard functions and their overloads. func DeclareStandardFunctions() DeclarationOption { return func(declarations *Declarations) error { for _, declaration := range StandardFunctionDeclarations() { if err := declarations.declare(declaration); err != nil { return err } } return nil } } // DeclareFunction is a DeclarationOption that declares a single function and its overloads. func DeclareFunction(name string, overloads ...*expr.Decl_FunctionDecl_Overload) DeclarationOption { return func(declarations *Declarations) error { return declarations.declareFunction(name, overloads...) } } // DeclareIdent is a DeclarationOption that declares a single ident. func DeclareIdent(name string, t *expr.Type) DeclarationOption { return func(declarations *Declarations) error { return declarations.declareIdent(name, t) } } func DeclareEnumIdent(name string, enumType protoreflect.EnumType) DeclarationOption { return func(declarations *Declarations) error { return declarations.declareEnumIdent(name, enumType) } } // NewDeclarations creates a new set of Declarations for filter expression type-checking. func NewDeclarations(opts ...DeclarationOption) (*Declarations, error) { d := &Declarations{ idents: make(map[string]*expr.Decl), functions: make(map[string]*expr.Decl), enums: make(map[string]protoreflect.EnumType), } for _, opt := range opts { if err := opt(d); err != nil { return nil, err } } return d, nil } func (d *Declarations) LookupIdent(name string) (*expr.Decl, bool) { result, ok := d.idents[name] return result, ok } func (d *Declarations) LookupFunction(name string) (*expr.Decl, bool) { result, ok := d.functions[name] return result, ok } func (d *Declarations) LookupEnumIdent(name string) (protoreflect.EnumType, bool) { result, ok := d.enums[name] return result, ok } func (d *Declarations) declareIdent(name string, t *expr.Type) error { newIdent := NewIdentDeclaration(name, t) if ident, ok := d.idents[name]; ok && !proto.Equal(newIdent, ident) { return fmt.Errorf("redeclaration of %s", name) } d.idents[name] = NewIdentDeclaration(name, t) return nil } func (d *Declarations) declareConstant(name string, constantType *expr.Type, constantValue *expr.Constant) error { constantDecl := NewConstantDeclaration(name, constantType, constantValue) if existingIdent, ok := d.idents[name]; ok { if !proto.Equal(constantDecl, existingIdent) { return fmt.Errorf("redeclaration of %s", name) } return nil } d.idents[name] = constantDecl return nil } func (d *Declarations) declareEnumIdent(name string, enumType protoreflect.EnumType) error { if _, ok := d.enums[name]; ok { return fmt.Errorf("redeclaration of %s", name) } d.enums[name] = enumType enumIdentType := TypeEnum(enumType) if err := d.declareIdent(name, enumIdentType); err != nil { return err } for _, fn := range []string{ FunctionEquals, FunctionNotEquals, } { if err := d.declareFunction( fn, NewFunctionOverload(fn+"_"+enumIdentType.GetMessageType(), TypeBool, enumIdentType, enumIdentType), ); err != nil { return err } } values := enumType.Descriptor().Values() for i := 0; i < values.Len(); i++ { valueName := string(values.Get(i).Name()) if err := d.declareConstant(valueName, enumIdentType, NewStringConstant(valueName)); err != nil { return err } } return nil } func (d *Declarations) declareFunction(name string, overloads ...*expr.Decl_FunctionDecl_Overload) error { decl, ok := d.functions[name] if !ok { decl = NewFunctionDeclaration(name) d.functions[name] = decl } function := decl.GetFunction() NewOverloadLoop: for _, newOverload := range overloads { for _, existingOverload := range function.GetOverloads() { if newOverload.GetOverloadId() == existingOverload.GetOverloadId() { if !proto.Equal(newOverload, existingOverload) { return fmt.Errorf("redeclaration of overload %s", existingOverload.GetOverloadId()) } continue NewOverloadLoop } } function.Overloads = append(function.Overloads, newOverload) } return nil } func (d *Declarations) declare(decl *expr.Decl) error { switch decl.GetDeclKind().(type) { case *expr.Decl_Function: return d.declareFunction(decl.GetName(), decl.GetFunction().GetOverloads()...) case *expr.Decl_Ident: if decl.GetIdent().GetValue() != nil { return d.declareConstant(decl.GetName(), decl.GetIdent().GetType(), decl.GetIdent().GetValue()) } return d.declareIdent(decl.GetName(), decl.GetIdent().GetType()) default: return fmt.Errorf("unsupported declaration kind") } } // merge merges the given declarations into the current declarations. // Given declarations take precedence over current declarations in case // of conflicts (such as same identifier name but different type or same function name but different definition). func (d *Declarations) merge(decl *Declarations) { if decl == nil { return } for name, ident := range decl.idents { d.idents[name] = ident } for name, function := range decl.functions { d.functions[name] = function } for name, enum := range decl.enums { d.enums[name] = enum } } aip-go-0.80.0/filtering/doc.go000066400000000000000000000002151513342120500160240ustar00rootroot00000000000000// Package filtering provides primitives for implementing AIP filtering. // // See: https://google.aip.dev/160 (Filtering) package filtering aip-go-0.80.0/filtering/errors.go000066400000000000000000000032751513342120500166040ustar00rootroot00000000000000package filtering import ( "errors" "fmt" "strings" ) type filterError interface { Position() Position Message() string Filter() string } func appendFilterError(s *strings.Builder, err error) { var errFilter filterError if !errors.As(err, &errFilter) { return } _ = s.WriteByte('\n') _, _ = s.WriteString(errFilter.Filter()) _ = s.WriteByte('\n') for i := int32(1); i < errFilter.Position().Column; i++ { _ = s.WriteByte(' ') } _, _ = s.WriteString("^ ") _, _ = s.WriteString(errFilter.Message()) _, _ = s.WriteString(" (") _, _ = s.WriteString(errFilter.Position().String()) _, _ = s.WriteString(")\n") appendFilterError(s, errors.Unwrap(err)) } type lexError struct { filter string position Position message string } var _ filterError = &lexError{} func (l *lexError) Filter() string { return l.filter } func (l *lexError) Position() Position { return l.position } func (l *lexError) Message() string { return l.message } func (l *lexError) Error() string { return fmt.Sprintf("%s: %s", l.position, l.message) } type parseError struct { filter string position Position message string err error } func (p *parseError) Filter() string { return p.filter } func (p *parseError) Position() Position { return p.position } func (p *parseError) Message() string { return p.message } func (p *parseError) Unwrap() error { return p.err } func (p *parseError) Error() string { if p == nil { return "" } var b strings.Builder appendFilterError(&b, p) return b.String() } type typeError struct { message string err error } func (t *typeError) Error() string { if t.err != nil { return fmt.Sprintf("%s: %v", t.message, t.err) } return t.message } aip-go-0.80.0/filtering/expr.go000066400000000000000000000062341513342120500162440ustar00rootroot00000000000000package filtering import ( "time" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) func Not(arg *expr.Expr) *expr.Expr { return Function(FunctionNot, arg) } func Member(operand *expr.Expr, field string) *expr.Expr { return &expr.Expr{ ExprKind: &expr.Expr_SelectExpr{ SelectExpr: &expr.Expr_Select{ Operand: operand, Field: field, }, }, } } func Function(name string, args ...*expr.Expr) *expr.Expr { return &expr.Expr{ ExprKind: &expr.Expr_CallExpr{ CallExpr: &expr.Expr_Call{ Function: name, Args: args, }, }, } } func Float(value float64) *expr.Expr { return &expr.Expr{ ExprKind: &expr.Expr_ConstExpr{ ConstExpr: &expr.Constant{ ConstantKind: &expr.Constant_DoubleValue{ DoubleValue: value, }, }, }, } } func Duration(value time.Duration) *expr.Expr { return Function(FunctionDuration, String(value.String())) } func Timestamp(value time.Time) *expr.Expr { return Function(FunctionTimestamp, String(value.Format(time.RFC3339))) } func Int(value int64) *expr.Expr { return &expr.Expr{ ExprKind: &expr.Expr_ConstExpr{ ConstExpr: &expr.Constant{ ConstantKind: &expr.Constant_Int64Value{ Int64Value: value, }, }, }, } } func Equals(lhs, rhs *expr.Expr) *expr.Expr { return Function(FunctionEquals, lhs, rhs) } func NotEquals(lhs, rhs *expr.Expr) *expr.Expr { return Function(FunctionNotEquals, lhs, rhs) } func Has(lhs, rhs *expr.Expr) *expr.Expr { return Function(FunctionHas, lhs, rhs) } func Or(args ...*expr.Expr) *expr.Expr { if len(args) <= 2 { return Function(FunctionOr, args...) } result := Function(FunctionOr, args[:2]...) for _, arg := range args[2:] { result = Function(FunctionOr, result, arg) } return result } func And(args ...*expr.Expr) *expr.Expr { if len(args) <= 2 { return Function(FunctionAnd, args...) } result := Function(FunctionAnd, args[:2]...) for _, arg := range args[2:] { result = Function(FunctionAnd, result, arg) } return result } func LessThan(lhs, rhs *expr.Expr) *expr.Expr { return Function(FunctionLessThan, lhs, rhs) } func LessEquals(lhs, rhs *expr.Expr) *expr.Expr { return Function(FunctionLessEquals, lhs, rhs) } func GreaterEquals(lhs, rhs *expr.Expr) *expr.Expr { return Function(FunctionGreaterEquals, lhs, rhs) } func GreaterThan(lhs, rhs *expr.Expr) *expr.Expr { return Function(FunctionGreaterThan, lhs, rhs) } func Text(text string) *expr.Expr { return &expr.Expr{ ExprKind: &expr.Expr_IdentExpr{ IdentExpr: &expr.Expr_Ident{ Name: text, }, }, } } func String(s string) *expr.Expr { return &expr.Expr{ ExprKind: &expr.Expr_ConstExpr{ ConstExpr: &expr.Constant{ ConstantKind: &expr.Constant_StringValue{ StringValue: s, }, }, }, } } func Expression(sequences ...*expr.Expr) *expr.Expr { return And(sequences...) } func Sequence(factors ...*expr.Expr) *expr.Expr { if len(factors) <= 2 { return Function(FunctionFuzzyAnd, factors...) } result := Function(FunctionFuzzyAnd, factors[:2]...) for _, arg := range factors[2:] { result = Function(FunctionFuzzyAnd, result, arg) } return result } func Factor(terms ...*expr.Expr) *expr.Expr { return Or(terms...) } aip-go-0.80.0/filtering/exprs/000077500000000000000000000000001513342120500160735ustar00rootroot00000000000000aip-go-0.80.0/filtering/exprs/doc.go000066400000000000000000000001321513342120500171630ustar00rootroot00000000000000// Package exprs provides primitives for working with v1alpha1/expr values. package exprs aip-go-0.80.0/filtering/exprs/match.go000066400000000000000000000105441513342120500175220ustar00rootroot00000000000000package exprs import ( expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) // Matcher returns true if the expr matches the predicate. type Matcher func(exp *expr.Expr) bool // MatchString matches an expr.Constant_StringValue with an // exact value. func MatchString(s string) Matcher { var s2 string m := MatchAnyString(&s2) return func(exp *expr.Expr) bool { return m(exp) && s == s2 } } // MatchAnyString matches an expr.Constant_StringValue with any // value. The value of the expr is populated in argument value. func MatchAnyString(value *string) Matcher { return func(exp *expr.Expr) bool { cons := exp.GetConstExpr() if cons == nil { return false } if _, ok := cons.GetConstantKind().(*expr.Constant_StringValue); !ok { return false } *value = cons.GetStringValue() return true } } // MatchFloat matches an expr.Constant_DoubleValue with an exact value. func MatchFloat(value float64) Matcher { var f2 float64 m := MatchAnyFloat(&f2) return func(exp *expr.Expr) bool { return m(exp) && value == f2 } } // MatchAnyFloat matches an expr.Constant_DoubleValue with any value. // The value of the expr is populated in argument value. func MatchAnyFloat(value *float64) Matcher { return func(exp *expr.Expr) bool { cons := exp.GetConstExpr() if cons == nil { return false } if _, ok := cons.GetConstantKind().(*expr.Constant_DoubleValue); !ok { return false } *value = cons.GetDoubleValue() return true } } // MatchInt matches an expr.Constant_Int64Value with an exact value. func MatchInt(value int64) Matcher { var i2 int64 m := MatchAnyInt(&i2) return func(exp *expr.Expr) bool { return m(exp) && value == i2 } } // MatchAnyInt matches an expr.Constant_Int64Value with any value. // The value of the expr is populated in argument value. func MatchAnyInt(value *int64) Matcher { return func(exp *expr.Expr) bool { cons := exp.GetConstExpr() if cons == nil { return false } if _, ok := cons.GetConstantKind().(*expr.Constant_Int64Value); !ok { return false } *value = cons.GetInt64Value() return true } } // MatchText matches an expr.Expr_Ident where the name // of the ident matches an exact value. func MatchText(text string) Matcher { var t2 string m := MatchAnyText(&t2) return func(exp *expr.Expr) bool { return m(exp) && text == t2 } } // MatchAnyText matches an expr.Expr_Ident with any name. // The name of the expr is populated in argument text. func MatchAnyText(text *string) Matcher { return func(exp *expr.Expr) bool { ident := exp.GetIdentExpr() if ident == nil { return false } *text = ident.GetName() return true } } // MatchMember matches an expr.Expr_Select where the operand matches // the argument operand, and the field matches argument field. func MatchMember(operand Matcher, field string) Matcher { var f2 string m := MatchAnyMember(operand, &f2) return func(exp *expr.Expr) bool { return m(exp) && field == f2 } } // MatchAnyMember matches an expr.Expr_Select where the operand matches // the argument operand. The field of the expr is populated in argument field. func MatchAnyMember(operand Matcher, field *string) Matcher { return func(exp *expr.Expr) bool { sel := exp.GetSelectExpr() if sel == nil { return false } if !operand(sel.GetOperand()) { return false } *field = sel.GetField() return true } } // MatchFunction matches an expr.Expr_Call where the name of the // expr matches argument name, and arguments of the function matches // the provided args (length must match). func MatchFunction(name string, args ...Matcher) Matcher { var n2 string m := MatchAnyFunction(&n2, args...) return func(exp *expr.Expr) bool { return m(exp) && name == n2 } } // MatchAnyFunction matches an expr.Expr_Call where the provided args // matches the function arguments. The name of the function is populated // in argument name. func MatchAnyFunction(name *string, args ...Matcher) Matcher { return func(exp *expr.Expr) bool { call := exp.GetCallExpr() if call == nil { return false } if len(call.GetArgs()) != len(args) { return false } for i, a := range call.GetArgs() { if !args[i](a) { return false } } *name = call.GetFunction() return true } } // MatchAny matches any expr.Expr. The expr is populated in argument e. func MatchAny(e **expr.Expr) Matcher { return func(exp *expr.Expr) bool { *e = exp return true } } aip-go-0.80.0/filtering/exprs/match_example_test.go000066400000000000000000000025701513342120500222740ustar00rootroot00000000000000package exprs import ( "fmt" "go.einride.tech/aip/filtering" "go.einride.tech/aip/resourcename" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) func ExampleMatchFunction_validateResourceNames() { const pattern = "books/{book}" var walkErr error walkFn := func(currExpr, _ *expr.Expr) bool { var name string // match any function expression with name '=' and LHS 'name' matcher := MatchFunction(filtering.FunctionEquals, MatchText("name"), MatchAnyString(&name)) if matcher(currExpr) && !resourcename.Match(pattern, name) { walkErr = fmt.Errorf("expected resource name matching '%s' but got '%s'", pattern, name) return false } return true } // name = "not a resource name" or name = "books/2" invalidExpr := filtering.Or( filtering.Equals(filtering.Text("name"), filtering.String("not a resource name")), filtering.Equals(filtering.Text("name"), filtering.String("books/2")), ) filtering.Walk(walkFn, invalidExpr) fmt.Println(walkErr) // reset walkErr = nil // name = "books/1" or name = "books/2" validExpr := filtering.Or( filtering.Equals(filtering.Text("name"), filtering.String("books/1")), filtering.Equals(filtering.Text("name"), filtering.String("books/2")), ) filtering.Walk(walkFn, validExpr) fmt.Println(walkErr) // Output: // expected resource name matching 'books/{book}' but got 'not a resource name' // } aip-go-0.80.0/filtering/exprs/match_test.go000066400000000000000000000121771513342120500205650ustar00rootroot00000000000000package exprs import ( "testing" "go.einride.tech/aip/filtering" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/testing/protocmp" "gotest.tools/v3/assert" ) func TestMatch(t *testing.T) { t.Parallel() for _, tt := range []struct { name string expr *expr.Expr matcher Matcher expected bool }{ { name: "string: match", matcher: MatchString("string"), expr: filtering.String("string"), expected: true, }, { name: "string: another expr", matcher: MatchString("string"), expr: filtering.Text("state"), }, { name: "string: wrong string", matcher: MatchString("string"), expr: filtering.String("another string"), }, { name: "float: match", matcher: MatchFloat(3.14), expr: filtering.Float(3.14), expected: true, }, { name: "float: another expr", matcher: MatchFloat(3.14), expr: filtering.Text("state"), }, { name: "float: wrong float", matcher: MatchFloat(3.14), expr: filtering.Float(1.23), }, { name: "int: match", matcher: MatchInt(3), expr: filtering.Int(3), expected: true, }, { name: "int: another expr", matcher: MatchInt(3), expr: filtering.Text("state"), }, { name: "int: wrong int", matcher: MatchInt(3), expr: filtering.Int(1), }, { name: "text: match", matcher: MatchText("text"), expr: filtering.Text("text"), expected: true, }, { name: "text: another expr", matcher: MatchText("text"), expr: filtering.Text("state"), }, { name: "text: wrong text", matcher: MatchText("text"), expr: filtering.Text("another_text"), }, { name: "member: match", matcher: MatchMember(MatchText("operand"), "field"), expr: filtering.Member(filtering.Text("operand"), "field"), expected: true, }, { name: "member: another expr", matcher: MatchMember(MatchText("operand"), "field"), expr: filtering.Text("state"), }, { name: "member: wrong field", matcher: MatchMember(MatchText("operand"), "field"), expr: filtering.Member(filtering.Text("operand"), "another_field"), }, { name: "function: match", matcher: MatchFunction( "=", MatchString("lhs"), MatchString("rhs"), ), expr: filtering.Function( "=", filtering.String("lhs"), filtering.String("rhs"), ), expected: true, }, { name: "function: another expr", matcher: MatchFunction( "=", MatchString("lhs"), MatchString("rhs"), ), expr: filtering.Text("state"), }, { name: "function: wrong number of args", matcher: MatchFunction( "=", MatchString("lhs"), MatchString("rhs"), ), expr: filtering.Function( "=", filtering.String("lhs"), ), }, { name: "function: wrong name", matcher: MatchFunction( "=", MatchString("lhs"), MatchString("rhs"), ), expr: filtering.Function( "*", filtering.String("lhs"), filtering.String("rhs"), ), }, { name: "function: wrong number of args", matcher: MatchFunction( "=", MatchString("lhs"), MatchString("rhs"), ), expr: filtering.Function( "=", filtering.String("lhs"), ), }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() got := tt.matcher(tt.expr) assert.Equal(t, tt.expected, got) }) } } func TestMatchAny(t *testing.T) { t.Parallel() t.Run("String", func(t *testing.T) { t.Parallel() var val string matcher := MatchAnyString(&val) exp := filtering.String("x") assert.Check(t, matcher(exp)) assert.Equal(t, "x", val) }) t.Run("Float", func(t *testing.T) { t.Parallel() var val float64 matcher := MatchAnyFloat(&val) exp := filtering.Float(3.14) assert.Check(t, matcher(exp)) assert.Equal(t, 3.14, val) }) t.Run("Int", func(t *testing.T) { t.Parallel() var val int64 matcher := MatchAnyInt(&val) exp := filtering.Int(3) assert.Check(t, matcher(exp)) assert.Equal(t, int64(3), val) }) t.Run("Text", func(t *testing.T) { t.Parallel() var val string matcher := MatchAnyText(&val) exp := filtering.Text("x") assert.Check(t, matcher(exp)) assert.Equal(t, "x", val) }) t.Run("Member", func(t *testing.T) { t.Parallel() var operand, field string matcher := MatchAnyMember(MatchAnyText(&operand), &field) exp := filtering.Member(filtering.Text("operand"), "field") assert.Check(t, matcher(exp)) assert.Equal(t, "operand", operand) assert.Equal(t, "field", field) }) t.Run("Function", func(t *testing.T) { t.Parallel() var fn, lhs, rhs string matcher := MatchAnyFunction(&fn, MatchAnyString(&lhs), MatchAnyString(&rhs)) exp := filtering.Function("=", filtering.String("lhs"), filtering.String("rhs")) assert.Check(t, matcher(exp)) assert.Equal(t, "=", fn) assert.Equal(t, "lhs", lhs) assert.Equal(t, "rhs", rhs) }) t.Run("Any", func(t *testing.T) { t.Parallel() ex := &expr.Expr{} matcher := MatchAny(&ex) exp := filtering.Function("=", filtering.String("lhs"), filtering.String("rhs")) assert.Check(t, matcher(exp)) assert.DeepEqual(t, exp, ex, protocmp.Transform()) }) } aip-go-0.80.0/filtering/filter.go000066400000000000000000000017121513342120500165470ustar00rootroot00000000000000package filtering import ( expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) // Filter represents a parsed and type-checked filter. type Filter struct { CheckedExpr *expr.CheckedExpr declarations *Declarations } // ApplyMacros modifies the filter by applying the provided macros. // EXPERIMENTAL: This method is experimental and may be changed or removed in the future. func (f *Filter) ApplyMacros(macros ...Macro) error { declarationOptions, err := applyMacros( f.CheckedExpr.GetExpr(), f.CheckedExpr.GetSourceInfo(), f.declarations, macros..., ) if err != nil { return err } newDeclarations, err := NewDeclarations(declarationOptions...) if err != nil { return err } f.declarations.merge(newDeclarations) var checker Checker checker.Init(f.CheckedExpr.GetExpr(), f.CheckedExpr.GetSourceInfo(), f.declarations) checkedExpr, err := checker.Check() if err != nil { return err } f.CheckedExpr = checkedExpr return nil } aip-go-0.80.0/filtering/filter_test.go000066400000000000000000000134371513342120500176150ustar00rootroot00000000000000package filtering import ( "testing" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/testing/protocmp" "gotest.tools/v3/assert" ) func TestFilter_ApplyMacros(t *testing.T) { t.Parallel() for _, tt := range []struct { name string filter string declarations []DeclarationOption macros []Macro expected *expr.Expr errorContains string }{ { name: "identifier rename macro", filter: `name = "value"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil { return } if identExpr.GetName() != "name" { return } cursor.ReplaceWithDeclarations( Text("renamed_name"), []DeclarationOption{ DeclareIdent("renamed_name", TypeString), }) }, }, expected: Equals(Text("renamed_name"), String("value")), }, { name: "no-op macro that doesn't match", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { // Macro that only matches "other_name", so this should be a no-op identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "other_name" { return } cursor.Replace(Text("renamed")) }, }, expected: Equals(Text("name"), String("test")), }, { name: "multiple macros applied in sequence", filter: `x = 5 AND y = 10`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x", TypeInt), DeclareIdent("y", TypeInt), }, macros: []Macro{ // First macro: rename x to x_renamed func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "x" { return } cursor.ReplaceWithDeclarations(Text("x_renamed"), []DeclarationOption{ DeclareIdent("x_renamed", TypeInt), }) }, // Second macro: rename y to y_renamed func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "y" { return } cursor.ReplaceWithDeclarations(Text("y_renamed"), []DeclarationOption{ DeclareIdent("y_renamed", TypeInt), }) }, }, expected: And( Equals(Text("x_renamed"), Int(5)), Equals(Text("y_renamed"), Int(10)), ), }, { name: "macro on nested expression", filter: `(x = 5) AND (y = 10)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x", TypeInt), DeclareIdent("y", TypeInt), }, macros: []Macro{ func(cursor *Cursor) { callExpr := cursor.Expr().GetCallExpr() if callExpr == nil || callExpr.GetFunction() != FunctionEquals { return } if len(callExpr.GetArgs()) != 2 { return } arg0Ident := callExpr.GetArgs()[0].GetIdentExpr() if arg0Ident == nil || arg0Ident.GetName() != "x" { return } // Transform x = 5 to x_renamed = 5 cursor.ReplaceWithDeclarations( Equals(Text("x_renamed"), callExpr.GetArgs()[1]), []DeclarationOption{ DeclareIdent("x_renamed", TypeInt), }, ) }, }, expected: And( Equals(Text("x_renamed"), Int(5)), Equals(Text("y"), Int(10)), ), }, { name: "change equal function lhs from string to int", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "name" { return } cursor.ReplaceWithDeclarations( Text("name"), []DeclarationOption{ DeclareIdent("name", TypeInt), DeclareFunction(FunctionEquals, NewFunctionOverload(FunctionEquals, TypeBool, TypeInt, TypeString)), }, ) }, }, }, { name: "empty macros list is no-op", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{}, expected: Equals(Text("name"), String("test")), }, { name: "same ident appears several times in the filter", filter: `name = "test" AND name = "test2"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "name" { return } cursor.ReplaceWithDeclarations( Text("name_renamed"), []DeclarationOption{ DeclareIdent("name_renamed", TypeString), }, ) }, }, expected: And( Equals(Text("name_renamed"), String("test")), Equals(Text("name_renamed"), String("test2")), ), }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() declarations, err := NewDeclarations(tt.declarations...) assert.NilError(t, err) filter, err := ParseFilter(&mockRequest{filter: tt.filter}, declarations) assert.NilError(t, err) err = filter.ApplyMacros(tt.macros...) if err != nil && tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) return } if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) return } assert.NilError(t, err) // Verify filter was modified in place if tt.expected != nil { assert.DeepEqual( t, tt.expected, filter.CheckedExpr.GetExpr(), protocmp.Transform(), protocmp.IgnoreFields(&expr.Expr{}, "id"), ) } }) } } aip-go-0.80.0/filtering/functions.go000066400000000000000000000266431513342120500173040ustar00rootroot00000000000000package filtering import expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" // Standard function names. const ( FunctionFuzzyAnd = "FUZZY" FunctionAnd = "AND" FunctionOr = "OR" FunctionNot = "NOT" FunctionEquals = "=" FunctionNotEquals = "!=" FunctionLessThan = "<" FunctionLessEquals = "<=" FunctionGreaterEquals = ">=" FunctionGreaterThan = ">" FunctionHas = ":" FunctionDuration = "duration" FunctionTimestamp = "timestamp" ) // StandardFunctionDeclarations returns declarations for all standard functions and their standard overloads. func StandardFunctionDeclarations() []*expr.Decl { return []*expr.Decl{ StandardFunctionTimestamp(), StandardFunctionDuration(), StandardFunctionHas(), StandardFunctionAnd(), StandardFunctionOr(), StandardFunctionNot(), StandardFunctionLessThan(), StandardFunctionLessEquals(), StandardFunctionGreaterThan(), StandardFunctionGreaterEquals(), StandardFunctionEquals(), StandardFunctionNotEquals(), } } // Timestamp overloads. const ( FunctionOverloadTimestampString = FunctionTimestamp + "_string" ) // StandardFunctionTimestamp returns a declaration for the standard `timestamp` function and all its standard overloads. func StandardFunctionTimestamp() *expr.Decl { return NewFunctionDeclaration( FunctionTimestamp, NewFunctionOverload(FunctionOverloadTimestampString, TypeTimestamp, TypeString), ) } // Duration overloads. const ( FunctionOverloadDurationString = FunctionDuration + "_string" ) // StandardFunctionDuration returns a declaration for the standard `duration` function and all its standard overloads. func StandardFunctionDuration() *expr.Decl { return NewFunctionDeclaration( FunctionDuration, NewFunctionOverload(FunctionOverloadDurationString, TypeDuration, TypeString), ) } // Has overloads. const ( FunctionOverloadHasString = FunctionHas + "_string" FunctionOverloadHasMapStringString = FunctionHas + "_map_string_string" FunctionOverloadHasListString = FunctionHas + "_list_string" ) // StandardFunctionHas returns a declaration for the standard `:` function and all its standard overloads. func StandardFunctionHas() *expr.Decl { return NewFunctionDeclaration( FunctionHas, NewFunctionOverload(FunctionOverloadHasString, TypeBool, TypeString, TypeString), // TODO: Remove this after implementing support for type parameters. NewFunctionOverload(FunctionOverloadHasMapStringString, TypeBool, TypeMap(TypeString, TypeString), TypeString), NewFunctionOverload(FunctionOverloadHasListString, TypeBool, TypeList(TypeString), TypeString), ) } // And overloads. const ( FunctionOverloadAndBool = FunctionAnd + "_bool" ) // StandardFunctionAnd returns a declaration for the standard `AND` function and all its standard overloads. func StandardFunctionAnd() *expr.Decl { return NewFunctionDeclaration( FunctionAnd, NewFunctionOverload(FunctionOverloadAndBool, TypeBool, TypeBool, TypeBool), ) } // Or overloads. const ( FunctionOverloadOrBool = FunctionOr + "_bool" ) // StandardFunctionOr returns a declaration for the standard `OR` function and all its standard overloads. func StandardFunctionOr() *expr.Decl { return NewFunctionDeclaration( FunctionOr, NewFunctionOverload(FunctionOverloadOrBool, TypeBool, TypeBool, TypeBool), ) } // Not overloads. const ( FunctionOverloadNotBool = FunctionNot + "_bool" ) // StandardFunctionNot returns a declaration for the standard `NOT` function and all its standard overloads. func StandardFunctionNot() *expr.Decl { return NewFunctionDeclaration( FunctionNot, NewFunctionOverload(FunctionOverloadNotBool, TypeBool, TypeBool), ) } // LessThan overloads. const ( FunctionOverloadLessThanInt = FunctionLessThan + "_int" FunctionOverloadLessThanFloat = FunctionLessThan + "_float" FunctionOverloadLessThanString = FunctionLessThan + "_string" FunctionOverloadLessThanTimestamp = FunctionLessThan + "_timestamp" FunctionOverloadLessThanTimestampString = FunctionLessThan + "_timestamp_string" FunctionOverloadLessThanDuration = FunctionLessThan + "_duration" ) // StandardFunctionLessThan returns a declaration for the standard '<' function and all its standard overloads. func StandardFunctionLessThan() *expr.Decl { return NewFunctionDeclaration( FunctionLessThan, NewFunctionOverload(FunctionOverloadLessThanInt, TypeBool, TypeInt, TypeInt), NewFunctionOverload(FunctionOverloadLessThanFloat, TypeBool, TypeFloat, TypeFloat), NewFunctionOverload(FunctionOverloadLessThanString, TypeBool, TypeString, TypeString), NewFunctionOverload(FunctionOverloadLessThanTimestamp, TypeBool, TypeTimestamp, TypeTimestamp), NewFunctionOverload(FunctionOverloadLessThanTimestampString, TypeBool, TypeTimestamp, TypeString), NewFunctionOverload(FunctionOverloadLessThanDuration, TypeBool, TypeDuration, TypeDuration), ) } // GreaterThan overloads. const ( FunctionOverloadGreaterThanInt = FunctionGreaterThan + "_int" FunctionOverloadGreaterThanFloat = FunctionGreaterThan + "_float" FunctionOverloadGreaterThanString = FunctionGreaterThan + "_string" FunctionOverloadGreaterThanTimestamp = FunctionGreaterThan + "_timestamp" FunctionOverloadGreaterThanTimestampString = FunctionGreaterThan + "_timestamp_string" FunctionOverloadGreaterThanDuration = FunctionGreaterThan + "_duration" ) // StandardFunctionGreaterThan returns a declaration for the standard '>' function and all its standard overloads. func StandardFunctionGreaterThan() *expr.Decl { return NewFunctionDeclaration( FunctionGreaterThan, NewFunctionOverload(FunctionOverloadGreaterThanInt, TypeBool, TypeInt, TypeInt), NewFunctionOverload(FunctionOverloadGreaterThanFloat, TypeBool, TypeFloat, TypeFloat), NewFunctionOverload(FunctionOverloadGreaterThanString, TypeBool, TypeString, TypeString), NewFunctionOverload(FunctionOverloadGreaterThanTimestamp, TypeBool, TypeTimestamp, TypeTimestamp), NewFunctionOverload(FunctionOverloadGreaterThanTimestampString, TypeBool, TypeTimestamp, TypeString), NewFunctionOverload(FunctionOverloadGreaterThanDuration, TypeBool, TypeDuration, TypeDuration), ) } // LessEquals overloads. const ( FunctionOverloadLessEqualsInt = FunctionLessEquals + "_int" FunctionOverloadLessEqualsFloat = FunctionLessEquals + "_float" FunctionOverloadLessEqualsString = FunctionLessEquals + "_string" FunctionOverloadLessEqualsTimestamp = FunctionLessEquals + "_timestamp" FunctionOverloadLessEqualsTimestampString = FunctionLessEquals + "_timestamp_string" FunctionOverloadLessEqualsDuration = FunctionLessEquals + "_duration" ) // StandardFunctionLessEquals returns a declaration for the standard '<=' function and all its standard overloads. func StandardFunctionLessEquals() *expr.Decl { return NewFunctionDeclaration( FunctionLessEquals, NewFunctionOverload(FunctionOverloadLessEqualsInt, TypeBool, TypeInt, TypeInt), NewFunctionOverload(FunctionOverloadLessEqualsFloat, TypeBool, TypeFloat, TypeFloat), NewFunctionOverload(FunctionOverloadLessEqualsString, TypeBool, TypeString, TypeString), NewFunctionOverload(FunctionOverloadLessEqualsTimestamp, TypeBool, TypeTimestamp, TypeTimestamp), NewFunctionOverload(FunctionOverloadLessEqualsTimestampString, TypeBool, TypeTimestamp, TypeString), NewFunctionOverload(FunctionOverloadLessEqualsDuration, TypeBool, TypeDuration, TypeDuration), ) } // GreaterEquals overloads. const ( FunctionOverloadGreaterEqualsInt = FunctionGreaterEquals + "_int" FunctionOverloadGreaterEqualsFloat = FunctionGreaterEquals + "_float" FunctionOverloadGreaterEqualsString = FunctionGreaterEquals + "_string" FunctionOverloadGreaterEqualsTimestamp = FunctionGreaterEquals + "_timestamp" FunctionOverloadGreaterEqualsTimestampString = FunctionGreaterEquals + "_timestamp_string" FunctionOverloadGreaterEqualsDuration = FunctionGreaterEquals + "_duration" ) // StandardFunctionGreaterEquals returns a declaration for the standard '>=' function and all its standard overloads. func StandardFunctionGreaterEquals() *expr.Decl { return NewFunctionDeclaration( FunctionGreaterEquals, NewFunctionOverload(FunctionOverloadGreaterEqualsInt, TypeBool, TypeInt, TypeInt), NewFunctionOverload(FunctionOverloadGreaterEqualsFloat, TypeBool, TypeFloat, TypeFloat), NewFunctionOverload(FunctionOverloadGreaterEqualsString, TypeBool, TypeString, TypeString), NewFunctionOverload(FunctionOverloadGreaterEqualsTimestamp, TypeBool, TypeTimestamp, TypeTimestamp), NewFunctionOverload(FunctionOverloadGreaterEqualsTimestampString, TypeBool, TypeTimestamp, TypeString), NewFunctionOverload(FunctionOverloadGreaterEqualsDuration, TypeBool, TypeDuration, TypeDuration), ) } // Equals overloads. const ( FunctionOverloadEqualsBool = FunctionEquals + "_bool" FunctionOverloadEqualsInt = FunctionEquals + "_int" FunctionOverloadEqualsFloat = FunctionEquals + "_float" FunctionOverloadEqualsString = FunctionEquals + "_string" FunctionOverloadEqualsTimestamp = FunctionEquals + "_timestamp" FunctionOverloadEqualsTimestampString = FunctionEquals + "_timestamp_string" FunctionOverloadEqualsDuration = FunctionEquals + "_duration" ) // StandardFunctionEquals returns a declaration for the standard '=' function and all its standard overloads. func StandardFunctionEquals() *expr.Decl { return NewFunctionDeclaration( FunctionEquals, NewFunctionOverload(FunctionOverloadEqualsBool, TypeBool, TypeBool, TypeBool), NewFunctionOverload(FunctionOverloadEqualsInt, TypeBool, TypeInt, TypeInt), NewFunctionOverload(FunctionOverloadEqualsFloat, TypeBool, TypeFloat, TypeFloat), NewFunctionOverload(FunctionOverloadEqualsString, TypeBool, TypeString, TypeString), NewFunctionOverload(FunctionOverloadEqualsTimestamp, TypeBool, TypeTimestamp, TypeTimestamp), NewFunctionOverload(FunctionOverloadEqualsTimestampString, TypeBool, TypeTimestamp, TypeString), NewFunctionOverload(FunctionOverloadEqualsDuration, TypeBool, TypeDuration, TypeDuration), ) } // NotEquals overloads. const ( FunctionOverloadNotEqualsBool = FunctionNotEquals + "_bool" FunctionOverloadNotEqualsInt = FunctionNotEquals + "_int" FunctionOverloadNotEqualsFloat = FunctionNotEquals + "_float" FunctionOverloadNotEqualsString = FunctionNotEquals + "_string" FunctionOverloadNotEqualsTimestamp = FunctionNotEquals + "_timestamp" FunctionOverloadNotEqualsTimestampString = FunctionNotEquals + "_timestamp_string" FunctionOverloadNotEqualsDuration = FunctionNotEquals + "_duration" ) // StandardFunctionNotEquals returns a declaration for the standard '!=' function and all its standard overloads. func StandardFunctionNotEquals() *expr.Decl { return NewFunctionDeclaration( FunctionNotEquals, NewFunctionOverload(FunctionOverloadNotEqualsBool, TypeBool, TypeBool, TypeBool), NewFunctionOverload(FunctionOverloadNotEqualsInt, TypeBool, TypeInt, TypeInt), NewFunctionOverload(FunctionOverloadNotEqualsFloat, TypeBool, TypeFloat, TypeFloat), NewFunctionOverload(FunctionOverloadNotEqualsString, TypeBool, TypeString, TypeString), NewFunctionOverload(FunctionOverloadNotEqualsTimestamp, TypeBool, TypeTimestamp, TypeTimestamp), NewFunctionOverload(FunctionOverloadNotEqualsTimestampString, TypeBool, TypeTimestamp, TypeString), NewFunctionOverload(FunctionOverloadNotEqualsDuration, TypeBool, TypeDuration, TypeDuration), ) } aip-go-0.80.0/filtering/lexer.go000066400000000000000000000101341513342120500163770ustar00rootroot00000000000000package filtering import ( "errors" "fmt" "io" "unicode" "unicode/utf8" ) // Lexer is a filter expression lexer. type Lexer struct { filter string tokenStart Position tokenEnd Position lineOffsets []int32 } // Init initializes the lexer with the provided filter. func (l *Lexer) Init(filter string) { *l = Lexer{ filter: filter, tokenStart: Position{Offset: 0, Line: 1, Column: 1}, tokenEnd: Position{Offset: 0, Line: 1, Column: 1}, lineOffsets: l.lineOffsets[:0], } } // Lex returns the next token in the filter expression, or io.EOF when there are no more tokens to lex. func (l *Lexer) Lex() (Token, error) { r, err := l.nextRune() if err != nil { return Token{}, err } switch r { // Single-character operator? case '(', ')', '-', '.', '=', ':', ',': return l.emit(TokenType(l.tokenValue())) // Two-character operator? case '<', '>', '!': if l.sniffRune('=') { _, _ = l.nextRune() } return l.emit(TokenType(l.tokenValue())) // String? case '\'', '"': quote := r escaped := false for { r2, err := l.nextRune() if err != nil { if errors.Is(err, io.EOF) { return Token{}, l.errorf("unterminated string") } return Token{}, err } if r2 == '\\' && !escaped { escaped = true continue } if r2 == quote && !escaped { return l.emit(TokenTypeString) } escaped = false } // Number? case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': // Hex number? if l.sniffRune('x') { _, _ = l.nextRune() for l.sniff(isHexDigit) { _, _ = l.nextRune() } return l.emit(TokenTypeHexNumber) } for l.sniff(unicode.IsDigit) { _, _ = l.nextRune() } return l.emit(TokenTypeNumber) } // Space? if unicode.IsSpace(r) { for l.sniff(unicode.IsSpace) { _, _ = l.nextRune() } return l.emit(TokenTypeWhitespace) } // Text or keyword. for l.sniff(isText) { _, _ = l.nextRune() } // Keyword? if tokenType := TokenType(l.tokenValue()); tokenType.IsKeyword() { return l.emit(tokenType) } // Text. return l.emit(TokenTypeText) } // Position returns the current position of the lexer. func (l *Lexer) Position() Position { return l.tokenStart } // LineOffsets returns a monotonically increasing list of character offsets where newlines appear. func (l *Lexer) LineOffsets() []int32 { return l.lineOffsets } func (l *Lexer) emit(t TokenType) (Token, error) { token := Token{ Position: l.tokenStart, Type: t, Value: l.tokenValue(), } l.tokenStart = l.tokenEnd return token, nil } func (l *Lexer) tokenValue() string { return l.filter[l.tokenStart.Offset:l.tokenEnd.Offset] } func (l *Lexer) remainingFilter() string { return l.filter[l.tokenEnd.Offset:] } func (l *Lexer) nextRune() (rune, error) { r, n := utf8.DecodeRuneInString(l.remainingFilter()) switch { case n == 0: return r, io.EOF // If the input rune was the replacement character (`\uFFFD`) preserve it. // // If the input rune was invalid (and converted to replacement character) // return an error. case r == utf8.RuneError && (n != 3 || l.remainingFilter()[:3] != "\xef\xbf\xbd"): return r, l.errorf("invalid UTF-8") } if r == '\n' { l.lineOffsets = append(l.lineOffsets, l.tokenEnd.Offset) l.tokenEnd.Line++ l.tokenEnd.Column = 1 } else { l.tokenEnd.Column++ } l.tokenEnd.Offset += int32(n) // #nosec G115 return r, nil } func (l *Lexer) sniff(wantFns ...func(rune) bool) bool { remaining := l.remainingFilter() for _, wantFn := range wantFns { r, n := utf8.DecodeRuneInString(l.remainingFilter()) if !wantFn(r) { return false } remaining = remaining[n:] } return true } func (l *Lexer) sniffRune(want rune) bool { r, _ := utf8.DecodeRuneInString(l.remainingFilter()) return r == want } func (l *Lexer) errorf(format string, args ...interface{}) error { return &lexError{ filter: l.filter, position: l.tokenStart, message: fmt.Sprintf(format, args...), } } func isText(r rune) bool { switch r { case utf8.RuneError, '(', ')', '-', '.', '=', ':', '<', '>', '!', ',': return false } return !unicode.IsSpace(r) } func isHexDigit(r rune) bool { return unicode.Is(unicode.ASCII_Hex_Digit, r) } aip-go-0.80.0/filtering/lexer_test.go000066400000000000000000000467061513342120500174540ustar00rootroot00000000000000package filtering import ( "errors" "io" "strings" "testing" "gotest.tools/v3/assert" ) func TestLexer(t *testing.T) { t.Parallel() for _, tt := range []struct { filter string expected []Token errorContains string }{ { filter: `New York Giants`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "New"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeText, Value: "York"}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeText, Value: "Giants"}, }, }, { filter: `New York Giants OR Yankees`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "New"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeText, Value: "York"}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeText, Value: "Giants"}, {Position: Position{Offset: 15, Column: 16, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 16, Column: 17, Line: 1}, Type: TokenTypeOr, Value: "OR"}, {Position: Position{Offset: 18, Column: 19, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 19, Column: 20, Line: 1}, Type: TokenTypeText, Value: "Yankees"}, }, }, { filter: `New York (Giants OR Yankees)`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "New"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeText, Value: "York"}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeText, Value: "Giants"}, {Position: Position{Offset: 16, Column: 17, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 17, Column: 18, Line: 1}, Type: TokenTypeOr, Value: "OR"}, {Position: Position{Offset: 19, Column: 20, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 20, Column: 21, Line: 1}, Type: TokenTypeText, Value: "Yankees"}, {Position: Position{Offset: 27, Column: 28, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, }, }, { filter: `a b AND c AND d`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "a"}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 2, Column: 3, Line: 1}, Type: TokenTypeText, Value: "b"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeAnd, Value: "AND"}, {Position: Position{Offset: 7, Column: 8, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeText, Value: "c"}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeAnd, Value: "AND"}, {Position: Position{Offset: 13, Column: 14, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 14, Column: 15, Line: 1}, Type: TokenTypeText, Value: "d"}, }, }, { filter: `(a b) AND c AND d`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeText, Value: "a"}, {Position: Position{Offset: 2, Column: 3, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeText, Value: "b"}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeAnd, Value: "AND"}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeText, Value: "c"}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 12, Column: 13, Line: 1}, Type: TokenTypeAnd, Value: "AND"}, {Position: Position{Offset: 15, Column: 16, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 16, Column: 17, Line: 1}, Type: TokenTypeText, Value: "d"}, }, }, { filter: `a < 10 OR a >= 100`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "a"}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 2, Column: 3, Line: 1}, Type: TokenTypeLessThan, Value: "<"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeNumber, Value: "10"}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 7, Column: 8, Line: 1}, Type: TokenTypeOr, Value: "OR"}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeText, Value: "a"}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 12, Column: 13, Line: 1}, Type: TokenTypeGreaterEquals, Value: ">="}, {Position: Position{Offset: 14, Column: 15, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 15, Column: 16, Line: 1}, Type: TokenTypeNumber, Value: "100"}, }, }, { filter: `NOT (a OR b)`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeNot, Value: "NOT"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeText, Value: "a"}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 7, Column: 8, Line: 1}, Type: TokenTypeOr, Value: "OR"}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeText, Value: "b"}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, }, }, { filter: `-file:".java"`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeMinus, Value: "-"}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeText, Value: "file"}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeHas, Value: ":"}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeString, Value: `".java"`}, }, }, { filter: `-30`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeMinus, Value: "-"}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeNumber, Value: "30"}, }, }, { filter: `package=com.google`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "package"}, {Position: Position{Offset: 7, Column: 8, Line: 1}, Type: TokenTypeEquals, Value: "="}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeText, Value: "com"}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 12, Column: 13, Line: 1}, Type: TokenTypeText, Value: "google"}, }, }, { filter: `msg != 'hello'`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "msg"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeNotEquals, Value: "!="}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 7, Column: 8, Line: 1}, Type: TokenTypeString, Value: "'hello'"}, }, }, { filter: `1 > 0`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeNumber, Value: "1"}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 2, Column: 3, Line: 1}, Type: TokenTypeGreaterThan, Value: ">"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeNumber, Value: "0"}, }, }, { filter: `2.5 >= 2.4`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeNumber, Value: "2"}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 2, Column: 3, Line: 1}, Type: TokenTypeNumber, Value: "5"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeGreaterEquals, Value: ">="}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 7, Column: 8, Line: 1}, Type: TokenTypeNumber, Value: "2"}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeNumber, Value: "4"}, }, }, { filter: `yesterday < request.time`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "yesterday"}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeLessThan, Value: "<"}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 12, Column: 13, Line: 1}, Type: TokenTypeText, Value: "request"}, {Position: Position{Offset: 19, Column: 20, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 20, Column: 21, Line: 1}, Type: TokenTypeText, Value: "time"}, }, }, { filter: `experiment.rollout <= cohort(request.user)`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "experiment"}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeText, Value: "rollout"}, {Position: Position{Offset: 18, Column: 19, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 19, Column: 20, Line: 1}, Type: TokenTypeLessEquals, Value: "<="}, {Position: Position{Offset: 21, Column: 22, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 22, Column: 23, Line: 1}, Type: TokenTypeText, Value: "cohort"}, {Position: Position{Offset: 28, Column: 29, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 29, Column: 30, Line: 1}, Type: TokenTypeText, Value: "request"}, {Position: Position{Offset: 36, Column: 37, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 37, Column: 38, Line: 1}, Type: TokenTypeText, Value: "user"}, {Position: Position{Offset: 41, Column: 42, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, }, }, { filter: `prod`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "prod"}, }, }, { filter: `expr.type_map.1.type`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "expr"}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeText, Value: "type_map"}, {Position: Position{Offset: 13, Column: 14, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 14, Column: 15, Line: 1}, Type: TokenTypeNumber, Value: "1"}, {Position: Position{Offset: 15, Column: 16, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 16, Column: 17, Line: 1}, Type: TokenTypeText, Value: "type"}, }, }, { filter: `regex(m.key, '^.*prod.*$')`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "regex"}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeText, Value: "m"}, {Position: Position{Offset: 7, Column: 8, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeText, Value: "key"}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeComma, Value: ","}, {Position: Position{Offset: 12, Column: 13, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 13, Column: 14, Line: 1}, Type: TokenTypeString, Value: "'^.*prod.*$'"}, {Position: Position{Offset: 25, Column: 26, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, }, }, { filter: `math.mem('30mb')`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "math"}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeText, Value: "mem"}, {Position: Position{Offset: 8, Column: 9, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeString, Value: "'30mb'"}, {Position: Position{Offset: 15, Column: 16, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, }, }, { filter: `(msg.endsWith('world') AND retries < 10)`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 1, Column: 2, Line: 1}, Type: TokenTypeText, Value: "msg"}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeDot, Value: "."}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeText, Value: "endsWith"}, {Position: Position{Offset: 13, Column: 14, Line: 1}, Type: TokenTypeLeftParen, Value: "("}, {Position: Position{Offset: 14, Column: 15, Line: 1}, Type: TokenTypeString, Value: "'world'"}, {Position: Position{Offset: 21, Column: 22, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, {Position: Position{Offset: 22, Column: 23, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 23, Column: 24, Line: 1}, Type: TokenTypeAnd, Value: "AND"}, {Position: Position{Offset: 26, Column: 27, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 27, Column: 28, Line: 1}, Type: TokenTypeText, Value: "retries"}, {Position: Position{Offset: 34, Column: 35, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 35, Column: 36, Line: 1}, Type: TokenTypeLessThan, Value: "<"}, {Position: Position{Offset: 36, Column: 37, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 37, Column: 38, Line: 1}, Type: TokenTypeNumber, Value: "10"}, {Position: Position{Offset: 39, Column: 40, Line: 1}, Type: TokenTypeRightParen, Value: ")"}, }, }, { filter: `foo = 0xdeadbeef`, expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "foo"}, {Position: Position{Offset: 3, Column: 4, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 4, Column: 5, Line: 1}, Type: TokenTypeEquals, Value: "="}, {Position: Position{Offset: 5, Column: 6, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 6, Column: 7, Line: 1}, Type: TokenTypeHexNumber, Value: "0xdeadbeef"}, }, }, { filter: `a = "foo`, errorContains: "unterminated string", }, { filter: "invalid = foo\xa0\x01bar", errorContains: "invalid UTF-8", }, { filter: `object_id = "�g/ml" OR object_id = "µg/ml"`, // replacement character is valid UTF-8 expected: []Token{ {Position: Position{Offset: 0, Column: 1, Line: 1}, Type: TokenTypeText, Value: "object_id"}, {Position: Position{Offset: 9, Column: 10, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 10, Column: 11, Line: 1}, Type: TokenTypeEquals, Value: "="}, {Position: Position{Offset: 11, Column: 12, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 12, Column: 13, Line: 1}, Type: TokenTypeString, Value: `"�g/ml"`}, {Position: Position{Offset: 21, Column: 20, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 22, Column: 21, Line: 1}, Type: TokenTypeOr, Value: "OR"}, {Position: Position{Offset: 24, Column: 23, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 25, Column: 24, Line: 1}, Type: TokenTypeText, Value: "object_id"}, {Position: Position{Offset: 34, Column: 33, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 35, Column: 34, Line: 1}, Type: TokenTypeEquals, Value: "="}, {Position: Position{Offset: 36, Column: 35, Line: 1}, Type: TokenTypeWhitespace, Value: " "}, {Position: Position{Offset: 37, Column: 36, Line: 1}, Type: TokenTypeString, Value: `"µg/ml"`}, }, }, } { t.Run(tt.filter, func(t *testing.T) { t.Parallel() var lexer Lexer lexer.Init(tt.filter) actual := make([]Token, 0, len(tt.expected)) var tokenValues strings.Builder tokenValues.Grow(len(tt.filter)) var token Token var err error for { token, err = lexer.Lex() if err != nil { break } _, _ = tokenValues.WriteString(token.Value) actual = append(actual, token) } if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) } else { assert.Assert(t, errors.Is(err, io.EOF)) assert.DeepEqual(t, tt.expected, actual) assert.Equal( t, tt.filter, tokenValues.String(), "concatenating all token values should give the original value", ) } }) } } //nolint:gochecknoglobals var tokenSink Token func BenchmarkLexer_Lex(b *testing.B) { const filter = `(msg.endsWith('world') AND retries < 10)` b.ReportAllocs() var lexer Lexer for i := 0; i < b.N; i++ { lexer.Init(filter) token, _ := lexer.Lex() tokenSink = token } } aip-go-0.80.0/filtering/macro.go000066400000000000000000000104741513342120500163700ustar00rootroot00000000000000package filtering import ( "fmt" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) // Macro represents a function that can perform macro replacements on a filter expression. type Macro func(*Cursor) // ApplyMacros applies the provided macros to the filter and type-checks the result against the provided declarations. func ApplyMacros(filter Filter, declarations *Declarations, macros ...Macro) (Filter, error) { // We ignore the return value as we validate against the given declarations instead. _, err := applyMacros(filter.CheckedExpr.GetExpr(), filter.CheckedExpr.GetSourceInfo(), filter.declarations, macros...) if err != nil { return Filter{}, err } var checker Checker checker.Init(filter.CheckedExpr.GetExpr(), filter.CheckedExpr.GetSourceInfo(), declarations) checkedExpr, err := checker.Check() if err != nil { return Filter{}, err } filter.CheckedExpr = checkedExpr return filter, nil } func applyMacros( exp *expr.Expr, sourceInfo *expr.SourceInfo, declarations *Declarations, macros ...Macro, ) ([]DeclarationOption, error) { declarationOptions := make([]DeclarationOption, 0, len(macros)) nextID := maxID(exp) + 1 decl := declarations if declarations == nil { var err error decl, err = NewDeclarations() if err != nil { return nil, fmt.Errorf("failed to create new declarations: %w", err) } } Walk(func(currExpr, parentExpr *expr.Expr) bool { cursor := &Cursor{ sourceInfo: sourceInfo, currExpr: currExpr, parentExpr: parentExpr, nextID: nextID, exprDeclarations: decl, } for _, macro := range macros { macro(cursor) nextID = cursor.nextID if cursor.replaced { declarationOptions = append(declarationOptions, cursor.replaceDeclOptions...) // Don't traverse children of replaced expr. return false } } return true }, exp) return declarationOptions, nil } // A Cursor describes an expression encountered while applying a Macro. // // The method Replace can be used to rewrite the filter. type Cursor struct { parentExpr *expr.Expr currExpr *expr.Expr sourceInfo *expr.SourceInfo exprDeclarations *Declarations replaced bool nextID int64 replaceDeclOptions []DeclarationOption } // Parent returns the parent of the current expression. func (c *Cursor) Parent() (*expr.Expr, bool) { return c.parentExpr, c.parentExpr != nil } // Expr returns the current expression. func (c *Cursor) Expr() *expr.Expr { return c.currExpr } // LookupIdentType looks up the type of an ident in the filter declarations. // EXPERIMENTAL: This method is experimental and may be changed or removed in the future. func (c *Cursor) LookupIdentType(name string) (*expr.Type, bool) { if c.exprDeclarations == nil { return nil, false } ident, ok := c.exprDeclarations.LookupIdent(name) if !ok { return nil, false } return ident.GetIdent().GetType(), true } // Replace the current expression with a new expression. func (c *Cursor) Replace(newExpr *expr.Expr) { Walk(func(childExpr, _ *expr.Expr) bool { childExpr.Id = c.nextID c.nextID++ return true }, newExpr) if c.sourceInfo.MacroCalls == nil { c.sourceInfo.MacroCalls = map[int64]*expr.Expr{} } c.sourceInfo.MacroCalls[newExpr.GetId()] = &expr.Expr{Id: c.currExpr.GetId(), ExprKind: c.currExpr.GetExprKind()} c.currExpr.Id = newExpr.GetId() c.currExpr.ExprKind = newExpr.GetExprKind() c.replaced = true } // ReplaceWithDeclarations replaces the current expression with a new expression and type. // EXPERIMENTAL: This method is experimental and may be changed or removed in the future. func (c *Cursor) ReplaceWithDeclarations(newExpr *expr.Expr, opts []DeclarationOption) { Walk(func(childExpr, _ *expr.Expr) bool { childExpr.Id = c.nextID c.nextID++ return true }, newExpr) if c.sourceInfo.MacroCalls == nil { c.sourceInfo.MacroCalls = map[int64]*expr.Expr{} } c.sourceInfo.MacroCalls[newExpr.GetId()] = &expr.Expr{Id: c.currExpr.GetId(), ExprKind: c.currExpr.GetExprKind()} c.currExpr.Id = newExpr.GetId() c.currExpr.ExprKind = newExpr.GetExprKind() c.replaceDeclOptions = append(c.replaceDeclOptions, opts...) c.replaced = true } func maxID(exp *expr.Expr) int64 { var maxFound int64 Walk(func(_, _ *expr.Expr) bool { if exp.GetId() > maxFound { maxFound = exp.GetId() } return true }, exp) return maxFound } aip-go-0.80.0/filtering/macro_test.go000066400000000000000000000331621513342120500174260ustar00rootroot00000000000000package filtering import ( "testing" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/testing/protocmp" "gotest.tools/v3/assert" ) func TestApplyMacros(t *testing.T) { t.Parallel() for _, tt := range []struct { name string filter string declarations []DeclarationOption macros []Macro macroDeclarations []DeclarationOption expected *expr.Expr errorContains string }{ { name: `annotations.schedule = "test" --> annotations: "schedule=test"`, filter: `annotations.schedule = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("annotations", TypeMap(TypeString, TypeString)), }, macros: []Macro{ func(cursor *Cursor) { callExpr := cursor.Expr().GetCallExpr() if callExpr == nil { return } if callExpr.GetFunction() != FunctionEquals { return } if len(callExpr.GetArgs()) != 2 { return } arg0Select := callExpr.GetArgs()[0].GetSelectExpr() if arg0Select == nil || arg0Select.GetOperand().GetIdentExpr().GetName() != "annotations" { return } arg1String := callExpr.GetArgs()[1].GetConstExpr().GetStringValue() if arg1String == "" { return } cursor.Replace(Has(arg0Select.GetOperand(), String(arg0Select.GetField()+"="+arg1String))) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("annotations", TypeList(TypeString)), }, expected: Has(Text("annotations"), String("schedule=test")), }, { name: "no-op macro that doesn't match", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { // Macro that only matches "other_name", so this should be a no-op identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "other_name" { return } cursor.Replace(Text("renamed")) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, expected: Equals(Text("name"), String("test")), }, { name: "empty macros list is no-op", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{}, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, expected: Equals(Text("name"), String("test")), }, { name: "multiple macros applied in sequence", filter: `x = 5 AND y = 10`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x", TypeInt), DeclareIdent("y", TypeInt), }, macros: []Macro{ // First macro: rename x to x_renamed func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "x" { return } cursor.Replace(Text("x_renamed")) }, // Second macro: rename y to y_renamed func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "y" { return } cursor.Replace(Text("y_renamed")) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x_renamed", TypeInt), DeclareIdent("y_renamed", TypeInt), }, expected: And( Equals(Text("x_renamed"), Int(5)), Equals(Text("y_renamed"), Int(10)), ), }, { name: "macro on nested expression", filter: `(x = 5) AND (y = 10)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x", TypeInt), DeclareIdent("y", TypeInt), }, macros: []Macro{ func(cursor *Cursor) { callExpr := cursor.Expr().GetCallExpr() if callExpr == nil || callExpr.GetFunction() != FunctionEquals { return } if len(callExpr.GetArgs()) != 2 { return } arg0Ident := callExpr.GetArgs()[0].GetIdentExpr() if arg0Ident == nil || arg0Ident.GetName() != "x" { return } // Transform x = 5 to x_renamed = 5 cursor.Replace(Equals(Text("x_renamed"), callExpr.GetArgs()[1])) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x_renamed", TypeInt), DeclareIdent("y", TypeInt), }, expected: And( Equals(Text("x_renamed"), Int(5)), Equals(Text("y"), Int(10)), ), }, { name: "same ident appears multiple times", filter: `name = "test" AND name = "test2"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "name" { return } cursor.Replace(Text("name_renamed")) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name_renamed", TypeString), }, expected: And( Equals(Text("name_renamed"), String("test")), Equals(Text("name_renamed"), String("test2")), ), }, { name: "macro using ReplaceWithDeclarations with same declaration done as validation", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "name" { return } cursor.ReplaceWithDeclarations( Text("renamed_name"), []DeclarationOption{ DeclareIdent("renamed_name", TypeString), }, ) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("renamed_name", TypeString), }, expected: Equals(Text("renamed_name"), String("test")), }, { name: "macro using ReplaceWithDeclarations, declaration in macro only", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "name" { return } cursor.ReplaceWithDeclarations( Text("renamed_name"), []DeclarationOption{ DeclareIdent("renamed_name", TypeString), }, ) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), // No declaration for renamed_name here }, errorContains: "undeclared identifier 'renamed_name'", }, { name: `name != "test" --> NOT(name = "test")`, filter: `name != "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { callExpr := cursor.Expr().GetCallExpr() if callExpr == nil || callExpr.GetFunction() != FunctionNotEquals { return } if len(callExpr.GetArgs()) != 2 { return } // Transform name != "test" to NOT(name = "test") cursor.Replace(Not(Equals(callExpr.GetArgs()[0], callExpr.GetArgs()[1]))) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, expected: Not(Equals(Text("name"), String("test"))), }, { name: `age < 18 --> age <= 17`, filter: `age < 18`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("age", TypeInt), }, macros: []Macro{ func(cursor *Cursor) { callExpr := cursor.Expr().GetCallExpr() if callExpr == nil || callExpr.GetFunction() != FunctionLessThan { return } if len(callExpr.GetArgs()) != 2 { return } // Transform age < 18 to age <= 17 arg0 := callExpr.GetArgs()[0] arg1Int := callExpr.GetArgs()[1].GetConstExpr().GetInt64Value() cursor.Replace(LessEquals(arg0, Int(arg1Int-1))) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("age", TypeInt), }, expected: LessEquals(Text("age"), Int(17)), }, { name: `x = 1 OR y = 2 --> NOT(NOT(x = 1) AND NOT(y = 2))`, filter: `x = 1 OR y = 2`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x", TypeInt), DeclareIdent("y", TypeInt), }, macros: []Macro{ func(cursor *Cursor) { callExpr := cursor.Expr().GetCallExpr() if callExpr == nil || callExpr.GetFunction() != FunctionOr { return } if len(callExpr.GetArgs()) != 2 { return } // Transform OR to NOT(AND(NOT(...), NOT(...))) cursor.Replace(Not(And(Not(callExpr.GetArgs()[0]), Not(callExpr.GetArgs()[1])))) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x", TypeInt), DeclareIdent("y", TypeInt), }, expected: Not(And( Not(Equals(Text("x"), Int(1))), Not(Equals(Text("y"), Int(2))), )), }, { name: `user.name = "John" --> user_name = "John"`, filter: `user.name = "John"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("user", TypeMap(TypeString, TypeString)), }, macros: []Macro{ func(cursor *Cursor) { selectExpr := cursor.Expr().GetSelectExpr() if selectExpr == nil { return } operandIdent := selectExpr.GetOperand().GetIdentExpr() if operandIdent == nil || operandIdent.GetName() != "user" { return } if selectExpr.GetField() != "name" { return } // Transform user.name to user_name cursor.Replace(Text("user_name")) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("user_name", TypeString), }, expected: Equals(Text("user_name"), String("John")), }, { name: "macro stops traversal after replacement", filter: `x = 5 AND y = 10`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("x", TypeInt), DeclareIdent("y", TypeInt), }, macros: []Macro{ // Macro that replaces the entire AND expression func(cursor *Cursor) { callExpr := cursor.Expr().GetCallExpr() if callExpr == nil || callExpr.GetFunction() != FunctionAnd { return } // Replace entire AND with a single condition cursor.Replace(Equals(Text("combined"), Int(15))) }, // This macro should not run because the parent was replaced func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() == "x" { cursor.Replace(Text("x_should_not_match")) } }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("combined", TypeInt), }, expected: Equals(Text("combined"), Int(15)), }, { name: "type checking error after macro application", filter: `name = "test"`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeString), }, macros: []Macro{ func(cursor *Cursor) { identExpr := cursor.Expr().GetIdentExpr() if identExpr == nil || identExpr.GetName() != "name" { return } // Replace with int type, but filter expects string - should cause type error cursor.Replace(Text("name")) }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("name", TypeInt), // Wrong type - should cause error }, errorContains: "no matching overload", }, { name: "complex nested structure with multiple macro matches", filter: `(a = 1 AND b = 2) OR (c = 3 AND d = 4)`, declarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("a", TypeInt), DeclareIdent("b", TypeInt), DeclareIdent("c", TypeInt), DeclareIdent("d", TypeInt), }, macros: []Macro{ // Macro that adds 10 to all integer constants func(cursor *Cursor) { constExpr := cursor.Expr().GetConstExpr() if constExpr == nil { return } if _, ok := constExpr.GetConstantKind().(*expr.Constant_Int64Value); ok { intVal := constExpr.GetInt64Value() cursor.Replace(Int(intVal + 10)) } }, }, macroDeclarations: []DeclarationOption{ DeclareStandardFunctions(), DeclareIdent("a", TypeInt), DeclareIdent("b", TypeInt), DeclareIdent("c", TypeInt), DeclareIdent("d", TypeInt), }, expected: Or( And( Equals(Text("a"), Int(11)), Equals(Text("b"), Int(12)), ), And( Equals(Text("c"), Int(13)), Equals(Text("d"), Int(14)), ), ), }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() declarations, err := NewDeclarations(tt.declarations...) assert.NilError(t, err) filter, err := ParseFilter(&mockRequest{filter: tt.filter}, declarations) if err != nil && tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) return } assert.NilError(t, err) macroDeclarations, err := NewDeclarations(tt.macroDeclarations...) assert.NilError(t, err) actual, err := ApplyMacros(filter, macroDeclarations, tt.macros...) if err != nil && tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) return } assert.NilError(t, err) assert.DeepEqual( t, tt.expected, actual.CheckedExpr.GetExpr(), protocmp.Transform(), protocmp.IgnoreFields(&expr.Expr{}, "id"), ) }) } } type mockRequest struct { filter string } func (m *mockRequest) GetFilter() string { return m.filter } aip-go-0.80.0/filtering/parsedexpr.go000066400000000000000000000022651513342120500174430ustar00rootroot00000000000000package filtering import expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" func parsedFloat(id int64, value float64) *expr.Expr { result := Float(value) result.Id = id return result } func parsedInt(id, value int64) *expr.Expr { result := Int(value) result.Id = id return result } func parsedText(id int64, s string) *expr.Expr { result := Text(s) result.Id = id return result } func parsedString(id int64, s string) *expr.Expr { result := String(s) result.Id = id return result } func parsedExpression(id int64, sequences ...*expr.Expr) *expr.Expr { result := Expression(sequences...) result.Id = id return result } func parsedSequence(id int64, factor1, factor2 *expr.Expr) *expr.Expr { return parsedFunction(id, FunctionFuzzyAnd, factor1, factor2) } func parsedFactor(id int64, term1, term2 *expr.Expr) *expr.Expr { result := Or(term1, term2) result.Id = id return result } func parsedMember(id int64, operand *expr.Expr, field string) *expr.Expr { result := Member(operand, field) result.Id = id return result } func parsedFunction(id int64, name string, args ...*expr.Expr) *expr.Expr { result := Function(name, args...) result.Id = id return result } aip-go-0.80.0/filtering/parser.go000066400000000000000000000353241513342120500165640ustar00rootroot00000000000000package filtering import ( "errors" "fmt" "io" "strconv" "strings" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) // Parser for filter expressions. type Parser struct { filter string lexer Lexer id int64 positions []int32 } // Init (re-)initializes the parser to parse the provided filter. func (p *Parser) Init(filter string) { filter = strings.TrimSpace(filter) *p = Parser{ filter: filter, positions: p.positions[:0], id: -1, } p.lexer.Init(filter) } // Parse the filter. func (p *Parser) Parse() (*expr.ParsedExpr, error) { e, err := p.ParseExpression() if err != nil { return nil, err } end := p.lexer.Position() if token, err := p.lexer.Lex(); err != nil { if !errors.Is(err, io.EOF) { return nil, err } } else { return nil, p.errorf(end, "unexpected trailing token %s", token.Type) } return &expr.ParsedExpr{ Expr: e, SourceInfo: p.SourceInfo(), }, nil } func (p *Parser) SourceInfo() *expr.SourceInfo { positions := make(map[int64]int32, len(p.positions)) for id, position := range p.positions { positions[int64(id)] = position } return &expr.SourceInfo{ LineOffsets: p.lexer.LineOffsets(), Positions: positions, } } // ParseExpression parses an Expression. // // EBNF // // expression // : sequence {WS AND WS sequence} // ; func (p *Parser) ParseExpression() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "expression") } }() sequences := make([]*expr.Expr, 0, 1) for { _ = p.eatTokens(TokenTypeWhitespace) sequence, err := p.ParseSequence() if err != nil { return nil, err } sequences = append(sequences, sequence) if err := p.eatTokens(TokenTypeWhitespace, TokenTypeAnd, TokenTypeWhitespace); err != nil { break } } exp := sequences[0] for _, seq := range sequences[1:] { exp = parsedExpression(p.nextID(start), exp, seq) } return exp, nil } // ParseSequence parses a Sequence. // // EBNF // // sequence // : factor {WS factor} // ; func (p *Parser) ParseSequence() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "sequence") } }() factors := make([]*expr.Expr, 0, 2) for { factor, err := p.ParseFactor() if err != nil { return nil, err } factors = append(factors, factor) if p.sniffTokens(TokenTypeWhitespace, TokenTypeAnd) { break } if err := p.eatTokens(TokenTypeWhitespace); err != nil { break } } if len(factors) == 1 { return factors[0], nil } result := parsedSequence(p.nextID(start), factors[0], factors[1]) for _, factor := range factors[2:] { result = parsedSequence(p.nextID(start), result, factor) } return result, nil } // ParseFactor parses a Factor. // // EBNF // // factor // : term {WS OR WS term} // ; func (p *Parser) ParseFactor() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "factor") } }() terms := make([]*expr.Expr, 0, 2) for { term, err := p.ParseTerm() if err != nil { return nil, err } terms = append(terms, term) if err := p.eatTokens(TokenTypeWhitespace, TokenTypeOr, TokenTypeWhitespace); err != nil { break } } if len(terms) == 1 { return terms[0], nil } result := parsedFactor(p.nextID(start), terms[0], terms[1]) for _, factor := range terms[2:] { result = parsedFactor(p.nextID(start), result, factor) } return result, nil } // ParseTerm parses a Term. // // EBNF // // term // : [(NOT WS | MINUS)] simple // ; func (p *Parser) ParseTerm() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "term") } }() var not, minus bool if err := p.eatTokens(TokenTypeNot, TokenTypeWhitespace); err == nil { not = true } else if err := p.eatTokens(TokenTypeMinus); err == nil { minus = true } simple, err := p.ParseSimple() if err != nil { return nil, err } switch { case not, minus: if minus { // Simplify MINUS number to negation of the constant value. if constExpr, ok := simple.GetExprKind().(*expr.Expr_ConstExpr); ok { switch constantKind := constExpr.ConstExpr.GetConstantKind().(type) { case *expr.Constant_Int64Value: constantKind.Int64Value *= -1 return simple, nil case *expr.Constant_DoubleValue: constantKind.DoubleValue *= -1 return simple, nil } } } return parsedFunction(p.nextID(start), FunctionNot, simple), nil default: return simple, nil } } // ParseSimple parses a Simple. // // EBNF // // simple // : restriction // | composite // ; func (p *Parser) ParseSimple() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "simple") } }() if p.sniffTokens(TokenTypeLeftParen) { return p.ParseComposite() } return p.ParseRestriction() } // ParseRestriction parses a Restriction. // // EBNF // // restriction // : comparable [comparator arg] // ; func (p *Parser) ParseRestriction() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "restriction") } }() comp, err := p.ParseComparable() if err != nil { return nil, err } if !(p.sniff(TokenType.IsComparator) || p.sniff(TokenTypeWhitespace.Test, TokenType.IsComparator)) { return comp, nil } _ = p.eatTokens(TokenTypeWhitespace) comparatorToken, err := p.parseToken(TokenType.IsComparator) if err != nil { return nil, err } _ = p.eatTokens(TokenTypeWhitespace) arg, err := p.ParseArg() if err != nil { return nil, err } // Special case for `:` if comparatorToken.Type == TokenTypeHas && arg.GetIdentExpr() != nil { // m:foo - true if m contains the key "foo". arg = parsedString(arg.GetId(), arg.GetIdentExpr().GetName()) } return parsedFunction(p.nextID(start), comparatorToken.Type.Function(), comp, arg), nil } // ParseComparable parses a Comparable. // // EBNF // // comparable // : member // | function // | number (custom) // ; func (p *Parser) ParseComparable() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "comparable") } }() if function, ok := p.TryParseFunction(); ok { return function, nil } if number, ok := p.TryParseNumber(); ok { return number, nil } return p.ParseMember() } // ParseMember parses a Member. // // EBNF // // member // : value {DOT field} // ; // // value // : TEXT // | STRING // ; // // field // : value // | keyword // | number // ; func (p *Parser) ParseMember() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "member") } }() valueToken, err := p.parseToken(TokenType.IsValue) if err != nil { return nil, err } if !p.sniffTokens(TokenTypeDot) { if valueToken.Type == TokenTypeString { unquoted, err := valueToken.Unquote() if err != nil { return nil, err } return parsedString(p.nextID(valueToken.Position), unquoted), nil } return parsedText(p.nextID(valueToken.Position), valueToken.Value), nil } unquoted, err := valueToken.Unquote() if err != nil { return nil, err } value := parsedText(p.nextID(valueToken.Position), unquoted) _ = p.eatTokens(TokenTypeDot) firstFieldToken, err := p.parseToken(TokenType.IsField) if err != nil { return nil, err } unquoted, err = firstFieldToken.Unquote() if err != nil { return nil, err } member := parsedMember(p.nextID(start), value, unquoted) for { if err := p.eatTokens(TokenTypeDot); err != nil { break } fieldToken, err := p.parseToken(TokenType.IsField) if err != nil { return nil, err } unquoted, err := fieldToken.Unquote() if err != nil { return nil, err } member = parsedMember(p.nextID(start), member, unquoted) } return member, nil } // ParseFunction parses a Function. // // EBNF // // function // : name {DOT name} LPAREN [argList] RPAREN // ; // // name // : TEXT // | keyword // ; func (p *Parser) ParseFunction() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "function") } }() var name strings.Builder for { nameToken, err := p.parseToken(TokenType.IsName) if err != nil { return nil, err } unquoted, err := nameToken.Unquote() if err != nil { return nil, err } _, _ = name.WriteString(unquoted) if err := p.eatTokens(TokenTypeDot); err != nil { break } _ = name.WriteByte('.') } if err := p.eatTokens(TokenTypeLeftParen); err != nil { return nil, err } _ = p.eatTokens(TokenTypeWhitespace) args := make([]*expr.Expr, 0) for !p.sniffTokens(TokenTypeRightParen) { arg, err := p.ParseArg() if err != nil { return nil, err } args = append(args, arg) _ = p.eatTokens(TokenTypeWhitespace) if err := p.eatTokens(TokenTypeComma); err != nil { break } _ = p.eatTokens(TokenTypeWhitespace) } _ = p.eatTokens(TokenTypeWhitespace) if err := p.eatTokens(TokenTypeRightParen); err != nil { return nil, err } return parsedFunction(p.nextID(start), name.String(), args...), nil } func (p *Parser) TryParseFunction() (*expr.Expr, bool) { start := *p function, err := p.ParseFunction() if err != nil { *p = start return nil, false } return function, true } // ParseComposite parses a Composite. // // EBNF // // composite // : LPAREN expression RPAREN // ; func (p *Parser) ParseComposite() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "composite") } }() if err := p.eatTokens(TokenTypeLeftParen); err != nil { return nil, err } _ = p.eatTokens(TokenTypeWhitespace) expression, err := p.ParseExpression() if err != nil { return nil, err } _ = p.eatTokens(TokenTypeWhitespace) if err := p.eatTokens(TokenTypeRightParen); err != nil { return nil, err } return expression, nil } // ParseNumber parses a number. // // EBNF // // number // : float // | int // ; // // float // : MINUS? (NUMBER DOT NUMBER* | DOT NUMBER) EXP? // ; // // int // : MINUS? NUMBER // | MINUS? HEX // ; func (p *Parser) ParseNumber() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "number") } }() if float, ok := p.TryParseFloat(); ok { return float, nil } return p.ParseInt() } func (p *Parser) TryParseNumber() (*expr.Expr, bool) { start := *p result, err := p.ParseNumber() if err != nil { *p = start return nil, false } return result, true } // ParseFloat parses a float. // // EBNF // // float // : MINUS? (NUMBER DOT NUMBER* | DOT NUMBER) EXP? // ; func (p *Parser) ParseFloat() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "float") } }() var minusToken Token if p.sniffTokens(TokenTypeMinus) { minusToken, _ = p.lexer.Lex() } var intToken Token var hasIntToken bool if p.sniffTokens(TokenTypeNumber) { intToken, err = p.lexer.Lex() if err != nil { return nil, err } hasIntToken = true } dotToken, err := p.parseToken(TokenTypeDot.Test) if err != nil { return nil, err } var fractionToken Token var hasFractionToken bool if p.sniffTokens(TokenTypeNumber) { fractionToken, err = p.lexer.Lex() if err != nil { return nil, err } hasFractionToken = true } // TODO: Support exponents. if !hasFractionToken && !hasIntToken { return nil, p.errorf(start, "expected int or fraction") } var stringValue strings.Builder stringValue.Grow( len(minusToken.Value) + len(intToken.Value) + len(dotToken.Value) + len(fractionToken.Value), ) _, _ = stringValue.WriteString(minusToken.Value) _, _ = stringValue.WriteString(intToken.Value) _, _ = stringValue.WriteString(dotToken.Value) _, _ = stringValue.WriteString(fractionToken.Value) floatValue, err := strconv.ParseFloat(stringValue.String(), 64) if err != nil { return nil, err } return parsedFloat(p.nextID(start), floatValue), nil } func (p *Parser) TryParseFloat() (*expr.Expr, bool) { start := *p result, err := p.ParseFloat() if err != nil { *p = start return nil, false } return result, true } // ParseInt parses an int. // // EBNF // // int // : MINUS? NUMBER // | MINUS? HEX // ; func (p *Parser) ParseInt() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "int") } }() minus := p.eatTokens(TokenTypeMinus) == nil token, err := p.parseToken(TokenTypeNumber.Test, TokenTypeHexNumber.Test) if err != nil { return nil, err } intValue, err := strconv.ParseInt(token.Value, 0, 64) if err != nil { return nil, err } if minus { intValue *= -1 } return parsedInt(p.nextID(start), intValue), nil } // ParseArg parses an Arg. // // EBNF // // arg // : comparable // | composite // ; func (p *Parser) ParseArg() (_ *expr.Expr, err error) { start := p.lexer.Position() defer func() { if err != nil { err = p.wrapf(err, start, "arg") } }() if p.sniffTokens(TokenTypeLeftParen) { return p.ParseComposite() } return p.ParseComparable() } func (p *Parser) parseToken(fns ...func(TokenType) bool) (Token, error) { start := p.lexer.Position() token, err := p.lexer.Lex() if err != nil { return Token{}, p.wrapf(err, start, "parse token") } for _, fn := range fns { if fn(token.Type) { return token, nil } } return Token{}, p.errorf(token.Position, "unexpected token %s", token.Type) } func (p *Parser) sniff(fns ...func(TokenType) bool) bool { start := *p defer func() { *p = start }() for _, fn := range fns { if token, err := p.lexer.Lex(); err != nil || !fn(token.Type) { return false } } return true } func (p *Parser) sniffTokens(wantTokenTypes ...TokenType) bool { start := *p defer func() { *p = start }() for _, wantTokenType := range wantTokenTypes { if token, err := p.lexer.Lex(); err != nil || token.Type != wantTokenType { return false } } return true } func (p *Parser) eatTokens(wantTokenTypes ...TokenType) error { start := *p for _, wantTokenType := range wantTokenTypes { if token, err := p.lexer.Lex(); err != nil || token.Type != wantTokenType { *p = start return p.errorf(start.lexer.Position(), "expected %s", wantTokenType) } } return nil } func (p *Parser) errorf(position Position, format string, args ...interface{}) error { return &parseError{ filter: p.filter, position: position, message: fmt.Sprintf(format, args...), } } func (p *Parser) wrapf(err error, position Position, format string, args ...interface{}) error { return &parseError{ filter: p.filter, position: position, message: fmt.Sprintf(format, args...), err: err, } } func (p *Parser) nextID(position Position) int64 { p.id++ p.positions = append(p.positions, position.Offset) return p.id } aip-go-0.80.0/filtering/parser_test.go000066400000000000000000000152551513342120500176240ustar00rootroot00000000000000package filtering import ( "testing" "time" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/testing/protocmp" "gotest.tools/v3/assert" ) func TestParser(t *testing.T) { t.Parallel() for _, tt := range []struct { filter string expected *expr.Expr errorContains string }{ { filter: "New York Giants", expected: Sequence(Text("New"), Text("York"), Text("Giants")), }, { filter: "New York Giants OR Yankees", expected: Sequence(Text("New"), Text("York"), Or(Text("Giants"), Text("Yankees"))), }, { filter: "New York (Giants OR Yankees)", expected: Sequence(Text("New"), Text("York"), Or(Text("Giants"), Text("Yankees"))), }, { filter: "a b AND c AND d", expected: And( Sequence(Text("a"), Text("b")), Text("c"), Text("d"), ), }, { filter: "(a b) AND c AND d", expected: And( Sequence(Text("a"), Text("b")), Text("c"), Text("d"), ), }, { filter: "a < 10 OR a >= 100", expected: Or( LessThan(Text("a"), Int(10)), GreaterEquals(Text("a"), Int(100)), ), }, { filter: "a OR b OR c", expected: Or( Text("a"), Text("b"), Text("c"), ), }, { filter: "NOT (a OR b)", expected: Not(Or(Text("a"), Text("b"))), }, { filter: `-file:".java"`, expected: Not(Has(Text("file"), String(".java"))), }, { filter: "-30", expected: Int(-30), }, { filter: "package=com.google", expected: Equals(Text("package"), Member(Text("com"), "google")), }, // Simple string unescaping tests. { filter: `msg != 'hello'`, expected: NotEquals(Text("msg"), String(`hello`)), }, { filter: `msg != "hello"`, expected: NotEquals(Text("msg"), String(`hello`)), }, { filter: `msg != ""`, expected: NotEquals(Text("msg"), String(``)), }, { filter: `msg != "\\\""`, expected: NotEquals(Text("msg"), String(`\"`)), }, { filter: `msg != "\\"`, expected: NotEquals(Text("msg"), String(`\`)), }, { filter: `msg != "\303\277"`, expected: NotEquals(Text("msg"), String(`ÿ`)), }, { filter: `msg != "\377"`, expected: NotEquals(Text("msg"), String(`ÿ`)), }, { filter: `msg != "\u263A\u263A"`, expected: NotEquals(Text("msg"), String(`☺☺`)), }, { filter: `msg != "\a\b\f\n\r\t\v\'\"\\\? Legal escapes"`, expected: NotEquals(Text("msg"), String("\a\b\f\n\r\t\v'\"\\? Legal escapes")), }, { filter: `msg != "[ 'hello' ]"`, expected: NotEquals(Text("msg"), String(`[ 'hello' ]`)), }, { filter: `msg != "[ \'hello\' ]"`, expected: NotEquals(Text("msg"), String(`[ 'hello' ]`)), }, { filter: `msg != "[ \"hello\" ]"`, expected: NotEquals(Text("msg"), String(`[ "hello" ]`)), }, { filter: `1 > 0`, expected: GreaterThan(Int(1), Int(0)), }, { filter: `2.5 >= 2.4`, expected: GreaterEquals(Float(2.5), Float(2.4)), }, { filter: `foo >= -2.4`, expected: GreaterEquals(Text("foo"), Float(-2.4)), }, { filter: `foo >= (-2.4)`, expected: GreaterEquals(Text("foo"), Float(-2.4)), }, { filter: `-2.5 >= -2.4`, expected: Not(GreaterEquals(Float(2.5), Float(-2.4))), }, { filter: `yesterday < request.time`, expected: LessThan(Text("yesterday"), Member(Text("request"), "time")), }, { filter: `experiment.rollout <= cohort(request.user)`, expected: LessEquals( Member(Text("experiment"), "rollout"), Function("cohort", Member(Text("request"), "user")), ), }, { filter: `prod`, expected: Text("prod"), }, { filter: `expr.type_map.1.type`, expected: Member(Member(Member(Text("expr"), "type_map"), "1"), "type"), }, { filter: `regex(m.key, '^.*prod.*$')`, expected: Function("regex", Member(Text("m"), "key"), String("^.*prod.*$")), }, { filter: `math.mem('30mb')`, expected: Function("math.mem", String("30mb")), }, { filter: `(msg.endsWith('world') AND retries < 10)`, expected: And( Function("msg.endsWith", String("world")), LessThan(Text("retries"), Int(10)), ), }, { filter: `(endsWith(msg, 'world') AND retries < 10)`, expected: And( Function("endsWith", Text("msg"), String("world")), LessThan(Text("retries"), Int(10)), ), }, { filter: "time.now()", expected: Function("time.now"), }, { filter: `timestamp("2012-04-21T11:30:00-04:00")`, expected: Timestamp( time.Date(2012, time.April, 21, 11, 30, 0, 0, time.FixedZone("EST", -4*int(time.Hour.Seconds()))), ), }, { filter: `duration("32s")`, expected: Duration(32 * time.Second), }, { filter: `duration("4h0m0s")`, expected: Duration(4 * time.Hour), }, { filter: ` start_time > timestamp("2006-01-02T15:04:05+07:00") AND (driver = "driver1" OR start_driver = "driver1" OR end_driver = "driver1") `, expected: And( GreaterThan( Text("start_time"), Timestamp( time.Date( 2006, time.January, 2, 15, 4, 5, 0, time.FixedZone("EST", 7*int(time.Hour.Seconds())), ), ), ), Or( Equals(Text("driver"), String("driver1")), Equals(Text("start_driver"), String("driver1")), Equals(Text("end_driver"), String("driver1")), ), ), }, { filter: `annotations:schedule`, expected: Has(Text("annotations"), String("schedule")), }, { filter: `annotations.schedule = "test"`, expected: Equals(Member(Text("annotations"), "schedule"), String("test")), }, { filter: "<", errorContains: "unexpected token <", }, { filter: `(-2.5) >= -2.4`, errorContains: "unexpected token >=", }, { filter: `a = "foo`, errorContains: "unterminated string", }, { filter: "invalid = foo\xa0\x01bar", errorContains: "invalid UTF-8", }, } { t.Run(tt.filter, func(t *testing.T) { t.Parallel() var parser Parser parser.Init(tt.filter) actual, err := parser.Parse() if tt.errorContains != "" { if actual != nil { t.Log(actual.GetExpr().String()) } assert.ErrorContains(t, err, tt.errorContains) } else { assert.NilError(t, err) assert.DeepEqual( t, tt.expected, actual.GetExpr(), protocmp.Transform(), protocmp.IgnoreFields(&expr.Expr{}, "id"), ) assertUniqueExprIDs(t, actual.GetExpr()) } }) } } func assertUniqueExprIDs(t *testing.T, exp *expr.Expr) { t.Helper() seenIDs := make(map[int64]struct{}) Walk(func(currExpr, _ *expr.Expr) bool { if _, ok := seenIDs[currExpr.GetId()]; ok { t.Fatalf("duplicate expression ID '%d' for expr %v", currExpr.GetId(), currExpr) } seenIDs[currExpr.GetId()] = struct{}{} return true }, exp) } aip-go-0.80.0/filtering/position.go000066400000000000000000000007601513342120500171300ustar00rootroot00000000000000package filtering import "fmt" // Position represents a position in a filter expression. type Position struct { // Offset is the byte offset, starting at 0. Offset int32 // Line is the line number, starting at 1. Line int32 // Column is the column number, starting at 1 (character count per line). Column int32 } // String returns a string representation of the position on the format :. func (p Position) String() string { return fmt.Sprintf("%d:%d", p.Line, p.Column) } aip-go-0.80.0/filtering/proto.go000066400000000000000000000117671513342120500164400ustar00rootroot00000000000000package filtering import ( "strings" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/dynamicpb" ) type FilterOption func(opts *filterOptions) type filterOptions struct { filterableFields []string } // WithFilterableFields marks the given fields as filterable. // To mark a simple field (native types) or a message field (with all its underlying fields, recursively) // as filterable, use the field name. // To mark a specific nested field as filterable, use the full path of the field using the dot notation. // For example: // - WithFilterableFields("string_field") marks the string_field as filterable. // - WithFilterableFields("nested_message") marks the nested_message field and all its underlying fields as // filterable. // - WithFilterableFields("nested_message.nested_string") marks the nested_string field as filterable. // - WithFilterableFields("nested_message.nested_string", "nested_message.nested_int32") marks the nested_string and // nested_int32 fields as filterable. // // EXPERIMENTAL: This option is experimental and may be changed or removed in the future. func WithFilterableFields(fields ...string) FilterOption { return func(opts *filterOptions) { opts.filterableFields = fields } } // ProtoDeclarations returns declarations for all fields marked as filterable in the proto message. // By default, no fields are marked as filterable. To mark a field as filterable, use the WithFilterableFields option. // EXPERIMENTAL: This function is experimental and may be changed or removed in the future. func ProtoDeclarations(msg proto.Message, opts ...FilterOption) (*Declarations, error) { options := filterOptions{} for _, opt := range opts { opt(&options) } declOpts := []DeclarationOption{ DeclareStandardFunctions(), } declOpts = append(declOpts, messageOptions(msg.ProtoReflect().Descriptor(), "", options)...) return NewDeclarations( declOpts..., ) } func messageOptions( msg protoreflect.MessageDescriptor, path string, options filterOptions, ) []DeclarationOption { var opts []DeclarationOption for i := 0; i < msg.Fields().Len(); i++ { field := msg.Fields().Get(i) currPath := path if len(currPath) > 0 { currPath += "." } currPath += string(field.Name()) // If filterable fields were explicitly set, check if this field is allowed var found bool for _, filter := range options.filterableFields { // Check if current path matches the filter exactly if currPath == filter { found = true break } // Check if current path is a prefix of the filter (for nested fields) // For example: filter="nested_message.nested_string" should match path="nested_message" if strings.HasPrefix(filter, currPath+".") { found = true break } // Check if filter is a prefix of current path (for allowing access to nested fields) // For example: filter="nested_message" should match path="nested_message.nested_string" if strings.HasPrefix(currPath, filter+".") { found = true break } } if !found { // Skip non-filterable fields continue } if field.IsList() { // TODO: Add support for lists? continue } if field.IsMap() { // TODO: Add support for maps? continue } switch field.Kind() { case protoreflect.StringKind: opts = append(opts, DeclareIdent(currPath, TypeString)) case protoreflect.EnumKind: // Use proper enum type declaration for better type safety and validation enumType := dynamicpb.NewEnumType(field.Enum()) opts = append(opts, DeclareEnumIdent(currPath, enumType)) case protoreflect.BoolKind: opts = append(opts, DeclareIdent(currPath, TypeBool)) case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind: opts = append(opts, DeclareIdent(currPath, TypeInt)) case protoreflect.Uint32Kind, protoreflect.Uint64Kind, protoreflect.Fixed32Kind, protoreflect.Fixed64Kind: // TODO: Can we support uint? opts = append(opts, DeclareIdent(currPath, TypeInt)) case protoreflect.FloatKind, protoreflect.DoubleKind: opts = append(opts, DeclareIdent(currPath, TypeFloat)) case protoreflect.BytesKind: // TODO: Can we support bytes? opts = append(opts, DeclareIdent(currPath, TypeString)) case protoreflect.MessageKind: // Special handling for well-known types switch field.Message().FullName() { case "google.protobuf.Timestamp": opts = append(opts, DeclareIdent(currPath, TypeTimestamp)) case "google.protobuf.Duration": opts = append(opts, DeclareIdent(currPath, TypeDuration)) default: // For nested messages, recursively process their fields // but pass the same filterable field options so nested fields are filtered correctly fieldOpts := messageOptions(field.Message(), currPath, options) opts = append(opts, fieldOpts...) } case protoreflect.GroupKind: // TODO: Add support for groups? continue default: continue } } return opts } aip-go-0.80.0/filtering/proto_test.go000066400000000000000000000213071513342120500174660ustar00rootroot00000000000000package filtering import ( "testing" "time" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/testing/protocmp" "gotest.tools/v3/assert" ) func TestProtoDeclarations(t *testing.T) { t.Parallel() protoMsg := fullProtobufMessage(t) testCases := []struct { name string opts []FilterOption filter string expectedExpr *expr.Expr expectError bool }{ // String fields { name: "ok - string field", opts: []FilterOption{WithFilterableFields("string_field")}, filter: `string_field = "test"`, expectedExpr: Equals(Text("string_field"), String("test")), expectError: false, }, // Boolean fields { name: "ok - bool field true", opts: []FilterOption{WithFilterableFields("bool_field")}, filter: `bool_field`, expectedExpr: Text("bool_field"), expectError: false, }, { name: "ok - bool field false", opts: []FilterOption{WithFilterableFields("bool_field")}, filter: `NOT bool_field`, expectedExpr: Not(Text("bool_field")), expectError: false, }, // Integer fields (various sizes) { name: "ok - int32 field", opts: []FilterOption{WithFilterableFields("int32_field")}, filter: `int32_field = 42`, expectedExpr: Equals(Text("int32_field"), Int(42)), expectError: false, }, { name: "ok - int64 field", opts: []FilterOption{WithFilterableFields("int64_field")}, expectedExpr: GreaterThan(Text("int64_field"), Int(100)), filter: `int64_field > 100`, expectError: false, }, { name: "ok - sint32 field", opts: []FilterOption{WithFilterableFields("sint32_field")}, filter: `sint32_field < -10`, expectedExpr: LessThan(Text("sint32_field"), Int(-10)), expectError: false, }, { name: "ok - sint64 field", opts: []FilterOption{WithFilterableFields("sint64_field")}, filter: `sint64_field >= -1000`, expectedExpr: GreaterEquals(Text("sint64_field"), Int(-1000)), expectError: false, }, { name: "ok - sfixed32 field", opts: []FilterOption{WithFilterableFields("sfixed32_field")}, filter: `sfixed32_field = 123`, expectedExpr: Equals(Text("sfixed32_field"), Int(123)), expectError: false, }, { name: "ok - sfixed64 field", opts: []FilterOption{WithFilterableFields("sfixed64_field")}, filter: `sfixed64_field <= 456`, expectedExpr: LessEquals(Text("sfixed64_field"), Int(456)), expectError: false, }, // Unsigned integer fields { name: "ok - uint32 field", opts: []FilterOption{WithFilterableFields("uint32_field")}, filter: `uint32_field = 789`, expectedExpr: Equals(Text("uint32_field"), Int(789)), expectError: false, }, { name: "ok - uint64 field", opts: []FilterOption{WithFilterableFields("uint64_field")}, filter: `uint64_field > 1000`, expectedExpr: GreaterThan(Text("uint64_field"), Int(1000)), expectError: false, }, { name: "ok - fixed32 field", opts: []FilterOption{WithFilterableFields("fixed32_field")}, filter: `fixed32_field < 2000`, expectedExpr: LessThan(Text("fixed32_field"), Int(2000)), expectError: false, }, { name: "ok - fixed64 field", opts: []FilterOption{WithFilterableFields("fixed64_field")}, filter: `fixed64_field >= 3000`, expectedExpr: GreaterEquals(Text("fixed64_field"), Int(3000)), expectError: false, }, // Float fields { name: "ok - float field", opts: []FilterOption{WithFilterableFields("float_field")}, filter: `float_field > 3.14`, expectedExpr: GreaterThan(Text("float_field"), Float(3.14)), expectError: false, }, { name: "ok - double field", opts: []FilterOption{WithFilterableFields("double_field")}, filter: `double_field <= 2.71`, expectedExpr: LessEquals(Text("double_field"), Float(2.71)), expectError: false, }, // Enum field { name: "ok - enum field", opts: []FilterOption{WithFilterableFields("enum_field")}, filter: `enum_field = ENUM_VALUE_ONE`, expectedExpr: Equals(Text("enum_field"), Text("ENUM_VALUE_ONE")), expectError: false, }, // Timestamp field (well-known type) { name: "ok - timestamp field", opts: []FilterOption{WithFilterableFields("timestamp_field")}, filter: `timestamp_field > "2023-01-01T00:00:00Z"`, expectedExpr: GreaterThan(Text("timestamp_field"), String("2023-01-01T00:00:00Z")), expectError: false, }, // Duration field (well-known type) { name: "ok - duration field", opts: []FilterOption{WithFilterableFields("duration_field")}, filter: `duration_field > duration("50s")`, expectedExpr: GreaterThan(Text("duration_field"), Duration(50*time.Second)), expectError: false, }, // Nested message field { name: "ok - nested message field", opts: []FilterOption{WithFilterableFields("nested_message.nested_string")}, filter: `nested_message.nested_string = "nested_value"`, expectedExpr: Equals(Member(Text("nested_message"), "nested_string"), String("nested_value")), expectError: false, }, // Deeply nested field { name: "ok - deeply nested field", opts: []FilterOption{WithFilterableFields("nested_message.deep_nested.deep_string")}, filter: `nested_message.deep_nested.deep_string = "deep_value"`, expectedExpr: Equals(Member(Member(Text("nested_message"), "deep_nested"), "deep_string"), String("deep_value")), expectError: false, }, { name: "ok - deeply nested field, filter by parent field", opts: []FilterOption{WithFilterableFields("nested_message.deep_nested")}, filter: `nested_message.deep_nested.deep_string = "deep_value"`, expectedExpr: Equals(Member(Member(Text("nested_message"), "deep_nested"), "deep_string"), String("deep_value")), expectError: false, }, { name: "ok - deeply nested field, filter by parent parent", opts: []FilterOption{WithFilterableFields("nested_message")}, filter: `nested_message.deep_nested.deep_string = "deep_value"`, expectedExpr: Equals(Member(Member(Text("nested_message"), "deep_nested"), "deep_string"), String("deep_value")), expectError: false, }, // Complex expressions { name: "ok - multiple field types", opts: []FilterOption{WithFilterableFields("string_field", "int32_field", "bool_field")}, filter: `string_field = "test" AND int32_field > 10 AND bool_field`, expectedExpr: And( Equals(Text("string_field"), String("test")), GreaterThan(Text("int32_field"), Int(10)), Text("bool_field"), ), expectError: false, }, // Unsupported fields (these should be skipped during declaration) { name: "error - list field should not be declared", opts: []FilterOption{WithFilterableFields("string_list")}, filter: `string_list = "test"`, expectError: true, }, { name: "error -map field should not be declared", opts: []FilterOption{WithFilterableFields("string_map")}, filter: `string_map = "test"`, expectError: true, }, { name: "error - no filterable fields option provided", opts: []FilterOption{}, filter: `string_field = "test"`, expectError: true, }, { name: "error - empty filterable fields explicitly specified (no fields available)", opts: []FilterOption{WithFilterableFields()}, filter: `string_field = "test"`, expectError: true, }, { name: "error - field not in filterable fields list", opts: []FilterOption{WithFilterableFields("bool_field")}, filter: `string_field = "test"`, expectError: true, }, } for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Given // When declarations, err := ProtoDeclarations(protoMsg, tt.opts...) // Then assert.NilError(t, err) assert.Assert(t, declarations != nil) // Create a mock request with the test filter req := &mockRequest{ filter: tt.filter, } // Parse the filter using our declarations f, err := ParseFilter(req, declarations) if tt.expectError { assert.Assert(t, err != nil, "expected error for filter: %s", tt.filter) return } assert.NilError(t, err, "unexpected error for filter: %s", tt.filter) if tt.expectedExpr != nil { assert.DeepEqual( t, tt.expectedExpr, f.CheckedExpr.GetExpr(), protocmp.Transform(), protocmp.IgnoreFields(&expr.Expr{}, "id"), ) } }) } } aip-go-0.80.0/filtering/request.go000066400000000000000000000016501513342120500167530ustar00rootroot00000000000000package filtering // Request is an interface for gRPC requests that contain a standard AIP filter. type Request interface { GetFilter() string } // ParseFilter parses and type-checks the filter in the provided Request. func ParseFilter(request Request, declarations *Declarations) (Filter, error) { return ParseFilterString(request.GetFilter(), declarations) } // ParseFilter parses and type-checks the provided filter. func ParseFilterString(filter string, declarations *Declarations) (Filter, error) { if filter == "" { return Filter{}, nil } var parser Parser parser.Init(filter) parsedExpr, err := parser.Parse() if err != nil { return Filter{}, err } var checker Checker checker.Init(parsedExpr.GetExpr(), parsedExpr.GetSourceInfo(), declarations) checkedExpr, err := checker.Check() if err != nil { return Filter{}, err } return Filter{ CheckedExpr: checkedExpr, declarations: declarations, }, nil } aip-go-0.80.0/filtering/token.go000066400000000000000000000005751513342120500164100ustar00rootroot00000000000000package filtering // Token represents a token in a filter expression. type Token struct { // Position of the token. Position Position // Type of the token. Type TokenType // Value of the token, if the token is a text or a string. Value string } func (t Token) Unquote() (string, error) { if t.Type == TokenTypeString { return unescape(t.Value) } return t.Value, nil } aip-go-0.80.0/filtering/tokentype.go000066400000000000000000000043761513342120500173150ustar00rootroot00000000000000package filtering // TokenType represents the type of a filter expression token. // // See: https://google.aip.dev/assets/misc/ebnf-filtering.txt type TokenType string // Value token types. const ( TokenTypeWhitespace TokenType = "WS" TokenTypeText TokenType = "TEXT" TokenTypeString TokenType = "STRING" ) // Keyword token types. const ( TokenTypeNot TokenType = "NOT" TokenTypeAnd TokenType = "AND" TokenTypeOr TokenType = "OR" ) // Numeric token types. const ( TokenTypeNumber TokenType = "NUM" TokenTypeHexNumber TokenType = "HEX" ) // Operator token types. const ( TokenTypeLeftParen TokenType = "(" TokenTypeRightParen TokenType = ")" TokenTypeMinus TokenType = "-" TokenTypeDot TokenType = "." TokenTypeEquals TokenType = "=" TokenTypeHas TokenType = ":" TokenTypeLessThan TokenType = "<" TokenTypeGreaterThan TokenType = ">" TokenTypeExclaim TokenType = "!" TokenTypeComma TokenType = "," TokenTypeLessEquals TokenType = "<=" TokenTypeGreaterEquals TokenType = ">=" TokenTypeNotEquals TokenType = "!=" ) func (t TokenType) Function() string { if t.IsComparator() || t.IsKeyword() { return string(t) } return "" } // IsField returns true if the token is a valid value. func (t TokenType) IsValue() bool { switch t { case TokenTypeText, TokenTypeString: return true default: return false } } // IsField returns true if the token is a valid field. func (t TokenType) IsField() bool { return t.IsValue() || t.IsKeyword() || t == TokenTypeNumber } // IsName returns true if the token is a valid name. func (t TokenType) IsName() bool { return t == TokenTypeText || t.IsKeyword() } func (t TokenType) Test(other TokenType) bool { return t == other } // IsKeyword returns true if the token is a valid keyword. func (t TokenType) IsKeyword() bool { switch t { case TokenTypeNot, TokenTypeAnd, TokenTypeOr: return true default: return false } } // IsComparator returns true if the token is a valid comparator. func (t TokenType) IsComparator() bool { switch t { case TokenTypeLessEquals, TokenTypeLessThan, TokenTypeGreaterEquals, TokenTypeGreaterThan, TokenTypeNotEquals, TokenTypeEquals, TokenTypeHas: return true default: return false } } aip-go-0.80.0/filtering/types.go000066400000000000000000000030301513342120500164210ustar00rootroot00000000000000package filtering import ( expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/reflect/protoreflect" ) // Primitive types. // //nolint:gochecknoglobals var ( TypeInt = &expr.Type{TypeKind: &expr.Type_Primitive{Primitive: expr.Type_INT64}} TypeFloat = &expr.Type{TypeKind: &expr.Type_Primitive{Primitive: expr.Type_DOUBLE}} TypeString = &expr.Type{TypeKind: &expr.Type_Primitive{Primitive: expr.Type_STRING}} TypeBool = &expr.Type{TypeKind: &expr.Type_Primitive{Primitive: expr.Type_BOOL}} ) // TypeMap returns the type for a map with the provided key and value types. func TypeMap(keyType, valueType *expr.Type) *expr.Type { return &expr.Type{ TypeKind: &expr.Type_MapType_{ MapType: &expr.Type_MapType{ KeyType: keyType, ValueType: valueType, }, }, } } // TypeList returns the type for a list with the provided element type. func TypeList(elementType *expr.Type) *expr.Type { return &expr.Type{ TypeKind: &expr.Type_ListType_{ ListType: &expr.Type_ListType{ ElemType: elementType, }, }, } } // TypeEnum returns the type of a protobuf enum. func TypeEnum(enumType protoreflect.EnumType) *expr.Type { return &expr.Type{ TypeKind: &expr.Type_MessageType{ MessageType: string(enumType.Descriptor().FullName()), }, } } // Well-known types. // //nolint:gochecknoglobals var ( TypeDuration = &expr.Type{TypeKind: &expr.Type_WellKnown{WellKnown: expr.Type_DURATION}} TypeTimestamp = &expr.Type{TypeKind: &expr.Type_WellKnown{WellKnown: expr.Type_TIMESTAMP}} ) aip-go-0.80.0/filtering/unescape.go000066400000000000000000000113521513342120500170660ustar00rootroot00000000000000package filtering import ( "errors" "strings" "unicode/utf8" ) // The following code is adapted from // https://github.com/google/cel-go/blob/f6d3c92171c2c8732a8d0a4b24d6729df4261520/parser/unescape.go#L1-L237. // Unescape takes a quoted string, unquotes, and unescapes it. // // This function performs escaping compatible with GoogleSQL. func unescape(value string) (string, error) { // All strings normalize newlines to the \n representation. value = newlineNormalizer.Replace(value) n := len(value) // Nothing to unescape / decode. if n < 2 { return value, errors.New("unable to unescape string") } // Quoted string of some form, must have same first and last char. if value[0] != value[n-1] || (value[0] != '"' && value[0] != '\'') { return value, errors.New("unable to unescape string") } value = value[1 : n-1] // If there is nothing to escape, then return. if !strings.ContainsRune(value, '\\') { return value, nil } // Otherwise the string contains escape characters. // The following logic is adapted from `strconv/quote.go` var runeTmp [utf8.UTFMax]byte buf := make([]byte, 0, 3*n/2) for len(value) > 0 { c, encode, rest, err := unescapeChar(value, false) if err != nil { return "", err } value = rest if c < utf8.RuneSelf || !encode { buf = append(buf, byte(c)) } else { n := utf8.EncodeRune(runeTmp[:], c) buf = append(buf, runeTmp[:n]...) } } return string(buf), nil } // unescapeChar takes a string input and returns the following info: // // value - the escaped unicode rune at the front of the string. // encode - the value should be unicode-encoded // tail - the remainder of the input string. // err - error value, if the character could not be unescaped. // // When encode is true the return value may still fit within a single byte, // but unicode encoding is attempted which is more expensive than when the // value is known to self-represent as a single byte. // // If isBytes is set, unescape as a bytes literal so octal and hex escapes // represent byte values, not unicode code points. func unescapeChar(s string, isBytes bool) (value rune, encode bool, tail string, err error) { // 1. Character is not an escape sequence. switch c := s[0]; { case c >= utf8.RuneSelf: r, size := utf8.DecodeRuneInString(s) return r, true, s[size:], nil case c != '\\': return rune(s[0]), false, s[1:], nil } // 2. Last character is the start of an escape sequence. if len(s) <= 1 { return value, encode, tail, errors.New("unable to unescape string, found '\\' as last character") } c := s[1] s = s[2:] // 3. Common escape sequences shared with Google SQL switch c { case 'a': value = '\a' case 'b': value = '\b' case 'f': value = '\f' case 'n': value = '\n' case 'r': value = '\r' case 't': value = '\t' case 'v': value = '\v' case '\\': value = '\\' case '\'': value = '\'' case '"': value = '"' case '`': value = '`' case '?': value = '?' // 4. Unicode escape sequences, reproduced from `strconv/quote.go` case 'x', 'X', 'u', 'U': n := 0 encode = true switch c { case 'x', 'X': n = 2 encode = !isBytes case 'u': n = 4 if isBytes { return value, encode, tail, errors.New("unable to unescape string") } case 'U': n = 8 if isBytes { return value, encode, tail, errors.New("unable to unescape string") } } var v rune if len(s) < n { return value, encode, tail, errors.New("unable to unescape string") } for j := 0; j < n; j++ { x, ok := unhex(s[j]) if !ok { return value, encode, tail, errors.New("unable to unescape string") } v = v<<4 | x } s = s[n:] if !isBytes && !utf8.ValidRune(v) { return value, encode, tail, errors.New("invalid unicode code point") } value = v // 5. Octal escape sequences, must be three digits \[0-3][0-7][0-7] case '0', '1', '2', '3': if len(s) < 2 { return value, encode, tail, errors.New("unable to unescape octal sequence in string") } v := rune(c - '0') for j := 0; j < 2; j++ { x := s[j] if x < '0' || x > '7' { return value, encode, tail, errors.New("unable to unescape octal sequence in string") } v = v*8 + rune(x-'0') } if !isBytes && !utf8.ValidRune(v) { return value, encode, tail, errors.New("invalid unicode code point") } value = v s = s[2:] encode = !isBytes // Unknown escape sequence. default: err = errors.New("unable to unescape string") } tail = s return value, encode, tail, err } func unhex(b byte) (rune, bool) { c := rune(b) switch { case '0' <= c && c <= '9': return c - '0', true case 'a' <= c && c <= 'f': return c - 'a' + 10, true case 'A' <= c && c <= 'F': return c - 'A' + 10, true } return 0, false } //nolint:gochecknoglobals var newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n") aip-go-0.80.0/filtering/unescape_test.go000066400000000000000000000033171513342120500201270ustar00rootroot00000000000000package filtering import ( "errors" "strings" "testing" ) // The following code is adapted from // https://github.com/google/cel-go/blob/f6d3c92171c2c8732a8d0a4b24d6729df4261520/parser/unescape.go#L1-L237. func TestUnescape(t *testing.T) { tests := []struct { in string out interface{} }{ // Simple string unescaping tests. {in: `'hello'`, out: `hello`}, {in: `""`, out: ``}, {in: `"\\\""`, out: `\"`}, {in: `"\\"`, out: `\`}, {in: `"\303\277"`, out: `ÿ`}, {in: `"\377"`, out: `ÿ`}, {in: `"\u263A\u263A"`, out: `☺☺`}, {in: `"\a\b\f\n\r\t\v\'\"\\\? Legal escapes"`, out: "\a\b\f\n\r\t\v'\"\\? Legal escapes"}, // Escaping errors. {in: `"\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>"`, out: errors.New("unable to unescape string")}, {in: `"\u00f"`, out: errors.New("unable to unescape string")}, {in: `"\u00fÿ"`, out: errors.New("unable to unescape string")}, {in: `"\26"`, out: errors.New("unable to unescape octal sequence")}, {in: `"\268"`, out: errors.New("unable to unescape octal sequence")}, {in: `"\267\"`, out: errors.New(`found '\' as last character`)}, {in: `'`, out: errors.New("unable to unescape string")}, {in: `*hello*`, out: errors.New("unable to unescape string")}, } for _, tst := range tests { tc := tst t.Run(tc.in, func(t *testing.T) { got, err := unescape(tc.in) if err != nil { expect, isErr := tc.out.(error) if isErr { if !strings.Contains(err.Error(), expect.Error()) { t.Errorf("unescape(%s) errored with %v, wanted %v", tc.in, err, expect) } } else { t.Fatalf("unescape(%s) failed: %v", tc.in, err) } } else if got != tc.out { t.Errorf("unescape(%s) got %v, wanted %v", tc.in, got, tc.out) } }) } } aip-go-0.80.0/filtering/utils_test.go000066400000000000000000000160301513342120500174600ustar00rootroot00000000000000package filtering import ( "testing" "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/dynamicpb" "gotest.tools/v3/assert" ) // fullProtobufMessage creates a comprehensive dynamic protobuf message for testing which includes // all supported protocol buffers field types. // EXPERIMENTAL: This function is experimental and may be changed or removed in the future. func fullProtobufMessage(t *testing.T) *dynamicpb.Message { // Create enum descriptor enumDesc := &descriptorpb.EnumDescriptorProto{ Name: toPtr("TestEnum"), Value: []*descriptorpb.EnumValueDescriptorProto{ {Name: toPtr("ENUM_VALUE_ZERO"), Number: toPtr(int32(0))}, {Name: toPtr("ENUM_VALUE_ONE"), Number: toPtr(int32(1))}, {Name: toPtr("ENUM_VALUE_TWO"), Number: toPtr(int32(2))}, }, } // Create nested message descriptor deepNestedDesc := &descriptorpb.DescriptorProto{ Name: toPtr("DeepNested"), Field: []*descriptorpb.FieldDescriptorProto{ { Name: toPtr("deep_string"), Number: toPtr(int32(1)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_STRING), }, }, } nestedDesc := &descriptorpb.DescriptorProto{ Name: toPtr("NestedMessage"), Field: []*descriptorpb.FieldDescriptorProto{ { Name: toPtr("nested_string"), Number: toPtr(int32(1)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_STRING), }, { Name: toPtr("deep_nested"), Number: toPtr(int32(2)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_MESSAGE), TypeName: toPtr(".test.TestMessage.NestedMessage.DeepNested"), }, }, NestedType: []*descriptorpb.DescriptorProto{deepNestedDesc}, } // Create main message descriptor with all field types msgDesc := &descriptorpb.DescriptorProto{ Name: toPtr("TestMessage"), Field: []*descriptorpb.FieldDescriptorProto{ // String field { Name: toPtr("string_field"), Number: toPtr(int32(1)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_STRING), }, // Bool field { Name: toPtr("bool_field"), Number: toPtr(int32(2)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_BOOL), }, // Integer fields { Name: toPtr("int32_field"), Number: toPtr(int32(3)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_INT32), }, { Name: toPtr("int64_field"), Number: toPtr(int32(4)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_INT64), }, { Name: toPtr("sint32_field"), Number: toPtr(int32(5)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_SINT32), }, { Name: toPtr("sint64_field"), Number: toPtr(int32(6)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_SINT64), }, { Name: toPtr("sfixed32_field"), Number: toPtr(int32(7)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_SFIXED32), }, { Name: toPtr("sfixed64_field"), Number: toPtr(int32(8)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_SFIXED64), }, // Unsigned integer fields { Name: toPtr("uint32_field"), Number: toPtr(int32(9)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_UINT32), }, { Name: toPtr("uint64_field"), Number: toPtr(int32(10)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_UINT64), }, { Name: toPtr("fixed32_field"), Number: toPtr(int32(11)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_FIXED32), }, { Name: toPtr("fixed64_field"), Number: toPtr(int32(12)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_FIXED64), }, // Float fields { Name: toPtr("float_field"), Number: toPtr(int32(13)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_FLOAT), }, { Name: toPtr("double_field"), Number: toPtr(int32(14)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_DOUBLE), }, // Bytes field { Name: toPtr("bytes_field"), Number: toPtr(int32(15)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_BYTES), }, // Enum field { Name: toPtr("enum_field"), Number: toPtr(int32(16)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_ENUM), TypeName: toPtr(".test.TestMessage.TestEnum"), }, // Timestamp field (well-known type) { Name: toPtr("timestamp_field"), Number: toPtr(int32(17)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_MESSAGE), TypeName: toPtr(".google.protobuf.Timestamp"), }, // Nested message field { Name: toPtr("nested_message"), Number: toPtr(int32(18)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_MESSAGE), TypeName: toPtr(".test.TestMessage.NestedMessage"), }, // List fields (should be skipped) { Name: toPtr("string_list"), Number: toPtr(int32(19)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_STRING), Label: toPtr(descriptorpb.FieldDescriptorProto_LABEL_REPEATED), }, // Map field (should be skipped) - maps are represented as repeated nested messages { Name: toPtr("string_map"), Number: toPtr(int32(20)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_MESSAGE), TypeName: toPtr(".test.TestMessage.StringMapEntry"), Label: toPtr(descriptorpb.FieldDescriptorProto_LABEL_REPEATED), }, // Duration field (well-known type) { Name: toPtr("duration_field"), Number: toPtr(int32(21)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_MESSAGE), TypeName: toPtr(".google.protobuf.Duration"), }, }, EnumType: []*descriptorpb.EnumDescriptorProto{enumDesc}, NestedType: []*descriptorpb.DescriptorProto{nestedDesc}, } // Map entry message for the map field mapEntryDesc := &descriptorpb.DescriptorProto{ Name: toPtr("StringMapEntry"), Options: &descriptorpb.MessageOptions{ MapEntry: toPtr(true), }, Field: []*descriptorpb.FieldDescriptorProto{ { Name: toPtr("key"), Number: toPtr(int32(1)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_STRING), }, { Name: toPtr("value"), Number: toPtr(int32(2)), Type: toPtr(descriptorpb.FieldDescriptorProto_TYPE_STRING), }, }, } msgDesc.NestedType = append(msgDesc.NestedType, mapEntryDesc) // Create file descriptor fileDesc := &descriptorpb.FileDescriptorProto{ Name: toPtr("test.proto"), Package: toPtr("test"), MessageType: []*descriptorpb.DescriptorProto{msgDesc}, Dependency: []string{"google/protobuf/timestamp.proto", "google/protobuf/duration.proto"}, } // Convert to protoreflect descriptor using global registry (includes well-known types) protoFile, err := protodesc.NewFile(fileDesc, protoregistry.GlobalFiles) assert.NilError(t, err) // Get the message descriptor messageDesc := protoFile.Messages().ByName("TestMessage") assert.Assert(t, messageDesc != nil) // Create dynamic message dynamicMsg := dynamicpb.NewMessage(messageDesc) return dynamicMsg } func toPtr[T any](v T) *T { return &v } aip-go-0.80.0/filtering/walk.go000066400000000000000000000026121513342120500162200ustar00rootroot00000000000000package filtering import expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" // WalkFunc is called for every expression while calling Walk. // Return false to stop Walk. type WalkFunc func(currExpr, parentExpr *expr.Expr) bool // Walk an expression in depth-first order. func Walk(fn WalkFunc, currExpr *expr.Expr) { walk(fn, currExpr, nil) } func walk(fn WalkFunc, currExpr, parentExpr *expr.Expr) { if fn == nil || currExpr == nil { return } if ok := fn(currExpr, parentExpr); !ok { return } switch v := currExpr.GetExprKind().(type) { case *expr.Expr_ConstExpr, *expr.Expr_IdentExpr: // Nothing to do here. case *expr.Expr_SelectExpr: walk(fn, v.SelectExpr.GetOperand(), currExpr) case *expr.Expr_CallExpr: walk(fn, v.CallExpr.GetTarget(), currExpr) for _, arg := range v.CallExpr.GetArgs() { walk(fn, arg, currExpr) } case *expr.Expr_ListExpr: for _, el := range v.ListExpr.GetElements() { walk(fn, el, currExpr) } case *expr.Expr_StructExpr: for _, entry := range v.StructExpr.GetEntries() { walk(fn, entry.GetValue(), currExpr) } case *expr.Expr_ComprehensionExpr: walk(fn, v.ComprehensionExpr.GetIterRange(), currExpr) walk(fn, v.ComprehensionExpr.GetAccuInit(), currExpr) walk(fn, v.ComprehensionExpr.GetLoopCondition(), currExpr) walk(fn, v.ComprehensionExpr.GetLoopStep(), currExpr) walk(fn, v.ComprehensionExpr.GetResult(), currExpr) } } aip-go-0.80.0/go.mod000066400000000000000000000011601513342120500140530ustar00rootroot00000000000000module go.einride.tech/aip go 1.23.0 toolchain go1.24.4 require ( github.com/google/uuid v1.6.0 github.com/stoewer/go-strcase v1.3.1 google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a google.golang.org/grpc v1.74.2 google.golang.org/protobuf v1.36.6 gotest.tools/v3 v3.5.2 ) require ( github.com/google/go-cmp v0.7.0 // indirect golang.org/x/net v0.40.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect ) aip-go-0.80.0/go.sum000066400000000000000000000121061513342120500141020ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d h1:/hmn0Ku5kWij/kjGsrcJeC1T/MrJi2iNWwgAqrihFwc= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ= google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= aip-go-0.80.0/ordering/000077500000000000000000000000001513342120500145605ustar00rootroot00000000000000aip-go-0.80.0/ordering/doc.go000066400000000000000000000002541513342120500156550ustar00rootroot00000000000000// Package ordering provides primitives for implementing AIP ordering. // // See: https://google.aip.dev/132#ordering (Standard methods: List > Ordering). package ordering aip-go-0.80.0/ordering/orderby.go000066400000000000000000000053701513342120500165620ustar00rootroot00000000000000package ordering import ( "fmt" "strconv" "strings" "unicode" "go.einride.tech/aip/fieldmask" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/fieldmaskpb" ) // OrderBy represents an ordering directive. type OrderBy struct { // Fields are the fields to order by. Fields []Field } // Field represents a single ordering field. type Field struct { // Path is the path of the field, including subfields. Path string // Desc indicates if the ordering of the field is descending. Desc bool } // SubFields returns the individual subfields of the field path, including the top-level subfield. // // Subfields are specified with a . character, such as foo.bar or address.street. func (f Field) SubFields() []string { if f.Path == "" { return nil } return strings.Split(f.Path, ".") } // UnmarshalString sets o from the provided ordering string. . func (o *OrderBy) UnmarshalString(s string) error { o.Fields = o.Fields[:0] if s == "" { // fast path for no ordering return nil } for _, r := range s { if !unicode.IsLetter(r) && !unicode.IsNumber(r) && r != '_' && r != ' ' && r != ',' && r != '.' { return fmt.Errorf("unmarshal order by '%s': invalid character %s", s, strconv.QuoteRune(r)) } } fields := strings.Split(s, ",") o.Fields = make([]Field, 0, len(fields)) for _, field := range fields { parts := strings.Fields(field) switch len(parts) { case 1: // default ordering (ascending) o.Fields = append(o.Fields, Field{Path: parts[0]}) case 2: // specific ordering order := parts[1] var desc bool switch order { case "asc": desc = false case "desc": desc = true default: // parse error return fmt.Errorf("unmarshal order by '%s': invalid format", s) } o.Fields = append(o.Fields, Field{Path: parts[0], Desc: desc}) case 0: fallthrough default: return fmt.Errorf("unmarshal order by '%s': invalid format", s) } } return nil } // ValidateForMessage validates that the ordering paths are syntactically valid and // refer to known fields in the specified message type. func (o OrderBy) ValidateForMessage(m proto.Message) error { fm := fieldmaskpb.FieldMask{ Paths: make([]string, 0, len(o.Fields)), } for _, field := range o.Fields { fm.Paths = append(fm.Paths, field.Path) } return fieldmask.Validate(&fm, m) } // ValidateForPaths validates that the ordering paths are syntactically valid and refer to one of the provided paths. func (o OrderBy) ValidateForPaths(paths ...string) error { FieldLoop: for _, field := range o.Fields { // Assumption that len(paths) is short enough that O(n^2) is not a problem. for _, path := range paths { if field.Path == path { continue FieldLoop } } return fmt.Errorf("invalid field path: %s", field.Path) } return nil } aip-go-0.80.0/ordering/orderby_test.go000066400000000000000000000111531513342120500176150ustar00rootroot00000000000000package ordering import ( "testing" "google.golang.org/genproto/googleapis/example/library/v1" "google.golang.org/protobuf/proto" "gotest.tools/v3/assert" ) func TestOrderBy_UnmarshalString(t *testing.T) { t.Parallel() for _, tt := range []struct { orderBy string expected OrderBy errorContains string }{ { orderBy: "", expected: OrderBy{}, }, { orderBy: "foo desc, bar", expected: OrderBy{ Fields: []Field{ {Path: "foo", Desc: true}, {Path: "bar"}, }, }, }, { orderBy: "foo.bar", expected: OrderBy{ Fields: []Field{ {Path: "foo.bar"}, }, }, }, { orderBy: " foo , bar desc ", expected: OrderBy{ Fields: []Field{ {Path: "foo"}, {Path: "bar", Desc: true}, }, }, }, {orderBy: "foo,", errorContains: "invalid format"}, {orderBy: ",", errorContains: "invalid "}, {orderBy: ",foo", errorContains: "invalid format"}, {orderBy: "foo/bar", errorContains: "invalid character '/'"}, {orderBy: "foo bar", errorContains: "invalid format"}, } { t.Run(tt.orderBy, func(t *testing.T) { t.Parallel() var actual OrderBy err := actual.UnmarshalString(tt.orderBy) if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) } else { assert.NilError(t, err) assert.DeepEqual(t, tt.expected, actual) } }) } } func TestOrderBy_ValidateForPaths(t *testing.T) { t.Parallel() for _, tt := range []struct { name string orderBy OrderBy paths []string errorContains string }{ { name: "valid empty", orderBy: OrderBy{}, paths: []string{}, }, { name: "valid single", orderBy: OrderBy{ Fields: []Field{ {Path: "name"}, {Path: "author"}, }, }, paths: []string{"name", "author", "read"}, }, { name: "invalid single", orderBy: OrderBy{ Fields: []Field{ {Path: "name"}, {Path: "foo"}, }, }, paths: []string{"name", "author", "read"}, errorContains: "invalid field path: foo", }, { name: "valid nested", orderBy: OrderBy{ Fields: []Field{ {Path: "name"}, {Path: "book.name"}, }, }, paths: []string{"name", "book.name", "book.author", "book.read"}, }, { name: "invalid nested", orderBy: OrderBy{ Fields: []Field{ {Path: "name"}, {Path: "book.foo"}, }, }, paths: []string{"name", "book.name", "book.author", "book.read"}, errorContains: "invalid field path: book.foo", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() if tt.errorContains != "" { assert.ErrorContains(t, tt.orderBy.ValidateForPaths(tt.paths...), tt.errorContains) } else { assert.NilError(t, tt.orderBy.ValidateForPaths(tt.paths...)) } }) } } func TestOrderBy_ValidateForMessage(t *testing.T) { t.Parallel() for _, tt := range []struct { name string orderBy OrderBy message proto.Message errorContains string }{ { name: "valid empty", orderBy: OrderBy{}, message: &library.Book{}, }, { name: "valid single", orderBy: OrderBy{ Fields: []Field{ {Path: "name"}, {Path: "author"}, }, }, message: &library.Book{}, }, { name: "invalid single", orderBy: OrderBy{ Fields: []Field{ {Path: "name"}, {Path: "foo"}, }, }, message: &library.Book{}, errorContains: "invalid field path: foo", }, { name: "valid nested", orderBy: OrderBy{ Fields: []Field{ {Path: "parent"}, {Path: "book.name"}, }, }, message: &library.CreateBookRequest{}, }, { name: "invalid nested", orderBy: OrderBy{ Fields: []Field{ {Path: "parent"}, {Path: "book.foo"}, }, }, message: &library.CreateBookRequest{}, errorContains: "invalid field path: book.foo", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() if tt.errorContains != "" { assert.ErrorContains(t, tt.orderBy.ValidateForMessage(tt.message), tt.errorContains) } else { assert.NilError(t, tt.orderBy.ValidateForMessage(tt.message)) } }) } } func TestField_SubFields(t *testing.T) { t.Parallel() for _, tt := range []struct { name string field Field expected []string }{ { name: "empty", field: Field{}, expected: nil, }, { name: "single", field: Field{Path: "foo"}, expected: []string{"foo"}, }, { name: "multiple", field: Field{Path: "foo.bar"}, expected: []string{"foo", "bar"}, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() assert.DeepEqual(t, tt.expected, tt.field.SubFields()) }) } } aip-go-0.80.0/ordering/request.go000066400000000000000000000010031513342120500165710ustar00rootroot00000000000000package ordering // Request is an interface for requests that support ordering. // // See: https://google.aip.dev/132#ordering (Standard methods: List > Ordering). type Request interface { // GetOrderBy returns the ordering of the request. GetOrderBy() string } // ParseOrderBy request parses the ordering field for a Request. func ParseOrderBy(r Request) (OrderBy, error) { var orderBy OrderBy if err := orderBy.UnmarshalString(r.GetOrderBy()); err != nil { return OrderBy{}, err } return orderBy, nil } aip-go-0.80.0/ordering/request_test.go000066400000000000000000000014321513342120500176360ustar00rootroot00000000000000package ordering import ( "testing" "gotest.tools/v3/assert" ) func TestParseOrderBy(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() r := mockRequest{orderBy: "foo asc,bar desc"} expected := OrderBy{ Fields: []Field{ {Path: "foo"}, {Path: "bar", Desc: true}, }, } actual, err := ParseOrderBy(r) assert.NilError(t, err) assert.DeepEqual(t, expected, actual) }) t.Run("error", func(t *testing.T) { t.Parallel() r := mockRequest{orderBy: "/foo"} actual, err := ParseOrderBy(r) assert.ErrorContains(t, err, "invalid character '/'") assert.DeepEqual(t, OrderBy{}, actual) }) } type mockRequest struct { orderBy string } var _ Request = &mockRequest{} func (m mockRequest) GetOrderBy() string { return m.orderBy } aip-go-0.80.0/pagination/000077500000000000000000000000001513342120500151005ustar00rootroot00000000000000aip-go-0.80.0/pagination/doc.go000066400000000000000000000002221513342120500161700ustar00rootroot00000000000000// Package pagination provides primitives for implementing AIP pagination. // // See: https://google.aip.dev/158 (Pagination). package pagination aip-go-0.80.0/pagination/pagetoken.go000066400000000000000000000037461513342120500174160ustar00rootroot00000000000000package pagination import ( "fmt" ) // PageToken is a page token that uses an offset to delineate which page to fetch. type PageToken struct { // Offset of the page. Offset int64 // RequestChecksum is the checksum of the request that generated the page token. RequestChecksum uint32 } // pageTokenChecksumMask is a random bitmask applied to offset-based page token checksums. // // Change the bitmask to force checksum failures when changing the page token implementation. const pageTokenChecksumMask uint32 = 0x9acb0442 // ParsePageToken parses an offset-based page token from the provided Request. // // If the request does not have a page token, a page token with offset 0 will be returned. func ParsePageToken(request Request) (_ PageToken, err error) { defer func() { if err != nil { err = fmt.Errorf("parse offset page token: %w", err) } }() requestChecksum, err := CalculateRequestChecksum(request) if err != nil { return PageToken{}, err } requestChecksum ^= pageTokenChecksumMask // apply checksum mask for PageToken if request.GetPageToken() == "" { offset := int64(0) if s, ok := request.(skipRequest); ok { offset += int64(s.GetSkip()) } return PageToken{ Offset: offset, RequestChecksum: requestChecksum, }, nil } var pageToken PageToken if err := DecodePageTokenStruct(request.GetPageToken(), &pageToken); err != nil { return PageToken{}, err } if pageToken.RequestChecksum != requestChecksum { return PageToken{}, fmt.Errorf( "checksum mismatch (got 0x%x but expected 0x%x)", pageToken.RequestChecksum, requestChecksum, ) } if s, ok := request.(skipRequest); ok { pageToken.Offset += int64(s.GetSkip()) } return pageToken, nil } // Next returns the next page token for the provided Request. func (p PageToken) Next(request Request) PageToken { p.Offset += int64(request.GetPageSize()) return p } // String returns a string representation of the page token. func (p PageToken) String() string { return EncodePageTokenStruct(&p) } aip-go-0.80.0/pagination/pagetoken_test.go000066400000000000000000000106651513342120500204530ustar00rootroot00000000000000package pagination import ( "testing" freightv1 "go.einride.tech/aip/proto/gen/einride/example/freight/v1" "google.golang.org/genproto/googleapis/example/library/v1" "gotest.tools/v3/assert" ) func TestParseOffsetPageToken(t *testing.T) { t.Parallel() t.Run("valid checksums", func(t *testing.T) { t.Parallel() request1 := &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 10, } pageToken1, err := ParsePageToken(request1) assert.NilError(t, err) request2 := &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 20, PageToken: pageToken1.Next(request1).String(), } pageToken2, err := ParsePageToken(request2) assert.NilError(t, err) assert.Equal(t, int64(10), pageToken2.Offset) request3 := &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 30, PageToken: pageToken2.Next(request2).String(), } pageToken3, err := ParsePageToken(request3) assert.NilError(t, err) assert.Equal(t, int64(30), pageToken3.Offset) }) t.Run("skip", func(t *testing.T) { t.Run("docs example 1", func(t *testing.T) { // From https://google.aip.dev/158: // A request with no page token and a skip value of 30 returns a single // page of results starting with the 31st result. pageToken, err := ParsePageToken(&freightv1.ListSitesRequest{ Parent: "shippers/1", Skip: 30, }) assert.NilError(t, err) assert.Equal(t, int64(30), pageToken.Offset) // 31st result }) t.Run("docs example 2", func(t *testing.T) { // From https://google.aip.dev/158: // A request with a page token corresponding to the 51st result (because the first // 50 results were returned on the first page) and a skip value of 30 returns a // single page of results starting with the 81st result. request1 := &freightv1.ListSitesRequest{ Parent: "shippers/1", PageSize: 50, } pageToken1, err := ParsePageToken(request1) assert.NilError(t, err) request2 := &freightv1.ListSitesRequest{ Parent: "shippers/1", Skip: 30, PageSize: 50, PageToken: pageToken1.Next(request1).String(), } pageToken2, err := ParsePageToken(request2) assert.NilError(t, err) assert.Equal(t, int64(80), pageToken2.Offset) }) t.Run("handle empty token with skip", func(t *testing.T) { request1 := &freightv1.ListSitesRequest{ Parent: "shippers/1", Skip: 30, PageSize: 20, } pageToken1, err := ParsePageToken(request1) assert.NilError(t, err) assert.Equal(t, int64(30), pageToken1.Offset) }) t.Run("handle existing token with another skip", func(t *testing.T) { request1 := &freightv1.ListSitesRequest{ Parent: "shippers/1", Skip: 50, PageSize: 20, } pageToken1, err := ParsePageToken(request1) assert.NilError(t, err) assert.Equal(t, int64(50), pageToken1.Offset) request2 := &freightv1.ListSitesRequest{ Parent: "shippers/1", Skip: 30, PageSize: 0, PageToken: pageToken1.String(), } pageToken2, err := ParsePageToken(request2) assert.NilError(t, err) pageToken3 := pageToken2.Next(request2) assert.Equal(t, int64(80), pageToken3.Offset) }) t.Run("handle existing token with pagesize and skip", func(t *testing.T) { request1 := &freightv1.ListSitesRequest{ Parent: "shippers/1", Skip: 50, PageSize: 20, } pageToken1, err := ParsePageToken(request1) assert.NilError(t, err) assert.Equal(t, int64(50), pageToken1.Offset) request2 := &freightv1.ListSitesRequest{ Parent: "shippers/1", Skip: 30, PageSize: 20, PageToken: pageToken1.String(), } pageToken2, err := ParsePageToken(request2) assert.NilError(t, err) pageToken3 := pageToken2.Next(request2) assert.Equal(t, int64(100), pageToken3.Offset) }) }) t.Run("invalid format", func(t *testing.T) { t.Parallel() request := &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 10, PageToken: "invalid", } pageToken1, err := ParsePageToken(request) assert.ErrorContains(t, err, "decode") assert.Equal(t, PageToken{}, pageToken1) }) t.Run("invalid checksum", func(t *testing.T) { t.Parallel() request := &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 10, PageToken: EncodePageTokenStruct(&PageToken{ Offset: 100, RequestChecksum: 1234, // invalid }), } pageToken1, err := ParsePageToken(request) assert.ErrorContains(t, err, "checksum") assert.Equal(t, PageToken{}, pageToken1) }) } aip-go-0.80.0/pagination/request.go000066400000000000000000000024151513342120500171210ustar00rootroot00000000000000package pagination import ( "fmt" "hash/crc32" "google.golang.org/protobuf/proto" ) // Request is an interface for paginated request messages. // // See: https://google.aip.dev/158 (Pagination). type Request interface { proto.Message // GetPageToken returns the page token of the request. GetPageToken() string // GetPageSize returns the page size of the request. GetPageSize() int32 } type skipRequest interface { proto.Message // GetSkip returns the skip of the request. // See: https://google.aip.dev/158#skipping-results GetSkip() int32 } // CalculateRequestChecksum calculates a checksum for all fields of the request that must be the same across calls. func CalculateRequestChecksum(request Request) (uint32, error) { // Clone the original request, clear fields that may vary across calls, then checksum the resulting message. clonedRequest := proto.Clone(request) r := clonedRequest.ProtoReflect() r.Clear(r.Descriptor().Fields().ByName("page_token")) r.Clear(r.Descriptor().Fields().ByName("page_size")) if _, ok := request.(skipRequest); ok { r.Clear(r.Descriptor().Fields().ByName("skip")) } data, err := proto.Marshal(clonedRequest) if err != nil { return 0, fmt.Errorf("calculate request checksum: %w", err) } return crc32.ChecksumIEEE(data), nil } aip-go-0.80.0/pagination/request_test.go000066400000000000000000000042061513342120500201600ustar00rootroot00000000000000package pagination import ( "testing" freightv1 "go.einride.tech/aip/proto/gen/einride/example/freight/v1" "google.golang.org/genproto/googleapis/example/library/v1" "gotest.tools/v3/assert" ) func TestCalculateRequestChecksum(t *testing.T) { t.Parallel() for _, tt := range []struct { name string request1 Request request2 Request equal bool }{ { name: "same request", request1: &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 100, PageToken: "token", }, request2: &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 100, PageToken: "token", }, equal: true, }, { name: "different parents", request1: &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 100, PageToken: "token", }, request2: &library.ListBooksRequest{ Parent: "shelves/2", PageSize: 100, PageToken: "token", }, equal: false, }, { name: "different page sizes", request1: &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 100, PageToken: "token", }, request2: &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 200, PageToken: "token", }, equal: true, }, { name: "different page tokens", request1: &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 100, PageToken: "token", }, request2: &library.ListBooksRequest{ Parent: "shelves/1", PageSize: 100, PageToken: "token2", }, equal: true, }, { name: "different skips", request1: &freightv1.ListSitesRequest{ Parent: "shippers/1", PageSize: 100, Skip: 0, }, request2: &freightv1.ListSitesRequest{ Parent: "shippers/1", PageSize: 100, Skip: 30, }, equal: true, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() checksum1, err := CalculateRequestChecksum(tt.request1) assert.NilError(t, err) checksum2, err := CalculateRequestChecksum(tt.request2) assert.NilError(t, err) if tt.equal { assert.Assert(t, checksum1 == checksum2) } else { assert.Assert(t, checksum1 != checksum2) } }) } } aip-go-0.80.0/pagination/struct.go000066400000000000000000000014261513342120500167560ustar00rootroot00000000000000package pagination import ( "encoding/base64" "encoding/gob" "errors" "fmt" "io" "strings" ) // EncodePageTokenStruct encodes an arbitrary struct as a page token. func EncodePageTokenStruct(v interface{}) string { var b strings.Builder base64Encoder := base64.NewEncoder(base64.URLEncoding, &b) gobEncoder := gob.NewEncoder(base64Encoder) _ = gobEncoder.Encode(v) _ = base64Encoder.Close() return b.String() } // DecodePageTokenStruct decodes an encoded page token into an arbitrary struct. func DecodePageTokenStruct(s string, v interface{}) error { dec := gob.NewDecoder(base64.NewDecoder(base64.URLEncoding, strings.NewReader(s))) if err := dec.Decode(v); err != nil && !errors.Is(err, io.EOF) { return fmt.Errorf("decode page token struct: %w", err) } return nil } aip-go-0.80.0/pagination/struct_test.go000066400000000000000000000011741513342120500200150ustar00rootroot00000000000000package pagination import ( "testing" "gotest.tools/v3/assert" ) func Test_PageTokenStruct(t *testing.T) { t.Parallel() type pageToken struct { Int int String string } for _, tt := range []struct { name string in pageToken }{ { name: "all set", in: pageToken{ Int: 42, String: "foo", }, }, { name: "default value", in: pageToken{ String: "foo", }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() str := EncodePageTokenStruct(tt.in) var out pageToken assert.NilError(t, DecodePageTokenStruct(str, &out), str) assert.Equal(t, tt.in, out) }) } } aip-go-0.80.0/proto/000077500000000000000000000000001513342120500141125ustar00rootroot00000000000000aip-go-0.80.0/proto/.gitignore000066400000000000000000000000221513342120500160740ustar00rootroot00000000000000tools/*/*/ build/ aip-go-0.80.0/proto/Makefile000066400000000000000000000036301513342120500155540ustar00rootroot00000000000000# Code generated by go.einride.tech/sage. DO NOT EDIT. # To learn more, see ../.sage/main.go and https://github.com/einride/sage. .DEFAULT_GOAL := all cwd := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) sagefile := $(abspath $(cwd)/../.sage/bin/sagefile) # Setup Go. go := $(shell command -v go 2>/dev/null) export GOWORK ?= off ifndef go SAGE_GO_VERSION ?= 1.23.4 export GOROOT := $(abspath $(cwd)/../.sage/tools/go/$(SAGE_GO_VERSION)/go) export PATH := $(PATH):$(GOROOT)/bin go := $(GOROOT)/bin/go os := $(shell uname | tr '[:upper:]' '[:lower:]') arch := $(shell uname -m) ifeq ($(arch),x86_64) arch := amd64 endif $(go): $(info installing Go $(SAGE_GO_VERSION)...) @mkdir -p $(dir $(GOROOT)) @curl -sSL https://go.dev/dl/go$(SAGE_GO_VERSION).$(os)-$(arch).tar.gz | tar xz -C $(dir $(GOROOT)) @touch $(GOROOT)/go.mod @chmod +x $(go) endif .PHONY: $(sagefile) $(sagefile): $(go) @cd ../.sage && $(go) mod tidy && $(go) run . .PHONY: sage sage: @$(MAKE) $(sagefile) .PHONY: update-sage update-sage: $(go) @cd ../.sage && $(go) get go.einride.tech/sage@latest && $(go) mod tidy && $(go) run . .PHONY: clean-sage clean-sage: @git clean -fdx ../.sage/tools ../.sage/bin ../.sage/build .PHONY: api-linter-lint api-linter-lint: $(sagefile) @$(sagefile) Proto:APILinterLint .PHONY: all all: $(sagefile) @$(sagefile) Proto:All .PHONY: buf-format buf-format: $(sagefile) @$(sagefile) Proto:BufFormat .PHONY: buf-generate buf-generate: $(sagefile) @$(sagefile) Proto:BufGenerate .PHONY: buf-generate-testdata buf-generate-testdata: $(sagefile) @$(sagefile) Proto:BufGenerateTestdata .PHONY: buf-lint buf-lint: $(sagefile) @$(sagefile) Proto:BufLint .PHONY: protoc-gen-go protoc-gen-go: $(sagefile) @$(sagefile) Proto:ProtocGenGo .PHONY: protoc-gen-go-aip protoc-gen-go-aip: $(sagefile) @$(sagefile) Proto:ProtocGenGoAIP .PHONY: protoc-gen-go-grpc protoc-gen-go-grpc: $(sagefile) @$(sagefile) Proto:ProtocGenGoGRPC aip-go-0.80.0/proto/api-linter.yaml000066400000000000000000000004221513342120500170400ustar00rootroot00000000000000- included_paths: ["**"] disabled_rules: - core::0191 # file options managed by buf - included_paths: ["einride/example/syntax/v1/**"] disabled_rules: - core::0191 - core::0141::forbidden-types - core::0140::reserved-words - core::0192::has-comments aip-go-0.80.0/proto/buf.gen.yaml000066400000000000000000000010631513342120500163220ustar00rootroot00000000000000version: v1 managed: enabled: true go_package_prefix: default: go.einride.tech/aip/proto/gen except: - buf.build/googleapis/googleapis plugins: - name: go out: gen opt: module=go.einride.tech/aip/proto/gen path: ../.sage/bin/protoc-gen-go - name: go-grpc out: gen opt: - module=go.einride.tech/aip/proto/gen - require_unimplemented_servers=false path: ../.sage/bin/protoc-gen-go-grpc - name: go-aip out: gen opt: module=go.einride.tech/aip/proto/gen path: ../.sage/bin/protoc-gen-go-aip aip-go-0.80.0/proto/buf.lock000066400000000000000000000004751513342120500155460ustar00rootroot00000000000000# Generated by buf. DO NOT EDIT. version: v1 deps: - remote: buf.build owner: googleapis repository: googleapis commit: cc916c31859748a68fd229a3c8d7a2e8 digest: shake256:469b049d0eb04203d5272062636c078decefc96fec69739159c25d85349c50c34c7706918a8b216c5c27f76939df48452148cff8c5c3ae77fa6ba5c25c1b8bf8 aip-go-0.80.0/proto/buf.yaml000066400000000000000000000003241513342120500155510ustar00rootroot00000000000000version: v1 name: buf.build/einride/aip deps: - buf.build/googleapis/googleapis lint: use: - DEFAULT except: - RPC_RESPONSE_STANDARD_NAME - RPC_REQUEST_RESPONSE_UNIQUE ignore: - google aip-go-0.80.0/proto/einride/000077500000000000000000000000001513342120500155315ustar00rootroot00000000000000aip-go-0.80.0/proto/einride/example/000077500000000000000000000000001513342120500171645ustar00rootroot00000000000000aip-go-0.80.0/proto/einride/example/freight/000077500000000000000000000000001513342120500206145ustar00rootroot00000000000000aip-go-0.80.0/proto/einride/example/freight/v1/000077500000000000000000000000001513342120500211425ustar00rootroot00000000000000aip-go-0.80.0/proto/einride/example/freight/v1/freight_service.proto000066400000000000000000000344501513342120500254050ustar00rootroot00000000000000syntax = "proto3"; package einride.example.freight.v1; import "einride/example/freight/v1/shipment.proto"; import "einride/example/freight/v1/shipper.proto"; import "einride/example/freight/v1/site.proto"; import "google/api/annotations.proto"; import "google/api/client.proto"; import "google/api/field_behavior.proto"; import "google/api/resource.proto"; import "google/protobuf/field_mask.proto"; // This API represents a simple freight service. // // It defines the following resource model: // // - The API has a collection of Shipper resources. // // - Each Shipper has a collection of Site resources. // // - Each Shipper has a collection of Shipment resources. service FreightService { option (google.api.default_host) = "freight-example.einride.tech"; // Get a shipper. // // See: https://google.aip.dev/131 (Standard methods: Get). rpc GetShipper(GetShipperRequest) returns (Shipper) { option (google.api.http) = {get: "/v1/{name=shippers/*}"}; option (google.api.method_signature) = "name"; } // List shippers. // // See: https://google.aip.dev/132 (Standard methods: List). rpc ListShippers(ListShippersRequest) returns (ListShippersResponse) { option (google.api.http) = {get: "/v1/shippers"}; } // Create a shipper. // // See: https://google.aip.dev/133 (Standard methods: Create). rpc CreateShipper(CreateShipperRequest) returns (Shipper) { option (google.api.http) = { post: "/v1/shippers" body: "shipper" }; option (google.api.method_signature) = "shipper"; } // Update a shipper. // // See: https://google.aip.dev/134 (Standard methods: Update). rpc UpdateShipper(UpdateShipperRequest) returns (Shipper) { option (google.api.http) = { patch: "/v1/{shipper.name=shippers/*}" body: "shipper" }; option (google.api.method_signature) = "shipper,update_mask"; } // Delete a shipper. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). rpc DeleteShipper(DeleteShipperRequest) returns (Shipper) { option (google.api.http) = {delete: "/v1/{name=shippers/*}"}; option (google.api.method_signature) = "name"; } // Get a site. // // See: https://google.aip.dev/131 (Standard methods: Get). rpc GetSite(GetSiteRequest) returns (Site) { option (google.api.http) = {get: "/v1/{name=shippers/*/sites/*}"}; option (google.api.method_signature) = "name"; } // List sites for a shipper. // // See: https://google.aip.dev/132 (Standard methods: List). rpc ListSites(ListSitesRequest) returns (ListSitesResponse) { option (google.api.http) = {get: "/v1/{parent=shippers/*}/sites"}; option (google.api.method_signature) = "parent"; } // Create a site. // // See: https://google.aip.dev/133 (Standard methods: Create). rpc CreateSite(CreateSiteRequest) returns (Site) { option (google.api.http) = { post: "/v1/{parent=shippers/*}/sites" body: "site" }; option (google.api.method_signature) = "parent,site"; } // Update a site. // // See: https://google.aip.dev/134 (Standard methods: Update). rpc UpdateSite(UpdateSiteRequest) returns (Site) { option (google.api.http) = { patch: "/v1/{site.name=shippers/*/sites/*}" body: "site" }; option (google.api.method_signature) = "site,update_mask"; } // Delete a site. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). rpc DeleteSite(DeleteSiteRequest) returns (Site) { option (google.api.http) = {delete: "/v1/{name=shippers/*/sites/*}"}; option (google.api.method_signature) = "name"; } // Batch get sites. // // See: https://google.aip.dev/231 (Batch methods: Get). rpc BatchGetSites(BatchGetSitesRequest) returns (BatchGetSitesResponse) { option (google.api.http) = {get: "/v1/{parent=shippers/*}/sites:batchGet"}; } // Get a shipment. // // See: https://google.aip.dev/131 (Standard methods: Get). rpc GetShipment(GetShipmentRequest) returns (Shipment) { option (google.api.http) = {get: "/v1/{name=shippers/*/shipments/*}"}; option (google.api.method_signature) = "name"; } // List shipments for a shipper. // // See: https://google.aip.dev/132 (Standard methods: List). rpc ListShipments(ListShipmentsRequest) returns (ListShipmentsResponse) { option (google.api.http) = {get: "/v1/{parent=shippers/*}/shipments"}; option (google.api.method_signature) = "parent"; } // Create a shipment. // // See: https://google.aip.dev/133 (Standard methods: Create). rpc CreateShipment(CreateShipmentRequest) returns (Shipment) { option (google.api.http) = { post: "/v1/{parent=shippers/*}/shipments" body: "shipment" }; option (google.api.method_signature) = "parent,shipment"; } // Update a shipment. // // See: https://google.aip.dev/134 (Standard methods: Update). rpc UpdateShipment(UpdateShipmentRequest) returns (Shipment) { option (google.api.http) = { patch: "/v1/{shipment.name=shippers/*/shipments/*}" body: "shipment" }; option (google.api.method_signature) = "shipment,update_mask"; } // Delete a shipment. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). rpc DeleteShipment(DeleteShipmentRequest) returns (Shipment) { option (google.api.http) = {delete: "/v1/{name=shippers/*/shipments/*}"}; option (google.api.method_signature) = "name"; } } // Request message for FreightService.GetShipper. message GetShipperRequest { // The resource name of the shipper to retrieve. // Format: shippers/{shipper} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Shipper" ]; } // Request message for FreightService.ListShippers. message ListShippersRequest { // Requested page size. Server may return fewer shippers than requested. // If unspecified, server will pick an appropriate default. int32 page_size = 1; // A token identifying a page of results the server should return. // Typically, this is the value of // [ListShippersResponse.next_page_token][einride.example.freight.v1.ListShippersResponse.next_page_token] // returned from the previous call to `ListShippers` method. string page_token = 2; } // Response message for FreightService.ListShippers. message ListShippersResponse { // The list of shippers. repeated Shipper shippers = 1; // A token to retrieve next page of results. Pass this value in the // [ListShippersRequest.page_token][einride.example.freight.v1.ListShippersRequest.page_token] // field in the subsequent call to `ListShippers` method to retrieve the next // page of results. string next_page_token = 2; } // Request message for FreightService.CreateShipper. message CreateShipperRequest { // The shipper to create. Shipper shipper = 1 [(google.api.field_behavior) = REQUIRED]; } // Request message for FreightService.UpdateShipper. message UpdateShipperRequest { // The shipper to update with. The name must match or be empty. // The shipper's `name` field is used to identify the shipper to be updated. // Format: shippers/{shipper} Shipper shipper = 1 [(google.api.field_behavior) = REQUIRED]; // The list of fields to be updated. google.protobuf.FieldMask update_mask = 2; } // Request message for FreightService.DeleteShipper. message DeleteShipperRequest { // The resource name of the shipper to delete. // Format: shippers/{shipper} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Shipper" ]; } // Request message for FreightService.GetSite. message GetSiteRequest { // The resource name of the site to retrieve. // Format: shippers/{shipper}/sites/{site} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Site" ]; } // Request message for FreightService.ListSites. message ListSitesRequest { // The resource name of the parent, which owns this collection of sites. // Format: shippers/{shipper} string parent = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "freight-example.einride.tech/Shipper"} ]; // Requested page size. Server may return fewer sites than requested. // If unspecified, server will pick an appropriate default. int32 page_size = 2; // A token identifying a page of results the server should return. // Typically, this is the value of // [ListSitesResponse.next_page_token][einride.example.freight.v1.ListSitesResponse.next_page_token] // returned from the previous call to `ListSites` method. string page_token = 3; // Number of resource to skip in the request. // * A request with no page token and a skip value of 30 returns a single // page of results starting with the 31st result. // * A request with a page token corresponding to the 51st result (because the // first 50 results were returned on the first page) and a skip value of 30 // returns a single page of results starting with the 81st result. int32 skip = 4; } // Response message for FreightService.ListSites. message ListSitesResponse { // The list of sites. repeated Site sites = 1; // A token to retrieve next page of results. Pass this value in the // [ListSitesRequest.page_token][einride.example.freight.v1.ListSitesRequest.page_token] // field in the subsequent call to `ListSites` method to retrieve the next // page of results. string next_page_token = 2; } // Request message for FreightService.CreateSite. message CreateSiteRequest { // The resource name of the parent shipper for which this site will be created. // Format: shippers/{shipper} string parent = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "freight-example.einride.tech/Shipper"} ]; // The site to create. Site site = 2 [(google.api.field_behavior) = REQUIRED]; } // Request message for FreightService.UpdateSite. message UpdateSiteRequest { // The site to update with. The name must match or be empty. // The site's `name` field is used to identify the site to be updated. // Format: shippers/{shipper}/sites/{site} Site site = 1 [(google.api.field_behavior) = REQUIRED]; // The list of fields to be updated. google.protobuf.FieldMask update_mask = 2; } // Request message for FreightService.DeleteSite. message DeleteSiteRequest { // The resource name of the site to delete. // Format: shippers/{shipper}/sites/{site} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Site" ]; } // Request message for FreightService.BatchGetSites. message BatchGetSitesRequest { // The parent resource shared by all sites being retrieved. // If this is set, the parent of all of the sites specified in `names` // must match this field. // Format: `shippers/{shipper}` string parent = 1 [(google.api.resource_reference) = {type: "freight-example.einride.tech/Shipper"}]; // The names of the sites to retrieve. // A maximum of 1000 sites can be retrieved in a batch. // Format: `shippers/{shipper}/sites/{site}` repeated string names = 2 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "freight-example.einride.tech/Site"} ]; } // Response message for FreightService.BatchGetSites. message BatchGetSitesResponse { // Sites requested. repeated Site sites = 1; } // Request message for FreightService.GetShipment. message GetShipmentRequest { // The resource name of the shipment to retrieve. // Format: shippers/{shipper}/shipments/{shipment} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Shipment" ]; } // Request message for FreightService.ListShipments. message ListShipmentsRequest { // The resource name of the parent, which owns this collection of shipments. // Format: shippers/{shipper} string parent = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "freight-example.einride.tech/Shipper"} ]; // Requested page size. Server may return fewer shipments than requested. // If unspecified, server will pick an appropriate default. int32 page_size = 2; // A token identifying a page of results the server should return. // Typically, this is the value of // [ListShipmentsResponse.next_page_token][einride.example.freight.v1.ListShipmentsResponse.next_page_token] // returned from the previous call to `ListShipments` method. string page_token = 3; } // Response message for FreightService.ListShipments. message ListShipmentsResponse { // The list of shipments. repeated Shipment shipments = 1; // A token to retrieve next page of results. Pass this value in the // [ListShipmentsRequest.page_token][einride.example.freight.v1.ListShipmentsRequest.page_token] // field in the subsequent call to `ListShipments` method to retrieve the next // page of results. string next_page_token = 2; } // Request message for FreightService.CreateShipment. message CreateShipmentRequest { // The resource name of the parent shipper for which this shipment will be created. // Format: shippers/{shipper} string parent = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference) = {type: "freight-example.einride.tech/Shipper"} ]; // The shipment to create. Shipment shipment = 2 [(google.api.field_behavior) = REQUIRED]; } // Request message for FreightService.UpdateShipment. message UpdateShipmentRequest { // The shipment to update with. The name must match or be empty. // The shipment's `name` field is used to identify the shipment to be updated. // Format: shippers/{shipper}/shipments/{shipment} Shipment shipment = 1 [(google.api.field_behavior) = REQUIRED]; // The list of fields to be updated. google.protobuf.FieldMask update_mask = 2; } // Request message for FreightService.DeleteShipment. message DeleteShipmentRequest { // The resource name of the shipment to delete. // Format: shippers/{shipper}/shipments/{shipment} string name = 1 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Shipment" ]; } aip-go-0.80.0/proto/einride/example/freight/v1/shipment.proto000066400000000000000000000056601513342120500240650ustar00rootroot00000000000000syntax = "proto3"; package einride.example.freight.v1; import "google/api/field_behavior.proto"; import "google/api/resource.proto"; import "google/protobuf/timestamp.proto"; // A shipment represents transportation of goods between an origin // [site][einride.example.freight.v1.Site] and a destination // [site][einride.example.freight.v1.Site]. message Shipment { option (google.api.resource) = { type: "freight-example.einride.tech/Shipment" pattern: "shippers/{shipper}/shipments/{shipment}" singular: "shipment" plural: "shipments" }; // The resource name of the shipment. string name = 1; // The creation timestamp of the shipment. google.protobuf.Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; // The last update timestamp of the shipment. // // Updated when create/update/delete operation is shipment. google.protobuf.Timestamp update_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; // The deletion timestamp of the shipment. google.protobuf.Timestamp delete_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; // The resource name of the origin site of the shipment. // Format: shippers/{shipper}/sites/{site} string origin_site = 5 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Site" ]; // The resource name of the destination site of the shipment. // Format: shippers/{shipper}/sites/{site} string destination_site = 6 [ (google.api.field_behavior) = REQUIRED, (google.api.resource_reference).type = "freight-example.einride.tech/Site" ]; // The earliest pickup time of the shipment at the origin site. google.protobuf.Timestamp pickup_earliest_time = 7 [(google.api.field_behavior) = REQUIRED]; // The latest pickup time of the shipment at the origin site. google.protobuf.Timestamp pickup_latest_time = 8 [(google.api.field_behavior) = REQUIRED]; // The earliest delivery time of the shipment at the destination site. google.protobuf.Timestamp delivery_earliest_time = 9 [(google.api.field_behavior) = REQUIRED]; // The latest delivery time of the shipment at the destination site. google.protobuf.Timestamp delivery_latest_time = 10 [(google.api.field_behavior) = REQUIRED]; // The line items of the shipment. repeated LineItem line_items = 11; // Annotations of the shipment. map annotations = 12; // Reference ID provided by external system. string external_reference_id = 13 [(google.api.field_behavior) = IMMUTABLE]; } // A shipment line item. message LineItem { // The title of the line item. string title = 1; // The quantity of the line item. float quantity = 2; // The weight of the line item in kilograms. float weight_kg = 3; // The volume of the line item in cubic meters. float volume_m3 = 4; // Reference ID provided by external system. string external_reference_id = 5 [(google.api.field_behavior) = IMMUTABLE]; } aip-go-0.80.0/proto/einride/example/freight/v1/shipper.proto000066400000000000000000000020521513342120500237000ustar00rootroot00000000000000syntax = "proto3"; package einride.example.freight.v1; import "google/api/field_behavior.proto"; import "google/api/resource.proto"; import "google/protobuf/timestamp.proto"; // A shipper is a supplier or owner of goods to be transported. message Shipper { option (google.api.resource) = { type: "freight-example.einride.tech/Shipper" pattern: "shippers/{shipper}" singular: "shipper" plural: "shippers" }; // The resource name of the shipper. string name = 1; // The creation timestamp of the shipper. google.protobuf.Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; // The last update timestamp of the shipper. // // Updated when create/update/delete operation is performed. google.protobuf.Timestamp update_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; // The deletion timestamp of the shipper. google.protobuf.Timestamp delete_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; // The display name of the shipper. string display_name = 5 [(google.api.field_behavior) = REQUIRED]; } aip-go-0.80.0/proto/einride/example/freight/v1/site.proto000066400000000000000000000022521513342120500231740ustar00rootroot00000000000000syntax = "proto3"; package einride.example.freight.v1; import "google/api/field_behavior.proto"; import "google/api/resource.proto"; import "google/protobuf/timestamp.proto"; import "google/type/latlng.proto"; // A site is a node in a [shipper][einride.example.freight.v1.Shipper]'s // transport network. message Site { option (google.api.resource) = { type: "freight-example.einride.tech/Site" pattern: "shippers/{shipper}/sites/{site}" singular: "site" plural: "sites" }; // The resource name of the site. string name = 1; // The creation timestamp of the site. google.protobuf.Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; // The last update timestamp of the site. // // Updated when create/update/delete operation is performed. google.protobuf.Timestamp update_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; // The deletion timestamp of the site. google.protobuf.Timestamp delete_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; // The display name of the site. string display_name = 5 [(google.api.field_behavior) = REQUIRED]; // The geographic location of the site. google.type.LatLng lat_lng = 6; } aip-go-0.80.0/proto/einride/example/syntax/000077500000000000000000000000001513342120500205125ustar00rootroot00000000000000aip-go-0.80.0/proto/einride/example/syntax/v1/000077500000000000000000000000001513342120500210405ustar00rootroot00000000000000aip-go-0.80.0/proto/einride/example/syntax/v1/fieldbehaviors.proto000066400000000000000000000030401513342120500251100ustar00rootroot00000000000000syntax = "proto3"; package einride.example.syntax.v1; import "google/api/field_behavior.proto"; message FieldBehaviorMessage { string field = 1; string output_only_field = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; string optional_field = 3 [(google.api.field_behavior) = OPTIONAL]; string immutable_field = 17 [(google.api.field_behavior) = IMMUTABLE]; FieldBehaviorMessage message_without_field_behavior = 12; FieldBehaviorMessage output_only_message = 13 [(google.api.field_behavior) = OUTPUT_ONLY]; FieldBehaviorMessage optional_message = 14 [(google.api.field_behavior) = OPTIONAL]; repeated FieldBehaviorMessage repeated_message = 4; repeated FieldBehaviorMessage repeated_output_only_message = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; repeated FieldBehaviorMessage repeated_optional_message = 6 [(google.api.field_behavior) = OPTIONAL]; map map_message = 7; map map_output_only_message = 8 [(google.api.field_behavior) = OUTPUT_ONLY]; map map_optional_message = 9 [(google.api.field_behavior) = OPTIONAL]; map string_map = 10; repeated string string_list = 11; oneof oneof { FieldBehaviorMessage field_behavior_message = 15; SmallFieldBehaviorMessage small_field_behavior_message = 16; } } message SmallFieldBehaviorMessage { string field = 1; string output_only_field = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; string optional_field = 3 [(google.api.field_behavior) = OPTIONAL]; } aip-go-0.80.0/proto/einride/example/syntax/v1/syntax.proto000066400000000000000000000026231513342120500234560ustar00rootroot00000000000000syntax = "proto3"; package einride.example.syntax.v1; message Message { double double = 1; float float = 2; int32 int32 = 3; int64 int64 = 4; uint32 uint32 = 5; uint64 uint64 = 6; sint32 sint32 = 7; sint64 sint64 = 8; fixed32 fixed32 = 9; fixed64 fixed64 = 10; sfixed32 sfixed32 = 11; sfixed64 sfixed64 = 12; bool bool = 13; string string = 14; bytes bytes = 15; Enum enum = 16; Message message = 17; repeated double repeated_double = 18; repeated float repeated_float = 19; repeated int32 repeated_int32 = 20; repeated int64 repeated_int64 = 21; repeated uint32 repeated_uint32 = 22; repeated uint64 repeated_uint64 = 23; repeated sint32 repeated_sint32 = 24; repeated sint64 repeated_sint64 = 25; repeated fixed32 repeated_fixed32 = 26; repeated fixed64 repeated_fixed64 = 27; repeated sfixed32 repeated_sfixed32 = 28; repeated sfixed64 repeated_sfixed64 = 29; repeated bool repeated_bool = 30; repeated string repeated_string = 31; repeated bytes repeated_bytes = 32; repeated Enum repeated_enum = 33; repeated Message repeated_message = 34; map map_string_string = 35; map map_string_message = 36; oneof oneof { string oneof_string = 37; Enum oneof_enum = 38; Message oneof_message1 = 39; Message oneof_message2 = 40; } } enum Enum { ENUM_UNSPECIFIED = 0; ENUM_ONE = 1; ENUM_TWO = 2; } aip-go-0.80.0/proto/gen/000077500000000000000000000000001513342120500146635ustar00rootroot00000000000000aip-go-0.80.0/proto/gen/einride/000077500000000000000000000000001513342120500163025ustar00rootroot00000000000000aip-go-0.80.0/proto/gen/einride/example/000077500000000000000000000000001513342120500177355ustar00rootroot00000000000000aip-go-0.80.0/proto/gen/einride/example/freight/000077500000000000000000000000001513342120500213655ustar00rootroot00000000000000aip-go-0.80.0/proto/gen/einride/example/freight/v1/000077500000000000000000000000001513342120500217135ustar00rootroot00000000000000aip-go-0.80.0/proto/gen/einride/example/freight/v1/freight_service.pb.go000066400000000000000000001472601513342120500260240ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.6 // protoc (unknown) // source: einride/example/freight/v1/freight_service.proto package freightv1 import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Request message for FreightService.GetShipper. type GetShipperRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the shipper to retrieve. // Format: shippers/{shipper} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetShipperRequest) Reset() { *x = GetShipperRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetShipperRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetShipperRequest) ProtoMessage() {} func (x *GetShipperRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetShipperRequest.ProtoReflect.Descriptor instead. func (*GetShipperRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{0} } func (x *GetShipperRequest) GetName() string { if x != nil { return x.Name } return "" } // Request message for FreightService.ListShippers. type ListShippersRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Requested page size. Server may return fewer shippers than requested. // If unspecified, server will pick an appropriate default. PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // A token identifying a page of results the server should return. // Typically, this is the value of // [ListShippersResponse.next_page_token][einride.example.freight.v1.ListShippersResponse.next_page_token] // returned from the previous call to `ListShippers` method. PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListShippersRequest) Reset() { *x = ListShippersRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListShippersRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListShippersRequest) ProtoMessage() {} func (x *ListShippersRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListShippersRequest.ProtoReflect.Descriptor instead. func (*ListShippersRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{1} } func (x *ListShippersRequest) GetPageSize() int32 { if x != nil { return x.PageSize } return 0 } func (x *ListShippersRequest) GetPageToken() string { if x != nil { return x.PageToken } return "" } // Response message for FreightService.ListShippers. type ListShippersResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The list of shippers. Shippers []*Shipper `protobuf:"bytes,1,rep,name=shippers,proto3" json:"shippers,omitempty"` // A token to retrieve next page of results. Pass this value in the // [ListShippersRequest.page_token][einride.example.freight.v1.ListShippersRequest.page_token] // field in the subsequent call to `ListShippers` method to retrieve the next // page of results. NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListShippersResponse) Reset() { *x = ListShippersResponse{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListShippersResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListShippersResponse) ProtoMessage() {} func (x *ListShippersResponse) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListShippersResponse.ProtoReflect.Descriptor instead. func (*ListShippersResponse) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{2} } func (x *ListShippersResponse) GetShippers() []*Shipper { if x != nil { return x.Shippers } return nil } func (x *ListShippersResponse) GetNextPageToken() string { if x != nil { return x.NextPageToken } return "" } // Request message for FreightService.CreateShipper. type CreateShipperRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The shipper to create. Shipper *Shipper `protobuf:"bytes,1,opt,name=shipper,proto3" json:"shipper,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateShipperRequest) Reset() { *x = CreateShipperRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CreateShipperRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateShipperRequest) ProtoMessage() {} func (x *CreateShipperRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateShipperRequest.ProtoReflect.Descriptor instead. func (*CreateShipperRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{3} } func (x *CreateShipperRequest) GetShipper() *Shipper { if x != nil { return x.Shipper } return nil } // Request message for FreightService.UpdateShipper. type UpdateShipperRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The shipper to update with. The name must match or be empty. // The shipper's `name` field is used to identify the shipper to be updated. // Format: shippers/{shipper} Shipper *Shipper `protobuf:"bytes,1,opt,name=shipper,proto3" json:"shipper,omitempty"` // The list of fields to be updated. UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateShipperRequest) Reset() { *x = UpdateShipperRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UpdateShipperRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpdateShipperRequest) ProtoMessage() {} func (x *UpdateShipperRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpdateShipperRequest.ProtoReflect.Descriptor instead. func (*UpdateShipperRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{4} } func (x *UpdateShipperRequest) GetShipper() *Shipper { if x != nil { return x.Shipper } return nil } func (x *UpdateShipperRequest) GetUpdateMask() *fieldmaskpb.FieldMask { if x != nil { return x.UpdateMask } return nil } // Request message for FreightService.DeleteShipper. type DeleteShipperRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the shipper to delete. // Format: shippers/{shipper} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DeleteShipperRequest) Reset() { *x = DeleteShipperRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DeleteShipperRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeleteShipperRequest) ProtoMessage() {} func (x *DeleteShipperRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeleteShipperRequest.ProtoReflect.Descriptor instead. func (*DeleteShipperRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{5} } func (x *DeleteShipperRequest) GetName() string { if x != nil { return x.Name } return "" } // Request message for FreightService.GetSite. type GetSiteRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the site to retrieve. // Format: shippers/{shipper}/sites/{site} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetSiteRequest) Reset() { *x = GetSiteRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetSiteRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetSiteRequest) ProtoMessage() {} func (x *GetSiteRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetSiteRequest.ProtoReflect.Descriptor instead. func (*GetSiteRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{6} } func (x *GetSiteRequest) GetName() string { if x != nil { return x.Name } return "" } // Request message for FreightService.ListSites. type ListSitesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the parent, which owns this collection of sites. // Format: shippers/{shipper} Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` // Requested page size. Server may return fewer sites than requested. // If unspecified, server will pick an appropriate default. PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // A token identifying a page of results the server should return. // Typically, this is the value of // [ListSitesResponse.next_page_token][einride.example.freight.v1.ListSitesResponse.next_page_token] // returned from the previous call to `ListSites` method. PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` // Number of resource to skip in the request. // - A request with no page token and a skip value of 30 returns a single // page of results starting with the 31st result. // - A request with a page token corresponding to the 51st result (because the // first 50 results were returned on the first page) and a skip value of 30 // returns a single page of results starting with the 81st result. Skip int32 `protobuf:"varint,4,opt,name=skip,proto3" json:"skip,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListSitesRequest) Reset() { *x = ListSitesRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListSitesRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListSitesRequest) ProtoMessage() {} func (x *ListSitesRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListSitesRequest.ProtoReflect.Descriptor instead. func (*ListSitesRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{7} } func (x *ListSitesRequest) GetParent() string { if x != nil { return x.Parent } return "" } func (x *ListSitesRequest) GetPageSize() int32 { if x != nil { return x.PageSize } return 0 } func (x *ListSitesRequest) GetPageToken() string { if x != nil { return x.PageToken } return "" } func (x *ListSitesRequest) GetSkip() int32 { if x != nil { return x.Skip } return 0 } // Response message for FreightService.ListSites. type ListSitesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The list of sites. Sites []*Site `protobuf:"bytes,1,rep,name=sites,proto3" json:"sites,omitempty"` // A token to retrieve next page of results. Pass this value in the // [ListSitesRequest.page_token][einride.example.freight.v1.ListSitesRequest.page_token] // field in the subsequent call to `ListSites` method to retrieve the next // page of results. NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListSitesResponse) Reset() { *x = ListSitesResponse{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListSitesResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListSitesResponse) ProtoMessage() {} func (x *ListSitesResponse) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListSitesResponse.ProtoReflect.Descriptor instead. func (*ListSitesResponse) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{8} } func (x *ListSitesResponse) GetSites() []*Site { if x != nil { return x.Sites } return nil } func (x *ListSitesResponse) GetNextPageToken() string { if x != nil { return x.NextPageToken } return "" } // Request message for FreightService.CreateSite. type CreateSiteRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the parent shipper for which this site will be created. // Format: shippers/{shipper} Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` // The site to create. Site *Site `protobuf:"bytes,2,opt,name=site,proto3" json:"site,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateSiteRequest) Reset() { *x = CreateSiteRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CreateSiteRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateSiteRequest) ProtoMessage() {} func (x *CreateSiteRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateSiteRequest.ProtoReflect.Descriptor instead. func (*CreateSiteRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{9} } func (x *CreateSiteRequest) GetParent() string { if x != nil { return x.Parent } return "" } func (x *CreateSiteRequest) GetSite() *Site { if x != nil { return x.Site } return nil } // Request message for FreightService.UpdateSite. type UpdateSiteRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The site to update with. The name must match or be empty. // The site's `name` field is used to identify the site to be updated. // Format: shippers/{shipper}/sites/{site} Site *Site `protobuf:"bytes,1,opt,name=site,proto3" json:"site,omitempty"` // The list of fields to be updated. UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateSiteRequest) Reset() { *x = UpdateSiteRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UpdateSiteRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpdateSiteRequest) ProtoMessage() {} func (x *UpdateSiteRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpdateSiteRequest.ProtoReflect.Descriptor instead. func (*UpdateSiteRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{10} } func (x *UpdateSiteRequest) GetSite() *Site { if x != nil { return x.Site } return nil } func (x *UpdateSiteRequest) GetUpdateMask() *fieldmaskpb.FieldMask { if x != nil { return x.UpdateMask } return nil } // Request message for FreightService.DeleteSite. type DeleteSiteRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the site to delete. // Format: shippers/{shipper}/sites/{site} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DeleteSiteRequest) Reset() { *x = DeleteSiteRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DeleteSiteRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeleteSiteRequest) ProtoMessage() {} func (x *DeleteSiteRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeleteSiteRequest.ProtoReflect.Descriptor instead. func (*DeleteSiteRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{11} } func (x *DeleteSiteRequest) GetName() string { if x != nil { return x.Name } return "" } // Request message for FreightService.BatchGetSites. type BatchGetSitesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The parent resource shared by all sites being retrieved. // If this is set, the parent of all of the sites specified in `names` // must match this field. // Format: `shippers/{shipper}` Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` // The names of the sites to retrieve. // A maximum of 1000 sites can be retrieved in a batch. // Format: `shippers/{shipper}/sites/{site}` Names []string `protobuf:"bytes,2,rep,name=names,proto3" json:"names,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BatchGetSitesRequest) Reset() { *x = BatchGetSitesRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BatchGetSitesRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*BatchGetSitesRequest) ProtoMessage() {} func (x *BatchGetSitesRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BatchGetSitesRequest.ProtoReflect.Descriptor instead. func (*BatchGetSitesRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{12} } func (x *BatchGetSitesRequest) GetParent() string { if x != nil { return x.Parent } return "" } func (x *BatchGetSitesRequest) GetNames() []string { if x != nil { return x.Names } return nil } // Response message for FreightService.BatchGetSites. type BatchGetSitesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Sites requested. Sites []*Site `protobuf:"bytes,1,rep,name=sites,proto3" json:"sites,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BatchGetSitesResponse) Reset() { *x = BatchGetSitesResponse{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BatchGetSitesResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*BatchGetSitesResponse) ProtoMessage() {} func (x *BatchGetSitesResponse) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BatchGetSitesResponse.ProtoReflect.Descriptor instead. func (*BatchGetSitesResponse) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{13} } func (x *BatchGetSitesResponse) GetSites() []*Site { if x != nil { return x.Sites } return nil } // Request message for FreightService.GetShipment. type GetShipmentRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the shipment to retrieve. // Format: shippers/{shipper}/shipments/{shipment} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GetShipmentRequest) Reset() { *x = GetShipmentRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GetShipmentRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetShipmentRequest) ProtoMessage() {} func (x *GetShipmentRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetShipmentRequest.ProtoReflect.Descriptor instead. func (*GetShipmentRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{14} } func (x *GetShipmentRequest) GetName() string { if x != nil { return x.Name } return "" } // Request message for FreightService.ListShipments. type ListShipmentsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the parent, which owns this collection of shipments. // Format: shippers/{shipper} Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` // Requested page size. Server may return fewer shipments than requested. // If unspecified, server will pick an appropriate default. PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // A token identifying a page of results the server should return. // Typically, this is the value of // [ListShipmentsResponse.next_page_token][einride.example.freight.v1.ListShipmentsResponse.next_page_token] // returned from the previous call to `ListShipments` method. PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListShipmentsRequest) Reset() { *x = ListShipmentsRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListShipmentsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListShipmentsRequest) ProtoMessage() {} func (x *ListShipmentsRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListShipmentsRequest.ProtoReflect.Descriptor instead. func (*ListShipmentsRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{15} } func (x *ListShipmentsRequest) GetParent() string { if x != nil { return x.Parent } return "" } func (x *ListShipmentsRequest) GetPageSize() int32 { if x != nil { return x.PageSize } return 0 } func (x *ListShipmentsRequest) GetPageToken() string { if x != nil { return x.PageToken } return "" } // Response message for FreightService.ListShipments. type ListShipmentsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The list of shipments. Shipments []*Shipment `protobuf:"bytes,1,rep,name=shipments,proto3" json:"shipments,omitempty"` // A token to retrieve next page of results. Pass this value in the // [ListShipmentsRequest.page_token][einride.example.freight.v1.ListShipmentsRequest.page_token] // field in the subsequent call to `ListShipments` method to retrieve the next // page of results. NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListShipmentsResponse) Reset() { *x = ListShipmentsResponse{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListShipmentsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListShipmentsResponse) ProtoMessage() {} func (x *ListShipmentsResponse) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListShipmentsResponse.ProtoReflect.Descriptor instead. func (*ListShipmentsResponse) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{16} } func (x *ListShipmentsResponse) GetShipments() []*Shipment { if x != nil { return x.Shipments } return nil } func (x *ListShipmentsResponse) GetNextPageToken() string { if x != nil { return x.NextPageToken } return "" } // Request message for FreightService.CreateShipment. type CreateShipmentRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the parent shipper for which this shipment will be created. // Format: shippers/{shipper} Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` // The shipment to create. Shipment *Shipment `protobuf:"bytes,2,opt,name=shipment,proto3" json:"shipment,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateShipmentRequest) Reset() { *x = CreateShipmentRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CreateShipmentRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateShipmentRequest) ProtoMessage() {} func (x *CreateShipmentRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateShipmentRequest.ProtoReflect.Descriptor instead. func (*CreateShipmentRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{17} } func (x *CreateShipmentRequest) GetParent() string { if x != nil { return x.Parent } return "" } func (x *CreateShipmentRequest) GetShipment() *Shipment { if x != nil { return x.Shipment } return nil } // Request message for FreightService.UpdateShipment. type UpdateShipmentRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The shipment to update with. The name must match or be empty. // The shipment's `name` field is used to identify the shipment to be updated. // Format: shippers/{shipper}/shipments/{shipment} Shipment *Shipment `protobuf:"bytes,1,opt,name=shipment,proto3" json:"shipment,omitempty"` // The list of fields to be updated. UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpdateShipmentRequest) Reset() { *x = UpdateShipmentRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UpdateShipmentRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpdateShipmentRequest) ProtoMessage() {} func (x *UpdateShipmentRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpdateShipmentRequest.ProtoReflect.Descriptor instead. func (*UpdateShipmentRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{18} } func (x *UpdateShipmentRequest) GetShipment() *Shipment { if x != nil { return x.Shipment } return nil } func (x *UpdateShipmentRequest) GetUpdateMask() *fieldmaskpb.FieldMask { if x != nil { return x.UpdateMask } return nil } // Request message for FreightService.DeleteShipment. type DeleteShipmentRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the shipment to delete. // Format: shippers/{shipper}/shipments/{shipment} Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DeleteShipmentRequest) Reset() { *x = DeleteShipmentRequest{} mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DeleteShipmentRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeleteShipmentRequest) ProtoMessage() {} func (x *DeleteShipmentRequest) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_freight_service_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeleteShipmentRequest.ProtoReflect.Descriptor instead. func (*DeleteShipmentRequest) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_freight_service_proto_rawDescGZIP(), []int{19} } func (x *DeleteShipmentRequest) GetName() string { if x != nil { return x.Name } return "" } var File_einride_example_freight_v1_freight_service_proto protoreflect.FileDescriptor const file_einride_example_freight_v1_freight_service_proto_rawDesc = "" + "\n" + "0einride/example/freight/v1/freight_service.proto\x12\x1aeinride.example.freight.v1\x1a)einride/example/freight/v1/shipment.proto\x1a(einride/example/freight/v1/shipper.proto\x1a%einride/example/freight/v1/site.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a google/protobuf/field_mask.proto\"V\n" + "\x11GetShipperRequest\x12A\n" + "\x04name\x18\x01 \x01(\tB-\xe2A\x01\x02\xfaA&\n" + "$freight-example.einride.tech/ShipperR\x04name\"Q\n" + "\x13ListShippersRequest\x12\x1b\n" + "\tpage_size\x18\x01 \x01(\x05R\bpageSize\x12\x1d\n" + "\n" + "page_token\x18\x02 \x01(\tR\tpageToken\"\x7f\n" + "\x14ListShippersResponse\x12?\n" + "\bshippers\x18\x01 \x03(\v2#.einride.example.freight.v1.ShipperR\bshippers\x12&\n" + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"[\n" + "\x14CreateShipperRequest\x12C\n" + "\ashipper\x18\x01 \x01(\v2#.einride.example.freight.v1.ShipperB\x04\xe2A\x01\x02R\ashipper\"\x98\x01\n" + "\x14UpdateShipperRequest\x12C\n" + "\ashipper\x18\x01 \x01(\v2#.einride.example.freight.v1.ShipperB\x04\xe2A\x01\x02R\ashipper\x12;\n" + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + "updateMask\"Y\n" + "\x14DeleteShipperRequest\x12A\n" + "\x04name\x18\x01 \x01(\tB-\xe2A\x01\x02\xfaA&\n" + "$freight-example.einride.tech/ShipperR\x04name\"P\n" + "\x0eGetSiteRequest\x12>\n" + "\x04name\x18\x01 \x01(\tB*\xe2A\x01\x02\xfaA#\n" + "!freight-example.einride.tech/SiteR\x04name\"\xa9\x01\n" + "\x10ListSitesRequest\x12E\n" + "\x06parent\x18\x01 \x01(\tB-\xe2A\x01\x02\xfaA&\n" + "$freight-example.einride.tech/ShipperR\x06parent\x12\x1b\n" + "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" + "\n" + "page_token\x18\x03 \x01(\tR\tpageToken\x12\x12\n" + "\x04skip\x18\x04 \x01(\x05R\x04skip\"s\n" + "\x11ListSitesResponse\x126\n" + "\x05sites\x18\x01 \x03(\v2 .einride.example.freight.v1.SiteR\x05sites\x12&\n" + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\x96\x01\n" + "\x11CreateSiteRequest\x12E\n" + "\x06parent\x18\x01 \x01(\tB-\xe2A\x01\x02\xfaA&\n" + "$freight-example.einride.tech/ShipperR\x06parent\x12:\n" + "\x04site\x18\x02 \x01(\v2 .einride.example.freight.v1.SiteB\x04\xe2A\x01\x02R\x04site\"\x8c\x01\n" + "\x11UpdateSiteRequest\x12:\n" + "\x04site\x18\x01 \x01(\v2 .einride.example.freight.v1.SiteB\x04\xe2A\x01\x02R\x04site\x12;\n" + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + "updateMask\"S\n" + "\x11DeleteSiteRequest\x12>\n" + "\x04name\x18\x01 \x01(\tB*\xe2A\x01\x02\xfaA#\n" + "!freight-example.einride.tech/SiteR\x04name\"\x9b\x01\n" + "\x14BatchGetSitesRequest\x12A\n" + "\x06parent\x18\x01 \x01(\tB)\xfaA&\n" + "$freight-example.einride.tech/ShipperR\x06parent\x12@\n" + "\x05names\x18\x02 \x03(\tB*\xe2A\x01\x02\xfaA#\n" + "!freight-example.einride.tech/SiteR\x05names\"O\n" + "\x15BatchGetSitesResponse\x126\n" + "\x05sites\x18\x01 \x03(\v2 .einride.example.freight.v1.SiteR\x05sites\"X\n" + "\x12GetShipmentRequest\x12B\n" + "\x04name\x18\x01 \x01(\tB.\xe2A\x01\x02\xfaA'\n" + "%freight-example.einride.tech/ShipmentR\x04name\"\x99\x01\n" + "\x14ListShipmentsRequest\x12E\n" + "\x06parent\x18\x01 \x01(\tB-\xe2A\x01\x02\xfaA&\n" + "$freight-example.einride.tech/ShipperR\x06parent\x12\x1b\n" + "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" + "\n" + "page_token\x18\x03 \x01(\tR\tpageToken\"\x83\x01\n" + "\x15ListShipmentsResponse\x12B\n" + "\tshipments\x18\x01 \x03(\v2$.einride.example.freight.v1.ShipmentR\tshipments\x12&\n" + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xa6\x01\n" + "\x15CreateShipmentRequest\x12E\n" + "\x06parent\x18\x01 \x01(\tB-\xe2A\x01\x02\xfaA&\n" + "$freight-example.einride.tech/ShipperR\x06parent\x12F\n" + "\bshipment\x18\x02 \x01(\v2$.einride.example.freight.v1.ShipmentB\x04\xe2A\x01\x02R\bshipment\"\x9c\x01\n" + "\x15UpdateShipmentRequest\x12F\n" + "\bshipment\x18\x01 \x01(\v2$.einride.example.freight.v1.ShipmentB\x04\xe2A\x01\x02R\bshipment\x12;\n" + "\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskR\n" + "updateMask\"[\n" + "\x15DeleteShipmentRequest\x12B\n" + "\x04name\x18\x01 \x01(\tB.\xe2A\x01\x02\xfaA'\n" + "%freight-example.einride.tech/ShipmentR\x04name2\x81\x14\n" + "\x0eFreightService\x12\x86\x01\n" + "\n" + "GetShipper\x12-.einride.example.freight.v1.GetShipperRequest\x1a#.einride.example.freight.v1.Shipper\"$\xdaA\x04name\x82\xd3\xe4\x93\x02\x17\x12\x15/v1/{name=shippers/*}\x12\x87\x01\n" + "\fListShippers\x12/.einride.example.freight.v1.ListShippersRequest\x1a0.einride.example.freight.v1.ListShippersResponse\"\x14\x82\xd3\xe4\x93\x02\x0e\x12\f/v1/shippers\x12\x8f\x01\n" + "\rCreateShipper\x120.einride.example.freight.v1.CreateShipperRequest\x1a#.einride.example.freight.v1.Shipper\"'\xdaA\ashipper\x82\xd3\xe4\x93\x02\x17:\ashipper\"\f/v1/shippers\x12\xac\x01\n" + "\rUpdateShipper\x120.einride.example.freight.v1.UpdateShipperRequest\x1a#.einride.example.freight.v1.Shipper\"D\xdaA\x13shipper,update_mask\x82\xd3\xe4\x93\x02(:\ashipper2\x1d/v1/{shipper.name=shippers/*}\x12\x8c\x01\n" + "\rDeleteShipper\x120.einride.example.freight.v1.DeleteShipperRequest\x1a#.einride.example.freight.v1.Shipper\"$\xdaA\x04name\x82\xd3\xe4\x93\x02\x17*\x15/v1/{name=shippers/*}\x12\x85\x01\n" + "\aGetSite\x12*.einride.example.freight.v1.GetSiteRequest\x1a .einride.example.freight.v1.Site\",\xdaA\x04name\x82\xd3\xe4\x93\x02\x1f\x12\x1d/v1/{name=shippers/*/sites/*}\x12\x98\x01\n" + "\tListSites\x12,.einride.example.freight.v1.ListSitesRequest\x1a-.einride.example.freight.v1.ListSitesResponse\".\xdaA\x06parent\x82\xd3\xe4\x93\x02\x1f\x12\x1d/v1/{parent=shippers/*}/sites\x12\x98\x01\n" + "\n" + "CreateSite\x12-.einride.example.freight.v1.CreateSiteRequest\x1a .einride.example.freight.v1.Site\"9\xdaA\vparent,site\x82\xd3\xe4\x93\x02%:\x04site\"\x1d/v1/{parent=shippers/*}/sites\x12\xa2\x01\n" + "\n" + "UpdateSite\x12-.einride.example.freight.v1.UpdateSiteRequest\x1a .einride.example.freight.v1.Site\"C\xdaA\x10site,update_mask\x82\xd3\xe4\x93\x02*:\x04site2\"/v1/{site.name=shippers/*/sites/*}\x12\x8b\x01\n" + "\n" + "DeleteSite\x12-.einride.example.freight.v1.DeleteSiteRequest\x1a .einride.example.freight.v1.Site\",\xdaA\x04name\x82\xd3\xe4\x93\x02\x1f*\x1d/v1/{name=shippers/*/sites/*}\x12\xa4\x01\n" + "\rBatchGetSites\x120.einride.example.freight.v1.BatchGetSitesRequest\x1a1.einride.example.freight.v1.BatchGetSitesResponse\".\x82\xd3\xe4\x93\x02(\x12&/v1/{parent=shippers/*}/sites:batchGet\x12\x95\x01\n" + "\vGetShipment\x12..einride.example.freight.v1.GetShipmentRequest\x1a$.einride.example.freight.v1.Shipment\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#\x12!/v1/{name=shippers/*/shipments/*}\x12\xa8\x01\n" + "\rListShipments\x120.einride.example.freight.v1.ListShipmentsRequest\x1a1.einride.example.freight.v1.ListShipmentsResponse\"2\xdaA\x06parent\x82\xd3\xe4\x93\x02#\x12!/v1/{parent=shippers/*}/shipments\x12\xb0\x01\n" + "\x0eCreateShipment\x121.einride.example.freight.v1.CreateShipmentRequest\x1a$.einride.example.freight.v1.Shipment\"E\xdaA\x0fparent,shipment\x82\xd3\xe4\x93\x02-:\bshipment\"!/v1/{parent=shippers/*}/shipments\x12\xbe\x01\n" + "\x0eUpdateShipment\x121.einride.example.freight.v1.UpdateShipmentRequest\x1a$.einride.example.freight.v1.Shipment\"S\xdaA\x14shipment,update_mask\x82\xd3\xe4\x93\x026:\bshipment2*/v1/{shipment.name=shippers/*/shipments/*}\x12\x9b\x01\n" + "\x0eDeleteShipment\x121.einride.example.freight.v1.DeleteShipmentRequest\x1a$.einride.example.freight.v1.Shipment\"0\xdaA\x04name\x82\xd3\xe4\x93\x02#*!/v1/{name=shippers/*/shipments/*}\x1a\x1f\xcaA\x1cfreight-example.einride.techB\x84\x02\n" + "\x1ecom.einride.example.freight.v1B\x13FreightServiceProtoP\x01ZBgo.einride.tech/aip/proto/gen/einride/example/freight/v1;freightv1\xa2\x02\x03EEF\xaa\x02\x1aEinride.Example.Freight.V1\xca\x02\x1aEinride\\Example\\Freight\\V1\xe2\x02&Einride\\Example\\Freight\\V1\\GPBMetadata\xea\x02\x1dEinride::Example::Freight::V1b\x06proto3" var ( file_einride_example_freight_v1_freight_service_proto_rawDescOnce sync.Once file_einride_example_freight_v1_freight_service_proto_rawDescData []byte ) func file_einride_example_freight_v1_freight_service_proto_rawDescGZIP() []byte { file_einride_example_freight_v1_freight_service_proto_rawDescOnce.Do(func() { file_einride_example_freight_v1_freight_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_freight_service_proto_rawDesc), len(file_einride_example_freight_v1_freight_service_proto_rawDesc))) }) return file_einride_example_freight_v1_freight_service_proto_rawDescData } var file_einride_example_freight_v1_freight_service_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_einride_example_freight_v1_freight_service_proto_goTypes = []any{ (*GetShipperRequest)(nil), // 0: einride.example.freight.v1.GetShipperRequest (*ListShippersRequest)(nil), // 1: einride.example.freight.v1.ListShippersRequest (*ListShippersResponse)(nil), // 2: einride.example.freight.v1.ListShippersResponse (*CreateShipperRequest)(nil), // 3: einride.example.freight.v1.CreateShipperRequest (*UpdateShipperRequest)(nil), // 4: einride.example.freight.v1.UpdateShipperRequest (*DeleteShipperRequest)(nil), // 5: einride.example.freight.v1.DeleteShipperRequest (*GetSiteRequest)(nil), // 6: einride.example.freight.v1.GetSiteRequest (*ListSitesRequest)(nil), // 7: einride.example.freight.v1.ListSitesRequest (*ListSitesResponse)(nil), // 8: einride.example.freight.v1.ListSitesResponse (*CreateSiteRequest)(nil), // 9: einride.example.freight.v1.CreateSiteRequest (*UpdateSiteRequest)(nil), // 10: einride.example.freight.v1.UpdateSiteRequest (*DeleteSiteRequest)(nil), // 11: einride.example.freight.v1.DeleteSiteRequest (*BatchGetSitesRequest)(nil), // 12: einride.example.freight.v1.BatchGetSitesRequest (*BatchGetSitesResponse)(nil), // 13: einride.example.freight.v1.BatchGetSitesResponse (*GetShipmentRequest)(nil), // 14: einride.example.freight.v1.GetShipmentRequest (*ListShipmentsRequest)(nil), // 15: einride.example.freight.v1.ListShipmentsRequest (*ListShipmentsResponse)(nil), // 16: einride.example.freight.v1.ListShipmentsResponse (*CreateShipmentRequest)(nil), // 17: einride.example.freight.v1.CreateShipmentRequest (*UpdateShipmentRequest)(nil), // 18: einride.example.freight.v1.UpdateShipmentRequest (*DeleteShipmentRequest)(nil), // 19: einride.example.freight.v1.DeleteShipmentRequest (*Shipper)(nil), // 20: einride.example.freight.v1.Shipper (*fieldmaskpb.FieldMask)(nil), // 21: google.protobuf.FieldMask (*Site)(nil), // 22: einride.example.freight.v1.Site (*Shipment)(nil), // 23: einride.example.freight.v1.Shipment } var file_einride_example_freight_v1_freight_service_proto_depIdxs = []int32{ 20, // 0: einride.example.freight.v1.ListShippersResponse.shippers:type_name -> einride.example.freight.v1.Shipper 20, // 1: einride.example.freight.v1.CreateShipperRequest.shipper:type_name -> einride.example.freight.v1.Shipper 20, // 2: einride.example.freight.v1.UpdateShipperRequest.shipper:type_name -> einride.example.freight.v1.Shipper 21, // 3: einride.example.freight.v1.UpdateShipperRequest.update_mask:type_name -> google.protobuf.FieldMask 22, // 4: einride.example.freight.v1.ListSitesResponse.sites:type_name -> einride.example.freight.v1.Site 22, // 5: einride.example.freight.v1.CreateSiteRequest.site:type_name -> einride.example.freight.v1.Site 22, // 6: einride.example.freight.v1.UpdateSiteRequest.site:type_name -> einride.example.freight.v1.Site 21, // 7: einride.example.freight.v1.UpdateSiteRequest.update_mask:type_name -> google.protobuf.FieldMask 22, // 8: einride.example.freight.v1.BatchGetSitesResponse.sites:type_name -> einride.example.freight.v1.Site 23, // 9: einride.example.freight.v1.ListShipmentsResponse.shipments:type_name -> einride.example.freight.v1.Shipment 23, // 10: einride.example.freight.v1.CreateShipmentRequest.shipment:type_name -> einride.example.freight.v1.Shipment 23, // 11: einride.example.freight.v1.UpdateShipmentRequest.shipment:type_name -> einride.example.freight.v1.Shipment 21, // 12: einride.example.freight.v1.UpdateShipmentRequest.update_mask:type_name -> google.protobuf.FieldMask 0, // 13: einride.example.freight.v1.FreightService.GetShipper:input_type -> einride.example.freight.v1.GetShipperRequest 1, // 14: einride.example.freight.v1.FreightService.ListShippers:input_type -> einride.example.freight.v1.ListShippersRequest 3, // 15: einride.example.freight.v1.FreightService.CreateShipper:input_type -> einride.example.freight.v1.CreateShipperRequest 4, // 16: einride.example.freight.v1.FreightService.UpdateShipper:input_type -> einride.example.freight.v1.UpdateShipperRequest 5, // 17: einride.example.freight.v1.FreightService.DeleteShipper:input_type -> einride.example.freight.v1.DeleteShipperRequest 6, // 18: einride.example.freight.v1.FreightService.GetSite:input_type -> einride.example.freight.v1.GetSiteRequest 7, // 19: einride.example.freight.v1.FreightService.ListSites:input_type -> einride.example.freight.v1.ListSitesRequest 9, // 20: einride.example.freight.v1.FreightService.CreateSite:input_type -> einride.example.freight.v1.CreateSiteRequest 10, // 21: einride.example.freight.v1.FreightService.UpdateSite:input_type -> einride.example.freight.v1.UpdateSiteRequest 11, // 22: einride.example.freight.v1.FreightService.DeleteSite:input_type -> einride.example.freight.v1.DeleteSiteRequest 12, // 23: einride.example.freight.v1.FreightService.BatchGetSites:input_type -> einride.example.freight.v1.BatchGetSitesRequest 14, // 24: einride.example.freight.v1.FreightService.GetShipment:input_type -> einride.example.freight.v1.GetShipmentRequest 15, // 25: einride.example.freight.v1.FreightService.ListShipments:input_type -> einride.example.freight.v1.ListShipmentsRequest 17, // 26: einride.example.freight.v1.FreightService.CreateShipment:input_type -> einride.example.freight.v1.CreateShipmentRequest 18, // 27: einride.example.freight.v1.FreightService.UpdateShipment:input_type -> einride.example.freight.v1.UpdateShipmentRequest 19, // 28: einride.example.freight.v1.FreightService.DeleteShipment:input_type -> einride.example.freight.v1.DeleteShipmentRequest 20, // 29: einride.example.freight.v1.FreightService.GetShipper:output_type -> einride.example.freight.v1.Shipper 2, // 30: einride.example.freight.v1.FreightService.ListShippers:output_type -> einride.example.freight.v1.ListShippersResponse 20, // 31: einride.example.freight.v1.FreightService.CreateShipper:output_type -> einride.example.freight.v1.Shipper 20, // 32: einride.example.freight.v1.FreightService.UpdateShipper:output_type -> einride.example.freight.v1.Shipper 20, // 33: einride.example.freight.v1.FreightService.DeleteShipper:output_type -> einride.example.freight.v1.Shipper 22, // 34: einride.example.freight.v1.FreightService.GetSite:output_type -> einride.example.freight.v1.Site 8, // 35: einride.example.freight.v1.FreightService.ListSites:output_type -> einride.example.freight.v1.ListSitesResponse 22, // 36: einride.example.freight.v1.FreightService.CreateSite:output_type -> einride.example.freight.v1.Site 22, // 37: einride.example.freight.v1.FreightService.UpdateSite:output_type -> einride.example.freight.v1.Site 22, // 38: einride.example.freight.v1.FreightService.DeleteSite:output_type -> einride.example.freight.v1.Site 13, // 39: einride.example.freight.v1.FreightService.BatchGetSites:output_type -> einride.example.freight.v1.BatchGetSitesResponse 23, // 40: einride.example.freight.v1.FreightService.GetShipment:output_type -> einride.example.freight.v1.Shipment 16, // 41: einride.example.freight.v1.FreightService.ListShipments:output_type -> einride.example.freight.v1.ListShipmentsResponse 23, // 42: einride.example.freight.v1.FreightService.CreateShipment:output_type -> einride.example.freight.v1.Shipment 23, // 43: einride.example.freight.v1.FreightService.UpdateShipment:output_type -> einride.example.freight.v1.Shipment 23, // 44: einride.example.freight.v1.FreightService.DeleteShipment:output_type -> einride.example.freight.v1.Shipment 29, // [29:45] is the sub-list for method output_type 13, // [13:29] is the sub-list for method input_type 13, // [13:13] is the sub-list for extension type_name 13, // [13:13] is the sub-list for extension extendee 0, // [0:13] is the sub-list for field type_name } func init() { file_einride_example_freight_v1_freight_service_proto_init() } func file_einride_example_freight_v1_freight_service_proto_init() { if File_einride_example_freight_v1_freight_service_proto != nil { return } file_einride_example_freight_v1_shipment_proto_init() file_einride_example_freight_v1_shipper_proto_init() file_einride_example_freight_v1_site_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_freight_service_proto_rawDesc), len(file_einride_example_freight_v1_freight_service_proto_rawDesc)), NumEnums: 0, NumMessages: 20, NumExtensions: 0, NumServices: 1, }, GoTypes: file_einride_example_freight_v1_freight_service_proto_goTypes, DependencyIndexes: file_einride_example_freight_v1_freight_service_proto_depIdxs, MessageInfos: file_einride_example_freight_v1_freight_service_proto_msgTypes, }.Build() File_einride_example_freight_v1_freight_service_proto = out.File file_einride_example_freight_v1_freight_service_proto_goTypes = nil file_einride_example_freight_v1_freight_service_proto_depIdxs = nil } aip-go-0.80.0/proto/gen/einride/example/freight/v1/freight_service_grpc.pb.go000066400000000000000000000706021513342120500270320ustar00rootroot00000000000000// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 // - protoc (unknown) // source: einride/example/freight/v1/freight_service.proto package freightv1 import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // FreightServiceClient is the client API for FreightService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type FreightServiceClient interface { // Get a shipper. // // See: https://google.aip.dev/131 (Standard methods: Get). GetShipper(ctx context.Context, in *GetShipperRequest, opts ...grpc.CallOption) (*Shipper, error) // List shippers. // // See: https://google.aip.dev/132 (Standard methods: List). ListShippers(ctx context.Context, in *ListShippersRequest, opts ...grpc.CallOption) (*ListShippersResponse, error) // Create a shipper. // // See: https://google.aip.dev/133 (Standard methods: Create). CreateShipper(ctx context.Context, in *CreateShipperRequest, opts ...grpc.CallOption) (*Shipper, error) // Update a shipper. // // See: https://google.aip.dev/134 (Standard methods: Update). UpdateShipper(ctx context.Context, in *UpdateShipperRequest, opts ...grpc.CallOption) (*Shipper, error) // Delete a shipper. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). DeleteShipper(ctx context.Context, in *DeleteShipperRequest, opts ...grpc.CallOption) (*Shipper, error) // Get a site. // // See: https://google.aip.dev/131 (Standard methods: Get). GetSite(ctx context.Context, in *GetSiteRequest, opts ...grpc.CallOption) (*Site, error) // List sites for a shipper. // // See: https://google.aip.dev/132 (Standard methods: List). ListSites(ctx context.Context, in *ListSitesRequest, opts ...grpc.CallOption) (*ListSitesResponse, error) // Create a site. // // See: https://google.aip.dev/133 (Standard methods: Create). CreateSite(ctx context.Context, in *CreateSiteRequest, opts ...grpc.CallOption) (*Site, error) // Update a site. // // See: https://google.aip.dev/134 (Standard methods: Update). UpdateSite(ctx context.Context, in *UpdateSiteRequest, opts ...grpc.CallOption) (*Site, error) // Delete a site. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). DeleteSite(ctx context.Context, in *DeleteSiteRequest, opts ...grpc.CallOption) (*Site, error) // Batch get sites. // // See: https://google.aip.dev/231 (Batch methods: Get). BatchGetSites(ctx context.Context, in *BatchGetSitesRequest, opts ...grpc.CallOption) (*BatchGetSitesResponse, error) // Get a shipment. // // See: https://google.aip.dev/131 (Standard methods: Get). GetShipment(ctx context.Context, in *GetShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) // List shipments for a shipper. // // See: https://google.aip.dev/132 (Standard methods: List). ListShipments(ctx context.Context, in *ListShipmentsRequest, opts ...grpc.CallOption) (*ListShipmentsResponse, error) // Create a shipment. // // See: https://google.aip.dev/133 (Standard methods: Create). CreateShipment(ctx context.Context, in *CreateShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) // Update a shipment. // // See: https://google.aip.dev/134 (Standard methods: Update). UpdateShipment(ctx context.Context, in *UpdateShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) // Delete a shipment. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). DeleteShipment(ctx context.Context, in *DeleteShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) } type freightServiceClient struct { cc grpc.ClientConnInterface } func NewFreightServiceClient(cc grpc.ClientConnInterface) FreightServiceClient { return &freightServiceClient{cc} } func (c *freightServiceClient) GetShipper(ctx context.Context, in *GetShipperRequest, opts ...grpc.CallOption) (*Shipper, error) { out := new(Shipper) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/GetShipper", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) ListShippers(ctx context.Context, in *ListShippersRequest, opts ...grpc.CallOption) (*ListShippersResponse, error) { out := new(ListShippersResponse) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/ListShippers", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) CreateShipper(ctx context.Context, in *CreateShipperRequest, opts ...grpc.CallOption) (*Shipper, error) { out := new(Shipper) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/CreateShipper", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) UpdateShipper(ctx context.Context, in *UpdateShipperRequest, opts ...grpc.CallOption) (*Shipper, error) { out := new(Shipper) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/UpdateShipper", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) DeleteShipper(ctx context.Context, in *DeleteShipperRequest, opts ...grpc.CallOption) (*Shipper, error) { out := new(Shipper) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/DeleteShipper", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) GetSite(ctx context.Context, in *GetSiteRequest, opts ...grpc.CallOption) (*Site, error) { out := new(Site) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/GetSite", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) ListSites(ctx context.Context, in *ListSitesRequest, opts ...grpc.CallOption) (*ListSitesResponse, error) { out := new(ListSitesResponse) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/ListSites", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) CreateSite(ctx context.Context, in *CreateSiteRequest, opts ...grpc.CallOption) (*Site, error) { out := new(Site) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/CreateSite", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) UpdateSite(ctx context.Context, in *UpdateSiteRequest, opts ...grpc.CallOption) (*Site, error) { out := new(Site) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/UpdateSite", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) DeleteSite(ctx context.Context, in *DeleteSiteRequest, opts ...grpc.CallOption) (*Site, error) { out := new(Site) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/DeleteSite", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) BatchGetSites(ctx context.Context, in *BatchGetSitesRequest, opts ...grpc.CallOption) (*BatchGetSitesResponse, error) { out := new(BatchGetSitesResponse) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/BatchGetSites", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) GetShipment(ctx context.Context, in *GetShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) { out := new(Shipment) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/GetShipment", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) ListShipments(ctx context.Context, in *ListShipmentsRequest, opts ...grpc.CallOption) (*ListShipmentsResponse, error) { out := new(ListShipmentsResponse) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/ListShipments", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) CreateShipment(ctx context.Context, in *CreateShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) { out := new(Shipment) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/CreateShipment", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) UpdateShipment(ctx context.Context, in *UpdateShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) { out := new(Shipment) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/UpdateShipment", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *freightServiceClient) DeleteShipment(ctx context.Context, in *DeleteShipmentRequest, opts ...grpc.CallOption) (*Shipment, error) { out := new(Shipment) err := c.cc.Invoke(ctx, "/einride.example.freight.v1.FreightService/DeleteShipment", in, out, opts...) if err != nil { return nil, err } return out, nil } // FreightServiceServer is the server API for FreightService service. // All implementations should embed UnimplementedFreightServiceServer // for forward compatibility type FreightServiceServer interface { // Get a shipper. // // See: https://google.aip.dev/131 (Standard methods: Get). GetShipper(context.Context, *GetShipperRequest) (*Shipper, error) // List shippers. // // See: https://google.aip.dev/132 (Standard methods: List). ListShippers(context.Context, *ListShippersRequest) (*ListShippersResponse, error) // Create a shipper. // // See: https://google.aip.dev/133 (Standard methods: Create). CreateShipper(context.Context, *CreateShipperRequest) (*Shipper, error) // Update a shipper. // // See: https://google.aip.dev/134 (Standard methods: Update). UpdateShipper(context.Context, *UpdateShipperRequest) (*Shipper, error) // Delete a shipper. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). DeleteShipper(context.Context, *DeleteShipperRequest) (*Shipper, error) // Get a site. // // See: https://google.aip.dev/131 (Standard methods: Get). GetSite(context.Context, *GetSiteRequest) (*Site, error) // List sites for a shipper. // // See: https://google.aip.dev/132 (Standard methods: List). ListSites(context.Context, *ListSitesRequest) (*ListSitesResponse, error) // Create a site. // // See: https://google.aip.dev/133 (Standard methods: Create). CreateSite(context.Context, *CreateSiteRequest) (*Site, error) // Update a site. // // See: https://google.aip.dev/134 (Standard methods: Update). UpdateSite(context.Context, *UpdateSiteRequest) (*Site, error) // Delete a site. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). DeleteSite(context.Context, *DeleteSiteRequest) (*Site, error) // Batch get sites. // // See: https://google.aip.dev/231 (Batch methods: Get). BatchGetSites(context.Context, *BatchGetSitesRequest) (*BatchGetSitesResponse, error) // Get a shipment. // // See: https://google.aip.dev/131 (Standard methods: Get). GetShipment(context.Context, *GetShipmentRequest) (*Shipment, error) // List shipments for a shipper. // // See: https://google.aip.dev/132 (Standard methods: List). ListShipments(context.Context, *ListShipmentsRequest) (*ListShipmentsResponse, error) // Create a shipment. // // See: https://google.aip.dev/133 (Standard methods: Create). CreateShipment(context.Context, *CreateShipmentRequest) (*Shipment, error) // Update a shipment. // // See: https://google.aip.dev/134 (Standard methods: Update). UpdateShipment(context.Context, *UpdateShipmentRequest) (*Shipment, error) // Delete a shipment. // // See: https://google.aip.dev/135 (Standard methods: Delete). // See: https://google.aip.dev/164 (Soft delete). DeleteShipment(context.Context, *DeleteShipmentRequest) (*Shipment, error) } // UnimplementedFreightServiceServer should be embedded to have forward compatible implementations. type UnimplementedFreightServiceServer struct { } func (UnimplementedFreightServiceServer) GetShipper(context.Context, *GetShipperRequest) (*Shipper, error) { return nil, status.Errorf(codes.Unimplemented, "method GetShipper not implemented") } func (UnimplementedFreightServiceServer) ListShippers(context.Context, *ListShippersRequest) (*ListShippersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListShippers not implemented") } func (UnimplementedFreightServiceServer) CreateShipper(context.Context, *CreateShipperRequest) (*Shipper, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateShipper not implemented") } func (UnimplementedFreightServiceServer) UpdateShipper(context.Context, *UpdateShipperRequest) (*Shipper, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateShipper not implemented") } func (UnimplementedFreightServiceServer) DeleteShipper(context.Context, *DeleteShipperRequest) (*Shipper, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteShipper not implemented") } func (UnimplementedFreightServiceServer) GetSite(context.Context, *GetSiteRequest) (*Site, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSite not implemented") } func (UnimplementedFreightServiceServer) ListSites(context.Context, *ListSitesRequest) (*ListSitesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListSites not implemented") } func (UnimplementedFreightServiceServer) CreateSite(context.Context, *CreateSiteRequest) (*Site, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateSite not implemented") } func (UnimplementedFreightServiceServer) UpdateSite(context.Context, *UpdateSiteRequest) (*Site, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateSite not implemented") } func (UnimplementedFreightServiceServer) DeleteSite(context.Context, *DeleteSiteRequest) (*Site, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteSite not implemented") } func (UnimplementedFreightServiceServer) BatchGetSites(context.Context, *BatchGetSitesRequest) (*BatchGetSitesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method BatchGetSites not implemented") } func (UnimplementedFreightServiceServer) GetShipment(context.Context, *GetShipmentRequest) (*Shipment, error) { return nil, status.Errorf(codes.Unimplemented, "method GetShipment not implemented") } func (UnimplementedFreightServiceServer) ListShipments(context.Context, *ListShipmentsRequest) (*ListShipmentsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListShipments not implemented") } func (UnimplementedFreightServiceServer) CreateShipment(context.Context, *CreateShipmentRequest) (*Shipment, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateShipment not implemented") } func (UnimplementedFreightServiceServer) UpdateShipment(context.Context, *UpdateShipmentRequest) (*Shipment, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateShipment not implemented") } func (UnimplementedFreightServiceServer) DeleteShipment(context.Context, *DeleteShipmentRequest) (*Shipment, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteShipment not implemented") } // UnsafeFreightServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to FreightServiceServer will // result in compilation errors. type UnsafeFreightServiceServer interface { mustEmbedUnimplementedFreightServiceServer() } func RegisterFreightServiceServer(s grpc.ServiceRegistrar, srv FreightServiceServer) { s.RegisterService(&FreightService_ServiceDesc, srv) } func _FreightService_GetShipper_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetShipperRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).GetShipper(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/GetShipper", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).GetShipper(ctx, req.(*GetShipperRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_ListShippers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListShippersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).ListShippers(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/ListShippers", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).ListShippers(ctx, req.(*ListShippersRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_CreateShipper_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateShipperRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).CreateShipper(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/CreateShipper", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).CreateShipper(ctx, req.(*CreateShipperRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_UpdateShipper_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpdateShipperRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).UpdateShipper(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/UpdateShipper", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).UpdateShipper(ctx, req.(*UpdateShipperRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_DeleteShipper_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteShipperRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).DeleteShipper(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/DeleteShipper", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).DeleteShipper(ctx, req.(*DeleteShipperRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_GetSite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetSiteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).GetSite(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/GetSite", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).GetSite(ctx, req.(*GetSiteRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_ListSites_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListSitesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).ListSites(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/ListSites", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).ListSites(ctx, req.(*ListSitesRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_CreateSite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateSiteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).CreateSite(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/CreateSite", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).CreateSite(ctx, req.(*CreateSiteRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_UpdateSite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpdateSiteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).UpdateSite(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/UpdateSite", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).UpdateSite(ctx, req.(*UpdateSiteRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_DeleteSite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteSiteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).DeleteSite(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/DeleteSite", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).DeleteSite(ctx, req.(*DeleteSiteRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_BatchGetSites_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(BatchGetSitesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).BatchGetSites(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/BatchGetSites", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).BatchGetSites(ctx, req.(*BatchGetSitesRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_GetShipment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetShipmentRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).GetShipment(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/GetShipment", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).GetShipment(ctx, req.(*GetShipmentRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_ListShipments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListShipmentsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).ListShipments(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/ListShipments", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).ListShipments(ctx, req.(*ListShipmentsRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_CreateShipment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateShipmentRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).CreateShipment(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/CreateShipment", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).CreateShipment(ctx, req.(*CreateShipmentRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_UpdateShipment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpdateShipmentRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).UpdateShipment(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/UpdateShipment", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).UpdateShipment(ctx, req.(*UpdateShipmentRequest)) } return interceptor(ctx, in, info, handler) } func _FreightService_DeleteShipment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteShipmentRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(FreightServiceServer).DeleteShipment(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/einride.example.freight.v1.FreightService/DeleteShipment", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FreightServiceServer).DeleteShipment(ctx, req.(*DeleteShipmentRequest)) } return interceptor(ctx, in, info, handler) } // FreightService_ServiceDesc is the grpc.ServiceDesc for FreightService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var FreightService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "einride.example.freight.v1.FreightService", HandlerType: (*FreightServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "GetShipper", Handler: _FreightService_GetShipper_Handler, }, { MethodName: "ListShippers", Handler: _FreightService_ListShippers_Handler, }, { MethodName: "CreateShipper", Handler: _FreightService_CreateShipper_Handler, }, { MethodName: "UpdateShipper", Handler: _FreightService_UpdateShipper_Handler, }, { MethodName: "DeleteShipper", Handler: _FreightService_DeleteShipper_Handler, }, { MethodName: "GetSite", Handler: _FreightService_GetSite_Handler, }, { MethodName: "ListSites", Handler: _FreightService_ListSites_Handler, }, { MethodName: "CreateSite", Handler: _FreightService_CreateSite_Handler, }, { MethodName: "UpdateSite", Handler: _FreightService_UpdateSite_Handler, }, { MethodName: "DeleteSite", Handler: _FreightService_DeleteSite_Handler, }, { MethodName: "BatchGetSites", Handler: _FreightService_BatchGetSites_Handler, }, { MethodName: "GetShipment", Handler: _FreightService_GetShipment_Handler, }, { MethodName: "ListShipments", Handler: _FreightService_ListShipments_Handler, }, { MethodName: "CreateShipment", Handler: _FreightService_CreateShipment_Handler, }, { MethodName: "UpdateShipment", Handler: _FreightService_UpdateShipment_Handler, }, { MethodName: "DeleteShipment", Handler: _FreightService_DeleteShipment_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "einride/example/freight/v1/freight_service.proto", } aip-go-0.80.0/proto/gen/einride/example/freight/v1/shipment.pb.go000066400000000000000000000340361513342120500244770ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.6 // protoc (unknown) // source: einride/example/freight/v1/shipment.proto package freightv1 import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // A shipment represents transportation of goods between an origin // [site][einride.example.freight.v1.Site] and a destination // [site][einride.example.freight.v1.Site]. type Shipment struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the shipment. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // The creation timestamp of the shipment. CreateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` // The last update timestamp of the shipment. // // Updated when create/update/delete operation is shipment. UpdateTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` // The deletion timestamp of the shipment. DeleteTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=delete_time,json=deleteTime,proto3" json:"delete_time,omitempty"` // The resource name of the origin site of the shipment. // Format: shippers/{shipper}/sites/{site} OriginSite string `protobuf:"bytes,5,opt,name=origin_site,json=originSite,proto3" json:"origin_site,omitempty"` // The resource name of the destination site of the shipment. // Format: shippers/{shipper}/sites/{site} DestinationSite string `protobuf:"bytes,6,opt,name=destination_site,json=destinationSite,proto3" json:"destination_site,omitempty"` // The earliest pickup time of the shipment at the origin site. PickupEarliestTime *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=pickup_earliest_time,json=pickupEarliestTime,proto3" json:"pickup_earliest_time,omitempty"` // The latest pickup time of the shipment at the origin site. PickupLatestTime *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=pickup_latest_time,json=pickupLatestTime,proto3" json:"pickup_latest_time,omitempty"` // The earliest delivery time of the shipment at the destination site. DeliveryEarliestTime *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=delivery_earliest_time,json=deliveryEarliestTime,proto3" json:"delivery_earliest_time,omitempty"` // The latest delivery time of the shipment at the destination site. DeliveryLatestTime *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=delivery_latest_time,json=deliveryLatestTime,proto3" json:"delivery_latest_time,omitempty"` // The line items of the shipment. LineItems []*LineItem `protobuf:"bytes,11,rep,name=line_items,json=lineItems,proto3" json:"line_items,omitempty"` // Annotations of the shipment. Annotations map[string]string `protobuf:"bytes,12,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Reference ID provided by external system. ExternalReferenceId string `protobuf:"bytes,13,opt,name=external_reference_id,json=externalReferenceId,proto3" json:"external_reference_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Shipment) Reset() { *x = Shipment{} mi := &file_einride_example_freight_v1_shipment_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Shipment) String() string { return protoimpl.X.MessageStringOf(x) } func (*Shipment) ProtoMessage() {} func (x *Shipment) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_shipment_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Shipment.ProtoReflect.Descriptor instead. func (*Shipment) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_shipment_proto_rawDescGZIP(), []int{0} } func (x *Shipment) GetName() string { if x != nil { return x.Name } return "" } func (x *Shipment) GetCreateTime() *timestamppb.Timestamp { if x != nil { return x.CreateTime } return nil } func (x *Shipment) GetUpdateTime() *timestamppb.Timestamp { if x != nil { return x.UpdateTime } return nil } func (x *Shipment) GetDeleteTime() *timestamppb.Timestamp { if x != nil { return x.DeleteTime } return nil } func (x *Shipment) GetOriginSite() string { if x != nil { return x.OriginSite } return "" } func (x *Shipment) GetDestinationSite() string { if x != nil { return x.DestinationSite } return "" } func (x *Shipment) GetPickupEarliestTime() *timestamppb.Timestamp { if x != nil { return x.PickupEarliestTime } return nil } func (x *Shipment) GetPickupLatestTime() *timestamppb.Timestamp { if x != nil { return x.PickupLatestTime } return nil } func (x *Shipment) GetDeliveryEarliestTime() *timestamppb.Timestamp { if x != nil { return x.DeliveryEarliestTime } return nil } func (x *Shipment) GetDeliveryLatestTime() *timestamppb.Timestamp { if x != nil { return x.DeliveryLatestTime } return nil } func (x *Shipment) GetLineItems() []*LineItem { if x != nil { return x.LineItems } return nil } func (x *Shipment) GetAnnotations() map[string]string { if x != nil { return x.Annotations } return nil } func (x *Shipment) GetExternalReferenceId() string { if x != nil { return x.ExternalReferenceId } return "" } // A shipment line item. type LineItem struct { state protoimpl.MessageState `protogen:"open.v1"` // The title of the line item. Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` // The quantity of the line item. Quantity float32 `protobuf:"fixed32,2,opt,name=quantity,proto3" json:"quantity,omitempty"` // The weight of the line item in kilograms. WeightKg float32 `protobuf:"fixed32,3,opt,name=weight_kg,json=weightKg,proto3" json:"weight_kg,omitempty"` // The volume of the line item in cubic meters. VolumeM3 float32 `protobuf:"fixed32,4,opt,name=volume_m3,json=volumeM3,proto3" json:"volume_m3,omitempty"` // Reference ID provided by external system. ExternalReferenceId string `protobuf:"bytes,5,opt,name=external_reference_id,json=externalReferenceId,proto3" json:"external_reference_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LineItem) Reset() { *x = LineItem{} mi := &file_einride_example_freight_v1_shipment_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LineItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*LineItem) ProtoMessage() {} func (x *LineItem) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_shipment_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LineItem.ProtoReflect.Descriptor instead. func (*LineItem) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_shipment_proto_rawDescGZIP(), []int{1} } func (x *LineItem) GetTitle() string { if x != nil { return x.Title } return "" } func (x *LineItem) GetQuantity() float32 { if x != nil { return x.Quantity } return 0 } func (x *LineItem) GetWeightKg() float32 { if x != nil { return x.WeightKg } return 0 } func (x *LineItem) GetVolumeM3() float32 { if x != nil { return x.VolumeM3 } return 0 } func (x *LineItem) GetExternalReferenceId() string { if x != nil { return x.ExternalReferenceId } return "" } var File_einride_example_freight_v1_shipment_proto protoreflect.FileDescriptor const file_einride_example_freight_v1_shipment_proto_rawDesc = "" + "\n" + ")einride/example/freight/v1/shipment.proto\x12\x1aeinride.example.freight.v1\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xdd\b\n" + "\bShipment\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12A\n" + "\vcreate_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "createTime\x12A\n" + "\vupdate_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "updateTime\x12A\n" + "\vdelete_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "deleteTime\x12K\n" + "\vorigin_site\x18\x05 \x01(\tB*\xe2A\x01\x02\xfaA#\n" + "!freight-example.einride.tech/SiteR\n" + "originSite\x12U\n" + "\x10destination_site\x18\x06 \x01(\tB*\xe2A\x01\x02\xfaA#\n" + "!freight-example.einride.tech/SiteR\x0fdestinationSite\x12R\n" + "\x14pickup_earliest_time\x18\a \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x02R\x12pickupEarliestTime\x12N\n" + "\x12pickup_latest_time\x18\b \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x02R\x10pickupLatestTime\x12V\n" + "\x16delivery_earliest_time\x18\t \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x02R\x14deliveryEarliestTime\x12R\n" + "\x14delivery_latest_time\x18\n" + " \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x02R\x12deliveryLatestTime\x12C\n" + "\n" + "line_items\x18\v \x03(\v2$.einride.example.freight.v1.LineItemR\tlineItems\x12W\n" + "\vannotations\x18\f \x03(\v25.einride.example.freight.v1.Shipment.AnnotationsEntryR\vannotations\x128\n" + "\x15external_reference_id\x18\r \x01(\tB\x04\xe2A\x01\x05R\x13externalReferenceId\x1a>\n" + "\x10AnnotationsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01:h\xeaAe\n" + "%freight-example.einride.tech/Shipment\x12'shippers/{shipper}/shipments/{shipment}*\tshipments2\bshipment\"\xb0\x01\n" + "\bLineItem\x12\x14\n" + "\x05title\x18\x01 \x01(\tR\x05title\x12\x1a\n" + "\bquantity\x18\x02 \x01(\x02R\bquantity\x12\x1b\n" + "\tweight_kg\x18\x03 \x01(\x02R\bweightKg\x12\x1b\n" + "\tvolume_m3\x18\x04 \x01(\x02R\bvolumeM3\x128\n" + "\x15external_reference_id\x18\x05 \x01(\tB\x04\xe2A\x01\x05R\x13externalReferenceIdB\xfe\x01\n" + "\x1ecom.einride.example.freight.v1B\rShipmentProtoP\x01ZBgo.einride.tech/aip/proto/gen/einride/example/freight/v1;freightv1\xa2\x02\x03EEF\xaa\x02\x1aEinride.Example.Freight.V1\xca\x02\x1aEinride\\Example\\Freight\\V1\xe2\x02&Einride\\Example\\Freight\\V1\\GPBMetadata\xea\x02\x1dEinride::Example::Freight::V1b\x06proto3" var ( file_einride_example_freight_v1_shipment_proto_rawDescOnce sync.Once file_einride_example_freight_v1_shipment_proto_rawDescData []byte ) func file_einride_example_freight_v1_shipment_proto_rawDescGZIP() []byte { file_einride_example_freight_v1_shipment_proto_rawDescOnce.Do(func() { file_einride_example_freight_v1_shipment_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_shipment_proto_rawDesc), len(file_einride_example_freight_v1_shipment_proto_rawDesc))) }) return file_einride_example_freight_v1_shipment_proto_rawDescData } var file_einride_example_freight_v1_shipment_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_einride_example_freight_v1_shipment_proto_goTypes = []any{ (*Shipment)(nil), // 0: einride.example.freight.v1.Shipment (*LineItem)(nil), // 1: einride.example.freight.v1.LineItem nil, // 2: einride.example.freight.v1.Shipment.AnnotationsEntry (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp } var file_einride_example_freight_v1_shipment_proto_depIdxs = []int32{ 3, // 0: einride.example.freight.v1.Shipment.create_time:type_name -> google.protobuf.Timestamp 3, // 1: einride.example.freight.v1.Shipment.update_time:type_name -> google.protobuf.Timestamp 3, // 2: einride.example.freight.v1.Shipment.delete_time:type_name -> google.protobuf.Timestamp 3, // 3: einride.example.freight.v1.Shipment.pickup_earliest_time:type_name -> google.protobuf.Timestamp 3, // 4: einride.example.freight.v1.Shipment.pickup_latest_time:type_name -> google.protobuf.Timestamp 3, // 5: einride.example.freight.v1.Shipment.delivery_earliest_time:type_name -> google.protobuf.Timestamp 3, // 6: einride.example.freight.v1.Shipment.delivery_latest_time:type_name -> google.protobuf.Timestamp 1, // 7: einride.example.freight.v1.Shipment.line_items:type_name -> einride.example.freight.v1.LineItem 2, // 8: einride.example.freight.v1.Shipment.annotations:type_name -> einride.example.freight.v1.Shipment.AnnotationsEntry 9, // [9:9] is the sub-list for method output_type 9, // [9:9] is the sub-list for method input_type 9, // [9:9] is the sub-list for extension type_name 9, // [9:9] is the sub-list for extension extendee 0, // [0:9] is the sub-list for field type_name } func init() { file_einride_example_freight_v1_shipment_proto_init() } func file_einride_example_freight_v1_shipment_proto_init() { if File_einride_example_freight_v1_shipment_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_shipment_proto_rawDesc), len(file_einride_example_freight_v1_shipment_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_einride_example_freight_v1_shipment_proto_goTypes, DependencyIndexes: file_einride_example_freight_v1_shipment_proto_depIdxs, MessageInfos: file_einride_example_freight_v1_shipment_proto_msgTypes, }.Build() File_einride_example_freight_v1_shipment_proto = out.File file_einride_example_freight_v1_shipment_proto_goTypes = nil file_einride_example_freight_v1_shipment_proto_depIdxs = nil } aip-go-0.80.0/proto/gen/einride/example/freight/v1/shipment_aip.go000066400000000000000000000042711513342120500247260ustar00rootroot00000000000000// Code generated by protoc-gen-go-aip. DO NOT EDIT. // // versions: // protoc-gen-go-aip development // protoc (unknown) // source: einride/example/freight/v1/shipment.proto package freightv1 import ( fmt "fmt" resourcename "go.einride.tech/aip/resourcename" strings "strings" ) type ShipmentResourceName struct { Shipper string Shipment string } func (n ShipperResourceName) ShipmentResourceName( shipment string, ) ShipmentResourceName { return ShipmentResourceName{ Shipper: n.Shipper, Shipment: shipment, } } func (n ShipmentResourceName) Validate() error { if n.Shipper == "" { return fmt.Errorf("shipper: empty") } if strings.IndexByte(n.Shipper, '/') != -1 { return fmt.Errorf("shipper: contains illegal character '/'") } if n.Shipment == "" { return fmt.Errorf("shipment: empty") } if strings.IndexByte(n.Shipment, '/') != -1 { return fmt.Errorf("shipment: contains illegal character '/'") } return nil } func (n ShipmentResourceName) ContainsWildcard() bool { return false || n.Shipper == "-" || n.Shipment == "-" } func (n ShipmentResourceName) String() string { return resourcename.Sprint( "shippers/{shipper}/shipments/{shipment}", n.Shipper, n.Shipment, ) } func (n ShipmentResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ShipmentResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ShipmentResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shippers/{shipper}/shipments/{shipment}", &n.Shipper, &n.Shipment, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ShipmentResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ShipmentResourceName) Type() string { return "freight-example.einride.tech/Shipment" } func (n ShipmentResourceName) ShipperResourceName() ShipperResourceName { return ShipperResourceName{ Shipper: n.Shipper, } } aip-go-0.80.0/proto/gen/einride/example/freight/v1/shipper.pb.go000066400000000000000000000154111513342120500243160ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.6 // protoc (unknown) // source: einride/example/freight/v1/shipper.proto package freightv1 import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // A shipper is a supplier or owner of goods to be transported. type Shipper struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the shipper. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // The creation timestamp of the shipper. CreateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` // The last update timestamp of the shipper. // // Updated when create/update/delete operation is performed. UpdateTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` // The deletion timestamp of the shipper. DeleteTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=delete_time,json=deleteTime,proto3" json:"delete_time,omitempty"` // The display name of the shipper. DisplayName string `protobuf:"bytes,5,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Shipper) Reset() { *x = Shipper{} mi := &file_einride_example_freight_v1_shipper_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Shipper) String() string { return protoimpl.X.MessageStringOf(x) } func (*Shipper) ProtoMessage() {} func (x *Shipper) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_shipper_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Shipper.ProtoReflect.Descriptor instead. func (*Shipper) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_shipper_proto_rawDescGZIP(), []int{0} } func (x *Shipper) GetName() string { if x != nil { return x.Name } return "" } func (x *Shipper) GetCreateTime() *timestamppb.Timestamp { if x != nil { return x.CreateTime } return nil } func (x *Shipper) GetUpdateTime() *timestamppb.Timestamp { if x != nil { return x.UpdateTime } return nil } func (x *Shipper) GetDeleteTime() *timestamppb.Timestamp { if x != nil { return x.DeleteTime } return nil } func (x *Shipper) GetDisplayName() string { if x != nil { return x.DisplayName } return "" } var File_einride_example_freight_v1_shipper_proto protoreflect.FileDescriptor const file_einride_example_freight_v1_shipper_proto_rawDesc = "" + "\n" + "(einride/example/freight/v1/shipper.proto\x12\x1aeinride.example.freight.v1\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xe1\x02\n" + "\aShipper\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12A\n" + "\vcreate_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "createTime\x12A\n" + "\vupdate_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "updateTime\x12A\n" + "\vdelete_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "deleteTime\x12'\n" + "\fdisplay_name\x18\x05 \x01(\tB\x04\xe2A\x01\x02R\vdisplayName:P\xeaAM\n" + "$freight-example.einride.tech/Shipper\x12\x12shippers/{shipper}*\bshippers2\ashipperB\xfd\x01\n" + "\x1ecom.einride.example.freight.v1B\fShipperProtoP\x01ZBgo.einride.tech/aip/proto/gen/einride/example/freight/v1;freightv1\xa2\x02\x03EEF\xaa\x02\x1aEinride.Example.Freight.V1\xca\x02\x1aEinride\\Example\\Freight\\V1\xe2\x02&Einride\\Example\\Freight\\V1\\GPBMetadata\xea\x02\x1dEinride::Example::Freight::V1b\x06proto3" var ( file_einride_example_freight_v1_shipper_proto_rawDescOnce sync.Once file_einride_example_freight_v1_shipper_proto_rawDescData []byte ) func file_einride_example_freight_v1_shipper_proto_rawDescGZIP() []byte { file_einride_example_freight_v1_shipper_proto_rawDescOnce.Do(func() { file_einride_example_freight_v1_shipper_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_shipper_proto_rawDesc), len(file_einride_example_freight_v1_shipper_proto_rawDesc))) }) return file_einride_example_freight_v1_shipper_proto_rawDescData } var file_einride_example_freight_v1_shipper_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_einride_example_freight_v1_shipper_proto_goTypes = []any{ (*Shipper)(nil), // 0: einride.example.freight.v1.Shipper (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp } var file_einride_example_freight_v1_shipper_proto_depIdxs = []int32{ 1, // 0: einride.example.freight.v1.Shipper.create_time:type_name -> google.protobuf.Timestamp 1, // 1: einride.example.freight.v1.Shipper.update_time:type_name -> google.protobuf.Timestamp 1, // 2: einride.example.freight.v1.Shipper.delete_time:type_name -> google.protobuf.Timestamp 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_einride_example_freight_v1_shipper_proto_init() } func file_einride_example_freight_v1_shipper_proto_init() { if File_einride_example_freight_v1_shipper_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_shipper_proto_rawDesc), len(file_einride_example_freight_v1_shipper_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_einride_example_freight_v1_shipper_proto_goTypes, DependencyIndexes: file_einride_example_freight_v1_shipper_proto_depIdxs, MessageInfos: file_einride_example_freight_v1_shipper_proto_msgTypes, }.Build() File_einride_example_freight_v1_shipper_proto = out.File file_einride_example_freight_v1_shipper_proto_goTypes = nil file_einride_example_freight_v1_shipper_proto_depIdxs = nil } aip-go-0.80.0/proto/gen/einride/example/freight/v1/shipper_aip.go000066400000000000000000000031331513342120500245450ustar00rootroot00000000000000// Code generated by protoc-gen-go-aip. DO NOT EDIT. // // versions: // protoc-gen-go-aip development // protoc (unknown) // source: einride/example/freight/v1/shipper.proto package freightv1 import ( fmt "fmt" resourcename "go.einride.tech/aip/resourcename" strings "strings" ) type ShipperResourceName struct { Shipper string } func (n ShipperResourceName) Validate() error { if n.Shipper == "" { return fmt.Errorf("shipper: empty") } if strings.IndexByte(n.Shipper, '/') != -1 { return fmt.Errorf("shipper: contains illegal character '/'") } return nil } func (n ShipperResourceName) ContainsWildcard() bool { return false || n.Shipper == "-" } func (n ShipperResourceName) String() string { return resourcename.Sprint( "shippers/{shipper}", n.Shipper, ) } func (n ShipperResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n ShipperResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *ShipperResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shippers/{shipper}", &n.Shipper, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *ShipperResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n ShipperResourceName) Type() string { return "freight-example.einride.tech/Shipper" } aip-go-0.80.0/proto/gen/einride/example/freight/v1/site.pb.go000066400000000000000000000162711513342120500236150ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.6 // protoc (unknown) // source: einride/example/freight/v1/site.proto package freightv1 import ( _ "google.golang.org/genproto/googleapis/api/annotations" latlng "google.golang.org/genproto/googleapis/type/latlng" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // A site is a node in a [shipper][einride.example.freight.v1.Shipper]'s // transport network. type Site struct { state protoimpl.MessageState `protogen:"open.v1"` // The resource name of the site. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // The creation timestamp of the site. CreateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` // The last update timestamp of the site. // // Updated when create/update/delete operation is performed. UpdateTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` // The deletion timestamp of the site. DeleteTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=delete_time,json=deleteTime,proto3" json:"delete_time,omitempty"` // The display name of the site. DisplayName string `protobuf:"bytes,5,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // The geographic location of the site. LatLng *latlng.LatLng `protobuf:"bytes,6,opt,name=lat_lng,json=latLng,proto3" json:"lat_lng,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Site) Reset() { *x = Site{} mi := &file_einride_example_freight_v1_site_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Site) String() string { return protoimpl.X.MessageStringOf(x) } func (*Site) ProtoMessage() {} func (x *Site) ProtoReflect() protoreflect.Message { mi := &file_einride_example_freight_v1_site_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Site.ProtoReflect.Descriptor instead. func (*Site) Descriptor() ([]byte, []int) { return file_einride_example_freight_v1_site_proto_rawDescGZIP(), []int{0} } func (x *Site) GetName() string { if x != nil { return x.Name } return "" } func (x *Site) GetCreateTime() *timestamppb.Timestamp { if x != nil { return x.CreateTime } return nil } func (x *Site) GetUpdateTime() *timestamppb.Timestamp { if x != nil { return x.UpdateTime } return nil } func (x *Site) GetDeleteTime() *timestamppb.Timestamp { if x != nil { return x.DeleteTime } return nil } func (x *Site) GetDisplayName() string { if x != nil { return x.DisplayName } return "" } func (x *Site) GetLatLng() *latlng.LatLng { if x != nil { return x.LatLng } return nil } var File_einride_example_freight_v1_site_proto protoreflect.FileDescriptor const file_einride_example_freight_v1_site_proto_rawDesc = "" + "\n" + "%einride/example/freight/v1/site.proto\x12\x1aeinride.example.freight.v1\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x18google/type/latlng.proto\"\x90\x03\n" + "\x04Site\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12A\n" + "\vcreate_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "createTime\x12A\n" + "\vupdate_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "updateTime\x12A\n" + "\vdelete_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampB\x04\xe2A\x01\x03R\n" + "deleteTime\x12'\n" + "\fdisplay_name\x18\x05 \x01(\tB\x04\xe2A\x01\x02R\vdisplayName\x12,\n" + "\alat_lng\x18\x06 \x01(\v2\x13.google.type.LatLngR\x06latLng:T\xeaAQ\n" + "!freight-example.einride.tech/Site\x12\x1fshippers/{shipper}/sites/{site}*\x05sites2\x04siteB\xfa\x01\n" + "\x1ecom.einride.example.freight.v1B\tSiteProtoP\x01ZBgo.einride.tech/aip/proto/gen/einride/example/freight/v1;freightv1\xa2\x02\x03EEF\xaa\x02\x1aEinride.Example.Freight.V1\xca\x02\x1aEinride\\Example\\Freight\\V1\xe2\x02&Einride\\Example\\Freight\\V1\\GPBMetadata\xea\x02\x1dEinride::Example::Freight::V1b\x06proto3" var ( file_einride_example_freight_v1_site_proto_rawDescOnce sync.Once file_einride_example_freight_v1_site_proto_rawDescData []byte ) func file_einride_example_freight_v1_site_proto_rawDescGZIP() []byte { file_einride_example_freight_v1_site_proto_rawDescOnce.Do(func() { file_einride_example_freight_v1_site_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_site_proto_rawDesc), len(file_einride_example_freight_v1_site_proto_rawDesc))) }) return file_einride_example_freight_v1_site_proto_rawDescData } var file_einride_example_freight_v1_site_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_einride_example_freight_v1_site_proto_goTypes = []any{ (*Site)(nil), // 0: einride.example.freight.v1.Site (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp (*latlng.LatLng)(nil), // 2: google.type.LatLng } var file_einride_example_freight_v1_site_proto_depIdxs = []int32{ 1, // 0: einride.example.freight.v1.Site.create_time:type_name -> google.protobuf.Timestamp 1, // 1: einride.example.freight.v1.Site.update_time:type_name -> google.protobuf.Timestamp 1, // 2: einride.example.freight.v1.Site.delete_time:type_name -> google.protobuf.Timestamp 2, // 3: einride.example.freight.v1.Site.lat_lng:type_name -> google.type.LatLng 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_einride_example_freight_v1_site_proto_init() } func file_einride_example_freight_v1_site_proto_init() { if File_einride_example_freight_v1_site_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_einride_example_freight_v1_site_proto_rawDesc), len(file_einride_example_freight_v1_site_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_einride_example_freight_v1_site_proto_goTypes, DependencyIndexes: file_einride_example_freight_v1_site_proto_depIdxs, MessageInfos: file_einride_example_freight_v1_site_proto_msgTypes, }.Build() File_einride_example_freight_v1_site_proto = out.File file_einride_example_freight_v1_site_proto_goTypes = nil file_einride_example_freight_v1_site_proto_depIdxs = nil } aip-go-0.80.0/proto/gen/einride/example/freight/v1/site_aip.go000066400000000000000000000041051513342120500240370ustar00rootroot00000000000000// Code generated by protoc-gen-go-aip. DO NOT EDIT. // // versions: // protoc-gen-go-aip development // protoc (unknown) // source: einride/example/freight/v1/site.proto package freightv1 import ( fmt "fmt" resourcename "go.einride.tech/aip/resourcename" strings "strings" ) type SiteResourceName struct { Shipper string Site string } func (n ShipperResourceName) SiteResourceName( site string, ) SiteResourceName { return SiteResourceName{ Shipper: n.Shipper, Site: site, } } func (n SiteResourceName) Validate() error { if n.Shipper == "" { return fmt.Errorf("shipper: empty") } if strings.IndexByte(n.Shipper, '/') != -1 { return fmt.Errorf("shipper: contains illegal character '/'") } if n.Site == "" { return fmt.Errorf("site: empty") } if strings.IndexByte(n.Site, '/') != -1 { return fmt.Errorf("site: contains illegal character '/'") } return nil } func (n SiteResourceName) ContainsWildcard() bool { return false || n.Shipper == "-" || n.Site == "-" } func (n SiteResourceName) String() string { return resourcename.Sprint( "shippers/{shipper}/sites/{site}", n.Shipper, n.Site, ) } func (n SiteResourceName) MarshalString() (string, error) { if err := n.Validate(); err != nil { return "", err } return n.String(), nil } // MarshalText implements the encoding.TextMarshaler interface. func (n SiteResourceName) MarshalText() ([]byte, error) { if err := n.Validate(); err != nil { return nil, err } return []byte(n.String()), nil } func (n *SiteResourceName) UnmarshalString(name string) error { err := resourcename.Sscan( name, "shippers/{shipper}/sites/{site}", &n.Shipper, &n.Site, ) if err != nil { return err } return n.Validate() } // UnmarshalText implements the encoding.TextUnmarshaler interface. func (n *SiteResourceName) UnmarshalText(text []byte) error { return n.UnmarshalString(string(text)) } func (n SiteResourceName) Type() string { return "freight-example.einride.tech/Site" } func (n SiteResourceName) ShipperResourceName() ShipperResourceName { return ShipperResourceName{ Shipper: n.Shipper, } } aip-go-0.80.0/proto/gen/einride/example/syntax/000077500000000000000000000000001513342120500212635ustar00rootroot00000000000000aip-go-0.80.0/proto/gen/einride/example/syntax/v1/000077500000000000000000000000001513342120500216115ustar00rootroot00000000000000aip-go-0.80.0/proto/gen/einride/example/syntax/v1/fieldbehaviors.pb.go000066400000000000000000000452451513342120500255400ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.6 // protoc (unknown) // source: einride/example/syntax/v1/fieldbehaviors.proto package syntaxv1 import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type FieldBehaviorMessage struct { state protoimpl.MessageState `protogen:"open.v1"` Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` OutputOnlyField string `protobuf:"bytes,2,opt,name=output_only_field,json=outputOnlyField,proto3" json:"output_only_field,omitempty"` OptionalField string `protobuf:"bytes,3,opt,name=optional_field,json=optionalField,proto3" json:"optional_field,omitempty"` ImmutableField string `protobuf:"bytes,17,opt,name=immutable_field,json=immutableField,proto3" json:"immutable_field,omitempty"` MessageWithoutFieldBehavior *FieldBehaviorMessage `protobuf:"bytes,12,opt,name=message_without_field_behavior,json=messageWithoutFieldBehavior,proto3" json:"message_without_field_behavior,omitempty"` OutputOnlyMessage *FieldBehaviorMessage `protobuf:"bytes,13,opt,name=output_only_message,json=outputOnlyMessage,proto3" json:"output_only_message,omitempty"` OptionalMessage *FieldBehaviorMessage `protobuf:"bytes,14,opt,name=optional_message,json=optionalMessage,proto3" json:"optional_message,omitempty"` RepeatedMessage []*FieldBehaviorMessage `protobuf:"bytes,4,rep,name=repeated_message,json=repeatedMessage,proto3" json:"repeated_message,omitempty"` RepeatedOutputOnlyMessage []*FieldBehaviorMessage `protobuf:"bytes,5,rep,name=repeated_output_only_message,json=repeatedOutputOnlyMessage,proto3" json:"repeated_output_only_message,omitempty"` RepeatedOptionalMessage []*FieldBehaviorMessage `protobuf:"bytes,6,rep,name=repeated_optional_message,json=repeatedOptionalMessage,proto3" json:"repeated_optional_message,omitempty"` MapMessage map[string]*FieldBehaviorMessage `protobuf:"bytes,7,rep,name=map_message,json=mapMessage,proto3" json:"map_message,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` MapOutputOnlyMessage map[string]*FieldBehaviorMessage `protobuf:"bytes,8,rep,name=map_output_only_message,json=mapOutputOnlyMessage,proto3" json:"map_output_only_message,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` MapOptionalMessage map[string]*FieldBehaviorMessage `protobuf:"bytes,9,rep,name=map_optional_message,json=mapOptionalMessage,proto3" json:"map_optional_message,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` StringMap map[string]string `protobuf:"bytes,10,rep,name=string_map,json=stringMap,proto3" json:"string_map,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` StringList []string `protobuf:"bytes,11,rep,name=string_list,json=stringList,proto3" json:"string_list,omitempty"` // Types that are valid to be assigned to Oneof: // // *FieldBehaviorMessage_FieldBehaviorMessage // *FieldBehaviorMessage_SmallFieldBehaviorMessage Oneof isFieldBehaviorMessage_Oneof `protobuf_oneof:"oneof"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FieldBehaviorMessage) Reset() { *x = FieldBehaviorMessage{} mi := &file_einride_example_syntax_v1_fieldbehaviors_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FieldBehaviorMessage) String() string { return protoimpl.X.MessageStringOf(x) } func (*FieldBehaviorMessage) ProtoMessage() {} func (x *FieldBehaviorMessage) ProtoReflect() protoreflect.Message { mi := &file_einride_example_syntax_v1_fieldbehaviors_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FieldBehaviorMessage.ProtoReflect.Descriptor instead. func (*FieldBehaviorMessage) Descriptor() ([]byte, []int) { return file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescGZIP(), []int{0} } func (x *FieldBehaviorMessage) GetField() string { if x != nil { return x.Field } return "" } func (x *FieldBehaviorMessage) GetOutputOnlyField() string { if x != nil { return x.OutputOnlyField } return "" } func (x *FieldBehaviorMessage) GetOptionalField() string { if x != nil { return x.OptionalField } return "" } func (x *FieldBehaviorMessage) GetImmutableField() string { if x != nil { return x.ImmutableField } return "" } func (x *FieldBehaviorMessage) GetMessageWithoutFieldBehavior() *FieldBehaviorMessage { if x != nil { return x.MessageWithoutFieldBehavior } return nil } func (x *FieldBehaviorMessage) GetOutputOnlyMessage() *FieldBehaviorMessage { if x != nil { return x.OutputOnlyMessage } return nil } func (x *FieldBehaviorMessage) GetOptionalMessage() *FieldBehaviorMessage { if x != nil { return x.OptionalMessage } return nil } func (x *FieldBehaviorMessage) GetRepeatedMessage() []*FieldBehaviorMessage { if x != nil { return x.RepeatedMessage } return nil } func (x *FieldBehaviorMessage) GetRepeatedOutputOnlyMessage() []*FieldBehaviorMessage { if x != nil { return x.RepeatedOutputOnlyMessage } return nil } func (x *FieldBehaviorMessage) GetRepeatedOptionalMessage() []*FieldBehaviorMessage { if x != nil { return x.RepeatedOptionalMessage } return nil } func (x *FieldBehaviorMessage) GetMapMessage() map[string]*FieldBehaviorMessage { if x != nil { return x.MapMessage } return nil } func (x *FieldBehaviorMessage) GetMapOutputOnlyMessage() map[string]*FieldBehaviorMessage { if x != nil { return x.MapOutputOnlyMessage } return nil } func (x *FieldBehaviorMessage) GetMapOptionalMessage() map[string]*FieldBehaviorMessage { if x != nil { return x.MapOptionalMessage } return nil } func (x *FieldBehaviorMessage) GetStringMap() map[string]string { if x != nil { return x.StringMap } return nil } func (x *FieldBehaviorMessage) GetStringList() []string { if x != nil { return x.StringList } return nil } func (x *FieldBehaviorMessage) GetOneof() isFieldBehaviorMessage_Oneof { if x != nil { return x.Oneof } return nil } func (x *FieldBehaviorMessage) GetFieldBehaviorMessage() *FieldBehaviorMessage { if x != nil { if x, ok := x.Oneof.(*FieldBehaviorMessage_FieldBehaviorMessage); ok { return x.FieldBehaviorMessage } } return nil } func (x *FieldBehaviorMessage) GetSmallFieldBehaviorMessage() *SmallFieldBehaviorMessage { if x != nil { if x, ok := x.Oneof.(*FieldBehaviorMessage_SmallFieldBehaviorMessage); ok { return x.SmallFieldBehaviorMessage } } return nil } type isFieldBehaviorMessage_Oneof interface { isFieldBehaviorMessage_Oneof() } type FieldBehaviorMessage_FieldBehaviorMessage struct { FieldBehaviorMessage *FieldBehaviorMessage `protobuf:"bytes,15,opt,name=field_behavior_message,json=fieldBehaviorMessage,proto3,oneof"` } type FieldBehaviorMessage_SmallFieldBehaviorMessage struct { SmallFieldBehaviorMessage *SmallFieldBehaviorMessage `protobuf:"bytes,16,opt,name=small_field_behavior_message,json=smallFieldBehaviorMessage,proto3,oneof"` } func (*FieldBehaviorMessage_FieldBehaviorMessage) isFieldBehaviorMessage_Oneof() {} func (*FieldBehaviorMessage_SmallFieldBehaviorMessage) isFieldBehaviorMessage_Oneof() {} type SmallFieldBehaviorMessage struct { state protoimpl.MessageState `protogen:"open.v1"` Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` OutputOnlyField string `protobuf:"bytes,2,opt,name=output_only_field,json=outputOnlyField,proto3" json:"output_only_field,omitempty"` OptionalField string `protobuf:"bytes,3,opt,name=optional_field,json=optionalField,proto3" json:"optional_field,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SmallFieldBehaviorMessage) Reset() { *x = SmallFieldBehaviorMessage{} mi := &file_einride_example_syntax_v1_fieldbehaviors_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SmallFieldBehaviorMessage) String() string { return protoimpl.X.MessageStringOf(x) } func (*SmallFieldBehaviorMessage) ProtoMessage() {} func (x *SmallFieldBehaviorMessage) ProtoReflect() protoreflect.Message { mi := &file_einride_example_syntax_v1_fieldbehaviors_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SmallFieldBehaviorMessage.ProtoReflect.Descriptor instead. func (*SmallFieldBehaviorMessage) Descriptor() ([]byte, []int) { return file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescGZIP(), []int{1} } func (x *SmallFieldBehaviorMessage) GetField() string { if x != nil { return x.Field } return "" } func (x *SmallFieldBehaviorMessage) GetOutputOnlyField() string { if x != nil { return x.OutputOnlyField } return "" } func (x *SmallFieldBehaviorMessage) GetOptionalField() string { if x != nil { return x.OptionalField } return "" } var File_einride_example_syntax_v1_fieldbehaviors_proto protoreflect.FileDescriptor const file_einride_example_syntax_v1_fieldbehaviors_proto_rawDesc = "" + "\n" + ".einride/example/syntax/v1/fieldbehaviors.proto\x12\x19einride.example.syntax.v1\x1a\x1fgoogle/api/field_behavior.proto\"\xb7\x0f\n" + "\x14FieldBehaviorMessage\x12\x14\n" + "\x05field\x18\x01 \x01(\tR\x05field\x120\n" + "\x11output_only_field\x18\x02 \x01(\tB\x04\xe2A\x01\x03R\x0foutputOnlyField\x12+\n" + "\x0eoptional_field\x18\x03 \x01(\tB\x04\xe2A\x01\x01R\roptionalField\x12-\n" + "\x0fimmutable_field\x18\x11 \x01(\tB\x04\xe2A\x01\x05R\x0eimmutableField\x12t\n" + "\x1emessage_without_field_behavior\x18\f \x01(\v2/.einride.example.syntax.v1.FieldBehaviorMessageR\x1bmessageWithoutFieldBehavior\x12e\n" + "\x13output_only_message\x18\r \x01(\v2/.einride.example.syntax.v1.FieldBehaviorMessageB\x04\xe2A\x01\x03R\x11outputOnlyMessage\x12`\n" + "\x10optional_message\x18\x0e \x01(\v2/.einride.example.syntax.v1.FieldBehaviorMessageB\x04\xe2A\x01\x01R\x0foptionalMessage\x12Z\n" + "\x10repeated_message\x18\x04 \x03(\v2/.einride.example.syntax.v1.FieldBehaviorMessageR\x0frepeatedMessage\x12v\n" + "\x1crepeated_output_only_message\x18\x05 \x03(\v2/.einride.example.syntax.v1.FieldBehaviorMessageB\x04\xe2A\x01\x03R\x19repeatedOutputOnlyMessage\x12q\n" + "\x19repeated_optional_message\x18\x06 \x03(\v2/.einride.example.syntax.v1.FieldBehaviorMessageB\x04\xe2A\x01\x01R\x17repeatedOptionalMessage\x12`\n" + "\vmap_message\x18\a \x03(\v2?.einride.example.syntax.v1.FieldBehaviorMessage.MapMessageEntryR\n" + "mapMessage\x12\x86\x01\n" + "\x17map_output_only_message\x18\b \x03(\v2I.einride.example.syntax.v1.FieldBehaviorMessage.MapOutputOnlyMessageEntryB\x04\xe2A\x01\x03R\x14mapOutputOnlyMessage\x12\x7f\n" + "\x14map_optional_message\x18\t \x03(\v2G.einride.example.syntax.v1.FieldBehaviorMessage.MapOptionalMessageEntryB\x04\xe2A\x01\x01R\x12mapOptionalMessage\x12]\n" + "\n" + "string_map\x18\n" + " \x03(\v2>.einride.example.syntax.v1.FieldBehaviorMessage.StringMapEntryR\tstringMap\x12\x1f\n" + "\vstring_list\x18\v \x03(\tR\n" + "stringList\x12g\n" + "\x16field_behavior_message\x18\x0f \x01(\v2/.einride.example.syntax.v1.FieldBehaviorMessageH\x00R\x14fieldBehaviorMessage\x12w\n" + "\x1csmall_field_behavior_message\x18\x10 \x01(\v24.einride.example.syntax.v1.SmallFieldBehaviorMessageH\x00R\x19smallFieldBehaviorMessage\x1an\n" + "\x0fMapMessageEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12E\n" + "\x05value\x18\x02 \x01(\v2/.einride.example.syntax.v1.FieldBehaviorMessageR\x05value:\x028\x01\x1ax\n" + "\x19MapOutputOnlyMessageEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12E\n" + "\x05value\x18\x02 \x01(\v2/.einride.example.syntax.v1.FieldBehaviorMessageR\x05value:\x028\x01\x1av\n" + "\x17MapOptionalMessageEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12E\n" + "\x05value\x18\x02 \x01(\v2/.einride.example.syntax.v1.FieldBehaviorMessageR\x05value:\x028\x01\x1a<\n" + "\x0eStringMapEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\a\n" + "\x05oneof\"\x90\x01\n" + "\x19SmallFieldBehaviorMessage\x12\x14\n" + "\x05field\x18\x01 \x01(\tR\x05field\x120\n" + "\x11output_only_field\x18\x02 \x01(\tB\x04\xe2A\x01\x03R\x0foutputOnlyField\x12+\n" + "\x0eoptional_field\x18\x03 \x01(\tB\x04\xe2A\x01\x01R\roptionalFieldB\xfd\x01\n" + "\x1dcom.einride.example.syntax.v1B\x13FieldbehaviorsProtoP\x01Z@go.einride.tech/aip/proto/gen/einride/example/syntax/v1;syntaxv1\xa2\x02\x03EES\xaa\x02\x19Einride.Example.Syntax.V1\xca\x02\x19Einride\\Example\\Syntax\\V1\xe2\x02%Einride\\Example\\Syntax\\V1\\GPBMetadata\xea\x02\x1cEinride::Example::Syntax::V1b\x06proto3" var ( file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescOnce sync.Once file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescData []byte ) func file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescGZIP() []byte { file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescOnce.Do(func() { file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_einride_example_syntax_v1_fieldbehaviors_proto_rawDesc), len(file_einride_example_syntax_v1_fieldbehaviors_proto_rawDesc))) }) return file_einride_example_syntax_v1_fieldbehaviors_proto_rawDescData } var file_einride_example_syntax_v1_fieldbehaviors_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_einride_example_syntax_v1_fieldbehaviors_proto_goTypes = []any{ (*FieldBehaviorMessage)(nil), // 0: einride.example.syntax.v1.FieldBehaviorMessage (*SmallFieldBehaviorMessage)(nil), // 1: einride.example.syntax.v1.SmallFieldBehaviorMessage nil, // 2: einride.example.syntax.v1.FieldBehaviorMessage.MapMessageEntry nil, // 3: einride.example.syntax.v1.FieldBehaviorMessage.MapOutputOnlyMessageEntry nil, // 4: einride.example.syntax.v1.FieldBehaviorMessage.MapOptionalMessageEntry nil, // 5: einride.example.syntax.v1.FieldBehaviorMessage.StringMapEntry } var file_einride_example_syntax_v1_fieldbehaviors_proto_depIdxs = []int32{ 0, // 0: einride.example.syntax.v1.FieldBehaviorMessage.message_without_field_behavior:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 0, // 1: einride.example.syntax.v1.FieldBehaviorMessage.output_only_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 0, // 2: einride.example.syntax.v1.FieldBehaviorMessage.optional_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 0, // 3: einride.example.syntax.v1.FieldBehaviorMessage.repeated_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 0, // 4: einride.example.syntax.v1.FieldBehaviorMessage.repeated_output_only_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 0, // 5: einride.example.syntax.v1.FieldBehaviorMessage.repeated_optional_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 2, // 6: einride.example.syntax.v1.FieldBehaviorMessage.map_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage.MapMessageEntry 3, // 7: einride.example.syntax.v1.FieldBehaviorMessage.map_output_only_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage.MapOutputOnlyMessageEntry 4, // 8: einride.example.syntax.v1.FieldBehaviorMessage.map_optional_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage.MapOptionalMessageEntry 5, // 9: einride.example.syntax.v1.FieldBehaviorMessage.string_map:type_name -> einride.example.syntax.v1.FieldBehaviorMessage.StringMapEntry 0, // 10: einride.example.syntax.v1.FieldBehaviorMessage.field_behavior_message:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 1, // 11: einride.example.syntax.v1.FieldBehaviorMessage.small_field_behavior_message:type_name -> einride.example.syntax.v1.SmallFieldBehaviorMessage 0, // 12: einride.example.syntax.v1.FieldBehaviorMessage.MapMessageEntry.value:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 0, // 13: einride.example.syntax.v1.FieldBehaviorMessage.MapOutputOnlyMessageEntry.value:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 0, // 14: einride.example.syntax.v1.FieldBehaviorMessage.MapOptionalMessageEntry.value:type_name -> einride.example.syntax.v1.FieldBehaviorMessage 15, // [15:15] is the sub-list for method output_type 15, // [15:15] is the sub-list for method input_type 15, // [15:15] is the sub-list for extension type_name 15, // [15:15] is the sub-list for extension extendee 0, // [0:15] is the sub-list for field type_name } func init() { file_einride_example_syntax_v1_fieldbehaviors_proto_init() } func file_einride_example_syntax_v1_fieldbehaviors_proto_init() { if File_einride_example_syntax_v1_fieldbehaviors_proto != nil { return } file_einride_example_syntax_v1_fieldbehaviors_proto_msgTypes[0].OneofWrappers = []any{ (*FieldBehaviorMessage_FieldBehaviorMessage)(nil), (*FieldBehaviorMessage_SmallFieldBehaviorMessage)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_einride_example_syntax_v1_fieldbehaviors_proto_rawDesc), len(file_einride_example_syntax_v1_fieldbehaviors_proto_rawDesc)), NumEnums: 0, NumMessages: 6, NumExtensions: 0, NumServices: 0, }, GoTypes: file_einride_example_syntax_v1_fieldbehaviors_proto_goTypes, DependencyIndexes: file_einride_example_syntax_v1_fieldbehaviors_proto_depIdxs, MessageInfos: file_einride_example_syntax_v1_fieldbehaviors_proto_msgTypes, }.Build() File_einride_example_syntax_v1_fieldbehaviors_proto = out.File file_einride_example_syntax_v1_fieldbehaviors_proto_goTypes = nil file_einride_example_syntax_v1_fieldbehaviors_proto_depIdxs = nil } aip-go-0.80.0/proto/gen/einride/example/syntax/v1/syntax.pb.go000066400000000000000000000503221513342120500240700ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.6 // protoc (unknown) // source: einride/example/syntax/v1/syntax.proto package syntaxv1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Enum int32 const ( Enum_ENUM_UNSPECIFIED Enum = 0 Enum_ENUM_ONE Enum = 1 Enum_ENUM_TWO Enum = 2 ) // Enum value maps for Enum. var ( Enum_name = map[int32]string{ 0: "ENUM_UNSPECIFIED", 1: "ENUM_ONE", 2: "ENUM_TWO", } Enum_value = map[string]int32{ "ENUM_UNSPECIFIED": 0, "ENUM_ONE": 1, "ENUM_TWO": 2, } ) func (x Enum) Enum() *Enum { p := new(Enum) *p = x return p } func (x Enum) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Enum) Descriptor() protoreflect.EnumDescriptor { return file_einride_example_syntax_v1_syntax_proto_enumTypes[0].Descriptor() } func (Enum) Type() protoreflect.EnumType { return &file_einride_example_syntax_v1_syntax_proto_enumTypes[0] } func (x Enum) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Enum.Descriptor instead. func (Enum) EnumDescriptor() ([]byte, []int) { return file_einride_example_syntax_v1_syntax_proto_rawDescGZIP(), []int{0} } type Message struct { state protoimpl.MessageState `protogen:"open.v1"` Double float64 `protobuf:"fixed64,1,opt,name=double,proto3" json:"double,omitempty"` Float float32 `protobuf:"fixed32,2,opt,name=float,proto3" json:"float,omitempty"` Int32 int32 `protobuf:"varint,3,opt,name=int32,proto3" json:"int32,omitempty"` Int64 int64 `protobuf:"varint,4,opt,name=int64,proto3" json:"int64,omitempty"` Uint32 uint32 `protobuf:"varint,5,opt,name=uint32,proto3" json:"uint32,omitempty"` Uint64 uint64 `protobuf:"varint,6,opt,name=uint64,proto3" json:"uint64,omitempty"` Sint32 int32 `protobuf:"zigzag32,7,opt,name=sint32,proto3" json:"sint32,omitempty"` Sint64 int64 `protobuf:"zigzag64,8,opt,name=sint64,proto3" json:"sint64,omitempty"` Fixed32 uint32 `protobuf:"fixed32,9,opt,name=fixed32,proto3" json:"fixed32,omitempty"` Fixed64 uint64 `protobuf:"fixed64,10,opt,name=fixed64,proto3" json:"fixed64,omitempty"` Sfixed32 int32 `protobuf:"fixed32,11,opt,name=sfixed32,proto3" json:"sfixed32,omitempty"` Sfixed64 int64 `protobuf:"fixed64,12,opt,name=sfixed64,proto3" json:"sfixed64,omitempty"` Bool bool `protobuf:"varint,13,opt,name=bool,proto3" json:"bool,omitempty"` String_ string `protobuf:"bytes,14,opt,name=string,proto3" json:"string,omitempty"` Bytes []byte `protobuf:"bytes,15,opt,name=bytes,proto3" json:"bytes,omitempty"` Enum Enum `protobuf:"varint,16,opt,name=enum,proto3,enum=einride.example.syntax.v1.Enum" json:"enum,omitempty"` Message *Message `protobuf:"bytes,17,opt,name=message,proto3" json:"message,omitempty"` RepeatedDouble []float64 `protobuf:"fixed64,18,rep,packed,name=repeated_double,json=repeatedDouble,proto3" json:"repeated_double,omitempty"` RepeatedFloat []float32 `protobuf:"fixed32,19,rep,packed,name=repeated_float,json=repeatedFloat,proto3" json:"repeated_float,omitempty"` RepeatedInt32 []int32 `protobuf:"varint,20,rep,packed,name=repeated_int32,json=repeatedInt32,proto3" json:"repeated_int32,omitempty"` RepeatedInt64 []int64 `protobuf:"varint,21,rep,packed,name=repeated_int64,json=repeatedInt64,proto3" json:"repeated_int64,omitempty"` RepeatedUint32 []uint32 `protobuf:"varint,22,rep,packed,name=repeated_uint32,json=repeatedUint32,proto3" json:"repeated_uint32,omitempty"` RepeatedUint64 []uint64 `protobuf:"varint,23,rep,packed,name=repeated_uint64,json=repeatedUint64,proto3" json:"repeated_uint64,omitempty"` RepeatedSint32 []int32 `protobuf:"zigzag32,24,rep,packed,name=repeated_sint32,json=repeatedSint32,proto3" json:"repeated_sint32,omitempty"` RepeatedSint64 []int64 `protobuf:"zigzag64,25,rep,packed,name=repeated_sint64,json=repeatedSint64,proto3" json:"repeated_sint64,omitempty"` RepeatedFixed32 []uint32 `protobuf:"fixed32,26,rep,packed,name=repeated_fixed32,json=repeatedFixed32,proto3" json:"repeated_fixed32,omitempty"` RepeatedFixed64 []uint64 `protobuf:"fixed64,27,rep,packed,name=repeated_fixed64,json=repeatedFixed64,proto3" json:"repeated_fixed64,omitempty"` RepeatedSfixed32 []int32 `protobuf:"fixed32,28,rep,packed,name=repeated_sfixed32,json=repeatedSfixed32,proto3" json:"repeated_sfixed32,omitempty"` RepeatedSfixed64 []int64 `protobuf:"fixed64,29,rep,packed,name=repeated_sfixed64,json=repeatedSfixed64,proto3" json:"repeated_sfixed64,omitempty"` RepeatedBool []bool `protobuf:"varint,30,rep,packed,name=repeated_bool,json=repeatedBool,proto3" json:"repeated_bool,omitempty"` RepeatedString []string `protobuf:"bytes,31,rep,name=repeated_string,json=repeatedString,proto3" json:"repeated_string,omitempty"` RepeatedBytes [][]byte `protobuf:"bytes,32,rep,name=repeated_bytes,json=repeatedBytes,proto3" json:"repeated_bytes,omitempty"` RepeatedEnum []Enum `protobuf:"varint,33,rep,packed,name=repeated_enum,json=repeatedEnum,proto3,enum=einride.example.syntax.v1.Enum" json:"repeated_enum,omitempty"` RepeatedMessage []*Message `protobuf:"bytes,34,rep,name=repeated_message,json=repeatedMessage,proto3" json:"repeated_message,omitempty"` MapStringString map[string]string `protobuf:"bytes,35,rep,name=map_string_string,json=mapStringString,proto3" json:"map_string_string,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` MapStringMessage map[string]*Message `protobuf:"bytes,36,rep,name=map_string_message,json=mapStringMessage,proto3" json:"map_string_message,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Types that are valid to be assigned to Oneof: // // *Message_OneofString // *Message_OneofEnum // *Message_OneofMessage1 // *Message_OneofMessage2 Oneof isMessage_Oneof `protobuf_oneof:"oneof"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Message) Reset() { *x = Message{} mi := &file_einride_example_syntax_v1_syntax_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Message) String() string { return protoimpl.X.MessageStringOf(x) } func (*Message) ProtoMessage() {} func (x *Message) ProtoReflect() protoreflect.Message { mi := &file_einride_example_syntax_v1_syntax_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Message.ProtoReflect.Descriptor instead. func (*Message) Descriptor() ([]byte, []int) { return file_einride_example_syntax_v1_syntax_proto_rawDescGZIP(), []int{0} } func (x *Message) GetDouble() float64 { if x != nil { return x.Double } return 0 } func (x *Message) GetFloat() float32 { if x != nil { return x.Float } return 0 } func (x *Message) GetInt32() int32 { if x != nil { return x.Int32 } return 0 } func (x *Message) GetInt64() int64 { if x != nil { return x.Int64 } return 0 } func (x *Message) GetUint32() uint32 { if x != nil { return x.Uint32 } return 0 } func (x *Message) GetUint64() uint64 { if x != nil { return x.Uint64 } return 0 } func (x *Message) GetSint32() int32 { if x != nil { return x.Sint32 } return 0 } func (x *Message) GetSint64() int64 { if x != nil { return x.Sint64 } return 0 } func (x *Message) GetFixed32() uint32 { if x != nil { return x.Fixed32 } return 0 } func (x *Message) GetFixed64() uint64 { if x != nil { return x.Fixed64 } return 0 } func (x *Message) GetSfixed32() int32 { if x != nil { return x.Sfixed32 } return 0 } func (x *Message) GetSfixed64() int64 { if x != nil { return x.Sfixed64 } return 0 } func (x *Message) GetBool() bool { if x != nil { return x.Bool } return false } func (x *Message) GetString_() string { if x != nil { return x.String_ } return "" } func (x *Message) GetBytes() []byte { if x != nil { return x.Bytes } return nil } func (x *Message) GetEnum() Enum { if x != nil { return x.Enum } return Enum_ENUM_UNSPECIFIED } func (x *Message) GetMessage() *Message { if x != nil { return x.Message } return nil } func (x *Message) GetRepeatedDouble() []float64 { if x != nil { return x.RepeatedDouble } return nil } func (x *Message) GetRepeatedFloat() []float32 { if x != nil { return x.RepeatedFloat } return nil } func (x *Message) GetRepeatedInt32() []int32 { if x != nil { return x.RepeatedInt32 } return nil } func (x *Message) GetRepeatedInt64() []int64 { if x != nil { return x.RepeatedInt64 } return nil } func (x *Message) GetRepeatedUint32() []uint32 { if x != nil { return x.RepeatedUint32 } return nil } func (x *Message) GetRepeatedUint64() []uint64 { if x != nil { return x.RepeatedUint64 } return nil } func (x *Message) GetRepeatedSint32() []int32 { if x != nil { return x.RepeatedSint32 } return nil } func (x *Message) GetRepeatedSint64() []int64 { if x != nil { return x.RepeatedSint64 } return nil } func (x *Message) GetRepeatedFixed32() []uint32 { if x != nil { return x.RepeatedFixed32 } return nil } func (x *Message) GetRepeatedFixed64() []uint64 { if x != nil { return x.RepeatedFixed64 } return nil } func (x *Message) GetRepeatedSfixed32() []int32 { if x != nil { return x.RepeatedSfixed32 } return nil } func (x *Message) GetRepeatedSfixed64() []int64 { if x != nil { return x.RepeatedSfixed64 } return nil } func (x *Message) GetRepeatedBool() []bool { if x != nil { return x.RepeatedBool } return nil } func (x *Message) GetRepeatedString() []string { if x != nil { return x.RepeatedString } return nil } func (x *Message) GetRepeatedBytes() [][]byte { if x != nil { return x.RepeatedBytes } return nil } func (x *Message) GetRepeatedEnum() []Enum { if x != nil { return x.RepeatedEnum } return nil } func (x *Message) GetRepeatedMessage() []*Message { if x != nil { return x.RepeatedMessage } return nil } func (x *Message) GetMapStringString() map[string]string { if x != nil { return x.MapStringString } return nil } func (x *Message) GetMapStringMessage() map[string]*Message { if x != nil { return x.MapStringMessage } return nil } func (x *Message) GetOneof() isMessage_Oneof { if x != nil { return x.Oneof } return nil } func (x *Message) GetOneofString() string { if x != nil { if x, ok := x.Oneof.(*Message_OneofString); ok { return x.OneofString } } return "" } func (x *Message) GetOneofEnum() Enum { if x != nil { if x, ok := x.Oneof.(*Message_OneofEnum); ok { return x.OneofEnum } } return Enum_ENUM_UNSPECIFIED } func (x *Message) GetOneofMessage1() *Message { if x != nil { if x, ok := x.Oneof.(*Message_OneofMessage1); ok { return x.OneofMessage1 } } return nil } func (x *Message) GetOneofMessage2() *Message { if x != nil { if x, ok := x.Oneof.(*Message_OneofMessage2); ok { return x.OneofMessage2 } } return nil } type isMessage_Oneof interface { isMessage_Oneof() } type Message_OneofString struct { OneofString string `protobuf:"bytes,37,opt,name=oneof_string,json=oneofString,proto3,oneof"` } type Message_OneofEnum struct { OneofEnum Enum `protobuf:"varint,38,opt,name=oneof_enum,json=oneofEnum,proto3,enum=einride.example.syntax.v1.Enum,oneof"` } type Message_OneofMessage1 struct { OneofMessage1 *Message `protobuf:"bytes,39,opt,name=oneof_message1,json=oneofMessage1,proto3,oneof"` } type Message_OneofMessage2 struct { OneofMessage2 *Message `protobuf:"bytes,40,opt,name=oneof_message2,json=oneofMessage2,proto3,oneof"` } func (*Message_OneofString) isMessage_Oneof() {} func (*Message_OneofEnum) isMessage_Oneof() {} func (*Message_OneofMessage1) isMessage_Oneof() {} func (*Message_OneofMessage2) isMessage_Oneof() {} var File_einride_example_syntax_v1_syntax_proto protoreflect.FileDescriptor const file_einride_example_syntax_v1_syntax_proto_rawDesc = "" + "\n" + "&einride/example/syntax/v1/syntax.proto\x12\x19einride.example.syntax.v1\"\xe4\x0e\n" + "\aMessage\x12\x16\n" + "\x06double\x18\x01 \x01(\x01R\x06double\x12\x14\n" + "\x05float\x18\x02 \x01(\x02R\x05float\x12\x14\n" + "\x05int32\x18\x03 \x01(\x05R\x05int32\x12\x14\n" + "\x05int64\x18\x04 \x01(\x03R\x05int64\x12\x16\n" + "\x06uint32\x18\x05 \x01(\rR\x06uint32\x12\x16\n" + "\x06uint64\x18\x06 \x01(\x04R\x06uint64\x12\x16\n" + "\x06sint32\x18\a \x01(\x11R\x06sint32\x12\x16\n" + "\x06sint64\x18\b \x01(\x12R\x06sint64\x12\x18\n" + "\afixed32\x18\t \x01(\aR\afixed32\x12\x18\n" + "\afixed64\x18\n" + " \x01(\x06R\afixed64\x12\x1a\n" + "\bsfixed32\x18\v \x01(\x0fR\bsfixed32\x12\x1a\n" + "\bsfixed64\x18\f \x01(\x10R\bsfixed64\x12\x12\n" + "\x04bool\x18\r \x01(\bR\x04bool\x12\x16\n" + "\x06string\x18\x0e \x01(\tR\x06string\x12\x14\n" + "\x05bytes\x18\x0f \x01(\fR\x05bytes\x123\n" + "\x04enum\x18\x10 \x01(\x0e2\x1f.einride.example.syntax.v1.EnumR\x04enum\x12<\n" + "\amessage\x18\x11 \x01(\v2\".einride.example.syntax.v1.MessageR\amessage\x12'\n" + "\x0frepeated_double\x18\x12 \x03(\x01R\x0erepeatedDouble\x12%\n" + "\x0erepeated_float\x18\x13 \x03(\x02R\rrepeatedFloat\x12%\n" + "\x0erepeated_int32\x18\x14 \x03(\x05R\rrepeatedInt32\x12%\n" + "\x0erepeated_int64\x18\x15 \x03(\x03R\rrepeatedInt64\x12'\n" + "\x0frepeated_uint32\x18\x16 \x03(\rR\x0erepeatedUint32\x12'\n" + "\x0frepeated_uint64\x18\x17 \x03(\x04R\x0erepeatedUint64\x12'\n" + "\x0frepeated_sint32\x18\x18 \x03(\x11R\x0erepeatedSint32\x12'\n" + "\x0frepeated_sint64\x18\x19 \x03(\x12R\x0erepeatedSint64\x12)\n" + "\x10repeated_fixed32\x18\x1a \x03(\aR\x0frepeatedFixed32\x12)\n" + "\x10repeated_fixed64\x18\x1b \x03(\x06R\x0frepeatedFixed64\x12+\n" + "\x11repeated_sfixed32\x18\x1c \x03(\x0fR\x10repeatedSfixed32\x12+\n" + "\x11repeated_sfixed64\x18\x1d \x03(\x10R\x10repeatedSfixed64\x12#\n" + "\rrepeated_bool\x18\x1e \x03(\bR\frepeatedBool\x12'\n" + "\x0frepeated_string\x18\x1f \x03(\tR\x0erepeatedString\x12%\n" + "\x0erepeated_bytes\x18 \x03(\fR\rrepeatedBytes\x12D\n" + "\rrepeated_enum\x18! \x03(\x0e2\x1f.einride.example.syntax.v1.EnumR\frepeatedEnum\x12M\n" + "\x10repeated_message\x18\" \x03(\v2\".einride.example.syntax.v1.MessageR\x0frepeatedMessage\x12c\n" + "\x11map_string_string\x18# \x03(\v27.einride.example.syntax.v1.Message.MapStringStringEntryR\x0fmapStringString\x12f\n" + "\x12map_string_message\x18$ \x03(\v28.einride.example.syntax.v1.Message.MapStringMessageEntryR\x10mapStringMessage\x12#\n" + "\foneof_string\x18% \x01(\tH\x00R\voneofString\x12@\n" + "\n" + "oneof_enum\x18& \x01(\x0e2\x1f.einride.example.syntax.v1.EnumH\x00R\toneofEnum\x12K\n" + "\x0eoneof_message1\x18' \x01(\v2\".einride.example.syntax.v1.MessageH\x00R\roneofMessage1\x12K\n" + "\x0eoneof_message2\x18( \x01(\v2\".einride.example.syntax.v1.MessageH\x00R\roneofMessage2\x1aB\n" + "\x14MapStringStringEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1ag\n" + "\x15MapStringMessageEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x128\n" + "\x05value\x18\x02 \x01(\v2\".einride.example.syntax.v1.MessageR\x05value:\x028\x01B\a\n" + "\x05oneof*8\n" + "\x04Enum\x12\x14\n" + "\x10ENUM_UNSPECIFIED\x10\x00\x12\f\n" + "\bENUM_ONE\x10\x01\x12\f\n" + "\bENUM_TWO\x10\x02B\xf5\x01\n" + "\x1dcom.einride.example.syntax.v1B\vSyntaxProtoP\x01Z@go.einride.tech/aip/proto/gen/einride/example/syntax/v1;syntaxv1\xa2\x02\x03EES\xaa\x02\x19Einride.Example.Syntax.V1\xca\x02\x19Einride\\Example\\Syntax\\V1\xe2\x02%Einride\\Example\\Syntax\\V1\\GPBMetadata\xea\x02\x1cEinride::Example::Syntax::V1b\x06proto3" var ( file_einride_example_syntax_v1_syntax_proto_rawDescOnce sync.Once file_einride_example_syntax_v1_syntax_proto_rawDescData []byte ) func file_einride_example_syntax_v1_syntax_proto_rawDescGZIP() []byte { file_einride_example_syntax_v1_syntax_proto_rawDescOnce.Do(func() { file_einride_example_syntax_v1_syntax_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_einride_example_syntax_v1_syntax_proto_rawDesc), len(file_einride_example_syntax_v1_syntax_proto_rawDesc))) }) return file_einride_example_syntax_v1_syntax_proto_rawDescData } var file_einride_example_syntax_v1_syntax_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_einride_example_syntax_v1_syntax_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_einride_example_syntax_v1_syntax_proto_goTypes = []any{ (Enum)(0), // 0: einride.example.syntax.v1.Enum (*Message)(nil), // 1: einride.example.syntax.v1.Message nil, // 2: einride.example.syntax.v1.Message.MapStringStringEntry nil, // 3: einride.example.syntax.v1.Message.MapStringMessageEntry } var file_einride_example_syntax_v1_syntax_proto_depIdxs = []int32{ 0, // 0: einride.example.syntax.v1.Message.enum:type_name -> einride.example.syntax.v1.Enum 1, // 1: einride.example.syntax.v1.Message.message:type_name -> einride.example.syntax.v1.Message 0, // 2: einride.example.syntax.v1.Message.repeated_enum:type_name -> einride.example.syntax.v1.Enum 1, // 3: einride.example.syntax.v1.Message.repeated_message:type_name -> einride.example.syntax.v1.Message 2, // 4: einride.example.syntax.v1.Message.map_string_string:type_name -> einride.example.syntax.v1.Message.MapStringStringEntry 3, // 5: einride.example.syntax.v1.Message.map_string_message:type_name -> einride.example.syntax.v1.Message.MapStringMessageEntry 0, // 6: einride.example.syntax.v1.Message.oneof_enum:type_name -> einride.example.syntax.v1.Enum 1, // 7: einride.example.syntax.v1.Message.oneof_message1:type_name -> einride.example.syntax.v1.Message 1, // 8: einride.example.syntax.v1.Message.oneof_message2:type_name -> einride.example.syntax.v1.Message 1, // 9: einride.example.syntax.v1.Message.MapStringMessageEntry.value:type_name -> einride.example.syntax.v1.Message 10, // [10:10] is the sub-list for method output_type 10, // [10:10] is the sub-list for method input_type 10, // [10:10] is the sub-list for extension type_name 10, // [10:10] is the sub-list for extension extendee 0, // [0:10] is the sub-list for field type_name } func init() { file_einride_example_syntax_v1_syntax_proto_init() } func file_einride_example_syntax_v1_syntax_proto_init() { if File_einride_example_syntax_v1_syntax_proto != nil { return } file_einride_example_syntax_v1_syntax_proto_msgTypes[0].OneofWrappers = []any{ (*Message_OneofString)(nil), (*Message_OneofEnum)(nil), (*Message_OneofMessage1)(nil), (*Message_OneofMessage2)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_einride_example_syntax_v1_syntax_proto_rawDesc), len(file_einride_example_syntax_v1_syntax_proto_rawDesc)), NumEnums: 1, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_einride_example_syntax_v1_syntax_proto_goTypes, DependencyIndexes: file_einride_example_syntax_v1_syntax_proto_depIdxs, EnumInfos: file_einride_example_syntax_v1_syntax_proto_enumTypes, MessageInfos: file_einride_example_syntax_v1_syntax_proto_msgTypes, }.Build() File_einride_example_syntax_v1_syntax_proto = out.File file_einride_example_syntax_v1_syntax_proto_goTypes = nil file_einride_example_syntax_v1_syntax_proto_depIdxs = nil } aip-go-0.80.0/reflect/000077500000000000000000000000001513342120500143735ustar00rootroot00000000000000aip-go-0.80.0/reflect/aipreflect/000077500000000000000000000000001513342120500165115ustar00rootroot00000000000000aip-go-0.80.0/reflect/aipreflect/doc.go000066400000000000000000000001541513342120500176050ustar00rootroot00000000000000// Package aipreflect provides primitives to manipulate AIP annotations and descriptors. package aipreflect aip-go-0.80.0/reflect/aipreflect/grammaticalname.go000066400000000000000000000020331513342120500221600ustar00rootroot00000000000000package aipreflect import ( "fmt" "unicode" "unicode/utf8" ) // GrammaticalName is the grammatical name for the singular or plural form of resource type. // Grammatical names must be URL-safe and use lowerCamelCase. type GrammaticalName string // e.g. "userEvents" // Validate checks that the grammatical name is non-empty, URL-safe, and uses lowerCamelCase. func (g GrammaticalName) Validate() error { if len(g) == 0 { return fmt.Errorf("validate grammatical name: must be non-empty") } for _, r := range g { if !unicode.In(r, unicode.Letter, unicode.Digit) { return fmt.Errorf("validate grammatical name '%s': contains forbidden character '%s'", g, string(r)) } } if r, _ := utf8.DecodeRuneInString(string(g)); !unicode.IsLower(r) { return fmt.Errorf("validate grammatical name '%s': must be lowerCamelCase", g) } return nil } // UpperCamelCase returns the UpperCamelCase version of the grammatical name, for use in e.g. method names. func (g GrammaticalName) UpperCamelCase() string { return initialUpperCase(string(g)) } aip-go-0.80.0/reflect/aipreflect/grammaticalname_test.go000066400000000000000000000012631513342120500232230ustar00rootroot00000000000000package aipreflect import ( "testing" "gotest.tools/v3/assert" ) func TestGrammaticalName_Validate(t *testing.T) { t.Parallel() for _, tt := range []struct { name string errorContains string }{ {name: "", errorContains: "must be non-empty"}, {name: "users"}, {name: "userEvents"}, {name: "UserEvents", errorContains: "must be lowerCamelCase"}, {name: "user events", errorContains: "contains forbidden character ' '"}, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() err := GrammaticalName(tt.name).Validate() if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) } else { assert.NilError(t, err) } }) } } aip-go-0.80.0/reflect/aipreflect/methodtype.go000066400000000000000000000051371513342120500212300ustar00rootroot00000000000000package aipreflect import "google.golang.org/protobuf/reflect/protoreflect" // MethodType is an AIP method type. type MethodType int //go:generate stringer -type MethodType -trimprefix MethodType const ( // MethodTypeNone represents no method type. MethodTypeNone MethodType = iota // MethodTypeGet is the method type of the AIP standard Get method. // See: https://google.aip.dev/131 (Standard methods: Get). MethodTypeGet // MethodTypeList is the method type of the AIP standard List method. // See: https://google.aip.dev/132 (Standard methods: List). MethodTypeList // MethodTypeCreate is the method type of the AIP standard Create method. // See: https://google.aip.dev/133 (Standard methods: Create). MethodTypeCreate // MethodTypeUpdate is the method type of the AIP standard Update method. // See: https://google.aip.dev/133 (Standard methods: Update). MethodTypeUpdate // MethodTypeDelete is the method type of the AIP standard Delete method. // See: https://google.aip.dev/135 (Standard methods: Delete). MethodTypeDelete // MethodTypeUndelete is the method type of the AIP Undelete method for soft delete. // See: https://google.aip.dev/164 (Soft delete). MethodTypeUndelete // MethodTypeBatchGet is the method type of the AIP standard BatchGet method. // See: https://google.aip.dev/231 (Batch methods: Get). MethodTypeBatchGet // MethodTypeBatchCreate is the method type of the AIP standard BatchCreate method. // See: https://google.aip.dev/233 (Batch methods: Create). MethodTypeBatchCreate // MethodTypeBatchUpdate is the method type of the AIP standard BatchUpdate method. // See: https://google.aip.dev/234 (Batch methods: Update). MethodTypeBatchUpdate // MethodTypeBatchDelete is the method type of the AIP standard BatchDelete method. // See: https://google.aip.dev/235 (Batch methods: Delete). MethodTypeBatchDelete // MethodTypeSearch is the method type of the custom AIP method for searching a resource collection. // See: https://google.aip.dev/136 (Custom methods). MethodTypeSearch ) // NamePrefix returns the method type's method name prefix. func (s MethodType) NamePrefix() protoreflect.Name { return protoreflect.Name(s.String()) } // IsPlural returns true if the method type relates to a plurality of resources. func (s MethodType) IsPlural() bool { switch s { case MethodTypeList, MethodTypeSearch, MethodTypeBatchGet, MethodTypeBatchCreate, MethodTypeBatchUpdate, MethodTypeBatchDelete: return true case MethodTypeNone, MethodTypeGet, MethodTypeCreate, MethodTypeUpdate, MethodTypeDelete, MethodTypeUndelete: return false } return false } aip-go-0.80.0/reflect/aipreflect/methodtype_string.go000066400000000000000000000020641513342120500226120ustar00rootroot00000000000000// Code generated by "stringer -type MethodType -trimprefix MethodType"; DO NOT EDIT. package aipreflect import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[MethodTypeNone-0] _ = x[MethodTypeGet-1] _ = x[MethodTypeList-2] _ = x[MethodTypeCreate-3] _ = x[MethodTypeUpdate-4] _ = x[MethodTypeDelete-5] _ = x[MethodTypeUndelete-6] _ = x[MethodTypeBatchGet-7] _ = x[MethodTypeBatchCreate-8] _ = x[MethodTypeBatchUpdate-9] _ = x[MethodTypeBatchDelete-10] _ = x[MethodTypeSearch-11] } const _MethodType_name = "NoneGetListCreateUpdateDeleteUndeleteBatchGetBatchCreateBatchUpdateBatchDeleteSearch" var _MethodType_index = [...]uint8{0, 4, 7, 11, 17, 23, 29, 37, 45, 56, 67, 78, 84} func (i MethodType) String() string { if i < 0 || i >= MethodType(len(_MethodType_index)-1) { return "MethodType(" + strconv.FormatInt(int64(i), 10) + ")" } return _MethodType_name[_MethodType_index[i]:_MethodType_index[i+1]] } aip-go-0.80.0/reflect/aipreflect/methodtype_test.go000066400000000000000000000016141513342120500222630ustar00rootroot00000000000000package aipreflect import ( "testing" "gotest.tools/v3/assert" ) func TestMethodType_IsPlural(t *testing.T) { t.Parallel() for _, tt := range []struct { methodType MethodType expected bool }{ {methodType: MethodTypeGet, expected: false}, {methodType: MethodTypeCreate, expected: false}, {methodType: MethodTypeDelete, expected: false}, {methodType: MethodTypeGet, expected: false}, {methodType: MethodTypeUndelete, expected: false}, {methodType: MethodTypeUpdate, expected: false}, {methodType: MethodTypeList, expected: true}, {methodType: MethodTypeSearch, expected: true}, {methodType: MethodTypeBatchGet, expected: true}, {methodType: MethodTypeBatchCreate, expected: true}, {methodType: MethodTypeBatchUpdate, expected: true}, {methodType: MethodTypeBatchDelete, expected: true}, } { assert.Assert(t, tt.methodType.IsPlural() == tt.expected, tt.methodType) } } aip-go-0.80.0/reflect/aipreflect/resources.go000066400000000000000000000053661513342120500210640ustar00rootroot00000000000000package aipreflect import ( "go.einride.tech/aip/resourcename" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" ) // RangeResourceDescriptorsInFile iterates over all resource descriptors in a file while fn returns true. // The iteration order is undefined. func RangeResourceDescriptorsInFile( file protoreflect.FileDescriptor, fn func(resource *annotations.ResourceDescriptor) bool, ) { for _, resource := range proto.GetExtension( file.Options(), annotations.E_ResourceDefinition, ).([]*annotations.ResourceDescriptor) { if !fn(resource) { return } } for i := 0; i < file.Messages().Len(); i++ { resource := proto.GetExtension( file.Messages().Get(i).Options(), annotations.E_Resource, ).(*annotations.ResourceDescriptor) if resource == nil { continue } if !fn(resource) { return } } } // RangeResourceDescriptorsInPackage iterates over all resource descriptors in a package while fn returns true. // The provided registry is used for looking up files in the package. // The iteration order is undefined. func RangeResourceDescriptorsInPackage( registry *protoregistry.Files, packageName protoreflect.FullName, fn func(resource *annotations.ResourceDescriptor) bool, ) { registry.RangeFilesByPackage(packageName, func(file protoreflect.FileDescriptor) bool { for _, resource := range proto.GetExtension( file.Options(), annotations.E_ResourceDefinition, ).([]*annotations.ResourceDescriptor) { if !fn(resource) { return false } } for i := 0; i < file.Messages().Len(); i++ { resource := proto.GetExtension( file.Messages().Get(i).Options(), annotations.E_Resource, ).(*annotations.ResourceDescriptor) if resource == nil { continue } if !fn(resource) { return false } } return true }) } // RangeParentResourcesInPackage iterates over all a resource's parent descriptors in a package while fn returns true. // The provided registry is used for looking up files in the package. // The iteration order is undefined. func RangeParentResourcesInPackage( registry *protoregistry.Files, packageName protoreflect.FullName, pattern string, fn func(parent *annotations.ResourceDescriptor) bool, ) { resourcename.RangeParents(pattern, func(parent string) bool { if parent == pattern { return true } var stop bool RangeResourceDescriptorsInPackage(registry, packageName, func(resource *annotations.ResourceDescriptor) bool { for _, candidatePattern := range resource.GetPattern() { if candidatePattern == parent { if !fn(resource) { stop = true return false } break } } return true }) return !stop }) } aip-go-0.80.0/reflect/aipreflect/resourcetype.go000066400000000000000000000032751513342120500216000ustar00rootroot00000000000000package aipreflect import ( "fmt" "strings" "unicode" "unicode/utf8" ) // ResourceType represents a resource type name. type ResourceType string // e.g. pubsub.googleapis.com/Topic. // Validate checks that the resource type name is syntactically valid. func (n ResourceType) Validate() error { if strings.Count(string(n), "/") != 1 { return fmt.Errorf("validate resource type name '%s': invalid format", n) } if err := validateServiceName(n.ServiceName()); err != nil { return fmt.Errorf("validate resource type name '%s': %w", n, err) } if err := validateType(n.Type()); err != nil { return fmt.Errorf("validate resource type name '%s': %w", n, err) } return nil } // ServiceName returns the service name of the resource type name. func (n ResourceType) ServiceName() string { if i := strings.LastIndexByte(string(n), '/'); i >= 0 { return string(n[:i]) } return "" } // Type returns the type of the resource type name. func (n ResourceType) Type() string { if i := strings.LastIndexByte(string(n), '/'); i >= 0 { return string(n[i+1:]) } return "" } func validateServiceName(serviceName string) error { if serviceName == "" { return fmt.Errorf("service name: empty") } if !strings.ContainsRune(serviceName, '.') { return fmt.Errorf("service name: must be a valid domain name") } return nil } func validateType(t string) error { if t == "" { return fmt.Errorf("type: is empty") } if firstRune, _ := utf8.DecodeRuneInString(t); !unicode.IsUpper(firstRune) { return fmt.Errorf("type: must start with an upper-case letter") } for _, r := range t { if !unicode.In(r, unicode.Letter, unicode.Digit) { return fmt.Errorf("type: must be UpperCamelCase") } } return nil } aip-go-0.80.0/reflect/aipreflect/resourcetype_test.go000066400000000000000000000015311513342120500226300ustar00rootroot00000000000000package aipreflect import ( "testing" "gotest.tools/v3/assert" ) func TestResourceTypeName_Validate(t *testing.T) { t.Parallel() for _, tt := range []struct { name string errorContains string }{ { name: "pubsub.googleapis.com/Topic", }, { name: "pubsub/Topic", errorContains: "service name: must be a valid domain name", }, { name: "pubsub.googleapis.com/topic", errorContains: "type: must start with an upper-case letter", }, { name: "pubsub.googleapis.com/Topic_2", errorContains: "type: must be UpperCamelCase", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() if tt.errorContains != "" { assert.ErrorContains(t, ResourceType(tt.name).Validate(), tt.errorContains) } else { assert.NilError(t, ResourceType(tt.name).Validate()) } }) } } aip-go-0.80.0/reflect/aipreflect/strcase.go000066400000000000000000000003251513342120500205040ustar00rootroot00000000000000package aipreflect import ( "unicode" "unicode/utf8" ) func initialUpperCase(s string) string { if len(s) == 0 { return s } r, n := utf8.DecodeRuneInString(s) return string(unicode.ToUpper(r)) + s[n:] } aip-go-0.80.0/reflect/aipreflect/strcase_test.go000066400000000000000000000006231513342120500215440ustar00rootroot00000000000000package aipreflect import ( "testing" "gotest.tools/v3/assert" ) func Test_initialUpperCase(t *testing.T) { t.Parallel() for _, tt := range []struct { s string expected string }{ {s: "", expected: ""}, {s: "a", expected: "A"}, {s: "aaa", expected: "Aaa"}, } { t.Run(tt.s, func(t *testing.T) { t.Parallel() assert.Equal(t, tt.expected, initialUpperCase(tt.s)) }) } } aip-go-0.80.0/reflect/aipreflect/validateresourcereferences.go000066400000000000000000000042501513342120500244440ustar00rootroot00000000000000package aipreflect import ( "fmt" "strings" "go.einride.tech/aip/resourcename" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protopath" "google.golang.org/protobuf/reflect/protorange" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" ) // ValidateResourceReferences validates the resource reference fields in a message. func ValidateResourceReferences(message proto.Message) error { return protorange.Range(message.ProtoReflect(), func(values protopath.Values) error { curr := values.Index(-1) var field protoreflect.FieldDescriptor var fieldValue string switch curr.Step.Kind() { case protopath.FieldAccessStep: field = curr.Step.FieldDescriptor() if field.Kind() != protoreflect.StringKind { return nil } if field.Cardinality() == protoreflect.Repeated { return nil } fieldValue = curr.Value.String() case protopath.ListIndexStep: prev := values.Index(-2) field = prev.Step.FieldDescriptor() if field.Kind() != protoreflect.StringKind { return nil } fieldValue = curr.Value.String() default: return nil } resourceReferenceAnnotation := proto.GetExtension( field.Options(), annotations.E_ResourceReference, ).(*annotations.ResourceReference) if resourceReferenceAnnotation == nil { return nil } var errValidate error RangeResourceDescriptorsInPackage( protoregistry.GlobalFiles, field.ParentFile().Package(), func(resource *annotations.ResourceDescriptor) bool { if resource.GetType() != resourceReferenceAnnotation.GetType() { return true } for _, pattern := range resource.GetPattern() { if resourcename.Match(pattern, fieldValue) { return false } } errValidate = fmt.Errorf( "value '%s' of field %s is not a valid resource reference for %s", fieldValue, // trim the message type from the path strings.TrimLeft(strings.TrimLeftFunc(values.Path.String(), func(r rune) bool { return r != ')' }), ")."), resourceReferenceAnnotation.GetType(), ) return false }, ) return errValidate }) } aip-go-0.80.0/reflect/aipreflect/validateresourcereferences_test.go000066400000000000000000000040601513342120500255020ustar00rootroot00000000000000package aipreflect import ( "testing" examplefreightv1 "go.einride.tech/aip/proto/gen/einride/example/freight/v1" "google.golang.org/protobuf/proto" "gotest.tools/v3/assert" ) func TestValidateResourceReferences(t *testing.T) { t.Parallel() for _, tt := range []struct { name string message proto.Message errorContains string }{ { name: "empty", message: &examplefreightv1.Shipment{}, }, { name: "valid", message: &examplefreightv1.Shipment{ OriginSite: "shippers/1/sites/1", DestinationSite: "shippers/1/sites/2", }, }, { name: "valid repeated empty", message: &examplefreightv1.BatchGetSitesRequest{}, }, { name: "valid repeated", message: &examplefreightv1.BatchGetSitesRequest{ Names: []string{ "shippers/1/sites/1", "shippers/1/sites/2", }, }, }, { name: "invalid", message: &examplefreightv1.Shipment{ OriginSite: "shippers/1", DestinationSite: "shippers/1/sites/2", }, errorContains: "value 'shippers/1' of field origin_site is not a valid resource reference" + " for freight-example.einride.tech/Site", }, { name: "invalid nested", message: &examplefreightv1.CreateShipmentRequest{ Shipment: &examplefreightv1.Shipment{ OriginSite: "shippers/1", DestinationSite: "shippers/1/sites/2", }, }, errorContains: "value 'shippers/1' of field shipment.origin_site is not a valid resource reference" + " for freight-example.einride.tech/Site", }, { name: "invalid repeated", message: &examplefreightv1.BatchGetSitesRequest{ Names: []string{ "shippers/1/sites/1", "shippers/1", }, }, errorContains: "value 'shippers/1' of field names[1] is not a valid resource reference" + " for freight-example.einride.tech/Site", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() err := ValidateResourceReferences(tt.message) if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) } else { assert.NilError(t, err) } }) } } aip-go-0.80.0/resourceid/000077500000000000000000000000001513342120500151135ustar00rootroot00000000000000aip-go-0.80.0/resourceid/systemgenerated.go000066400000000000000000000010371513342120500206460ustar00rootroot00000000000000package resourceid import ( "encoding/base32" "github.com/google/uuid" ) //nolint:gochecknoglobals var base32Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding) // NewSystemGenerated returns a new system-generated resource ID. func NewSystemGenerated() string { return uuid.New().String() } // NewSystemGenerated returns a new system-generated resource ID encoded as base32 lowercase. func NewSystemGeneratedBase32() string { id := uuid.New() return base32Encoding.EncodeToString(id[:]) } aip-go-0.80.0/resourceid/systemgenerated_test.go000066400000000000000000000010441513342120500217030ustar00rootroot00000000000000package resourceid import ( "regexp" "testing" "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" ) func TestNewSystemGenerated(t *testing.T) { t.Parallel() const uuidV4Regexp = `^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$` assert.Assert(t, cmp.Regexp(regexp.MustCompile(uuidV4Regexp), NewSystemGenerated())) } func TestNewSystemGeneratedBase32(t *testing.T) { t.Parallel() const base32Regexp = `^[a-z2-7]{26}$` assert.Assert(t, cmp.Regexp(regexp.MustCompile(base32Regexp), NewSystemGeneratedBase32())) } aip-go-0.80.0/resourceid/usersettable.go000066400000000000000000000027401513342120500201470ustar00rootroot00000000000000package resourceid import ( "fmt" "unicode" ) // ValidateUserSettable validates a user-settable resource ID. // // From https://google.aip.dev/122#resource-id-segments: // // User-settable resource IDs should conform to RFC-1034; which restricts to letters, numbers, and hyphen, // with the first character a letter, the last a letter or a number, and a 63 character maximum. // Additionally, user-settable resource IDs should restrict letters to lower-case. // // User-settable IDs should not be permitted to be a UUID (or any value that syntactically appears to be a UUID). // // See also: https://google.aip.dev/133#user-specified-ids func ValidateUserSettable(id string) error { if len(id) < 1 || 63 < len(id) { return fmt.Errorf("user-settable ID must be between 1 and 63 characters") } if !unicode.IsLetter(rune(id[0])) { return fmt.Errorf("user-settable ID must begin with a letter") } if id[len(id)-1] == '-' { return fmt.Errorf("user-settable ID must end with a letter or number") } for position, character := range id { switch character { case // numbers '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // hyphen '-', // lower-case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z': default: return fmt.Errorf( "user-settable ID must only contain lowercase, numbers and hyphens (got: '%c' in position %d)", character, position, ) } } return nil } aip-go-0.80.0/resourceid/usersettable_test.go000066400000000000000000000017151513342120500212070ustar00rootroot00000000000000package resourceid import ( "strings" "testing" "gotest.tools/v3/assert" ) func TestValidateUserSettable(t *testing.T) { t.Parallel() for _, tt := range []struct { id string errorContains string }{ {id: "abcd"}, {id: "abcd-efgh-1234"}, {id: "", errorContains: "must be between 1 and 63 characters"}, {id: strings.Repeat("a", 64), errorContains: "must be between 1 and 63 characters"}, {id: "-abc", errorContains: "must begin with a letter"}, {id: "abc-", errorContains: "must end with a letter or number"}, {id: "123-abc", errorContains: "must begin with a letter"}, {id: "daf1cb3e-f33b-43f1-81cc-e65fda51efa5"}, {id: "abcd/efgh", errorContains: "must only contain lowercase, numbers and hyphens"}, } { t.Run(tt.id, func(t *testing.T) { t.Parallel() err := ValidateUserSettable(tt.id) if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) } else { assert.NilError(t, err) } }) } } aip-go-0.80.0/resourcename/000077500000000000000000000000001513342120500154375ustar00rootroot00000000000000aip-go-0.80.0/resourcename/ancestor.go000066400000000000000000000011631513342120500176050ustar00rootroot00000000000000package resourcename // Ancestor extracts an ancestor from the provided name, using a pattern for the ancestor. func Ancestor(name, pattern string) (string, bool) { if name == "" || pattern == "" { return "", false } var scName, scPattern Scanner scName.Init(name) scPattern.Init(pattern) for scPattern.Scan() { if !scName.Scan() { return "", false } segment := scPattern.Segment() if segment.IsWildcard() { return "", false // wildcards not supported in patterns } if !segment.IsVariable() && segment != scName.Segment() { return "", false // not a match } } return name[:scName.end], true } aip-go-0.80.0/resourcename/ancestor_test.go000066400000000000000000000022071513342120500206440ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestAncestor(t *testing.T) { t.Parallel() for _, tt := range []struct { name string input string pattern string expected string expectedOK bool }{ { name: "empty all", input: "", pattern: "", expected: "", }, { name: "empty pattern", input: "foo/1/bar/2", pattern: "", expected: "", }, { name: "empty name", input: "", pattern: "foo/{foo}", expected: "", }, { name: "non-matching pattern", input: "foo/1/bar/2", pattern: "baz/{baz}", expected: "", }, { name: "ok", input: "foo/1/bar/2", pattern: "foo/{foo}", expected: "foo/1", expectedOK: true, }, { name: "ok full", input: "//foo.example.com/foo/1/bar/2", pattern: "foo/{foo}", expected: "//foo.example.com/foo/1", expectedOK: true, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() actual, ok := Ancestor(tt.input, tt.pattern) assert.Equal(t, tt.expectedOK, ok) assert.Equal(t, tt.expected, actual) }) } } aip-go-0.80.0/resourcename/containswildcard.go000066400000000000000000000004271513342120500213210ustar00rootroot00000000000000package resourcename // ContainsWildcard reports whether the specified resource name contains any wildcard segments. func ContainsWildcard(name string) bool { var sc Scanner sc.Init(name) for sc.Scan() { if sc.Segment().IsWildcard() { return true } } return false } aip-go-0.80.0/resourcename/containswildcard_test.go000066400000000000000000000016321513342120500223570ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestContainsWildcard(t *testing.T) { t.Parallel() for _, tt := range []struct { name string input string expected bool }{ { name: "empty", input: "", expected: false, }, { name: "singleton", input: "foo", expected: false, }, { name: "singleton wildcard", input: "-", expected: true, }, { name: "multi", input: "foo/bar", expected: false, }, { name: "multi wildcard at start", input: "-/bar", expected: true, }, { name: "multi wildcard at end", input: "foo/-", expected: true, }, { name: "multi wildcard at middle", input: "foo/-/bar", expected: true, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() assert.Equal(t, tt.expected, ContainsWildcard(tt.input)) }) } } aip-go-0.80.0/resourcename/doc.go000066400000000000000000000001711513342120500165320ustar00rootroot00000000000000// Package resourcename implements simple functions to manipulate UTF-8 encoded AIP resource names. package resourcename aip-go-0.80.0/resourcename/hasparent.go000066400000000000000000000021111513342120500177460ustar00rootroot00000000000000package resourcename // HasParent tests whether name has the specified parent. Wildcard segments (-) are considered. // Resource names without revisions are considered parents of the same resource name with a revision. func HasParent(name, parent string) bool { if name == "" || parent == "" || name == parent { return false } var parentScanner, nameScanner Scanner parentScanner.Init(parent) nameScanner.Init(name) for parentScanner.Scan() { if !nameScanner.Scan() { return false } if parentScanner.Segment().IsWildcard() { continue } // Special-case: Identical resource IDs without revision are parents of revisioned resource IDs. if nameScanner.Segment().Literal().HasRevision() && !parentScanner.Segment().Literal().HasRevision() && (nameScanner.Segment().Literal().ResourceID() == parentScanner.Segment().Literal().ResourceID()) { continue } if parentScanner.Segment() != nameScanner.Segment() { return false } } if parentScanner.Full() && nameScanner.Full() { return parentScanner.ServiceName() == nameScanner.ServiceName() } return true } aip-go-0.80.0/resourcename/hasparent_test.go000066400000000000000000000056501513342120500210200ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestHasParent(t *testing.T) { t.Parallel() for _, tt := range []struct { test string name string parent string expected bool }{ { test: "valid parent and child", name: "shippers/1/sites/1", parent: "shippers/1", expected: true, }, { test: "not parent of self", name: "shippers/1/sites/1/settings", parent: "shippers/1/sites/1/settings", expected: false, }, { test: "empty parent", name: "shippers/1/sites/1", parent: "", expected: false, }, { test: "empty child and empty parent", name: "", parent: "", expected: false, }, { test: "singleton", name: "shippers/1/settings", parent: "shippers/1", expected: true, }, { test: "singleton child", name: "shippers/1/sites/1/settings", parent: "shippers/1/sites/1", expected: true, }, { test: "wildcard parent", name: "shippers/1/sites/1", parent: "shippers/-", expected: true, }, { test: "full child", name: "//freight-example.einride.tech/shippers/1/sites/1", parent: "shippers/-", expected: true, }, { test: "full parent", name: "shippers/1/sites/1", parent: "//freight-example.einride.tech/shippers/-", expected: true, }, { test: "full parent", name: "shippers/1/sites/1", parent: "//freight-example.einride.tech/shippers/-", expected: true, }, { test: "full parent and child with different service names", name: "//other-example.einride.tech/shippers/1/sites/1", parent: "//freight-example.einride.tech/shippers/-", expected: false, }, { test: "revisioned child", name: "shippers/1/sites/1@beef", parent: "shippers/1/sites/1", expected: true, }, { test: "revisioned child with other revision", name: "shippers/1/sites/1@beef", parent: "shippers/1/sites/1@dead", expected: false, }, { test: "identical revisioned child", name: "shippers/1/sites/1@beef", parent: "shippers/1/sites/1@beef", expected: false, }, { test: "revisioned parent", parent: "datasets/1@beef", name: "datasets/1@beef/tables/1", expected: true, }, { test: "revisioned parent with non-revisoned child", parent: "datasets/1@beef", name: "datasets/1/tables/1", expected: false, }, { test: "revisioned parent with non-matching revision child", parent: "datasets/1@beef", name: "datasets/1@dead/tables/1", expected: false, }, { test: "non-revisioned parent with revisioned child", parent: "datasets/1", name: "datasets/1@beef/tables/1", expected: true, }, } { t.Run(tt.test, func(t *testing.T) { t.Parallel() assert.Assert(t, HasParent(tt.name, tt.parent) == tt.expected) }) } } aip-go-0.80.0/resourcename/isdomainname.go000066400000000000000000000030321513342120500204300ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package resourcename // isDomainName is copied from go/src/net/dnsclient.go pending resolution of the following issues: // - https://github.com/golang/go/issues/31671 // - https://github.com/golang/go/issues/17659 func isDomainName(s string) bool { // See RFC 1035, RFC 3696. // Presentation format has dots before every label except the first, and the // terminal empty label is optional here because we assume fully-qualified // (absolute) input. We must therefore reserve space for the first and last // labels' length octets in wire format, where they are necessary and the // maximum total length is 255. // So our _effective_ maximum is 253, but 254 is not rejected if the last // character is a dot. l := len(s) if l == 0 || l > 254 || l == 254 && s[l-1] != '.' { return false } last := byte('.') partlen := 0 for i := 0; i < len(s); i++ { c := s[i] switch { default: return false case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || '0' <= c && c <= '9': partlen++ case c == '-': // Byte before dash cannot be dot. if last == '.' { return false } partlen++ case c == '.': // Byte before dot cannot be dot, dash. if last == '.' || last == '-' { return false } if partlen > 63 || partlen == 0 { return false } partlen = 0 } last = c } if last == '-' || partlen > 63 { return false } return true } aip-go-0.80.0/resourcename/join.go000066400000000000000000000011541513342120500167260ustar00rootroot00000000000000package resourcename import ( "fmt" "strings" ) // Join combines resource names, separating them by slashes. func Join(elems ...string) string { segments := make([]string, 0, len(elems)) for elemIndex, elem := range elems { var sc Scanner sc.Init(elem) for sc.Scan() { if elemIndex == 0 && len(segments) == 0 && sc.Full() { segments = append(segments, fmt.Sprintf("//%s", sc.ServiceName())) } segment := sc.Segment() if segment == "" { continue } segments = append(segments, string(segment)) } } if len(segments) == 0 { return "/" } return strings.Join(segments, "/") } aip-go-0.80.0/resourcename/join_test.go000066400000000000000000000054201513342120500177650ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestJoin(t *testing.T) { t.Parallel() for _, tt := range []struct { name string input []string expected string }{ { name: "zero", input: []string{}, expected: "/", }, { name: "one", input: []string{"parent/1"}, expected: "parent/1", }, { name: "two", input: []string{ "parent/1", "child/2", }, expected: "parent/1/child/2", }, { name: "root first", input: []string{ "/", "child/2", }, expected: "child/2", }, { name: "root last", input: []string{ "parent/1", "/", }, expected: "parent/1", }, { name: "root second", input: []string{ "parent/1", "/", "child/2", }, expected: "parent/1/child/2", }, { name: "root first and last", input: []string{ "/", "child/1", "/", }, expected: "child/1", }, { name: "only roots", input: []string{ "/", "/", }, expected: "/", }, { name: "empty first", input: []string{ "", "child/2", }, expected: "child/2", }, { name: "empty second", input: []string{ "parent/1", "", "child/2", }, expected: "parent/1/child/2", }, { name: "invalid first suffix", input: []string{ "parent/1/", "child/2", }, expected: "parent/1/child/2", }, { name: "invalid last suffix", input: []string{ "parent/1", "child/2/", }, expected: "parent/1/child/2", }, { name: "fully qualified first", input: []string{ "//foo.example.com/foo/1", "bar/2", }, expected: "//foo.example.com/foo/1/bar/2", }, { name: "fully qualified second", input: []string{ "foo/1", "//foo.example.com/bar/2", }, expected: "foo/1/bar/2", }, { name: "fully qualified both", input: []string{ "//foo.example.com/foo/1", "//bar.example.com/bar/2", }, expected: "//foo.example.com/foo/1/bar/2", }, // TODO: Should these be disallowed? // See https://github.com/einride/aip-go/pull/258 { name: "first slash prefix", input: []string{ "/parent/1", "child/2", }, expected: "parent/1/child/2", }, { name: "second slash prefix", input: []string{ "parent/1", "/child/2", }, expected: "parent/1/child/2", }, { name: "thirds slash prefix", input: []string{ "parent/1", "child/2", "/extra/3", }, expected: "parent/1/child/2/extra/3", }, { name: "all slash prefix", input: []string{ "/parent/1", "/child/2", "/extra/3", }, expected: "parent/1/child/2/extra/3", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() actual := Join(tt.input...) assert.Equal(t, actual, tt.expected) }) } } aip-go-0.80.0/resourcename/matches.go000066400000000000000000000017751513342120500174240ustar00rootroot00000000000000package resourcename // Match reports whether the specified resource name matches the specified resource name pattern. func Match(pattern, name string) bool { var nameScanner, patternScanner Scanner nameScanner.Init(name) patternScanner.Init(pattern) for patternScanner.Scan() { if !nameScanner.Scan() { return false } nameSegment := nameScanner.Segment() if nameSegment.IsVariable() { return false } patternSegment := patternScanner.Segment() if patternSegment.IsWildcard() { return false // edge case - wildcard not allowed in patterns } if patternSegment.IsVariable() { if nameSegment == "" { return false } } else if nameSegment != patternSegment { return false } } switch { case nameScanner.Scan(), // name has more segments than pattern, no match patternScanner.Segment() == "", // edge case - empty pattern never matches patternScanner.Full(): // edge case - full resource name not allowed in patterns return false } return true } aip-go-0.80.0/resourcename/matches_test.go000066400000000000000000000035061513342120500204550ustar00rootroot00000000000000package resourcename import ( "fmt" "testing" "gotest.tools/v3/assert" ) func TestMatches(t *testing.T) { t.Parallel() for _, tt := range []struct { test string name string pattern string expected bool }{ { test: "valid pattern", name: "shippers/1/sites/1", pattern: "shippers/{shipper}/sites/{site}", expected: true, }, { test: "name longer than pattern", name: "shippers/1/sites/1/settings", pattern: "shippers/{shipper}/sites/{site}", expected: false, }, { test: "empty pattern", pattern: "", name: "shippers/1/sites/1", expected: false, }, { test: "empty pattern and empty name", pattern: "", name: "", expected: false, }, { test: "singleton", name: "shippers/1/sites/1/settings", pattern: "shippers/{shipper}/sites/{site}/settings", expected: true, }, { test: "wildcard pattern", name: "shippers/1/sites/1", pattern: "shippers/-/sites/-", expected: false, }, { test: "full parent", name: "//freight-example.einride.tech/shippers/1/sites/1", pattern: "shippers/{shipper}/sites/{site}", expected: true, }, { test: "full pattern", name: "shippers/1", pattern: "//freight-example.einride.tech/shippers/{shipper}", expected: false, }, { test: "slash prefix in the name", name: "/shippers/1", pattern: "shippers/{shipper}", expected: true, }, { test: "slash prefix in the pattern", name: "shippers/1", pattern: "/shippers/{shipper}", expected: true, }, } { t.Run(tt.test, func(t *testing.T) { t.Parallel() assert.Check( t, Match(tt.pattern, tt.name) == tt.expected, fmt.Sprintf("expected Match(%q, %q)=%t", tt.pattern, tt.name, tt.expected), ) }) } } aip-go-0.80.0/resourcename/rangeparents.go000066400000000000000000000013041513342120500204550ustar00rootroot00000000000000package resourcename // RangeParents iterates over all parents of the provided resource name. // The iteration order is from root ancestor down to the closest parent. // Collection segments are included in the iteration, to not require knowing the pattern. // For full resource names, the service is omitted. func RangeParents(name string, fn func(parent string) bool) { var sc Scanner sc.Init(name) // First segment: special-case to handle full resource names. if !sc.Scan() { return } start := sc.Start() if sc.End() != len(name) && !fn(name[start:sc.End()]) { return } // Scan remaining segments. for sc.Scan() { if sc.End() != len(name) && !fn(name[start:sc.End()]) { return } } } aip-go-0.80.0/resourcename/rangeparents_test.go000066400000000000000000000016421513342120500215210ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestRangeParents(t *testing.T) { t.Parallel() for _, tt := range []struct { name string input string expected []string }{ { name: "empty", input: "", }, { name: "singleton", input: "foo", }, { name: "single", input: "foo/bar", expected: []string{ "foo", }, }, { name: "multiple", input: "foo/bar/baz/123", expected: []string{ "foo", "foo/bar", "foo/bar/baz", }, }, { name: "full", input: "//test.example.com/foo/bar/baz/123", expected: []string{ "foo", "foo/bar", "foo/bar/baz", }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() var actual []string RangeParents(tt.input, func(parent string) bool { actual = append(actual, parent) return true }) assert.DeepEqual(t, tt.expected, actual) }) } } aip-go-0.80.0/resourcename/scanner.go000066400000000000000000000035561513342120500174300ustar00rootroot00000000000000package resourcename import ( "strings" ) // Scanner scans a resource name. type Scanner struct { name string start, end int serviceStart, serviceEnd int full bool } // Init initializes the scanner. func (s *Scanner) Init(name string) { s.name = name s.start, s.end = 0, 0 s.full = false } // Scan to the next segment. func (s *Scanner) Scan() bool { switch s.end { case len(s.name): return false case 0: // Special case for full resource names. if strings.HasPrefix(s.name, "//") { s.full = true s.start, s.end = 2, 2 nextSlash := strings.IndexByte(s.name[s.start:], '/') if nextSlash == -1 { s.serviceStart, s.serviceEnd = s.start, len(s.name) s.start, s.end = len(s.name), len(s.name) return false } s.serviceStart, s.serviceEnd = s.start, s.start+nextSlash s.start, s.end = s.start+nextSlash+1, s.start+nextSlash+1 } else if strings.HasPrefix(s.name, "/") { s.start = s.end + 1 // start past beginning slash } default: s.start = s.end + 1 // start past latest slash } if nextSlash := strings.IndexByte(s.name[s.start:], '/'); nextSlash == -1 { s.end = len(s.name) } else { s.end = s.start + nextSlash } return true } // Start returns the start index (inclusive) of the current segment. func (s *Scanner) Start() int { return s.start } // End returns the end index (exclusive) of the current segment. func (s *Scanner) End() int { return s.end } // Segment returns the current segment. func (s *Scanner) Segment() Segment { return Segment(s.name[s.start:s.end]) } // Full returns true if the scanner has detected a full resource name. func (s *Scanner) Full() bool { return s.full } // ServiceName returns the service name, when the scanner has detected a full resource name. func (s *Scanner) ServiceName() string { return s.name[s.serviceStart:s.serviceEnd] } aip-go-0.80.0/resourcename/scanner_test.go000066400000000000000000000044561513342120500204670ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestSegmentScanner(t *testing.T) { t.Parallel() for _, tt := range []struct { name string input string full bool serviceName string segments []Segment }{ { name: "empty", input: "", segments: []Segment{}, }, { name: "singleton", input: "singleton", segments: []Segment{"singleton"}, }, { name: "two segments", input: "shippers/1", segments: []Segment{"shippers", "1"}, }, { name: "three segments", input: "shippers/1/settings", segments: []Segment{"shippers", "1", "settings"}, }, { name: "wildcard segment", input: "shippers/1/shipments/-", segments: []Segment{"shippers", "1", "shipments", "-"}, }, { name: "empty middle segment", input: "shippers//shipments", segments: []Segment{"shippers", "", "shipments"}, }, { name: "empty end segment", input: "shippers/", segments: []Segment{"shippers", ""}, }, { name: "full", input: "//library.googleapis.com/publishers/123/books/les-miserables", full: true, serviceName: "library.googleapis.com", segments: []Segment{"publishers", "123", "books", "les-miserables"}, }, { name: "full without segments", input: "//library.googleapis.com", full: true, serviceName: "library.googleapis.com", segments: []Segment{}, }, { name: "full without service name", input: "//", full: true, serviceName: "", segments: []Segment{}, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() actualSegments := make([]Segment, 0, len(tt.segments)) var sc Scanner sc.Init(tt.input) for sc.Scan() { actualSegments = append(actualSegments, sc.Segment()) } assert.Equal(t, tt.full, sc.Full()) assert.Equal(t, tt.serviceName, sc.ServiceName()) assert.DeepEqual(t, tt.segments, actualSegments) }) } } //nolint:gochecknoglobals var stringSink string func BenchmarkScanner(b *testing.B) { const name = "//library.googleapis.com/publishers/123/books/les-miserables" for i := 0; i < b.N; i++ { var sc Scanner sc.Init(name) for sc.Scan() { stringSink = sc.Segment().Literal().ResourceID() } } } aip-go-0.80.0/resourcename/segment.go000066400000000000000000000036611513342120500174360ustar00rootroot00000000000000package resourcename import "strings" // RevisionSeparator is the separator character used to separate resource IDs from revision IDs. const RevisionSeparator = '@' // Segment is a segment of a resource name or a resource name pattern. // // EBNF // // Segment = Literal | Variable ; // Variable = "{" Literal "}" ; type Segment string // IsVariable reports whether the segment is a variable segment. func (s Segment) IsVariable() bool { return len(s) > 2 && s[0] == '{' && s[len(s)-1] == '}' } // Literal returns the literal value of the segment. // For variables, the literal value is the name of the variable. func (s Segment) Literal() Literal { switch { case s.IsVariable(): return Literal(s[1 : len(s)-1]) default: return Literal(s) } } // IsWildcard reports whether the segment is a wildcard. func (s Segment) IsWildcard() bool { return s == Wildcard } // Literal is the literal part of a resource name segment. // // EBNF // // Literal = RESOURCE_ID | RevisionLiteral ; // RevisionLiteral = RESOURCE_ID "@" REVISION_ID ; type Literal string // ResourceID returns the literal's resource ID. func (l Literal) ResourceID() string { if !l.HasRevision() { return string(l) } return string(l[:strings.IndexByte(string(l), RevisionSeparator)]) } // RevisionID returns the literal's revision ID. func (l Literal) RevisionID() string { if !l.HasRevision() { return "" } return string(l[strings.IndexByte(string(l), RevisionSeparator)+1:]) } // HasRevision returns true if the literal has a valid revision. func (l Literal) HasRevision() bool { revisionSeparatorIndex := strings.IndexByte(string(l), RevisionSeparator) if revisionSeparatorIndex < 1 || revisionSeparatorIndex >= len(l)-1 { return false // must have content on each side of the revision marker } if strings.IndexByte(string(l[revisionSeparatorIndex+1:]), RevisionSeparator) != -1 { return false // multiple revision markers means no valid revision } return true } aip-go-0.80.0/resourcename/sprint.go000066400000000000000000000017601513342120500173110ustar00rootroot00000000000000package resourcename import "strings" // Sprint formats resource name variables according to a pattern and returns the resulting string. func Sprint(pattern string, variables ...string) string { var length, segments int var patternScanner Scanner patternScanner.Init(pattern) for patternScanner.Scan() { segment := patternScanner.Segment() if !segment.IsVariable() { length += len(segment.Literal()) } segments++ } for _, variable := range variables { length += len(variable) } if segments > 0 { length += segments - 1 } var result strings.Builder result.Grow(length) patternScanner.Init(pattern) var i, variable int for patternScanner.Scan() { segment := patternScanner.Segment() if segment.IsVariable() { if variable < len(variables) { _, _ = result.WriteString(variables[variable]) variable++ } } else { _, _ = result.WriteString(string(segment.Literal())) } if i < segments-1 { _ = result.WriteByte('/') } i++ } return result.String() } aip-go-0.80.0/resourcename/sprint_test.go000066400000000000000000000032201513342120500203410ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestSprint(t *testing.T) { t.Parallel() for _, tt := range []struct { name string pattern string variables []string expected string }{ { name: "no variables", pattern: "singleton", expected: "singleton", }, { name: "too many variables", pattern: "singleton", variables: []string{"foo"}, expected: "singleton", }, { name: "single variable", pattern: "publishers/{publisher}", variables: []string{"foo"}, expected: "publishers/foo", }, { name: "two variables", pattern: "publishers/{publisher}/books/{book}", variables: []string{"foo", "bar"}, expected: "publishers/foo/books/bar", }, { name: "singleton two variables", pattern: "publishers/{publisher}/books/{book}/settings", variables: []string{"foo", "bar"}, expected: "publishers/foo/books/bar/settings", }, { name: "empty variable", pattern: "publishers/{publisher}/books/{book}", variables: []string{"foo", ""}, expected: "publishers/foo/books/", }, { name: "too few variables", pattern: "publishers/{publisher}/books/{book}/settings", variables: []string{"foo"}, expected: "publishers/foo/books//settings", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() assert.Equal(t, tt.expected, Sprint(tt.pattern, tt.variables...)) }) } } //nolint:gochecknoglobals var benchmarkSprintSink string func BenchmarkSprint(b *testing.B) { for i := 0; i < b.N; i++ { benchmarkSprintSink = Sprint("publishers/{publisher}/books/{book}", "foo", "bar") } } aip-go-0.80.0/resourcename/sscan.go000066400000000000000000000024731513342120500171030ustar00rootroot00000000000000package resourcename import ( "fmt" "io" ) // Sscan scans a resource name, storing successive segments into successive variables // as determined by the provided pattern. func Sscan(name, pattern string, variables ...*string) (err error) { defer func() { if err != nil { err = fmt.Errorf("parse resource name '%s' with pattern '%s': %w", name, pattern, err) } }() var nameScanner, patternScanner Scanner nameScanner.Init(name) patternScanner.Init(pattern) var i int for patternScanner.Scan() { if patternScanner.Full() { return fmt.Errorf("invalid pattern") } if !nameScanner.Scan() { return fmt.Errorf("segment %s: %w", patternScanner.Segment(), io.ErrUnexpectedEOF) } nameSegment, patternSegment := nameScanner.Segment(), patternScanner.Segment() if !patternSegment.IsVariable() { if patternSegment.Literal() != nameSegment.Literal() { return fmt.Errorf("segment %s: got %s", patternSegment, nameSegment) } continue } if i > len(variables)-1 { return fmt.Errorf("segment %s: too few variables", patternSegment) } *variables[i] = string(nameSegment.Literal()) i++ } if nameScanner.Scan() { return fmt.Errorf("got trailing segments in name") } if i != len(variables) { return fmt.Errorf("too many variables: got %d but expected %d", i, len(variables)) } return nil } aip-go-0.80.0/resourcename/sscan_test.go000066400000000000000000000051411513342120500201350ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestScan(t *testing.T) { t.Parallel() t.Run("no variables", func(t *testing.T) { t.Parallel() assert.NilError( t, Sscan( "publishers", "publishers", ), ) }) t.Run("single variable", func(t *testing.T) { t.Parallel() var publisher string assert.NilError( t, Sscan( "publishers/foo", "publishers/{publisher}", &publisher, ), ) assert.Equal(t, "foo", publisher) }) t.Run("two variables", func(t *testing.T) { t.Parallel() var publisher, book string assert.NilError( t, Sscan( "publishers/foo/books/bar", "publishers/{publisher}/books/{book}", &publisher, &book, ), ) assert.Equal(t, "foo", publisher) assert.Equal(t, "bar", book) }) t.Run("two variables singleton", func(t *testing.T) { t.Parallel() var publisher, book string assert.NilError( t, Sscan( "publishers/foo/books/bar/settings", "publishers/{publisher}/books/{book}/settings", &publisher, &book, ), ) assert.Equal(t, "foo", publisher) assert.Equal(t, "bar", book) }) t.Run("two variables singleton", func(t *testing.T) { t.Parallel() var publisher, book string assert.NilError( t, Sscan( "publishers/foo/books/bar/settings", "publishers/{publisher}/books/{book}/settings", &publisher, &book, ), ) assert.Equal(t, "foo", publisher) assert.Equal(t, "bar", book) }) t.Run("trailing segments", func(t *testing.T) { t.Parallel() var publisher, book string assert.ErrorContains( t, Sscan( "publishers/foo/books/bar/settings", "publishers/{publisher}/books/{book}", &publisher, &book, ), "trailing", ) }) t.Run("too few variables", func(t *testing.T) { t.Parallel() var publisher string assert.ErrorContains( t, Sscan( "publishers/foo/books/bar/settings", "publishers/{publisher}/books/{book}", &publisher, ), "too few variables", ) }) t.Run("too many variables", func(t *testing.T) { t.Parallel() var publisher, book, extra string assert.ErrorContains( t, Sscan( "publishers/foo/books/bar", "publishers/{publisher}/books/{book}", &publisher, &book, &extra, ), "too many variables", ) }) } //nolint:gochecknoglobals var benchmarkScanSink string func BenchmarkScan(b *testing.B) { for i := 0; i < b.N; i++ { var publisher, book string if err := Sscan( "publishers/foo/books/bar", "publishers/{publisher}/books/{book}", &publisher, &book, ); err != nil { b.Fatal(err) } benchmarkScanSink = publisher } } aip-go-0.80.0/resourcename/validate.go000066400000000000000000000040641513342120500175630ustar00rootroot00000000000000package resourcename import ( "fmt" "unicode" ) // Validate that a resource name conforms to the restrictions outlined in AIP-122. // See: https://google.aip.dev/122 func Validate(name string) error { if name == "" { return fmt.Errorf("empty") } var sc Scanner sc.Init(name) var i int for sc.Scan() { i++ segment := sc.Segment() switch { case segment == "": return fmt.Errorf("segment %d is empty", i) case segment == Wildcard: continue case segment.IsVariable(): return fmt.Errorf("segment '%s': valid resource names must not contain variables", sc.Segment()) case !isDomainName(string(sc.Segment())): return fmt.Errorf("segment '%s': not a valid DNS name", sc.Segment()) } } if sc.Full() && !isDomainName(sc.ServiceName()) { return fmt.Errorf("service '%s': not a valid DNS name", sc.Segment()) } return nil } // ValidatePattern that a resource name pattern conforms to the restrictions outlined in AIP-122. // See: https://google.aip.dev/122 func ValidatePattern(pattern string) error { if pattern == "" { return fmt.Errorf("empty") } var sc Scanner sc.Init(pattern) var i int for sc.Scan() { i++ segment := sc.Segment() switch { case segment == "": return fmt.Errorf("segment %d is empty", i) case segment == Wildcard: return fmt.Errorf("segment '%d': wildcards not allowed in patterns", i) case segment.IsVariable(): switch { case segment.Literal() == "": return fmt.Errorf("segment '%s': missing variable name", sc.Segment()) case !isSnakeCase(string(segment.Literal())): return fmt.Errorf("segment '%s': must be valid snake case", sc.Segment()) } case !isDomainName(string(sc.Segment())): return fmt.Errorf("segment '%s': not a valid DNS name", sc.Segment()) } } if sc.Full() { return fmt.Errorf("patterns can not be full resource names") } return nil } func isSnakeCase(s string) bool { for i, r := range s { if i == 0 { if !unicode.IsLower(r) { return false } } else if !(r == '_' || unicode.In(r, unicode.Lower, unicode.Digit)) { return false } } return true } aip-go-0.80.0/resourcename/validate_test.go000066400000000000000000000072521513342120500206240ustar00rootroot00000000000000package resourcename import ( "testing" "gotest.tools/v3/assert" ) func TestValidate(t *testing.T) { t.Parallel() for _, tt := range []struct { name string input string errorContains string }{ { name: "empty", input: "", errorContains: "empty", }, { name: "invalid DNS characters", input: "ice cream is best", errorContains: "not a valid DNS name", }, { name: "invalid DNS characters in segment", input: "foo/bar/ice cream is best", errorContains: "not a valid DNS name", }, { name: "invalid DNS characters in domain", input: "//ice cream is best.com/foo/bar", errorContains: "not a valid DNS name", }, { name: "variable", input: "foo/bar/{baz}", errorContains: "must not contain variables", }, { name: "singleton", input: "foo", }, { name: "singleton wildcard", input: "-", }, { name: "multi", input: "foo/bar", }, { name: "multi wildcard at start", input: "-/bar", }, { name: "multi wildcard at end", input: "foo/-", }, { name: "multi wildcard at middle", input: "foo/-/bar", }, { name: "numeric", input: "foo/1234/bar", }, { name: "camelCase", input: "FOO/1234/bAr", }, { name: "full", input: "//example.com/foo/bar", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() err := Validate(tt.input) if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) } else { assert.NilError(t, err) } }) } } func TestValidatePattern(t *testing.T) { t.Parallel() for _, tt := range []struct { name string input string errorContains string }{ { name: "empty", input: "", errorContains: "empty", }, { name: "invalid DNS characters", input: "ice cream is best", errorContains: "not a valid DNS name", }, { name: "invalid DNS characters in segment", input: "foo/bar/ice cream is best", errorContains: "not a valid DNS name", }, { name: "invalid DNS characters in domain", input: "//ice cream is best.com/foo/bar", errorContains: "patterns can not be full resource names", }, { name: "variable", input: "foo/bar/{baz}", }, { name: "singleton", input: "foo", }, { name: "singleton wildcard", input: "-", errorContains: "wildcards not allowed in patterns", }, { name: "multi", input: "foo/bar", }, { name: "multi wildcard at start", input: "-/bar", errorContains: "wildcards not allowed in patterns", }, { name: "multi wildcard at end", input: "foo/-", errorContains: "wildcards not allowed in patterns", }, { name: "multi wildcard at middle", input: "foo/-/bar", errorContains: "wildcards not allowed in patterns", }, { name: "numeric", input: "foo/1234/bar", }, { name: "camelCase", input: "FOO/1234/bAr", }, { name: "full", input: "//example.com/foo/bar", errorContains: "patterns can not be full resource names", }, { name: "invalid variable name", input: "fooBars/{fooBar}", errorContains: "must be valid snake case", }, { name: "valid variable name", input: "fooBars/{foo_bar}", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() err := ValidatePattern(tt.input) if tt.errorContains != "" { assert.ErrorContains(t, err, tt.errorContains) } else { assert.NilError(t, err) } }) } } aip-go-0.80.0/resourcename/wildcard.go000066400000000000000000000001441513342120500175560ustar00rootroot00000000000000package resourcename // Wildcard is the resource name wildcard character "-". const Wildcard = "-" aip-go-0.80.0/validation/000077500000000000000000000000001513342120500151015ustar00rootroot00000000000000aip-go-0.80.0/validation/doc.go000066400000000000000000000001561513342120500161770ustar00rootroot00000000000000// Package validation provides primitives for validating proto messages and gRPC requests. package validation aip-go-0.80.0/validation/error.go000066400000000000000000000040341513342120500165620ustar00rootroot00000000000000package validation import ( "fmt" "strings" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // Error represents a message validation error. type Error struct { fieldViolations []*errdetails.BadRequest_FieldViolation grpcStatus *status.Status str string } // NewError creates a new validation error from the provided field violations. func NewError(fieldViolations []*errdetails.BadRequest_FieldViolation) error { if len(fieldViolations) == 0 { panic("validation.NewError: must provide at least one field violation") } return &Error{ fieldViolations: fieldViolations, } } // GRPCStatus converts the validation error to a gRPC status with code INVALID_ARGUMENT. func (e *Error) GRPCStatus() *status.Status { if e.grpcStatus == nil { var fields strings.Builder for i, fieldViolation := range e.fieldViolations { _, _ = fields.WriteString(fieldViolation.GetField()) if i < len(e.fieldViolations)-1 { _, _ = fields.WriteString(", ") } } withoutDetails := status.Newf(codes.InvalidArgument, "invalid fields: %s", fields.String()) if withDetails, err := withoutDetails.WithDetails(&errdetails.BadRequest{ FieldViolations: e.fieldViolations, }); err != nil { e.grpcStatus = withoutDetails } else { e.grpcStatus = withDetails } } return e.grpcStatus } // Error implements the error interface. func (e *Error) Error() string { if e.str == "" { if len(e.fieldViolations) == 1 { e.str = fmt.Sprintf( "field violation on %s: %s", e.fieldViolations[0].GetField(), e.fieldViolations[0].GetDescription(), ) } else { var result strings.Builder _, _ = result.WriteString("field violation on multiple fields:\n") for i, fieldViolation := range e.fieldViolations { _, _ = result.WriteString(fmt.Sprintf(" | %s: %s", fieldViolation.GetField(), fieldViolation.GetDescription())) if i < len(e.fieldViolations)-1 { _ = result.WriteByte('\n') } } e.str = result.String() } } return e.str } aip-go-0.80.0/validation/error_test.go000066400000000000000000000025461513342120500176270ustar00rootroot00000000000000package validation import ( "testing" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/testing/protocmp" "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" ) func TestError_NewError(t *testing.T) { t.Parallel() t.Run("panics on empty field violations", func(t *testing.T) { t.Parallel() assert.Assert(t, cmp.Panics(func() { _ = NewError(nil) })) }) } func TestError_Error(t *testing.T) { t.Parallel() err := NewError([]*errdetails.BadRequest_FieldViolation{ {Field: "foo.bar", Description: "test"}, {Field: "baz", Description: "test2"}, }) assert.Error(t, err, `field violation on multiple fields: | foo.bar: test | baz: test2`) } func TestError_GRPCStatus(t *testing.T) { t.Parallel() expected := &errdetails.BadRequest{ FieldViolations: []*errdetails.BadRequest_FieldViolation{ {Field: "foo.bar", Description: "test"}, {Field: "baz", Description: "test2"}, }, } s := status.Convert(NewError(expected.GetFieldViolations())) assert.Equal(t, codes.InvalidArgument, s.Code()) assert.Equal(t, "invalid fields: foo.bar, baz", s.Message()) details := s.Details() assert.Assert(t, len(details) == 1) actual, ok := details[0].(*errdetails.BadRequest) assert.Assert(t, ok) assert.DeepEqual(t, expected, actual, protocmp.Transform()) } aip-go-0.80.0/validation/messagevalidator.go000066400000000000000000000043311513342120500207630ustar00rootroot00000000000000package validation import ( "errors" "fmt" "strings" "google.golang.org/genproto/googleapis/rpc/errdetails" ) // MessageValidator provides primitives for validating the fields of a message. type MessageValidator struct { parentField string fieldViolations []*errdetails.BadRequest_FieldViolation } // SetParentField sets a parent field which will be prepended to all the subsequently added violations. func (m *MessageValidator) SetParentField(parentField string) { m.parentField = parentField } // AddFieldViolation adds a field violation to the message validator. func (m *MessageValidator) AddFieldViolation(field, description string, formatArgs ...interface{}) { if m.parentField != "" { field = makeFieldWithParent(m.parentField, field) } if len(formatArgs) > 0 { description = fmt.Sprintf(description, formatArgs...) } m.fieldViolations = append(m.fieldViolations, &errdetails.BadRequest_FieldViolation{ Field: field, Description: description, }) } // AddFieldError adds a field violation from the provided error. // If the provided error is a validation.Error, the individual field violations from the provided error are added. func (m *MessageValidator) AddFieldError(field string, err error) { var errValidation *Error if errors.As(err, &errValidation) { // Add the child field violations with the current field as parent. originalParentField := m.parentField m.parentField = makeFieldWithParent(m.parentField, field) for _, fieldViolation := range errValidation.fieldViolations { m.AddFieldViolation(fieldViolation.GetField(), fieldViolation.GetDescription()) } m.parentField = originalParentField } else { m.AddFieldViolation(field, err.Error()) } } // Err returns the validator's current validation error, or nil if no field validations have been registered. func (m *MessageValidator) Err() error { if len(m.fieldViolations) > 0 { return NewError(m.fieldViolations) } return nil } func makeFieldWithParent(parentField, field string) string { if parentField == "" { return field } var result strings.Builder result.Grow(len(parentField) + 1 + len(field)) _, _ = result.WriteString(parentField) _ = result.WriteByte('.') _, _ = result.WriteString(field) return result.String() } aip-go-0.80.0/validation/messagevalidator_test.go000066400000000000000000000021641513342120500220240ustar00rootroot00000000000000package validation import ( "errors" "testing" "gotest.tools/v3/assert" ) func TestMessageValidator(t *testing.T) { t.Parallel() t.Run("no violation", func(t *testing.T) { t.Parallel() var v MessageValidator assert.NilError(t, v.Err()) }) t.Run("add single violation", func(t *testing.T) { t.Parallel() var v MessageValidator v.AddFieldViolation("foo", "bar") assert.Error(t, v.Err(), "field violation on foo: bar") }) t.Run("add single violation with parent", func(t *testing.T) { t.Parallel() var v MessageValidator v.SetParentField("foo") v.AddFieldViolation("bar", "baz") assert.Error(t, v.Err(), "field violation on foo.bar: baz") }) t.Run("add nested violations", func(t *testing.T) { t.Parallel() var inner MessageValidator inner.AddFieldViolation("b", "c") var outer MessageValidator outer.AddFieldError("a", inner.Err()) assert.Error(t, outer.Err(), "field violation on a.b: c") }) t.Run("add field error", func(t *testing.T) { t.Parallel() var v MessageValidator v.AddFieldError("a", errors.New("boom")) assert.Error(t, v.Err(), "field violation on a: boom") }) }