@@ -5,32 +5,58 @@ import (
5
5
"log"
6
6
"os"
7
7
"path/filepath"
8
+ "reflect"
9
+ "strconv"
8
10
"strings"
9
11
)
10
12
13
+ type ResourceIDFieldType string
14
+
11
15
var (
12
- defaultSeparator = ":"
13
- allIDs = []* ResourceID {}
16
+ defaultSeparator = ":"
17
+ ResourceIDFieldTypeInt = ResourceIDFieldType ("int" )
18
+ ResourceIDFieldTypeString = ResourceIDFieldType ("string" )
19
+ allIDs = []* ResourceID {}
14
20
)
15
21
22
+ type ResourceIDField struct {
23
+ Name string
24
+ Type ResourceIDFieldType
25
+ // Optional bool // Unimplemented. Will be used for org ID
26
+ }
27
+
28
+ func StringIDField (name string ) ResourceIDField {
29
+ return ResourceIDField {
30
+ Name : name ,
31
+ Type : ResourceIDFieldTypeString ,
32
+ }
33
+ }
34
+
35
+ func IntIDField (name string ) ResourceIDField {
36
+ return ResourceIDField {
37
+ Name : name ,
38
+ Type : ResourceIDFieldTypeInt ,
39
+ }
40
+ }
41
+
16
42
type ResourceID struct {
17
43
resourceName string
18
44
separators []string
19
- expectedFields []string
45
+ expectedFields []ResourceIDField
20
46
}
21
47
22
- func NewResourceID (resourceName string , expectedFields ... string ) * ResourceID {
48
+ func NewResourceID (resourceName string , expectedFields ... ResourceIDField ) * ResourceID {
23
49
return newResourceIDWithSeparators (resourceName , []string {defaultSeparator }, expectedFields ... )
24
50
}
25
51
26
52
// Deprecated: Use NewResourceID instead
27
53
// We should standardize on a single separator, so that function should only be used for old resources
28
54
// On major versions, switch to NewResourceID and remove uses of this function
29
- func NewResourceIDWithLegacySeparator (resourceName , legacySeparator string , expectedFields ... string ) * ResourceID {
55
+ func NewResourceIDWithLegacySeparator (resourceName , legacySeparator string , expectedFields ... ResourceIDField ) * ResourceID {
30
56
return newResourceIDWithSeparators (resourceName , []string {defaultSeparator , legacySeparator }, expectedFields ... )
31
57
}
32
58
33
- func newResourceIDWithSeparators (resourceName string , separators []string , expectedFields ... string ) * ResourceID {
59
+ func newResourceIDWithSeparators (resourceName string , separators []string , expectedFields ... ResourceIDField ) * ResourceID {
34
60
tfID := & ResourceID {
35
61
resourceName : resourceName ,
36
62
separators : separators ,
@@ -43,31 +69,83 @@ func newResourceIDWithSeparators(resourceName string, separators []string, expec
43
69
func (id * ResourceID ) Example () string {
44
70
fields := make ([]string , len (id .expectedFields ))
45
71
for i := range fields {
46
- fields [i ] = fmt .Sprintf ("{{ %s }}" , id .expectedFields [i ])
72
+ fields [i ] = fmt .Sprintf ("{{ %s }}" , id .expectedFields [i ]. Name )
47
73
}
48
74
return fmt .Sprintf (`terraform import %s.name %q
49
75
` , id .resourceName , strings .Join (fields , defaultSeparator ))
50
76
}
51
77
78
+ // Make creates a resource ID from the given parts
79
+ // The parts must have the correct number of fields and types
52
80
func (id * ResourceID ) Make (parts ... any ) string {
53
81
if len (parts ) != len (id .expectedFields ) {
54
82
panic (fmt .Sprintf ("expected %d fields, got %d" , len (id .expectedFields ), len (parts ))) // This is a coding error, so panic is appropriate
55
83
}
56
84
stringParts := make ([]string , len (parts ))
57
85
for i , part := range parts {
58
- stringParts [i ] = fmt .Sprintf ("%v" , part )
86
+ // Unwrap pointers
87
+ if reflect .ValueOf (part ).Kind () == reflect .Ptr {
88
+ part = reflect .ValueOf (part ).Elem ().Interface ()
89
+ }
90
+ expectedField := id .expectedFields [i ]
91
+ switch expectedField .Type {
92
+ case ResourceIDFieldTypeInt :
93
+ asInt , ok := part .(int64 )
94
+ if ! ok {
95
+ panic (fmt .Sprintf ("expected int64 for field %q, got %T" , expectedField .Name , part )) // This is a coding error, so panic is appropriate
96
+ }
97
+ stringParts [i ] = strconv .FormatInt (asInt , 10 )
98
+ case ResourceIDFieldTypeString :
99
+ asString , ok := part .(string )
100
+ if ! ok {
101
+ panic (fmt .Sprintf ("expected string for field %q, got %T" , expectedField .Name , part )) // This is a coding error, so panic is appropriate
102
+ }
103
+ stringParts [i ] = asString
104
+ }
59
105
}
106
+
60
107
return strings .Join (stringParts , defaultSeparator )
61
108
}
62
109
63
- func (id * ResourceID ) Split (resourceID string ) ([]string , error ) {
110
+ // Single parses a resource ID into a single value
111
+ func (id * ResourceID ) Single (resourceID string ) (any , error ) {
112
+ parts , err := id .Split (resourceID )
113
+ if err != nil {
114
+ return nil , err
115
+ }
116
+ return parts [0 ], nil
117
+ }
118
+
119
+ // Split parses a resource ID into its parts
120
+ // The parts will be cast to the expected types
121
+ func (id * ResourceID ) Split (resourceID string ) ([]any , error ) {
64
122
for _ , sep := range id .separators {
65
123
parts := strings .Split (resourceID , sep )
66
124
if len (parts ) == len (id .expectedFields ) {
67
- return parts , nil
125
+ partsAsAny := make ([]any , len (parts ))
126
+ for i , part := range parts {
127
+ expectedField := id .expectedFields [i ]
128
+ switch expectedField .Type {
129
+ case ResourceIDFieldTypeInt :
130
+ asInt , err := strconv .ParseInt (part , 10 , 64 )
131
+ if err != nil {
132
+ return nil , fmt .Errorf ("expected int for field %q, got %q" , expectedField .Name , part )
133
+ }
134
+ partsAsAny [i ] = asInt
135
+ case ResourceIDFieldTypeString :
136
+ partsAsAny [i ] = part
137
+ }
138
+ }
139
+
140
+ return partsAsAny , nil
68
141
}
69
142
}
70
- return nil , fmt .Errorf ("id %q does not match expected format. Should be in the format: %s" , resourceID , strings .Join (id .expectedFields , defaultSeparator ))
143
+
144
+ expectedFieldNames := make ([]string , len (id .expectedFields ))
145
+ for i , f := range id .expectedFields {
146
+ expectedFieldNames [i ] = f .Name
147
+ }
148
+ return nil , fmt .Errorf ("id %q does not match expected format. Should be in the format: %s" , resourceID , strings .Join (expectedFieldNames , defaultSeparator ))
71
149
}
72
150
73
151
// GenerateImportFiles generates import files for all resources that use a helper defined in this package
0 commit comments