fix crystallib imports

This commit is contained in:
2024-12-29 18:00:58 +02:00
parent 705fbfdebc
commit df63231db5
11 changed files with 504 additions and 491 deletions

View File

@@ -5,7 +5,7 @@ works very well in combination with heroscript
## How to get the paramsparser
```v
import freeflowuniverse.crystallib.data.paramsparser
import freeflowuniverse.herolib.data.paramsparser
// Create new params from text
params := paramsparser.new("color:red size:'large' priority:1 enable:true")!
@@ -25,6 +25,7 @@ The parser supports several formats:
4. Comments: `// this is a comment`
Example:
```v
text := "name:'John Doe' age:30 active:true // user details"
params := paramsparser.new(text)!
@@ -59,6 +60,7 @@ progress := params.get_percentage("progress")!
The module supports various type conversions:
### Basic Types
- `get_int()`: Convert to int32
- `get_u32()`: Convert to unsigned 32-bit integer
- `get_u64()`: Convert to unsigned 64-bit integer
@@ -67,10 +69,12 @@ The module supports various type conversions:
- `get_percentage()`: Convert percentage string to float (e.g., "80%" → 0.8)
### Boolean Values
- `get_default_true()`: Returns true if value is empty, "1", "true", "y", or "yes"
- `get_default_false()`: Returns false if value is empty, "0", "false", "n", or "no"
### Lists
The module provides robust support for parsing and converting lists:
```v
@@ -89,6 +93,7 @@ clean_names := params.get_list_namefix("categories")!
```
Supported list types:
- `get_list()`: String list
- `get_list_u8()`, `get_list_u16()`, `get_list_u32()`, `get_list_u64()`: Unsigned integers
- `get_list_i8()`, `get_list_i16()`, `get_list_int()`, `get_list_i64()`: Signed integers
@@ -97,6 +102,7 @@ Supported list types:
Each list method has a corresponding `_default` version that accepts a default value.
Valid list formats:
```v
users: "john, jane,bob"
ids: "1,2,3,4,5"
@@ -133,4 +139,4 @@ get_timestamp(key string) !Duration
get_timestamp_default(key string, defval Duration) !Duration
```
```

View File

