github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/project/namespace.go (about) 1 package project 2 3 import ( 4 "fmt" 5 "regexp" 6 7 "github.com/ActiveState/cli/internal/fileutils" 8 "github.com/ActiveState/cli/internal/locale" 9 "github.com/ActiveState/cli/pkg/projectfile" 10 "github.com/go-openapi/strfmt" 11 ) 12 13 // NamespaceRegex matches the org and project name in a namespace, eg. org/project 14 const NamespaceRegex = `^([\w-_]+)\/([\w-_\.]+)(?:#([-a-fA-F0-9]*))?$` 15 16 // ProjectRegex matches the project name for a namespace with omitted org. 17 const ProjectRegex = `^([\w-_\.]+)(?:#([-a-fA-F0-9]*))?$` 18 19 // Namespaced represents a project namespace of the form <org/project> 20 type Namespaced struct { 21 Owner string 22 Project string 23 CommitID *strfmt.UUID 24 AllowOmitOwner bool 25 } 26 27 type ConfigAble interface { 28 projectfile.ConfigGetter 29 } 30 31 func NewNamespace(owner, project, commitID string) *Namespaced { 32 ns := &Namespaced{ 33 Owner: owner, 34 Project: project, 35 } 36 if commitID != "" { 37 commitUUID := strfmt.UUID(commitID) 38 ns.CommitID = &commitUUID 39 } 40 return ns 41 } 42 43 // Set implements the captain argmarshaler interface. 44 func (ns *Namespaced) Set(v string) error { 45 if ns == nil { 46 return fmt.Errorf("cannot set nil value") 47 } 48 49 parsedNs, err := ParseNamespace(v) 50 if err != nil { 51 if !ns.AllowOmitOwner { 52 return err 53 } 54 parsedNs, err = ParseProjectNoOwner(v) 55 if err != nil { 56 return err 57 } 58 } 59 60 *ns = *parsedNs 61 return nil 62 } 63 64 // String implements the fmt.Stringer interface. 65 func (ns *Namespaced) String() string { 66 if ns == nil { 67 return "" 68 } 69 70 var sep string 71 if ns.IsValid() { 72 sep = "/" 73 } 74 return fmt.Sprintf("%s%s%s", ns.Owner, sep, ns.Project) 75 } 76 77 // Type returns the human readable type name of Namespaced. 78 func (ns *Namespaced) Type() string { 79 return "namespace" 80 } 81 82 // IsValid returns whether or not the namespace is set sufficiently. 83 func (ns *Namespaced) IsValid() bool { 84 return ns != nil && ns.Project != "" 85 } 86 87 // Validate returns a failure if the namespace is not valid. 88 func (ns *Namespaced) Validate() error { 89 if ns == nil || !ns.IsValid() { 90 return locale.NewInputError("err_invalid_namespace", "", ns.String()) 91 } 92 return nil 93 } 94 95 // ParseNamespace returns a valid project namespace 96 func ParseNamespace(raw string) (*Namespaced, error) { 97 rx := regexp.MustCompile(NamespaceRegex) 98 groups := rx.FindStringSubmatch(raw) 99 if len(groups) < 3 { 100 return nil, locale.NewInputError("err_invalid_namespace", "", raw) 101 } 102 103 names := Namespaced{ 104 Owner: groups[1], 105 Project: groups[2], 106 } 107 108 if len(groups) > 3 && len(groups[3]) > 0 { 109 uuidString := groups[3] 110 if !strfmt.IsUUID(uuidString) { 111 return nil, locale.NewInputError("err_invalid_commit_id", "", uuidString) 112 } 113 uuid := strfmt.UUID(uuidString) 114 names.CommitID = &uuid 115 } 116 117 return &names, nil 118 } 119 120 func ParseProjectNoOwner(raw string) (*Namespaced, error) { 121 rx := regexp.MustCompile(ProjectRegex) 122 groups := rx.FindStringSubmatch(raw) 123 if len(groups) < 2 { 124 return nil, locale.NewInputError("err_invalid_project_name", "", raw) 125 } 126 127 names := Namespaced{ 128 Project: groups[1], 129 AllowOmitOwner: true, 130 } 131 132 if len(groups) > 2 && len(groups[2]) > 0 { 133 uuidString := groups[2] 134 if !strfmt.IsUUID(uuidString) { 135 return nil, locale.NewInputError("err_invalid_commit_id", "", uuidString) 136 } 137 uuid := strfmt.UUID(uuidString) 138 names.CommitID = &uuid 139 } 140 141 return &names, nil 142 } 143 144 // NameSpaceForConfig returns a valid project namespace. 145 // This version prefers to create a namespace from a configFile if it exists 146 func NameSpaceForConfig(configFile string) *Namespaced { 147 if !fileutils.FileExists(configFile) { 148 return nil 149 } 150 151 prj, err := FromPath(configFile) 152 if err != nil { 153 return nil 154 } 155 156 names := Namespaced{ 157 Owner: prj.Owner(), 158 Project: prj.Name(), 159 } 160 161 commitID := strfmt.UUID(prj.LegacyCommitID()) // Not using localcommit due to import cycle. See anti-pattern comment in localcommit pkg. 162 if commitID != "" { 163 names.CommitID = &commitID 164 } 165 166 return &names 167 }