adding dify installer
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -47,3 +47,4 @@ tmp
|
||||
compile_summary.log
|
||||
.summary_lock
|
||||
.aider*
|
||||
*.dylib
|
||||
BIN
examples/installers/infra/dify
Executable file
BIN
examples/installers/infra/dify
Executable file
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,8 @@
|
||||
module code
|
||||
|
||||
import freeflowuniverse.herolib.mcp
|
||||
import freeflowuniverse.herolib.mcp.logger
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
import os
|
||||
import log
|
||||
|
||||
// ===== FILE AND DIRECTORY OPERATIONS =====
|
||||
|
||||
@@ -90,7 +89,7 @@ pub fn get_function_from_module(module_path string, function_name string) !Funct
|
||||
return error('Failed to list V files in ${module_path}: ${err}')
|
||||
}
|
||||
|
||||
log.error('Found ${v_files} V files in ${module_path}')
|
||||
console.print_stderr('Found ${v_files} V files in ${module_path}')
|
||||
for v_file in v_files {
|
||||
// Read the file content
|
||||
content := os.read_file(v_file) or { continue }
|
||||
@@ -114,13 +113,13 @@ pub fn get_function_from_module(module_path string, function_name string) !Funct
|
||||
// RETURNS:
|
||||
// string - the type definition if found, or error if not found
|
||||
pub fn get_type_from_module(module_path string, type_name string) !string {
|
||||
println('Looking for type ${type_name} in module ${module_path}')
|
||||
console.print_debug('Looking for type ${type_name} in module ${module_path}')
|
||||
v_files := list_v_files(module_path) or {
|
||||
return error('Failed to list V files in ${module_path}: ${err}')
|
||||
}
|
||||
|
||||
for v_file in v_files {
|
||||
println('Checking file: ${v_file}')
|
||||
console.print_debug('Checking file: ${v_file}')
|
||||
content := os.read_file(v_file) or { return error('Failed to read file ${v_file}: ${err}') }
|
||||
|
||||
// Look for both regular and pub struct declarations
|
||||
@@ -139,13 +138,12 @@ pub fn get_type_from_module(module_path string, type_name string) !string {
|
||||
type_import := content.split_into_lines().filter(it.contains('import')
|
||||
&& it.contains(type_name))
|
||||
if type_import.len > 0 {
|
||||
log.debug('debugzoooo')
|
||||
mod := type_import[0].trim_space().trim_string_left('import ').all_before(' ')
|
||||
return get_type_from_module(get_module_dir(mod), type_name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
println('Found type ${type_name} in ${v_file} at position ${i}')
|
||||
console.print_debug('Found type ${type_name} in ${v_file} at position ${i}')
|
||||
|
||||
// Find the start of the struct definition including comments
|
||||
mut comment_start := i
|
||||
@@ -186,7 +184,7 @@ pub fn get_type_from_module(module_path string, type_name string) !string {
|
||||
|
||||
// Get the full struct definition including the struct declaration line
|
||||
full_struct := content.substr(line_start, closing_i + 1)
|
||||
println('Found struct definition:\n${full_struct}')
|
||||
console.print_debug('Found struct definition:\n${full_struct}')
|
||||
|
||||
// Return the full struct definition
|
||||
return full_struct
|
||||
@@ -203,7 +201,7 @@ pub fn get_type_from_module(module_path string, type_name string) !string {
|
||||
// RETURNS:
|
||||
// string - test results output, or error if test fails
|
||||
pub fn vtest(fullpath string) !string {
|
||||
logger.info('test ${fullpath}')
|
||||
console.print_item('test ${fullpath}')
|
||||
if !os.exists(fullpath) {
|
||||
return error('File or directory does not exist: ${fullpath}')
|
||||
}
|
||||
@@ -216,12 +214,12 @@ pub fn vtest(fullpath string) !string {
|
||||
return results
|
||||
} else {
|
||||
cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test ${fullpath}'
|
||||
logger.debug('Executing command: ${cmd}')
|
||||
console.print_debug('Executing command: ${cmd}')
|
||||
result := os.execute(cmd)
|
||||
if result.exit_code != 0 {
|
||||
return error('Test failed for ${fullpath} with exit code ${result.exit_code}\n${result.output}')
|
||||
} else {
|
||||
logger.info('Test completed for ${fullpath}')
|
||||
console.print_item('Test completed for ${fullpath}')
|
||||
}
|
||||
return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
|
||||
}
|
||||
@@ -234,12 +232,12 @@ pub fn vtest(fullpath string) !string {
|
||||
// string - vet results output, or error if vet fails
|
||||
fn vet_file(file string) !string {
|
||||
cmd := 'v vet -v -w ${file}'
|
||||
logger.debug('Executing command: ${cmd}')
|
||||
console.print_debug('Executing command: ${cmd}')
|
||||
result := os.execute(cmd)
|
||||
if result.exit_code != 0 {
|
||||
return error('Vet failed for ${file} with exit code ${result.exit_code}\n${result.output}')
|
||||
} else {
|
||||
logger.info('Vet completed for ${file}')
|
||||
console.print_item('Vet completed for ${file}')
|
||||
}
|
||||
return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
|
||||
}
|
||||
@@ -250,7 +248,7 @@ fn vet_file(file string) !string {
|
||||
// RETURNS:
|
||||
// string - vet results output, or error if vet fails
|
||||
pub fn vvet(fullpath string) !string {
|
||||
logger.info('vet ${fullpath}')
|
||||
console.print_item('vet ${fullpath}')
|
||||
if !os.exists(fullpath) {
|
||||
return error('File or directory does not exist: ${fullpath}')
|
||||
}
|
||||
@@ -260,7 +258,7 @@ pub fn vvet(fullpath string) !string {
|
||||
files := list_v_files(fullpath) or { return error('Error listing V files: ${err}') }
|
||||
for file in files {
|
||||
results += vet_file(file) or {
|
||||
logger.error('Failed to vet ${file}: ${err}')
|
||||
console.print_stderr('Failed to vet ${file}: ${err}')
|
||||
return error('Failed to vet ${file}: ${err}')
|
||||
}
|
||||
results += '\n-----------------------\n'
|
||||
|
||||
@@ -69,7 +69,6 @@ fn installed() !bool {
|
||||
mut cfg := get()!
|
||||
mut docker := docker_installer.get()!
|
||||
docker.install()!
|
||||
os.execute("systemctl start docker")
|
||||
|
||||
cmd := "docker ps | grep dify-web"
|
||||
result := os.execute(cmd)
|
||||
@@ -101,7 +100,6 @@ fn install() ! {
|
||||
[ -d ${cfg.path} ] || git clone https://github.com/langgenius/dify.git -b 1.4.0 ${cfg.path}
|
||||
cp ${cfg.path}/docker/.env.example ${cfg.path}/docker/.env
|
||||
|
||||
# Ensure all Docker containers inherit higher open files limits
|
||||
sudo bash -c 'cat > /etc/docker/daemon.json <<EOF
|
||||
{
|
||||
\"default-ulimits\": {
|
||||
|
||||
@@ -25,21 +25,122 @@ This module provides a robust implementation of the JSON-RPC 2.0 protocol in VLa
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. **Client Setup**
|
||||
### 1. **Client Setup and Custom Transports**
|
||||
|
||||
Create a new JSON-RPC client using a custom transport layer.
|
||||
Create a new JSON-RPC client using a custom transport layer. The transport must implement the `IRPCTransportClient` interface, which requires a `send` method:
|
||||
|
||||
```v
|
||||
import jsonrpc
|
||||
pub interface IRPCTransportClient {
|
||||
mut:
|
||||
send(request string, params SendParams) !string
|
||||
}
|
||||
```
|
||||
|
||||
// Implement the IRPCTransportClient interface for your transport (e.g., WebSocket)
|
||||
#### Example: WebSocket Transport
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import net.websocket
|
||||
|
||||
// Implement the IRPCTransportClient interface for WebSocket
|
||||
struct WebSocketTransport {
|
||||
// Add your transport-specific implementation here
|
||||
mut:
|
||||
ws &websocket.Client
|
||||
connected bool
|
||||
}
|
||||
|
||||
// Create a new JSON-RPC client
|
||||
// Create a new WebSocket transport
|
||||
fn new_websocket_transport(url string) !&WebSocketTransport {
|
||||
mut ws := websocket.new_client(url)!
|
||||
ws.connect()!
|
||||
|
||||
return &WebSocketTransport{
|
||||
ws: ws
|
||||
connected: true
|
||||
}
|
||||
}
|
||||
|
||||
// Implement the send method required by IRPCTransportClient
|
||||
fn (mut t WebSocketTransport) send(request string, params jsonrpc.SendParams) !string {
|
||||
if !t.connected {
|
||||
return error('WebSocket not connected')
|
||||
}
|
||||
|
||||
// Send the request
|
||||
t.ws.write_string(request)!
|
||||
|
||||
// Wait for and return the response
|
||||
response := t.ws.read_string()!
|
||||
return response
|
||||
}
|
||||
|
||||
// Create a new JSON-RPC client with WebSocket transport
|
||||
mut transport := new_websocket_transport('ws://localhost:8080')!
|
||||
mut client := jsonrpc.new_client(jsonrpc.Client{
|
||||
transport: WebSocketTransport{}
|
||||
transport: transport
|
||||
})
|
||||
```
|
||||
|
||||
#### Example: Unix Domain Socket Transport
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
import net.unix
|
||||
import time
|
||||
|
||||
// Implement the IRPCTransportClient interface for Unix domain sockets
|
||||
struct UnixSocketTransport {
|
||||
mut:
|
||||
socket_path string
|
||||
}
|
||||
|
||||
// Create a new Unix socket transport
|
||||
fn new_unix_socket_transport(socket_path string) &UnixSocketTransport {
|
||||
return &UnixSocketTransport{
|
||||
socket_path: socket_path
|
||||
}
|
||||
}
|
||||
|
||||
// Implement the send method required by IRPCTransportClient
|
||||
fn (mut t UnixSocketTransport) send(request string, params jsonrpc.SendParams) !string {
|
||||
// Create a Unix domain socket client
|
||||
mut socket := unix.connect_stream(t.socket_path)!
|
||||
defer { socket.close() }
|
||||
|
||||
// Set timeout if specified
|
||||
if params.timeout > 0 {
|
||||
socket.set_read_timeout(params.timeout * time.second)
|
||||
socket.set_write_timeout(params.timeout * time.second)
|
||||
}
|
||||
|
||||
// Send the request
|
||||
socket.write_string(request)!
|
||||
|
||||
// Read the response
|
||||
mut response := ''
|
||||
mut buf := []u8{len: 4096}
|
||||
|
||||
for {
|
||||
bytes_read := socket.read(mut buf)!
|
||||
if bytes_read <= 0 {
|
||||
break
|
||||
}
|
||||
response += buf[..bytes_read].bytestr()
|
||||
|
||||
// Check if we've received a complete JSON response
|
||||
// This is a simple approach; a more robust implementation would parse the JSON
|
||||
if response.ends_with('}') {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// Create a new JSON-RPC client with Unix socket transport
|
||||
mut transport := new_unix_socket_transport('/tmp/jsonrpc.sock')
|
||||
mut client := jsonrpc.new_client(jsonrpc.Client{
|
||||
transport: transport
|
||||
})
|
||||
```
|
||||
|
||||
@@ -48,11 +149,26 @@ mut client := jsonrpc.new_client(jsonrpc.Client{
|
||||
Send a strongly-typed JSON-RPC request and handle the response.
|
||||
|
||||
```v
|
||||
import jsonrpc
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
|
||||
// Define a request method and parameters
|
||||
params := YourParams{...}
|
||||
request := jsonrpc.new_request_generic('methodName', params)
|
||||
// Define your parameter and result types
|
||||
struct UserParams {
|
||||
id int
|
||||
include_details bool
|
||||
}
|
||||
|
||||
struct UserResult {
|
||||
name string
|
||||
email string
|
||||
role string
|
||||
}
|
||||
|
||||
// Create a strongly-typed request with generic parameters
|
||||
params := UserParams{
|
||||
id: 123
|
||||
include_details: true
|
||||
}
|
||||
request := jsonrpc.new_request_generic('getUser', params)
|
||||
|
||||
// Configure send parameters
|
||||
send_params := jsonrpc.SendParams{
|
||||
@@ -60,13 +176,15 @@ send_params := jsonrpc.SendParams{
|
||||
retry: 3
|
||||
}
|
||||
|
||||
// Send the request and process the response
|
||||
response := client.send[YourParams, YourResult](request, send_params) or {
|
||||
// Send the request and receive a strongly-typed response
|
||||
// The generic types [UserParams, UserResult] ensure type safety for both request and response
|
||||
user := client.send[UserParams, UserResult](request, send_params) or {
|
||||
eprintln('Error sending request: $err')
|
||||
return
|
||||
}
|
||||
|
||||
println('Response result: $response')
|
||||
// Access the strongly-typed result fields directly
|
||||
println('User name: ${user.name}, email: ${user.email}, role: ${user.role}')
|
||||
```
|
||||
|
||||
### 3. **Handling Errors**
|
||||
@@ -74,7 +192,7 @@ println('Response result: $response')
|
||||
Use the predefined JSON-RPC errors or create custom ones.
|
||||
|
||||
```v
|
||||
import jsonrpc
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
|
||||
// Predefined error
|
||||
err := jsonrpc.method_not_found
|
||||
@@ -93,17 +211,89 @@ println(response)
|
||||
|
||||
---
|
||||
|
||||
### 4. **Working with Generic Responses**
|
||||
|
||||
The JSON-RPC module provides strong typing for responses using generics, allowing you to define the exact structure of your expected results.
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
|
||||
// Define your result type
|
||||
struct ServerStats {
|
||||
cpu_usage f64
|
||||
memory_usage f64
|
||||
uptime int
|
||||
active_connections int
|
||||
}
|
||||
|
||||
// Create a request (with or without parameters)
|
||||
request := jsonrpc.new_request('getServerStats', '{}')
|
||||
|
||||
// Decode a response directly to your type
|
||||
response_json := '{"jsonrpc":"2.0","result":{"cpu_usage":45.2,"memory_usage":62.7,"uptime":86400,"active_connections":128},"id":1}'
|
||||
response := jsonrpc.decode_response_generic[ServerStats](response_json) or {
|
||||
eprintln('Failed to decode response: $err')
|
||||
return
|
||||
}
|
||||
|
||||
// Access the strongly-typed result
|
||||
if !response.is_error() {
|
||||
stats := response.result() or {
|
||||
eprintln('Error getting result: $err')
|
||||
return
|
||||
}
|
||||
|
||||
println('Server stats:')
|
||||
println('- CPU: ${stats.cpu_usage}%')
|
||||
println('- Memory: ${stats.memory_usage}%')
|
||||
println('- Uptime: ${stats.uptime} seconds')
|
||||
println('- Connections: ${stats.active_connections}')
|
||||
}
|
||||
```
|
||||
|
||||
### 5. **Creating Generic Responses**
|
||||
|
||||
When implementing a JSON-RPC server, you can create strongly-typed responses:
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
|
||||
// Define a result type
|
||||
struct SearchResult {
|
||||
total_count int
|
||||
items []string
|
||||
page int
|
||||
page_size int
|
||||
}
|
||||
|
||||
// Create a response with a strongly-typed result
|
||||
result := SearchResult{
|
||||
total_count: 157
|
||||
items: ['item1', 'item2', 'item3']
|
||||
page: 1
|
||||
page_size: 3
|
||||
}
|
||||
|
||||
// Create a generic response with the strongly-typed result
|
||||
response := jsonrpc.new_response_generic(1, result)
|
||||
|
||||
// Encode the response to send it
|
||||
json := response.encode()
|
||||
println(json)
|
||||
// Output: {"jsonrpc":"2.0","id":1,"result":{"total_count":157,"items":["item1","item2","item3"],"page":1,"page_size":3}}
|
||||
```
|
||||
|
||||
## Modules and Key Components
|
||||
|
||||
### 1. **`model_request.v`**
|
||||
Handles JSON-RPC requests:
|
||||
- Structs: `Request`, `RequestGeneric`
|
||||
- Methods: `new_request`, `new_request_generic`, `decode_request`, etc.
|
||||
- Structs: `Request`, `RequestGeneric[T]`
|
||||
- Methods: `new_request`, `new_request_generic[T]`, `decode_request`, `decode_request_generic[T]`, etc.
|
||||
|
||||
### 2. **`model_response.v`**
|
||||
Handles JSON-RPC responses:
|
||||
- Structs: `Response`, `ResponseGeneric`
|
||||
- Methods: `new_response`, `new_response_generic`, `decode_response`, `validate`, etc.
|
||||
- Structs: `Response`, `ResponseGeneric[D]`
|
||||
- Methods: `new_response`, `new_response_generic[D]`, `decode_response`, `decode_response_generic[D]`, `validate`, etc.
|
||||
|
||||
### 3. **`model_error.v`**
|
||||
Manages JSON-RPC errors:
|
||||
@@ -115,7 +305,7 @@ Manages JSON-RPC errors:
|
||||
Implements the JSON-RPC client:
|
||||
- Structs: `Client`, `SendParams`, `ClientConfig`
|
||||
- Interface: `IRPCTransportClient`
|
||||
- Method: `send`
|
||||
- Method: `send[T, D]` - Generic method for sending requests with parameters of type T and receiving responses with results of type D
|
||||
|
||||
---
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,37 +1,161 @@
|
||||
# JSON Schema
|
||||
|
||||
A V library for the JSON Schema model, and a few handy functions.
|
||||
A V library for working with JSON Schema - providing model definitions, bidirectional code generation, and utility functions.
|
||||
|
||||
## Overview
|
||||
|
||||
This module provides comprehensive tools for working with [JSON Schema](https://json-schema.org/), which is "a declarative language that allows you to annotate and validate JSON documents." The module offers:
|
||||
|
||||
1. **JSON Schema Model**: Complete V struct definitions that map to the JSON Schema specification
|
||||
2. **V Code ↔ JSON Schema Conversion**: Bidirectional conversion between V code and JSON Schema
|
||||
3. **Code Generation**: Generate V structs from JSON Schema and vice versa
|
||||
|
||||
## Module Structure
|
||||
|
||||
- `model.v`: Core JSON Schema model definitions
|
||||
- `decode.v`: Functions to decode JSON Schema strings into Schema structures
|
||||
- `consts_numeric.v`: Numeric constants for JSON Schema
|
||||
- `codegen/`: Code generation functionality
|
||||
- `generate.v`: Generate JSON Schema from V code models
|
||||
- `codegen.v`: Generate V code from JSON Schema
|
||||
- `templates/`: Templates for code generation
|
||||
|
||||
## JSON Schema Model
|
||||
|
||||
Defined [here](https://json-schema.org/), "JSON Schema is a declarative language that allows you to annotate and validate JSON documents." The model in this module provides a struct that can easily be encoded into a JSON Schema.
|
||||
The module provides a comprehensive V struct representation of JSON Schema (based on draft-07), including:
|
||||
|
||||
## Generating a Schema
|
||||
|
||||
The generate.v file provides functions that can generate JSONSchema from [codemodels](../codemodel/). This allows for easy generation of JSON Schema from structs, and is useful for generating schemas from parsed code in v.
|
||||
|
||||
Example:
|
||||
```go
|
||||
struct_ := code.Struct {
|
||||
name: "Mystruct"
|
||||
fields: [
|
||||
code.StructField {
|
||||
name: "myfield"
|
||||
typ: "string"
|
||||
}
|
||||
]
|
||||
```v
|
||||
pub struct Schema {
|
||||
pub mut:
|
||||
schema string // The $schema keyword identifies which version of JSON Schema
|
||||
id string // The $id keyword defines a URI for the schema
|
||||
title string // Human-readable title
|
||||
description string // Human-readable description
|
||||
typ string // Data type (string, number, object, array, boolean, null)
|
||||
properties map[string]SchemaRef // Object properties when type is "object"
|
||||
additional_properties ?SchemaRef // Controls additional properties
|
||||
required []string // List of required property names
|
||||
items ?Items // Schema for array items when type is "array"
|
||||
// ... and many more validation properties
|
||||
}
|
||||
schema := struct_to_schema(struct_)
|
||||
```
|
||||
|
||||
### Generating Schemas for Anonymous Structs
|
||||
|
||||
The properties of a JSON Schema is a list of key value pairs, where keys represent the subschema's name and the value is the schema (or the reference to the schema which is defined elsewhere) of the property. This is analogous to the fields of a struct, which is represented by a field name and a type.
|
||||
|
||||
It's good practice to define object type schemas separately and reference them in properties, especially if the same schema is used in multiple places. However, object type schemas can also be defined in property definitions. This may make sense if the schema is exclusively used as a property of a schema, similar to using an anonymous struct for the type definition of a field of a struct.
|
||||
|
||||
As such, schema's generated from structs that declare anonymous structs as field types, include a schema definition in the property field.
|
||||
## Code Generation
|
||||
|
||||
### V Code to JSON Schema
|
||||
|
||||
The module can generate JSON Schema from V code models, making it easy to create schemas from your existing V structs:
|
||||
|
||||
```v
|
||||
// Example: Generate JSON Schema from a V struct
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen
|
||||
|
||||
// Create a struct model
|
||||
struct_ := code.Struct{
|
||||
name: 'Person'
|
||||
description: 'A person record'
|
||||
fields: [
|
||||
code.StructField{
|
||||
name: 'name'
|
||||
typ: 'string'
|
||||
description: 'Full name'
|
||||
},
|
||||
code.StructField{
|
||||
name: 'age'
|
||||
typ: 'int'
|
||||
description: 'Age in years'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Generate JSON Schema from the struct
|
||||
schema := codegen.struct_to_schema(struct_)
|
||||
|
||||
// The resulting schema will represent:
|
||||
// {
|
||||
// "title": "Person",
|
||||
// "description": "A person record",
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "name": {
|
||||
// "type": "string",
|
||||
// "description": "Full name"
|
||||
// },
|
||||
// "age": {
|
||||
// "type": "integer",
|
||||
// "description": "Age in years"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
### JSON Schema to V Code
|
||||
|
||||
The module can also generate V code from JSON Schema:
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen
|
||||
|
||||
// Create or load a JSON Schema
|
||||
schema := jsonschema.Schema{
|
||||
title: 'Person'
|
||||
description: 'A person record'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'name': jsonschema.Schema{
|
||||
typ: 'string'
|
||||
description: 'Full name'
|
||||
}
|
||||
'age': jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
description: 'Age in years'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate V structs from the schema
|
||||
v_code := codegen.schema_to_v(schema)
|
||||
|
||||
// The resulting V code will be:
|
||||
// module schema.title.
|
||||
//
|
||||
// // A person record
|
||||
// struct Person {
|
||||
// name string // Full name
|
||||
// age int // Age in years
|
||||
// }
|
||||
```
|
||||
|
||||
### Advanced Features
|
||||
|
||||
#### Handling References
|
||||
|
||||
The module supports JSON Schema references (`$ref`), allowing for modular schema definitions:
|
||||
|
||||
```v
|
||||
// Example of a schema with references
|
||||
schema := jsonschema.Schema{
|
||||
// ...
|
||||
properties: {
|
||||
'address': jsonschema.Reference{
|
||||
ref: '#/components/schemas/Address'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Anonymous Structs
|
||||
|
||||
When generating schemas from V structs with anonymous struct fields, the module creates inline schema definitions in the property field, similar to how anonymous structs work in V.
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
As [this issue](https://github.com/vlang/v/issues/15081) is still not resolved, a json schema cannot be decoded into the json schema structure defined in this module. As such, to decode json schema string into a structure the `pub fn decode(data str) !Schema` function defined in `decode.v` must be used.
|
||||
Due to [this issue](https://github.com/vlang/v/issues/15081), a JSON Schema cannot be directly decoded into the JSON Schema structure defined in this module. To decode a JSON Schema string into a structure, use the `pub fn decode(data str) !Schema` function defined in `decode.v`.
|
||||
@@ -27,7 +27,7 @@ struct TestPerson {
|
||||
}
|
||||
}
|
||||
}
|
||||
encoded := schema_to_structs(schema)!
|
||||
encoded := schema_to_structs(schema)
|
||||
assert encoded.len == 1
|
||||
assert encoded[0].trim_space() == struct_str.trim_space()
|
||||
}
|
||||
@@ -60,7 +60,7 @@ struct TestPerson {
|
||||
}
|
||||
}
|
||||
}
|
||||
encoded := schema_to_structs(schema)!
|
||||
encoded := schema_to_structs(schema)
|
||||
assert encoded.len == 1
|
||||
assert encoded[0].trim_space() == struct_str.trim_space()
|
||||
}
|
||||
@@ -97,6 +97,6 @@ fn test_schema_to_structs_recursive() ! {
|
||||
}
|
||||
}
|
||||
}
|
||||
encoded := schema_to_structs(schema)!
|
||||
encoded := schema_to_structs(schema)
|
||||
log.debug(encoded.str())
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
module doctreeclient
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import os
|
||||
|
||||
// List of recognized image file extensions
|
||||
@@ -16,13 +17,17 @@ pub enum DocTreeError {
|
||||
|
||||
// get_page_path returns the path for a page in a collection
|
||||
pub fn (mut c DocTreeClient) get_page_path(collection_name string, page_name string) !string {
|
||||
// Apply name_fix to collection and page names
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
fixed_page_name := texttools.name_fix(page_name)
|
||||
|
||||
// Check if the collection exists
|
||||
collection_path := c.redis.hget('doctree:path', collection_name) or {
|
||||
collection_path := c.redis.hget('doctree:path', fixed_collection_name) or {
|
||||
return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found')
|
||||
}
|
||||
|
||||
// Get the relative path of the page within the collection
|
||||
rel_path := c.redis.hget('doctree:${collection_name}', page_name) or {
|
||||
rel_path := c.redis.hget('doctree:${fixed_collection_name}', fixed_page_name) or {
|
||||
return error('${DocTreeError.page_not_found}: Page "${page_name}" not found in collection "${collection_name}"')
|
||||
}
|
||||
|
||||
@@ -32,13 +37,17 @@ pub fn (mut c DocTreeClient) get_page_path(collection_name string, page_name str
|
||||
|
||||
// get_file_path returns the path for a file in a collection
|
||||
pub fn (mut c DocTreeClient) get_file_path(collection_name string, file_name string) !string {
|
||||
// Apply name_fix to collection and file names
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
fixed_file_name := texttools.name_fix(file_name)
|
||||
|
||||
// Check if the collection exists
|
||||
collection_path := c.redis.hget('doctree:path', collection_name) or {
|
||||
collection_path := c.redis.hget('doctree:path', fixed_collection_name) or {
|
||||
return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found')
|
||||
}
|
||||
|
||||
// Get the relative path of the file within the collection
|
||||
rel_path := c.redis.hget('doctree:${collection_name}', file_name) or {
|
||||
rel_path := c.redis.hget('doctree:${fixed_collection_name}', fixed_file_name) or {
|
||||
return error('${DocTreeError.file_not_found}: File "${file_name}" not found in collection "${collection_name}"')
|
||||
}
|
||||
|
||||
@@ -48,13 +57,17 @@ pub fn (mut c DocTreeClient) get_file_path(collection_name string, file_name str
|
||||
|
||||
// get_image_path returns the path for an image in a collection
|
||||
pub fn (mut c DocTreeClient) get_image_path(collection_name string, image_name string) !string {
|
||||
// Apply name_fix to collection and image names
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
fixed_image_name := texttools.name_fix(image_name)
|
||||
|
||||
// Check if the collection exists
|
||||
collection_path := c.redis.hget('doctree:path', collection_name) or {
|
||||
collection_path := c.redis.hget('doctree:path', fixed_collection_name) or {
|
||||
return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found')
|
||||
}
|
||||
|
||||
// Get the relative path of the image within the collection
|
||||
rel_path := c.redis.hget('doctree:${collection_name}', image_name) or {
|
||||
rel_path := c.redis.hget('doctree:${fixed_collection_name}', fixed_image_name) or {
|
||||
return error('${DocTreeError.image_not_found}: Image "${image_name}" not found in collection "${collection_name}"')
|
||||
}
|
||||
|
||||
@@ -64,51 +77,66 @@ pub fn (mut c DocTreeClient) get_image_path(collection_name string, image_name s
|
||||
|
||||
// page_exists checks if a page exists in a collection
|
||||
pub fn (mut c DocTreeClient) page_exists(collection_name string, page_name string) bool {
|
||||
// Apply name_fix to collection and page names
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
fixed_page_name := texttools.name_fix(page_name)
|
||||
|
||||
// Check if the collection exists
|
||||
e:=c.redis.hexists('doctree:path', collection_name) or {
|
||||
e := c.redis.hexists('doctree:path', fixed_collection_name) or {
|
||||
return false
|
||||
}
|
||||
if !e{
|
||||
if !e {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the page exists in the collection
|
||||
return c.redis.hexists('doctree:${collection_name}', page_name) or {false}
|
||||
return c.redis.hexists('doctree:${fixed_collection_name}', fixed_page_name) or {false}
|
||||
}
|
||||
|
||||
// file_exists checks if a file exists in a collection
|
||||
pub fn (mut c DocTreeClient) file_exists(collection_name string, file_name string) bool {
|
||||
// Check if the collection exists
|
||||
e:=c.redis.hexists('doctree:path', collection_name) or {
|
||||
return false
|
||||
}
|
||||
if !e{
|
||||
return false
|
||||
}
|
||||
// Apply name_fix to collection and file names
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
fixed_file_name := texttools.name_fix(file_name)
|
||||
|
||||
// Check if the collection exists
|
||||
e := c.redis.hexists('doctree:path', fixed_collection_name) or {
|
||||
return false
|
||||
}
|
||||
if !e {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the file exists in the collection
|
||||
return c.redis.hexists('doctree:${collection_name}', file_name) or {false}
|
||||
return c.redis.hexists('doctree:${fixed_collection_name}', fixed_file_name) or {false}
|
||||
}
|
||||
|
||||
// image_exists checks if an image exists in a collection
|
||||
pub fn (mut c DocTreeClient) image_exists(collection_name string, image_name string) bool {
|
||||
// Apply name_fix to collection and image names
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
fixed_image_name := texttools.name_fix(image_name)
|
||||
|
||||
// Check if the collection exists
|
||||
e:=c.redis.hexists('doctree:path', collection_name) or {
|
||||
e := c.redis.hexists('doctree:path', fixed_collection_name) or {
|
||||
return false
|
||||
}
|
||||
if !e{
|
||||
if !e {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the image exists in the collection
|
||||
return c.redis.hexists('doctree:${collection_name}', image_name) or {false}
|
||||
return c.redis.hexists('doctree:${fixed_collection_name}', fixed_image_name) or {false}
|
||||
}
|
||||
|
||||
// get_page_content returns the content of a page in a collection
|
||||
pub fn (mut c DocTreeClient) get_page_content(collection_name string, page_name string) !string {
|
||||
// Apply name_fix to collection and page names
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
fixed_page_name := texttools.name_fix(page_name)
|
||||
|
||||
// Get the path for the page
|
||||
page_path := c.get_page_path(collection_name, page_name)!
|
||||
page_path := c.get_page_path(fixed_collection_name, fixed_page_name)!
|
||||
|
||||
// Use pathlib to read the file content
|
||||
mut path := pathlib.get_file(path: page_path)!
|
||||
@@ -130,13 +158,16 @@ pub fn (mut c DocTreeClient) list_collections() ![]string {
|
||||
|
||||
// list_pages returns a list of all page names in a collection
|
||||
pub fn (mut c DocTreeClient) list_pages(collection_name string) ![]string {
|
||||
// Apply name_fix to collection name
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
|
||||
// Check if the collection exists
|
||||
if ! (c.redis.hexists('doctree:path', collection_name) or {false}) {
|
||||
if ! (c.redis.hexists('doctree:path', fixed_collection_name) or {false}) {
|
||||
return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found')
|
||||
}
|
||||
|
||||
// Get all keys from the collection hash
|
||||
all_keys := c.redis.hkeys('doctree:${collection_name}')!
|
||||
all_keys := c.redis.hkeys('doctree:${fixed_collection_name}')!
|
||||
|
||||
// Filter out only the page names (those without file extensions)
|
||||
mut page_names := []string{}
|
||||
@@ -151,19 +182,22 @@ pub fn (mut c DocTreeClient) list_pages(collection_name string) ![]string {
|
||||
|
||||
// list_files returns a list of all file names in a collection
|
||||
pub fn (mut c DocTreeClient) list_files(collection_name string) ![]string {
|
||||
// Apply name_fix to collection name
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
|
||||
// Check if the collection exists
|
||||
if ! (c.redis.hexists('doctree:path', collection_name) or {false}) {
|
||||
if ! (c.redis.hexists('doctree:path', fixed_collection_name) or {false}) {
|
||||
return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found')
|
||||
}
|
||||
|
||||
// Get all keys from the collection hash
|
||||
all_keys := c.redis.hkeys('doctree:${collection_name}')!
|
||||
all_keys := c.redis.hkeys('doctree:${fixed_collection_name}')!
|
||||
|
||||
// Filter out only the file names (those with file extensions, but not images)
|
||||
mut file_names := []string{}
|
||||
for key in all_keys {
|
||||
// Get the value (path) for this key
|
||||
value := c.redis.hget('doctree:${collection_name}', key) or { continue }
|
||||
value := c.redis.hget('doctree:${fixed_collection_name}', key) or { continue }
|
||||
|
||||
// Check if the value contains a file extension (has a dot)
|
||||
if value.contains('.') {
|
||||
@@ -188,19 +222,22 @@ pub fn (mut c DocTreeClient) list_files(collection_name string) ![]string {
|
||||
|
||||
// list_images returns a list of all image names in a collection
|
||||
pub fn (mut c DocTreeClient) list_images(collection_name string) ![]string {
|
||||
// Apply name_fix to collection name
|
||||
fixed_collection_name := texttools.name_fix(collection_name)
|
||||
|
||||
// Check if the collection exists
|
||||
if ! (c.redis.hexists('doctree:path', collection_name) or {false}) {
|
||||
if ! (c.redis.hexists('doctree:path', fixed_collection_name) or {false}) {
|
||||
return error('${DocTreeError.collection_not_found}: Collection "${collection_name}" not found')
|
||||
}
|
||||
|
||||
// Get all keys from the collection hash
|
||||
all_keys := c.redis.hkeys('doctree:${collection_name}')!
|
||||
all_keys := c.redis.hkeys('doctree:${fixed_collection_name}')!
|
||||
|
||||
// Filter out only the image names (those whose values end with image extensions)
|
||||
mut image_names := []string{}
|
||||
for key in all_keys {
|
||||
// Get the value (path) for this key
|
||||
value := c.redis.hget('doctree:${collection_name}', key) or { continue }
|
||||
value := c.redis.hget('doctree:${fixed_collection_name}', key) or { continue }
|
||||
|
||||
// Check if the value ends with any of the image extensions
|
||||
for ext in image_extensions {
|
||||
|
||||
Reference in New Issue
Block a user