77 "fmt"
88 "strings"
99
10+ "github.com/hashicorp/hcl/v2"
11+ "github.com/hashicorp/hcl/v2/hclsyntax"
12+
1013 "github.com/hashicorp/terraform/internal/tfdiags"
1114)
1215
@@ -284,7 +287,7 @@ func (a AbsActionInstance) UniqueKey() UniqueKey {
284287 return absActionInstanceKey (a .String ())
285288}
286289
287- func (r absActionInstanceKey ) uniqueKeySigil () {}
290+ func (a absActionInstanceKey ) uniqueKeySigil () {}
288291
289292// ConfigAction is the address for an action within the configuration.
290293type ConfigAction struct {
@@ -337,7 +340,7 @@ type AbsActionInvocationInstance struct {
337340 TriggerIndex int
338341
339342 // TriggerBlockSourceRange is the location of the action_trigger block
340- // within the resources lifecyclye block that triggered this action.
343+ // within the resources lifecycle block that triggered this action.
341344 TriggerBlockSourceRange * tfdiags.SourceRange
342345
343346 // ActionReferenceSourceRange is the location of the action reference
@@ -348,3 +351,152 @@ type AbsActionInvocationInstance struct {
348351func (a AbsActionInvocationInstance ) String () string {
349352 return fmt .Sprintf ("%s.%d.%s" , a .TriggeringResource .String (), a .TriggerIndex , a .Action .String ())
350353}
354+
355+ // ParseAbsActionInstanceStr is a helper wrapper around
356+ // ParseAbsActionInstance that takes a string and parses it with the HCL
357+ // native syntax traversal parser before interpreting it.
358+ //
359+ // Error diagnostics are returned if either the parsing fails or the analysis
360+ // of the traversal fails. There is no way for the caller to distinguish the
361+ // two kinds of diagnostics programmatically. If error diagnostics are returned
362+ // the returned address may be incomplete.
363+ //
364+ // Since this function has no context about the source of the given string,
365+ // any returned diagnostics will not have meaningful source location
366+ // information.
367+ func ParseAbsActionInstanceStr (str string ) (AbsActionInstance , tfdiags.Diagnostics ) {
368+ var diags tfdiags.Diagnostics
369+
370+ traversal , parseDiags := hclsyntax .ParseTraversalAbs ([]byte (str ), "" , hcl.Pos {Line : 1 , Column : 1 })
371+ diags = diags .Append (parseDiags )
372+ if parseDiags .HasErrors () {
373+ return AbsActionInstance {}, diags
374+ }
375+
376+ addr , addrDiags := ParseAbsActionInstance (traversal )
377+ diags = diags .Append (addrDiags )
378+ return addr , diags
379+ }
380+
381+ // ParseAbsActionInstance attempts to interpret the given traversal as an
382+ // absolute action instance address, using the same syntax as expected by
383+ // ParseTarget.
384+ //
385+ // If no error diagnostics are returned, the returned target includes the
386+ // address that was extracted and the source range it was extracted from.
387+ //
388+ // If error diagnostics are returned then the AbsResource value is invalid and
389+ // must not be used.
390+ func ParseAbsActionInstance (traversal hcl.Traversal ) (AbsActionInstance , tfdiags.Diagnostics ) {
391+ moduleAddr , remain , diags := parseModuleInstancePrefix (traversal , false )
392+ if diags .HasErrors () {
393+ return AbsActionInstance {}, diags
394+ }
395+
396+ if remain .IsRelative () {
397+ // (relative means that there's either nothing left or what's next isn't an identifier)
398+ diags = diags .Append (& hcl.Diagnostic {
399+ Severity : hcl .DiagError ,
400+ Summary : "Invalid action address" ,
401+ Detail : "Module path must be followed by an action instance address." ,
402+ Subject : remain .SourceRange ().Ptr (),
403+ })
404+ return AbsActionInstance {}, diags
405+ }
406+
407+ if remain .RootName () != "action" {
408+ diags = diags .Append (& hcl.Diagnostic {
409+ Severity : hcl .DiagError ,
410+ Summary : "Invalid address" ,
411+ Detail : "Action address must start with \" action.\" ." ,
412+ Subject : remain [0 ].SourceRange ().Ptr (),
413+ })
414+ return AbsActionInstance {}, diags
415+ }
416+ remain = remain [1 :]
417+
418+ if len (remain ) < 2 {
419+ diags = diags .Append (& hcl.Diagnostic {
420+ Severity : hcl .DiagError ,
421+ Summary : "Invalid address" ,
422+ Detail : "Action specification must include an action type and name." ,
423+ Subject : remain .SourceRange ().Ptr (),
424+ })
425+ return AbsActionInstance {}, diags
426+ }
427+
428+ var actionType , name string
429+ switch tt := remain [0 ].(type ) {
430+ case hcl.TraverseRoot :
431+ actionType = tt .Name
432+ case hcl.TraverseAttr :
433+ actionType = tt .Name
434+ default :
435+ diags = diags .Append (& hcl.Diagnostic {
436+ Severity : hcl .DiagError ,
437+ Summary : "Invalid action address" ,
438+ Detail : "An action name is required." ,
439+ Subject : remain [0 ].SourceRange ().Ptr (),
440+ })
441+ return AbsActionInstance {}, diags
442+ }
443+
444+ switch tt := remain [1 ].(type ) {
445+ case hcl.TraverseAttr :
446+ name = tt .Name
447+ default :
448+ diags = diags .Append (& hcl.Diagnostic {
449+ Severity : hcl .DiagError ,
450+ Summary : "Invalid address" ,
451+ Detail : "An action name is required." ,
452+ Subject : remain [1 ].SourceRange ().Ptr (),
453+ })
454+ return AbsActionInstance {}, diags
455+ }
456+
457+ remain = remain [2 :]
458+ switch len (remain ) {
459+ case 0 :
460+ return moduleAddr .ActionInstance (actionType , name , NoKey ), diags
461+ case 1 :
462+ switch tt := remain [0 ].(type ) {
463+ case hcl.TraverseIndex :
464+ key , err := ParseInstanceKey (tt .Key )
465+ if err != nil {
466+ diags = diags .Append (& hcl.Diagnostic {
467+ Severity : hcl .DiagError ,
468+ Summary : "Invalid address" ,
469+ Detail : fmt .Sprintf ("Invalid resource instance key: %s." , err ),
470+ Subject : remain [0 ].SourceRange ().Ptr (),
471+ })
472+ return AbsActionInstance {}, diags
473+ }
474+ return moduleAddr .ActionInstance (actionType , name , key ), diags
475+ case hcl.TraverseSplat :
476+ // Not yet supported!
477+ diags = diags .Append (& hcl.Diagnostic {
478+ Severity : hcl .DiagError ,
479+ Summary : "Invalid address" ,
480+ Detail : "Action instance key must be given in square brackets." ,
481+ Subject : remain [0 ].SourceRange ().Ptr (),
482+ })
483+ return AbsActionInstance {}, diags
484+ default :
485+ diags = diags .Append (& hcl.Diagnostic {
486+ Severity : hcl .DiagError ,
487+ Summary : "Invalid address" ,
488+ Detail : "Action instance key must be given in square brackets." ,
489+ Subject : remain [0 ].SourceRange ().Ptr (),
490+ })
491+ return AbsActionInstance {}, diags
492+ }
493+ default :
494+ diags = diags .Append (& hcl.Diagnostic {
495+ Severity : hcl .DiagError ,
496+ Summary : "Invalid address" ,
497+ Detail : "Unexpected extra operators after address." ,
498+ Subject : remain [1 ].SourceRange ().Ptr (),
499+ })
500+ return AbsActionInstance {}, diags
501+ }
502+ }
0 commit comments