...
This commit is contained in:
		
							
								
								
									
										2
									
								
								pkg2/heroscript/cmd/herohandler/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								pkg2/heroscript/cmd/herohandler/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| herohandler | ||||
| example | ||||
							
								
								
									
										106
									
								
								pkg2/heroscript/cmd/herohandler/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								pkg2/heroscript/cmd/herohandler/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| # HeroHandler Example | ||||
|  | ||||
| This package demonstrates how to implement and use a handler for HeroScript in the HeroLauncher project. | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| The HeroHandler example provides a simple key-value store implementation that showcases how to: | ||||
|  | ||||
| 1. Create a custom handler that extends the base handler functionality | ||||
| 2. Implement action methods that can be called via HeroScript | ||||
| 3. Parse parameters from HeroScript actions | ||||
| 4. Process and execute HeroScript commands | ||||
|  | ||||
| ## Project Structure | ||||
|  | ||||
| ``` | ||||
| ./herohandler/ | ||||
| ├── README.md           # This documentation file | ||||
| ├── main.go             # Main executable that uses the example handler | ||||
| └── internal/           # Internal package for the example handler implementation | ||||
|     └── example_handler.go  # Example handler implementation | ||||
| ``` | ||||
|  | ||||
| ## Handler Actions | ||||
|  | ||||
| The example handler supports the following actions: | ||||
|  | ||||
| - `example.set`: Store a key-value pair | ||||
|   - Parameters: `key`, `value` | ||||
| - `example.get`: Retrieve a value by key | ||||
|   - Parameters: `key` | ||||
| - `example.list`: List all stored key-value pairs | ||||
|   - No parameters | ||||
| - `example.delete`: Remove a key-value pair | ||||
|   - Parameters: `key` | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| You can run the example handler using the provided `main.go`: | ||||
|  | ||||
| ```bash | ||||
| # Build the binary | ||||
| cd pkg/heroscript/cmd/herohandler | ||||
| go build -o herohandler | ||||
|  | ||||
| # Set a key-value pair | ||||
| ./herohandler "example.set key:mykey value:myvalue" | ||||
|  | ||||
| # Get a value by key | ||||
| ./herohandler "example.get key:mykey" | ||||
|  | ||||
| # List all stored key-value pairs | ||||
| ./herohandler "example.list" | ||||
|  | ||||
| # Delete a key-value pair | ||||
| ./herohandler "example.delete key:mykey" | ||||
| ``` | ||||
|  | ||||
| ### Important Note on State Persistence | ||||
|  | ||||
| The example handler maintains its key-value store in memory only for the duration of a single command execution. Each time you run the `herohandler` command, a new instance of the handler is created with an empty data store. This is by design to keep the example simple. | ||||
|  | ||||
| In a real-world application, you would typically implement persistence using a database, file storage, or other mechanisms to maintain state between command executions. | ||||
|  | ||||
| ### Multi-Command Example | ||||
|  | ||||
| To execute multiple commands in a single script, you can create a HeroScript file and pass it to the handler. For example: | ||||
|  | ||||
| ```bash | ||||
| # Create a script file | ||||
| cat > example.hero << EOF | ||||
| !!example.set key:user value:john | ||||
| !!example.set key:role value:admin | ||||
| !!example.list | ||||
| EOF | ||||
|  | ||||
| # Run the script | ||||
| cat example.hero | ./herohandler | ||||
| ``` | ||||
|  | ||||
| This would process all commands in a single execution, allowing the in-memory state to be shared between commands. | ||||
|  | ||||
| ## Implementation Details | ||||
|  | ||||
| The example handler demonstrates several important concepts: | ||||
|  | ||||
| 1. **Handler Structure**: The `ExampleHandler` extends the `handlers.BaseHandler` which provides common functionality for all handlers. | ||||
|  | ||||
| 2. **Action Methods**: Each action is implemented as a method on the handler struct (e.g., `Set`, `Get`, `List`, `Delete`). | ||||
|  | ||||
| 3. **Parameter Parsing**: The `ParseParams` method from `BaseHandler` is used to extract parameters from HeroScript. | ||||
|  | ||||
| 4. **Action Execution**: The `Play` method from `BaseHandler` uses reflection to find and call the appropriate method based on the action name. | ||||
|  | ||||
| 5. **In-Memory Storage**: The example handler maintains a simple in-memory key-value store using a map. | ||||
|  | ||||
| ## Extending the Example | ||||
|  | ||||
| To create your own handler: | ||||
|  | ||||
| 1. Create a new struct that embeds the `handlers.BaseHandler` | ||||
| 2. Implement methods for each action your handler will support | ||||
| 3. Create a constructor function that initializes your handler with the appropriate actor name | ||||
| 4. Use the `Play` method to process HeroScript commands | ||||
|  | ||||
| For more complex handlers, you might need to add additional fields to store state or configuration. | ||||
							
								
								
									
										7
									
								
								pkg2/heroscript/cmd/herohandler/example.hero
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								pkg2/heroscript/cmd/herohandler/example.hero
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| !!example.set key:username value:johndoe | ||||
| !!example.set key:email value:john@example.com | ||||
| !!example.set key:role value:admin | ||||
| !!example.list | ||||
| !!example.get key:username | ||||
| !!example.delete key:email | ||||
| !!example.list | ||||
							
								
								
									
										102
									
								
								pkg2/heroscript/cmd/herohandler/internal/example_handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								pkg2/heroscript/cmd/herohandler/internal/example_handler.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| package internal | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory" | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/handlers" | ||||
