瀏覽代碼

Move config upgrade helper to mautrix-go

Tulir Asokan 3 年之前
父節點
當前提交
42a4839a4e
共有 3 個文件被更改,包括 144 次插入472 次删除
  1. 138 189
      config/upgrade.go
  2. 0 278
      config/upgradehelper.go
  3. 6 5
      main.go

+ 138 - 189
config/upgrade.go

@@ -17,214 +17,163 @@
 package config
 
 import (
-	"fmt"
-	"os"
-	"path"
 	"strings"
 
-	"gopkg.in/yaml.v3"
-
 	"maunium.net/go/mautrix/appservice"
+	up "maunium.net/go/mautrix/util/configupgrade"
 )
 
-func (helper *UpgradeHelper) doUpgrade() {
-	helper.Copy(Str, "homeserver", "address")
-	helper.Copy(Str, "homeserver", "domain")
-	helper.Copy(Bool, "homeserver", "asmux")
-	helper.Copy(Str|Null, "homeserver", "status_endpoint")
-	helper.Copy(Str|Null, "homeserver", "message_send_checkpoint_endpoint")
-	helper.Copy(Bool, "homeserver", "async_media")
+type waUpgrader struct{}
+
+func (wau waUpgrader) GetBase() string {
+	return ExampleConfig
+}
 
-	helper.Copy(Str, "appservice", "address")
-	helper.Copy(Str, "appservice", "hostname")
-	helper.Copy(Int, "appservice", "port")
-	helper.Copy(Str, "appservice", "database", "type")
-	helper.Copy(Str, "appservice", "database", "uri")
-	helper.Copy(Int, "appservice", "database", "max_open_conns")
-	helper.Copy(Int, "appservice", "database", "max_idle_conns")
-	helper.Copy(Str|Null, "appservice", "database", "max_conn_idle_time")
-	helper.Copy(Str|Null, "appservice", "database", "max_conn_lifetime")
-	if prefix, ok := helper.Get(Str, "appservice", "provisioning", "prefix"); ok && strings.HasSuffix(prefix, "/v1") {
-		helper.Set(Str, strings.TrimSuffix(prefix, "/v1"), "appservice", "provisioning", "prefix")
+func (wau waUpgrader) DoUpgrade(helper *up.Helper) {
+	helper.Copy(up.Str, "homeserver", "address")
+	helper.Copy(up.Str, "homeserver", "domain")
+	helper.Copy(up.Bool, "homeserver", "asmux")
+	helper.Copy(up.Str|up.Null, "homeserver", "status_endpoint")
+	helper.Copy(up.Str|up.Null, "homeserver", "message_send_checkpoint_endpoint")
+	helper.Copy(up.Bool, "homeserver", "async_media")
+
+	helper.Copy(up.Str, "appservice", "address")
+	helper.Copy(up.Str, "appservice", "hostname")
+	helper.Copy(up.Int, "appservice", "port")
+	helper.Copy(up.Str, "appservice", "database", "type")
+	helper.Copy(up.Str, "appservice", "database", "uri")
+	helper.Copy(up.Int, "appservice", "database", "max_open_conns")
+	helper.Copy(up.Int, "appservice", "database", "max_idle_conns")
+	helper.Copy(up.Str|up.Null, "appservice", "database", "max_conn_idle_time")
+	helper.Copy(up.Str|up.Null, "appservice", "database", "max_conn_lifetime")
+	if prefix, ok := helper.Get(up.Str, "appservice", "provisioning", "prefix"); ok && strings.HasSuffix(prefix, "/v1") {
+		helper.Set(up.Str, strings.TrimSuffix(prefix, "/v1"), "appservice", "provisioning", "prefix")
 	} else {
-		helper.Copy(Str, "appservice", "provisioning", "prefix")
+		helper.Copy(up.Str, "appservice", "provisioning", "prefix")
 	}
-	if secret, ok := helper.Get(Str, "appservice", "provisioning", "shared_secret"); !ok || secret == "generate" {
+	if secret, ok := helper.Get(up.Str, "appservice", "provisioning", "shared_secret"); !ok || secret == "generate" {
 		sharedSecret := appservice.RandomString(64)
-		helper.Set(Str, sharedSecret, "appservice", "provisioning", "shared_secret")
+		helper.Set(up.Str, sharedSecret, "appservice", "provisioning", "shared_secret")
 	} else {
-		helper.Copy(Str, "appservice", "provisioning", "shared_secret")
+		helper.Copy(up.Str, "appservice", "provisioning", "shared_secret")
 	}
-	helper.Copy(Str, "appservice", "id")
-	helper.Copy(Str, "appservice", "bot", "username")
-	helper.Copy(Str, "appservice", "bot", "displayname")
-	helper.Copy(Str, "appservice", "bot", "avatar")
-	helper.Copy(Bool, "appservice", "ephemeral_events")
-	helper.Copy(Str, "appservice", "as_token")
-	helper.Copy(Str, "appservice", "hs_token")
-
-	helper.Copy(Str|Null, "segment_key")
-
-	helper.Copy(Bool, "metrics", "enabled")
-	helper.Copy(Str, "metrics", "listen")
-
-	helper.Copy(Str, "whatsapp", "os_name")
-	helper.Copy(Str, "whatsapp", "browser_name")
-
-	helper.Copy(Str, "bridge", "username_template")
-	helper.Copy(Str, "bridge", "displayname_template")
-	helper.Copy(Bool, "bridge", "personal_filtering_spaces")
-	helper.Copy(Bool, "bridge", "delivery_receipts")
-	helper.Copy(Int, "bridge", "portal_message_buffer")
-	helper.Copy(Bool, "bridge", "call_start_notices")
-	helper.Copy(Bool, "bridge", "identity_change_notices")
-	helper.Copy(Bool, "bridge", "history_sync", "create_portals")
-	helper.Copy(Bool, "bridge", "history_sync", "backfill")
-	helper.Copy(Bool, "bridge", "history_sync", "double_puppet_backfill")
-	helper.Copy(Bool, "bridge", "history_sync", "request_full_sync")
-	helper.Copy(Bool, "bridge", "history_sync", "media_requests", "auto_request_media")
-	helper.Copy(Str, "bridge", "history_sync", "media_requests", "request_method")
-	helper.Copy(Int, "bridge", "history_sync", "media_requests", "request_local_time")
-	helper.Copy(Int, "bridge", "history_sync", "max_initial_conversations")
-	helper.Copy(Int, "bridge", "history_sync", "immediate", "worker_count")
-	helper.Copy(Int, "bridge", "history_sync", "immediate", "max_events")
-	helper.Copy(List, "bridge", "history_sync", "deferred")
-	helper.Copy(Bool, "bridge", "user_avatar_sync")
-	helper.Copy(Bool, "bridge", "bridge_matrix_leave")
-	helper.Copy(Bool, "bridge", "sync_with_custom_puppets")
-	helper.Copy(Bool, "bridge", "sync_direct_chat_list")
-	helper.Copy(Bool, "bridge", "default_bridge_receipts")
-	helper.Copy(Bool, "bridge", "default_bridge_presence")
-	helper.Copy(Bool, "bridge", "send_presence_on_typing")
-	helper.Copy(Bool, "bridge", "force_active_delivery_receipts")
-	helper.Copy(Map, "bridge", "double_puppet_server_map")
-	helper.Copy(Bool, "bridge", "double_puppet_allow_discovery")
-	if legacySecret, ok := helper.Get(Str, "bridge", "login_shared_secret"); ok && len(legacySecret) > 0 {
+	helper.Copy(up.Str, "appservice", "id")
+	helper.Copy(up.Str, "appservice", "bot", "username")
+	helper.Copy(up.Str, "appservice", "bot", "displayname")
+	helper.Copy(up.Str, "appservice", "bot", "avatar")
+	helper.Copy(up.Bool, "appservice", "ephemeral_events")
+	helper.Copy(up.Str, "appservice", "as_token")
+	helper.Copy(up.Str, "appservice", "hs_token")
+
+	helper.Copy(up.Str|up.Null, "segment_key")
+
+	helper.Copy(up.Bool, "metrics", "enabled")
+	helper.Copy(up.Str, "metrics", "listen")
+
+	helper.Copy(up.Str, "whatsapp", "os_name")
+	helper.Copy(up.Str, "whatsapp", "browser_name")
+
+	helper.Copy(up.Str, "bridge", "username_template")
+	helper.Copy(up.Str, "bridge", "displayname_template")
+	helper.Copy(up.Bool, "bridge", "personal_filtering_spaces")
+	helper.Copy(up.Bool, "bridge", "delivery_receipts")
+	helper.Copy(up.Int, "bridge", "portal_message_buffer")
+	helper.Copy(up.Bool, "bridge", "call_start_notices")
+	helper.Copy(up.Bool, "bridge", "identity_change_notices")
+	helper.Copy(up.Bool, "bridge", "history_sync", "create_portals")
+	helper.Copy(up.Bool, "bridge", "history_sync", "backfill")
+	helper.Copy(up.Bool, "bridge", "history_sync", "double_puppet_backfill")
+	helper.Copy(up.Bool, "bridge", "history_sync", "request_full_sync")
+	helper.Copy(up.Bool, "bridge", "history_sync", "media_requests", "auto_request_media")
+	helper.Copy(up.Str, "bridge", "history_sync", "media_requests", "request_method")
+	helper.Copy(up.Int, "bridge", "history_sync", "media_requests", "request_local_time")
+	helper.Copy(up.Int, "bridge", "history_sync", "max_initial_conversations")
+	helper.Copy(up.Int, "bridge", "history_sync", "immediate", "worker_count")
+	helper.Copy(up.Int, "bridge", "history_sync", "immediate", "max_events")
+	helper.Copy(up.List, "bridge", "history_sync", "deferred")
+	helper.Copy(up.Bool, "bridge", "user_avatar_sync")
+	helper.Copy(up.Bool, "bridge", "bridge_matrix_leave")
+	helper.Copy(up.Bool, "bridge", "sync_with_custom_puppets")
+	helper.Copy(up.Bool, "bridge", "sync_direct_chat_list")
+	helper.Copy(up.Bool, "bridge", "default_bridge_receipts")
+	helper.Copy(up.Bool, "bridge", "default_bridge_presence")
+	helper.Copy(up.Bool, "bridge", "send_presence_on_typing")
+	helper.Copy(up.Bool, "bridge", "force_active_delivery_receipts")
+	helper.Copy(up.Map, "bridge", "double_puppet_server_map")
+	helper.Copy(up.Bool, "bridge", "double_puppet_allow_discovery")
+	if legacySecret, ok := helper.Get(up.Str, "bridge", "login_shared_secret"); ok && len(legacySecret) > 0 {
 		baseNode := helper.GetBaseNode("bridge", "login_shared_secret_map")
-		baseNode.Map[helper.GetBase("homeserver", "domain")] = YAMLNode{Node: makeStringNode(legacySecret)}
-		baseNode.Content = baseNode.Map.toNodes()
+		baseNode.Map[helper.GetBase("homeserver", "domain")] = up.StringNode(legacySecret)
+		baseNode.UpdateContent()
 	} else {
-		helper.Copy(Map, "bridge", "login_shared_secret_map")
+		helper.Copy(up.Map, "bridge", "login_shared_secret_map")
 	}
-	helper.Copy(Bool, "bridge", "private_chat_portal_meta")
-	helper.Copy(Bool, "bridge", "bridge_notices")
-	helper.Copy(Bool, "bridge", "resend_bridge_info")
-	helper.Copy(Bool, "bridge", "mute_bridging")
-	helper.Copy(Str|Null, "bridge", "archive_tag")
-	helper.Copy(Str|Null, "bridge", "pinned_tag")
-	helper.Copy(Bool, "bridge", "tag_only_on_create")
-	helper.Copy(Bool, "bridge", "enable_status_broadcast")
-	helper.Copy(Bool, "bridge", "mute_status_broadcast")
-	helper.Copy(Str|Null, "bridge", "status_broadcast_tag")
-	helper.Copy(Bool, "bridge", "whatsapp_thumbnail")
-	helper.Copy(Bool, "bridge", "allow_user_invite")
-	helper.Copy(Str, "bridge", "command_prefix")
-	helper.Copy(Bool, "bridge", "federate_rooms")
-	helper.Copy(Bool, "bridge", "disappearing_messages_in_groups")
-	helper.Copy(Bool, "bridge", "disable_bridge_alerts")
-	helper.Copy(Bool, "bridge", "url_previews")
-	helper.Copy(Str, "bridge", "management_room_text", "welcome")
-	helper.Copy(Str, "bridge", "management_room_text", "welcome_connected")
-	helper.Copy(Str, "bridge", "management_room_text", "welcome_unconnected")
-	helper.Copy(Str|Null, "bridge", "management_room_text", "additional_help")
-	helper.Copy(Bool, "bridge", "encryption", "allow")
-	helper.Copy(Bool, "bridge", "encryption", "default")
-	helper.Copy(Bool, "bridge", "encryption", "key_sharing", "allow")
-	helper.Copy(Bool, "bridge", "encryption", "key_sharing", "require_cross_signing")
-	helper.Copy(Bool, "bridge", "encryption", "key_sharing", "require_verification")
-	helper.Copy(Map, "bridge", "permissions")
-	helper.Copy(Bool, "bridge", "relay", "enabled")
-	helper.Copy(Bool, "bridge", "relay", "admin_only")
-	helper.Copy(Map, "bridge", "relay", "message_formats")
-
-	helper.Copy(Str, "logging", "directory")
-	helper.Copy(Str|Null, "logging", "file_name_format")
-	helper.Copy(Str|Timestamp, "logging", "file_date_format")
-	helper.Copy(Int, "logging", "file_mode")
-	helper.Copy(Str|Timestamp, "logging", "timestamp_format")
-	helper.Copy(Str, "logging", "print_level")
-}
-
-func Mutate(path string, mutate func(helper *UpgradeHelper)) error {
-	_, _, err := upgrade(path, true, mutate)
-	return err
-}
-
-func Upgrade(path string, save bool) ([]byte, bool, error) {
-	return upgrade(path, save, nil)
+	helper.Copy(up.Bool, "bridge", "private_chat_portal_meta")
+	helper.Copy(up.Bool, "bridge", "bridge_notices")
+	helper.Copy(up.Bool, "bridge", "resend_bridge_info")
+	helper.Copy(up.Bool, "bridge", "mute_bridging")
+	helper.Copy(up.Str|up.Null, "bridge", "archive_tag")
+	helper.Copy(up.Str|up.Null, "bridge", "pinned_tag")
+	helper.Copy(up.Bool, "bridge", "tag_only_on_create")
+	helper.Copy(up.Bool, "bridge", "enable_status_broadcast")
+	helper.Copy(up.Bool, "bridge", "mute_status_broadcast")
+	helper.Copy(up.Str|up.Null, "bridge", "status_broadcast_tag")
+	helper.Copy(up.Bool, "bridge", "whatsapp_thumbnail")
+	helper.Copy(up.Bool, "bridge", "allow_user_invite")
+	helper.Copy(up.Str, "bridge", "command_prefix")
+	helper.Copy(up.Bool, "bridge", "federate_rooms")
+	helper.Copy(up.Bool, "bridge", "disappearing_messages_in_groups")
+	helper.Copy(up.Bool, "bridge", "disable_bridge_alerts")
+	helper.Copy(up.Bool, "bridge", "url_previews")
+	helper.Copy(up.Str, "bridge", "management_room_text", "welcome")
+	helper.Copy(up.Str, "bridge", "management_room_text", "welcome_connected")
+	helper.Copy(up.Str, "bridge", "management_room_text", "welcome_unconnected")
+	helper.Copy(up.Str|up.Null, "bridge", "management_room_text", "additional_help")
+	helper.Copy(up.Bool, "bridge", "encryption", "allow")
+	helper.Copy(up.Bool, "bridge", "encryption", "default")
+	helper.Copy(up.Bool, "bridge", "encryption", "key_sharing", "allow")
+	helper.Copy(up.Bool, "bridge", "encryption", "key_sharing", "require_cross_signing")
+	helper.Copy(up.Bool, "bridge", "encryption", "key_sharing", "require_verification")
+	helper.Copy(up.Map, "bridge", "permissions")
+	helper.Copy(up.Bool, "bridge", "relay", "enabled")
+	helper.Copy(up.Bool, "bridge", "relay", "admin_only")
+	helper.Copy(up.Map, "bridge", "relay", "message_formats")
+
+	helper.Copy(up.Str, "logging", "directory")
+	helper.Copy(up.Str|up.Null, "logging", "file_name_format")
+	helper.Copy(up.Str|up.Timestamp, "logging", "file_date_format")
+	helper.Copy(up.Int, "logging", "file_mode")
+	helper.Copy(up.Str|up.Timestamp, "logging", "timestamp_format")
+	helper.Copy(up.Str, "logging", "print_level")
 }
 
-func (helper *UpgradeHelper) addSpaceBeforeComment(path ...string) {
-	node := helper.GetBaseNode(path...)
-	if node.Key == nil {
-		panic(fmt.Errorf("didn't find key at %+v", path))
+func (wau waUpgrader) SpacedBlocks() [][]string {
+	return [][]string{
+		{"homeserver", "asmux"},
+		{"appservice"},
+		{"appservice", "hostname"},
+		{"appservice", "database"},
+		{"appservice", "provisioning"},
+		{"appservice", "id"},
+		{"appservice", "as_token"},
+		{"segment_key"},
+		{"metrics"},
+		{"whatsapp"},
+		{"bridge"},
+		{"bridge", "command_prefix"},
+		{"bridge", "management_room_text"},
+		{"bridge", "encryption"},
+		{"bridge", "permissions"},
+		{"bridge", "relay"},
+		{"logging"},
 	}
-	node.Key.HeadComment = "\n" + node.Key.HeadComment
 }
 
-func (helper *UpgradeHelper) addSpaces() {
-	// The yaml package doesn't preserve blank lines, so re-add them manually where appropriate
-	helper.addSpaceBeforeComment("homeserver", "asmux")
-	helper.addSpaceBeforeComment("appservice")
-	helper.addSpaceBeforeComment("appservice", "hostname")
-	helper.addSpaceBeforeComment("appservice", "database")
-	helper.addSpaceBeforeComment("appservice", "provisioning")
-	helper.addSpaceBeforeComment("appservice", "id")
-	helper.addSpaceBeforeComment("appservice", "as_token")
-	helper.addSpaceBeforeComment("segment_key")
-	helper.addSpaceBeforeComment("metrics")
-	helper.addSpaceBeforeComment("whatsapp")
-	helper.addSpaceBeforeComment("bridge")
-	helper.addSpaceBeforeComment("bridge", "command_prefix")
-	helper.addSpaceBeforeComment("bridge", "management_room_text")
-	helper.addSpaceBeforeComment("bridge", "encryption")
-	helper.addSpaceBeforeComment("bridge", "permissions")
-	helper.addSpaceBeforeComment("bridge", "relay")
-	helper.addSpaceBeforeComment("logging")
+func Mutate(path string, mutate func(helper *up.Helper)) error {
+	_, _, err := up.Do(path, true, waUpgrader{}, up.SimpleUpgrader(mutate))
+	return err
 }
 
-func upgrade(configPath string, save bool, mutate func(helper *UpgradeHelper)) ([]byte, bool, error) {
-	sourceData, err := os.ReadFile(configPath)
-	if err != nil {
-		return nil, false, fmt.Errorf("failed to read config: %w", err)
-	}
-	var base, cfg yaml.Node
-	err = yaml.Unmarshal([]byte(ExampleConfig), &base)
-	if err != nil {
-		return sourceData, false, fmt.Errorf("failed to unmarshal example config: %w", err)
-	}
-	err = yaml.Unmarshal(sourceData, &cfg)
-	if err != nil {
-		return sourceData, false, fmt.Errorf("failed to unmarshal config: %w", err)
-	}
-
-	helper := NewUpgradeHelper(&base, &cfg)
-	helper.doUpgrade()
-	if mutate != nil {
-		mutate(helper)
-	}
-	helper.addSpaces()
-
-	output, err := yaml.Marshal(&base)
-	if err != nil {
-		return sourceData, false, fmt.Errorf("failed to marshal updated config: %w", err)
-	}
-	if save {
-		var tempFile *os.File
-		tempFile, err = os.CreateTemp(path.Dir(configPath), "wa-config-*.yaml")
-		if err != nil {
-			return output, true, fmt.Errorf("failed to create temp file for writing config: %w", err)
-		}
-		_, err = tempFile.Write(output)
-		if err != nil {
-			_ = os.Remove(tempFile.Name())
-			return output, true, fmt.Errorf("failed to write updated config to temp file: %w", err)
-		}
-		err = os.Rename(tempFile.Name(), configPath)
-		if err != nil {
-			_ = os.Remove(tempFile.Name())
-			return output, true, fmt.Errorf("failed to override current config with temp file: %w", err)
-		}
-	}
-	return output, true, nil
+func Upgrade(path string, save bool) ([]byte, bool, error) {
+	return up.Do(path, save, waUpgrader{})
 }

+ 0 - 278
config/upgradehelper.go

@@ -1,278 +0,0 @@
-// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
-// Copyright (C) 2021 Tulir Asokan
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-package config
-
-import (
-	"fmt"
-	"os"
-	"strings"
-
-	"gopkg.in/yaml.v3"
-)
-
-type YAMLMap map[string]YAMLNode
-type YAMLList []YAMLNode
-
-type YAMLNode struct {
-	*yaml.Node
-	Map  YAMLMap
-	List YAMLList
-	Key  *yaml.Node
-}
-
-type YAMLType uint32
-
-const (
-	Null YAMLType = 1 << iota
-	Bool
-	Str
-	Int
-	Float
-	Timestamp
-	List
-	Map
-	Binary
-)
-
-func (t YAMLType) String() string {
-	switch t {
-	case Null:
-		return NullTag
-	case Bool:
-		return BoolTag
-	case Str:
-		return StrTag
-	case Int:
-		return IntTag
-	case Float:
-		return FloatTag
-	case Timestamp:
-		return TimestampTag
-	case List:
-		return SeqTag
-	case Map:
-		return MapTag
-	case Binary:
-		return BinaryTag
-	default:
-		panic(fmt.Errorf("can't convert type %d to string", t))
-	}
-}
-
-func tagToType(tag string) YAMLType {
-	switch tag {
-	case NullTag:
-		return Null
-	case BoolTag:
-		return Bool
-	case StrTag:
-		return Str
-	case IntTag:
-		return Int
-	case FloatTag:
-		return Float
-	case TimestampTag:
-		return Timestamp
-	case SeqTag:
-		return List
-	case MapTag:
-		return Map
-	case BinaryTag:
-		return Binary
-	default:
-		return 0
-	}
-}
-
-const (
-	NullTag      = "!!null"
-	BoolTag      = "!!bool"
-	StrTag       = "!!str"
-	IntTag       = "!!int"
-	FloatTag     = "!!float"
-	TimestampTag = "!!timestamp"
-	SeqTag       = "!!seq"
-	MapTag       = "!!map"
-	BinaryTag    = "!!binary"
-)
-
-func fromNode(node, key *yaml.Node) YAMLNode {
-	switch node.Kind {
-	case yaml.DocumentNode:
-		return fromNode(node.Content[0], nil)
-	case yaml.AliasNode:
-		return fromNode(node.Alias, nil)
-	case yaml.MappingNode:
-		return YAMLNode{
-			Node: node,
-			Map:  parseYAMLMap(node),
-			Key:  key,
-		}
-	case yaml.SequenceNode:
-		return YAMLNode{
-			Node: node,
-			List: parseYAMLList(node),
-		}
-	default:
-		return YAMLNode{Node: node, Key: key}
-	}
-}
-
-func (yn *YAMLNode) toNode() *yaml.Node {
-	switch {
-	case yn.Map != nil && yn.Node.Kind == yaml.MappingNode:
-		yn.Content = yn.Map.toNodes()
-	case yn.List != nil && yn.Node.Kind == yaml.SequenceNode:
-		yn.Content = yn.List.toNodes()
-	}
-	return yn.Node
-}
-
-func parseYAMLList(node *yaml.Node) YAMLList {
-	data := make(YAMLList, len(node.Content))
-	for i, item := range node.Content {
-		data[i] = fromNode(item, nil)
-	}
-	return data
-}
-
-func (yl YAMLList) toNodes() []*yaml.Node {
-	nodes := make([]*yaml.Node, len(yl))
-	for i, item := range yl {
-		nodes[i] = item.toNode()
-	}
-	return nodes
-}
-
-func parseYAMLMap(node *yaml.Node) YAMLMap {
-	if len(node.Content)%2 != 0 {
-		panic(fmt.Errorf("uneven number of items in YAML map (%d)", len(node.Content)))
-	}
-	data := make(YAMLMap, len(node.Content)/2)
-	for i := 0; i < len(node.Content); i += 2 {
-		key := node.Content[i]
-		value := node.Content[i+1]
-		if key.Kind == yaml.ScalarNode {
-			data[key.Value] = fromNode(value, key)
-		}
-	}
-	return data
-}
-
-func (ym YAMLMap) toNodes() []*yaml.Node {
-	nodes := make([]*yaml.Node, len(ym)*2)
-	i := 0
-	for key, value := range ym {
-		nodes[i] = makeStringNode(key)
-		nodes[i+1] = value.toNode()
-		i += 2
-	}
-	return nodes
-}
-
-func makeStringNode(val string) *yaml.Node {
-	var node yaml.Node
-	node.SetString(val)
-	return &node
-}
-
-type UpgradeHelper struct {
-	base YAMLNode
-	cfg  YAMLNode
-}
-
-func NewUpgradeHelper(base, cfg *yaml.Node) *UpgradeHelper {
-	return &UpgradeHelper{
-		base: fromNode(base, nil),
-		cfg:  fromNode(cfg, nil),
-	}
-}
-
-func (helper *UpgradeHelper) Copy(allowedTypes YAMLType, path ...string) {
-	base, cfg := helper.base, helper.cfg
-	var ok bool
-	for _, item := range path {
-		base = base.Map[item]
-		cfg, ok = cfg.Map[item]
-		if !ok {
-			return
-		}
-	}
-	if allowedTypes&tagToType(cfg.Tag) == 0 {
-		_, _ = fmt.Fprintf(os.Stderr, "Ignoring incorrect config field type %s at %s\n", cfg.Tag, strings.Join(path, "->"))
-		return
-	}
-	base.Tag = cfg.Tag
-	base.Style = cfg.Style
-	switch base.Kind {
-	case yaml.ScalarNode:
-		base.Value = cfg.Value
-	case yaml.SequenceNode, yaml.MappingNode:
-		base.Content = cfg.Content
-	}
-}
-
-func getNode(cfg YAMLNode, path []string) *YAMLNode {
-	var ok bool
-	for _, item := range path {
-		cfg, ok = cfg.Map[item]
-		if !ok {
-			return nil
-		}
-	}
-	return &cfg
-}
-
-func (helper *UpgradeHelper) GetNode(path ...string) *YAMLNode {
-	return getNode(helper.cfg, path)
-}
-
-func (helper *UpgradeHelper) GetBaseNode(path ...string) *YAMLNode {
-	return getNode(helper.base, path)
-}
-
-func (helper *UpgradeHelper) Get(tag YAMLType, path ...string) (string, bool) {
-	node := helper.GetNode(path...)
-	if node == nil || node.Kind != yaml.ScalarNode || tag&tagToType(node.Tag) == 0 {
-		return "", false
-	}
-	return node.Value, true
-}
-
-func (helper *UpgradeHelper) GetBase(path ...string) string {
-	return helper.GetBaseNode(path...).Value
-}
-
-func (helper *UpgradeHelper) Set(tag YAMLType, value string, path ...string) {
-	base := helper.base
-	for _, item := range path {
-		base = base.Map[item]
-	}
-	base.Tag = tag.String()
-	base.Value = value
-}
-
-func (helper *UpgradeHelper) SetMap(value YAMLMap, path ...string) {
-	base := helper.base
-	for _, item := range path {
-		base = base.Map[item]
-	}
-	if base.Tag != MapTag || base.Kind != yaml.MappingNode {
-		panic(fmt.Errorf("invalid target for SetMap(%+v): tag:%s, kind:%d", path, base.Tag, base.Kind))
-	}
-	base.Content = value.toNodes()
-}

+ 6 - 5
main.go

@@ -44,6 +44,7 @@ import (
 	"maunium.net/go/mautrix/appservice"
 	"maunium.net/go/mautrix/event"
 	"maunium.net/go/mautrix/id"
+	"maunium.net/go/mautrix/util/configupgrade"
 
 	"maunium.net/go/mautrix-whatsapp/config"
 	"maunium.net/go/mautrix-whatsapp/database"
@@ -126,9 +127,9 @@ func (bridge *Bridge) GenerateRegistration() {
 		os.Exit(21)
 	}
 
-	err = config.Mutate(*configPath, func(helper *config.UpgradeHelper) {
-		helper.Set(config.Str, bridge.Config.AppService.ASToken, "appservice", "as_token")
-		helper.Set(config.Str, bridge.Config.AppService.HSToken, "appservice", "hs_token")
+	err = config.Mutate(*configPath, func(helper *configupgrade.Helper) {
+		helper.Set(configupgrade.Str, bridge.Config.AppService.ASToken, "appservice", "as_token")
+		helper.Set(configupgrade.Str, bridge.Config.AppService.HSToken, "appservice", "hs_token")
 	})
 	if err != nil {
 		_, _ = fmt.Fprintln(os.Stderr, "Failed to save config:", err)
@@ -405,8 +406,8 @@ func (bridge *Bridge) ResendBridgeInfo() {
 	if *dontSaveConfig {
 		bridge.Log.Warnln("Not setting resend_bridge_info to false in config due to --no-update flag")
 	} else {
-		err := config.Mutate(*configPath, func(helper *config.UpgradeHelper) {
-			helper.Set(config.Bool, "false", "bridge", "resend_bridge_info")
+		err := config.Mutate(*configPath, func(helper *configupgrade.Helper) {
+			helper.Set(configupgrade.Bool, "false", "bridge", "resend_bridge_info")
 		})
 		if err != nil {
 			bridge.Log.Errorln("Failed to save config after setting resend_bridge_info to false:", err)