...
This commit is contained in:
parent
0c02d0e99f
commit
6e01f99958
0
cargo_instructions.md
Normal file
0
cargo_instructions.md
Normal file
@ -12,23 +12,18 @@ readme = "README.md"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
".",
|
||||
"vault",
|
||||
"git",
|
||||
"redisclient",
|
||||
"mycelium",
|
||||
"text",
|
||||
"os",
|
||||
"net",
|
||||
"zinit_client",
|
||||
"process",
|
||||
"virt",
|
||||
"zos",
|
||||
"postgresclient",
|
||||
"kubernetes",
|
||||
"rhai",
|
||||
"herodo",
|
||||
"service_manager",
|
||||
"clients/myceliumclient",
|
||||
"clients/postgresclient",
|
||||
"clients/redisclient",
|
||||
"clients/zinitclient",
|
||||
"core/net",
|
||||
"core/text",
|
||||
"crypt/vault",
|
||||
"system/git",
|
||||
"system/kubernetes",
|
||||
"system/os",
|
||||
"system/process",
|
||||
"system/virt",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
@ -89,25 +84,22 @@ urlencoding = "2.1.3"
|
||||
tokio-test = "0.4.4"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "2.0.12" # For error handling in the main Error enum
|
||||
tokio = { workspace = true } # For async examples
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
# Optional dependencies - users can choose which modules to include
|
||||
sal-git = { path = "git", optional = true }
|
||||
sal-kubernetes = { path = "kubernetes", optional = true }
|
||||
sal-redisclient = { path = "redisclient", optional = true }
|
||||
sal-mycelium = { path = "mycelium", optional = true }
|
||||
sal-text = { path = "text", optional = true }
|
||||
sal-os = { path = "os", optional = true }
|
||||
sal-net = { path = "net", optional = true }
|
||||
sal-zinit-client = { path = "zinit_client", optional = true }
|
||||
sal-process = { path = "process", optional = true }
|
||||
sal-virt = { path = "virt", optional = true }
|
||||
sal-postgresclient = { path = "postgresclient", optional = true }
|
||||
sal-vault = { path = "vault", optional = true }
|
||||
sal-rhai = { path = "rhai", optional = true }
|
||||
sal-service-manager = { path = "service_manager", optional = true }
|
||||
zinit-client.workspace = true
|
||||
sal-git = { path = "../system/git", optional = true }
|
||||
sal-kubernetes = { path = "../system/kubernetes", optional = true }
|
||||
sal-redisclient = { path = "../clients/redisclient", optional = true }
|
||||
sal-mycelium = { path = "../clients/myceliumclient", optional = true }
|
||||
sal-text = { path = "../core/text", optional = true }
|
||||
sal-os = { path = "../system/os", optional = true }
|
||||
sal-net = { path = "../core/net", optional = true }
|
||||
sal-zinit-client = { path = "../clients/zinitclient", optional = true }
|
||||
sal-process = { path = "../system/process", optional = true }
|
||||
sal-virt = { path = "../system/virt", optional = true }
|
||||
sal-postgresclient = { path = "../clients/postgresclient", optional = true }
|
||||
sal-vault = { path = "../crypt/vault", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
@ -125,14 +117,11 @@ process = ["dep:sal-process"]
|
||||
virt = ["dep:sal-virt"]
|
||||
postgresclient = ["dep:sal-postgresclient"]
|
||||
vault = ["dep:sal-vault"]
|
||||
rhai = ["dep:sal-rhai"]
|
||||
service_manager = ["dep:sal-service-manager"]
|
||||
|
||||
# Convenience feature groups
|
||||
core = ["os", "process", "text", "net"]
|
||||
clients = ["redisclient", "postgresclient", "zinit_client", "mycelium"]
|
||||
infrastructure = ["git", "vault", "kubernetes", "virt", "service_manager"]
|
||||
scripting = ["rhai"]
|
||||
infrastructure = ["git", "vault", "kubernetes", "virt"]
|
||||
all = [
|
||||
"git",
|
||||
"kubernetes",
|
||||
@ -146,22 +135,4 @@ all = [
|
||||
"virt",
|
||||
"postgresclient",
|
||||
"vault",
|
||||
"rhai",
|
||||
"service_manager",
|
||||
]
|
||||
|
||||
# Examples
|
||||
[[example]]
|
||||
name = "postgres_cluster"
|
||||
path = "examples/kubernetes/clusters/postgres.rs"
|
||||
required-features = ["kubernetes"]
|
||||
|
||||
[[example]]
|
||||
name = "redis_cluster"
|
||||
path = "examples/kubernetes/clusters/redis.rs"
|
||||
required-features = ["kubernetes"]
|
||||
|
||||
[[example]]
|
||||
name = "generic_cluster"
|
||||
path = "examples/kubernetes/clusters/generic.rs"
|
||||
required-features = ["kubernetes"]
|
||||
|
825
packages/core/logger/instructions.md
Normal file
825
packages/core/logger/instructions.md
Normal file
@ -0,0 +1,825 @@
|
||||
<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>
|
Loading…
Reference in New Issue
Block a user