Files
herolib/lib/data/encoderhero/readme.md
Mahmoud-Emad ca95464115 refactor: Improve struct decoding and skip logic
- Refine `decode_struct` to handle data parsing and error messages
- Enhance `should_skip_field_decode` to cover more skip attribute variations
- Update constants and tests related to struct names
- Adjust `decode_value` to better handle optional types and existing keys
- Modify `decode_struct` to skip optional fields during decoding
2025-10-12 16:35:10 +03:00

238 lines
4.9 KiB
Markdown

# HeroEncoder - Simple Struct Serialization
HeroEncoder provides bidirectional conversion between V structs and HeroScript format.
## Design: Single Level Deep Only
This module is designed for **simple, flat structs** only:
**Supported:**
- Basic types: `int`, `string`, `bool`, `f32`, `f64`, `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`
- Arrays of basic types: `[]string`, `[]int`, etc.
- Time handling: `ourtime.OurTime`
- Embedded structs (for inheritance)
**Not Supported:**
- Nested structs (non-embedded fields)
- Arrays of structs
- Complex nested structures
Use `ourdb` or `json` for complex data structures.
## HeroScript Format
```heroscript
!!define.typename param1:value1 param2:'value with spaces' list:item1,item2,item3
```
or
```heroscript
!!configure.typename param1:value1 param2:'value with spaces'
```
## Basic Usage
### Simple Struct
```v
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import incubaid.herolib.data.encoderhero
import incubaid.herolib.data.ourtime
struct Person {
mut:
name string
age int = 20
active bool
birthday ourtime.OurTime
}
mut person := Person{
name: 'Bob'
age: 25
active: true
birthday: ourtime.new('2000-01-15')!
}
// Encode to heroscript
heroscript := encoderhero.encode[Person](person)!
println(heroscript)
// Output: !!define.person name:Bob age:25 active:true birthday:'2000-01-15 00:00'
// Decode back
person2 := encoderhero.decode[Person](heroscript)!
assert person2.name == person.name
assert person2.age == person.age
```
### Embedded Structs (Inheritance)
```v
import incubaid.herolib.data.encoderhero
import incubaid.herolib.data.ourtime
struct Base {
id int
created ourtime.OurTime
}
struct User {
Base // Embedded struct - fields are flattened
mut:
name string
email string
}
user := User{
id: 123
created: ourtime.now()
name: 'Alice'
email: 'alice@example.com'
}
heroscript := encoderhero.encode[User](user)!
// Output: !!define.user id:123 created:'2024-01-15 10:30' name:Alice email:alice@example.com
decoded := encoderhero.decode[User](heroscript)!
assert decoded.id == user.id
assert decoded.name == user.name
```
### Arrays of Basic Types
```v
struct Config {
mut:
hosts []string
ports []int
name string
}
config := Config{
name: 'prod'
hosts: ['server1.com', 'server2.com', 'server3.com']
ports: [8080, 8081, 8082]
}
heroscript := encoderhero.encode[Config](config)!
// Output: !!define.config name:prod hosts:server1.com,server2.com,server3.com ports:8080,8081,8082
decoded := encoderhero.decode[Config](heroscript)!
assert decoded.hosts.len == 3
assert decoded.ports[0] == 8080
```
### Skip Attributes
Use `@[skip]` to exclude fields from encoding/decoding:
```v
struct MyStruct {
id int
name string
runtime_cache ?&Cache @[skip] // Won't be encoded/decoded
}
```
## Common Use Cases
### Configuration Files
```v
struct PostgresqlClient {
pub mut:
name string = 'default'
user string = 'root'
port int = 5432
host string = 'localhost'
password string
dbname string = 'postgres'
}
// Load from heroscript
script := '!!postgresql_client.configure name:production user:app_user port:5433'
client := encoderhero.decode[PostgresqlClient](script)!
```
### Data Exchange
```v
struct ApiResponse {
mut:
status int
message string
timestamp ourtime.OurTime
errors []string
}
response := ApiResponse{
status: 200
message: 'Success'
timestamp: ourtime.now()
errors: []
}
// Send as heroscript
script := encoderhero.encode[ApiResponse](response)!
```
## Time Handling with OurTime
Always use `incubaid.herolib.data.ourtime.OurTime` for time fields:
```v
import incubaid.herolib.data.ourtime
struct Event {
mut:
name string
start_time ourtime.OurTime
end_time ourtime.OurTime
}
event := Event{
name: 'Meeting'
start_time: ourtime.new('2024-06-15 14:00')!
end_time: ourtime.new('2024-06-15 15:30')!
}
script := encoderhero.encode[Event](event)!
decoded := encoderhero.decode[Event](script)!
// OurTime provides flexible formatting
println(decoded.start_time.str()) // '2024-06-15 14:00'
println(decoded.start_time.day()) // '2024-06-15'
```
## Error Handling
```v
// Decoding with error handling
decoded := encoderhero.decode[MyStruct](heroscript) or {
eprintln('Failed to decode: ${err}')
MyStruct{} // Return default
}
// Encoding should not fail for simple structs
encoded := encoderhero.encode[MyStruct](my_struct)!
```
## Limitations
**For Complex Data Structures, Use:**
- `incubaid.herolib.data.ourdb` - For nested data storage
- V's built-in `json` module - For JSON serialization
- Custom serialization - For specific needs
**This module is optimized for:**
- Configuration files
- Simple data exchange
- Flat data structures
- Heroscript integration