@@ -1,10 +1,9 @@
# module osal
import as
import as
```vlang
import freeflowuniverse.crystallib.osal
import freeflowuniverse.herolib.osal
osal.ping...
@@ -46,7 +45,6 @@ pub enum CPUType {
## process
### execute jobs
```v
@@ -56,7 +54,7 @@ println(job2)
//wont die, the result can be found in /tmp/execscripts
mut job:=osal.exec(cmd:"ls dsds",ignore_error:true)?
//this one has an error
println(job)
println(job)
```
All scripts are executed from a file from /tmp/execscripts
@@ -91,24 +89,24 @@ info returns like:
## other commands
fn bin_path() !string
fn cmd_add(args_ CmdAddArgs) !
copy a binary to the right location on the local computer . e.g. is /usr/local/bin on linux . e.g. is ~/hero/bin on osx . will also add the bin location to the path of .zprofile and .zshrc (different per platform)
fn cmd_exists(cmd string) bool
fn bin*path() !string
fn cmd_add(args* CmdAddArgs) !
copy a binary to the right location on the local computer . e.g. is /usr/local/bin on linux . e.g. is ~/hero/bin on osx . will also add the bin location to the path of .zprofile and .zshrc (different per platform)
fn cmd*exists(cmd string) bool
fn cmd_exists_profile(cmd string) bool
fn cmd_path(cmd string) !string
is same as executing which in OS returns path or error
is same as executing which in OS returns path or error
fn cmd_to_script_path(cmd Command) !string
will return temporary path which then can be executed, is a helper function for making script out of command
will return temporary path which then can be executed, is a helper function for making script out of command
fn cputype() CPUType
fn cputype_enum_from_string(cpytype string) CPUType
Returns the enum value that matches the provided string for CPUType
Returns the enum value that matches the provided string for CPUType
fn dir_delete(path string) !
remove all if it exists
remove all if it exists
fn dir_ensure(path string) !
remove all if it exists
remove all if it exists
fn dir_reset(path string) !
remove all if it exists and then (re-)create
remove all if it exists and then (re-)create
fn done_delete(key string) !
fn done_exists(key string) bool
fn done_get(key string) ?string
@@ -117,45 +115,45 @@ fn done_get_str(key string) string
fn done_print() !
fn done_reset() !
fn done_set(key string, val string) !
fn download(args_ DownloadArgs) !pathlib.Path
if name is not specified, then will be the filename part if the last ends in an extension like .md .txt .log .text ... the file will be downloaded
fn download(args* DownloadArgs) !pathlib.Path
if name is not specified, then will be the filename part if the last ends in an extension like .md .txt .log .text ... the file will be downloaded
fn env_get(key string) !string
Returns the requested environment variable if it exists or throws an error if it does not
Returns the requested environment variable if it exists or throws an error if it does not
fn env_get_all() map[string]string
Returns all existing environment variables
Returns all existing environment variables
fn env_get_default(key string, def string) string
Returns the requested environment variable if it exists or returns the provided default value if it does not
Returns the requested environment variable if it exists or returns the provided default value if it does not
fn env_set(args EnvSet)
Sets an environment if it was not set before, it overwrites the enviroment variable if it exists and if overwrite was set to true (default)
Sets an environment if it was not set before, it overwrites the enviroment variable if it exists and if overwrite was set to true (default)
fn env_set_all(args EnvSetAll)
Allows to set multiple enviroment variables in one go, if clear_before_set is true all existing environment variables will be unset before the operation, if overwrite_if_exists is set to true it will overwrite all existing enviromnent variables
Allows to set multiple enviroment variables in one go, if clear_before_set is true all existing environment variables will be unset before the operation, if overwrite_if_exists is set to true it will overwrite all existing enviromnent variables
fn env_unset(key string)
Unsets an environment variable
Unsets an environment variable
fn env_unset_all()
Unsets all environment variables
Unsets all environment variables
fn exec(cmd Command) !Job
cmd is the cmd to execute can use ' ' and spaces . if \n in cmd it will write it to ext and then execute with bash . if die==false then will just return returncode,out but not return error . if stdout will show stderr and stdout . . if cmd starts with find or ls, will give to bash -c so it can execute . if cmd has no path, path will be found . . Command argument: .
```
name string // to give a name to your command, good to see logs...
cmd string
description string
timeout int = 3600 // timeout in sec
stdout bool = true
stdout_log bool = true
raise_error bool = true // if false, will not raise an error but still error report
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
work_folder string // location where cmd will be executed
environment map[string]string // env variables
ignore_error_codes []int
scriptpath string // is the path where the script will be put which is executed
scriptkeep bool // means we don't remove the script
debug bool // if debug will put +ex in the script which is being executed and will make sure script stays
shell bool // means we will execute it in a shell interactive
retry int
interactive bool = true // make sure we run on non interactive way
async bool
runtime RunTime (.bash, .python)
cmd is the cmd to execute can use ' ' and spaces . if \n in cmd it will write it to ext and then execute with bash . if die==false then will just return returncode,out but not return error . if stdout will show stderr and stdout . . if cmd starts with find or ls, will give to bash -c so it can execute . if cmd has no path, path will be found . . Command argument: .
```
name string // to give a name to your command, good to see logs...
cmd string
description string
timeout int = 3600 // timeout in sec
stdout bool = true
stdout_log bool = true
raise_error bool = true // if false, will not raise an error but still error report
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
work_folder string // location where cmd will be executed
environment map[string]string // env variables
ignore_error_codes []int
scriptpath string // is the path where the script will be put which is executed
scriptkeep bool // means we don't remove the script
debug bool // if debug will put +ex in the script which is being executed and will make sure script stays
shell bool // means we will execute it in a shell interactive
retry int
interactive bool = true // make sure we run on non interactive way
async bool
runtime RunTime (.bash, .python)
returns Job:
start time.Time
end time.Time
@@ -167,33 +165,35 @@ fn exec(cmd Command) !Job
process os.Process
```
return Job .
fn exec_string(cmd Command) !string
cmd is the cmd to execute can use ' ' and spaces if \n in cmd it will write it to ext and then execute with bash if die==false then will just return returncode,out but not return error if stdout will show stderr and stdout
cmd is the cmd to execute can use ' ' and spaces if \n in cmd it will write it to ext and then execute with bash if die==false then will just return returncode,out but not return error if stdout will show stderr and stdout
if cmd starts with find or ls, will give to bash -c so it can execute if cmd has no path, path will be found $... are remplaced by environment arguments TODO:implement
Command argument: cmd string timeout int = 600 stdout bool = true die bool = true debug bool
return what needs to be executed can give it to bash -c ...
fn execute_debug(cmd string) !string
fn execute*debug(cmd string) !string
fn execute_interactive(cmd string) !
shortcut to execute a job interactive means in shell
shortcut to execute a job interactive means in shell
fn execute_ok(cmd string) bool
executes a cmd, if not error return true
executes a cmd, if not error return true
fn execute_silent(cmd string) !string
shortcut to execute a job silent
shortcut to execute a job silent
fn execute_stdout(cmd string) !string
shortcut to execute a job to stdout
shortcut to execute a job to stdout
fn file_read(path string) !string
fn file_write(path string, text string) !
fn get_logger() log.Logger
Returns a logger object and allows you to specify via environment argument OSAL_LOG_LEVEL the debug level
Returns a logger object and allows you to specify via environment argument OSAL_LOG_LEVEL the debug level
fn hero_path() !string
fn hostname() !string
fn initname() !string
e.g. systemd, bash, zinit
e.g. systemd, bash, zinit
fn ipaddr_pub_get() !string
Returns the ipaddress as known on the public side is using resolver4.opendns.com
Returns the ipaddress as known on the public side is using resolver4.opendns.com
fn is_linux() bool
fn is_linux_arm() bool
fn is_linux_intel() bool
@@ -205,24 +205,23 @@ fn load_env_file(file_path string) !
fn memdb_exists(key string) bool
fn memdb_get(key string) string
fn memdb_set(key string, val string)
fn package_install(name_ string) !
install a package will use right commands per platform
fn package_install(name* string) !
install a package will use right commands per platform
fn package_refresh() !
update the package list
update the package list
fn ping(args PingArgs) PingResult
if reached in timout result will be True address is e.g. 8.8.8.8 ping means we check if the destination responds
if reached in timout result will be True address is e.g. 8.8.8.8 ping means we check if the destination responds
fn platform() PlatformType
fn platform_enum_from_string(platform string) PlatformType
fn process_exists(pid int) bool
fn process_exists_byname(name string) !bool
fn process_kill_recursive(args ProcessKillArgs) !
kill process and all the ones underneith
kill process and all the ones underneith
fn processinfo_children(pid int) !ProcessMap
get all children of 1 process
get all children of 1 process
fn processinfo_get(pid int) !ProcessInfo
get process info from 1 specific process returns
```
pub struct ProcessInfo {
get process info from 1 specific process returns
` pub struct ProcessInfo {
pub mut:
cpu_perc f32
mem_perc f32
@@ -232,209 +231,209 @@ fn processinfo_get(pid int) !ProcessInfo
//resident memory
rss int
}
```
`
fn processinfo_get_byname(name string) ![]ProcessInfo
fn processinfo_with_children(pid int) !ProcessMap
return the process and its children
return the process and its children
fn processmap_get() !ProcessMap
make sure to use new first, so that the connection has been initted then you can get it everywhere
make sure to use new first, so that the connection has been initted then you can get it everywhere
fn profile_path() string
fn profile_path_add(args ProfilePathAddArgs) !
add the following path to a profile
add the following path to a profile
fn profile_path_add_hero() !string
fn profile_path_source() string
return the source statement if the profile exists
return the source statement if the profile exists
fn profile_path_source_and() string
return source $path && . or empty if it doesn't exist
return source $path && . or empty if it doesn't exist
fn sleep(duration int)
sleep in seconds
sleep in seconds
fn tcp_port_test(args TcpPortTestArgs) bool
test if a tcp port answers
```
address string //192.168.8.8
test if a tcp port answers
` address string //192.168.8.8
port int = 22
timeout u16 = 2000 // total time in milliseconds to keep on trying
```
`
fn user_add(args UserArgs) !int
add's a user if the user does not exist yet
add's a user if the user does not exist yet
fn user_exists(username string) bool
fn user_id_get(username string) !int
fn usr_local_path() !string
/usr/local on linux, ${os.home_dir()}/hero on osx
/usr/local on linux, ${os.home_dir()}/hero on osx
fn whoami() !string
fn write_flags[T](options T) string
enum CPUType {
unknown
intel
arm
intel32
arm32
unknown
intel
arm
intel32
arm32
}
enum ErrorType {
exec
timeout
args
exec
timeout
args
}
enum JobStatus {
init
running
error_exec
error_timeout
error_args
done
init
running
error_exec
error_timeout
error_args
done
}
enum PMState {
init
ok
old
init
ok
old
}
enum PingResult {
ok
timeout // timeout from ping
unknownhost // means we don't know the hostname its a dns issue
ok
timeout // timeout from ping
unknownhost // means we don't know the hostname its a dns issue
}
enum PlatformType {
unknown
osx
ubuntu
alpine
arch
suse
unknown
osx
ubuntu
alpine
arch
suse
}
enum RunTime {
bash
python
heroscript
herocmd
v
bash
python
heroscript
herocmd
v
}
struct CmdAddArgs {
pub mut:
cmdname string
source string @[required] // path where the binary is
symlink bool // if rather than copy do a symlink
reset bool // if existing cmd will delete
// bin_repo_url string = 'https://github.com/freeflowuniverse/freeflow_binary' // binary where we put the results
cmdname string
source string @[required] // path where the binary is
symlink bool // if rather than copy do a symlink
reset bool // if existing cmd will delete
// bin_repo_url string = 'https://github.com/freeflowuniverse/freeflow_binary' // binary where we put the results
}
struct Command {
pub mut:
name string // to give a name to your command, good to see logs...
cmd string
description string
timeout int = 3600 // timeout in sec
stdout bool = true
stdout_log bool = true
raise_error bool = true // if false, will not raise an error but still error report
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
work_folder string // location where cmd will be executed
environment map[string]string // env variables
ignore_error_codes []int
scriptpath string // is the path where the script will be put which is executed
scriptkeep bool // means we don't remove the script
debug bool // if debug will put +ex in the script which is being executed and will make sure script stays
shell bool // means we will execute it in a shell interactive
retry int
interactive bool = true
async bool
runtime RunTime
name string // to give a name to your command, good to see logs...
cmd string
description string
timeout int = 3600 // timeout in sec
stdout bool = true
stdout_log bool = true
raise_error bool = true // if false, will not raise an error but still error report
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
work_folder string // location where cmd will be executed
environment map[string]string // env variables
ignore_error_codes []int
scriptpath string // is the path where the script will be put which is executed
scriptkeep bool // means we don't remove the script
debug bool // if debug will put +ex in the script which is being executed and will make sure script stays
shell bool // means we will execute it in a shell interactive
retry int
interactive bool = true
async bool
runtime RunTime
}
struct DownloadArgs {
pub mut:
name string // optional (otherwise derived out of filename)
url string
reset bool // will remove
hash string // if hash is known, will verify what hash is
dest string // if specified will copy to that destination
timeout int = 180
retry int = 3
minsize_kb u32 = 10 // is always in kb
maxsize_kb u32
expand_dir string
expand_file string
name string // optional (otherwise derived out of filename)
url string
reset bool // will remove
hash string // if hash is known, will verify what hash is
dest string // if specified will copy to that destination
timeout int = 180
retry int = 3
minsize_kb u32 = 10 // is always in kb
maxsize_kb u32
expand_dir string
expand_file string
}
struct EnvSet {
pub mut:
key string @[required]
value string @[required]
overwrite bool = true
key string @[required]
value string @[required]
overwrite bool = true
}
struct EnvSetAll {
pub mut:
env map[string]string
clear_before_set bool
overwrite_if_exists bool = true
env map[string]string
clear_before_set bool
overwrite_if_exists bool = true
}
struct Job {
pub mut:
start time.Time
end time.Time
cmd Command
output string
error string
exit_code int
status JobStatus
process ?&os.Process @[skip; str: skip]
runnr int // nr of time it runs, is for retry
start time.Time
end time.Time
cmd Command
output string
error string
exit_code int
status JobStatus
process ?&os.Process @[skip; str: skip]
runnr int // nr of time it runs, is for retry
}
fn (mut job Job) execute_retry() !
execute the job and wait on result will retry as specified
execute the job and wait on result will retry as specified
fn (mut job Job) execute() !
execute the job, start process, process will not be closed . important you need to close the process later by job.close()! otherwise we get zombie processes
execute the job, start process, process will not be closed . important you need to close the process later by job.close()! otherwise we get zombie processes
fn (mut job Job) wait() !
wait till the job finishes or goes in error
wait till the job finishes or goes in error
fn (mut job Job) process() !
process (read std.err and std.out of process)
process (read std.err and std.out of process)
fn (mut job Job) close() !
will wait & close
will wait & close
struct JobError {
Error
Error
pub mut:
job Job
error_type ErrorType
job Job
error_type ErrorType
}
struct PingArgs {
pub mut:
address string @[required]
count u8 = 1 // the ping is successful if it got count amount of replies from the other side
timeout u16 = 1 // the time in which the other side should respond in seconds
retry u8
address string @[required]
count u8 = 1 // the ping is successful if it got count amount of replies from the other side
timeout u16 = 1 // the time in which the other side should respond in seconds
retry u8
}
struct ProcessInfo {
pub mut:
cpu_perc f32
mem_perc f32
cmd string
pid int
ppid int // parentpid
// resident memory
rss int
cpu_perc f32
mem_perc f32
cmd string
pid int
ppid int // parentpid
// resident memory
rss int
}
fn (mut p ProcessInfo) str() string
struct ProcessKillArgs {
pub mut:
name string
pid int
name string
pid int
}
struct ProcessMap {
pub mut:
processes []ProcessInfo
lastscan time.Time
state PMState
pids []int
processes []ProcessInfo
lastscan time.Time
state PMState
pids []int
}
struct ProfilePathAddArgs {
pub mut:
path string @[required]
todelete string // see which one to remove
path string @[required]
todelete string // see which one to remove
}
struct TcpPortTestArgs {
pub mut:
address string @[required] // 192.168.8.8
port int = 22
timeout u16 = 2000 // total time in milliseconds to keep on trying
address string @[required] // 192.168.8.8
port int = 22
timeout u16 = 2000 // total time in milliseconds to keep on trying
}
struct UserArgs {
pub mut:
name string @[required]
name string @[required]
}
*
-

