blob: ca48641c187ed5cfe60acb1bf23a14258421080d [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 bufpluginref
import (
"fmt"
"strings"
)
// PluginIdentity is a plugin identity.
//
// It just contains remote, owner, plugin.
type PluginIdentity interface {
Remote() string
Owner() string
Plugin() string
// IdentityString is the string remote/owner/plugin.
IdentityString() string
// Prevents this type from being implemented by
// another package.
isPluginIdentity()
}
// NewPluginIdentity returns a new PluginIdentity.
func NewPluginIdentity(
remote string,
owner string,
plugin string,
) (PluginIdentity, error) {
return newPluginIdentity(remote, owner, plugin)
}
// PluginIdentityForString returns a new PluginIdentity for the given string.
//
// This parses the path in the form remote/owner/plugin.
func PluginIdentityForString(path string) (PluginIdentity, error) {
remote, owner, plugin, err := parsePluginIdentityComponents(path)
if err != nil {
return nil, err
}
return NewPluginIdentity(remote, owner, plugin)
}
// PluginReference uniquely references a plugin (including version and revision information).
//
// It can be used to identify dependencies on other plugins.
type PluginReference interface {
PluginIdentity
// ReferenceString is the string representation of identity:version:revision.
ReferenceString() string
// Version is the plugin's semantic version.
Version() string
// Revision is the plugin's revision number.
//
// The accepted range for this value is 0 - math.MaxInt32.
Revision() int
// Prevents this type from being implemented by
// another package.
isPluginReference()
}
// NewPluginReference returns a new PluginReference.
func NewPluginReference(
identity PluginIdentity,
version string,
revision int,
) (PluginReference, error) {
return newPluginReference(identity, version, revision)
}
// PluginReferenceForString returns a new PluginReference for the given string.
//
// This parses the path in the form remote/owner/plugin:version.
func PluginReferenceForString(reference string, revision int) (PluginReference, error) {
return parsePluginReference(reference, revision)
}
// ParsePluginIdentityOptionalVersion returns the PluginIdentity and version for the given string.
// If the string does not contain a version, the version is assumed to be an empty string.
// This parses the path in the form remote/owner/plugin:version.
func ParsePluginIdentityOptionalVersion(rawReference string) (PluginIdentity, string, error) {
if reference, err := PluginReferenceForString(rawReference, 0); err == nil {
return reference, reference.Version(), nil
}
// Try parsing as a plugin identity (no version information)
identity, err := PluginIdentityForString(rawReference)
if err != nil {
return nil, "", fmt.Errorf("invalid remote plugin %s", rawReference)
}
return identity, "", nil
}
// IsPluginReferenceOrIdentity returns true if the argument matches a plugin
// reference (with version) or a plugin identity (without version).
func IsPluginReferenceOrIdentity(plugin string) bool {
if _, err := PluginReferenceForString(plugin, 0); err == nil {
return true
}
if _, err := PluginIdentityForString(plugin); err == nil {
return true
}
return false
}
func parsePluginIdentityComponents(path string) (remote string, owner string, plugin string, err error) {
slashSplit := strings.Split(path, "/")
if len(slashSplit) != 3 {
return "", "", "", newInvalidPluginIdentityStringError(path)
}
remote = strings.TrimSpace(slashSplit[0])
if remote == "" {
return "", "", "", newInvalidPluginIdentityStringError(path)
}
owner = strings.TrimSpace(slashSplit[1])
if owner == "" {
return "", "", "", newInvalidPluginIdentityStringError(path)
}
plugin = strings.TrimSpace(slashSplit[2])
if plugin == "" || strings.ContainsRune(plugin, ':') {
return "", "", "", newInvalidPluginIdentityStringError(path)
}
return remote, owner, plugin, nil
}
func newInvalidPluginIdentityStringError(s string) error {
return fmt.Errorf("plugin identity %q is invalid: must be in the form remote/owner/plugin", s)
}
func parsePluginReference(reference string, revision int) (PluginReference, error) {
name, version, ok := strings.Cut(reference, ":")
if !ok {
return nil, fmt.Errorf("plugin references must be specified as \"<name>:<version>\" strings")
}
identity, err := PluginIdentityForString(name)
if err != nil {
return nil, err
}
return NewPluginReference(identity, version, revision)
}