refactor and reorganize code modules
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
module embedding
|
||||
|
||||
pub struct Embedder {
|
||||
Embedded
|
||||
}
|
||||
|
||||
pub struct Embedded {
|
||||
id int
|
||||
related_ids []int
|
||||
name string
|
||||
tags []string
|
||||
date time.Time
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.core.codemodel { Struct }
|
||||
|
||||
code_path := '${os.dir(@FILE)}/embedding.v'
|
||||
|
||||
code := codeparser.parse_v(code_path)!
|
||||
assert code.len == 2
|
||||
assert code[0] is Struct
|
||||
embedder_struct := code[0] as Struct
|
||||
println(embedder_struct.fields.map('${it.name}: ${it.typ.symbol}'))
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import os
|
||||
import flag
|
||||
import freeflowuniverse.herolib.code.generator.installer_client as generator
|
||||
import freeflowuniverse.herolib.core.generator.installer_client as generator
|
||||
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application('generate.vsh')
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
# Code Model
|
||||
|
||||
A set of models that represent code structures, such as structs, functions, imports, and constants. The motivation behind this module is to provide a more generic and lighter alternative to v.ast code models, that can be used for code parsing and code generation across multiple languages.
|
||||
|
||||
## Features
|
||||
|
||||
- **Struct Modeling**: Complete struct representation including:
|
||||
- Fields with types, visibility, and mutability
|
||||
- Embedded structs
|
||||
- Generic type support
|
||||
- Attributes
|
||||
- Documentation comments
|
||||
|
||||
- **Function Modeling**: Comprehensive function support with:
|
||||
- Parameters and return types
|
||||
- Receiver methods
|
||||
- Optional and result types
|
||||
- Function body content
|
||||
- Visibility modifiers
|
||||
|
||||
- **Type System**: Rich type representation including:
|
||||
- Basic types
|
||||
- Reference types
|
||||
- Arrays and maps
|
||||
- Optional and result types
|
||||
- Mutable and shared types
|
||||
|
||||
- **Code Organization**:
|
||||
- Import statements with module and type specifications
|
||||
- Constants (both single and grouped)
|
||||
- Custom code blocks for specialized content
|
||||
- Documentation through single and multi-line comments
|
||||
|
||||
## Using Codemodel
|
||||
|
||||
The codemodel module provides a set of types and utilities for working with code structures. Here are some examples of how to use the module:
|
||||
|
||||
### Working with Functions
|
||||
|
||||
```v
|
||||
// Parse a function definition
|
||||
fn_def := 'pub fn (mut app App) process() !string'
|
||||
function := codemodel.parse_function(fn_def)!
|
||||
println(function.name) // prints: process
|
||||
println(function.receiver.name) // prints: app
|
||||
println(function.result.typ.symbol) // prints: string
|
||||
|
||||
// Create a function model
|
||||
my_fn := Function{
|
||||
name: 'add'
|
||||
is_pub: true
|
||||
params: [
|
||||
Param{
|
||||
name: 'x'
|
||||
typ: Type{symbol: 'int'}
|
||||
},
|
||||
Param{
|
||||
name: 'y'
|
||||
typ: Type{symbol: 'int'}
|
||||
}
|
||||
]
|
||||
result: Result{
|
||||
typ: Type{symbol: 'int'}
|
||||
}
|
||||
body: 'return x + y'
|
||||
}
|
||||
```
|
||||
|
||||
### Working with Imports
|
||||
|
||||
```v
|
||||
// Parse an import statement
|
||||
import_def := 'import os { exists }'
|
||||
imp := codemodel.parse_import(import_def)
|
||||
println(imp.mod) // prints: os
|
||||
println(imp.types) // prints: ['exists']
|
||||
|
||||
// Create an import model
|
||||
my_import := Import{
|
||||
mod: 'json'
|
||||
types: ['encode', 'decode']
|
||||
}
|
||||
```
|
||||
|
||||
### Working with Constants
|
||||
|
||||
```v
|
||||
// Parse constant definitions
|
||||
const_def := 'const max_size = 1000'
|
||||
constant := codemodel.parse_const(const_def)!
|
||||
println(constant.name) // prints: max_size
|
||||
println(constant.value) // prints: 1000
|
||||
|
||||
// Parse grouped constants
|
||||
const_block := 'const (
|
||||
pi = 3.14
|
||||
e = 2.718
|
||||
)'
|
||||
constants := codemodel.parse_consts(const_block)!
|
||||
```
|
||||
|
||||
### Working with Types
|
||||
|
||||
The module provides rich type modeling capabilities:
|
||||
|
||||
```v
|
||||
// Basic type
|
||||
basic := Type{
|
||||
symbol: 'string'
|
||||
}
|
||||
|
||||
// Array type
|
||||
array := Type{
|
||||
symbol: 'string'
|
||||
is_array: true
|
||||
}
|
||||
|
||||
// Optional type
|
||||
optional := Type{
|
||||
symbol: 'int'
|
||||
is_optional: true
|
||||
}
|
||||
|
||||
// Result type
|
||||
result := Type{
|
||||
symbol: 'string'
|
||||
is_result: true
|
||||
}
|
||||
```
|
||||
|
||||
## Code Generation
|
||||
|
||||
The codemodel types can be used as intermediate structures for code generation. For example, generating documentation:
|
||||
|
||||
```v
|
||||
mut doc := ''
|
||||
|
||||
// Document a struct
|
||||
for field in my_struct.fields {
|
||||
doc += '- ${field.name}: ${field.typ.symbol}'
|
||||
if field.description != '' {
|
||||
doc += ' // ${field.description}'
|
||||
}
|
||||
doc += '\n'
|
||||
}
|
||||
```
|
||||
|
||||
The codemodel module provides a foundation for building tools that need to work with code structures, whether for analysis, transformation, or generation purposes.
|
||||
@@ -1,205 +0,0 @@
|
||||
module codemodel
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
// Code is a list of statements
|
||||
// pub type Code = []CodeItem
|
||||
|
||||
pub type CodeItem = Alias | Comment | CustomCode | Function | Import | Struct | Sumtype
|
||||
|
||||
// item for adding custom code in
|
||||
pub struct CustomCode {
|
||||
pub:
|
||||
text string
|
||||
}
|
||||
|
||||
pub struct Comment {
|
||||
pub:
|
||||
text string
|
||||
is_multi bool
|
||||
}
|
||||
|
||||
pub struct Struct {
|
||||
pub mut:
|
||||
name string
|
||||
description string
|
||||
mod string
|
||||
is_pub bool
|
||||
embeds []Struct @[str: skip]
|
||||
generics map[string]string @[str: skip]
|
||||
attrs []Attribute
|
||||
fields []StructField
|
||||
}
|
||||
|
||||
pub struct Sumtype {
|
||||
pub:
|
||||
name string
|
||||
description string
|
||||
types []Type
|
||||
}
|
||||
|
||||
pub struct StructField {
|
||||
pub mut:
|
||||
comments []Comment
|
||||
attrs []Attribute
|
||||
name string
|
||||
description string
|
||||
default string
|
||||
is_pub bool
|
||||
is_mut bool
|
||||
is_ref bool
|
||||
anon_struct Struct @[str: skip] // sometimes fields may hold anonymous structs
|
||||
typ Type
|
||||
structure Struct @[str: skip]
|
||||
}
|
||||
|
||||
pub struct Attribute {
|
||||
pub:
|
||||
name string // [name]
|
||||
has_arg bool
|
||||
arg string // [name: arg]
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub:
|
||||
name string
|
||||
receiver Param
|
||||
is_pub bool
|
||||
mod string
|
||||
pub mut:
|
||||
description string
|
||||
params []Param
|
||||
body string
|
||||
result Result
|
||||
has_return bool
|
||||
}
|
||||
|
||||
pub fn parse_function(code_ string) !Function {
|
||||
mut code := code_.trim_space()
|
||||
is_pub := code.starts_with('pub ')
|
||||
if is_pub {
|
||||
code = code.trim_string_left('pub ').trim_space()
|
||||
}
|
||||
|
||||
is_fn := code.starts_with('fn ')
|
||||
if !is_fn {
|
||||
return error('invalid function format')
|
||||
}
|
||||
code = code.trim_string_left('fn ').trim_space()
|
||||
|
||||
receiver := if code.starts_with('(') {
|
||||
param_str := code.all_after('(').all_before(')').trim_space()
|
||||
code = code.all_after(')').trim_space()
|
||||
parse_param(param_str)!
|
||||
} else {
|
||||
Param{}
|
||||
}
|
||||
|
||||
name := code.all_before('(').trim_space()
|
||||
code = code.trim_string_left(name).trim_space()
|
||||
|
||||
params_str := code.all_after('(').all_before(')')
|
||||
params := if params_str.trim_space() != '' {
|
||||
params_str_lst := params_str.split(',')
|
||||
params_str_lst.map(parse_param(it)!)
|
||||
} else {
|
||||
[]Param{}
|
||||
}
|
||||
result := parse_result(code.all_after(')').all_before('{').replace(' ', ''))!
|
||||
|
||||
body := if code.contains('{') { code.all_after('{').all_before_last('}') } else { '' }
|
||||
return Function{
|
||||
name: name
|
||||
receiver: receiver
|
||||
params: params
|
||||
result: result
|
||||
body: body
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_param(code_ string) !Param {
|
||||
mut code := code_.trim_space()
|
||||
is_mut := code.starts_with('mut ')
|
||||
if is_mut {
|
||||
code = code.trim_string_left('mut ').trim_space()
|
||||
}
|
||||
split := code.split(' ').filter(it != '')
|
||||
if split.len != 2 {
|
||||
return error('invalid param format: ${code_}')
|
||||
}
|
||||
return Param{
|
||||
name: split[0]
|
||||
typ: Type{
|
||||
symbol: split[1]
|
||||
}
|
||||
mutable: is_mut
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_result(code_ string) !Result {
|
||||
code := code_.replace(' ', '').trim_space()
|
||||
|
||||
return Result{
|
||||
result: code_.starts_with('!')
|
||||
optional: code_.starts_with('?')
|
||||
typ: Type{
|
||||
symbol: code.trim('!?')
|
||||
is_optional: code.starts_with('?')
|
||||
is_result: code.starts_with('!')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Param {
|
||||
pub:
|
||||
required bool
|
||||
mutable bool
|
||||
is_shared bool
|
||||
is_optional bool
|
||||
description string
|
||||
name string
|
||||
typ Type
|
||||
struct_ Struct
|
||||
}
|
||||
|
||||
pub struct Result {
|
||||
pub mut:
|
||||
typ Type
|
||||
description string
|
||||
name string
|
||||
result bool // whether is result type
|
||||
optional bool // whether is result type
|
||||
structure Struct
|
||||
}
|
||||
|
||||
// todo: maybe make 'is_' fields methods?
|
||||
pub struct Type {
|
||||
pub mut:
|
||||
is_reference bool @[str: skip]
|
||||
is_map bool @[str: skip]
|
||||
is_array bool
|
||||
is_mutable bool @[str: skip]
|
||||
is_shared bool @[str: skip]
|
||||
is_optional bool @[str: skip]
|
||||
is_result bool @[str: skip]
|
||||
symbol string
|
||||
mod string @[str: skip]
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
pub mut:
|
||||
name string
|
||||
extension string
|
||||
content string
|
||||
}
|
||||
|
||||
pub fn (f File) write(path string) ! {
|
||||
mut fd_file := pathlib.get_file(path: '${path}/${f.name}.${f.extension}')!
|
||||
fd_file.write(f.content)!
|
||||
}
|
||||
|
||||
pub struct Alias {
|
||||
pub:
|
||||
name string
|
||||
description string
|
||||
typ Type
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
module codemodel
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
pub struct Module {
|
||||
pub mut:
|
||||
name string
|
||||
files []CodeFile
|
||||
misc_files []File
|
||||
// model CodeFile
|
||||
// methods CodeFile
|
||||
}
|
||||
|
||||
pub fn (mod Module) write_v(path string, options WriteOptions) ! {
|
||||
mut module_dir := pathlib.get_dir(
|
||||
path: '${path}/${mod.name}'
|
||||
empty: options.overwrite
|
||||
)!
|
||||
|
||||
if !options.overwrite && module_dir.exists() {
|
||||
return
|
||||
}
|
||||
|
||||
for file in mod.files {
|
||||
file.write_v(module_dir.path, options)!
|
||||
}
|
||||
for file in mod.misc_files {
|
||||
file.write(module_dir.path)!
|
||||
}
|
||||
|
||||
if options.format {
|
||||
os.execute('v fmt -w ${module_dir.path}')
|
||||
}
|
||||
if options.document {
|
||||
os.execute('v doc -f html -o ${module_dir.path}/docs ${module_dir.path}')
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
module codemodel
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
pub struct WriteCode {
|
||||
destination string
|
||||
}
|
||||
|
||||
interface ICodeItem {
|
||||
vgen() string
|
||||
}
|
||||
|
||||
pub fn vgen(code []CodeItem) string {
|
||||
mut str := ''
|
||||
for item in code {
|
||||
if item is Function {
|
||||
str += '\n${item.vgen()}'
|
||||
}
|
||||
if item is Struct {
|
||||
str += '\n${item.vgen()}'
|
||||
}
|
||||
if item is CustomCode {
|
||||
str += '\n${item.vgen()}'
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// pub fn (code Code) vgen() string {
|
||||
// return code.items.map(it.vgen()).join_lines()
|
||||
// }
|
||||
|
||||
// vgen_import generates an import statement for a given type
|
||||
pub fn (import_ Import) vgen() string {
|
||||
types_str := if import_.types.len > 0 {
|
||||
'{${import_.types.join(', ')}}'
|
||||
} else {
|
||||
''
|
||||
} // comma separated string list of types
|
||||
return 'import ${import_.mod} ${types_str}'
|
||||
}
|
||||
|
||||
// TODO: enfore that cant be both mutable and shared
|
||||
pub fn (type_ Type) vgen() string {
|
||||
mut type_str := ''
|
||||
if type_.is_mutable {
|
||||
type_str += 'mut '
|
||||
} else if type_.is_shared {
|
||||
type_str += 'shared '
|
||||
}
|
||||
|
||||
if type_.is_optional {
|
||||
type_str += '?'
|
||||
} else if type_.is_result {
|
||||
type_str += '!'
|
||||
}
|
||||
|
||||
return '${type_str} ${type_.symbol}'
|
||||
}
|
||||
|
||||
pub fn (field StructField) vgen() string {
|
||||
symbol := field.get_type_symbol()
|
||||
mut vstr := '${field.name} ${symbol}'
|
||||
if field.description != '' {
|
||||
vstr += '// ${field.description}'
|
||||
}
|
||||
return vstr
|
||||
}
|
||||
|
||||
pub fn (field StructField) get_type_symbol() string {
|
||||
mut field_str := if field.structure.name != '' {
|
||||
field.structure.get_type_symbol()
|
||||
} else {
|
||||
field.typ.symbol
|
||||
}
|
||||
|
||||
if field.is_ref {
|
||||
field_str = '&${field_str}'
|
||||
}
|
||||
|
||||
return field_str
|
||||
}
|
||||
|
||||
pub fn (structure Struct) get_type_symbol() string {
|
||||
mut symbol := if structure.mod != '' {
|
||||
'${structure.mod.all_after_last('.')}.${structure.name}'
|
||||
} else {
|
||||
structure.name
|
||||
}
|
||||
if structure.generics.len > 0 {
|
||||
symbol = '${symbol}${vgen_generics(structure.generics)}'
|
||||
}
|
||||
|
||||
return symbol
|
||||
}
|
||||
|
||||
pub fn vgen_generics(generics map[string]string) string {
|
||||
if generics.keys().len == 0 {
|
||||
return ''
|
||||
}
|
||||
mut vstr := '['
|
||||
for key, val in generics {
|
||||
vstr += if val != '' { val } else { key }
|
||||
}
|
||||
return '${vstr}]'
|
||||
}
|
||||
|
||||
// vgen_function generates a function statement for a function
|
||||
pub fn (function Function) vgen(options WriteOptions) string {
|
||||
mut params_ := function.params.map(Param{
|
||||
...it
|
||||
typ: Type{
|
||||
symbol: if it.struct_.name != '' {
|
||||
it.struct_.name
|
||||
} else {
|
||||
it.typ.symbol
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
optionals := params_.filter(it.is_optional)
|
||||
options_struct := Struct{
|
||||
name: '${texttools.name_fix_snake_to_pascal(function.name)}Options'
|
||||
attrs: [Attribute{
|
||||
name: 'params'
|
||||
}]
|
||||
fields: optionals.map(StructField{
|
||||
name: it.name
|
||||
description: it.description
|
||||
typ: Type{
|
||||
symbol: it.typ.symbol
|
||||
}
|
||||
})
|
||||
}
|
||||
if optionals.len > 0 {
|
||||
params_ << Param{
|
||||
name: 'options'
|
||||
typ: Type{
|
||||
symbol: options_struct.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params := params_.filter(!it.is_optional).map('${it.name} ${it.typ.symbol}').join(', ')
|
||||
|
||||
receiver := function.receiver.vgen()
|
||||
|
||||
mut function_str := $tmpl('templates/function/function.v.template')
|
||||
|
||||
// if options.format {
|
||||
// result := os.execute_opt('echo "${function_str.replace('$', '\\$')}" | v fmt') or {
|
||||
// panic('${function_str}\n${err}')
|
||||
// }
|
||||
// function_str = result.output
|
||||
// }
|
||||
function_str = function_str.split_into_lines().filter(!it.starts_with('import ')).join('\n')
|
||||
|
||||
return if options_struct.fields.len != 0 {
|
||||
'${options_struct.vgen()}\n${function_str}'
|
||||
} else {
|
||||
function_str
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (param Param) vgen() string {
|
||||
if param.name == '' {
|
||||
return ''
|
||||
}
|
||||
sym := if param.struct_.name != '' {
|
||||
param.struct_.get_type_symbol()
|
||||
} else {
|
||||
param.typ.symbol
|
||||
}
|
||||
|
||||
mut vstr := '${param.name} ${sym}'
|
||||
if param.typ.is_reference {
|
||||
vstr = '&${vstr}'
|
||||
}
|
||||
if param.mutable {
|
||||
vstr = 'mut ${vstr}'
|
||||
}
|
||||
return '(${vstr})'
|
||||
}
|
||||
|
||||
// vgen_function generates a function statement for a function
|
||||
pub fn (struct_ Struct) vgen() string {
|
||||
gen := VGenerator{false}
|
||||
return gen.generate_struct(struct_) or { panic(err) }
|
||||
// mut struct_str := $tmpl('templates/struct/struct.v.template')
|
||||
// return struct_str
|
||||
// result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {panic(err)}
|
||||
// return result.output
|
||||
}
|
||||
|
||||
pub struct VGenerator {
|
||||
format bool
|
||||
}
|
||||
|
||||
pub fn (gen VGenerator) generate_struct(struct_ Struct) !string {
|
||||
name := if struct_.generics.len > 0 {
|
||||
'${struct_.name}${vgen_generics(struct_.generics)}'
|
||||
} else {
|
||||
struct_.name
|
||||
}
|
||||
|
||||
prefix := if struct_.is_pub {
|
||||
'pub'
|
||||
} else {
|
||||
''
|
||||
}
|
||||
|
||||
priv_fields := struct_.fields.filter(!it.is_mut && !it.is_pub).map(gen.generate_struct_field(it))
|
||||
pub_fields := struct_.fields.filter(!it.is_mut && it.is_pub).map(gen.generate_struct_field(it))
|
||||
mut_fields := struct_.fields.filter(it.is_mut && !it.is_pub).map(gen.generate_struct_field(it))
|
||||
pub_mut_fields := struct_.fields.filter(it.is_mut && it.is_pub).map(gen.generate_struct_field(it))
|
||||
|
||||
mut struct_str := $tmpl('templates/struct/struct.v.template')
|
||||
if gen.format {
|
||||
result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {
|
||||
console.print_debug(struct_str)
|
||||
panic(err)
|
||||
}
|
||||
return result.output
|
||||
}
|
||||
return struct_str
|
||||
}
|
||||
|
||||
pub fn (gen VGenerator) generate_struct_field(field StructField) string {
|
||||
symbol := field.get_type_symbol()
|
||||
mut vstr := '${field.name} ${symbol}'
|
||||
if field.description != '' {
|
||||
vstr += '// ${field.description}'
|
||||
}
|
||||
return vstr
|
||||
}
|
||||
|
||||
pub fn (custom CustomCode) vgen() string {
|
||||
return custom.text
|
||||
}
|
||||
|
||||
// vgen_function generates a function statement for a function
|
||||
pub fn (result Result) vgen() string {
|
||||
result_type := if result.structure.name != '' {
|
||||
result.structure.get_type_symbol()
|
||||
} else if result.typ.symbol == 'void' {
|
||||
''
|
||||
} else {
|
||||
if result.typ.is_array {
|
||||
'[]${result.typ.symbol}'
|
||||
} else {
|
||||
result.typ.symbol
|
||||
}
|
||||
}
|
||||
str := if result.result {
|
||||
'!'
|
||||
} else if result.typ.is_result {
|
||||
'!'
|
||||
} else {
|
||||
''
|
||||
}
|
||||
return '${str}${result_type}'
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct WriteOptions {
|
||||
pub:
|
||||
format bool
|
||||
overwrite bool
|
||||
document bool
|
||||
prefix string
|
||||
}
|
||||
38
lib/core/code/README.md
Normal file
38
lib/core/code/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Code Model
|
||||
|
||||
A set of models that represent code, such as structs and functions. The motivation behind this module is to provide a more generic, and lighter alternative to v.ast code models, that can be used for code parsing and code generation across multiple languages.
|
||||
|
||||
## Using Codemodel
|
||||
|
||||
While the models in this module can be used in any domain, the models here are used extensively in the modules [codeparser](../codeparser/) and codegen (under development). Below are examples on how codemodel can be used for parsing and generating code.
|
||||
## Code parsing with codemodel
|
||||
|
||||
As shown in the example below, the codemodels returned by the parser can be used to infer information about the code written
|
||||
|
||||
```js
|
||||
code := codeparser.parse("somedir") // code is a list of code models
|
||||
|
||||
num_functions := code.filter(it is Function).len
|
||||
structs := code.filter(it is Struct)
|
||||
println("This directory has ${num_functions} functions")
|
||||
println('The directory has the structs: ${structs.map(it.name)}')
|
||||
|
||||
```
|
||||
|
||||
or can be used as intermediate structures to serialize code into some other format:
|
||||
|
||||
```js
|
||||
code_md := ''
|
||||
|
||||
// describes the struct in markdown format
|
||||
for struct in structs {
|
||||
code_md += '# ${struct.name}'
|
||||
code_md += 'Type: ${struct.typ.symbol()}'
|
||||
code_md += '## Fields:'
|
||||
for field in struct.fields {
|
||||
code_md += '- ${field.name}'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The [openrpc/docgen](../openrpc/docgen/) module demonstrates a good use case, where codemodels are serialized into JSON schema's, to generate an OpenRPC description document from a client in v.
|
||||
BIN
lib/core/code/code.dylib
Executable file
BIN
lib/core/code/code.dylib
Executable file
Binary file not shown.
32
lib/core/code/model.v
Normal file
32
lib/core/code/model.v
Normal file
@@ -0,0 +1,32 @@
|
||||
module code
|
||||
|
||||
// Code is a list of statements
|
||||
// pub type Code = []CodeItem
|
||||
|
||||
pub type CodeItem = Alias | Comment | CustomCode | Function | Import | Struct | Sumtype | Interface
|
||||
|
||||
// item for adding custom code in
|
||||
pub struct CustomCode {
|
||||
pub:
|
||||
text string
|
||||
}
|
||||
|
||||
pub struct Comment {
|
||||
pub:
|
||||
text string
|
||||
is_multi bool
|
||||
}
|
||||
|
||||
pub struct Sumtype {
|
||||
pub:
|
||||
name string
|
||||
description string
|
||||
types []Type
|
||||
}
|
||||
|
||||
pub struct Attribute {
|
||||
pub:
|
||||
name string // [name]
|
||||
has_arg bool
|
||||
arg string // [name: arg]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module codemodel
|
||||
module code
|
||||
|
||||
pub struct Const {
|
||||
name string
|
||||
@@ -11,7 +11,7 @@ pub fn parse_const(code_ string) !Const {
|
||||
return error('code <${code_}> is not of const')
|
||||
}
|
||||
return Const{
|
||||
name: code.split('=')[0].trim_space()
|
||||
name: code.split('=')[0].trim_space()
|
||||
value: code.split('=')[1].trim_space()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module codemodel
|
||||
module code
|
||||
|
||||
pub struct Example {
|
||||
function Function
|
||||
@@ -1,10 +1,37 @@
|
||||
module codemodel
|
||||
module code
|
||||
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
pub struct CodeFile {
|
||||
pub interface IFile {
|
||||
write(string, WriteOptions) !
|
||||
name string
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
pub mut:
|
||||
name string
|
||||
extension string
|
||||
content string
|
||||
}
|
||||
|
||||
pub fn (f File) write(path string, params WriteOptions) ! {
|
||||
mut fd_file := pathlib.get_file(path: '${path}/${f.name}.${f.extension}')!
|
||||
fd_file.write(f.content)!
|
||||
if f.extension == 'ts' {
|
||||
return f.typescript(path, params)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (f File) typescript(path string, params WriteOptions) ! {
|
||||
if params.format {
|
||||
os.execute('npx prettier --write ${path}')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct VFile {
|
||||
pub mut:
|
||||
name string
|
||||
mod string
|
||||
@@ -14,15 +41,15 @@ pub mut:
|
||||
content string
|
||||
}
|
||||
|
||||
pub fn new_file(config CodeFile) CodeFile {
|
||||
return CodeFile{
|
||||
pub fn new_file(config VFile) VFile {
|
||||
return VFile{
|
||||
...config
|
||||
mod: texttools.name_fix(config.mod)
|
||||
mod: texttools.name_fix(config.mod)
|
||||
items: config.items
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut file CodeFile) add_import(import_ Import) ! {
|
||||
pub fn (mut file VFile) add_import(import_ Import) ! {
|
||||
for mut i in file.imports {
|
||||
if i.mod == import_.mod {
|
||||
i.add_types(import_.types)
|
||||
@@ -32,7 +59,7 @@ pub fn (mut file CodeFile) add_import(import_ Import) ! {
|
||||
file.imports << import_
|
||||
}
|
||||
|
||||
pub fn (code CodeFile) write_v(path string, options WriteOptions) ! {
|
||||
pub fn (code VFile) write(path string, options WriteOptions) ! {
|
||||
filename := '${options.prefix}${texttools.name_fix(code.name)}.v'
|
||||
mut filepath := pathlib.get('${path}/${filename}')
|
||||
|
||||
@@ -58,16 +85,21 @@ pub fn (code CodeFile) write_v(path string, options WriteOptions) ! {
|
||||
}
|
||||
|
||||
mut file := pathlib.get_file(
|
||||
path: filepath.path
|
||||
path: filepath.path
|
||||
create: true
|
||||
)!
|
||||
file.write('module ${code.mod}\n${imports_str}\n${consts_str}\n${code_str}')!
|
||||
|
||||
mod_stmt := if code.mod == '' {''} else {
|
||||
'module ${code.mod}'
|
||||
}
|
||||
|
||||
file.write('${mod_stmt}\n${imports_str}\n${consts_str}${code_str}')!
|
||||
if options.format {
|
||||
os.execute('v fmt -w ${file.path}')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (file CodeFile) get_function(name string) ?Function {
|
||||
pub fn (file VFile) get_function(name string) ?Function {
|
||||
functions := file.items.filter(it is Function).map(it as Function)
|
||||
target_lst := functions.filter(it.name == name)
|
||||
|
||||
@@ -80,7 +112,7 @@ pub fn (file CodeFile) get_function(name string) ?Function {
|
||||
return target_lst[0]
|
||||
}
|
||||
|
||||
pub fn (mut file CodeFile) set_function(function Function) ! {
|
||||
pub fn (mut file VFile) set_function(function Function) ! {
|
||||
function_names := file.items.map(if it is Function { it.name } else { '' })
|
||||
|
||||
index := function_names.index(function.name)
|
||||
@@ -90,10 +122,10 @@ pub fn (mut file CodeFile) set_function(function Function) ! {
|
||||
file.items[index] = function
|
||||
}
|
||||
|
||||
pub fn (file CodeFile) functions() []Function {
|
||||
pub fn (file VFile) functions() []Function {
|
||||
return file.items.filter(it is Function).map(it as Function)
|
||||
}
|
||||
|
||||
pub fn (file CodeFile) structs() []Struct {
|
||||
pub fn (file VFile) structs() []Struct {
|
||||
return file.items.filter(it is Struct).map(it as Struct)
|
||||
}
|
||||
39
lib/core/code/model_folder.v
Normal file
39
lib/core/code/model_folder.v
Normal file
@@ -0,0 +1,39 @@
|
||||
module code
|
||||
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
|
||||
pub interface IFolder {
|
||||
name string
|
||||
files []IFile
|
||||
modules []Module
|
||||
write(string, WriteOptions) !
|
||||
}
|
||||
|
||||
pub struct Folder {
|
||||
pub:
|
||||
name string
|
||||
files []IFile
|
||||
folders []IFolder
|
||||
modules []Module
|
||||
}
|
||||
|
||||
pub fn (f Folder) write(path string, options WriteOptions) ! {
|
||||
mut dir := pathlib.get_dir(
|
||||
path: '${path}/${f.name}'
|
||||
empty: options.overwrite
|
||||
)!
|
||||
|
||||
if !options.overwrite && dir.exists() {
|
||||
return
|
||||
}
|
||||
|
||||
for file in f.files {
|
||||
file.write(dir.path, options)!
|
||||
}
|
||||
for folder in f.folders {
|
||||
folder.write(dir.path, options)!
|
||||
}
|
||||
for mod in f.modules {
|
||||
mod.write(dir.path, options)!
|
||||
}
|
||||
}
|
||||
124
lib/core/code/model_function.v
Normal file
124
lib/core/code/model_function.v
Normal file
@@ -0,0 +1,124 @@
|
||||
module code
|
||||
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
pub struct Function {
|
||||
pub:
|
||||
name string @[omitempty]
|
||||
receiver Param @[omitempty]
|
||||
is_pub bool @[omitempty]
|
||||
mod string @[omitempty]
|
||||
pub mut:
|
||||
summary string @[omitempty]
|
||||
description string @[omitempty]
|
||||
params []Param @[omitempty]
|
||||
body string @[omitempty]
|
||||
result Param @[omitempty]
|
||||
has_return bool @[omitempty]
|
||||
}
|
||||
|
||||
|
||||
// vgen_function generates a function statement for a function
|
||||
pub fn (function Function) vgen(options WriteOptions) string {
|
||||
mut params_ := function.params.clone()
|
||||
optionals := function.params.filter(it.is_optional)
|
||||
options_struct := Struct{
|
||||
name: '${texttools.pascal_case(function.name)}Options'
|
||||
attrs: [Attribute{
|
||||
name: 'params'
|
||||
}]
|
||||
fields: optionals.map(StructField{
|
||||
name: it.name
|
||||
description: it.description
|
||||
typ: it.typ
|
||||
})
|
||||
}
|
||||
if optionals.len > 0 {
|
||||
params_ << Param{
|
||||
name: 'options'
|
||||
typ: type_from_symbol(options_struct.name)
|
||||
}
|
||||
}
|
||||
|
||||
params := params_.filter(!it.is_optional).map(it.vgen()).join(', ')
|
||||
|
||||
receiver_ := Param{
|
||||
...function.receiver,
|
||||
typ: if function.receiver.typ is Result {
|
||||
function.receiver.typ.typ
|
||||
} else {function.receiver.typ}
|
||||
|
||||
}
|
||||
receiver := if receiver_.vgen().trim_space() != '' {
|
||||
'(${receiver_.vgen()})'
|
||||
} else {''}
|
||||
|
||||
name := texttools.name_fix(function.name)
|
||||
result := function.result.typ.vgen()
|
||||
|
||||
mut function_str := $tmpl('templates/function/function.v.template')
|
||||
|
||||
// if options.format {
|
||||
// result := os.execute_opt('echo "${function_str.replace('$', '\\$')}" | v fmt') or {
|
||||
// panic('${function_str}\n${err}')
|
||||
// }
|
||||
// function_str = result.output
|
||||
// }
|
||||
function_str = function_str.split_into_lines().filter(!it.starts_with('import ')).join('\n')
|
||||
|
||||
return if options_struct.fields.len != 0 {
|
||||
'${options_struct.vgen()}\n${function_str}'
|
||||
} else {
|
||||
function_str
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_function(code string) !Function {
|
||||
// TODO: implement function from file line
|
||||
return parse_function(code)!
|
||||
}
|
||||
|
||||
pub fn parse_function(code_ string) !Function {
|
||||
mut code := code_.trim_space()
|
||||
is_pub := code.starts_with('pub ')
|
||||
if is_pub {
|
||||
code = code.trim_string_left('pub ').trim_space()
|
||||
}
|
||||
|
||||
is_fn := code.starts_with('fn ')
|
||||
if !is_fn {
|
||||
return error('invalid function format')
|
||||
}
|
||||
code = code.trim_string_left('fn ').trim_space()
|
||||
|
||||
receiver := if code.starts_with('(') {
|
||||
param_str := code.all_after('(').all_before(')').trim_space()
|
||||
code = code.all_after(')').trim_space()
|
||||
parse_param(param_str)!
|
||||
} else {
|
||||
Param{}
|
||||
}
|
||||
|
||||
name := code.all_before('(').trim_space()
|
||||
code = code.trim_string_left(name).trim_space()
|
||||
|
||||
params_str := code.all_after('(').all_before(')')
|
||||
params := if params_str.trim_space() != '' {
|
||||
params_str_lst := params_str.split(',')
|
||||
params_str_lst.map(parse_param(it)!)
|
||||
} else {
|
||||
[]Param{}
|
||||
}
|
||||
result := new_param(
|
||||
v: code.all_after(')').all_before('{').replace(' ', '')
|
||||
)!
|
||||
|
||||
body := if code.contains('{') { code.all_after('{').all_before_last('}') } else { '' }
|
||||
return Function{
|
||||
name: name
|
||||
receiver: receiver
|
||||
params: params
|
||||
result: result
|
||||
body: body
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module codemodel
|
||||
module code
|
||||
|
||||
pub struct Import {
|
||||
pub mut:
|
||||
@@ -14,7 +14,7 @@ pub fn parse_import(code_ string) Import {
|
||||
code := code_.trim_space().trim_string_left('import').trim_space()
|
||||
types_str := if code.contains(' ') { code.all_after(' ').trim('{}') } else { '' }
|
||||
return Import{
|
||||
mod: code.all_before(' ')
|
||||
mod: code.all_before(' ')
|
||||
types: if types_str != '' {
|
||||
types_str.split(',').map(it.trim_space())
|
||||
} else {
|
||||
83
lib/core/code/model_module.v
Normal file
83
lib/core/code/model_module.v
Normal file
@@ -0,0 +1,83 @@
|
||||
module code
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import os
|
||||
import log
|
||||
|
||||
pub struct Module {
|
||||
pub mut:
|
||||
name string
|
||||
description string
|
||||
version string = '0.0.1'
|
||||
license string = 'apache2'
|
||||
vcs string = 'git'
|
||||
files []IFile
|
||||
folders []IFolder
|
||||
modules []Module
|
||||
in_src bool // whether mod will be generated in src folder
|
||||
}
|
||||
|
||||
pub fn new_module(mod Module) Module {
|
||||
return Module {
|
||||
...mod
|
||||
files: mod.files.map(
|
||||
if it is VFile {
|
||||
IFile(VFile{...it, mod: mod.name})
|
||||
} else {it}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mod Module) write(path string, options WriteOptions) ! {
|
||||
mut module_dir := pathlib.get_dir(
|
||||
path: if mod.in_src { '${path}/src' } else { '${path}/${mod.name}' }
|
||||
empty: options.overwrite
|
||||
)!
|
||||
console.print_debug("write ${module_dir.path}")
|
||||
// pre:="v -n -w -enable-globals"
|
||||
pre:="v -n -w -gc none -cc tcc -d use_openssl -enable-globals run"
|
||||
|
||||
if !options.overwrite && module_dir.exists() {
|
||||
return
|
||||
}
|
||||
|
||||
for file in mod.files {
|
||||
console.print_debug("mod file write ${file.name}")
|
||||
file.write(module_dir.path, options)!
|
||||
}
|
||||
|
||||
for folder in mod.folders {
|
||||
console.print_debug("mod folder write ${folder.name}")
|
||||
folder.write('${path}/${mod.name}', options)!
|
||||
}
|
||||
|
||||
for mod_ in mod.modules {
|
||||
console.print_debug("mod write ${mod_.name}")
|
||||
mod_.write('${path}/${mod.name}', options)!
|
||||
}
|
||||
|
||||
if options.format {
|
||||
console.print_debug("format ${module_dir.path}")
|
||||
os.execute('v fmt -w ${module_dir.path}')
|
||||
}
|
||||
if options.compile {
|
||||
console.print_debug("compile shared ${module_dir.path}")
|
||||
os.execute_opt('${pre} -shared ${module_dir.path}') or {
|
||||
log.fatal(err.msg())
|
||||
}
|
||||
}
|
||||
if options.test {
|
||||
console.print_debug("test ${module_dir.path}")
|
||||
os.execute_opt('${pre} test ${module_dir.path}') or {
|
||||
log.fatal(err.msg())
|
||||
}
|
||||
}
|
||||
if options.document {
|
||||
docs_path := '${path}/${mod.name}/docs'
|
||||
console.print_debug("document ${module_dir.path}")
|
||||
os.execute('v doc -f html -o ${docs_path} ${module_dir.path}')
|
||||
}
|
||||
|
||||
mut mod_file := pathlib.get_file(path: '${module_dir.path}/v.mod')!
|
||||
mod_file.write($tmpl('templates/v.mod.template'))!
|
||||
}
|
||||
74
lib/core/code/model_param.v
Normal file
74
lib/core/code/model_param.v
Normal file
@@ -0,0 +1,74 @@
|
||||
module code
|
||||
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
pub struct Param {
|
||||
pub mut:
|
||||
required bool @[omitempty]
|
||||
mutable bool @[omitempty]
|
||||
is_shared bool @[omitempty]
|
||||
is_optional bool @[omitempty]
|
||||
is_result bool @[omitempty]
|
||||
description string @[omitempty]
|
||||
name string @[omitempty]
|
||||
typ Type @[omitempty]
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct Params{
|
||||
pub:
|
||||
v string
|
||||
}
|
||||
|
||||
pub fn new_param(params Params) !Param {
|
||||
// TODO: implement function from file line
|
||||
return parse_param(params.v)!
|
||||
}
|
||||
|
||||
pub fn (param Param) vgen() string {
|
||||
sym := param.typ.symbol()
|
||||
param_name := texttools.snake_case(param.name)
|
||||
mut vstr := '${param_name} ${sym}'
|
||||
if param.mutable {
|
||||
vstr = 'mut ${vstr}'
|
||||
}
|
||||
return '${vstr}'
|
||||
}
|
||||
|
||||
pub fn (p Param) typescript() string {
|
||||
name := texttools.camel_case(p.name)
|
||||
suffix := if p.is_optional {'?'} else {''}
|
||||
return '${name}${suffix}: ${p.typ.typescript()}'
|
||||
}
|
||||
|
||||
pub fn parse_param(code_ string) !Param {
|
||||
mut code := code_.trim_space()
|
||||
|
||||
if code == '!' {
|
||||
return Param{is_result: true}
|
||||
} else if code == '?' {
|
||||
return Param{is_optional: true}
|
||||
}
|
||||
|
||||
is_mut := code.starts_with('mut ')
|
||||
if is_mut {
|
||||
code = code.trim_string_left('mut ').trim_space()
|
||||
}
|
||||
split := code.split(' ').filter(it != '')
|
||||
|
||||
if split.len == 1 {
|
||||
// means anonymous param
|
||||
return Param{
|
||||
typ: type_from_symbol(split[0])
|
||||
mutable: is_mut
|
||||
}
|
||||
}
|
||||
if split.len != 2 {
|
||||
return error('invalid param format: ${code_}')
|
||||
}
|
||||
return Param{
|
||||
name: split[0]
|
||||
typ: type_from_symbol(split[1])
|
||||
mutable: is_mut
|
||||
}
|
||||
}
|
||||
136
lib/core/code/model_struct.v
Normal file
136
lib/core/code/model_struct.v
Normal file
@@ -0,0 +1,136 @@
|
||||
module code
|
||||
|
||||
import log
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
pub struct Struct {
|
||||
pub mut:
|
||||
name string
|
||||
description string
|
||||
mod string
|
||||
is_pub bool
|
||||
embeds []Struct @[str: skip]
|
||||
generics map[string]string @[str: skip]
|
||||
attrs []Attribute
|
||||
fields []StructField
|
||||
}
|
||||
|
||||
// vgen_function generates a function statement for a function
|
||||
pub fn (struct_ Struct) vgen() string {
|
||||
name_ := if struct_.generics.len > 0 {
|
||||
'${struct_.name}${vgen_generics(struct_.generics)}'
|
||||
} else {
|
||||
struct_.name
|
||||
}
|
||||
name := texttools.pascal_case(name_)
|
||||
|
||||
prefix := if struct_.is_pub {
|
||||
'pub '
|
||||
} else {
|
||||
''
|
||||
}
|
||||
|
||||
comments := if struct_.description.trim_space() != '' {
|
||||
'// ${struct_.description.trim_space()}'
|
||||
} else {''}
|
||||
|
||||
priv_fields := struct_.fields.filter(!it.is_mut && !it.is_pub).map(it.vgen())
|
||||
pub_fields := struct_.fields.filter(!it.is_mut && it.is_pub).map(it.vgen())
|
||||
mut_fields := struct_.fields.filter(it.is_mut && !it.is_pub).map(it.vgen())
|
||||
pub_mut_fields := struct_.fields.filter(it.is_mut && it.is_pub).map(it.vgen())
|
||||
|
||||
mut struct_str := $tmpl('templates/struct/struct.v.template')
|
||||
if false {
|
||||
result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {
|
||||
log.debug(struct_str)
|
||||
panic(err)
|
||||
}
|
||||
return result.output
|
||||
}
|
||||
return struct_str
|
||||
}
|
||||
|
||||
|
||||
pub struct Interface {
|
||||
pub mut:
|
||||
name string
|
||||
description string
|
||||
is_pub bool
|
||||
embeds []Interface @[str: skip]
|
||||
attrs []Attribute
|
||||
fields []StructField
|
||||
methods []Function
|
||||
}
|
||||
|
||||
pub fn (iface Interface) vgen() string {
|
||||
name := texttools.pascal_case(iface.name)
|
||||
|
||||
prefix := if iface.is_pub {
|
||||
'pub'
|
||||
} else {
|
||||
''
|
||||
}
|
||||
|
||||
mut fields := iface.fields.filter(!it.is_mut).map(it.vgen())
|
||||
mut mut_fields := iface.fields.filter(it.is_mut).map(it.vgen())
|
||||
|
||||
fields << iface.methods.filter(!it.receiver.mutable).map(function_to_interface_field(it))
|
||||
mut_fields << iface.methods.filter(it.receiver.mutable).map(function_to_interface_field(it))
|
||||
|
||||
mut iface_str := $tmpl('templates/interface/interface.v.template')
|
||||
if false {
|
||||
result := os.execute_opt('echo "${iface_str.replace('$', '\$')}" | v fmt') or {
|
||||
log.debug(iface_str)
|
||||
panic(err)
|
||||
}
|
||||
return result.output
|
||||
}
|
||||
return iface_str
|
||||
}
|
||||
|
||||
pub fn function_to_interface_field(f Function) string {
|
||||
param_types := f.params.map(it.typ.vgen()).join(', ')
|
||||
return '${f.name}(${param_types}) ${f.result.typ.vgen()}'
|
||||
}
|
||||
|
||||
pub struct StructField {
|
||||
Param
|
||||
pub mut:
|
||||
comments []Comment
|
||||
attrs []Attribute
|
||||
description string
|
||||
default string
|
||||
is_pub bool
|
||||
is_mut bool
|
||||
is_ref bool
|
||||
anon_struct Struct @[str: skip] // sometimes fields may hold anonymous structs
|
||||
structure Struct @[str: skip]
|
||||
}
|
||||
|
||||
pub fn (field StructField) vgen() string {
|
||||
mut vstr := field.Param.vgen()
|
||||
if field.description != '' {
|
||||
vstr += '// ${field.description}'
|
||||
}
|
||||
return vstr
|
||||
}
|
||||
|
||||
pub fn (structure Struct) get_type_symbol() string {
|
||||
mut symbol := if structure.mod != '' {
|
||||
'${structure.mod.all_after_last('.')}.${structure.name}'
|
||||
} else {
|
||||
structure.name
|
||||
}
|
||||
if structure.generics.len > 0 {
|
||||
symbol = '${symbol}${vgen_generics(structure.generics)}'
|
||||
}
|
||||
|
||||
return symbol
|
||||
}
|
||||
|
||||
pub fn (s Struct) typescript() string {
|
||||
name := texttools.pascal_case(s.name)
|
||||
fields := s.fields.map(it.typescript()).join_lines()
|
||||
return 'export interface ${name} {\n${fields}\n}'
|
||||
}
|
||||
165
lib/core/code/model_types.v
Normal file
165
lib/core/code/model_types.v
Normal file
@@ -0,0 +1,165 @@
|
||||
module code
|
||||
|
||||
struct Float {
|
||||
bytes int
|
||||
}
|
||||
|
||||
// Integer types
|
||||
pub const type_i8 = Integer{
|
||||
bytes: 8
|
||||
}
|
||||
|
||||
pub const type_u8 = Integer{
|
||||
bytes: 8
|
||||
signed: false
|
||||
}
|
||||
|
||||
pub const type_i16 = Integer{
|
||||
bytes: 16
|
||||
}
|
||||
|
||||
pub const type_u16 = Integer{
|
||||
bytes: 16
|
||||
signed: false
|
||||
}
|
||||
|
||||
pub const type_i32 = Integer{
|
||||
bytes: 32
|
||||
}
|
||||
|
||||
pub const type_u32 = Integer{
|
||||
bytes: 32
|
||||
signed: false
|
||||
}
|
||||
|
||||
pub const type_i64 = Integer{
|
||||
bytes: 64
|
||||
}
|
||||
|
||||
pub const type_u64 = Integer{
|
||||
bytes: 64
|
||||
signed: false
|
||||
}
|
||||
|
||||
// Floating-point types
|
||||
pub const type_f32 = Float{
|
||||
bytes: 32
|
||||
}
|
||||
|
||||
pub const type_f64 = Float{
|
||||
bytes: 64
|
||||
}
|
||||
|
||||
pub type Type = Void | Map | Array | Object | Result | Integer | Alias | String | Boolean | Function
|
||||
|
||||
pub struct Alias {
|
||||
pub:
|
||||
name string
|
||||
description string
|
||||
typ Type
|
||||
}
|
||||
|
||||
pub struct Boolean{}
|
||||
|
||||
pub struct Void{}
|
||||
|
||||
pub struct Integer {
|
||||
bytes u8
|
||||
signed bool = true
|
||||
}
|
||||
|
||||
pub fn type_from_symbol(symbol_ string) Type {
|
||||
mut symbol := symbol_.trim_space()
|
||||
if symbol.starts_with('[]') {
|
||||
return Array{type_from_symbol(symbol.all_after('[]'))}
|
||||
} else if symbol == 'int' {
|
||||
return Integer{}
|
||||
} else if symbol == 'string' {
|
||||
return String{}
|
||||
} else if symbol == 'bool' || symbol == 'boolean' {
|
||||
return Boolean{}
|
||||
}
|
||||
return Object{symbol}
|
||||
}
|
||||
|
||||
pub fn (t Type) symbol() string {
|
||||
return match t {
|
||||
Array { '[]${t.typ.symbol()}' }
|
||||
Object { t.name }
|
||||
Result { '!${t.typ.symbol()}'}
|
||||
Integer {
|
||||
mut str := ''
|
||||
if !t.signed {
|
||||
str += 'u'
|
||||
}
|
||||
if t.bytes != 0 {
|
||||
'${str}${t.bytes}'
|
||||
} else {
|
||||
'${str}int'
|
||||
}
|
||||
}
|
||||
Alias {t.name}
|
||||
String {'string'}
|
||||
Boolean {'bool'}
|
||||
Map{'map[string]${t.typ.symbol()}'}
|
||||
Function{'fn ()'}
|
||||
Void {''}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct String {}
|
||||
|
||||
pub struct Array {
|
||||
pub:
|
||||
typ Type
|
||||
}
|
||||
|
||||
pub struct Map {
|
||||
pub:
|
||||
typ Type
|
||||
}
|
||||
|
||||
pub struct Object {
|
||||
pub:
|
||||
name string
|
||||
}
|
||||
|
||||
pub struct Result {
|
||||
pub:
|
||||
typ Type
|
||||
}
|
||||
|
||||
pub fn (t Type) typescript() string {
|
||||
return match t {
|
||||
Map {'Record<string, ${t.typ.typescript()}>'}
|
||||
Array { '${t.typ.typescript()}[]' }
|
||||
Object { t.name }
|
||||
Result { '${t.typ.typescript()}'}
|
||||
Boolean { 'boolean'}
|
||||
Integer { 'number' }
|
||||
Alias {t.name}
|
||||
String {'string'}
|
||||
Function {'func'}
|
||||
Void {''}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: enfore that cant be both mutable and shared
|
||||
pub fn (t Type) vgen() string {
|
||||
return t.symbol()
|
||||
}
|
||||
|
||||
pub fn (t Type) empty_value() string {
|
||||
return match t {
|
||||
Map {'{}'}
|
||||
Array { '[]${t.typ.symbol()}{}' }
|
||||
Object { if t.name != '' {'${t.name}{}'} else {''} }
|
||||
Result { t.typ.empty_value() }
|
||||
Boolean { 'false' }
|
||||
Integer { '0' }
|
||||
Alias {''}
|
||||
String {"''"}
|
||||
Function {''}
|
||||
Void {''}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@if function.description != ''
|
||||
// @{function.description}
|
||||
@endif
|
||||
pub fn @receiver @{function.name}(@{params}) @{function.result.vgen()} {
|
||||
pub fn @receiver @{name}(@{params}) @{result} {
|
||||
@{function.body.trim_space().replace('\t', '')}
|
||||
}
|
||||
15
lib/core/code/templates/interface/interface.v.template
Normal file
15
lib/core/code/templates/interface/interface.v.template
Normal file
@@ -0,0 +1,15 @@
|
||||
// @{iface.description}
|
||||
@if iface.attrs.len > 0
|
||||
[
|
||||
@for attr in iface.attrs
|
||||
@{attr.name}
|
||||
@end
|
||||
]
|
||||
@end
|
||||
@{prefix} interface @{name} {
|
||||
@{fields.join_lines()}
|
||||
@if mut_fields.len > 0
|
||||
mut:
|
||||
@{mut_fields.join_lines()}
|
||||
@end
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@{struct_.description}
|
||||
@{comments}
|
||||
@if struct_.attrs.len > 0
|
||||
[
|
||||
@for attr in struct_.attrs
|
||||
@@ -6,7 +6,7 @@
|
||||
@end
|
||||
]
|
||||
@end
|
||||
@{prefix} struct @{name} {
|
||||
@{prefix}struct @{name} {
|
||||
@for embed in struct_.embeds
|
||||
@{embed.get_type_symbol()}
|
||||
@end
|
||||
7
lib/core/code/templates/v.mod.template
Normal file
7
lib/core/code/templates/v.mod.template
Normal file
@@ -0,0 +1,7 @@
|
||||
Module {
|
||||
name: '@{mod.name}'
|
||||
description: '@{mod.description}'
|
||||
version: '@{mod.version}'
|
||||
vcs: '@{mod.vcs}'
|
||||
license: '@{mod.license}'
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module codemodel
|
||||
module code
|
||||
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import log
|
||||
import rand
|
||||
|
||||
pub struct GetStruct {
|
||||
@@ -34,10 +34,10 @@ pub fn inflate_types(mut code []CodeItem) {
|
||||
pub fn inflate_struct_fields(code []CodeItem, mut struct_ CodeItem) {
|
||||
for mut field in (struct_ as Struct).fields {
|
||||
// TODO: fix inflation for imported types
|
||||
if field.typ.symbol.starts_with_capital() {
|
||||
if field.typ.symbol().starts_with_capital() {
|
||||
field.structure = get_struct(
|
||||
code: code
|
||||
name: field.typ.symbol
|
||||
name: field.typ.symbol()
|
||||
) or { continue }
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ pub:
|
||||
|
||||
pub fn (func Function) generate_call(params GenerateCallParams) !string {
|
||||
mut call := ''
|
||||
if func.result.typ.symbol != '' {
|
||||
if func.result.typ.symbol() != '' {
|
||||
call = 'result := '
|
||||
}
|
||||
call += if params.receiver != '' {
|
||||
@@ -68,7 +68,7 @@ pub fn (func Function) generate_call(params GenerateCallParams) !string {
|
||||
'()'
|
||||
}
|
||||
|
||||
if func.result.result {
|
||||
if func.result.is_result {
|
||||
call += '!'
|
||||
}
|
||||
return call
|
||||
@@ -79,14 +79,14 @@ pub struct GenerateValueParams {
|
||||
}
|
||||
|
||||
pub fn (param Param) generate_value() !string {
|
||||
if param.typ.symbol == 'string' {
|
||||
if param.typ.symbol() == 'string' {
|
||||
return "'mock_string_${rand.string(3)}'"
|
||||
} else if param.typ.symbol == 'int' || param.typ.symbol == 'u32' {
|
||||
} else if param.typ.symbol() == 'int' || param.typ.symbol() == 'u32' {
|
||||
return '42'
|
||||
} else if param.typ.symbol[0].is_capital() {
|
||||
return '${param.typ.symbol}{}'
|
||||
} else if param.typ.symbol()[0].is_capital() {
|
||||
return '${param.typ.symbol()}{}'
|
||||
} else {
|
||||
console.print_debug('mock values for types other than strings and ints are not yet supported')
|
||||
log.debug('mock values for types other than strings and ints are not yet supported')
|
||||
}
|
||||
return ''
|
||||
}
|
||||
70
lib/core/code/write.v
Normal file
70
lib/core/code/write.v
Normal file
@@ -0,0 +1,70 @@
|
||||
module code
|
||||
|
||||
pub struct WriteCode {
|
||||
destination string
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct WriteOptions {
|
||||
pub:
|
||||
format bool
|
||||
overwrite bool
|
||||
document bool
|
||||
prefix string
|
||||
compile bool // whether to compile the written code
|
||||
test bool // whether to test the written code
|
||||
}
|
||||
|
||||
|
||||
interface ICodeItem {
|
||||
vgen() string
|
||||
}
|
||||
|
||||
pub fn vgen(code []CodeItem) string {
|
||||
mut str := ''
|
||||
for item in code {
|
||||
if item is Function {
|
||||
str += '\n${item.vgen()}'
|
||||
}
|
||||
if item is Struct {
|
||||
str += '\n${item.vgen()}'
|
||||
}
|
||||
if item is Interface {
|
||||
str += '\n${item.vgen()}'
|
||||
}
|
||||
if item is CustomCode {
|
||||
str += '\n${item.vgen()}'
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// pub fn (code Code) vgen() string {
|
||||
// return code.items.map(it.vgen()).join_lines()
|
||||
// }
|
||||
|
||||
// vgen_import generates an import statement for a given type
|
||||
pub fn (import_ Import) vgen() string {
|
||||
types_str := if import_.types.len > 0 {
|
||||
'{${import_.types.join(', ')}}'
|
||||
} else {
|
||||
''
|
||||
} // comma separated string list of types
|
||||
return 'import ${import_.mod} ${types_str}'
|
||||
}
|
||||
|
||||
|
||||
pub fn vgen_generics(generics map[string]string) string {
|
||||
if generics.keys().len == 0 {
|
||||
return ''
|
||||
}
|
||||
mut vstr := '['
|
||||
for key, val in generics {
|
||||
vstr += if val != '' { val } else { key }
|
||||
}
|
||||
return '${vstr}]'
|
||||
}
|
||||
|
||||
pub fn (custom CustomCode) vgen() string {
|
||||
return custom.text
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { CustomCode, File, Folder, IFile, Module, VFile }
|
||||
import freeflowuniverse.herolib.core.code { CustomCode, File, Folder, IFile, Module, VFile }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.data.markdownparser
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.hero.baobab.specification
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { CodeItem, CustomCode, Import, VFile }
|
||||
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Import, VFile }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.data.markdownparser
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { CodeItem, CustomCode, Import, VFile }
|
||||
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Import, VFile }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.hero.baobab.specification { ActorMethod, ActorSpecification }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { CodeItem, CustomCode, VFile }
|
||||
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, VFile }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.data.markdownparser
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { CodeItem, CustomCode, VFile }
|
||||
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, VFile }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.data.markdownparser
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module generator
|
||||
|
||||
import json
|
||||
import freeflowuniverse.herolib.core.codemodel { File, Function, Struct, VFile }
|
||||
import freeflowuniverse.herolib.core.code { File, Function, Struct, VFile }
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.hero.baobab.specification
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { Function, Param, Result, Struct, Type }
|
||||
import freeflowuniverse.herolib.core.code { Function, Param, Result, Struct, Type }
|
||||
import freeflowuniverse.herolib.rpc.openrpc
|
||||
|
||||
const test_actor_specification = ActorSpecification{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.hero.baobab.specification { BaseObject }
|
||||
import freeflowuniverse.herolib.core.codemodel { CodeItem, Function, Import, Param, Result, Struct, StructField, Type, VFile }
|
||||
import freeflowuniverse.herolib.core.code { CodeItem, Function, Import, Param, Result, Struct, StructField, Type, VFile }
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import os
|
||||
@@ -14,7 +14,7 @@ const id_param = Param{
|
||||
}
|
||||
|
||||
pub fn generate_object_code(actor Struct, object BaseObject) VFile {
|
||||
obj_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
obj_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
mut items := []CodeItem{}
|
||||
@@ -51,7 +51,7 @@ pub fn generate_object_code(actor Struct, object BaseObject) VFile {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_get_method(actor Struct, object BaseObject) Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
get_method := Function{
|
||||
@@ -78,7 +78,7 @@ fn generate_get_method(actor Struct, object BaseObject) Function {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_set_method(actor Struct, object BaseObject) Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
param_getters := generate_param_getters(
|
||||
@@ -115,7 +115,7 @@ fn generate_set_method(actor Struct, object BaseObject) Function {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_delete_method(actor Struct, object BaseObject) Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
body := 'actor.backend.delete[${object_type}](id)!'
|
||||
@@ -140,7 +140,7 @@ fn generate_delete_method(actor Struct, object BaseObject) Function {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_new_method(actor Struct, object BaseObject) Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
param_getters := generate_param_getters(
|
||||
@@ -180,7 +180,7 @@ fn generate_new_method(actor Struct, object BaseObject) Function {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_list_result_struct(actor Struct, object BaseObject) Struct {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
return Struct{
|
||||
name: '${object_type}List'
|
||||
@@ -198,7 +198,7 @@ fn generate_list_result_struct(actor Struct, object BaseObject) Struct {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_list_method(actor Struct, object BaseObject) Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
list_struct := Struct{
|
||||
@@ -242,7 +242,7 @@ fn generate_list_method(actor Struct, object BaseObject) Function {
|
||||
}
|
||||
|
||||
fn generate_filter_params(actor Struct, object BaseObject) []Struct {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
return [
|
||||
@@ -272,7 +272,7 @@ fn generate_filter_params(actor Struct, object BaseObject) []Struct {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_filter_method(actor Struct, object BaseObject) Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
param_getters := generate_param_getters(
|
||||
@@ -312,7 +312,7 @@ fn generate_filter_method(actor Struct, object BaseObject) Function {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_object_methods(actor Struct, object BaseObject) []Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
mut funcs := []Function{}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import os
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { CustomCode, Function, Import, Struct, VFile }
|
||||
import freeflowuniverse.herolib.core.code { CustomCode, Function, Import, Struct, VFile }
|
||||
import freeflowuniverse.herolib.core.codeparser
|
||||
import freeflowuniverse.herolib.hero.baobab.specification { BaseObject }
|
||||
import rand
|
||||
@@ -25,7 +25,7 @@ pub fn generate_object_test_code(actor Struct, object BaseObject) !VFile {
|
||||
}
|
||||
|
||||
actor_name := texttools.name_fix(actor.name)
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
// TODO: support modules outside of hero
|
||||
|
||||
@@ -60,7 +60,7 @@ pub fn generate_object_test_code(actor Struct, object BaseObject) !VFile {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_new_method_test(actor Struct, object BaseObject) !Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
required_fields := object.structure.fields.filter(it.attrs.any(it.name == 'required'))
|
||||
@@ -88,7 +88,7 @@ fn generate_new_method_test(actor Struct, object BaseObject) !Function {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_get_method_test(actor Struct, object BaseObject) !Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
required_fields := object.structure.fields.filter(it.attrs.any(it.name == 'required'))
|
||||
@@ -114,7 +114,7 @@ fn generate_get_method_test(actor Struct, object BaseObject) !Function {
|
||||
|
||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||
fn generate_filter_test(actor Struct, object BaseObject) !Function {
|
||||
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
|
||||
object_name := texttools.snake_case(object.structure.name)
|
||||
object_type := object.structure.name
|
||||
|
||||
index_fields := object.structure.fields.filter(it.attrs.any(it.name == 'index'))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.core.codemodel { Function, Struct }
|
||||
import freeflowuniverse.herolib.core.code { Function, Struct }
|
||||
|
||||
pub struct ActorSpecification {
|
||||
pub mut:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.web.openapi { Components, Info, OpenAPI, Operation, Parameter, PathItem, ServerSpec }
|
||||
import freeflowuniverse.herolib.core.codemodel { Function, Param, Struct }
|
||||
import freeflowuniverse.herolib.core.code { Function, Param, Struct }
|
||||
import freeflowuniverse.herolib.data.jsonschema { SchemaRef }
|
||||
|
||||
// Helper function: Convert OpenAPI parameter to codemodel Param
|
||||
|
||||
Reference in New Issue
Block a user