This commit is contained in:
2025-08-06 08:50:32 +02:00
parent 5cb52ba6b1
commit 04403b62a4
83 changed files with 892 additions and 807 deletions

View File

@@ -0,0 +1,52 @@
params:
- filepath: /Users/despiegk/code/github/freeflowuniverse/herolib/lib/clients/openai
make a dense overview of the code above, easy to understand for AI
the result is 1 markdown file called codeoverview.md and is stored in $filepath
try to figure out which functions are more important and which are less important, so that the most important functions are at the top of section you are working on
the template is as follows
```md
# the name of the module
2-5 liner description
## factory
is there factory, which one and quick example how to call, dont say in which file not relevant
show how to import the module is as follows: import freeflowuniverse.herolib.
and then starting from lib e.g. lib/clients/mycelium would result in import freeflowuniverse.herolib. clients.mycelium
## overview
quick overview as list with identations, of the structs and its methods
## structs
### structname
now list the methods & arguments, for arguments use table
for each method show the arguments needed to call the method, and what it returns
### methods
- if any methods which are on module
- only show public methods, don't show the get/set/exists methods on module level as part of factory.
```
don't mention what we don't show because of rules above.
the only output we want is markdown file as follows
===WRITE===
$filepath
===CONTENT===
$the content of the generated markdown file
===END===

View File

@@ -0,0 +1,22 @@
remove all navigation elements, and index
for each method, move the args as used in the methods to the method section so its easier to read
start of output file is:
# the name of the module
2-5 liner description
## factory
is there factory, which one and quick example how to call, dont say in which file not relevant
show how to import the module is as follows: import freeflowuniverse.herolib.
and then starting from lib e.g. lib/clients/mycelium would result in import freeflowuniverse.herolib. clients.mycelium
## structs and methods
quick overview as list with identations, of the structs and its methods
ONLY OUTPUT THE MARKDOWN FILE, NOTHING ELSE

11
doc.vsh
View File

@@ -36,6 +36,11 @@ if os.system('v doc -m -f html . -readme -comments -no-timestamp -o ../docs') !=
panic('Failed to generate HTML documentation')
}
if os.system('v doc -m -f md . -no-color -o ../vdocs/') != 0 {
panic('Failed to generate Hero markdown documentation')
}
os.chdir(abs_dir_of_script) or {
panic('Failed to change directory to abs_dir_of_script: ${err}')
}
@@ -47,9 +52,9 @@ println('Generating Markdown documentation...')
// panic('Failed to generate V markdown documentation')
// }
if os.system('v doc -m -no-color -f md -o vdocs/') != 0 {
panic('Failed to generate Hero markdown documentation')
}
// if os.system('v doc -m -no-color -f md . -o vdocs/') != 0 {
// panic('Failed to generate Hero markdown documentation')
// }
// Open documentation in browser on non-Linux systems
$if !linux {

View File

@@ -3,10 +3,9 @@
import freeflowuniverse.herolib.biz.bizmodel
import os
heroscript := os.join_path(os.dir(@FILE), 'examples/complete.heroscript')
// Execute the script and print results
bizmodel.play(heroscript_path:heroscript)!
mut bm := bizmodel.get("threefold")!
bizmodel.play(heroscript_path: heroscript)!
mut bm := bizmodel.get('threefold')!
bm.sheet.pprint(nr_columns: 10)!

View File

@@ -12,10 +12,9 @@ import os
heroscript := os.join_path(os.dir(@FILE), 'examples/complete.heroscript')
// Execute the script and print results
bizmodel.play(heroscript_path:heroscript)!
bizmodel.play(heroscript_path: heroscript)!
mut bm := bizmodel.get("threefold")!
mut bm := bizmodel.get('threefold')!
bm.sheet.pprint(nr_columns: 10)!
// buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
@@ -32,5 +31,5 @@ bm.sheet.pprint(nr_columns: 10)!
bm.export(
name: 'example_report'
title: 'Example Business Model'
path: '/tmp/bizmodel_export'
path: '/tmp/bizmodel_export'
)!

View File

@@ -3,10 +3,9 @@
import freeflowuniverse.herolib.biz.bizmodel
import os
heroscript := os.join_path(os.dir(@FILE), 'examples/full')
// Execute the script and print results
bizmodel.play(heroscript_path:heroscript)!
mut bm := bizmodel.get("threefold")!
bizmodel.play(heroscript_path: heroscript)!
mut bm := bizmodel.get('threefold')!
bm.sheet.pprint(nr_columns: 25)!

View File

@@ -49,4 +49,4 @@ bizmodel.play(heroscript: heroscript)!
mut bm := bizmodel.get('test')!
bm.sheet.pprint(nr_columns: 20)!
bm.sheet.pprint(nr_columns: 20)!

View File

@@ -21,4 +21,4 @@ bizmodel.play(heroscript: heroscript)!
mut bm := bizmodel.get('test')!
bm.sheet.pprint(nr_columns: 20)!
bm.sheet.pprint(nr_columns: 20)!

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.clients.openai
import freeflowuniverse.herolib.core.playcmds
playcmds.run(
heroscript: '
!!openai.configure name: "default" key: "" url: "https://openrouter.ai/api/v1" model_default: "gpt-oss-120b"
'
)!
// name:'default' is the default, you can change it to whatever you want
mut client := openai.get()!
mut r := client.chat_completion(
model: 'gpt-3.5-turbo'
message: 'Hello, world!'
temperature: 0.5
max_completion_tokens: 1024
)!

View File

@@ -4,7 +4,7 @@ import freeflowuniverse.herolib.core.generator.generic as generator
import freeflowuniverse.herolib.core.pathlib
mut args := generator.GeneratorArgs{
path: '~/code/github/freeflowuniverse/herolib/lib/clients/postgresql_client'
path: '~/code/github/freeflowuniverse/herolib/lib/clients/postgresql_client'
force: true
}
@@ -13,5 +13,4 @@ mut args := generator.GeneratorArgs{
// force: true
// }
generator.scan(args)!

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env -S v -cg -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib...
import compress.gzip
// Define some sample data to compress
@@ -18,12 +17,12 @@ println('Compressed Data Length: ${compressed_data.len}')
// Decompress the data
decompressed_data := gzip.decompress(compressed_data)!
println('Decompressed Data: "${decompressed_data.string()}"')
println('Decompressed Data: "${decompressed_data.bytestr()}"')
println('Decompressed Data Length: ${decompressed_data.len}')
// Verify if the decompressed data matches the original data
if data.bytes() == decompressed_data {
println('Compression and decompression successful! Data matches.')
println('Compression and decompression successful! Data matches.')
} else {
println('Error: Decompressed data does not match original data.')
}
println('Error: Decompressed data does not match original data.')
}

View File

@@ -1,30 +1,27 @@
#!/usr/bin/env -S v -n -cg -w -gc none -cc tcc -d use_openssl -enable-globals run
// #!/usr/bin/env -S v -n -w -enable-globals run
import freeflowuniverse.herolib.clients.postgresql_client
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.hero.models.circle
import freeflowuniverse.herolib.core.playcmds
import freeflowuniverse.herolib.hero.db.hero_db
import db.pg
// psql -h /tmp -U myuser -d mydb
mut db := pg.connect(pg.Config{
host: '/tmp'
port: 5432
user: 'myuser'
password: 'mypassword'
dbname: 'mydb'
})!
host: '/tmp'
port: 5432
user: 'myuser'
password: 'mypassword'
dbname: 'mydb'
})!
mut r:=db.exec("select * from users;")!
mut r := db.exec('select * from users;')!
println(r)
// // Configure PostgreSQL client
// heroscript := "
// !!postgresql_client.configure
@@ -50,7 +47,6 @@ heroscript := "
mut plbook := playbook.new(text: heroscript)!
postgresql_client.play(mut plbook)!
// //Get the configured client
mut db_client := postgresql_client.get(name: 'aaa')!
@@ -78,7 +74,6 @@ mut db_client := postgresql_client.get(name: 'aaa')!
// println('Database and table setup completed successfully!')
// // Create HeroDB for Circle type
// mut circle_db := hero_db.new[circle.Circle]()!
@@ -112,5 +107,4 @@ mut db_client := postgresql_client.get(name: 'aaa')!
// // Search circles by status
// active_circles := circle_db.search_by_index("status", "active")!
//https://www.moncefbelyamani.com/how-to-install-postgresql-on-a-mac-with-homebrew-and-lunchy/
// https://www.moncefbelyamani.com/how-to-install-postgresql-on-a-mac-with-homebrew-and-lunchy/

View File

