blob: 398442633c79bd450995ffe05765b4e297c07283 [file] [log] [blame]
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package names
import (
"fmt"
"regexp"
)
// CharmTagKind specifies charm tag kind
const CharmTagKind = "charm"
// Valid charm url can be either in V1 or V3 format. (V2 is a
// charmstore web URL like https://jujucharms.com/postgresql/105, but
// that's not valid as a tag.)
//
// V1 is of the form:
// schema:~user/series/name-revision
// where
// schema is optional and can be either "local" or "cs".
// When not supplied, "cs" is implied.
// user is optional and is only applicable for "cs" schema
// series is optional and is a valid series name
// name is mandatory and is the name of the charm
// revision is optional and can be -1 if revision is unset
//
// V3 is of the form
// schema:user/name/series/revision
// with the same fields and constraints as the V1 format.
var (
// SeriesSnippet is a regular expression representing series
SeriesSnippet = "[a-z]+([a-z0-9]+)?"
// CharmNameSnippet is a regular expression representing charm name
CharmNameSnippet = "[a-z][a-z0-9]*(-[a-z0-9]*[a-z][a-z0-9]*)*"
localSchemaSnippet = "local:"
v1CharmStoreSchemaSnippet = "cs:(~" + validUserNameSnippet + "/)?"
revisionSnippet = "(-1|0|[1-9][0-9]*)"
validV1CharmRegEx = regexp.MustCompile("^(" +
localSchemaSnippet + "|" +
v1CharmStoreSchemaSnippet + ")?(" +
SeriesSnippet + "/)?" +
CharmNameSnippet + "(-" +
revisionSnippet + ")?$")
v3CharmStoreSchemaSnippet = "(cs:)?(" + validUserNameSnippet + "/)?"
validV3CharmRegEx = regexp.MustCompile("^(" +
localSchemaSnippet + "|" +
v3CharmStoreSchemaSnippet + ")" +
CharmNameSnippet + "(/" +
SeriesSnippet + ")?(/" +
revisionSnippet + ")?$")
)
// CharmTag represents tag for charm
// using charm's URL
type CharmTag struct {
url string
}
// String satisfies Tag interface.
// Produces string representation of charm tag.
func (t CharmTag) String() string { return t.Kind() + "-" + t.Id() }
// Kind satisfies Tag interface.
// Returns Charm tag kind.
func (t CharmTag) Kind() string { return CharmTagKind }
// Id satisfies Tag interface.
// Returns charm URL.
func (t CharmTag) Id() string { return t.url }
// NewCharmTag returns the tag for the charm with the given url.
// It will panic if the given charm url is not valid.
func NewCharmTag(charmURL string) CharmTag {
if !IsValidCharm(charmURL) {
panic(fmt.Sprintf("%q is not a valid charm name", charmURL))
}
return CharmTag{url: charmURL}
}
var emptyTag = CharmTag{}
// ParseCharmTag parses a charm tag string.
func ParseCharmTag(charmTag string) (CharmTag, error) {
tag, err := ParseTag(charmTag)
if err != nil {
return emptyTag, err
}
ct, ok := tag.(CharmTag)
if !ok {
return emptyTag, invalidTagError(charmTag, CharmTagKind)
}
return ct, nil
}
// IsValidCharm returns whether name is a valid charm url.
func IsValidCharm(url string) bool {
return validV1CharmRegEx.MatchString(url) || validV3CharmRegEx.MatchString(url)
}