package format import ( "gopkg.in/yaml.v3" ) type ordering struct { props []string children map[string]ordering } var orders = ordering{ props: []string{ "AWSTemplateFormatVersion", "Description", "Metadata", "Parameters", "Rules", "Mappings", "Conditions", "Transform", "Globals", "Resources", "Outputs", }, children: map[string]ordering{ "Transform": { props: []string{}, children: map[string]ordering{ "*": { props: []string{"Name", "Parameters"}, }, }, }, "Metadata": { props: []string{}, children: map[string]ordering{ "*": { props: []string{"Description"}, }, }, }, "Parameters": { props: []string{}, children: map[string]ordering{ "*": { props: []string{"Description", "Type", "AllowedValues", "Default"}, }, }, }, "Resources": { props: []string{}, children: map[string]ordering{ "*": { props: []string{"CreationPolicy", "DeletionPolicy", "UpdatePolicy", "UpdateReplacePolicy", "Type", "DependsOn", "Metadata", "Properties"}, }, }, }, "Outputs": { props: []string{}, children: map[string]ordering{ "*": { props: []string{"Description", "Value", "Export"}, }, }, }, }, } func orderTemplate(node *yaml.Node) *yaml.Node { if node.Kind == yaml.DocumentNode { node = node.Content[0] } return orderNode(node, orders) } type nodeMap struct { keys []*yaml.Node keyMap map[string]*yaml.Node values map[string]*yaml.Node } func toNodeMap(node *yaml.Node) nodeMap { out := nodeMap{ keys: make([]*yaml.Node, 0), keyMap: make(map[string]*yaml.Node), values: make(map[string]*yaml.Node), } if node.Kind != yaml.MappingNode { return out } for i := 0; i < len(node.Content); i += 2 { key := node.Content[i] value := node.Content[i+1] out.keys = append(out.keys, key) out.keyMap[key.Value] = key out.values[key.Value] = value } return out } func orderNode(node *yaml.Node, order ordering) *yaml.Node { if node == nil { return nil } if node.Kind != yaml.MappingNode { return node } // Discover the keys and values nm := toNodeMap(node) // Apply any sub-orderings for key, value := range nm.values { if subOrder, ok := order.children[key]; ok { nm.values[key] = orderNode(value, subOrder) } else if subOrder, ok := order.children["*"]; ok { nm.values[key] = orderNode(value, subOrder) } } // Build the output node out := &yaml.Node{ Kind: yaml.MappingNode, } done := make(map[string]bool) // Place priority keys first for _, prop := range order.props { if child, ok := nm.values[prop]; ok { out.Content = append(out.Content, nm.keyMap[prop]) out.Content = append(out.Content, child) done[prop] = true } } // Place remaining keys for _, key := range nm.keys { if !done[key.Value] { out.Content = append(out.Content, key) out.Content = append(out.Content, nm.values[key.Value]) } } return out }