Skip to content

Commit 8df6fc5

Browse files
committed
bake: add rfc3339parse function
Signed-off-by: CrazyMax <[email protected]>
1 parent 661a159 commit 8df6fc5

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

bake/hclparser/stdlib.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ var stdlibFunctions = []funcDef{
9595
{name: "replace", fn: stdlib.ReplaceFunc},
9696
{name: "reverse", fn: stdlib.ReverseFunc},
9797
{name: "reverselist", fn: stdlib.ReverseListFunc},
98+
{name: "rfc3339parse", factory: rfc3339ParseFunc},
9899
{name: "rsadecrypt", fn: crypto.RsaDecryptFunc, descriptionAlt: `Decrypts an RSA-encrypted ciphertext.`},
99100
{name: "sanitize", factory: sanitizeFunc},
100101
{name: "sethaselement", fn: stdlib.SetHasElementFunc},
@@ -242,7 +243,7 @@ func sanitizeFunc() function.Function {
242243

243244
// timestampFunc constructs a function that returns a string representation of the current date and time.
244245
//
245-
// This function was imported from terraform's datetime utilities.
246+
// This function was imported from Terraform's datetime utilities.
246247
func timestampFunc() function.Function {
247248
return function.New(&function.Spec{
248249
Description: `Returns a string representation of the current date and time.`,
@@ -254,6 +255,62 @@ func timestampFunc() function.Function {
254255
})
255256
}
256257

258+
// rfc3339ParseFunc, given an RFC3339 timestamp string, will parse and return an
259+
// object representation of that date and time.
260+
//
261+
// This function is similar to the `rfc3339_parse` function in Terraform:
262+
// https://registry.terraform.io/providers/hashicorp/time/latest/docs/functions/rfc3339_parse
263+
func rfc3339ParseFunc() function.Function {
264+
return function.New(&function.Spec{
265+
Description: `Given an RFC3339 timestamp string, will parse and return an object representation of that date and time.`,
266+
Params: []function.Parameter{
267+
{
268+
Name: "timestamp",
269+
Description: "RFC3339 timestamp string to parse",
270+
Type: cty.String,
271+
},
272+
},
273+
Type: function.StaticReturnType(cty.Object(map[string]cty.Type{
274+
"year": cty.Number,
275+
"year_day": cty.Number,
276+
"day": cty.Number,
277+
"month": cty.Number,
278+
"month_name": cty.String,
279+
"weekday": cty.Number,
280+
"weekday_name": cty.String,
281+
"hour": cty.Number,
282+
"minute": cty.Number,
283+
"second": cty.Number,
284+
"unix": cty.Number,
285+
"iso_year": cty.Number,
286+
"iso_week": cty.Number,
287+
})),
288+
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
289+
ts := args[0].AsString()
290+
rfc3339, err := time.Parse(time.RFC3339, ts)
291+
if err != nil {
292+
return cty.NilVal, err
293+
}
294+
isoYear, isoWeek := rfc3339.ISOWeek()
295+
return cty.ObjectVal(map[string]cty.Value{
296+
"year": cty.NumberIntVal(int64(rfc3339.Year())),
297+
"year_day": cty.NumberIntVal(int64(rfc3339.YearDay())),
298+
"day": cty.NumberIntVal(int64(rfc3339.Day())),
299+
"month": cty.NumberIntVal(int64(rfc3339.Month())),
300+
"month_name": cty.StringVal(rfc3339.Month().String()),
301+
"weekday": cty.NumberIntVal(int64(rfc3339.Weekday())),
302+
"weekday_name": cty.StringVal(rfc3339.Weekday().String()),
303+
"hour": cty.NumberIntVal(int64(rfc3339.Hour())),
304+
"minute": cty.NumberIntVal(int64(rfc3339.Minute())),
305+
"second": cty.NumberIntVal(int64(rfc3339.Second())),
306+
"unix": cty.NumberIntVal(int64(rfc3339.Unix())),
307+
"iso_year": cty.NumberIntVal(int64(isoYear)),
308+
"iso_week": cty.NumberIntVal(int64(isoWeek)),
309+
}), nil
310+
},
311+
})
312+
}
313+
257314
func Stdlib() map[string]function.Function {
258315
funcs := make(map[string]function.Function, len(stdlibFunctions))
259316
for _, v := range stdlibFunctions {

bake/hclparser/stdlib_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,32 @@ func TestSanitize(t *testing.T) {
197197
})
198198
}
199199
}
200+
201+
func TestRfc3339ParseFunc(t *testing.T) {
202+
fn := rfc3339ParseFunc()
203+
input := cty.StringVal("2024-06-01T15:04:05Z")
204+
got, err := fn.Call([]cty.Value{input})
205+
require.NoError(t, err)
206+
207+
expected := map[string]cty.Value{
208+
"year": cty.NumberIntVal(2024),
209+
"year_day": cty.NumberIntVal(153),
210+
"day": cty.NumberIntVal(1),
211+
"month": cty.NumberIntVal(6),
212+
"month_name": cty.StringVal("June"),
213+
"weekday": cty.NumberIntVal(6),
214+
"weekday_name": cty.StringVal("Saturday"),
215+
"hour": cty.NumberIntVal(15),
216+
"minute": cty.NumberIntVal(4),
217+
"second": cty.NumberIntVal(5),
218+
"unix": cty.NumberIntVal(1717254245),
219+
"iso_year": cty.NumberIntVal(2024),
220+
"iso_week": cty.NumberIntVal(22),
221+
}
222+
for k, v := range expected {
223+
require.True(t, got.GetAttr(k).RawEquals(v), "field %s: got %v, want %v", k, got.GetAttr(k), v)
224+
}
225+
226+
_, err = fn.Call([]cty.Value{cty.StringVal("not-a-date")})
227+
require.Error(t, err)
228+
}

docs/bake-stdlib.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ title: Bake standard library functions
7676
| `replace` | Replaces all instances of the given substring in the given string with the given replacement string. |
7777
| `reverse` | Returns the given string with all of its Unicode characters in reverse order. |
7878
| `reverselist` | Returns the given list with its elements in reverse order. |
79+
| `rfc3339parse` | Given an RFC3339 timestamp string, will parse and return an object representation of that date and time. |
7980
| `rsadecrypt` | Decrypts an RSA-encrypted ciphertext. |
8081
| `sanitize` | Replaces all non-alphanumeric characters with a underscore, leaving only characters that are valid for a Bake target name. |
8182
| `sethaselement` | Returns true if the given set contains the given element, or false otherwise. |

0 commit comments

Comments
 (0)