View File

@@ -1,14 +1,14 @@
# module ui.console.chalk
Chalk offers functions:- `console.color_fg(text string, color string)` - To change the foreground color.
- `console.color_bg(text string, color string)` - To change the background color.
- `console.style(text string, style string)` - To change the text style.
Example:
```vlang
import freeflowuniverse.crystallib.ui.console
import freeflowuniverse.herolib.ui.console
# basic usage
println('I am really ' + console.color_fg('happy', 'green'))
@@ -18,6 +18,7 @@ println('I am really ' + console.color_fg(console.style('ANGRY', 'bold'), 'red')
```
Available colors:- black
- red
- green
- yellow
@@ -36,6 +37,7 @@ Available colors:- black
- white
Available styles:- bold
- dim
- underline
- blink

View File

@@ -2,17 +2,16 @@
has mechanisms to print better to console, see the methods below
import as
import as
```vlang
import freeflowuniverse.crystallib.ui.console
import freeflowuniverse.herolib.ui.console
```
## Methods
```v
````v
fn clear()
//reset the console screen
@@ -86,13 +85,12 @@ fn style(c Style) string
fn trim(c_ string) string
```
````
## Console Object
Is used to ask feedback to users
```v
struct UIConsole {
@@ -105,14 +103,14 @@ pub mut:
}
//DropDownArgs:
// - description string
// - items []string
// - warning string
// - description string
// - items []string
// - warning string
// - clear bool = true
fn (mut c UIConsole) ask_dropdown_int(args_ DropDownArgs) !int
// return the dropdown as an int
// return the dropdown as an int
fn (mut c UIConsole) ask_dropdown_multiple(args_ DropDownArgs) ![]string
// result can be multiple, aloso can select all description string items []string warning string clear bool = true
@@ -135,7 +133,7 @@ fn (mut c UIConsole) ask_time(args QuestionArgs) !string
fn (mut c UIConsole) ask_date(args QuestionArgs) !string
fn (mut c UIConsole) ask_yesno(args YesNoArgs) !bool
// yes is true, no is false
// yes is true, no is false
// args:
// - description string
// - question string
@@ -148,14 +146,11 @@ fn (mut c UIConsole) status() string
```
## enums
```v
enum BackgroundColor {
default_color = 49 // 'default' is a reserved keyword in V
default_color = 49 // 'default' is a reserved keyword in V
black = 40
red = 41
green = 42
@@ -174,7 +169,7 @@ enum BackgroundColor {
white = 107
}
enum ForegroundColor {
default_color = 39 // 'default' is a reserved keyword in V
default_color = 39 // 'default' is a reserved keyword in V
white = 97
black = 30
red = 31

View File

@@ -1,4 +1,3 @@
# how to run the vshell example scripts
this is how we want example scripts to be, see the first line
@@ -6,7 +5,7 @@ this is how we want example scripts to be, see the first line
```vlang
#!/usr/bin/env -S v -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.crystallib.installers.sysadmintools.daguserver
import freeflowuniverse.herolib.installers.sysadmintools.daguserver
mut ds := daguserver.get()!
@@ -18,4 +17,3 @@ the files are in ~/code/github/freeflowuniverse/crystallib/examples for crystall
## important instructions
- never use fn main() in a .vsh script