@@ -2,7 +2,8 @@
import freeflowuniverse.herolib.core.playcmds
mut plbook := heroscript.new(heroscript: '
mut plbook := heroscript.new(
heroscript: '
!!docusaurus.define
path_build: "/tmp/docusaurus_build"
@@ -18,6 +19,5 @@ mut plbook := heroscript.new(heroscript: '
git_pull:1
!!docusaurus.build
')!
'
)!

View File

@@ -84,12 +84,12 @@ pub fn (mut m BizModel) export_overview_action(action Action) !Action {
}
fn (mut m BizModel) new_report_action(action Action) !Action {
mut p:=action.params
mut p := action.params
path := p.get_default('path', '')!
name := p.get_default('name', '')!
title := p.get_default('title', '')!
description := p.get_default('description', '')!
m.export(path:path,name:name,title:title,description:description)!
m.export(path: path, name: name, title: title, description: description)!
return action
}

View File

@@ -36,7 +36,6 @@ pub fn (b BizModel) export(args ExportArgs) ! {
// b.export_fundraising(export)
}
pub fn (model BizModel) write_introduction(args ExportArgs) ! {
mut index_page := pathlib.get_file(path: '${args.path}/introduction.md')!
// mut tmpl_index := $tmpl('templates/index.md')

View File

@@ -13,9 +13,7 @@ import math
// costcenter:'marketing_cc'
// cost_percent_revenue:'1%'
fn (mut m BizModel) cost_define_action(action Action) !Action {
mut name := action.params.get('name') or {
return error('Cost name is required.')
}
mut name := action.params.get('name') or { return error('Cost name is required.') }
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
descr = action.params.get_default('description', '')!
@@ -32,7 +30,8 @@ fn (mut m BizModel) cost_define_action(action Action) !Action {
return error('Costcenter `${costcenter}` not found. Please define it first.')
}
cost_percent_revenue := action.params.get_percentage_default('cost_percent_revenue','0%')!
cost_percent_revenue := action.params.get_percentage_default('cost_percent_revenue',
'0%')!
indexation := action.params.get_percentage_default('indexation', '0%')!
@@ -55,7 +54,6 @@ fn (mut m BizModel) cost_define_action(action Action) !Action {
)!
cost_row = cost_row.action(action: .reverse)!
if cost_percent_revenue > 0 {
// println(cost_row)
mut revtotal := m.sheet.row_get('revenue_total')!
@@ -73,9 +71,6 @@ fn (mut m BizModel) cost_define_action(action Action) !Action {
return action
}
fn (mut sim BizModel) cost_total() ! {
sim.sheet.group2row(
name: 'cost_total'

View File

@@ -9,9 +9,7 @@ import freeflowuniverse.herolib.core.texttools
// descr:'Marketing Cost Center'
// department:'marketing'
fn (mut m BizModel) costcenter_define_action(action Action) !Action {
mut name := action.params.get('name') or {
return error('Costcenter name is required.')
}
mut name := action.params.get('name') or { return error('Costcenter name is required.') }
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
descr = action.params.get_default('description', '')!

View File

@@ -102,7 +102,6 @@ fn (mut m BizModel) employee_define_action(action Action) !Action {
return action
}
fn (mut sim BizModel) hrcost_total() ! {
sim.sheet.group2row(
name: 'hr_cost_total'

View File

@@ -1,7 +1,7 @@
module bizmodel
import arrays
import freeflowuniverse.herolib.core.playbook { Action, PlayBook }
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
// revenue_total calculates and aggregates the total revenue and cost of goods sold (COGS) for the business model
@@ -22,5 +22,4 @@ fn (mut sim BizModel) pl_total() ! {
// println(pl_total)
// if true{panic("sdsd")}
}

View File

@@ -28,5 +28,4 @@ fn (mut sim BizModel) revenue_total() ! {
// println(revenue_total)
// println(cogs_total)
// println(margin_total)
}

View File

@@ -0,0 +1,19 @@
# Quick Example: Transcribing Audio
```v
import freeflowuniverse.herolib.clients.openai
mut client:= openai.get()! //will be the default client, key is in `AIKEY` on environment variable or `OPENROUTER_API_KEY`
// Assuming you have an audio file named 'audio.mp3' in the same directory
// For a real application, handle file paths dynamically
audio_file_path := 'audio.mp3'
resp := client.audio.create_transcription(
file: audio_file_path,
model: 'whisper-1'
)!
```

View File

@@ -1,9 +1,88 @@
module openai
module audio
import json
import freeflowuniverse.herolib.core.httpconnection
import os
import net.http
import freeflowuniverse.herolib.clients.openai { OpenAI }
type OpenAIAlias = OpenAI
pub enum Voice {
alloy
ash
coral
echo
fable
onyx
nova
sage
shimmer
}
fn voice_str(x Voice) string {
return match x {
.alloy {
'alloy'
}
.ash {
'ash'
}
.coral {
'coral'
}
.echo {
'echo'
}
.fable {
'fable'
}
.onyx {
'onyx'
}
.nova {
'nova'
}
.sage {
'sage'
}
.shimmer {
'shimmer'
}
}
}
pub enum AudioFormat {
mp3
opus
aac
flac
wav
pcm
}
fn audio_format_str(x AudioFormat) string {
return match x {
.mp3 {
'mp3'
}
.opus {
'opus'
}
.aac {
'aac'
}
.flac {
'flac'
}
.wav {
'wav'
}
.pcm {
'pcm'
}
}
}
pub enum AudioRespType {
json
@@ -44,6 +123,7 @@ fn audio_resp_type_str(i AudioRespType) string {
}
}
@[params]
pub struct AudioArgs {
pub mut:
filepath string
@@ -60,17 +140,17 @@ pub mut:
// create transcription from an audio file
// supported audio formats are mp3, mp4, mpeg, mpga, m4a, wav, or webm
pub fn (mut f OpenAI) create_transcription(args AudioArgs) !AudioResponse {
pub fn (mut f OpenAIAlias) create_transcription(args AudioArgs) !AudioResponse {
return f.create_audio_request(args, 'audio/transcriptions')
}
// create translation to english from an audio file
// supported audio formats are mp3, mp4, mpeg, mpga, m4a, wav, or webm
pub fn (mut f OpenAI) create_tranlation(args AudioArgs) !AudioResponse {
pub fn (mut f OpenAIAlias) create_tranlation(args AudioArgs) !AudioResponse {
return f.create_audio_request(args, 'audio/translations')
}
fn (mut f OpenAI) create_audio_request(args AudioArgs, endpoint string) !AudioResponse {
fn (mut f OpenAIAlias) create_audio_request(args AudioArgs, endpoint string) !AudioResponse {
file_content := os.read_file(args.filepath)!
ext := os.file_ext(args.filepath)
mut file_mime_type := ''
@@ -131,7 +211,7 @@ pub:
speed f32
}
pub fn (mut f OpenAI) create_speech(args CreateSpeechArgs) ! {
pub fn (mut f OpenAIAlias) create_speech(args CreateSpeechArgs) ! {
mut output_file := os.open_file(args.output_path, 'w+')!
req := CreateSpeechRequest{

View File

@@ -0,0 +1,26 @@
module audio_test
import os
import clients.openai
import clients.openai.audio
import clients.openai.openai_factory_ { get }
import freeflowuniverse.crystallib.osal { play }
fn test_audio() {
key := os.getenv('OPENAI_API_KEY')
heroscript := '!!openai.configure api_key: "${key}"'
play(heroscript: heroscript)!
mut client := get()!
// create speech
client.create_speech(
input: 'the quick brown fox jumps over the lazy dog'
output_path: '/tmp/output.mp3'
voice: audio.Voice.alloy
response_format: audio.AudioFormat.mp3
)!
assert os.exists('/tmp/output.mp3')
}

View File

@@ -1,6 +1,10 @@
module openai
import os
import openai.audio
import openai.files
import openai.finetune
import openai.images
fn test_chat_completion() {
mut client := get()!
@@ -24,83 +28,3 @@ fn test_chat_completion() {
assert res.choices[0].message.content == 'AI is getting out of hand'
}
// fn test_embeddings() {
// key := os.getenv('OPENAI_API_KEY')
// heroscript := '!!openai.configure api_key: "${key}"'
// play(heroscript: heroscript)!
// mut client := get()!
// res := client.create_embeddings(
// input: ['The food was delicious and the waiter..']
// model: .text_embedding_ada
// )!
// assert res.data.len == 1
// assert res.data[0].embedding.len == 1536
// }
// fn test_files() {
// key := os.getenv('OPENAI_API_KEY')
// heroscript := '!!openai.configure api_key: "${key}"'
// play(heroscript: heroscript)!
// mut client := get()!
// uploaded_file := client.upload_file(
// filepath: '${os.dir(@FILE) + '/testdata/testfile.txt'}'
// purpose: .assistants
// )!
// assert uploaded_file.filename == 'testfile.txt'
// assert uploaded_file.purpose == 'assistants'
// got_file := client.get_file(uploaded_file.id)!
// assert got_file == uploaded_file
// uploaded_file2 := client.upload_file(
// filepath: '${os.dir(@FILE) + '/testdata/testfile2.txt'}'
// purpose: .assistants
// )!
// assert uploaded_file2.filename == 'testfile2.txt'
// assert uploaded_file2.purpose == 'assistants'
// mut got_list := client.list_files()!
// assert got_list.data.len >= 2 // there could be other older files
// mut ids := []string{}
// for file in got_list.data {
// ids << file.id
// }
// assert uploaded_file.id in ids
// assert uploaded_file2.id in ids
// for file in got_list.data {
// client.delete_file(file.id)!
// }
// got_list = client.list_files()!
// assert got_list.data.len == 0
// }
// fn test_audio() {
// key := os.getenv('OPENAI_API_KEY')
// heroscript := '!!openai.configure api_key: "${key}"'
// play(heroscript: heroscript)!
// mut client := get()!
// // create speech
// client.create_speech(
// input: 'the quick brown fox jumps over the lazy dog'
// output_path: '/tmp/output.mp3'
// )!
// assert os.exists('/tmp/output.mp3')
// }

View File

@@ -2,63 +2,61 @@ module openai
import json
pub struct ChatCompletion {
pub mut:
id string
object string
created u32
choices []Choice
usage Usage
}
pub struct Choice {
pub mut:
index int
message MessageRaw
finish_reason string
}
pub struct Message {
pub mut:
role RoleType
content string
}
pub struct Usage {
pub mut:
prompt_tokens int
completion_tokens int
total_tokens int
}
pub struct Messages {
pub mut:
messages []Message
}
pub struct MessageRaw {
pub mut:
role string
content string
}
struct ChatMessagesRaw {
mut:
model string
messages []MessageRaw
temperature f64 = 0.5
max_completion_tokens int = 32000
}
@[params]
pub struct CompletionArgs {
pub mut:
model string
msgs Messages
temperature f64 = 0.5
messages []Message // optional because we can use message, which means we just pass a string
message string
temperature f64 = 0.2
max_completion_tokens int = 32000
}
struct Message {
mut:
role RoleType
content string
}
pub enum RoleType {
system
user
assistant
function
}
fn roletype_str(x RoleType) string {
return match x {
.system {
'system'
}
.user {
'user'
}
.assistant {
'assistant'
}
.function {
'function'
}
}
}
struct Usage {
mut:
prompt_tokens int
completion_tokens int
total_tokens int
}
struct ChatCompletion {
mut:
id string
created u32
result string
usage Usage
}
// creates a new chat completion given a list of messages
// each message consists of message content and the role of the author
pub fn (mut f OpenAI) chat_completion(args_ CompletionArgs) !ChatCompletion {
@@ -71,19 +69,38 @@ pub fn (mut f OpenAI) chat_completion(args_ CompletionArgs) !ChatCompletion {
temperature: args.temperature
max_completion_tokens: args.max_completion_tokens
}
for msg in args.msgs.messages {
for msg in args.messages {
mr := MessageRaw{
role: roletype_str(msg.role)
content: msg.content
}
m.messages << mr
}
if args.message != '' {
mr := MessageRaw{
role: 'user'
content: args.message
}
m.messages << mr
}
data := json.encode(m)
// println('data: ${data}')
mut conn := f.connection()!
r := conn.post_json_str(prefix: 'chat/completions', data: data)!
// println('res: ${r}')
res := json.decode(ChatCompletion, r)!
return res
res := json.decode(ChatCompletionRaw, r)!
mut result := ''
for choice in res.choices {
result += choice.message.content
}
mut chat_completion_result := ChatCompletion{
id: res.id
created: res.created
result: result
usage: res.usage
}
return chat_completion_result
}

View File

@@ -0,0 +1,17 @@
# Quick Example: Creating Embeddings
```v
import freeflowuniverse.herolib.clients.openai
mut client:= openai.get()! //will be the default client, key is in `AIKEY` on environment variable or `OPENROUTER_API_KEY`
text_to_embed := 'The quick brown fox jumps over the lazy dog.'
resp := client.embeddings.create_embedding(
input: text_to_embed,
model: 'text-embedding-ada-002'
)!
```

View File

@@ -1,6 +1,9 @@
module openai
module embeddings
import json
import freeflowuniverse.herolib.clients.openai { OpenAI, Usage }
type OpenAIAlias = OpenAI
pub enum EmbeddingModel {
text_embedding_ada
@@ -42,7 +45,7 @@ pub mut:
usage Usage
}
pub fn (mut f OpenAI) create_embeddings(args EmbeddingCreateArgs) !EmbeddingResponse {
pub fn (mut f OpenAIAlias) create_embeddings(args EmbeddingCreateArgs) !EmbeddingResponse {
req := EmbeddingCreateRequest{
input: args.input
model: embedding_model_str(args.model)

View File

@@ -0,0 +1,24 @@
module embeddings_test
import os
import clients.openai
import clients.openai.embeddings
import clients.openai.openai_factory_ { get }
import freeflowuniverse.crystallib.osal { play }
fn test_embeddings() {
key := os.getenv('OPENAI_API_KEY')
heroscript := '!!openai.configure api_key: "${key}"'
play(heroscript: heroscript)!
mut client := get()!
res := client.create_embeddings(
input: ['The food was delicious and the waiter..']
model: embeddings.EmbeddingModel.text_embedding_ada
)!
assert res.data.len == 1
assert res.data[0].embedding.len == 1536
}

View File

@@ -0,0 +1,25 @@
# Example: Uploading a File
```v
import freeflowuniverse.herolib.clients.openai
mut client:= openai.get()! //will be the default client, key is in `AIKEY` on environment variable or `OPENROUTER_API_KEY`
// Assuming you have a file named 'mydata.jsonl' in the same directory
// For a real application, handle file paths dynamically
file_path := 'mydata.jsonl'
resp := client.files.upload_file(
file: file_path,
purpose: 'fine-tune' // or 'assistants', 'batch', 'vision'
)
if resp.id.len > 0 {
println('File uploaded successfully with ID: ${resp.id}')
} else {
eprintln('Failed to upload file.')
}
```

View File

@@ -1,9 +1,12 @@
module openai
module files
import json
import freeflowuniverse.herolib.core.httpconnection
import os
import net.http
import freeflowuniverse.herolib.clients.openai { OpenAI }
type OpenAIAlias = OpenAI
const jsonl_mime_type = 'text/jsonl'
@@ -44,7 +47,7 @@ pub mut:
}
// upload file to client org, usually used for fine tuning
pub fn (mut f OpenAI) upload_file(args FileUploadArgs) !File {
pub fn (mut f OpenAIAlias) upload_file(args FileUploadArgs) !File {
file_content := os.read_file(args.filepath)!
file_data := http.FileData{
@@ -74,28 +77,28 @@ pub fn (mut f OpenAI) upload_file(args FileUploadArgs) !File {
}
// list all files in client org
pub fn (mut f OpenAI) list_files() !Files {
pub fn (mut f OpenAIAlias) list_files() !Files {
mut conn := f.connection()!
r := conn.get(prefix: 'files')!
return json.decode(Files, r)!
}
// deletes a file
pub fn (mut f OpenAI) delete_file(file_id string) !DeleteResp {
pub fn (mut f OpenAIAlias) delete_file(file_id string) !DeleteResp {
mut conn := f.connection()!
r := conn.delete(prefix: 'files/' + file_id)!
return json.decode(DeleteResp, r)!
}
// returns a single file metadata
pub fn (mut f OpenAI) get_file(file_id string) !File {
pub fn (mut f OpenAIAlias) get_file(file_id string) !File {
mut conn := f.connection()!
r := conn.get(prefix: 'files/' + file_id)!
return json.decode(File, r)!
}
// returns the content of a specific file
pub fn (mut f OpenAI) get_file_content(file_id string) !string {
pub fn (mut f OpenAIAlias) get_file_content(file_id string) !string {
mut conn := f.connection()!
r := conn.get(prefix: 'files/' + file_id + '/content')!
return r

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,22 @@
# OpenAI Fine-tuning Client
```v
import freeflowuniverse.herolib.clients.openai
mut client:= openai.get()! //will be the default client, key is in `AIKEY` on environment variable or `OPENROUTER_API_KEY`
// Assuming you have a training file ID from the Files API
training_file_id := 'file-xxxxxxxxxxxxxxxxxxxxxxxxx'
resp := client.finetune.create_fine_tune(
training_file: training_file_id,
model: 'gpt-3.5-turbo'
)
if resp.id.len > 0 {
println('Fine-tuning job created with ID: ${resp.id}')
} else {
eprintln('Failed to create fine-tuning job.')
}
```

View File

@@ -1,7 +1,11 @@
module openai
module finetune
import freeflowuniverse.herolib.clients.openai { OpenAI }
import freeflowuniverse.herolib.clients.openai.files { File }
import json
type OpenAIAlias = OpenAI
pub struct FineTune {
pub:
id string
@@ -61,7 +65,7 @@ pub mut:
}
// creates a new fine-tune based on an already uploaded file
pub fn (mut f OpenAI) create_fine_tune(args FineTuneCreateArgs) !FineTune {
pub fn (mut f OpenAIAlias) create_fine_tune(args FineTuneCreateArgs) !FineTune {
data := json.encode(args)
mut conn := f.connection()!
r := conn.post_json_str(prefix: 'fine-tunes', data: data)!
@@ -70,28 +74,28 @@ pub fn (mut f OpenAI) create_fine_tune(args FineTuneCreateArgs) !FineTune {
}
// returns all fine-tunes in this account
pub fn (mut f OpenAI) list_fine_tunes() !FineTuneList {
pub fn (mut f OpenAIAlias) list_fine_tunes() !FineTuneList {
mut conn := f.connection()!
r := conn.get(prefix: 'fine-tunes')!
return json.decode(FineTuneList, r)!
}
// get a single fine-tune information
pub fn (mut f OpenAI) get_fine_tune(fine_tune string) !FineTune {
pub fn (mut f OpenAIAlias) get_fine_tune(fine_tune string) !FineTune {
mut conn := f.connection()!
r := conn.get(prefix: 'fine-tunes/' + fine_tune)!
return json.decode(FineTune, r)!
}
// cancel a fine-tune that didn't finish yet
pub fn (mut f OpenAI) cancel_fine_tune(fine_tune string) !FineTune {
pub fn (mut f OpenAIAlias) cancel_fine_tune(fine_tune string) !FineTune {
mut conn := f.connection()!
r := conn.post_json_str(prefix: 'fine-tunes/' + fine_tune + '/cancel')!
return json.decode(FineTune, r)!
}
// returns all events for a fine tune in this account
pub fn (mut f OpenAI) list_fine_tune_events(fine_tune string) !FineTuneEventList {
pub fn (mut f OpenAIAlias) list_fine_tune_events(fine_tune string) !FineTuneEventList {
mut conn := f.connection()!
r := conn.get(prefix: 'fine-tunes/' + fine_tune + '/events')!
return json.decode(FineTuneEventList, r)!

View File

@@ -0,0 +1,21 @@
# Example: Creating an Image
```v
import freeflowuniverse.herolib.clients.openai
mut client:= openai.get()! //will be the default client, key is in `AIKEY` on environment variable or `OPENROUTER_API_KEY`
resp := client.images.create_image(
prompt: 'A futuristic city at sunset',
n: 1,
size: '1024x1024'
)
if resp.data.len > 0 {
println('Image created. URL: ${resp.data[0].url}')
} else {
eprintln('Failed to create image.')
}
```

View File

@@ -4,6 +4,9 @@ import json
import net.http
import os
import freeflowuniverse.herolib.core.httpconnection
import freeflowuniverse.herolib.clients.openai { OpenAI }
type OpenAIAlias = OpenAI
const image_mine_type = 'image/png'
@@ -43,6 +46,7 @@ fn image_resp_type_str(i ImageRespType) string {
}
}
@[params]
pub struct ImageCreateArgs {
pub mut:
prompt string
@@ -95,7 +99,7 @@ pub mut:
// Create new images generation given a prompt
// the amount of images returned is specified by `num_images`
pub fn (mut f OpenAI) create_image(args ImageCreateArgs) !Images {
pub fn (mut f OpenAIAlias) create_image(args ImageCreateArgs) !Images {
image_size := image_size_str(args.size)
response_format := image_resp_type_str(args.format)
request := ImageRequest{
@@ -115,7 +119,7 @@ pub fn (mut f OpenAI) create_image(args ImageCreateArgs) !Images {
// image needs to be in PNG format and transparent or else a mask of the same size needs
// to be specified to indicate where the image should be in the generated image
// the amount of images returned is specified by `num_images`
pub fn (mut f OpenAI) create_edit_image(args ImageEditArgs) !Images {
pub fn (mut f OpenAIAlias) create_edit_image(args ImageEditArgs) !Images {
image_content := os.read_file(args.image_path)!
image_file := http.FileData{
filename: os.base(args.image_path)
@@ -160,7 +164,7 @@ pub fn (mut f OpenAI) create_edit_image(args ImageEditArgs) !Images {
// create variations of the given image
// image needs to be in PNG format
// the amount of images returned is specified by `num_images`
pub fn (mut f OpenAI) create_variation_image(args ImageVariationArgs) !Images {
pub fn (mut f OpenAIAlias) create_variation_image(args ImageVariationArgs) !Images {
image_content := os.read_file(args.image_path)!
image_file := http.FileData{
filename: os.base(args.image_path)

View File

@@ -1,101 +0,0 @@
module openai
pub enum RoleType {
system
user
assistant
function
}
fn roletype_str(x RoleType) string {
return match x {
.system {
'system'
}
.user {
'user'
}
.assistant {
'assistant'
}
.function {
'function'
}
}
}
pub enum Voice {
alloy
ash
coral
echo
fable
onyx
nova
sage
shimmer
}
fn voice_str(x Voice) string {
return match x {
.alloy {
'alloy'
}
.ash {
'ash'
}
.coral {
'coral'
}
.echo {
'echo'
}
.fable {
'fable'
}
.onyx {
'onyx'
}
.nova {
'nova'
}
.sage {
'sage'
}
.shimmer {
'shimmer'
}
}
}
pub enum AudioFormat {
mp3
opus
aac
flac
wav
pcm
}
fn audio_format_str(x AudioFormat) string {
return match x {
.mp3 {
'mp3'
}
.opus {
'opus'
}
.aac {
'aac'
}
.flac {
'flac'
}
.wav {
'wav'
}
.pcm {
'pcm'
}
}
}

View File

@@ -0,0 +1,31 @@
module openai
struct ChatCompletionRaw {
mut:
id string
object string
created u32
choices []ChoiceRaw
usage Usage
}
struct ChoiceRaw {
mut:
index int
message MessageRaw
finish_reason string
}
struct MessageRaw {
mut:
role string
content string
}
struct ChatMessagesRaw {
mut:
model string
messages []MessageRaw
temperature f64 = 0.5
max_completion_tokens int = 32000
}

View File

@@ -0,0 +1,30 @@
# OpenAI Moderation Client
This directory contains the V client for OpenAI's Moderation API.
```v
import freeflowuniverse.herolib.clients.openai
mut client:= openai.get()! //will be the default client, key is in `AIKEY` on environment variable or `OPENROUTER_API_KEY`
text_to_moderate := 'I want to kill them all.'
resp := client.moderation.create_moderation(
input: text_to_moderate
)
if resp.results.len > 0 {
if resp.results[0].flagged {
println('Text was flagged for moderation.')
println('Categories: ${resp.results[0].categories}')
} else {
println('Text passed moderation.')
}
} else {
eprintln('Failed to get moderation result.')
}
```

View File

@@ -1,6 +1,9 @@
module openai
module moderation
import json
import freeflowuniverse.herolib.clients.openai { OpenAI }
type OpenAIAlias = OpenAI
pub enum ModerationModel {
text_moderation_latest
@@ -69,7 +72,7 @@ pub mut:
results []ModerationResult
}
pub fn (mut f OpenAI) create_moderation(input string, model ModerationModel) !ModerationResponse {
pub fn (mut f OpenAIAlias) create_moderation(input string, model ModerationModel) !ModerationResponse {
req := ModerationRequest{
input: input
model: moderation_model_str(model)

View File

@@ -8,57 +8,42 @@ pub const version = '0.0.0'
const singleton = false
const default = true
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
// @[heap]
// pub struct OpenAIBase {
// pub mut:
// name string = 'default'
// api_key string
// url string
// model_default string
// }
@[heap]
pub struct OpenAI {
pub mut:
name string = 'default'
api_key string
url string
model_default string
url string = 'https://openrouter.ai/api/v1'
model_default string = 'gpt-oss-120b'
conn ?&httpconnection.HTTPConnection @[skip; str: skip]
}
// your checking & initialization code if needed
fn obj_init(mycfg_ OpenAI) !OpenAI {
mut mycfg := mycfg_
if mycfg.model_default == '' {
k := os.getenv('AIMODEL')
if k != '' {
mycfg.model_default = k
}
}
if mycfg.url == '' {
k := os.getenv('AIURL')
if k != '' {
mycfg.url = k
}
}
if mycfg.api_key == '' {
mut k := os.getenv('AIKEY')
k := os.getenv('AIKEY')
if k != '' {
mycfg.api_key = k
k = os.getenv('AIURL')
if k != '' {
mycfg.url = k
} else {
return error('found AIKEY in env, but not AIURL')
}
k = os.getenv('AIMODEL')
if k != '' {
mycfg.model_default = k
}
return mycfg
}
mycfg.url = 'https://api.openai.com/v1/models'
k = os.getenv('OPENAI_API_KEY')
if k != '' {
mycfg.api_key = k
return mycfg
}
k = os.getenv('OPENROUTER_API_KEY')
if k != '' {
mycfg.api_key = k
mycfg.url = 'https://openrouter.ai/api/v1'
return mycfg
if k == '' {
k2 := os.getenv('OPENROUTER_API_KEY')
if k2 != '' {
mycfg.api_key = k2
}
}
}
return mycfg

View File

@@ -4,21 +4,27 @@ To get started
```vlang
import freeflowuniverse.herolib.clients.openai
import freeflowuniverse.herolib.core.playcmds
playcmds.run(
heroscript:'
!!openai.configure name: "default" key: "" url: "https://openrouter.ai/api/v1" model_default: "gpt-oss-120b"
'
heroscript_path:''
reset: false
)!
//name:'default' is the default, you can change it to whatever you want
mut client:= openai.get()!
client...
mut r:=client.chat_completion(
model: "gpt-3.5-turbo",
message: 'Hello, world!'
temperature: 0.5
max_completion_tokens: 1024
)!
```
## example heroscript
```hero
!!openai.configure key
```
if key empty then will try to get it from environment variable `AIKEY` or `OPENROUTER_API_KEY`

View File

@@ -1 +0,0 @@
hello world

View File

@@ -1 +0,0 @@
testfile2 content

View File

@@ -163,68 +163,47 @@ fn cmd_docusaurus_execute(cmd Command) ! {
mut builddevpublish := cmd.flags.get_bool('builddevpublish') or { false }
mut dev := cmd.flags.get_bool('dev') or { false }
// Process heroscript files from the ebook directory to get proper site configuration
mut plbook := playbook.new(path: heroscript_config_dir)!
// Dynamically add ebook configuration files
config_file := '${heroscript_config_dir}/config.heroscript'
if os.exists(config_file) {
plbook.add(path: config_file)!
}
// // Get the site configuration that was processed from the heroscript files
// // The site.play() function processes the heroscript and creates sites in the global websites map
// // We need to get the site by name from the processed configuration
// config_actions := plbook.find(filter: 'site.config')!
// if config_actions.len == 0 {
// return error('No site.config found in heroscript files. Make sure config.heroscript contains !!site.config.')
// }
menus_file := '${heroscript_config_dir}/menus.heroscript'
if os.exists(menus_file) {
plbook.add(path: menus_file)!
}
// // Get the site name from the first site.config action
// site_name := config_actions[0].params.get('name') or {
// return error('site.config must specify a name parameter')
// }
pages_dir := '${heroscript_config_dir}/pages'
if os.exists(pages_dir) && os.is_dir(pages_dir) {
plbook.add(path: pages_dir)!
}
// // Get the processed site configuration
// mut generic_site := site.get(name: site_name)!
playcmds.run(plbook: plbook)!
// // Add docusaurus site
// mut dsite := docusaurus.dsite_add(
// site: generic_site
// path_src: url // Use URL as source path for now
// path_build: build_path
// path_publish: publish_path
// reset: false
// template_update: update
// install: init
// )!
// Process site pages to generate the actual documentation files
site.play(mut plbook)!
// // Conditional site actions based on flags
// if buildpublish {
// dsite.build_publish()!
// } else if builddevpublish {
// dsite.build_dev_publish()!
// } else if dev {
// dsite.dev(host: 'localhost', port: 3000, open: open)!
// } else if open {
// dsite.open('localhost', 3000)!
// } else {
// // If no specific action (build/dev/open) is requested, just generate the site
// dsite.generate()!
// }
// Get the site configuration that was processed from the heroscript files
// The site.play() function processes the heroscript and creates sites in the global websites map
// We need to get the site by name from the processed configuration
config_actions := plbook.find(filter: 'site.config')!
if config_actions.len == 0 {
return error('No site.config found in heroscript files. Make sure config.heroscript contains !!site.config.')
}
// Get the site name from the first site.config action
site_name := config_actions[0].params.get('name') or {
return error('site.config must specify a name parameter')
}
// Get the processed site configuration
mut generic_site := site.get(name: site_name)!
// Add docusaurus site
mut dsite := docusaurus.add(
site: generic_site
path_src: url // Use URL as source path for now
path_build: build_path
path_publish: publish_path
reset: false
template_update: update
install: init
)!
// Conditional site actions based on flags
if buildpublish {
dsite.build_publish()!
} else if builddevpublish {
dsite.build_dev_publish()!
} else if dev {
dsite.dev(host: 'localhost', port: 3000, open: open)!
} else if open {
dsite.open('localhost', 3000)!
} else {
// If no specific action (build/dev/open) is requested, just generate the site
dsite.generate()!
}
panic("implement")
}

View File

@@ -2,7 +2,7 @@ module herocmds
// import freeflowuniverse.herolib.web.starlight
import os
import cli { Command, Flag }
import cli
// pub fn cmd_starlight(mut cmdroot Command) {
// mut cmd_run := Command{

View File

@@ -92,7 +92,6 @@ pub fn (mut plbook PlayBook) find(args FindArgs) ![]&Action {
return res
}
pub fn (mut plbook PlayBook) exists_once(args FindArgs) bool {
mut res := plbook.find(args) or { [] }
return res.len == 1

View File

@@ -73,7 +73,7 @@ pub fn (mut plbook PlayBook) actions_sorted(args SortArgs) ![]&Action {
}
action_ids := plbook.priorities[nr] or { panic('bug') }
for id in action_ids {
mut a := plbook.actions[id-1] or { panic("bug in actions sorted") }
mut a := plbook.actions[id - 1] or { panic('bug in actions sorted') }
res << a
}
}
@@ -99,7 +99,7 @@ pub fn (mut plbook PlayBook) heroscript(args HeroScriptArgs) !string {
// out += '${plbook.othertext}'
// }
out = texttools.remove_empty_js_blocks(out)
out +="\n\n"
out += '\n\n'
return out
}

View File

@@ -17,8 +17,8 @@ pub fn (mut plbook PlayBook) add(args_ PlayBookNewArgs) ! {
if args.git_url.len > 0 {
mut git_path_args := gittools.GitPathGetArgs{
git_url: args.git_url
git_pull: args.git_pull
git_url: args.git_url
git_pull: args.git_pull
git_reset: args.git_reset
}
args.path = gittools.path(git_path_args)!.path

View File

@@ -1,7 +1,6 @@
# heroscript
is our small language which allows us to run parser
Our heroscript is a simple way to execute commands in a playbook. It allows you to define a series of actions that can be executed in sequence, making it easy to automate tasks and workflows.
## execute a playbook
@@ -25,7 +24,6 @@ playcmds.run(mut plbook)!
```
## execute a heroscript and make executable
```bash
@@ -39,54 +37,8 @@ playcmds.run(mut plbook)!
you can now just execute this script and hero will interprete the content
## parser
are text based representatsions of parser which need to be executed
example
```js
!!tflibrary.circlesmanager.circle_add
gitsource:'books'
path:'technology/src'
name:technology
```
the first one is the action, the rest are the params
```v
import freeflowuniverse.herolib.core.playbook
mut plbook := playbook.new(text: "....")!
```
## way how to use for a module
```v
import freeflowuniverse.herolib.core.playbook
// !!hr.employee_define
// descr:'Junior Engineer'
// growth:'1:5,60:30' cost:'4000USD' indexation:'5%'
// department:'engineering'
// populate the params for hr
fn (mut m BizModel) hr_actions(actions playbook.PlayBook) ! {
mut actions2 := actions.find('hr.*,vm.start')!
for action in actions2 {
if action.name == 'employee_define' {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
//...
}
}
}
```
## we can also use the filtersort
## filtersort
```v

View File

@@ -5,6 +5,9 @@ import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.data.doctree
import freeflowuniverse.herolib.biz.bizmodel
import freeflowuniverse.herolib.web.docusaurus
import freeflowuniverse.herolib.clients.openai
// import freeflowuniverse.herolib.hero.publishing
// import freeflowuniverse.herolib.threefold.grid4.gridsimulator
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
@@ -48,6 +51,7 @@ pub fn run(args_ PlayArgs) ! {
bizmodel.play(mut plbook)!
doctree.play(mut plbook)!
docusaurus.play(mut plbook)!
openai.play(mut plbook)!
// slides.play(mut plbook)!
// base_install(play(mut plbook)!

View File

@@ -1,11 +1,10 @@
module playcmds
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.core.playbook {PlayBook}
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.ui.console
fn play_git(mut plbook PlayBook) ! {
// Handle !!git.define action first to configure GitStructure
define_actions := plbook.find(filter: 'git.define')!
mut gs := if define_actions.len > 0 {
@@ -150,6 +149,4 @@ fn play_git(mut plbook PlayBook) ! {
gs = gittools.new(coderoot: coderoot)!
gs.load(true)! // Force reload
}
}

View File

@@ -1,11 +1,10 @@
module playcmds
import freeflowuniverse.herolib.develop.luadns
import freeflowuniverse.herolib.core.playbook {PlayBook}
import freeflowuniverse.herolib.core.playbook { PlayBook }
// import os
fn play_luadns(mut plbook PlayBook) ! {
// Variables below are not used, commenting them out
// mut buildroot := '${os.home_dir()}/hero/var/mdbuild'
// mut publishroot := '${os.home_dir()}/hero/www/info'
@@ -34,6 +33,4 @@ fn play_luadns(mut plbook PlayBook) ! {
dns.set_domain(domain, ip)!
action.done = true
}
}

View File

@@ -1,10 +1,9 @@
module playcmds
import freeflowuniverse.herolib.osal.sshagent
import freeflowuniverse.herolib.core.playbook {PlayBook}
import freeflowuniverse.herolib.core.playbook { PlayBook }
fn play_ssh(mut plbook PlayBook) ! {
mut agent := sshagent.new()!
for mut action in plbook.find(filter: 'sshagent.*')! {
mut p := action.params
@@ -20,5 +19,4 @@ fn play_ssh(mut plbook PlayBook) ! {
}
action.done = true
}
}

View File

@@ -1,49 +1,13 @@
# how to use the playcmds
```v
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playcmds
mut s:=base.session_new(
coderoot:'/tmp/code'
interactive:true
playcmds.run(
heroscript:''
heroscript_path:''
reset: false
//plbook ?PlayBook
)!
// Path to the code execution directory
path string
// Command text to execute (e.g., "ls -la")
text string
// Git repository URL for version control
git_url string
// Pull latest changes from git
git_pull bool
// Git branch to use
git_branch string
// Reset repository before pull
git_reset bool
// Execute command after setup
execute bool = true
// Optional session object for state management
session ?&base.Session
mut plbook := playbook.new(text: "....",session:s) or { panic(err) }
```
## some core play commands
```heroscript
```

View File

@@ -1,7 +1,7 @@
module playmacros
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.playbook {PlayBook, Action}
import freeflowuniverse.herolib.core.playbook { Action, PlayBook }
import freeflowuniverse.herolib.threefold.grid4.gridsimulator
import freeflowuniverse.herolib.threefold.grid4.farmingsimulator
import freeflowuniverse.herolib.biz.bizmodel

View File

@@ -19,7 +19,7 @@ fn decode_struct[T](_ T, data string) !T {
mut typ := T{}
// println(data)
$if T is $struct {
obj_name := texttools.snake_case(T.name.all_after_last('.'))
obj_name := T.name.all_after_last('.').to_lower()
mut action_name := '${obj_name}.define'
if !data.contains(action_name) {
action_name = '${obj_name}.configure'

View File

@@ -118,7 +118,7 @@ pub fn (mut e Encoder) encode_struct[T](t T) ! {
mut mytype := reflection.type_of[T](t)
struct_attrs := attrs_get_reflection(mytype)
mut action_name := texttools.snake_case(T.name.all_after_last('.'))
mut action_name := T.name.all_after_last('.').to_lower()
// println('action_name: ${action_name} ${T.name}')
if 'alias' in struct_attrs {
action_name = struct_attrs['alias'].to_lower()

View File

@@ -1,6 +1,5 @@
module encoderhero
pub struct PostgresqlClient {
pub mut:
name string = 'default'
@@ -11,14 +10,13 @@ pub mut:
dbname string = 'postgres'
}
const postgres_client_blank = '!!postgresql_client.configure'
const postgres_client_full = '!!postgresql_client.configure name:production user:app_user port:5433 host:db.example.com password:secret123 dbname:myapp'
const postgres_client_partial = '!!postgresql_client.configure name:dev host:localhost password:devpass'
const postgres_client_complex = "
const postgres_client_complex = '
!!postgresql_client.configure name:staging user:stage_user port:5434 host:staging.db.com password:stagepass dbname:stagingdb
"
'
fn test_postgres_client_decode_blank() ! {
mut client := decode[PostgresqlClient](postgres_client_blank)!
@@ -63,14 +61,14 @@ fn test_postgres_client_decode_complex() ! {
fn test_postgres_client_encode_decode_roundtrip() ! {
// Test encoding and decoding roundtrip
original := PostgresqlClient{
name: 'testdb'
user: 'testuser'
port: 5435
host: 'test.host.com'
name: 'testdb'
user: 'testuser'
port: 5435
host: 'test.host.com'
password: 'testpass123'
dbname: 'testdb'
dbname: 'testdb'
}
// Encode to heroscript
encoded := encode[PostgresqlClient](original)!
@@ -78,10 +76,10 @@ fn test_postgres_client_encode_decode_roundtrip() ! {
// if true {
// panic("sss")
// }
// Decode back from heroscript
decoded := decode[PostgresqlClient](encoded)!
// Verify roundtrip
assert decoded.name == original.name
assert decoded.user == original.user
@@ -95,35 +93,35 @@ fn test_postgres_client_encode() ! {
// Test encoding with different configurations
test_cases := [
PostgresqlClient{
name: 'minimal'
user: 'root'
port: 5432
host: 'localhost'
name: 'minimal'
user: 'root'
port: 5432
host: 'localhost'
password: ''
dbname: 'postgres'
dbname: 'postgres'
},
PostgresqlClient{
name: 'full_config'
user: 'admin'
port: 5433
host: 'remote.server.com'
name: 'full_config'
user: 'admin'
port: 5433
host: 'remote.server.com'
password: 'securepass'
dbname: 'production'
dbname: 'production'
},
PostgresqlClient{
name: 'localhost_dev'
user: 'dev'
port: 5432
host: '127.0.0.1'
name: 'localhost_dev'
user: 'dev'
port: 5432
host: '127.0.0.1'
password: 'devpassword'
dbname: 'devdb'
}
dbname: 'devdb'
},
]
for client in test_cases {
encoded := encode[PostgresqlClient](client)!
decoded := decode[PostgresqlClient](encoded)!
assert decoded.name == client.name
assert decoded.user == client.user
assert decoded.port == client.port
@@ -134,7 +132,7 @@ fn test_postgres_client_encode() ! {
}
// Play script for interactive testing
const play_script = "
const play_script = '
# PostgresqlClient Encode/Decode Play Script
# This script demonstrates encoding and decoding PostgresqlClient configurations
@@ -149,35 +147,35 @@ const play_script = "
# Default configuration (all defaults)
!!postgresql_client.configure
"
'
fn test_play_script() ! {
// Test the play script with multiple configurations
lines := play_script.split_into_lines().filter(fn (line string) bool {
return line.trim(' ') != '' && !line.starts_with('#')
})
mut clients := []PostgresqlClient{}
for line in lines {
if line.starts_with('!!postgresql_client.configure') {
client := decode[PostgresqlClient](line)!
clients << client
}
}
assert clients.len == 3
// First client: full configuration
assert clients[0].name == 'playground'
assert clients[0].user == 'play_user'
assert clients[0].port == 5432
// Second client: partial configuration
assert clients[1].name == 'quick_test'
assert clients[1].host == '127.0.0.1'
assert clients[1].user == 'root' // default
// Third client: defaults only
assert clients[2].name == 'default'
assert clients[2].host == 'localhost'
@@ -188,48 +186,48 @@ fn test_play_script() ! {
pub fn run_play_script() ! {
println('=== PostgresqlClient Encode/Decode Play Script ===')
println('Testing encoding and decoding of PostgresqlClient configurations...')
// Test 1: Basic encoding
println('\n1. Testing basic encoding...')
client := PostgresqlClient{
name: 'example'
user: 'example_user'
port: 5432
host: 'example.com'
name: 'example'
user: 'example_user'
port: 5432
host: 'example.com'
password: 'example_pass'
dbname: 'example_db'
dbname: 'example_db'
}
encoded := encode[PostgresqlClient](client)!
println('Encoded: ${encoded}')
decoded := decode[PostgresqlClient](encoded)!
println('Decoded name: ${decoded.name}')
println('Decoded host: ${decoded.host}')
// Test 2: Play script
println('\n2. Testing play script...')
test_play_script()!
println('Play script test passed!')
// Test 3: Edge cases
println('\n3. Testing edge cases...')
edge_client := PostgresqlClient{
name: 'edge'
user: ''
port: 0
host: ''
name: 'edge'
user: ''
port: 0
host: ''
password: ''
dbname: ''
dbname: ''
}
edge_encoded := encode[PostgresqlClient](edge_client)!
edge_decoded := decode[PostgresqlClient](edge_encoded)!
assert edge_decoded.name == 'edge'
assert edge_decoded.user == ''
assert edge_decoded.port == 0
println('Edge cases test passed!')
println('\n=== All tests completed successfully! ===')
}
}

View File

@@ -1,7 +1,6 @@
module elements
//TODO: def is broken, way how we do it is bad
// TODO: def is broken, way how we do it is bad
@[heap]
pub struct Def {

View File

@@ -39,7 +39,7 @@ fn (self Paragraph) html() !string {
mut out := self.DocBase.html()! // the children should have all the content
if self.children.len == 1 {
if out.trim_space() != '' {
if self.children[0] or { panic("bug") } is Link {
if self.children[0] or { panic('bug') } is Link {
return out
} else {
return out

View File

@@ -26,13 +26,13 @@ struct TestChild {
}
const test_child = TestChild{
child_name: 'test_child'
child_number: 3
child_yesno: false
child_liststr: ['three', 'four']
child_listint: [3, 4]
child_name: 'test_child'
child_number: 3
child_yesno: false
child_liststr: ['three', 'four']
child_listint: [3, 4]
child_listbool: [true, false]
child_listu32: [u32(5), u32(6)]
child_listu32: [u32(5), u32(6)]
}
const test_struct = TestStruct{
@@ -51,7 +51,6 @@ const test_struct = TestStruct{
child: test_child
}
const test_child_params = Params{
params: [
Param{
@@ -116,7 +115,6 @@ const test_params = Params{
}]
}
fn test_encode_struct() {
encoded_struct := encode[TestStruct](test_struct)!
assert encoded_struct == test_params

View File

@@ -8,24 +8,23 @@ __global (
gsinstances map[string]&GitStructure
)
@[params]
pub struct GetRepoArgs{
pub struct GetRepoArgs {
pub mut:
path string //if used will check if path exists if yes, just return
git_url string
git_pull bool
path string // if used will check if path exists if yes, just return
git_url string
git_pull bool
git_reset bool
}
// get_repo_path implements the GitUrlResolver interface
pub fn get_repo_path(args GetRepoArgs) !string {
if os.exists(args.path){
pub fn get_repo_path(args GetRepoArgs) !string {
if os.exists(args.path) {
return args.path
}
mut gs := get()!
mut repo := gs.get_repo(
url: args.git_url
url: args.git_url
pull: args.git_pull
reset: args.git_reset
)!

View File

@@ -45,9 +45,9 @@ pub fn (mut gs GitStructure) do(args_ ReposActionsArgs) !string {
mut args := args_
// console.print_debug('git do ${args.cmd}')
if args.path == '' && args.url == '' && args.repo == '' && args.account == '' && args.provider == '' && args.filter == ''{
if args.path == '' && args.url == '' && args.repo == '' && args.account == ''
&& args.provider == '' && args.filter == '' {
args.path = os.getwd()
}
if args.path != '' {
@@ -84,7 +84,7 @@ pub fn (mut gs GitStructure) do(args_ ReposActionsArgs) !string {
)!
// reset the status for the repo
if args.reload || args.cmd == 'reload'{
if args.reload || args.cmd == 'reload' {
for mut repo in repos {
repo.cache_last_load_clear()!
}

View File

@@ -85,9 +85,7 @@ pub fn (mut repo GitRepo) commit(msg string) ! {
return error('Commit message is empty.')
}
repo_path := repo.path()
repo.exec('git add . -A') or {
return error('Cannot add to repo: ${repo_path}. Error: ${err}')
}
repo.exec('git add . -A') or { return error('Cannot add to repo: ${repo_path}. Error: ${err}') }
repo.exec('git commit -m "${msg}"') or {
return error('Cannot commit repo: ${repo_path}. Error: ${err}')
}

View File

@@ -136,5 +136,5 @@ pub fn (self GitRepo) get_last_local_commit() !string {
if self.status_local.branch in self.status_local.branches {
return self.status_local.branches[self.status_local.branch]
}
return ""
return ''
}

View File

@@ -8,7 +8,7 @@ import freeflowuniverse.herolib.core.texttools
// Generic database interface for Hero root objects
pub struct HeroDB[T] {
pub mut:
db pg.DB
db pg.DB
table_name string
}
@@ -22,14 +22,12 @@ pub fn new[T]() !HeroDB[T] {
table_name = '${dirname}_${texttools.snake_case(T.name)}'
}
mut dbclient:=postgresql_client.get()!
mut dbclient := postgresql_client.get()!
mut dbcl := dbclient.db() or { return error('Failed to connect to database') }
mut dbcl:=dbclient.db() or {
return error('Failed to connect to database')
}
return HeroDB[T]{
db: dbcl
db: dbcl
table_name: table_name
}
}
@@ -38,13 +36,13 @@ pub fn new[T]() !HeroDB[T] {
pub fn (mut self HeroDB[T]) ensure_table() ! {
// Get index fields from struct reflection
index_fields := self.get_index_fields()
// Build index column definitions
mut index_cols := []string{}
for field in index_fields {
index_cols << '${field} varchar(255)'
}
// Create table with JSON storage
create_sql := '
CREATE TABLE IF NOT EXISTS ${self.table_name} (
@@ -54,11 +52,9 @@ pub fn (mut self HeroDB[T]) ensure_table() ! {
created_at timestamp DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp DEFAULT CURRENT_TIMESTAMP
)
'
// self.db.exec(create_sql)!
' // self.db.exec(create_sql)!
// Create indexes on index fields
for field in index_fields {
index_sql := 'CREATE INDEX IF NOT EXISTS idx_${self.table_name}_${field} ON ${self.table_name}(${field})'
// self.db.exec(index_sql)!
@@ -80,22 +76,22 @@ fn (self HeroDB[T]) get_index_fields() []string {
pub fn (mut self HeroDB[T]) save(obj T) ! {
// Get index values from object
index_data := self.extract_index_values(obj)
// Serialize to JSON
json_data := json.encode_pretty(obj)
// Check if object already exists
mut query := 'SELECT id FROM ${self.table_name} WHERE '
mut params := []string{}
// Build WHERE clause for unique lookup
for key, value in index_data {
params << '${key} = \'${value}\''
}
query += params.join(' AND ')
existing :=self.db.exec(query)!
existing := self.db.exec(query)!
if existing.len > 0 {
// Update existing record
id_val := existing[0].vals[0] or { return error('no id') }
@@ -114,22 +110,21 @@ pub fn (mut self HeroDB[T]) save(obj T) ! {
// Insert new record
mut columns := []string{}
mut values := []string{}
// Add index columns
for key, value in index_data {
columns << key
values << "'${value}'"
}
// Add JSON data
columns << 'data'
values << "'${json_data}'"
insert_sql := '
INSERT INTO ${self.table_name} (${columns.join(', ')})
VALUES (${values.join(', ')})
'
// self.db.exec(insert_sql)!
' // self.db.exec(insert_sql)!
}
}
@@ -137,7 +132,7 @@ pub fn (mut self HeroDB[T]) save(obj T) ! {
pub fn (mut self HeroDB[T]) get_by_index(index_values map[string]string) !T {
mut query := 'SELECT data FROM ${self.table_name} WHERE '
mut params := []string{}
for key, value in index_values {
params << '${key} = \'${value}\''
}
@@ -147,16 +142,16 @@ pub fn (mut self HeroDB[T]) get_by_index(index_values map[string]string) !T {
if rows.len == 0 {
return error('${T.name} not found with index values: ${index_values}')
}
json_data_val := rows[0].vals[0] or { return error('no data') }
println('json_data_val: ${json_data_val}')
if true{
if true {
panic('sd2221')
}
// mut obj := json.decode(T, json_data_val) or {
// return error('Failed to decode JSON: ${err}')
// }
// return &obj
return T{}
}
@@ -165,7 +160,7 @@ pub fn (mut self HeroDB[T]) get_by_index(index_values map[string]string) !T {
// pub fn (mut self HeroDB[T]) get_all() ![]T {
// query := 'SELECT data FROM ${self.table_name} ORDER BY id DESC'
// rows := self.db_client.db()!.exec(query)!
// mut results := []T{}
// for row in rows {
// json_data_val := row.vals[0] or { continue }
@@ -176,7 +171,7 @@ pub fn (mut self HeroDB[T]) get_by_index(index_values map[string]string) !T {
// }
// results << &obj
// }
// return results
// }
@@ -184,7 +179,7 @@ pub fn (mut self HeroDB[T]) get_by_index(index_values map[string]string) !T {
// pub fn (mut self HeroDB[T]) search_by_index(field_name string, value string) ![]T {
// query := 'SELECT data FROM ${self.table_name} WHERE ${field_name} = \'${value}\' ORDER BY id DESC'
// rows := self.db_client.db()!.exec(query)!
// mut results := []T{}
// for row in rows {
// json_data_val := row.vals[0] or { continue }
@@ -194,7 +189,7 @@ pub fn (mut self HeroDB[T]) get_by_index(index_values map[string]string) !T {
// }
// results << &obj
// }
// return results
// }
@@ -202,12 +197,12 @@ pub fn (mut self HeroDB[T]) get_by_index(index_values map[string]string) !T {
// pub fn (mut self HeroDB[T]) delete_by_index(index_values map[string]string) ! {
// mut query := 'DELETE FROM ${self.table_name} WHERE '
// mut params := []string{}
// for key, value in index_values {
// params << '${key} = \'${value}\''
// }
// query += params.join(' AND ')
// self.db_client.db()!.exec(query)!
// }

View File

@@ -74,7 +74,7 @@ fn install() ! {
return error('only support linux for now')
}
release_url := 'https://github.com/threefoldtech/zinit/releases/download/v0.2.14/zinit'
release_url := 'https://github.com/threefoldtech/zinit/releases/download/v0.2.25/zinit'
mut dest := osal.download(
url: release_url

View File

@@ -41,9 +41,9 @@ pub:
// HttpConfig holds HTTP-specific configuration
pub struct HttpConfig {
pub:
port int = 8080 // Port to listen on
host string = 'localhost' // Host to bind to
protocol HttpMode = .both // Which HTTP protocols to support
port int = 8080 // Port to listen on
host string = 'localhost' // Host to bind to
protocol HttpMode = .both // Which HTTP protocols to support
}
// HttpMode defines which HTTP protocols the server should support

View File

@@ -1,4 +1,4 @@
module core
module core
fn test_done_set() ! {
done_set('mykey', 'myvalue')!

View File

@@ -18,7 +18,7 @@ pub mut:
path_build pathlib.Path
errors []SiteError
config Configuration
website sitemodule.Site
website sitemodule.Site
}
pub fn (mut s DocSite) build() ! {

View File

@@ -13,14 +13,14 @@ import freeflowuniverse.herolib.core.playbook
@[params]
pub struct AddArgs {
pub mut:
sitename string //needs to exist in web.site module
path string //site of the docusaurus site with the config as is needed to populate the docusaurus site
git_url string
git_reset bool
git_root string
git_pull bool
sitename string // needs to exist in web.site module
path string // site of the docusaurus site with the config as is needed to populate the docusaurus site
git_url string
git_reset bool
git_root string
git_pull bool
path_publish string
play bool = true
play bool = true
}
pub fn dsite_add(args_ AddArgs) !&DocSite {
@@ -29,10 +29,9 @@ pub fn dsite_add(args_ AddArgs) !&DocSite {
console.print_header('Add Docusaurus Site: ${args.sitename}')
if args.sitename in docusaurus_sites {
return error('Docusaurus site ${args.sitename} already exists, returning existing.')
}
if args.sitename in docusaurus_sites {
return error('Docusaurus site ${args.sitename} already exists, returning existing.')
}
mut path := gittools.path(
path: args.path
@@ -47,7 +46,7 @@ pub fn dsite_add(args_ AddArgs) !&DocSite {
return error('path is not a directory')
}
if ! os.exists('${args.path}/cfg') {
if !os.exists('${args.path}/cfg') {
return error('config directory for docusaurus does not exist in ${args.path}/cfg.\n${args}')
}
@@ -66,13 +65,13 @@ pub fn dsite_add(args_ AddArgs) !&DocSite {
mut website := site.get(name: args.sitename)!
mut myconfig := new_configuration(website.siteconfig)! //go from site.SiteConfig to docusaurus.Configuration
mut myconfig := new_configuration(website.siteconfig)! // go from site.SiteConfig to docusaurus.Configuration
if myconfig.main.name.len == 0 {
return error('main.name is not set in the site configuration')
}
mut f:=factory_get()!
mut f := factory_get()!
if args.path_publish == '' {
args.path_publish = '${f.path_publish.path}/${args.sitename}'
@@ -80,37 +79,33 @@ pub fn dsite_add(args_ AddArgs) !&DocSite {
path_build_ := '${f.path_build.path}/${args.sitename}'
//get our website
// get our website
mut mysite := site.new(name: args.sitename)!
if site.exists(name: args.sitename) {
console.print_debug('Docusaurus site ${args.sitename} already exists, using existing site.')
mysite = site.get(name: args.sitename)!
} else {
if !args.play{
if !args.play {
return error('Docusaurus site ${args.sitename} does not exist, please set play to true to create it.')
}
console.print_debug('Creating new Docusaurus site ${args.sitename}.')
mut plbook := playbook.new(path: "${args.path}/cfg")!
mut plbook := playbook.new(path: '${args.path}/cfg')!
site.play(mut plbook)!
mysite = site.get(name: args.sitename) or {
return error('Failed to get site after playing playbook: ${args.sitename}')
}
}
}
// Create the DocSite instance
mut dsite := &DocSite{
name: args.sitename
path_src: pathlib.get_dir(path: args.path, create: false)!
path_publish: pathlib.get_dir(path: args.path_publish, create: true)!
path_build: pathlib.get_dir(path: path_build_, create: true)!
config: new_configuration(website.siteconfig)!
// Create the DocSite instance
mut dsite := &DocSite{
name: args.sitename
path_src: pathlib.get_dir(path: args.path, create: false)!
path_publish: pathlib.get_dir(path: args.path_publish, create: true)!
path_build: pathlib.get_dir(path: path_build_, create: true)!
config: new_configuration(website.siteconfig)!
website: mysite
}
}
docusaurus_sites[args.sitename] = dsite
return dsite
docusaurus_sites[args.sitename] = dsite
return dsite
}

View File

@@ -2,7 +2,7 @@ module docusaurus
import freeflowuniverse.herolib.web.site
//IS THE ONE AS USED BY DOCUSAURUS
// IS THE ONE AS USED BY DOCUSAURUS
pub struct Configuration {
pub mut:

View File

@@ -12,13 +12,11 @@ import freeflowuniverse.herolib.core.texttools.regext
import freeflowuniverse.herolib.web.site as sitegen
pub fn (mut site DocSite) generate() ! {
mut f:=factory_get()!
mut f := factory_get()!
console.print_header(' site generate: ${site.name} on ${f.path_build.path}')
console.print_header(' site source on ${site.path_src.path}')
// lets make sure we remove the cfg dir so we rebuild
cfg_path := os.join_path(f.path_build.path, 'cfg')
osal.rm(cfg_path)!

View File

@@ -8,71 +8,72 @@ import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.installers.web.bun
__global (
docusaurus_sites map[string]&DocSite
docusaurus_factory ?DocSiteFactory
docusaurus_sites map[string]&DocSite
docusaurus_factory []DocSiteFactory
)
pub struct DocSiteFactory{
pub struct DocSiteFactory {
pub mut:
path_publish pathlib.Path
path_build pathlib.Path
path_build pathlib.Path
}
@[params]
pub struct DocSiteFactoryArgs {
pub mut:
path_build string
path_publish string
install bool
reset bool
template_update bool
path_build string
path_publish string
install bool
reset bool
template_update bool
}
pub fn factory_get(args_ DocSiteFactoryArgs) !DocSiteFactory {
mut args:= args_
mut f:= docusaurus_factory or {
mut factory:=factory_set(args)!
factory
}
return f
mut args := args_
if docusaurus_factory.len > 1 {
panic("multiple docusaurus factories found, please specify which one to use")
}
if docusaurus_factory.len > 0 {
return docusaurus_factory[0]
}
return factory_set(args)!
}
pub fn factory_set(args_ DocSiteFactoryArgs) !DocSiteFactory {
mut args:= args_
if args.path_build == '' {
args.path_build = '${os.home_dir()}/hero/var/docusaurus/build'
}
if args.path_publish == '' {
args.path_publish = '${os.home_dir()}/hero/var/docusaurus/publish'
}
mut factory := DocSiteFactory{
path_publish: pathlib.get_dir(path:args.path_publish,create:true)!
path_build: pathlib.get_dir(path:args.path_build,create:true)!
}
mut args := args_
if args.path_build == '' {
args.path_build = '${os.home_dir()}/hero/var/docusaurus/build'
}
if args.path_publish == '' {
args.path_publish = '${os.home_dir()}/hero/var/docusaurus/publish'
}
mut factory := DocSiteFactory{
path_publish: pathlib.get_dir(path: args.path_publish, create: true)!
path_build: pathlib.get_dir(path: args.path_build, create: true)!
}
if !os.exists('${args.path_build}/node_modules') {
args.install = true
}
if !os.exists('${args.path_build}/node_modules') {
args.install = true
}
if args.install {
factory.install(args.reset, args.template_update)!
}
docusaurus_factory << factory
if args.install {
factory.install(args.reset, args.template_update)!
}
return factory
}
pub fn dsite_get(name_ string) !&DocSite {
name := texttools.name_fix(name_)
return docusaurus_sites[name] or {
return error('docusaurus site with name "${name}" does not exist')
}
name := texttools.name_fix(name_)
return docusaurus_sites[name] or {
return error('docusaurus site with name "${name}" does not exist')
}
}
pub fn dsite_exists(name_ string) !bool {
name := texttools.name_fix(name_)
d := docusaurus_sites[name] or {
return false
}
return true
name := texttools.name_fix(name_)
d := docusaurus_sites[name] or { return false }
return true
}

View File

@@ -2,12 +2,11 @@ module docusaurus
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.web.doctreeclient
import freeflowuniverse.herolib.web.site {Site, Page, Section}
import freeflowuniverse.herolib.web.site { Page, Section, Site }
import freeflowuniverse.herolib.data.markdown.tools as markdowntools
// import freeflowuniverse.herolib.ui.console
//THIS CODE GENERATES A DOCUSAURUS SITE FROM A DOCTREECLIENT AND SITE DEFINITION
// THIS CODE GENERATES A DOCUSAURUS SITE FROM A DOCTREECLIENT AND SITE DEFINITION
struct SiteGenerator {
mut:
@@ -15,7 +14,7 @@ mut:
path pathlib.Path
client &doctreeclient.DocTreeClient
flat bool // if flat then won't use sitenames as subdir's
site Site
site Site
}
@[params]
@@ -36,7 +35,7 @@ fn generate(args SiteGeneratorArgs) ! {
path: pathlib.get_dir(path: path, create: true)!
client: doctreeclient.new()!
flat: args.flat
site: args.site
site: args.site
}
for section in gen.site.sections {

View File

@@ -6,7 +6,6 @@ import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.installers.web.bun
fn (mut f DocSiteFactory) install(reset bool, template_update bool) ! {
mut gs := gittools.new()!
@@ -23,19 +22,18 @@ fn (mut f DocSiteFactory) install(reset bool, template_update bool) ! {
mut template_path0 := pathlib.get_dir(path: template_path, create: false)!
template_path0.copy(dest: f.path_build.path, delete:reset)! // Changed args.delete to args.reset
template_path0.copy(dest: f.path_build.path, delete: reset)! // Changed args.delete to args.reset
// install bun
mut installer := bun.get()!
installer.install()!
osal.exec(
// always stay in the context of the build directory
cmd: '
// install bun
mut installer := bun.get()!
installer.install()!
osal.exec(
// always stay in the context of the build directory
cmd: '
${osal.profile_path_source_and()!}
export PATH=${f.path_build.path}/node_modules/.bin::${os.home_dir()}/.bun/bin/:\$PATH
cd ${f.path_build.path}
bun install
'
)!
}
)!
}

View File

@@ -4,48 +4,55 @@ import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.web.site
pub fn play(mut plbook PlayBook) ! {
if !plbook.exists_once(filter: 'docusaurus.') {
return
}
// 1. Process generic site configuration first.
// This populates the global `site.websites` map.
site.play(mut plbook)!
// check if docusaurus.define exists, if not, we create a default factory
mut f:=DocSiteFactory{}
if plbook.exists_once(filter: 'docusaurus.define'){
mut a:=plbook.get(filter: 'docusaurus.define') or {
mut f := DocSiteFactory{}
if plbook.exists_once(filter: 'docusaurus.define') {
mut a := plbook.get(filter: 'docusaurus.define') or {
panic('docusaurus.define action not found, this should not happen.')
}
mut p:=a.params
f=factory_set(
path_build: p.get_default('path_build', '')!
path_publish: p.get_default('path_publish', '')!
reset: p.get_default_false('reset')
template_update: p.get_default_false('template_update')
install: p.get_default_false('install')
)!
a.done = true
}else{
f=factory_get()!
mut p := a.params
f = factory_set(
path_build: p.get_default('path_build', '')!
path_publish: p.get_default('path_publish', '')!
reset: p.get_default_false('reset')
template_update: p.get_default_false('template_update')
install: p.get_default_false('install')
)!
a.done = true
} else {
f = factory_get()!
}
// 3. Process `docusaurus.add` actions to create sites.
for mut action in plbook.find(filter: 'docusaurus.add')! {
mut p := action.params
site_name := p.get('sitename') or { return error('In docusaurus.add, param "sitename" is required.') }
site_name := p.get('sitename') or {
return error('In docusaurus.add, param "sitename" is required.')
}
dsite_add(
sitename: site_name
path: p.get('path')!
git_url: p.get('git_url')!
git_reset: p.get_default_false('git_reset')
git_root: p.get('git_root')!
git_pull: p.get_default_false('git_pull')
sitename: site_name
path: p.get('path')!
git_url: p.get('git_url')!
git_reset: p.get_default_false('git_reset')
git_root: p.get('git_root')!
git_pull: p.get_default_false('git_pull')
path_publish: p.get_default('path_publish', f.path_publish.path)!
play:false //need to make sure we don't play again
play: false // need to make sure we don't play again
)!
action.done = true
}
mut actions_dev:=plbook.find(filter: 'docusaurus.dev')!
mut actions_dev := plbook.find(filter: 'docusaurus.dev')!
if actions_dev.len > 1 {
return error('Multiple "docusaurus.dev" actions found. Only one is allowed.')
}
@@ -54,15 +61,15 @@ pub fn play(mut plbook PlayBook) ! {
site_name := p.get('site')!
mut dsite := dsite_get(site_name)!
dsite.dev(
host: p.get_default('host', 'localhost')!
port: p.get_int_default('port', 3000)!
open: p.get_default_false('open')
host: p.get_default('host', 'localhost')!
port: p.get_int_default('port', 3000)!
open: p.get_default_false('open')
watch_changes: p.get_default_false('watch_changes')
)!
action.done = true
}
mut actions_build:=plbook.find(filter: 'docusaurus.build')!
mut actions_build := plbook.find(filter: 'docusaurus.build')!
if actions_build.len > 1 {
return error('Multiple "docusaurus.build" actions found. Only one is allowed.')
}
@@ -73,5 +80,4 @@ pub fn play(mut plbook PlayBook) ! {
dsite.build()!
action.done = true
}
}
}

View File

@@ -3,13 +3,13 @@ module site
import freeflowuniverse.herolib.core.texttools
__global (
websites map[string]&Site
websites map[string]&Site
)
@[params]
pub struct FactoryArgs {
pub mut:
name string = "default"
name string = 'default'
}
pub fn new(args FactoryArgs) !&Site {
@@ -19,29 +19,24 @@ pub fn new(args FactoryArgs) !&Site {
name: name
}
}
return get(name:name)!
return get(name: name)!
}
pub fn get(args FactoryArgs) !&Site {
name := texttools.name_fix(args.name)
mut sc := websites[name] or {
return error('siteconfig with name "${name}" does not exist')
}
mut sc := websites[name] or { return error('siteconfig with name "${name}" does not exist') }
return sc
}
pub fn exists(args FactoryArgs) bool {
name := texttools.name_fix(args.name)
mut sc := websites[name] or {
return false
}
mut sc := websites[name] or { return false }
return true
}
pub fn default() !&Site {
if websites.len == 0 {
return new(name:'default')!
return new(name: 'default')!
}
return get()!
}

View File

@@ -1,13 +1,11 @@
module site
@[heap]
pub struct Site {
pub mut:
pages []Page
sections []Section
pages []Page
sections []Section
siteconfig SiteConfig
}
pub struct Page {
@@ -17,10 +15,10 @@ pub mut:
draft bool
position int
hide_title bool
src string @[required] //always in format collection:page_name
src string @[required] // always in format collection:page_name
path string @[required]
title_nr int
slug string
slug string
}
pub struct Section {

View File

@@ -1,6 +1,8 @@
module site
import os
// Combined config structure
@[heap]
pub struct SiteConfig {
pub mut:

View File

@@ -15,7 +15,7 @@ pub fn play(mut plbook PlayBook) ! {
// Process each site configuration separately
for mut config_action in config_actions {
mut website := play_config_single(mut config_action)!
mut config := &website.siteconfig
play_import(mut plbook, mut config)!
@@ -30,9 +30,7 @@ pub fn play(mut plbook PlayBook) ! {
fn play_config_single(mut action Action) !&Site {
mut p := action.params
name := p.get('name') or {
return error('need to specify name in site.config.\n${action}')
}
name := p.get('name') or { return error('need to specify name in site.config.\n${action}') }
mut website := new(name: name)!
mut config := &website.siteconfig

View File

@@ -2,12 +2,11 @@ module site
import freeflowuniverse.herolib.core.playbook { PlayBook }
//plays the sections & pages
// plays the sections & pages
fn play_pages(mut plbook PlayBook, mut site Site) ! {
// mut siteconfig := &site.siteconfig
//if only 1 doctree is specified, then we use that as the default doctree name
// if only 1 doctree is specified, then we use that as the default doctree name
mut doctreename := 'main'
if plbook.exists(filter: 'site.doctree') {
if plbook.exists_once(filter: 'site.doctree') {