Skip to content

matus-u/gyml

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⚙️ gyml: Generic YAML Manipulator

GitHub go.mod Go version License: MIT

gyml is a lightweight Go library designed for intuitive and flexible manipulation of YAML files using path-based lookups. Say goodbye to cumbersome struct definitions for every YAML configuration – gyml allows you to access, set, and delete values within any YAML document without needing to know its full structure beforehand.

✨ Features

  • Path-Based Access: Easily target specific YAML nodes using a simple key-based path (e.g., ["database", "connections", "[0]", "port"]).
  • Dynamic Type Handling: Automatically handles different YAML node types (maps, sequences, scalars) during read and write operations.
  • Value Setting & Appending: Set new values or append items to sequences, with automatic creation of intermediate nodes if they don't exist.
  • Value Deletion: Remove specific nodes or entire sub-trees from your YAML document.
  • Generic Data Types: Work with any Go data type that can be marshaled/unmarshaled by gopkg.in/yaml.v3.
  • Error Handling: Comprehensive error reporting for invalid paths, out-of-bound indices, and unsupported operations.

🚀 Installation

To integrate gyml into your Go project, simply run the following command:

go get github.com/matus-u/gyml

📚 Usage

Here's how you can use gyml to manipulate your YAML data:

Key Path Examples

The following examples demonstrate how to use key paths to target specific nodes in your YAML structure, as described in the package documentation:

  • SetValue(&root, data, "Company", "CEO") - Sets a value at the path /Company/CEO.
  • SetValue(&root, 35, "some_list", "[]") - Appends an item to the some_list sequence.
  • SetValue(&root, 12, "some_list", "[8]") - Sets a value at index 8 in some_list.
  • GetValue[int](&root, "persons_list", "[10]", "age") - Retrieves the age of the 10th person in persons_list.
  • GetValue[string](&root, "user", "address", "city") - Retrieves a value from /user/address/city.
  • SetValue(&root, true, "settings", "notifications") - Sets a boolean value true at /settings/notifications.
  • SetValue(&root, 3.14, "math", "pi") - Sets a float value 3.14 at /math/pi.
  • DeleteValue(&root, "database", "users", "[0]") - Deletes the first item from the users sequence.
  • DeleteValue(&root, "database", "users") - Deletes the entire users sequence from the database map.
  • DeleteValue(&root, "database", "port") - Deletes the port value from the database map.

Setting Values

Use gyml.SetValue to add or modify values. Intermediate nodes will be created as needed.

package main

import (
	"fmt"
	"log"

	"gopkg.in/yaml.v3"
	"github.com/matus-u/gyml"
)

func main() {
	var root yaml.Node

	// Set a simple scalar value
	err := gyml.SetValue(&root, "My Application", "application", "name")
	if err != nil {
		log.Fatalf("SetValue error: %v", err)
	}
	fmt.Println("YAML after setting application name:", toYAMLString(&root))

	// Set a nested struct
	type DBConfig struct {
		Host string `yaml:"host"`
		Port int    `yaml:"port"`
	}
	err = gyml.SetValue(&root, DBConfig{Host: "db.example.com", Port: 8080}, "application", "database")
	if err != nil {
		log.Fatalf("SetValue error: %v", err)
	}
	fmt.Println("YAML after setting database config:", toYAMLString(&root))

	// Append to a list
	err = gyml.SetValue(&root, "user1", "application", "users", "[]")
	if err != nil {
		log.Fatalf("SetValue error: %v", err)
	}
	err = gyml.SetValue(&root, "user2", "application", "users", "[]")
	if err != nil {
		log.Fatalf("SetValue error: %v", err)
	}
	fmt.Println("YAML after appending users:", toYAMLString(&root))

	// Set a value at a specific index in a list
	err = gyml.SetValue(&root, "user_one_updated", "application", "users", "[0]")
	if err != nil {
		log.Fatalf("SetValue error: %v", err)
	}
	fmt.Println("YAML after updating user at index 0:", toYAMLString(&root))

	// Set a boolean value
	err = gyml.SetValue(&root, true, "application", "settings", "notifications")
	if err != nil {
		log.Fatalf("SetValue error: %v", err)
	}
	fmt.Println("YAML after setting notifications:", toYAMLString(&root))
}

// Helper to convert yaml.Node back to string for printing
func toYAMLString(node *yaml.Node) string {
	if len(node.Content) == 0 {
		return "{}" // Represent empty YAML as an empty map
	}
	data, err := yaml.Marshal(node)
	if err != nil {
		return fmt.Sprintf("Error marshaling: %v", err)
	}
	return string(data)
}

