...
This commit is contained in:
2
pkg/heroscript/cmd/herohandler/.gitignore
vendored
Normal file
2
pkg/heroscript/cmd/herohandler/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
herohandler
|
||||
example
|
106
pkg/heroscript/cmd/herohandler/README.md
Normal file
106
pkg/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
pkg/heroscript/cmd/herohandler/example.hero
Normal file
7
pkg/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
pkg/heroscript/cmd/herohandler/internal/example_handler.go
Normal file
102
pkg/heroscript/cmd/herohandler/internal/example_handler.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/freeflowuniverse/heroagent/pkg/handlerfactory"
|
||||
"github.com/freeflowuniverse/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
pkg/heroscript/cmd/herohandler/main.go
Normal file
101
pkg/heroscript/cmd/herohandler/main.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/freeflowuniverse/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