Compare commits
No commits in common. "dfd6931c5b02bca53510126fb1b7976b34eb25de" and "7856fc0a4e397d82e42108cd85d66f4027284f81" have entirely different histories.
dfd6931c5b
...
7856fc0a4e
81
Cargo.toml
81
Cargo.toml
@ -12,20 +12,23 @@ readme = "README.md"
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"packages/clients/myceliumclient",
|
".",
|
||||||
"packages/clients/postgresclient",
|
"vault",
|
||||||
"packages/clients/redisclient",
|
"git",
|
||||||
"packages/clients/zinitclient",
|
"redisclient",
|
||||||
"packages/core/net",
|
"mycelium",
|
||||||
"packages/core/text",
|
"text",
|
||||||
"packages/crypt/vault",
|
"os",
|
||||||
"packages/system/git",
|
"net",
|
||||||
"packages/system/kubernetes",
|
"zinit_client",
|
||||||
"packages/system/os",
|
"process",
|
||||||
"packages/system/process",
|
"virt",
|
||||||
"packages/system/virt",
|
"zos",
|
||||||
|
"postgresclient",
|
||||||
|
"kubernetes",
|
||||||
"rhai",
|
"rhai",
|
||||||
"herodo",
|
"herodo",
|
||||||
|
"service_manager",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
@ -68,10 +71,6 @@ chacha20poly1305 = "0.10.1"
|
|||||||
k256 = { version = "0.13.4", features = ["ecdsa", "ecdh"] }
|
k256 = { version = "0.13.4", features = ["ecdsa", "ecdh"] }
|
||||||
sha2 = "0.10.7"
|
sha2 = "0.10.7"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
bincode = { version = "2.0.1", features = ["serde"] }
|
|
||||||
pbkdf2 = "0.12.2"
|
|
||||||
getrandom = { version = "0.3.3", features = ["wasm_js"] }
|
|
||||||
tera = "1.19.0"
|
|
||||||
|
|
||||||
# Ethereum dependencies
|
# Ethereum dependencies
|
||||||
ethers = { version = "2.0.7", features = ["legacy"] }
|
ethers = { version = "2.0.7", features = ["legacy"] }
|
||||||
@ -88,39 +87,27 @@ windows = { version = "0.61.1", features = [
|
|||||||
zinit-client = "0.4.0"
|
zinit-client = "0.4.0"
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
tokio-test = "0.4.4"
|
tokio-test = "0.4.4"
|
||||||
kube = { version = "0.95.0", features = ["client", "config", "derive"] }
|
|
||||||
k8s-openapi = { version = "0.23.0", features = ["latest"] }
|
|
||||||
tokio-retry = "0.3.0"
|
|
||||||
governor = "0.6.3"
|
|
||||||
tower = { version = "0.5.2", features = ["timeout", "limit"] }
|
|
||||||
serde_yaml = "0.9"
|
|
||||||
postgres-types = "0.2.5"
|
|
||||||
r2d2 = "0.8.10"
|
|
||||||
|
|
||||||
# SAL dependencies
|
|
||||||
sal-text = { path = "packages/core/text" }
|
|
||||||
sal-os = { path = "packages/system/os" }
|
|
||||||
sal-process = { path = "packages/system/process" }
|
|
||||||
sal-virt = { path = "packages/system/virt" }
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = { workspace = true }
|
thiserror = "2.0.12" # For error handling in the main Error enum
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true } # For async examples
|
||||||
|
|
||||||
# Optional dependencies - users can choose which modules to include
|
# Optional dependencies - users can choose which modules to include
|
||||||
sal-git = { path = "packages/system/git", optional = true }
|
sal-git = { path = "git", optional = true }
|
||||||
sal-kubernetes = { path = "packages/system/kubernetes", optional = true }
|
sal-kubernetes = { path = "kubernetes", optional = true }
|
||||||
sal-redisclient = { path = "packages/clients/redisclient", optional = true }
|
sal-redisclient = { path = "redisclient", optional = true }
|
||||||
sal-mycelium = { path = "packages/clients/myceliumclient", optional = true }
|
sal-mycelium = { path = "mycelium", optional = true }
|
||||||
sal-text = { path = "packages/core/text", optional = true }
|
sal-text = { path = "text", optional = true }
|
||||||
sal-os = { path = "packages/system/os", optional = true }
|
sal-os = { path = "os", optional = true }
|
||||||
sal-net = { path = "packages/core/net", optional = true }
|
sal-net = { path = "net", optional = true }
|
||||||
sal-zinit-client = { path = "packages/clients/zinitclient", optional = true }
|
sal-zinit-client = { path = "zinit_client", optional = true }
|
||||||
sal-process = { path = "packages/system/process", optional = true }
|
sal-process = { path = "process", optional = true }
|
||||||
sal-virt = { path = "packages/system/virt", optional = true }
|
sal-virt = { path = "virt", optional = true }
|
||||||
sal-postgresclient = { path = "packages/clients/postgresclient", optional = true }
|
sal-postgresclient = { path = "postgresclient", optional = true }
|
||||||
sal-vault = { path = "packages/crypt/vault", optional = true }
|
sal-vault = { path = "vault", optional = true }
|
||||||
sal-rhai = { path = "rhai", optional = true }
|
sal-rhai = { path = "rhai", optional = true }
|
||||||
|
sal-service-manager = { path = "service_manager", optional = true }
|
||||||
|
zinit-client.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
@ -139,12 +126,12 @@ virt = ["dep:sal-virt"]
|
|||||||
postgresclient = ["dep:sal-postgresclient"]
|
postgresclient = ["dep:sal-postgresclient"]
|
||||||
vault = ["dep:sal-vault"]
|
vault = ["dep:sal-vault"]
|
||||||
rhai = ["dep:sal-rhai"]
|
rhai = ["dep:sal-rhai"]
|
||||||
# service_manager is removed as it's not a direct member anymore
|
service_manager = ["dep:sal-service-manager"]
|
||||||
|
|
||||||
# Convenience feature groups
|
# Convenience feature groups
|
||||||
core = ["os", "process", "text", "net"]
|
core = ["os", "process", "text", "net"]
|
||||||
clients = ["redisclient", "postgresclient", "zinit_client", "mycelium"]
|
clients = ["redisclient", "postgresclient", "zinit_client", "mycelium"]
|
||||||
infrastructure = ["git", "vault", "kubernetes", "virt"]
|
infrastructure = ["git", "vault", "kubernetes", "virt", "service_manager"]
|
||||||
scripting = ["rhai"]
|
scripting = ["rhai"]
|
||||||
all = [
|
all = [
|
||||||
"git",
|
"git",
|
||||||
@ -160,6 +147,7 @@ all = [
|
|||||||
"postgresclient",
|
"postgresclient",
|
||||||
"vault",
|
"vault",
|
||||||
"rhai",
|
"rhai",
|
||||||
|
"service_manager",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
@ -177,4 +165,3 @@ required-features = ["kubernetes"]
|
|||||||
name = "generic_cluster"
|
name = "generic_cluster"
|
||||||
path = "examples/kubernetes/clusters/generic.rs"
|
path = "examples/kubernetes/clusters/generic.rs"
|
||||||
required-features = ["kubernetes"]
|
required-features = ["kubernetes"]
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ license = "Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# Use workspace dependencies for consistency
|
# Use workspace dependencies for consistency
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
|
redis = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
rhai = { workspace = true }
|
rhai = { workspace = true }
|
@ -11,46 +11,46 @@ categories = ["api-bindings", "development-tools"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Kubernetes client library
|
# Kubernetes client library
|
||||||
kube = { workspace = true }
|
kube = { version = "0.95.0", features = ["client", "config", "derive"] }
|
||||||
k8s-openapi = { workspace = true }
|
k8s-openapi = { version = "0.23.0", features = ["latest"] }
|
||||||
|
|
||||||
# Async runtime
|
# Async runtime
|
||||||
tokio = { workspace = true }
|
tokio = { version = "1.45.0", features = ["full"] }
|
||||||
|
|
||||||
# Production safety features
|
# Production safety features
|
||||||
tokio-retry = { workspace = true }
|
tokio-retry = "0.3.0"
|
||||||
governor = { workspace = true }
|
governor = "0.6.3"
|
||||||
tower = { workspace = true }
|
tower = { version = "0.5.2", features = ["timeout", "limit"] }
|
||||||
|
|
||||||
# Error handling
|
# Error handling
|
||||||
thiserror = { workspace = true }
|
thiserror = "2.0.12"
|
||||||
anyhow = { workspace = true }
|
anyhow = "1.0.98"
|
||||||
|
|
||||||
# Serialization
|
# Serialization
|
||||||
serde = { workspace = true }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = "1.0"
|
||||||
serde_yaml = { workspace = true }
|
serde_yaml = "0.9"
|
||||||
|
|
||||||
# Regular expressions for pattern matching
|
# Regular expressions for pattern matching
|
||||||
regex = { workspace = true }
|
regex = "1.10.2"
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
log = { workspace = true }
|
log = "0.4"
|
||||||
|
|
||||||
# Rhai scripting support (optional)
|
# Rhai scripting support (optional)
|
||||||
rhai = { workspace = true, optional = true }
|
rhai = { version = "1.12.0", features = ["sync"], optional = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = "1.20.2"
|
||||||
|
|
||||||
# UUID for resource identification
|
# UUID for resource identification
|
||||||
uuid = { workspace = true }
|
uuid = { version = "1.16.0", features = ["v4"] }
|
||||||
|
|
||||||
# Base64 encoding for secrets
|
# Base64 encoding for secrets
|
||||||
base64 = { workspace = true }
|
base64 = "0.22.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = { workspace = true }
|
tempfile = "3.5"
|
||||||
tokio-test = { workspace = true }
|
tokio-test = "0.4.4"
|
||||||
env_logger = { workspace = true }
|
env_logger = "0.11.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rhai"]
|
default = ["rhai"]
|
@ -9,22 +9,22 @@ license = "Apache-2.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# HTTP client for async requests
|
# HTTP client for async requests
|
||||||
reqwest = { workspace = true }
|
reqwest = { version = "0.12.15", features = ["json"] }
|
||||||
# JSON handling
|
# JSON handling
|
||||||
serde_json = { workspace = true }
|
serde_json = "1.0"
|
||||||
# Base64 encoding/decoding for message payloads
|
# Base64 encoding/decoding for message payloads
|
||||||
base64 = { workspace = true }
|
base64 = "0.22.1"
|
||||||
# Async runtime
|
# Async runtime
|
||||||
tokio = { workspace = true }
|
tokio = { version = "1.45.0", features = ["full"] }
|
||||||
# Rhai scripting support
|
# Rhai scripting support
|
||||||
rhai = { workspace = true }
|
rhai = { version = "1.12.0", features = ["sync"] }
|
||||||
# Logging
|
# Logging
|
||||||
log = { workspace = true }
|
log = "0.4"
|
||||||
# URL encoding for API parameters
|
# URL encoding for API parameters
|
||||||
urlencoding = { workspace = true }
|
urlencoding = "2.1.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# For async testing
|
# For async testing
|
||||||
tokio-test = { workspace = true }
|
tokio-test = "0.4.4"
|
||||||
# For temporary files in tests
|
# For temporary files in tests
|
||||||
tempfile = { workspace = true }
|
tempfile = "3.5"
|
@ -10,7 +10,7 @@ keywords = ["network", "tcp", "http", "ssh", "connectivity"]
|
|||||||
categories = ["network-programming", "api-bindings"]
|
categories = ["network-programming", "api-bindings"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = "1.0.98"
|
||||||
tokio = { workspace = true }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
reqwest = { workspace = true, features = ["json", "blocking"] }
|
reqwest = { version = "0.12", features = ["json", "blocking"] }
|
||||||
rhai = { workspace = true }
|
rhai = "1.19.0"
|
@ -14,8 +14,7 @@ categories = ["os", "filesystem", "api-bindings"]
|
|||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
glob = { workspace = true }
|
glob = { workspace = true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
anyhow = {workspace = true}
|
|
||||||
reqwest = {workspace = true}
|
|
||||||
# Error handling
|
# Error handling
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
@ -1,825 +0,0 @@
|
|||||||
<file_map>
|
|
||||||
/Users/despiegk/code/github/freeflowuniverse/herolib
|
|
||||||
├── aiprompts
|
|
||||||
│ └── herolib_core
|
|
||||||
│ ├── core_ourtime.md
|
|
||||||
│ ├── core_paths.md
|
|
||||||
│ └── core_text.md
|
|
||||||
└── lib
|
|
||||||
└── core
|
|
||||||
└── logger
|
|
||||||
├── factory.v
|
|
||||||
├── log_test.v
|
|
||||||
├── log.v
|
|
||||||
├── model.v
|
|
||||||
├── readme.md
|
|
||||||
└── search.v
|
|
||||||
|
|
||||||
</file_map>
|
|
||||||
|
|
||||||
<file_contents>
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/lib/core/logger/factory.v
|
|
||||||
```v
|
|
||||||
module logger
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
|
|
||||||
pub fn new(path string) !Logger {
|
|
||||||
mut p := pathlib.get_dir(path: path, create: true)!
|
|
||||||
return Logger{
|
|
||||||
path: p
|
|
||||||
lastlog_time: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/lib/core/logger/log_test.v
|
|
||||||
```v
|
|
||||||
module logger
|
|
||||||
|
|
||||||
import os
|
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
|
|
||||||
fn testsuite_begin() {
|
|
||||||
if os.exists('/tmp/testlogs') {
|
|
||||||
os.rmdir_all('/tmp/testlogs')!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_logger() {
|
|
||||||
mut logger := new('/tmp/testlogs')!
|
|
||||||
|
|
||||||
// Test stdout logging
|
|
||||||
logger.log(LogItemArgs{
|
|
||||||
cat: 'test-app'
|
|
||||||
log: 'This is a test message\nWith a second line\nAnd a third line'
|
|
||||||
logtype: .stdout
|
|
||||||
timestamp: ourtime.new('2022-12-05 20:14:35')!
|
|
||||||
})!
|
|
||||||
|
|
||||||
// Test error logging
|
|
||||||
logger.log(LogItemArgs{
|
|
||||||
cat: 'error-test'
|
|
||||||
log: 'This is an error\nWith details'
|
|
||||||
logtype: .error
|
|
||||||
timestamp: ourtime.new('2022-12-05 20:14:35')!
|
|
||||||
})!
|
|
||||||
|
|
||||||
logger.log(LogItemArgs{
|
|
||||||
cat: 'test-app'
|
|
||||||
log: 'This is a test message\nWith a second line\nAnd a third line'
|
|
||||||
logtype: .stdout
|
|
||||||
timestamp: ourtime.new('2022-12-05 20:14:36')!
|
|
||||||
})!
|
|
||||||
|
|
||||||
logger.log(LogItemArgs{
|
|
||||||
cat: 'error-test'
|
|
||||||
log: '
|
|
||||||
This is an error
|
|
||||||
|
|
||||||
With details
|
|
||||||
'
|
|
||||||
logtype: .error
|
|
||||||
timestamp: ourtime.new('2022-12-05 20:14:36')!
|
|
||||||
})!
|
|
||||||
|
|
||||||
logger.log(LogItemArgs{
|
|
||||||
cat: 'error-test'
|
|
||||||
log: '
|
|
||||||
aaa
|
|
||||||
|
|
||||||
bbb
|
|
||||||
'
|
|
||||||
logtype: .error
|
|
||||||
timestamp: ourtime.new('2022-12-05 22:14:36')!
|
|
||||||
})!
|
|
||||||
|
|
||||||
logger.log(LogItemArgs{
|
|
||||||
cat: 'error-test'
|
|
||||||
log: '
|
|
||||||
aaa2
|
|
||||||
|
|
||||||
bbb2
|
|
||||||
'
|
|
||||||
logtype: .error
|
|
||||||
timestamp: ourtime.new('2022-12-05 22:14:36')!
|
|
||||||
})!
|
|
||||||
|
|
||||||
// Verify log directory exists
|
|
||||||
assert os.exists('/tmp/testlogs'), 'Log directory should exist'
|
|
||||||
|
|
||||||
// Get log file
|
|
||||||
files := os.ls('/tmp/testlogs')!
|
|
||||||
assert files.len == 2
|
|
||||||
|
|
||||||
mut file := pathlib.get_file(
|
|
||||||
path: '/tmp/testlogs/${files[0]}'
|
|
||||||
create: false
|
|
||||||
)!
|
|
||||||
|
|
||||||
content := file.read()!.trim_space()
|
|
||||||
|
|
||||||
items_stdout := logger.search(
|
|
||||||
timestamp_from: ourtime.new('2022-11-1 20:14:35')!
|
|
||||||
timestamp_to: ourtime.new('2025-11-1 20:14:35')!
|
|
||||||
logtype: .stdout
|
|
||||||
)!
|
|
||||||
assert items_stdout.len == 2
|
|
||||||
|
|
||||||
items_error := logger.search(
|
|
||||||
timestamp_from: ourtime.new('2022-11-1 20:14:35')!
|
|
||||||
timestamp_to: ourtime.new('2025-11-1 20:14:35')!
|
|
||||||
logtype: .error
|
|
||||||
)!
|
|
||||||
assert items_error.len == 4
|
|
||||||
}
|
|
||||||
|
|
||||||
fn testsuite_end() {
|
|
||||||
// if os.exists('/tmp/testlogs') {
|
|
||||||
// os.rmdir_all('/tmp/testlogs')!
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/lib/core/logger/log.v
|
|
||||||
```v
|
|
||||||
module logger
|
|
||||||
|
|
||||||
import os
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct LogItemArgs {
|
|
||||||
pub mut:
|
|
||||||
timestamp ?ourtime.OurTime
|
|
||||||
cat string
|
|
||||||
log string
|
|
||||||
logtype LogType
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut l Logger) log(args_ LogItemArgs) ! {
|
|
||||||
mut args := args_
|
|
||||||
|
|
||||||
t := args.timestamp or {
|
|
||||||
t2 := ourtime.now()
|
|
||||||
t2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format category (max 10 chars, ascii only)
|
|
||||||
args.cat = texttools.name_fix(args.cat)
|
|
||||||
if args.cat.len > 10 {
|
|
||||||
return error('category cannot be longer than 10 chars')
|
|
||||||
}
|
|
||||||
args.cat = texttools.expand(args.cat, 10, ' ')
|
|
||||||
|
|
||||||
args.log = texttools.dedent(args.log).trim_space()
|
|
||||||
|
|
||||||
mut logfile_path := '${l.path.path}/${t.dayhour()}.log'
|
|
||||||
|
|
||||||
// Create log file if it doesn't exist
|
|
||||||
if !os.exists(logfile_path) {
|
|
||||||
os.write_file(logfile_path, '')!
|
|
||||||
l.lastlog_time = 0 // make sure we put time again
|
|
||||||
}
|
|
||||||
|
|
||||||
mut f := os.open_append(logfile_path)!
|
|
||||||
|
|
||||||
mut content := ''
|
|
||||||
|
|
||||||
// Add timestamp if we're in a new second
|
|
||||||
if t.unix() > l.lastlog_time {
|
|
||||||
content += '\n${t.time().format_ss()}\n'
|
|
||||||
l.lastlog_time = t.unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format log lines
|
|
||||||
error_prefix := if args.logtype == .error { 'E' } else { ' ' }
|
|
||||||
lines := args.log.split('\n')
|
|
||||||
|
|
||||||
for i, line in lines {
|
|
||||||
if i == 0 {
|
|
||||||
content += '${error_prefix} ${args.cat} - ${line}\n'
|
|
||||||
} else {
|
|
||||||
content += '${error_prefix} ${line}\n'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.writeln(content.trim_space_right())!
|
|
||||||
f.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/lib/core/logger/model.v
|
|
||||||
```v
|
|
||||||
module logger
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
|
|
||||||
@[heap]
|
|
||||||
pub struct Logger {
|
|
||||||
pub mut:
|
|
||||||
path pathlib.Path
|
|
||||||
lastlog_time i64 // to see in log format, every second we put a time down, we need to know if we are in a new second (logs can come in much faster)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LogItem {
|
|
||||||
pub mut:
|
|
||||||
timestamp ourtime.OurTime
|
|
||||||
cat string
|
|
||||||
log string
|
|
||||||
logtype LogType
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum LogType {
|
|
||||||
stdout
|
|
||||||
error
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/lib/core/logger/readme.md
|
|
||||||
```md
|
|
||||||
# Logger Module
|
|
||||||
|
|
||||||
A simple logging system that provides structured logging with search capabilities.
|
|
||||||
|
|
||||||
Logs are stored in hourly files with a consistent format that makes them both human-readable and machine-parseable.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Structured logging with categories and error types
|
|
||||||
- Automatic timestamp management
|
|
||||||
- Multi-line message support
|
|
||||||
- Search functionality with filtering options
|
|
||||||
- Human-readable log format
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```v
|
|
||||||
import freeflowuniverse.herolib.core.logger
|
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
|
||||||
|
|
||||||
// Create a new logger
|
|
||||||
mut l := logger.new(path: '/var/logs')!
|
|
||||||
|
|
||||||
// Log a message
|
|
||||||
l.log(
|
|
||||||
cat: 'system',
|
|
||||||
log: 'System started successfully',
|
|
||||||
logtype: .stdout
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Log an error
|
|
||||||
l.log(
|
|
||||||
cat: 'system',
|
|
||||||
log: 'Failed to connect\nRetrying in 5 seconds...',
|
|
||||||
logtype: .error
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Search logs
|
|
||||||
results := l.search(
|
|
||||||
timestamp_from: ourtime.now().warp("-24h"), // Last 24 hours
|
|
||||||
cat: 'system', // Filter by category
|
|
||||||
log: 'failed', // Search in message content
|
|
||||||
logtype: .error, // Only error messages
|
|
||||||
maxitems: 100 // Limit results
|
|
||||||
)!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Log Format
|
|
||||||
|
|
||||||
Each log file is named using the format `YYYY-MM-DD-HH.log` and contains entries in the following format:
|
|
||||||
|
|
||||||
```
|
|
||||||
21:23:42
|
|
||||||
system - This is a normal log message
|
|
||||||
system - This is a multi-line message
|
|
||||||
second line with proper indentation
|
|
||||||
third line maintaining alignment
|
|
||||||
E error_cat - This is an error message
|
|
||||||
E second line of error
|
|
||||||
E third line of error
|
|
||||||
```
|
|
||||||
|
|
||||||
### Format Rules
|
|
||||||
|
|
||||||
- Time stamps (HH:MM:SS) are written once per second when the log time changes
|
|
||||||
- Categories are:
|
|
||||||
- Limited to 10 characters maximum
|
|
||||||
- Padded with spaces to exactly 10 characters
|
|
||||||
- Any `-` in category names are converted to `_`
|
|
||||||
- Each line starts with either:
|
|
||||||
- ` ` (space) for normal logs (LogType.stdout)
|
|
||||||
- `E` for error logs (LogType.error)
|
|
||||||
- Multi-line messages maintain consistent indentation (14 spaces after the prefix)
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/lib/core/logger/search.v
|
|
||||||
```v
|
|
||||||
module logger
|
|
||||||
|
|
||||||
import os
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct SearchArgs {
|
|
||||||
pub mut:
|
|
||||||
timestamp_from ?ourtime.OurTime
|
|
||||||
timestamp_to ?ourtime.OurTime
|
|
||||||
cat string // can be empty
|
|
||||||
log string // any content in here will be looked for
|
|
||||||
logtype LogType
|
|
||||||
maxitems int = 10000
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut l Logger) search(args_ SearchArgs) ![]LogItem {
|
|
||||||
mut args := args_
|
|
||||||
|
|
||||||
// Format category (max 10 chars, ascii only)
|
|
||||||
args.cat = texttools.name_fix(args.cat)
|
|
||||||
if args.cat.len > 10 {
|
|
||||||
return error('category cannot be longer than 10 chars')
|
|
||||||
}
|
|
||||||
|
|
||||||
mut timestamp_from := args.timestamp_from or { ourtime.OurTime{} }
|
|
||||||
mut timestamp_to := args.timestamp_to or { ourtime.OurTime{} }
|
|
||||||
|
|
||||||
// Get time range
|
|
||||||
from_time := timestamp_from.unix()
|
|
||||||
to_time := timestamp_to.unix()
|
|
||||||
if from_time > to_time {
|
|
||||||
return error('from_time cannot be after to_time: ${from_time} < ${to_time}')
|
|
||||||
}
|
|
||||||
|
|
||||||
mut result := []LogItem{}
|
|
||||||
|
|
||||||
// Find log files in time range
|
|
||||||
mut files := os.ls(l.path.path)!
|
|
||||||
files.sort()
|
|
||||||
|
|
||||||
for file in files {
|
|
||||||
if !file.ends_with('.log') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse dayhour from filename
|
|
||||||
dayhour := file[..file.len - 4] // remove .log
|
|
||||||
file_time := ourtime.new(dayhour)!
|
|
||||||
mut current_time := ourtime.OurTime{}
|
|
||||||
mut current_item := LogItem{}
|
|
||||||
mut collecting := false
|
|
||||||
|
|
||||||
// Skip if file is outside time range
|
|
||||||
if file_time.unix() < from_time || file_time.unix() > to_time {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read and parse log file
|
|
||||||
content := os.read_file('${l.path.path}/${file}')!
|
|
||||||
lines := content.split('\n')
|
|
||||||
|
|
||||||
for line in lines {
|
|
||||||
if result.len >= args.maxitems {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
line_trim := line.trim_space()
|
|
||||||
if line_trim == '' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is a timestamp line
|
|
||||||
if !(line.starts_with(' ') || line.starts_with('E')) {
|
|
||||||
current_time = ourtime.new(line_trim)!
|
|
||||||
if collecting {
|
|
||||||
process(mut result, current_item, current_time, args, from_time, to_time)!
|
|
||||||
}
|
|
||||||
collecting = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if collecting && line.len > 14 && line[13] == `-` {
|
|
||||||
process(mut result, current_item, current_time, args, from_time, to_time)!
|
|
||||||
collecting = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse log line
|
|
||||||
is_error := line.starts_with('E')
|
|
||||||
if !collecting {
|
|
||||||
// Start new item
|
|
||||||
current_item = LogItem{
|
|
||||||
timestamp: current_time
|
|
||||||
cat: line[2..12].trim_space()
|
|
||||||
log: line[15..].trim_space()
|
|
||||||
logtype: if is_error { .error } else { .stdout }
|
|
||||||
}
|
|
||||||
// println('new current item: ${current_item}')
|
|
||||||
collecting = true
|
|
||||||
} else {
|
|
||||||
// Continuation line
|
|
||||||
if line_trim.len < 16 {
|
|
||||||
current_item.log += '\n'
|
|
||||||
} else {
|
|
||||||
current_item.log += '\n' + line[15..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add last item if collecting
|
|
||||||
if collecting {
|
|
||||||
process(mut result, current_item, current_time, args, from_time, to_time)!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process(mut result []LogItem, current_item LogItem, current_time ourtime.OurTime, args SearchArgs, from_time i64, to_time i64) ! {
|
|
||||||
// Add previous item if it matches filters
|
|
||||||
log_epoch := current_item.timestamp.unix()
|
|
||||||
if log_epoch < from_time || log_epoch > to_time {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (args.cat == '' || current_item.cat.trim_space() == args.cat)
|
|
||||||
&& (args.log == '' || current_item.log.contains(args.log))
|
|
||||||
&& args.logtype == current_item.logtype {
|
|
||||||
result << current_item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/aiprompts/herolib_core/core_ourtime.md
|
|
||||||
```md
|
|
||||||
# OurTime Module
|
|
||||||
|
|
||||||
The `OurTime` module in V provides flexible time handling, supporting relative and absolute time formats, Unix timestamps, and formatting utilities.
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
- Create time objects from strings or current time
|
|
||||||
- Relative time expressions (e.g., `+1h`, `-2d`)
|
|
||||||
- Absolute time formats (e.g., `YYYY-MM-DD HH:mm:ss`)
|
|
||||||
- Unix timestamp conversion
|
|
||||||
- Time formatting and warping
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```v
|
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
|
||||||
|
|
||||||
// Current time
|
|
||||||
mut t := ourtime.now()
|
|
||||||
|
|
||||||
// From string
|
|
||||||
t2 := ourtime.new('2022-12-05 20:14:35')!
|
|
||||||
|
|
||||||
// Get formatted string
|
|
||||||
println(t2.str()) // e.g., 2022-12-05 20:14
|
|
||||||
|
|
||||||
// Get Unix timestamp
|
|
||||||
println(t2.unix()) // e.g., 1670271275
|
|
||||||
```
|
|
||||||
|
|
||||||
## Time Formats
|
|
||||||
|
|
||||||
### Relative Time
|
|
||||||
|
|
||||||
Use `s` (seconds), `h` (hours), `d` (days), `w` (weeks), `M` (months), `Q` (quarters), `Y` (years).
|
|
||||||
|
|
||||||
```v
|
|
||||||
// Create with relative time
|
|
||||||
mut t := ourtime.new('+1w +2d -4h')!
|
|
||||||
|
|
||||||
// Warp existing time
|
|
||||||
mut t2 := ourtime.now()
|
|
||||||
t2.warp('+1h')!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Absolute Time
|
|
||||||
|
|
||||||
Supports `YYYY-MM-DD HH:mm:ss`, `YYYY-MM-DD HH:mm`, `YYYY-MM-DD HH`, `YYYY-MM-DD`, `DD-MM-YYYY`.
|
|
||||||
|
|
||||||
```v
|
|
||||||
t1 := ourtime.new('2022-12-05 20:14:35')!
|
|
||||||
t2 := ourtime.new('2022-12-05')! // Time defaults to 00:00:00
|
|
||||||
```
|
|
||||||
|
|
||||||
## Methods Overview
|
|
||||||
|
|
||||||
### Creation
|
|
||||||
|
|
||||||
```v
|
|
||||||
now_time := ourtime.now()
|
|
||||||
from_string := ourtime.new('2023-01-15')!
|
|
||||||
from_epoch := ourtime.new_from_epoch(1673788800)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Formatting
|
|
||||||
|
|
||||||
```v
|
|
||||||
mut t := ourtime.now()
|
|
||||||
println(t.str()) // YYYY-MM-DD HH:mm
|
|
||||||
println(t.day()) // YYYY-MM-DD
|
|
||||||
println(t.key()) // YYYY_MM_DD_HH_mm_ss
|
|
||||||
println(t.md()) // Markdown format
|
|
||||||
```
|
|
||||||
|
|
||||||
### Operations
|
|
||||||
|
|
||||||
```v
|
|
||||||
mut t := ourtime.now()
|
|
||||||
t.warp('+1h')! // Move 1 hour forward
|
|
||||||
unix_ts := t.unix()
|
|
||||||
is_empty := t.empty()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
Time parsing methods return a `Result` type and should be handled with `!` or `or` blocks.
|
|
||||||
|
|
||||||
```v
|
|
||||||
t_valid := ourtime.new('2023-01-01')!
|
|
||||||
t_invalid := ourtime.new('bad-date') or {
|
|
||||||
println('Error: ${err}')
|
|
||||||
ourtime.now() // Fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/aiprompts/herolib_core/core_paths.md
|
|
||||||
```md
|
|
||||||
# Pathlib Usage Guide
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The pathlib module provides a comprehensive interface for handling file system operations. Key features include:
|
|
||||||
|
|
||||||
- Robust path handling for files, directories, and symlinks
|
|
||||||
- Support for both absolute and relative paths
|
|
||||||
- Automatic home directory expansion (~)
|
|
||||||
- Recursive directory operations
|
|
||||||
- Path filtering and listing
|
|
||||||
- File and directory metadata access
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
### Importing pathlib
|
|
||||||
```v
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
```
|
|
||||||
|
|
||||||
### Creating Path Objects
|
|
||||||
```v
|
|
||||||
// Create a Path object for a file
|
|
||||||
mut file_path := pathlib.get("path/to/file.txt")
|
|
||||||
|
|
||||||
// Create a Path object for a directory
|
|
||||||
mut dir_path := pathlib.get("path/to/directory")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Basic Path Operations
|
|
||||||
```v
|
|
||||||
// Get absolute path
|
|
||||||
abs_path := file_path.absolute()
|
|
||||||
|
|
||||||
// Get real path (resolves symlinks)
|
|
||||||
real_path := file_path.realpath()
|
|
||||||
|
|
||||||
// Check if path exists
|
|
||||||
if file_path.exists() {
|
|
||||||
// Path exists
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Path Properties and Methods
|
|
||||||
|
|
||||||
### Path Types
|
|
||||||
```v
|
|
||||||
// Check if path is a file
|
|
||||||
if file_path.is_file() {
|
|
||||||
// Handle as file
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if path is a directory
|
|
||||||
if dir_path.is_dir() {
|
|
||||||
// Handle as directory
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if path is a symlink
|
|
||||||
if file_path.is_link() {
|
|
||||||
// Handle as symlink
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Path Normalization
|
|
||||||
```v
|
|
||||||
// Normalize path (remove extra slashes, resolve . and ..)
|
|
||||||
normalized_path := file_path.path_normalize()
|
|
||||||
|
|
||||||
// Get path directory
|
|
||||||
dir_path := file_path.path_dir()
|
|
||||||
|
|
||||||
// Get path name without extension
|
|
||||||
name_no_ext := file_path.name_no_ext()
|
|
||||||
```
|
|
||||||
|
|
||||||
## File and Directory Operations
|
|
||||||
|
|
||||||
### File Operations
|
|
||||||
```v
|
|
||||||
// Write to file
|
|
||||||
file_path.write("Content to write")!
|
|
||||||
|
|
||||||
// Read from file
|
|
||||||
content := file_path.read()!
|
|
||||||
|
|
||||||
// Delete file
|
|
||||||
file_path.delete()!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Directory Operations
|
|
||||||
```v
|
|
||||||
// Create directory
|
|
||||||
mut dir := pathlib.get_dir(
|
|
||||||
path: "path/to/new/dir"
|
|
||||||
create: true
|
|
||||||
)!
|
|
||||||
|
|
||||||
// List directory contents
|
|
||||||
mut dir_list := dir.list()!
|
|
||||||
|
|
||||||
// Delete directory
|
|
||||||
dir.delete()!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Symlink Operations
|
|
||||||
```v
|
|
||||||
// Create symlink
|
|
||||||
file_path.link("path/to/symlink", delete_exists: true)!
|
|
||||||
|
|
||||||
// Resolve symlink
|
|
||||||
real_path := file_path.realpath()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Advanced Operations
|
|
||||||
|
|
||||||
### Path Copying
|
|
||||||
```v
|
|
||||||
// Copy file to destination
|
|
||||||
file_path.copy(dest: "path/to/destination")!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Recursive Operations
|
|
||||||
```v
|
|
||||||
// List directory recursively
|
|
||||||
mut recursive_list := dir.list(recursive: true)!
|
|
||||||
|
|
||||||
// Delete directory recursively
|
|
||||||
dir.delete()!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Path Filtering
|
|
||||||
```v
|
|
||||||
// List files matching pattern
|
|
||||||
mut filtered_list := dir.list(
|
|
||||||
regex: [r".*\.txt$"],
|
|
||||||
recursive: true
|
|
||||||
)!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
```v
|
|
||||||
if file_path.exists() {
|
|
||||||
// Safe to operate
|
|
||||||
} else {
|
|
||||||
// Handle missing file
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
File: /Users/despiegk/code/github/freeflowuniverse/herolib/aiprompts/herolib_core/core_text.md
|
|
||||||
```md
|
|
||||||
# TextTools Module
|
|
||||||
|
|
||||||
The `texttools` module provides a comprehensive set of utilities for text manipulation and processing.
|
|
||||||
|
|
||||||
## Functions and Examples:
|
|
||||||
|
|
||||||
```v
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
|
|
||||||
assert hello_world == texttools.name_fix("Hello World!")
|
|
||||||
|
|
||||||
```
|
|
||||||
### Name/Path Processing
|
|
||||||
* `name_fix(name string) string`: Normalizes filenames and paths.
|
|
||||||
* `name_fix_keepspace(name string) !string`: Like name_fix but preserves spaces.
|
|
||||||
* `name_fix_no_ext(name_ string) string`: Removes file extension.
|
|
||||||
* `name_fix_snake_to_pascal(name string) string`: Converts snake_case to PascalCase.
|
|
||||||
```v
|
|
||||||
name := texttools.name_fix_snake_to_pascal("hello_world") // Result: "HelloWorld"
|
|
||||||
```
|
|
||||||
* `snake_case(name string) string`: Converts PascalCase to snake_case.
|
|
||||||
```v
|
|
||||||
name := texttools.snake_case("HelloWorld") // Result: "hello_world"
|
|
||||||
```
|
|
||||||
* `name_split(name string) !(string, string)`: Splits name into site and page components.
|
|
||||||
|
|
||||||
|
|
||||||
### Text Cleaning
|
|
||||||
* `name_clean(r string) string`: Normalizes names by removing special characters.
|
|
||||||
```v
|
|
||||||
name := texttools.name_clean("Hello@World!") // Result: "HelloWorld"
|
|
||||||
```
|
|
||||||
* `ascii_clean(r string) string`: Removes all non-ASCII characters.
|
|
||||||
* `remove_empty_lines(text string) string`: Removes empty lines from text.
|
|
||||||
```v
|
|
||||||
text := texttools.remove_empty_lines("line1\n\nline2\n\n\nline3") // Result: "line1\nline2\nline3"
|
|
||||||
```
|
|
||||||
* `remove_double_lines(text string) string`: Removes consecutive empty lines.
|
|
||||||
* `remove_empty_js_blocks(text string) string`: Removes empty code blocks (```...```).
|
|
||||||
|
|
||||||
### Command Line Parsing
|
|
||||||
* `cmd_line_args_parser(text string) ![]string`: Parses command line arguments with support for quotes and escaping.
|
|
||||||
```v
|
|
||||||
args := texttools.cmd_line_args_parser("'arg with spaces' --flag=value") // Result: ['arg with spaces', '--flag=value']
|
|
||||||
```
|
|
||||||
* `text_remove_quotes(text string) string`: Removes quoted sections from text.
|
|
||||||
* `check_exists_outside_quotes(text string, items []string) bool`: Checks if items exist in text outside of quotes.
|
|
||||||
|
|
||||||
### Text Expansion
|
|
||||||
* `expand(txt_ string, l int, expand_with string) string`: Expands text to a specified length with a given character.
|
|
||||||
|
|
||||||
### Indentation
|
|
||||||
* `indent(text string, prefix string) string`: Adds indentation prefix to each line.
|
|
||||||
```v
|
|
||||||
text := texttools.indent("line1\nline2", " ") // Result: " line1\n line2\n"
|
|
||||||
```
|
|
||||||
* `dedent(text string) string`: Removes common leading whitespace from every line.
|
|
||||||
```v
|
|
||||||
text := texttools.dedent(" line1\n line2") // Result: "line1\nline2"
|
|
||||||
```
|
|
||||||
|
|
||||||
### String Validation
|
|
||||||
* `is_int(text string) bool`: Checks if text contains only digits.
|
|
||||||
* `is_upper_text(text string) bool`: Checks if text contains only uppercase letters.
|
|
||||||
|
|
||||||
### Multiline Processing
|
|
||||||
* `multiline_to_single(text string) !string`: Converts multiline text to a single line with proper escaping.
|
|
||||||
|
|
||||||
### Text Splitting
|
|
||||||
* `split_smart(t string, delimiter_ string) []string`: Intelligent string splitting that respects quotes.
|
|
||||||
|
|
||||||
### Tokenization
|
|
||||||
* `tokenize(text_ string) TokenizerResult`: Tokenizes text into meaningful parts.
|
|
||||||
* `text_token_replace(text string, tofind string, replacewith string) !string`: Replaces tokens in text.
|
|
||||||
|
|
||||||
### Version Parsing
|
|
||||||
* `version(text_ string) int`: Converts version strings to comparable integers.
|
|
||||||
```v
|
|
||||||
ver := texttools.version("v0.4.36") // Result: 4036
|
|
||||||
ver = texttools.version("v1.4.36") // Result: 1004036
|
|
||||||
```
|
|
||||||
|
|
||||||
### Formatting
|
|
||||||
* `format_rfc1123(t time.Time) string`: Formats a time.Time object into RFC 1123 format.
|
|
||||||
|
|
||||||
|
|
||||||
### Array Operations
|
|
||||||
* `to_array(r string) []string`: Converts a comma or newline separated list to an array of strings.
|
|
||||||
```v
|
|
||||||
text := "item1,item2,item3"
|
|
||||||
array := texttools.to_array(text) // Result: ['item1', 'item2', 'item3']
|
|
||||||
```
|
|
||||||
* `to_array_int(r string) []int`: Converts a text list to an array of integers.
|
|
||||||
* `to_map(mapstring string, line string, delimiter_ string) map[string]string`: Intelligent mapping of a line to a map based on a template.
|
|
||||||
```v
|
|
||||||
r := texttools.to_map("name,-,-,-,-,pid,-,-,-,-,path",
|
|
||||||
"root 304 0.0 0.0 408185328 1360 ?? S 16Dec23 0:34.06 /usr/sbin/distnoted")
|
|
||||||
// Result: {'name': 'root', 'pid': '1360', 'path': '/usr/sbin/distnoted'}
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
</file_contents>
|
|
||||||
<user_instructions>
|
|
||||||
create a module in rust in location packages/core/logger
|
|
||||||
|
|
||||||
which reimplements herolib/lib/core/logger
|
|
||||||
all features need to be reimplemented
|
|
||||||
|
|
||||||
|
|
||||||
write me an implementation plan for my coding agent
|
|
||||||
|
|
||||||
|
|
||||||
</user_instructions>
|
|
@ -11,24 +11,24 @@ categories = ["database", "api-bindings"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# PostgreSQL client dependencies
|
# PostgreSQL client dependencies
|
||||||
postgres = { workspace = true }
|
postgres = "0.19.4"
|
||||||
postgres-types = { workspace = true }
|
postgres-types = "0.2.5"
|
||||||
tokio-postgres = { workspace = true }
|
tokio-postgres = "0.7.8"
|
||||||
|
|
||||||
# Connection pooling
|
# Connection pooling
|
||||||
r2d2 = { workspace = true }
|
r2d2 = "0.8.10"
|
||||||
r2d2_postgres = { workspace = true }
|
r2d2_postgres = "0.18.2"
|
||||||
|
|
||||||
# Utility dependencies
|
# Utility dependencies
|
||||||
lazy_static = { workspace = true }
|
lazy_static = "1.4.0"
|
||||||
thiserror = { workspace = true }
|
thiserror = "2.0.12"
|
||||||
|
|
||||||
# Rhai scripting support
|
# Rhai scripting support
|
||||||
rhai = { workspace = true }
|
rhai = { version = "1.12.0", features = ["sync"] }
|
||||||
|
|
||||||
# SAL dependencies
|
# SAL dependencies
|
||||||
sal-virt = { workspace = true }
|
sal-virt = { path = "../virt" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = { workspace = true }
|
tempfile = "3.5"
|
||||||
tokio-test = { workspace = true }
|
tokio-test = "0.4.4"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user