1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License. See License.txt in the project root for license information.
3
4package model
5
6import (
7	"fmt"
8	"regexp"
9	"strings"
10)
11
12// OptionType describes the type of the option, possible values are 'Argument', 'Flag' or 'KeyValue'
13type OptionType string
14
15const (
16	// Argument ...
17	Argument OptionType = "Argument"
18	// Flag ...
19	Flag OptionType = "Flag"
20	// KeyValue ...
21	KeyValue OptionType = "KeyValue"
22)
23
24// Option describes an option of a autorest command line
25type Option interface {
26	// Type returns the type of this option
27	Type() OptionType
28	// Format returns the actual option in string
29	Format() string
30}
31
32// ArgumentOption is an option not starting with '--' or '-'
33type ArgumentOption interface {
34	Option
35	Argument() string
36}
37
38// FlagOption is an option that starts with '--' but do not have a value
39type FlagOption interface {
40	Option
41	Flag() string
42}
43
44// KeyValueOption is an option that starts with '--' and have a value
45type KeyValueOption interface {
46	Option
47	Key() string
48	Value() string
49}
50
51// NewOption returns an Option from a formatted string. We should hold this result using this function: input == result.Format()
52func NewOption(input string) (Option, error) {
53	if strings.HasPrefix(input, "--") {
54		// this option is either a flag or a key value pair option
55		return parseFlagOrKeyValuePair(strings.TrimPrefix(input, "--"))
56	}
57	// this should be an argument
58	return argument{value: input}, nil
59}
60
61var flagRegex = regexp.MustCompile(`^[a-zA-Z]`)
62
63func parseFlagOrKeyValuePair(input string) (Option, error) {
64	if !flagRegex.MatchString(input) {
65		return nil, fmt.Errorf("cannot parse flag '%s', a flag or option should only start with letters", input)
66	}
67	segments := strings.Split(input, "=")
68	if len(segments) <= 1 {
69		// this should be a flag
70		return flagOption{value: input}, nil
71	}
72	return keyValueOption{
73		key:   segments[0],
74		value: strings.Join(segments[1:], "="),
75	}, nil
76}
77
78type argument struct {
79	value string
80}
81
82func (f argument) Type() OptionType {
83	return Argument
84}
85
86// Format ...
87func (f argument) Format() string {
88	return f.value
89}
90
91func (f argument) Argument() string {
92	return f.value
93}
94
95func (f argument) String() string {
96	return f.Format()
97}
98
99var _ ArgumentOption = (*argument)(nil)
100
101type flagOption struct {
102	value string
103}
104
105func (f flagOption) Type() OptionType {
106	return Flag
107}
108
109// Format ...
110func (f flagOption) Format() string {
111	return fmt.Sprintf("--%s", f.value)
112}
113
114func (f flagOption) String() string {
115	return f.Format()
116}
117
118func (f flagOption) Flag() string {
119	return f.value
120}
121
122var _ FlagOption = (*flagOption)(nil)
123
124type keyValueOption struct {
125	key   string
126	value string
127}
128
129func (f keyValueOption) Type() OptionType {
130	return KeyValue
131}
132
133// Format ...
134func (f keyValueOption) Format() string {
135	return fmt.Sprintf("--%s=%s", f.key, f.value)
136}
137
138func (f keyValueOption) String() string {
139	return f.Format()
140}
141
142func (f keyValueOption) Key() string {
143	return f.key
144}
145
146func (f keyValueOption) Value() string {
147	return f.value
148}
149
150var _ KeyValueOption = (*keyValueOption)(nil)
151
152// NewArgument returns a new argument option (without "--")
153func NewArgument(value string) Option {
154	return argument{
155		value: value,
156	}
157}
158
159// NewFlagOption returns a flag option (with "--", without value)
160func NewFlagOption(flag string) Option {
161	return flagOption{
162		value: flag,
163	}
164}
165
166// NewKeyValueOption returns a key-value option like "--tag=something"
167func NewKeyValueOption(key, value string) Option {
168	return keyValueOption{
169		key:   key,
170		value: value,
171	}
172}
173