Merge branch 'development' of github.com:Incubaid/herolib into development
* 'development' of github.com:Incubaid/herolib: chore: Add FsListArg struct and update imports refactor: Rename module and import path refactor: Use snake_case for object names and constants fix: fix conflicts refactor: Improve struct decoding and skip logic # Conflicts: # libarchive/encoderherocomplex/any_base.v
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
module vcode
|
module vcode
|
||||||
|
|
||||||
import incubaid.herolib.ai.mcp
|
import incubaid.herolib.ai.mcp
|
||||||
import incubaid.herolib.develop.codetools as code
|
import incubaid.herolib.core.code
|
||||||
import incubaid.herolib.schemas.jsonschema
|
import incubaid.herolib.schemas.jsonschema
|
||||||
import x.json2 { Any }
|
import x.json2 { Any }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module codetools
|
module code
|
||||||
|
|
||||||
import incubaid.herolib.ui.console
|
import incubaid.herolib.ui.console
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ module encoderhero
|
|||||||
|
|
||||||
import incubaid.herolib.data.paramsparser
|
import incubaid.herolib.data.paramsparser
|
||||||
import incubaid.herolib.data.ourtime
|
import incubaid.herolib.data.ourtime
|
||||||
|
import incubaid.herolib.core.texttools
|
||||||
|
|
||||||
pub struct Decoder[T] {
|
pub struct Decoder[T] {
|
||||||
pub mut:
|
pub mut:
|
||||||
@@ -17,10 +18,10 @@ pub fn decode[T](data string) !T {
|
|||||||
// Only supports single-level structs (no nested structs or arrays of structs)
|
// Only supports single-level structs (no nested structs or arrays of structs)
|
||||||
fn decode_struct[T](_ T, data string) !T {
|
fn decode_struct[T](_ T, data string) !T {
|
||||||
mut typ := T{}
|
mut typ := T{}
|
||||||
|
|
||||||
$if T is $struct {
|
$if T is $struct {
|
||||||
obj_name := T.name.all_after_last('.').to_lower()
|
obj_name := texttools.snake_case(T.name.all_after_last('.'))
|
||||||
|
|
||||||
// Define possible action name formats to try
|
// Define possible action name formats to try
|
||||||
action_names_to_try := [
|
action_names_to_try := [
|
||||||
'define.${obj_name}',
|
'define.${obj_name}',
|
||||||
@@ -28,15 +29,15 @@ fn decode_struct[T](_ T, data string) !T {
|
|||||||
'${obj_name}.define',
|
'${obj_name}.define',
|
||||||
'${obj_name}.configure',
|
'${obj_name}.configure',
|
||||||
]
|
]
|
||||||
|
|
||||||
mut found_action_name := ''
|
mut found_action_name := ''
|
||||||
mut actions := []string{}
|
mut actions := []string{}
|
||||||
mut action_str := '' // Declare action_str here
|
mut action_str := '' // Declare action_str here
|
||||||
mut params_str := '' // Declare params_str here
|
mut params_str := '' // Declare params_str here
|
||||||
|
|
||||||
// Find the action line
|
// Find the action line
|
||||||
actions_split := data.split('!!')
|
actions_split := data.split('!!')
|
||||||
|
|
||||||
for name_format in action_names_to_try {
|
for name_format in action_names_to_try {
|
||||||
actions = actions_split.filter(it.contains(name_format))
|
actions = actions_split.filter(it.contains(name_format))
|
||||||
if actions.len > 0 {
|
if actions.len > 0 {
|
||||||
@@ -44,24 +45,24 @@ fn decode_struct[T](_ T, data string) !T {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if found_action_name == '' {
|
if found_action_name == '' {
|
||||||
return error('Data does not contain expected action format for ${obj_name}\nData: ${data}')
|
return error('Data does not contain expected action format for ${obj_name}\nData: ${data}')
|
||||||
}
|
}
|
||||||
|
|
||||||
if actions.len > 1 {
|
if actions.len > 1 {
|
||||||
return error('Multiple actions found for ${found_action_name}. Only single-level structs supported.')
|
return error('Multiple actions found for ${found_action_name}. Only single-level structs supported.')
|
||||||
}
|
}
|
||||||
|
|
||||||
action_str = actions[0]
|
action_str = actions[0]
|
||||||
params_str = action_str.all_after(found_action_name).trim_space()
|
params_str = action_str.all_after(found_action_name).trim_space()
|
||||||
params := paramsparser.parse(params_str) or {
|
params := paramsparser.parse(params_str) or {
|
||||||
return error('Could not parse params: ${params_str}\n${err}')
|
return error('Could not parse params: ${params_str}\n${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode all fields (paramsparser.decode handles embedded structs)
|
// Decode all fields (paramsparser.decode handles embedded structs)
|
||||||
typ = params.decode[T](typ)!
|
typ = params.decode[T](typ)!
|
||||||
|
|
||||||
// Validate no nested structs or struct arrays in the decoded type
|
// Validate no nested structs or struct arrays in the decoded type
|
||||||
$for field in T.fields {
|
$for field in T.fields {
|
||||||
if !should_skip_field_decode(field.attrs) {
|
if !should_skip_field_decode(field.attrs) {
|
||||||
@@ -79,11 +80,10 @@ fn decode_struct[T](_ T, data string) !T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} $else {
|
} $else {
|
||||||
return error("The type `${T.name}` can't be decoded. Only structs are supported.")
|
return error("The type `${T.name}` can't be decoded. Only structs are supported.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,16 +91,12 @@ fn decode_struct[T](_ T, data string) !T {
|
|||||||
fn should_skip_field_decode(attrs []string) bool {
|
fn should_skip_field_decode(attrs []string) bool {
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
attr_clean := attr.to_lower().replace(' ', '').replace('\t', '')
|
attr_clean := attr.to_lower().replace(' ', '').replace('\t', '')
|
||||||
if attr_clean == 'skip'
|
if attr_clean == 'skip' || attr_clean.starts_with('skip;') || attr_clean.ends_with(';skip')
|
||||||
|| attr_clean.starts_with('skip;')
|
|| attr_clean.contains(';skip;') || attr_clean == 'skipdecode'
|
||||||
|| attr_clean.ends_with(';skip')
|
|| attr_clean.starts_with('skipdecode;') || attr_clean.ends_with(';skipdecode')
|
||||||
|| attr_clean.contains(';skip;')
|
|
||||||
|| attr_clean == 'skipdecode'
|
|
||||||
|| attr_clean.starts_with('skipdecode;')
|
|
||||||
|| attr_clean.ends_with(';skipdecode')
|
|
||||||
|| attr_clean.contains(';skipdecode;') {
|
|| attr_clean.contains(';skipdecode;') {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ pub struct TestStruct {
|
|||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
const blank_script = '!!define.teststruct'
|
const blank_script = '!!define.test_struct'
|
||||||
const full_script = '!!define.teststruct id:42 name:testobject'
|
const full_script = '!!define.test_struct id:42 name:testobject'
|
||||||
const invalid_script = '!!define.another_struct'
|
const invalid_script = '!!define.another_struct'
|
||||||
|
|
||||||
fn test_decode_simple() ! {
|
fn test_decode_simple() ! {
|
||||||
@@ -35,7 +35,7 @@ pub struct ConfigStruct {
|
|||||||
hosts []string
|
hosts []string
|
||||||
}
|
}
|
||||||
|
|
||||||
const config_script = "!!define.configstruct name:production enabled:true timeout:60 hosts:'host1.com,host2.com,host3.com'"
|
const config_script = "!!define.config_struct name:production enabled:true timeout:60 hosts:'host1.com,host2.com,host3.com'"
|
||||||
|
|
||||||
fn test_decode_with_arrays() ! {
|
fn test_decode_with_arrays() ! {
|
||||||
object := decode[ConfigStruct](config_script)!
|
object := decode[ConfigStruct](config_script)!
|
||||||
@@ -52,7 +52,7 @@ pub struct Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
Base // Embedded struct
|
Base // Embedded struct
|
||||||
mut:
|
mut:
|
||||||
name string
|
name string
|
||||||
age int
|
age int
|
||||||
@@ -95,4 +95,4 @@ fn test_decode_configure_format() ! {
|
|||||||
assert object.id == 2
|
assert object.id == 2
|
||||||
assert object.name == 'Alice'
|
assert object.name == 'Alice'
|
||||||
assert object.age == 30
|
assert object.age == 30
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ module encoderhero
|
|||||||
|
|
||||||
import incubaid.herolib.data.paramsparser
|
import incubaid.herolib.data.paramsparser
|
||||||
import incubaid.herolib.data.ourtime
|
import incubaid.herolib.data.ourtime
|
||||||
|
import incubaid.herolib.core.texttools
|
||||||
import v.reflection
|
import v.reflection
|
||||||
|
|
||||||
// Encoder encodes a struct into HEROSCRIPT representation.
|
// Encoder encodes a struct into HEROSCRIPT representation.
|
||||||
@@ -42,10 +43,10 @@ pub fn (mut e Encoder) encode_struct[T](t T) ! {
|
|||||||
mut mytype := reflection.type_of[T](t)
|
mut mytype := reflection.type_of[T](t)
|
||||||
struct_attrs := attrs_get_reflection(mytype)
|
struct_attrs := attrs_get_reflection(mytype)
|
||||||
|
|
||||||
mut action_name := T.name.all_after_last('.').to_lower()
|
mut action_name := texttools.snake_case(T.name.all_after_last('.'))
|
||||||
|
|
||||||
if 'alias' in struct_attrs {
|
if 'alias' in struct_attrs {
|
||||||
action_name = struct_attrs['alias'].to_lower()
|
action_name = texttools.snake_case(struct_attrs['alias'])
|
||||||
}
|
}
|
||||||
e.action_names << action_name.to_lower()
|
e.action_names << action_name.to_lower()
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ pub fn (mut e Encoder) encode_struct[T](t T) ! {
|
|||||||
$for field in T.fields {
|
$for field in T.fields {
|
||||||
if !should_skip_field(field.attrs) {
|
if !should_skip_field(field.attrs) {
|
||||||
val := t.$(field.name)
|
val := t.$(field.name)
|
||||||
|
|
||||||
// Check for unsupported nested structs (non-embedded, non-time)
|
// Check for unsupported nested structs (non-embedded, non-time)
|
||||||
$if val is $struct {
|
$if val is $struct {
|
||||||
$if val !is ourtime.OurTime {
|
$if val !is ourtime.OurTime {
|
||||||
@@ -81,11 +82,8 @@ pub fn (mut e Encoder) encode_struct[T](t T) ! {
|
|||||||
fn should_skip_field(attrs []string) bool {
|
fn should_skip_field(attrs []string) bool {
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
attr_clean := attr.to_lower().replace(' ', '').replace('\t', '')
|
attr_clean := attr.to_lower().replace(' ', '').replace('\t', '')
|
||||||
if attr_clean == 'skip'
|
if attr_clean == 'skip' || attr_clean.starts_with('skip;') || attr_clean.ends_with(';skip')
|
||||||
|| attr_clean.starts_with('skip;')
|
|| attr_clean.contains(';skip;') {
|
||||||
|| attr_clean.ends_with(';skip')
|
|
||||||
|| attr_clean.contains(';skip;')
|
|
||||||
{
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,4 +96,4 @@ fn is_struct_array[U](arr []U) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ fn test_encode_skip() ! {
|
|||||||
|
|
||||||
fn test_encode_skip_multiple_attrs() ! {
|
fn test_encode_skip_multiple_attrs() ! {
|
||||||
struct SkipTest {
|
struct SkipTest {
|
||||||
id int
|
id int
|
||||||
name string
|
name string
|
||||||
skip1 string @[skip]
|
skip1 string @[skip]
|
||||||
skip2 int @[skip; other]
|
skip2 int @[other; skip]
|
||||||
skip3 bool @[skipdecode]
|
skip3 bool @[skipdecode]
|
||||||
}
|
}
|
||||||
|
|
||||||
obj := SkipTest{
|
obj := SkipTest{
|
||||||
id: 1
|
id: 1
|
||||||
name: 'test'
|
name: 'test'
|
||||||
@@ -46,12 +46,12 @@ fn test_encode_skip_multiple_attrs() ! {
|
|||||||
skip2: 999
|
skip2: 999
|
||||||
skip3: true
|
skip3: true
|
||||||
}
|
}
|
||||||
|
|
||||||
script := encode[SkipTest](obj)!
|
script := encode[SkipTest](obj)!
|
||||||
|
|
||||||
assert script.contains('id:1')
|
assert script.contains('id:1')
|
||||||
assert script.contains('name:test')
|
assert script.contains('name:test')
|
||||||
assert !script.contains('skip1')
|
assert !script.contains('skip1')
|
||||||
assert !script.contains('skip2')
|
assert !script.contains('skip2')
|
||||||
assert script.contains('skip3')
|
assert script.contains('skip3')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ pub struct Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
Base // Embedded struct
|
Base // Embedded struct
|
||||||
mut:
|
mut:
|
||||||
name string
|
name string
|
||||||
age int = 20
|
age int = 20
|
||||||
@@ -40,10 +40,10 @@ fn test_encode_with_arrays() ! {
|
|||||||
age: 30
|
age: 30
|
||||||
tags: ['developer', 'manager', 'team-lead']
|
tags: ['developer', 'manager', 'team-lead']
|
||||||
}
|
}
|
||||||
|
|
||||||
script := encode[Person](p)!
|
script := encode[Person](p)!
|
||||||
assert script.contains('tags:developer,manager,team-lead')
|
assert script.contains('tags:developer,manager,team-lead')
|
||||||
|
|
||||||
// Roundtrip test
|
// Roundtrip test
|
||||||
decoded := decode[Person](script)!
|
decoded := decode[Person](script)!
|
||||||
assert decoded.tags.len == 3
|
assert decoded.tags.len == 3
|
||||||
@@ -55,9 +55,9 @@ fn test_encode_defaults() ! {
|
|||||||
id: 3
|
id: 3
|
||||||
name: 'Charlie'
|
name: 'Charlie'
|
||||||
}
|
}
|
||||||
|
|
||||||
script := encode[Person](minimal)!
|
script := encode[Person](minimal)!
|
||||||
|
|
||||||
// Should include default values
|
// Should include default values
|
||||||
assert script.contains('age:20')
|
assert script.contains('age:20')
|
||||||
assert script.contains('active:true')
|
assert script.contains('active:true')
|
||||||
@@ -80,15 +80,15 @@ fn test_encode_config() ! {
|
|||||||
servers: ['srv1.com', 'srv2.com']
|
servers: ['srv1.com', 'srv2.com']
|
||||||
ports: [8080, 8081]
|
ports: [8080, 8081]
|
||||||
}
|
}
|
||||||
|
|
||||||
script := encode[Config](config)!
|
script := encode[Config](config)!
|
||||||
|
|
||||||
assert script.contains('name:production')
|
assert script.contains('name:production')
|
||||||
assert script.contains('timeout:300')
|
assert script.contains('timeout:300')
|
||||||
assert script.contains('enabled:true')
|
assert script.contains('enabled:true')
|
||||||
assert script.contains('servers:srv1.com,srv2.com')
|
assert script.contains('servers:srv1.com,srv2.com')
|
||||||
assert script.contains('ports:8080,8081')
|
assert script.contains('ports:8080,8081')
|
||||||
|
|
||||||
// Roundtrip
|
// Roundtrip
|
||||||
decoded := decode[Config](script)!
|
decoded := decode[Config](script)!
|
||||||
assert decoded.name == config.name
|
assert decoded.name == config.name
|
||||||
@@ -106,10 +106,10 @@ fn test_roundtrip() ! {
|
|||||||
active: false
|
active: false
|
||||||
tags: ['tag1', 'tag2']
|
tags: ['tag1', 'tag2']
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded := encode[Person](original)!
|
encoded := encode[Person](original)!
|
||||||
decoded := decode[Person](encoded)!
|
decoded := decode[Person](encoded)!
|
||||||
|
|
||||||
assert decoded.id == original.id
|
assert decoded.id == original.id
|
||||||
assert decoded.version == original.version
|
assert decoded.version == original.version
|
||||||
assert decoded.name == original.name
|
assert decoded.name == original.name
|
||||||
@@ -130,10 +130,12 @@ pub struct NestedParent {
|
|||||||
|
|
||||||
fn test_encode_nested_fails() ! {
|
fn test_encode_nested_fails() ! {
|
||||||
parent := NestedParent{
|
parent := NestedParent{
|
||||||
name: 'parent'
|
name: 'parent'
|
||||||
child: NestedChild{value: 'test'}
|
child: NestedChild{
|
||||||
|
value: 'test'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encode[NestedParent](parent) or {
|
encode[NestedParent](parent) or {
|
||||||
assert err.msg().contains('Nested structs are not supported')
|
assert err.msg().contains('Nested structs are not supported')
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ pub mut:
|
|||||||
dbname string = 'postgres'
|
dbname string = 'postgres'
|
||||||
}
|
}
|
||||||
|
|
||||||
const postgres_client_blank = '!!define.postgresqlclient'
|
const postgres_client_blank = '!!define.postgresql_client'
|
||||||
const postgres_client_full = '!!define.postgresqlclient name:production user:app_user port:5433 host:db.example.com password:secret123 dbname:myapp'
|
const postgres_client_full = '!!define.postgresql_client name:production user:app_user port:5433 host:db.example.com password:secret123 dbname:myapp'
|
||||||
const postgres_client_partial = '!!define.postgresqlclient name:dev host:localhost password:devpass'
|
const postgres_client_partial = '!!define.postgresql_client name:dev host:localhost password:devpass'
|
||||||
|
|
||||||
fn test_postgres_client_decode_blank() ! {
|
fn test_postgres_client_decode_blank() ! {
|
||||||
mut client := decode[PostgresqlClient](postgres_client_blank)!
|
mut client := decode[PostgresqlClient](postgres_client_blank)!
|
||||||
@@ -96,4 +96,4 @@ fn test_postgres_client_encode() ! {
|
|||||||
assert decoded.password == client.password
|
assert decoded.password == client.password
|
||||||
assert decoded.dbname == client.dbname
|
assert decoded.dbname == client.dbname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ HeroEncoder provides bidirectional conversion between V structs and HeroScript f
|
|||||||
This module is designed for **simple, flat structs** only:
|
This module is designed for **simple, flat structs** only:
|
||||||
|
|
||||||
✅ **Supported:**
|
✅ **Supported:**
|
||||||
|
|
||||||
- Basic types: `int`, `string`, `bool`, `f32`, `f64`, `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`
|
- Basic types: `int`, `string`, `bool`, `f32`, `f64`, `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`
|
||||||
- Arrays of basic types: `[]string`, `[]int`, etc.
|
- Arrays of basic types: `[]string`, `[]int`, etc.
|
||||||
- Time handling: `ourtime.OurTime`
|
- Time handling: `ourtime.OurTime`
|
||||||
- Embedded structs (for inheritance)
|
- Embedded structs (for inheritance)
|
||||||
|
|
||||||
❌ **Not Supported:**
|
❌ **Not Supported:**
|
||||||
|
|
||||||
- Nested structs (non-embedded fields)
|
- Nested structs (non-embedded fields)
|
||||||
- Arrays of structs
|
- Arrays of structs
|
||||||
- Complex nested structures
|
- Complex nested structures
|
||||||
@@ -222,12 +224,14 @@ encoded := encoderhero.encode[MyStruct](my_struct)!
|
|||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
**For Complex Data Structures, Use:**
|
**For Complex Data Structures, Use:**
|
||||||
|
|
||||||
- `incubaid.herolib.data.ourdb` - For nested data storage
|
- `incubaid.herolib.data.ourdb` - For nested data storage
|
||||||
- V's built-in `json` module - For JSON serialization
|
- V's built-in `json` module - For JSON serialization
|
||||||
- Custom serialization - For specific needs
|
- Custom serialization - For specific needs
|
||||||
|
|
||||||
**This module is optimized for:**
|
**This module is optimized for:**
|
||||||
|
|
||||||
- Configuration files
|
- Configuration files
|
||||||
- Simple data exchange
|
- Simple data exchange
|
||||||
- Flat data structures
|
- Flat data structures
|
||||||
- Heroscript integration
|
- Heroscript integration
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ pub interface Encodable {
|
|||||||
// Constants for internal use
|
// Constants for internal use
|
||||||
const null_in_bytes = 'null'
|
const null_in_bytes = 'null'
|
||||||
const true_in_string = 'true'
|
const true_in_string = 'true'
|
||||||
const false_in_string = 'false'
|
const false_in_string = 'false'
|
||||||
|
|||||||
@@ -22,16 +22,15 @@ pub fn (params Params) decode_struct[T](start T) !T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// println('Field: ${field.name}, should_skip: ${should_skip}, attrs: ${field.attrs}')
|
// println('Field: ${field.name}, should_skip: ${should_skip}, attrs: ${field.attrs}')
|
||||||
if ! should_skip {
|
if !should_skip {
|
||||||
$if field.is_enum {
|
$if field.is_enum {
|
||||||
t.$(field.name) = params.get_int(field.name) or { int(t.$(field.name)) }
|
t.$(field.name) = params.get_int(field.name) or { int(t.$(field.name)) }
|
||||||
} $else {
|
} $else {
|
||||||
// super annoying didn't find other way, then to ignore options
|
// super annoying didn't find other way, then to ignore options
|
||||||
$if field.is_option {
|
$if field.is_option {
|
||||||
// For optional fields, if the key exists, decode it. Otherwise, leave it as none.
|
// For optional fields, skip decoding entirely
|
||||||
if params.exists(field.name) {
|
// They will remain as none (default value)
|
||||||
t.$(field.name) = params.decode_value(t.$(field.name), field.name)!
|
// This avoids type system issues with ?T vs !T
|
||||||
}
|
|
||||||
} $else {
|
} $else {
|
||||||
if field.name[0].is_capital() {
|
if field.name[0].is_capital() {
|
||||||
t.$(field.name) = params.decode_struct(t.$(field.name))!
|
t.$(field.name) = params.decode_struct(t.$(field.name))!
|
||||||
@@ -46,13 +45,9 @@ pub fn (params Params) decode_struct[T](start T) !T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (params Params) decode_value[T](val T, key string) !T {
|
pub fn (params Params) decode_value[T](val T, key string) !T {
|
||||||
$if T is $option {
|
|
||||||
return error("is option")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: handle required fields
|
// TODO: handle required fields
|
||||||
if !params.exists(key) {
|
if !params.exists(key) {
|
||||||
return val // For optional types, this will be `none`. For non-optional, it's the default value.
|
return val // For non-optional types, this is the default value
|
||||||
}
|
}
|
||||||
|
|
||||||
$if T is string {
|
$if T is string {
|
||||||
@@ -89,10 +84,10 @@ pub fn (params Params) decode_value[T](val T, key string) !T {
|
|||||||
child_params := params.get_params(key)!
|
child_params := params.get_params(key)!
|
||||||
child := child_params.decode_struct(T{})!
|
child := child_params.decode_struct(T{})!
|
||||||
return child
|
return child
|
||||||
|
} $else {
|
||||||
|
// For any other type, return the default
|
||||||
|
return val
|
||||||
}
|
}
|
||||||
// If no specific decode path is found, return the default value for T.
|
|
||||||
// For optional types, this will be `none`.
|
|
||||||
return T{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (params Params) get_list_bool(key string) ![]bool {
|
pub fn (params Params) get_list_bool(key string) ![]bool {
|
||||||
@@ -124,8 +119,10 @@ pub fn encode[T](t T, args EncodeArgs) !Params {
|
|||||||
|
|
||||||
// Check each attribute for skip patterns
|
// Check each attribute for skip patterns
|
||||||
for attr in field.attrs {
|
for attr in field.attrs {
|
||||||
attr_clean := attr.to_lower()
|
attr_clean := attr.to_lower().replace(' ', '').replace('\t', '')
|
||||||
if attr_clean.contains('skip') {
|
// During encoding, only skip fields with @[skip], not @[skipdecode]
|
||||||
|
if attr_clean == 'skip' || attr_clean.starts_with('skip;')
|
||||||
|
|| attr_clean.ends_with(';skip') || attr_clean.contains(';skip;') {
|
||||||
should_skip = true
|
should_skip = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,13 @@ pub mut:
|
|||||||
messages []db.MessageArg
|
messages []db.MessageArg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@[params]
|
||||||
|
pub struct FsListArg {
|
||||||
|
pub mut:
|
||||||
|
group_id u32
|
||||||
|
limit int = 100 // Default limit is 100
|
||||||
|
}
|
||||||
|
|
||||||
// get new filesystem, not from the DB
|
// get new filesystem, not from the DB
|
||||||
pub fn (mut self DBFs) new(args FsArg) !Fs {
|
pub fn (mut self DBFs) new(args FsArg) !Fs {
|
||||||
mut o := Fs{
|
mut o := Fs{
|
||||||
|
|||||||
@@ -1,349 +0,0 @@
|
|||||||
module encoderherocomplex
|
|
||||||
|
|
||||||
// import time
|
|
||||||
|
|
||||||
// // i8 uses `Any` as a 16-bit integer.
|
|
||||||
// pub fn (f Any) i8() i8 {
|
|
||||||
// match f {
|
|
||||||
// i8 {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// i16, i32, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
|
||||||
// return i8(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.i8()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // i16 uses `Any` as a 16-bit integer.
|
|
||||||
// pub fn (f Any) i16() i16 {
|
|
||||||
// match f {
|
|
||||||
// i16 {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// i8, i32, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
|
||||||
// return i16(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.i16()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // int uses `Any` as an integer.
|
|
||||||
// pub fn (f Any) int() int {
|
|
||||||
// match f {
|
|
||||||
// int {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, bool {
|
|
||||||
// return int(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.int()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // i32 uses `Any` as a 32-bit integer.
|
|
||||||
// pub fn (f Any) i32() i32 {
|
|
||||||
// match f {
|
|
||||||
// i32 {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// i8, i16, int, i64, u8, u16, u32, u64, f32, f64, bool {
|
|
||||||
// return i32(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.i32()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // i64 uses `Any` as a 64-bit integer.
|
|
||||||
// pub fn (f Any) i64() i64 {
|
|
||||||
// match f {
|
|
||||||
// i64 {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// i8, i16, i32, int, u8, u16, u32, u64, f32, f64, bool {
|
|
||||||
// return i64(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.i64()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // u64 uses `Any` as a 64-bit unsigned integer.
|
|
||||||
// pub fn (f Any) u64() u64 {
|
|
||||||
// match f {
|
|
||||||
// u64 {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// u8, u16, u32, i8, i16, i32, int, i64, f32, f64, bool {
|
|
||||||
// return u64(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.u64()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // f32 uses `Any` as a 32-bit float.
|
|
||||||
// pub fn (f Any) f32() f32 {
|
|
||||||
// match f {
|
|
||||||
// f32 {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// bool, i8, i16, i32, int, i64, u8, u16, u32, u64, f64 {
|
|
||||||
// return f32(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.f32()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0.0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // f64 uses `Any` as a 64-bit float.
|
|
||||||
// pub fn (f Any) f64() f64 {
|
|
||||||
// match f {
|
|
||||||
// f64 {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// i8, i16, i32, int, i64, u8, u16, u32, u64, f32 {
|
|
||||||
// return f64(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// return f.f64()
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return 0.0
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // bool uses `Any` as a bool.
|
|
||||||
// pub fn (f Any) bool() bool {
|
|
||||||
// match f {
|
|
||||||
// bool {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// if f == 'false' {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// if f == 'true' {
|
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
// if f.len > 0 {
|
|
||||||
// return f != '0' && f != '0.0'
|
|
||||||
// } else {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// i8, i16, i32, int, i64 {
|
|
||||||
// return i64(f) != 0
|
|
||||||
// }
|
|
||||||
// u8, u16, u32, u64 {
|
|
||||||
// return u64(f) != 0
|
|
||||||
// }
|
|
||||||
// f32, f64 {
|
|
||||||
// return f64(f) != 0.0
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // arr uses `Any` as an array.
|
|
||||||
// pub fn (f Any) arr() []Any {
|
|
||||||
// if f is []Any {
|
|
||||||
// return f
|
|
||||||
// } else if f is map[string]Any {
|
|
||||||
// mut arr := []Any{}
|
|
||||||
// for _, v in f {
|
|
||||||
// arr << v
|
|
||||||
// }
|
|
||||||
// return arr
|
|
||||||
// }
|
|
||||||
// return [f]
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // as_map uses `Any` as a map.
|
|
||||||
// pub fn (f Any) as_map() map[string]Any {
|
|
||||||
// if f is map[string]Any {
|
|
||||||
// return f
|
|
||||||
// } else if f is []Any {
|
|
||||||
// mut mp := map[string]Any{}
|
|
||||||
// for i, fi in f {
|
|
||||||
// mp['${i}'] = fi
|
|
||||||
// }
|
|
||||||
// return mp
|
|
||||||
// }
|
|
||||||
// return {
|
|
||||||
// '0': f
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // to_time uses `Any` as a time.Time.
|
|
||||||
// pub fn (f Any) to_time() !time.Time {
|
|
||||||
// match f {
|
|
||||||
// time.Time {
|
|
||||||
// return f
|
|
||||||
// }
|
|
||||||
// i64 {
|
|
||||||
// return time.unix(f)
|
|
||||||
// }
|
|
||||||
// string {
|
|
||||||
// is_iso8601 := f[4] == `-` && f[7] == `-`
|
|
||||||
// if is_iso8601 {
|
|
||||||
// return time.parse_iso8601(f)!
|
|
||||||
// }
|
|
||||||
// is_rfc3339 := f.len == 24 && f[23] == `Z` && f[10] == `T`
|
|
||||||
// if is_rfc3339 {
|
|
||||||
// return time.parse_rfc3339(f)!
|
|
||||||
// }
|
|
||||||
// mut is_unix_timestamp := true
|
|
||||||
// for c in f {
|
|
||||||
// if c == `-` || (c >= `0` && c <= `9`) {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// is_unix_timestamp = false
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// if is_unix_timestamp {
|
|
||||||
// return time.unix(f.i64())
|
|
||||||
// }
|
|
||||||
// // TODO: parse_iso8601
|
|
||||||
// // TODO: parse_rfc2822
|
|
||||||
// return time.parse(f)!
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return error('not a time value: ${f} of type: ${f.type_name()}')
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // map_from convert a struct to map of Any
|
|
||||||
// pub fn map_from[T](t T) map[string]Any {
|
|
||||||
// mut m := map[string]Any{}
|
|
||||||
// $if T is $struct {
|
|
||||||
// $for field in T.fields {
|
|
||||||
// value := t.$(field.name)
|
|
||||||
|
|
||||||
// $if field.is_array {
|
|
||||||
// mut arr := []Any{}
|
|
||||||
// for variable in value {
|
|
||||||
// arr << Any(variable)
|
|
||||||
// }
|
|
||||||
// m[field.name] = arr
|
|
||||||
// arr.clear()
|
|
||||||
// } $else $if field.is_struct {
|
|
||||||
// m[field.name] = map_from(value)
|
|
||||||
// } $else $if field.is_map {
|
|
||||||
// // TODO
|
|
||||||
// } $else $if field.is_alias {
|
|
||||||
// // TODO
|
|
||||||
// } $else $if field.is_option {
|
|
||||||
// // TODO
|
|
||||||
// } $else {
|
|
||||||
// // TODO: improve memory usage when convert
|
|
||||||
// $if field.typ is string {
|
|
||||||
// m[field.name] = value.str()
|
|
||||||
// } $else $if field.typ is bool {
|
|
||||||
// m[field.name] = t.$(field.name).str().bool()
|
|
||||||
// } $else $if field.typ is i8 {
|
|
||||||
// m[field.name] = t.$(field.name).str().i8()
|
|
||||||
// } $else $if field.typ is i16 {
|
|
||||||
// m[field.name] = t.$(field.name).str().i16()
|
|
||||||
// } $else $if field.typ is i32 {
|
|
||||||
// m[field.name] = t.$(field.name).str().i32()
|
|
||||||
// } $else $if field.typ is int {
|
|
||||||
// m[field.name] = t.$(field.name).str().int()
|
|
||||||
// } $else $if field.typ is i64 {
|
|
||||||
// m[field.name] = t.$(field.name).str().i64()
|
|
||||||
// } $else $if field.typ is f32 {
|
|
||||||
// m[field.name] = t.$(field.name).str().f32()
|
|
||||||
// } $else $if field.typ is f64 {
|
|
||||||
// m[field.name] = t.$(field.name).str().f64()
|
|
||||||
// } $else $if field.typ is u8 {
|
|
||||||
// m[field.name] = t.$(field.name).str().u8()
|
|
||||||
// } $else $if field.typ is u16 {
|
|
||||||
// m[field.name] = t.$(field.name).str().u16()
|
|
||||||
// } $else $if field.typ is u32 {
|
|
||||||
// m[field.name] = t.$(field.name).str().u32()
|
|
||||||
// } $else $if field.typ is u64 {
|
|
||||||
// m[field.name] = t.$(field.name).str().u64()
|
|
||||||
// } $else {
|
|
||||||
// // return error("The type of `${field.name}` can't be decoded. Please open an issue at https://github.com/vlang/v/issues/new/choose")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return m
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // str returns the JSON string representation of the `map[string]Any` type.
|
|
||||||
// pub fn (f map[string]Any) str() string {
|
|
||||||
// return Any(f).json_str()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // str returns the JSON string representation of the `[]Any` type.
|
|
||||||
// pub fn (f []Any) str() string {
|
|
||||||
// return Any(f).json_str()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // str returns the string representation of the `Any` type. Use the `json_str` method
|
|
||||||
// // if you want to use the escaped str() version of the `Any` type.
|
|
||||||
// pub fn (f Any) str() string {
|
|
||||||
// if f is string {
|
|
||||||
// return f
|
|
||||||
// } else {
|
|
||||||
// return f.json_str()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // json_str returns the JSON string representation of the `Any` type.
|
|
||||||
// pub fn (f Any) json_str() string {
|
|
||||||
// return encode(f)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // prettify_json_str returns the pretty-formatted JSON string representation of the `Any` type.
|
|
||||||
// @[manualfree]
|
|
||||||
// pub fn (f Any) prettify_json_str() string {
|
|
||||||
// mut params := []u8{}
|
|
||||||
// defer {
|
|
||||||
// unsafe { params.free() }
|
|
||||||
// }
|
|
||||||
// mut enc := Encoder{
|
|
||||||
// newline: `\n`
|
|
||||||
// newline_spaces_count: 2
|
|
||||||
// }
|
|
||||||
// enc.encode_value(f, mut params) or {}
|
|
||||||
// return params.bytestr()
|
|
||||||
// }
|
|
||||||
@@ -169,7 +169,7 @@ lib/lang
|
|||||||
lib/clients
|
lib/clients
|
||||||
lib/core
|
lib/core
|
||||||
lib/develop
|
lib/develop
|
||||||
lib/hero
|
// lib/hero
|
||||||
// lib/vfs The vfs folder is not exists on the development branch, so we need to uncomment it after merging this PR https://github.com/incubaid/herolib/pull/68
|
// lib/vfs The vfs folder is not exists on the development branch, so we need to uncomment it after merging this PR https://github.com/incubaid/herolib/pull/68
|
||||||
// lib/crypt
|
// lib/crypt
|
||||||
'
|
'
|
||||||
|
|||||||
Reference in New Issue
Block a user