View File

@@ -1,8 +1,7 @@
#!/usr/bin/env -S v -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.hero.bootstrap
import freeflowuniverse.crystallib.hero.bootstrap
mut al := bootstrap.new_alpine_loader()
mut al:=bootstrap.new_alpine_loader()
al.start()!
al.start()!

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env -S v -w -n -enable-globals run
import freeflowuniverse.crystallib.hero.generation
import freeflowuniverse.herolib.hero.generation
generation.generate_actor(
name: 'Example'
)
)

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env -S v -w -n -enable-globals run
import freeflowuniverse.crystallib.hero.generation
import freeflowuniverse.herolib.hero.generation
generation.generate_actor(
name: 'Example'
interfaces: []
)
)

View File

@@ -1,8 +1,8 @@
module example_actor
import os
import freeflowuniverse.crystallib.hero.baobab.actor {IActor, RunParams}
import freeflowuniverse.crystallib.web.openapi
import freeflowuniverse.herolib.hero.baobab.actor { IActor, RunParams }
import freeflowuniverse.herolib.web.openapi
import time
const openapi_spec_path = '${os.dir(@FILE)}/specs/openapi.json'
@@ -10,27 +10,25 @@ const openapi_spec_json = os.read_file(openapi_spec_path) or { panic(err) }
const openapi_specification = openapi.json_decode(openapi_spec_json)!
struct ExampleActor {
actor.Actor
actor.Actor
}
fn new() !ExampleActor {
return ExampleActor{
actor.new('example')
}
return ExampleActor{actor.new('example')}
}
pub fn run() ! {
mut a_ := new()!
mut a := IActor(a_)
a.run()!
mut a_ := new()!
mut a := IActor(a_)
a.run()!
}
pub fn run_server(params RunParams) ! {
mut a := new()!
mut server := actor.new_server(
redis_url: 'localhost:6379'
redis_queue: a.name
openapi_spec: openapi_specification
)!
server.run(params)
}
mut a := new()!
mut server := actor.new_server(
redis_url: 'localhost:6379'
redis_queue: a.name
openapi_spec: example_actor.openapi_specification
)!
server.run(params)
}