Getting Values

Use gyml.GetValue to retrieve values from your YAML document and deserialize them into Go types.

package main

import (
	"fmt"
	"log"

	"gopkg.in/yaml.v3"
	"github.com/matus-u/gyml"
)

func main() {
	yamlString := `
application:
  name: MyWebApp
  version: 1.0.0
database:
  host: localhost
  port: 5432
  users:
    - name: admin
      role: superuser
    - name: guest
      role: readonly
settings:
  notifications: true
`
	var root yaml.Node
	err := yaml.Unmarshal([]byte(yamlString), &root)
	if err != nil {
		log.Fatalf("Error unmarshaling YAML: %v", err)
	}

	// Get a string value
	appName, err := gyml.GetValue[string](&root, "application", "name")
	if err != nil {
		log.Fatalf("GetValue error for app name: %v", err)
	}
	fmt.Printf("Application Name: %s
", *appName)

	// Get an int value
	dbPort, err := gyml.GetValue[int](&root, "database", "port")
	if err != nil {
		log.Fatalf("GetValue error for database port: %v", err)
	}
	fmt.Printf("Database Port: %d
", *dbPort)

	// Get a boolean value
	notificationsEnabled, err := gyml.GetValue[bool](&root, "settings", "notifications")
	if err != nil {
		log.Fatalf("GetValue error for notifications: %v", err)
	}
	fmt.Printf("Notifications Enabled: %t
", *notificationsEnabled)

	// Get a struct
	type User struct {
		Name string `yaml:"name"`
		Role string `yaml:"role"`
	}
	firstUser, err := gyml.GetValue[User](&root, "database", "users", "[0]")
	if err != nil {
		log.Fatalf("GetValue error for first user: %v", err)
	}
	fmt.Printf("First User: %+v
", *firstUser)

	// Get a slice of strings
	type Application struct {
		Users []string `yaml:"users"`
	}
	// For example, if you set users as just strings.
	// You might need to adjust based on how you set it.
	// usersList, err := gyml.GetValue[[]string](&root, "database", "users")
	// if err != nil {
	// 	log.Fatalf("GetValue error for users list: %v", err)
	// }
	// fmt.Printf("Users List: %v
", *usersList)
}

Deleting Values

Use gyml.DeleteValue to remove nodes from your YAML document.

package main

import (
	"fmt"
	"log"

	"gopkg.in/yaml.v3"
	"github.com/matus-u/gyml"
)

func main() {
	yamlString := `
application:
  name: MyWebApp
  version: 1.0.0
database:
  host: localhost
  port: 5432
  users:
    - name: admin
      role: superuser
    - name: guest
      role: readonly
`
	var root yaml.Node
	err := yaml.Unmarshal([]byte(yamlString), &root)
	if err != nil {
		log.Fatalf("Error unmarshaling YAML: %v", err)
	}
	fmt.Println("Original YAML:", toYAMLString(&root))

	// Delete a specific key
	err = gyml.DeleteValue(&root, "application", "version")
	if err != nil {
		log.Fatalf("DeleteValue error: %v", err)
	}
	fmt.Println("YAML after deleting application version:", toYAMLString(&root))

	// Delete an item from a sequence by index
	err = gyml.DeleteValue(&root, "database", "users", "[0]")
	if err != nil {
		log.Fatalf("DeleteValue error: %v", err)
	}
	fmt.Println("YAML after deleting first user:", toYAMLString(&root))

	// Delete an entire sub-map
	err = gyml.DeleteValue(&root, "database", "host") // Delete host only
	if err != nil {
		log.Fatalf("DeleteValue error: %v", err)
	}
	fmt.Println("YAML after deleting database host:", toYAMLString(&root))

	err = gyml.DeleteValue(&root, "database", "port") // Delete port, now database map is empty
	if err != nil {
		log.Fatalf("DeleteValue error: %v", err)
	}
	fmt.Println("YAML after deleting database port (database should be gone if empty):", toYAMLString(&root))
}

// Helper to convert yaml.Node back to string for printing
func toYAMLString(node *yaml.Node) string {
	if len(node.Content) == 0 {
		return "{}" // Represent empty YAML as an empty map
	}
	data, err := yaml.Marshal(node)
	if err != nil {
		return fmt.Sprintf("Error marshaling: %v", err)
	}
	return string(data)
}

🤝 Contributing

Contributions are welcome! Please feel free to open issues or submit pull requests.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Generic yaml manipulator library

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages