...
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
import freeflowuniverse.herolib.clients.zinit
|
import freeflowuniverse.herolib.clients.zinit
|
||||||
import freeflowuniverse.herolib.installers.infra.zinit_installer
|
import freeflowuniverse.herolib.installers.infra.zinit_installer
|
||||||
@@ -38,6 +38,7 @@ mut client := zinit.new()!
|
|||||||
|
|
||||||
println(client)
|
println(client)
|
||||||
|
|
||||||
|
|
||||||
println('✓ Created Zinit RPC client')
|
println('✓ Created Zinit RPC client')
|
||||||
|
|
||||||
// 1. Discover API specification
|
// 1. Discover API specification
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
import freeflowuniverse.herolib.installers.infra.livekit as livekit_installer
|
import freeflowuniverse.herolib.installers.infra.livekit as livekit_installer
|
||||||
|
|
||||||
mut livekit := livekit_installer.get()!
|
mut livekit := livekit_installer.get(create:true)!
|
||||||
livekit.install()!
|
livekit.install()!
|
||||||
livekit.start()!
|
livekit.start()!
|
||||||
livekit.destroy()!
|
livekit.destroy()!
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module zinit
|
module zinit
|
||||||
|
|
||||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||||
import freeflowuniverse.herolib.schemas.jsonrpcmodel
|
import freeflowuniverse.herolib.schemas.openrpc
|
||||||
|
|
||||||
// Helper function to get or create the RPC client
|
// Helper function to get or create the RPC client
|
||||||
fn (mut c ZinitRPC) client_() !&jsonrpc.Client {
|
fn (mut c ZinitRPC) client_() !&jsonrpc.Client {
|
||||||
@@ -13,10 +13,15 @@ fn (mut c ZinitRPC) client_() !&jsonrpc.Client {
|
|||||||
// Admin methods
|
// Admin methods
|
||||||
|
|
||||||
// rpc_discover returns the OpenRPC specification for the API
|
// rpc_discover returns the OpenRPC specification for the API
|
||||||
pub fn (mut c ZinitRPC) rpc_discover() !jsonrpcmodel.OpenRPCSpec {
|
pub fn (mut c ZinitRPC) rpc_discover() !openrpc.OpenRPC {
|
||||||
mut client := c.client_()!
|
mut client := c.client_()!
|
||||||
|
println(1)
|
||||||
request := jsonrpc.new_request_generic('rpc.discover', []string{})
|
request := jsonrpc.new_request_generic('rpc.discover', []string{})
|
||||||
return client.send[[]string, jsonrpcmodel.OpenRPCSpec](request)!
|
println(2)
|
||||||
|
mut r:= client.send[[]string, openrpc.OpenRPC](request)!
|
||||||
|
// dump(r)
|
||||||
|
$dbg;
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// service_list lists all services managed by Zinit
|
// service_list lists all services managed by Zinit
|
||||||
@@ -97,10 +102,7 @@ pub fn (mut c ZinitRPC) service_create(name string, config ServiceConfig) !strin
|
|||||||
name: name
|
name: name
|
||||||
content: config
|
content: config
|
||||||
}
|
}
|
||||||
println(params)
|
|
||||||
$dbg;
|
|
||||||
request := jsonrpc.new_request_generic('service_create', params)
|
request := jsonrpc.new_request_generic('service_create', params)
|
||||||
$dbg;
|
|
||||||
return client.send[ServiceCreateParams, string](request)!
|
return client.send[ServiceCreateParams, string](request)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ pub fn get(args ArgsGet) !&${args.classname} {
|
|||||||
if r.hexists('context:${args.name}', args.name)! {
|
if r.hexists('context:${args.name}', args.name)! {
|
||||||
data := r.hget('context:${args.name}', args.name)!
|
data := r.hget('context:${args.name}', args.name)!
|
||||||
if data.len == 0 {
|
if data.len == 0 {
|
||||||
|
print_backtrace()
|
||||||
return error('${args.classname} with name: ${args.name} does not exist, prob bug.')
|
return error('${args.classname} with name: ${args.name} does not exist, prob bug.')
|
||||||
}
|
}
|
||||||
mut obj := json.decode(${args.classname},data)!
|
mut obj := json.decode(${args.classname},data)!
|
||||||
@@ -62,12 +63,14 @@ pub fn get(args ArgsGet) !&${args.classname} {
|
|||||||
if args.create {
|
if args.create {
|
||||||
new(args)!
|
new(args)!
|
||||||
}else{
|
}else{
|
||||||
|
print_backtrace()
|
||||||
return error("${args.classname} with name '${args.name}' does not exist")
|
return error("${args.classname} with name '${args.name}' does not exist")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return get(name: args.name)! //no longer from db nor create
|
return get(name: args.name)! //no longer from db nor create
|
||||||
}
|
}
|
||||||
return ${args.name}_global[args.name] or {
|
return ${args.name}_global[args.name] or {
|
||||||
|
print_backtrace()
|
||||||
return error('could not get config for ${args.name} with name:${args.name}')
|
return error('could not get config for ${args.name} with name:${args.name}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,18 +223,18 @@ fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.Sta
|
|||||||
// systemd
|
// systemd
|
||||||
match cat{
|
match cat{
|
||||||
.screen {
|
.screen {
|
||||||
console.print_debug("startupmanager: screen")
|
console.print_debug("installer: ${args.name}' startupmanager get screen")
|
||||||
return startupmanager.get(.screen)!
|
return startupmanager.get(.screen)!
|
||||||
}
|
}
|
||||||
.zinit{
|
.zinit{
|
||||||
console.print_debug("startupmanager: zinit")
|
console.print_debug("installer: ${args.name}' startupmanager get zinit")
|
||||||
return startupmanager.get(.zinit)!
|
return startupmanager.get(.zinit)!
|
||||||
}
|
}
|
||||||
.systemd{
|
.systemd{
|
||||||
console.print_debug("startupmanager: systemd")
|
console.print_debug("installer: ${args.name}' startupmanager get systemd")
|
||||||
return startupmanager.get(.systemd)!
|
return startupmanager.get(.systemd)!
|
||||||
}else{
|
}else{
|
||||||
console.print_debug("startupmanager: auto")
|
console.print_debug("installer: ${args.name}' startupmanager get auto")
|
||||||
return startupmanager.get(.auto)!
|
return startupmanager.get(.auto)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +260,7 @@ pub fn (mut self ${args.classname}) start() ! {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.print_header('${args.name} start')
|
console.print_header('installer: ${args.name} start')
|
||||||
|
|
||||||
if ! installed()!{
|
if ! installed()!{
|
||||||
install()!
|
install()!
|
||||||
@@ -270,7 +273,7 @@ pub fn (mut self ${args.classname}) start() ! {
|
|||||||
for zprocess in startupcmd()!{
|
for zprocess in startupcmd()!{
|
||||||
mut sm:=startupmanager_get(zprocess.startuptype)!
|
mut sm:=startupmanager_get(zprocess.startuptype)!
|
||||||
|
|
||||||
console.print_debug('starting ${args.name} with ??{zprocess.startuptype}...')
|
console.print_debug('installer: ${args.name} starting with ??{zprocess.startuptype}...')
|
||||||
|
|
||||||
sm.new(zprocess)!
|
sm.new(zprocess)!
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(cat StartupManagerType) !StartupManager {
|
pub fn get(cat StartupManagerType) !StartupManager {
|
||||||
console.print_debug('startupmanager get ${cat}')
|
console.print_debug('startupmanager (factory): get ${cat}')
|
||||||
mut sm := StartupManager{
|
mut sm := StartupManager{
|
||||||
cat: cat
|
cat: cat
|
||||||
}
|
}
|
||||||
@@ -70,11 +70,11 @@ pub fn (mut sm StartupManager) new(args ZProcessNewArgs) ! {
|
|||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
.zinit {
|
.zinit {
|
||||||
console.print_debug('zinit start ${args.name}.')
|
console.print_debug('startupmanager: zinit process create ${args.name}.')
|
||||||
// Get the Zinit RPC client instance.
|
// Get the Zinit RPC client instance.
|
||||||
// We assume it's properly configured (e.g., socket_path) via its factory setup.
|
// We assume it's properly configured (e.g., socket_path) via its factory setup.
|
||||||
mut zinit_client := zinit.get(create: true)!
|
mut zinit_client := zinit.get(create: true)!
|
||||||
|
|
||||||
// Map ZProcessNewArgs to zinit.ServiceConfig
|
// Map ZProcessNewArgs to zinit.ServiceConfig
|
||||||
mut service_config := zinit.ServiceConfig{
|
mut service_config := zinit.ServiceConfig{
|
||||||
exec: args.cmd
|
exec: args.cmd
|
||||||
@@ -89,8 +89,9 @@ pub fn (mut sm StartupManager) new(args ZProcessNewArgs) ! {
|
|||||||
|
|
||||||
// Create the service configuration file in zinit
|
// Create the service configuration file in zinit
|
||||||
zinit_client.service_create(args.name, service_config) or {
|
zinit_client.service_create(args.name, service_config) or {
|
||||||
return error('Failed to create zinit service ${args.name}: ${err}')
|
return error('startupmanager: failed to create zinit service ${args.name}: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
panic('to implement, startup manager only support screen & systemd for now: ${mycat}')
|
panic('to implement, startup manager only support screen & systemd for now: ${mycat}')
|
||||||
}
|
}
|
||||||
@@ -102,35 +103,31 @@ pub fn (mut sm StartupManager) new(args ZProcessNewArgs) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut sm StartupManager) start(name string) ! {
|
pub fn (mut sm StartupManager) start(name string) ! {
|
||||||
println(sm)
|
|
||||||
$dbg;
|
|
||||||
match sm.cat {
|
match sm.cat {
|
||||||
.screen {
|
.screen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
.systemd {
|
.systemd {
|
||||||
console.print_debug('systemd process start ${name}')
|
console.print_debug('startupmanager: systemd process start ${name}')
|
||||||
mut systemdfactory := systemd.new()!
|
mut systemdfactory := systemd.new()!
|
||||||
$dbg;
|
|
||||||
if systemdfactory.exists(name) {
|
if systemdfactory.exists(name) {
|
||||||
// console.print_header("*************")
|
// console.print_header("*************")
|
||||||
mut systemdprocess := systemdfactory.get(name)!
|
mut systemdprocess := systemdfactory.get(name)!
|
||||||
systemdprocess.start()!
|
systemdprocess.start()!
|
||||||
$dbg;
|
|
||||||
} else {
|
} else {
|
||||||
return error('process in systemd with name ${name} not found')
|
return error('process in systemd with name ${name} not found')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.zinit {
|
.zinit {
|
||||||
console.print_debug('zinit process start ${name} using clients.zinit')
|
console.print_debug('startupmanager: zinit process start ${name}')
|
||||||
mut zinit_client := zinit.get()! // Get the already configured zinit client
|
mut zinit_client := zinit.get()! // Get the already configured zinit client
|
||||||
zinit_client.service_start(name) or {
|
zinit_client.service_start(name) or {
|
||||||
return error('Failed to start zinit service ${name}: ${err}')
|
return error('startupmanager: Failed to start zinit service ${name}: ${err}')
|
||||||
}
|
}
|
||||||
// Monitor loads the config, if it's new it starts it.
|
// Monitor loads the config, if it's new it starts it.
|
||||||
// If the service is already managed, this will bring it back up.
|
// If the service is already managed, this will bring it back up.
|
||||||
zinit_client.service_monitor(name) or {
|
zinit_client.service_monitor(name) or {
|
||||||
return error('Failed to monitor zinit service ${name}: ${err}')
|
return error('startupmanager: Failed to monitor zinit service ${name}: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -243,7 +240,7 @@ pub fn (mut sm StartupManager) status(name string) !ProcessStatus {
|
|||||||
.screen {
|
.screen {
|
||||||
mut screen_factory := screen.new(reset: false)!
|
mut screen_factory := screen.new(reset: false)!
|
||||||
mut scr := screen_factory.get(name) or {
|
mut scr := screen_factory.get(name) or {
|
||||||
return error('process with name ${name} not found')
|
return error('startup manager: failed to get status of process ${name}\n${err} in screen.')
|
||||||
}
|
}
|
||||||
match scr.status()! {
|
match scr.status()! {
|
||||||
.active { return .active }
|
.active { return .active }
|
||||||
@@ -255,13 +252,13 @@ pub fn (mut sm StartupManager) status(name string) !ProcessStatus {
|
|||||||
mut systemdfactory := systemd.new()!
|
mut systemdfactory := systemd.new()!
|
||||||
mut systemdprocess := systemdfactory.get(name) or { return .unknown }
|
mut systemdprocess := systemdfactory.get(name) or { return .unknown }
|
||||||
systemd_status := systemdprocess.status() or {
|
systemd_status := systemdprocess.status() or {
|
||||||
return error('Failed to get status of process ${name}\n${err}')
|
return error('startup manager: failed to get status of process ${name}\n${err} in systemd.')
|
||||||
}
|
}
|
||||||
s := ProcessStatus.from(systemd_status.str())!
|
s := ProcessStatus.from(systemd_status.str())!
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
.zinit {
|
.zinit {
|
||||||
console.print_debug('zinit status ${name} using clients.zinit')
|
console.print_debug('startup manager: zinit status ${name}.')
|
||||||
mut zinit_client := zinit.get()!
|
mut zinit_client := zinit.get()!
|
||||||
// Attempt to get the service status. Handle "Service not found" as .unknown.
|
// Attempt to get the service status. Handle "Service not found" as .unknown.
|
||||||
status_info := zinit_client.service_status(name) or {
|
status_info := zinit_client.service_status(name) or {
|
||||||
@@ -269,7 +266,7 @@ pub fn (mut sm StartupManager) status(name string) !ProcessStatus {
|
|||||||
if err_val.contains('service not found') {
|
if err_val.contains('service not found') {
|
||||||
return .unknown
|
return .unknown
|
||||||
} else {
|
} else {
|
||||||
return error('Failed to get zinit service status: ${err}')
|
return error('startup manager: failed to get status of process ${name}\n${err} in zinit.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +290,7 @@ pub fn (mut sm StartupManager) status(name string) !ProcessStatus {
|
|||||||
return .inactive
|
return .inactive
|
||||||
} // 'restarted' here means it's about to be 'running' again, but in the context of a single status check it might be transient. For simplicity map it to inactive here.
|
} // 'restarted' here means it's about to be 'running' again, but in the context of a single status check it might be transient. For simplicity map it to inactive here.
|
||||||
else {
|
else {
|
||||||
console.print_debug('Unknown Zinit state for ${name}: ${status_info.state}')
|
console.print_debug('startup manager: unknown zinit state for ${name}: ${status_info.state} zinit')
|
||||||
return .unknown
|
return .unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,7 +355,7 @@ pub fn (mut sm StartupManager) exists(name string) !bool {
|
|||||||
}
|
}
|
||||||
.zinit {
|
.zinit {
|
||||||
console.print_debug('zinit exists ${name} using clients.zinit')
|
console.print_debug('zinit exists ${name} using clients.zinit')
|
||||||
mut zinit_client := zinit.get()!
|
mut zinit_client := zinit.get(create:true)!
|
||||||
zinit_client.service_status(name) or { return false }
|
zinit_client.service_status(name) or { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ pub fn (mut self SystemdProcess) start() ! {
|
|||||||
// If we get here, service didn't start in time
|
// If we get here, service didn't start in time
|
||||||
logs := self.get_logs(50)!
|
logs := self.get_logs(50)!
|
||||||
return error('Service ${self.name} did not start within expected time. Status: ${self.status()!}. Recent logs:\n${logs}')
|
return error('Service ${self.name} did not start within expected time. Status: ${self.status()!}. Recent logs:\n${logs}')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get status from system
|
// get status from system
|
||||||
@@ -162,6 +163,8 @@ pub fn (self SystemdProcess) status() !SystemdStatus {
|
|||||||
cmd := 'systemctl is-active ${name_fix(self.name)}'
|
cmd := 'systemctl is-active ${name_fix(self.name)}'
|
||||||
|
|
||||||
job := osal.exec(cmd: cmd, stdout: false, ignore_error: true)!
|
job := osal.exec(cmd: cmd, stdout: false, ignore_error: true)!
|
||||||
|
|
||||||
|
// console.print_debug("${cmd} \n***\n${job.output}\n***")
|
||||||
|
|
||||||
match job.output.trim_space() {
|
match job.output.trim_space() {
|
||||||
'active' { return .active }
|
'active' { return .active }
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ mut:
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - The response string or an error if the send operation fails
|
// - The response string or an error if the send operation fails
|
||||||
send(request string, params SendParams) !string
|
send(request string, params SendParams) !string
|
||||||
|
url()string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendParams defines configuration options for sending JSON-RPC requests.
|
// SendParams defines configuration options for sending JSON-RPC requests.
|
||||||
@@ -65,12 +66,19 @@ pub fn new_client(transport IRPCTransportClient) &Client {
|
|||||||
// - The response result of type D or an error if any step in the process fails
|
// - The response result of type D or an error if any step in the process fails
|
||||||
pub fn (mut c Client) send[T, D](request RequestGeneric[T], params SendParams) !D {
|
pub fn (mut c Client) send[T, D](request RequestGeneric[T], params SendParams) !D {
|
||||||
// Send the encoded request through the transport layer
|
// Send the encoded request through the transport layer
|
||||||
console.print_debug('Sending request: ${request.encode()}')
|
console.print_debug('Sending request: \n*****\n${request.encode()}\n*****\n')
|
||||||
response_json := c.transport.send(request.encode(), params)!
|
response_json := c.transport.send(request.encode(), params) or {
|
||||||
|
if err.msg().contains("net: op timed out"){
|
||||||
|
console.print_debug("time out")
|
||||||
|
}
|
||||||
|
// print_backtrace()
|
||||||
|
// println(err)
|
||||||
|
// $dbg;
|
||||||
|
return error('Failed to send request for jsonrpc:\n${request.encode()}\n${err}')
|
||||||
|
}
|
||||||
// Decode the response JSON into a strongly-typed response object
|
// Decode the response JSON into a strongly-typed response object
|
||||||
response := decode_response_generic[D](response_json) or {
|
response := decode_response_generic[D](response_json) or {
|
||||||
return error('Unable to decode response.\n- Response: ${response_json}\n- Error: ${err}')
|
return error('Unable to decode response.\nRequest: ${request.encode()}\nResponse: ${response_json}\nError: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the response according to the JSON-RPC specification
|
// Validate the response according to the JSON-RPC specification
|
||||||
@@ -82,5 +90,16 @@ pub fn (mut c Client) send[T, D](request RequestGeneric[T], params SendParams) !
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the result or propagate any error from the response
|
// Return the result or propagate any error from the response
|
||||||
return response.result()!
|
return response.result() or {
|
||||||
|
myerror := response.error_ or { return error('Failed to get error from response:\nRequest: ${request.encode()}\nResponse: ${response_json}\n${err}') }
|
||||||
|
// print_backtrace()
|
||||||
|
mut myreq:=request.encode()
|
||||||
|
if c.transport is UnixSocketTransport {
|
||||||
|
myreq="To Test:\n**********\necho '\n${myreq}\n' | nc -U ${c.transport.url()}\n**********"
|
||||||
|
} else {
|
||||||
|
myreq="Path:${c.transport.url()}\nRequest:\n${myreq}"
|
||||||
|
}
|
||||||
|
return error('\nRPC Request Failed.\n${myreq}\n${myerror}')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub fn decode_request(data string) !Request {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - A JSON string representation of the Request
|
// - A JSON string representation of the Request
|
||||||
pub fn (req Request) encode() string {
|
pub fn (req Request) encode() string {
|
||||||
return json2.encode(req)
|
return json2.encode_pretty(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate checks if the Request object contains all required fields
|
// validate checks if the Request object contains all required fields
|
||||||
@@ -158,5 +158,5 @@ pub fn decode_request_generic[T](data string) !RequestGeneric[T] {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - A JSON string representation of the RequestGeneric object
|
// - A JSON string representation of the RequestGeneric object
|
||||||
pub fn (req RequestGeneric[T]) encode[T]() string {
|
pub fn (req RequestGeneric[T]) encode[T]() string {
|
||||||
return json2.encode(req)
|
return json2.encode_pretty(req).replace('\\/', '/')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,8 +70,10 @@ pub fn decode_response(data string) !Response {
|
|||||||
|
|
||||||
// Validate that the response contains either result or error, but not both or neither
|
// Validate that the response contains either result or error, but not both or neither
|
||||||
if 'error' !in raw_map.keys() && 'result' !in raw_map.keys() {
|
if 'error' !in raw_map.keys() && 'result' !in raw_map.keys() {
|
||||||
|
print_backtrace()
|
||||||
return error('Invalid JSONRPC response, no error and result found.')
|
return error('Invalid JSONRPC response, no error and result found.')
|
||||||
} else if 'error' in raw_map.keys() && 'result' in raw_map.keys() {
|
} else if 'error' in raw_map.keys() && 'result' in raw_map.keys() {
|
||||||
|
print_backtrace()
|
||||||
return error('Invalid JSONRPC response, both error and result found.')
|
return error('Invalid JSONRPC response, both error and result found.')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +89,10 @@ pub fn decode_response(data string) !Response {
|
|||||||
|
|
||||||
// Handle successful responses
|
// Handle successful responses
|
||||||
return Response{
|
return Response{
|
||||||
id: raw_map['id'] or { return error('Invalid JSONRPC response, no ID Field found') }.int()
|
id: raw_map['id'] or {
|
||||||
|
print_backtrace()
|
||||||
|
return error('Invalid JSONRPC response, no ID Field found')
|
||||||
|
}.int()
|
||||||
jsonrpc: jsonrpc_version
|
jsonrpc: jsonrpc_version
|
||||||
result: raw_map['result']!.str()
|
result: raw_map['result']!.str()
|
||||||
}
|
}
|
||||||
@@ -113,12 +118,15 @@ pub fn (resp Response) encode() string {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - An error if validation fails, otherwise nothing
|
// - An error if validation fails, otherwise nothing
|
||||||
pub fn (resp Response) validate() ! {
|
pub fn (resp Response) validate() ! {
|
||||||
// Note: This validation is currently commented out but should be implemented
|
if resp.error_ != none && resp.result != none {
|
||||||
// if err := resp.error_ && resp.result != '' {
|
return error('Response contains both error and result.\n- Error: ${resp.error_.str()}\n- Result: ${resp.result}')
|
||||||
// return error('Response contains both error and result.\n- Error: ${resp.error_.str()}\n- Result: ${resp.result}')
|
}
|
||||||
// }
|
if resp.error_ == none && resp.result == none {
|
||||||
}
|
return error('Response contains neither error nor result.')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// is_error checks if the response contains an error.
|
// is_error checks if the response contains an error.
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
@@ -202,15 +210,15 @@ pub fn new_response_generic[D](id int, result D) ResponseGeneric[D] {
|
|||||||
// Returns:
|
// Returns:
|
||||||
// - A ResponseGeneric object with result of type D, or an error if parsing fails
|
// - A ResponseGeneric object with result of type D, or an error if parsing fails
|
||||||
pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
||||||
// Debug output - consider removing in production
|
|
||||||
|
|
||||||
raw := json2.raw_decode(data)!
|
raw := json2.raw_decode(data)!
|
||||||
raw_map := raw.as_map()
|
raw_map := raw.as_map()
|
||||||
|
|
||||||
// Validate that the response contains either result or error, but not both or neither
|
// Validate that the response contains either result or error, but not both or neither
|
||||||
if 'error' !in raw_map.keys() && 'result' !in raw_map.keys() {
|
if 'error' !in raw_map.keys() && 'result' !in raw_map.keys() {
|
||||||
|
print_backtrace()
|
||||||
return error('Invalid JSONRPC response, no error and result found.')
|
return error('Invalid JSONRPC response, no error and result found.')
|
||||||
} else if 'error' in raw_map.keys() && 'result' in raw_map.keys() {
|
} else if 'error' in raw_map.keys() && 'result' in raw_map.keys() {
|
||||||
|
print_backtrace()
|
||||||
return error('Invalid JSONRPC response, both error and result found.')
|
return error('Invalid JSONRPC response, both error and result found.')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +226,7 @@ pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
|||||||
if err := raw_map['error'] {
|
if err := raw_map['error'] {
|
||||||
return ResponseGeneric[D]{
|
return ResponseGeneric[D]{
|
||||||
id: raw_map['id'] or {
|
id: raw_map['id'] or {
|
||||||
|
print_backtrace()
|
||||||
return error('Invalid JSONRPC response, no ID Field found')
|
return error('Invalid JSONRPC response, no ID Field found')
|
||||||
}.int()
|
}.int()
|
||||||
jsonrpc: jsonrpc_version
|
jsonrpc: jsonrpc_version
|
||||||
@@ -228,7 +237,10 @@ pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
|||||||
// Handle successful responses
|
// Handle successful responses
|
||||||
resp := json.decode(ResponseGeneric[D], data)!
|
resp := json.decode(ResponseGeneric[D], data)!
|
||||||
return ResponseGeneric[D]{
|
return ResponseGeneric[D]{
|
||||||
id: raw_map['id'] or { return error('Invalid JSONRPC response, no ID Field found') }.int()
|
id: raw_map['id'] or {
|
||||||
|
print_backtrace()
|
||||||
|
return error('Invalid JSONRPC response, no ID Field found')
|
||||||
|
}.int()
|
||||||
jsonrpc: jsonrpc_version
|
jsonrpc: jsonrpc_version
|
||||||
result: resp.result
|
result: resp.result
|
||||||
}
|
}
|
||||||
@@ -249,6 +261,7 @@ pub fn (resp ResponseGeneric[D]) encode() string {
|
|||||||
// - An error if validation fails, otherwise nothing
|
// - An error if validation fails, otherwise nothing
|
||||||
pub fn (resp ResponseGeneric[D]) validate() ! {
|
pub fn (resp ResponseGeneric[D]) validate() ! {
|
||||||
if resp.is_error() && resp.is_result() {
|
if resp.is_error() && resp.is_result() {
|
||||||
|
print_backtrace()
|
||||||
return error('Response contains both error and result.\n- Error: ${resp.error.str()}\n- Result: ${resp.result}')
|
return error('Response contains both error and result.\n- Error: ${resp.error.str()}\n- Result: ${resp.result}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module jsonrpc
|
|||||||
import net.unix
|
import net.unix
|
||||||
import time
|
import time
|
||||||
import net
|
import net
|
||||||
|
import freeflowuniverse.herolib.ui.console
|
||||||
|
|
||||||
// UnixSocketTransport implements the IRPCTransportClient interface for Unix domain sockets
|
// UnixSocketTransport implements the IRPCTransportClient interface for Unix domain sockets
|
||||||
struct UnixSocketTransport {
|
struct UnixSocketTransport {
|
||||||
@@ -17,6 +18,10 @@ pub fn new_unix_socket_transport(socket_path string) &UnixSocketTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut t UnixSocketTransport) url() string {
|
||||||
|
return '${t.socket_path}'
|
||||||
|
}
|
||||||
|
|
||||||
// send implements the IRPCTransportClient interface
|
// send implements the IRPCTransportClient interface
|
||||||
pub fn (mut t UnixSocketTransport) send(request string, params SendParams) !string {
|
pub fn (mut t UnixSocketTransport) send(request string, params SendParams) !string {
|
||||||
// Create a Unix domain socket client
|
// Create a Unix domain socket client
|
||||||
@@ -28,7 +33,8 @@ pub fn (mut t UnixSocketTransport) send(request string, params SendParams) !stri
|
|||||||
// Close the socket explicitly
|
// Close the socket explicitly
|
||||||
unix.shutdown(socket.sock.handle)
|
unix.shutdown(socket.sock.handle)
|
||||||
socket.close() or {}
|
socket.close() or {}
|
||||||
// console.print_debug('Socket closed')
|
print_backtrace()
|
||||||
|
console.print_debug('The server did not close the socket, we did timeout or there was other error.')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set timeout if specified
|
// Set timeout if specified
|
||||||
@@ -51,19 +57,35 @@ pub fn (mut t UnixSocketTransport) send(request string, params SendParams) !stri
|
|||||||
// console.print_debug('Reading response from socket...')
|
// console.print_debug('Reading response from socket...')
|
||||||
// Read up to 64000 bytes
|
// Read up to 64000 bytes
|
||||||
mut res := []u8{len: 64000, cap: 64000}
|
mut res := []u8{len: 64000, cap: 64000}
|
||||||
n := socket.read(mut res)!
|
n := socket.read(mut res) or {
|
||||||
|
//can be timeout
|
||||||
|
if err.code() == 11 { // Resource temporarily unavailable (EWOULDBLOCK)
|
||||||
|
console.print_debug('Resource temporarily unavailable, retrying...')
|
||||||
|
time.sleep(100 * time.millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err.code() == 9 {
|
||||||
|
console.print_debug('Timeout...')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
// console.print_debug('Read ${n} bytes from socket')
|
// console.print_debug('Read ${n} bytes from socket')
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
// console.print_debug('No more data to read, breaking loop')
|
// breaking loop')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Append the newly read data to the total response
|
// Append the newly read data to the total response
|
||||||
res_total << res[..n]
|
res_total << res[..n]
|
||||||
if n < 8192 {
|
if n < 8192 {
|
||||||
// console.print_debug('No more data to read, breaking loop after ${n} bytes')
|
// console.print_debug('No more data to read, breaking loop after ${n} bytes')
|
||||||
|
//TODO: this seems weird
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unix.shutdown(socket.sock.handle)
|
||||||
|
socket.close() or {}
|
||||||
|
|
||||||
// println(res_total.bytestr().trim_space())
|
// println(res_total.bytestr().trim_space())
|
||||||
|
|
||||||
// println(19)
|
// println(19)
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
module jsonrpcmodel
|
|
||||||
|
|
||||||
// OpenRPCSpec represents the OpenRPC specification structure
|
|
||||||
pub struct OpenRPCSpec {
|
|
||||||
pub mut:
|
|
||||||
openrpc string @[json: 'openrpc'] // OpenRPC version
|
|
||||||
info OpenRPCInfo @[json: 'info'] // API information
|
|
||||||
methods []OpenRPCMethod @[json: 'methods'] // Available methods
|
|
||||||
servers []OpenRPCServer @[json: 'servers'] // Server information
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenRPCInfo represents API information
|
|
||||||
pub struct OpenRPCInfo {
|
|
||||||
pub mut:
|
|
||||||
version string @[json: 'version'] // API version
|
|
||||||
title string @[json: 'title'] // API title
|
|
||||||
description string @[json: 'description'] // API description
|
|
||||||
license OpenRPCLicense @[json: 'license'] // License information
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenRPCLicense represents license information
|
|
||||||
pub struct OpenRPCLicense {
|
|
||||||
pub mut:
|
|
||||||
name string @[json: 'name'] // License name
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenRPCMethod represents an RPC method
|
|
||||||
pub struct OpenRPCMethod {
|
|
||||||
pub mut:
|
|
||||||
name string @[json: 'name'] // Method name
|
|
||||||
description string @[json: 'description'] // Method description
|
|
||||||
// Note: params and result are dynamic and would need more complex handling
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenRPCServer represents server information
|
|
||||||
pub struct OpenRPCServer {
|
|
||||||
pub mut:
|
|
||||||
name string @[json: 'name'] // Server name
|
|
||||||
url string @[json: 'url'] // Server URL
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,7 @@ OpenRPC V library. Model for OpenRPC, client code generation, and specification
|
|||||||
## Definitions
|
## Definitions
|
||||||
|
|
||||||
- OpenRPC Specifications: Specifications that define standards for describing JSON-RPC API's.
|
- OpenRPC Specifications: Specifications that define standards for describing JSON-RPC API's.
|
||||||
|
|
||||||
- [OpenRPC Document](https://spec.open-rpc.org/#openrpc-document): "A document that defines or describes an API conforming to the OpenRPC Specification."
|
- [OpenRPC Document](https://spec.open-rpc.org/#openrpc-document): "A document that defines or describes an API conforming to the OpenRPC Specification."
|
||||||
|
|
||||||
- OpenRPC Client: An API Client (using either HTTP or Websocket) that governs functions (one per RPC Method defined in OpenRPC Document) to communicate with RPC servers and perform RPCs.
|
- OpenRPC Client: An API Client (using either HTTP or Websocket) that governs functions (one per RPC Method defined in OpenRPC Document) to communicate with RPC servers and perform RPCs.
|
||||||
|
|
||||||
## OpenRPC Document Generation
|
## OpenRPC Document Generation
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user