View File

@@ -6,210 +6,229 @@ import veb
import json
import x.json2
import net.http
import freeflowuniverse.crystallib.web.openapi {Server, Context, Request, Response}
import freeflowuniverse.crystallib.hero.processor {Processor, ProcedureCall, ProcedureResponse, ProcessParams}
import freeflowuniverse.crystallib.clients.redisclient
import freeflowuniverse.herolib.web.openapi
import freeflowuniverse.herolib.hero.processor
import freeflowuniverse.herolib.clients.redisclient
@[heap]
struct Actor {
mut:
rpc redisclient.RedisRpc
data_store DataStore
rpc redisclient.RedisRpc
data_store DataStore
}
pub struct DataStore {
mut:
pets map[int]Pet
orders map[int]Order
users map[int]User
pets map[int]Pet
orders map[int]Order
users map[int]User
}
struct Pet {
id int
name string
tag string
id int
name string
tag string
}
struct Order {
id int
pet_id int
quantity int
ship_date string
status string
complete bool
id int
pet_id int
quantity int
ship_date string
status string
complete bool
}
struct User {
id int
username string
email string
phone string
id int
username string
email string
phone string
}
// Entry point for the actor
fn main() {
mut redis := redisclient.new('localhost:6379') or {panic(err)}
mut rpc := redis.rpc_get('procedure_queue')
mut redis := redisclient.new('localhost:6379') or { panic(err) }
mut rpc := redis.rpc_get('procedure_queue')
mut actor := Actor{
rpc: rpc
data_store: DataStore{}
}
mut actor := Actor{
rpc: rpc
data_store: DataStore{}
}
actor.listen() or {panic(err)}
actor.listen() or { panic(err) }
}
// Actor listens to the Redis queue for method invocations
fn (mut actor Actor) listen() ! {
println('Actor started and listening for tasks...')
for {
actor.rpc.process(actor.handle_method)!
time.sleep(time.millisecond * 100) // Prevent CPU spinning
}
println('Actor started and listening for tasks...')
for {
actor.rpc.process(actor.handle_method)!
time.sleep(time.millisecond * 100) // Prevent CPU spinning
}
}
// Handle method invocations
fn (mut actor Actor) handle_method(cmd string, data string) !string {
println('debugzo received rpc ${cmd}:${data}')
param_anys := json2.raw_decode(data)!.arr()
match cmd {
'listPets' {
pets := if param_anys.len == 0 {
actor.data_store.list_pets()
} else {
params := json.decode(ListPetParams, param_anys[0].str())!
actor.data_store.list_pets(params)
}
return json.encode(pets)
}
'createPet' {
response := if param_anys.len == 0 {
return error('at least data expected')
} else if param_anys.len == 1 {
payload := json.decode(NewPet, param_anys[0].str())!
actor.data_store.create_pet(payload)
} else {
return error('expected 1 param, found too many')
}
// data := json.decode(NewPet, data) or { return error('Invalid pet data: $err') }
// created_pet := actor.data_store.create_pet(pet)
return json.encode(response)
}
'getPet' {
response := if param_anys.len == 0 {
return error('at least data expected')
} else if param_anys.len == 1 {
payload := param_anys[0].int()
actor.data_store.get_pet(payload)!
} else {
return error('expected 1 param, found too many')
}
return json.encode(response)
}
'deletePet' {
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
actor.data_store.delete_pet(params['petId']) or { return error('Pet not found: $err') }
return json.encode({'message': 'Pet deleted'})
}
'listOrders' {
orders := actor.data_store.list_orders()
return json.encode(orders)
}
'getOrder' {
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
order := actor.data_store.get_order(params['orderId']) or {
return error('Order not found: $err')
}
return json.encode(order)
}
'deleteOrder' {
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
actor.data_store.delete_order(params['orderId']) or {
return error('Order not found: $err')
}
return json.encode({'message': 'Order deleted'})
}
'createUser' {
user := json.decode(NewUser, data) or { return error('Invalid user data: $err') }
created_user := actor.data_store.create_user(user)
return json.encode(created_user)
}
else {
return error('Unknown method: $cmd')
}
}
println('debugzo received rpc ${cmd}:${data}')
param_anys := json2.raw_decode(data)!.arr()
match cmd {
'listPets' {
pets := if param_anys.len == 0 {
actor.data_store.list_pets()
} else {
params := json.decode(ListPetParams, param_anys[0].str())!
actor.data_store.list_pets(params)
}
return json.encode(pets)
}
'createPet' {
response := if param_anys.len == 0 {
return error('at least data expected')
} else if param_anys.len == 1 {
payload := json.decode(NewPet, param_anys[0].str())!
actor.data_store.create_pet(payload)
} else {
return error('expected 1 param, found too many')
}
// data := json.decode(NewPet, data) or { return error('Invalid pet data: $err') }
// created_pet := actor.data_store.create_pet(pet)
return json.encode(response)
}
'getPet' {
response := if param_anys.len == 0 {
return error('at least data expected')
} else if param_anys.len == 1 {
payload := param_anys[0].int()
actor.data_store.get_pet(payload)!
} else {
return error('expected 1 param, found too many')
}
return json.encode(response)
}
'deletePet' {
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
actor.data_store.delete_pet(params['petId']) or {
return error('Pet not found: ${err}')
}
return json.encode({
'message': 'Pet deleted'
})
}
'listOrders' {
orders := actor.data_store.list_orders()
return json.encode(orders)
}
'getOrder' {
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
order := actor.data_store.get_order(params['orderId']) or {
return error('Order not found: ${err}')
}
return json.encode(order)
}
'deleteOrder' {
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
actor.data_store.delete_order(params['orderId']) or {
return error('Order not found: ${err}')
}
return json.encode({
'message': 'Order deleted'
})
}
'createUser' {
user := json.decode(NewUser, data) or { return error('Invalid user data: ${err}') }
created_user := actor.data_store.create_user(user)
return json.encode(created_user)
}
else {
return error('Unknown method: ${cmd}')
}
}
}
@[params]
pub struct ListPetParams {
limit u32
limit u32
}
// DataStore methods for managing data
fn (mut store DataStore) list_pets(params ListPetParams) []Pet {
if params.limit > 0 {
if params.limit >= store.pets.values().len {
return store.pets.values()
}
return store.pets.values()[..params.limit]
}
return store.pets.values()
if params.limit > 0 {
if params.limit >= store.pets.values().len {
return store.pets.values()
}
return store.pets.values()[..params.limit]
}
return store.pets.values()
}
fn (mut store DataStore) create_pet(new_pet NewPet) Pet {
id := store.pets.keys().len + 1
pet := Pet{id: id, name: new_pet.name, tag: new_pet.tag}
store.pets[id] = pet
return pet
id := store.pets.keys().len + 1
pet := Pet{
id: id
name: new_pet.name
tag: new_pet.tag
}
store.pets[id] = pet
return pet
}
fn (mut store DataStore) get_pet(id int) !Pet {
return store.pets[id] or {
return error('Pet with id ${id} not found.')
}
return store.pets[id] or { return error('Pet with id ${id} not found.') }
}
fn (mut store DataStore) delete_pet(id int) ! {
if id in store.pets {
store.pets.delete(id)
return
}
return error('Pet not found')
if id in store.pets {
store.pets.delete(id)
return
}
return error('Pet not found')
}
fn (mut store DataStore) list_orders() []Order {
return store.orders.values()
return store.orders.values()
}
fn (mut store DataStore) get_order(id int) !Order {
return store.orders[id] or { none }
return store.orders[id] or { none }
}
fn (mut store DataStore) delete_order(id int) ! {
if id in store.orders {
store.orders.delete(id)
return
}
return error('Order not found')
if id in store.orders {
store.orders.delete(id)
return
}
return error('Order not found')
}
fn (mut store DataStore) create_user(new_user NewUser) User {
id := store.users.keys().len + 1
user := User{id: id, username: new_user.username, email: new_user.email, phone: new_user.phone}
store.users[id] = user
return user
id := store.users.keys().len + 1
user := User{
id: id
username: new_user.username
email: new_user.email
phone: new_user.phone
}
store.users[id] = user
return user
}
// NewPet struct for creating a pet
struct NewPet {
name string
tag string
name string
tag string
}
// NewUser struct for creating a user
struct NewUser {
username string
email string
phone string
}
username string
email string
phone string
}

