crossplane-tools/internal/fields/fields.go

228 lines
7.8 KiB
Go

/*
Copyright 2019 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package fields defines and matches common struct fields.
package fields
import (
"go/types"
"strings"
)
// Field names.
const (
NameTypeMeta = "TypeMeta"
NameObjectMeta = "ObjectMeta"
NameListMeta = "ListMeta"
NameSpec = "Spec"
NameSpecTemplate = "SpecTemplate"
NameStatus = "Status"
NameResourceSpec = "ResourceSpec"
NameResourceStatus = "ResourceStatus"
NameProviderConfigSpec = "ProviderConfigSpec"
NameProviderConfigStatus = "ProviderConfigStatus"
NameProviderConfigUsage = "ProviderConfigUsage"
NameItems = "Items"
NameTypedProviderConfigUsage = "TypedProviderConfigUsage"
NameResourceV2Spec = "ManagedResourceSpec"
)
// Field type suffixes.
const (
TypeSuffixTypeMeta = "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta"
TypeSuffixObjectMeta = "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"
TypeSuffixListMeta = "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"
TypeSuffixSpec = NameSpec
TypeSuffixSpecTemplate = NameSpecTemplate
TypeSuffixStatus = NameStatus
TypeSuffixResourceSpec = "github.com/crossplane/crossplane-runtime/apis/common/v1.ResourceSpec"
TypeSuffixResourceStatus = "github.com/crossplane/crossplane-runtime/apis/common/v1.ResourceStatus"
TypeSuffixProviderConfigSpec = "github.com/crossplane/crossplane-runtime/apis/common/v1.ProviderConfigSpec"
TypeSuffixProviderConfigStatus = "github.com/crossplane/crossplane-runtime/apis/common/v1.ProviderConfigStatus"
TypeSuffixProviderConfigUsage = "github.com/crossplane/crossplane-runtime/apis/common/v1.ProviderConfigUsage"
TypeSuffixProviderConfigUsageV2 = "github.com/crossplane/crossplane-runtime/apis/common/v2.TypedProviderConfigUsage"
TypeSuffixResourceV2Spec = "github.com/crossplane/crossplane-runtime/apis/common/v2.ManagedResourceSpec"
)
func matches(s *types.Struct, m Matcher) bool {
for i := range s.NumFields() {
if m(s.Field(i)) {
return true
}
}
return false
}
// A Matcher is a function that returns true if the supplied Var (assumed to be
// a struct field) matches its requirements.
type Matcher func(f *types.Var) bool
// And chains the original Matcher o with a new Matcher m.
func (o Matcher) And(m Matcher) Matcher {
return func(f *types.Var) bool {
return o(f) && m(f)
}
}
// Has returns true if the supplied Object's underlying type is struct (or a
// slice or map of struct), and it matches all of the supplied field Matchers.
func Has(o types.Object, m ...Matcher) bool {
s := findStruct(o)
if s == nil {
return false
}
for _, matcher := range m {
if !matches(s, matcher) {
return false
}
}
return true
}
func findStruct(o types.Object) *types.Struct {
switch t := o.Type().Underlying().(type) {
case *types.Struct:
return t
case *types.Slice:
s, ok := t.Elem().Underlying().(*types.Struct)
if !ok {
return nil
}
return s
case *types.Map:
s, ok := t.Elem().Underlying().(*types.Struct)
if !ok {
return nil
}
return s
}
return nil
}
// IsEmbedded returns a Matcher that returns true if the supplied field is
// embedded.
func IsEmbedded() Matcher {
return func(f *types.Var) bool {
return f.Embedded()
}
}
// IsSlice returns a Matcher that returns true if the supplied field is a slice.
func IsSlice() Matcher {
return func(f *types.Var) bool {
_, ok := f.Type().Underlying().(*types.Slice)
return ok
}
}
// IsNamed returns a Matcher that returns true if the supplied field has the
// supplied name.
func IsNamed(name string) Matcher {
return func(f *types.Var) bool {
if !f.IsField() {
return false
}
return f.Name() == name
}
}
// IsTypeNamed returns a Matcher that returns true if the supplied field has the
// supplied type name suffix and name.
func IsTypeNamed(typeNameSuffix, name string) Matcher {
return func(f *types.Var) bool {
if !IsNamed(name)(f) {
return false
}
return strings.HasSuffix(f.Type().String(), typeNameSuffix)
}
}
// HasFieldThat returns a Matcher that returns true if the supplied field is a
// struct that matches the supplied field matchers.
func HasFieldThat(m ...Matcher) Matcher {
return func(f *types.Var) bool {
return Has(f, m...)
}
}
// IsTypeMeta returns a Matcher that returns true if the supplied field appears
// to be Kubernetes type metadata.
func IsTypeMeta() Matcher { return IsTypeNamed(TypeSuffixTypeMeta, NameTypeMeta) }
// IsObjectMeta returns a Matcher that returns true if the supplied field
// appears to be Kubernetes object metadata.
func IsObjectMeta() Matcher { return IsTypeNamed(TypeSuffixObjectMeta, NameObjectMeta) }
// IsListMeta returns a Matcher that returns true if the supplied field appears
// to be Kubernetes list metadata.
func IsListMeta() Matcher { return IsTypeNamed(TypeSuffixListMeta, NameListMeta) }
// IsSpec returns a Matcher that returns true if the supplied field appears to
// be a Kubernetes resource spec.
func IsSpec() Matcher { return IsTypeNamed(NameSpec, TypeSuffixSpec) }
// IsSpecTemplate returns a Matcher that returns true if the supplied field
// appears to be a Crossplane resource class spec template.
func IsSpecTemplate() Matcher { return IsTypeNamed(NameSpecTemplate, TypeSuffixSpecTemplate) }
// IsStatus returns a Matcher that returns true if the supplied field appears to
// be a Kubernetes resource status.
func IsStatus() Matcher { return IsTypeNamed(NameStatus, TypeSuffixStatus) }
// IsResourceSpec returns a Matcher that returns true if the supplied field
// appears to be a Crossplane managed resource spec.
func IsResourceSpec() Matcher { return IsTypeNamed(TypeSuffixResourceSpec, NameResourceSpec) }
// IsResourceV2Spec returns a Matcher that returns true if the supplied field
// appears to be a Crossplane managed resource spec.
func IsResourceV2Spec() Matcher { return IsTypeNamed(TypeSuffixResourceV2Spec, NameResourceV2Spec) }
// IsResourceStatus returns a Matcher that returns true if the supplied field
// appears to be a Crossplane managed resource status.
func IsResourceStatus() Matcher { return IsTypeNamed(TypeSuffixResourceStatus, NameResourceStatus) }
// IsProviderConfigSpec returns a Matcher that returns true if the supplied
// field appears to be a Crossplane provider config spec.
func IsProviderConfigSpec() Matcher {
return IsTypeNamed(TypeSuffixProviderConfigSpec, NameProviderConfigSpec)
}
// IsProviderConfigStatus returns a Matcher that returns true if the supplied
// field appears to be a Crossplane provider config status.
func IsProviderConfigStatus() Matcher {
return IsTypeNamed(TypeSuffixProviderConfigStatus, NameProviderConfigStatus)
}
// IsProviderConfigUsage returns a Matcher that returns true if the supplied
// field appears to be a Crossplane provider config usage.
func IsProviderConfigUsage() Matcher {
return IsTypeNamed(TypeSuffixProviderConfigUsage, NameProviderConfigUsage)
}
// IsTypedProviderConfigUsage returns a Matcher that returns true if the supplied
// field appears to be a Crossplane provider config usage with typed ref.
func IsTypedProviderConfigUsage() Matcher {
return IsTypeNamed(TypeSuffixProviderConfigUsageV2, NameTypedProviderConfigUsage)
}
// IsItems returns a Matcher that returns true if the supplied field appears to
// be the Items of a Kubernetes list.
func IsItems() Matcher {
return IsNamed(NameItems)
}