blob: 398442633c79bd450995ffe05765b4e297c07283 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2014 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4package names
5
6import (
7 "fmt"
8 "regexp"
9)
10
11// CharmTagKind specifies charm tag kind
12const CharmTagKind = "charm"
13
14// Valid charm url can be either in V1 or V3 format. (V2 is a
15// charmstore web URL like https://jujucharms.com/postgresql/105, but
16// that's not valid as a tag.)
17//
18// V1 is of the form:
19// schema:~user/series/name-revision
20// where
21// schema is optional and can be either "local" or "cs".
22// When not supplied, "cs" is implied.
23// user is optional and is only applicable for "cs" schema
24// series is optional and is a valid series name
25// name is mandatory and is the name of the charm
26// revision is optional and can be -1 if revision is unset
27//
28// V3 is of the form
29// schema:user/name/series/revision
30// with the same fields and constraints as the V1 format.
31
32var (
33 // SeriesSnippet is a regular expression representing series
34 SeriesSnippet = "[a-z]+([a-z0-9]+)?"
35
36 // CharmNameSnippet is a regular expression representing charm name
37 CharmNameSnippet = "[a-z][a-z0-9]*(-[a-z0-9]*[a-z][a-z0-9]*)*"
38
39 localSchemaSnippet = "local:"
40 v1CharmStoreSchemaSnippet = "cs:(~" + validUserNameSnippet + "/)?"
41 revisionSnippet = "(-1|0|[1-9][0-9]*)"
42
43 validV1CharmRegEx = regexp.MustCompile("^(" +
44 localSchemaSnippet + "|" +
45 v1CharmStoreSchemaSnippet + ")?(" +
46 SeriesSnippet + "/)?" +
47 CharmNameSnippet + "(-" +
48 revisionSnippet + ")?$")
49
50 v3CharmStoreSchemaSnippet = "(cs:)?(" + validUserNameSnippet + "/)?"
51
52 validV3CharmRegEx = regexp.MustCompile("^(" +
53 localSchemaSnippet + "|" +
54 v3CharmStoreSchemaSnippet + ")" +
55 CharmNameSnippet + "(/" +
56 SeriesSnippet + ")?(/" +
57 revisionSnippet + ")?$")
58)
59
60// CharmTag represents tag for charm
61// using charm's URL
62type CharmTag struct {
63 url string
64}
65
66// String satisfies Tag interface.
67// Produces string representation of charm tag.
68func (t CharmTag) String() string { return t.Kind() + "-" + t.Id() }
69
70// Kind satisfies Tag interface.
71// Returns Charm tag kind.
72func (t CharmTag) Kind() string { return CharmTagKind }
73
74// Id satisfies Tag interface.
75// Returns charm URL.
76func (t CharmTag) Id() string { return t.url }
77
78// NewCharmTag returns the tag for the charm with the given url.
79// It will panic if the given charm url is not valid.
80func NewCharmTag(charmURL string) CharmTag {
81 if !IsValidCharm(charmURL) {
82 panic(fmt.Sprintf("%q is not a valid charm name", charmURL))
83 }
84 return CharmTag{url: charmURL}
85}
86
87var emptyTag = CharmTag{}
88
89// ParseCharmTag parses a charm tag string.
90func ParseCharmTag(charmTag string) (CharmTag, error) {
91 tag, err := ParseTag(charmTag)
92 if err != nil {
93 return emptyTag, err
94 }
95 ct, ok := tag.(CharmTag)
96 if !ok {
97 return emptyTag, invalidTagError(charmTag, CharmTagKind)
98 }
99 return ct, nil
100}
101
102// IsValidCharm returns whether name is a valid charm url.
103func IsValidCharm(url string) bool {
104 return validV1CharmRegEx.MatchString(url) || validV3CharmRegEx.MatchString(url)
105}