| ) | ||||
|  | ||||
| // ExampleHandler handles example actions | ||||
| type ExampleHandler struct { | ||||
| 	handlers.BaseHandler | ||||
| 	data map[string]string | ||||
| } | ||||
|  | ||||
| // NewExampleHandler creates a new example handler | ||||
| func NewExampleHandler() *ExampleHandler { | ||||
| 	return &ExampleHandler{ | ||||
| 		BaseHandler: handlers.BaseHandler{ | ||||
| 			BaseHandler: handlerfactory.BaseHandler{ | ||||
| 				ActorName: "example", | ||||
| 			}, | ||||
| 		}, | ||||
| 		data: make(map[string]string), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Set handles the example.set action | ||||
| func (h *ExampleHandler) Set(script string) string { | ||||
| 	params, err := h.BaseHandler.ParseParams(script) | ||||
| 	if err != nil { | ||||
| 		return fmt.Sprintf("Error parsing parameters: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	key := params.Get("key") | ||||
| 	if key == "" { | ||||
| 		return "Error: key is required" | ||||
| 	} | ||||
|  | ||||
| 	value := params.Get("value") | ||||
| 	if value == "" { | ||||
| 		return "Error: value is required" | ||||
| 	} | ||||
|  | ||||
| 	h.data[key] = value | ||||
| 	return fmt.Sprintf("Set %s = %s", key, value) | ||||
| } | ||||
|  | ||||
| // Get handles the example.get action | ||||
| func (h *ExampleHandler) Get(script string) string { | ||||
| 	params, err := h.BaseHandler.ParseParams(script) | ||||
| 	if err != nil { | ||||
| 		return fmt.Sprintf("Error parsing parameters: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	key := params.Get("key") | ||||
| 	if key == "" { | ||||
| 		return "Error: key is required" | ||||
| 	} | ||||
|  | ||||
| 	value, exists := h.data[key] | ||||
| 	if !exists { | ||||
| 		return fmt.Sprintf("Key '%s' not found", key) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf("%s = %s", key, value) | ||||
| } | ||||
|  | ||||
| // List handles the example.list action | ||||
| func (h *ExampleHandler) List(script string) string { | ||||
| 	if len(h.data) == 0 { | ||||
| 		return "No data stored" | ||||
| 	} | ||||
|  | ||||
| 	var result string | ||||
| 	for key, value := range h.data { | ||||
| 		result += fmt.Sprintf("%s = %s\n", key, value) | ||||
| 	} | ||||
|  | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| // Delete handles the example.delete action | ||||
| func (h *ExampleHandler) Delete(script string) string { | ||||
| 	params, err := h.BaseHandler.ParseParams(script) | ||||
| 	if err != nil { | ||||
| 		return fmt.Sprintf("Error parsing parameters: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	key := params.Get("key") | ||||
| 	if key == "" { | ||||
| 		return "Error: key is required" | ||||
| 	} | ||||
|  | ||||
| 	_, exists := h.data[key] | ||||
| 	if !exists { | ||||
| 		return fmt.Sprintf("Key '%s' not found", key) | ||||
| 	} | ||||
|  | ||||
| 	delete(h.data, key) | ||||
| 	return fmt.Sprintf("Deleted key '%s'", key) | ||||
| } | ||||
							
								
								
									
										101
									
								
								pkg2/heroscript/cmd/herohandler/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								pkg2/heroscript/cmd/herohandler/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"git.ourworld.tf/herocode/heroagent/pkg/heroscript/cmd/herohandler/internal" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// Create a new example handler | ||||
| 	handler := internal.NewExampleHandler() | ||||
|  | ||||
| 	// Check if input is coming from stdin (piped input) | ||||
| 	stat, _ := os.Stdin.Stat() | ||||
| 	if (stat.Mode() & os.ModeCharDevice) == 0 { | ||||
| 		// Reading from stdin (pipe or redirect) | ||||
| 		processStdin(handler) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Check if there are command-line arguments | ||||
| 	if len(os.Args) < 2 { | ||||
| 		printUsage() | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Get the command from arguments | ||||
| 	command := strings.Join(os.Args[1:], " ") | ||||
|  | ||||
| 	// Format as proper HeroScript with !! prefix if not already prefixed | ||||
| 	script := command | ||||
| 	if !strings.HasPrefix(script, "!!") { | ||||
| 		script = "!!" + script | ||||
| 	} | ||||
|  | ||||
| 	// Process the script | ||||
| 	result, err := handler.Play(script, handler) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error: %v\n", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Print the result | ||||
| 	fmt.Println(result) | ||||
| } | ||||
|  | ||||
| func printUsage() { | ||||
| 	fmt.Println("Usage: herohandler <action>") | ||||
| 	fmt.Println("       cat script.hero | herohandler") | ||||
| 	fmt.Println("\nExample commands:") | ||||
| 	fmt.Println("  example.set key:mykey value:myvalue") | ||||
| 	fmt.Println("  example.get key:mykey") | ||||
| 	fmt.Println("  example.list") | ||||
| 	fmt.Println("  example.delete key:mykey") | ||||
| 	fmt.Println("\nNote: The command will be automatically formatted as HeroScript with !! prefix.") | ||||
| 	fmt.Println("      You can also pipe a multi-line HeroScript file to process multiple commands.") | ||||
| } | ||||
|  | ||||
| // processStdin reads and processes HeroScript from stdin | ||||
| func processStdin(handler *internal.ExampleHandler) { | ||||
| 	reader := bufio.NewReader(os.Stdin) | ||||
| 	var scriptBuilder strings.Builder | ||||
|  | ||||
| 	// Read all lines from stdin | ||||
| 	for { | ||||
| 		line, err := reader.ReadString('\n') | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			fmt.Printf("Error reading from stdin: %v\n", err) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// Add the line to our script | ||||
| 		scriptBuilder.WriteString(line) | ||||
|  | ||||
| 		// If we've reached EOF, break | ||||
| 		if err == io.EOF { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Process the complete script | ||||
| 	script := scriptBuilder.String() | ||||
| 	if script == "" { | ||||
| 		fmt.Println("Error: Empty script") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Process the script | ||||
| 	result, err := handler.Play(script, handler) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error: %v\n", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Print the result | ||||
| 	fmt.Println(result) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user