View File

@@ -4,138 +4,135 @@ import os
import time
import veb
import json
import x.json2 {Any}
import x.json2 { Any }
import net.http
import freeflowuniverse.crystallib.data.jsonschema {Schema}
import freeflowuniverse.crystallib.web.openapi {Server, Context, Request, Response}
import freeflowuniverse.crystallib.hero.processor {Processor, ProcedureCall, ProcedureResponse, ProcessParams}
import freeflowuniverse.crystallib.clients.redisclient
import freeflowuniverse.herolib.data.jsonschema { Schema }
import freeflowuniverse.herolib.web.openapi { Context, Request, Response, Server }
import freeflowuniverse.herolib.hero.processor { ProcedureCall, ProcessParams, Processor }
import freeflowuniverse.herolib.clients.redisclient
const spec_path = '${os.dir(@FILE)}/data/openapi.json'
const spec_json = os.read_file(spec_path) or { panic(err) }
// Main function to start the server
fn main() {
// Initialize the Redis client and RPC mechanism
mut redis := redisclient.new('localhost:6379')!
mut rpc := redis.rpc_get('procedure_queue')
// Initialize the Redis client and RPC mechanism
mut redis := redisclient.new('localhost:6379')!
mut rpc := redis.rpc_get('procedure_queue')
// Initialize the server
mut server := &Server{
specification: openapi.json_decode(spec_json)!
handler: Handler{
processor: Processor{
rpc: rpc
}
}
}
// Initialize the server
mut server := &Server{
specification: openapi.json_decode(spec_json)!
handler: Handler{
processor: Processor{
rpc: rpc
}
}
}
// Start the server
veb.run[Server, Context](mut server, 8080)
// Start the server
veb.run[Server, Context](mut server, 8080)
}
pub struct Handler {
mut:
processor Processor
mut:
processor Processor
}
fn (mut handler Handler) handle(request Request) !Response {
// Convert incoming OpenAPI request to a procedure call
mut params := []string{}
// Convert incoming OpenAPI request to a procedure call
mut params := []string{}
if request.arguments.len > 0 {
params = request.arguments.values().map(it.str()).clone()
}
if request.arguments.len > 0 {
params = request.arguments.values().map(it.str()).clone()
}
if request.body != '' {
params << request.body
}
if request.body != '' {
params << request.body
}
if request.parameters.len != 0 {
mut param_map := map[string]Any{} // Store parameters with correct types
if request.parameters.len != 0 {
mut param_map := map[string]Any{} // Store parameters with correct types
for param_name, param_value in request.parameters {
operation_param := request.operation.parameters.filter(it.name == param_name)
if operation_param.len > 0 {
param_schema := operation_param[0].schema as Schema
param_type := param_schema.typ
param_format := param_schema.format
for param_name, param_value in request.parameters {
operation_param := request.operation.parameters.filter(it.name == param_name)
if operation_param.len > 0 {
param_schema := operation_param[0].schema as Schema
param_type := param_schema.typ
param_format := param_schema.format
// Convert parameter value to corresponding type
match param_type {
'integer' {
match param_format {
'int32' {
param_map[param_name] = param_value.int() // Convert to int
}
'int64' {
param_map[param_name] = param_value.i64() // Convert to i64
}
else {
param_map[param_name] = param_value.int() // Default to int
}
}
}
'string' {
param_map[param_name] = param_value // Already a string
}
'boolean' {
param_map[param_name] = param_value.bool() // Convert to bool
}
'number' {
match param_format {
'float' {
param_map[param_name] = param_value.f32() // Convert to float
}
'double' {
param_map[param_name] = param_value.f64() // Convert to double
}
else {
param_map[param_name] = param_value.f64() // Default to double
}
}
}
else {
param_map[param_name] = param_value // Leave as string for unknown types
}
}
} else {
// If the parameter is not defined in the OpenAPI operation, skip or log it
println('Unknown parameter: $param_name')
}
}
// Convert parameter value to corresponding type
match param_type {
'integer' {
match param_format {
'int32' {
param_map[param_name] = param_value.int() // Convert to int
}
'int64' {
param_map[param_name] = param_value.i64() // Convert to i64
}
else {
param_map[param_name] = param_value.int() // Default to int
}
}
}
'string' {
param_map[param_name] = param_value // Already a string
}
'boolean' {
param_map[param_name] = param_value.bool() // Convert to bool
}
'number' {
match param_format {
'float' {
param_map[param_name] = param_value.f32() // Convert to float
}
'double' {
param_map[param_name] = param_value.f64() // Convert to double
}
else {
param_map[param_name] = param_value.f64() // Default to double
}
}
}
else {
param_map[param_name] = param_value // Leave as string for unknown types
}
}
} else {
// If the parameter is not defined in the OpenAPI operation, skip or log it
println('Unknown parameter: ${param_name}')
}
}
// Encode the parameter map to JSON if needed
params << json.encode(param_map.str())
}
// Encode the parameter map to JSON if needed
params << json.encode(param_map.str())
}
call := ProcedureCall{
method: request.operation.operation_id
params: "[${params.join(',')}]" // Keep as a string since ProcedureCall expects a string
}
call := ProcedureCall{
method: request.operation.operation_id
params: '[${params.join(',')}]' // Keep as a string since ProcedureCall expects a string
}
// Process the procedure call
procedure_response := handler.processor.process(
call,
ProcessParams{
timeout: 30 // Set timeout in seconds
}
) or {
// Handle ProcedureError
if err is processor.ProcedureError {
return Response{
status: http.status_from_int(err.code()) // Map ProcedureError reason to HTTP status code
body: json.encode({
'error': err.msg()
})
}
}
return error('Unexpected error: $err')
}
// Process the procedure call
procedure_response := handler.processor.process(call, ProcessParams{
timeout: 30 // Set timeout in seconds
}) or {
// Handle ProcedureError
if err is processor.ProcedureError {
return Response{
status: http.status_from_int(err.code()) // Map ProcedureError reason to HTTP status code
body: json.encode({
'error': err.msg()
})
}
}
return error('Unexpected error: ${err}')
}
// Convert returned procedure response to OpenAPI response
return Response{
status: http.Status.ok // Assuming success if no error
body: procedure_response.result
}
}
// Convert returned procedure response to OpenAPI response
return Response{
status: http.Status.ok // Assuming success if no error
body: procedure_response.result
}
}