first commit

This commit is contained in:
yangshangbo
2025-04-10 18:14:01 +08:00
commit 8b18de9693
26 changed files with 270858 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.v]
indent_style = tab
+8
View File
@@ -0,0 +1,8 @@
* text=auto eol=lf
*.bat eol=crlf
*.v linguist-language=V
*.vv linguist-language=V
*.vsh linguist-language=V
v.mod linguist-language=V
.vdocignore linguist-language=ignore
+24
View File
@@ -0,0 +1,24 @@
# Binaries for programs and plugins
main
v
*.exe
*.exe~
*.so
*.dylib
*.dll
# Ignore binary output folders
bin/
# Ignore common editor/system specific metadata
.DS_Store
.idea/
.vscode/
*.iml
# ENV
.env
# vweb and database
*.db
*.js
+24
View File
@@ -0,0 +1,24 @@
# Features
- [x] Hono App
- [x] `fn get`
- [x] `fn post`
- [x] `fn put`
- [x] `fn delete`
- [x] `fn patch`
- [x] `fn head`
- [x] `fn options`
- [x] `fn listen`
- [x] Request
- [x] `property url`
- [x] `property header`
- [x] `property query`
- [ ] `property header`
- [ ] `property data`
- [ ] `property IP`
- [x] Response
- [x] `fn text`
- [x] `fn json`
- [x] `fn html`
- [ ] Router
- [ ] Middleware
+160
View File
@@ -0,0 +1,160 @@
module hono
import net.urllib
import net.http
import regex
import time
type Handler = fn (req Request) http.Response
struct RequestHandler {
path string
handler Handler = unsafe { nil }
}
struct Router {
mut:
handlers struct {
mut:
get []RequestHandler
post []RequestHandler
put []RequestHandler
delete []RequestHandler
patch []RequestHandler
head []RequestHandler
options []RequestHandler
}
}
pub struct Hono {
mut:
server http.Server = http.Server{}
router Router = Router{}
}
pub fn (mut app Hono) get(path string, handler Handler) {
app.router.handlers.get << RequestHandler{path, handler}
}
pub fn (mut app Hono) post(path string, handler Handler) {
app.router.handlers.post << RequestHandler{path, handler}
}
pub fn (mut app Hono) put(path string, handler Handler) {
app.router.handlers.put << RequestHandler{path, handler}
}
pub fn (mut app Hono) delete(path string, handler Handler) {
app.router.handlers.delete << RequestHandler{path, handler}
}
pub fn (mut app Hono) patch(path string, handler Handler) {
app.router.handlers.patch << RequestHandler{path, handler}
}
pub fn (mut app Hono) head(path string, handler Handler) {
app.router.handlers.head << RequestHandler{path, handler}
}
pub fn (mut app Hono) options(path string, handler Handler) {
app.router.handlers.options << RequestHandler{path, handler}
}
struct ServerHanler {
app Hono
}
fn ServerHanler.new(app Hono) ServerHanler {
return ServerHanler{
app: app
}
}
fn match_path_with_regex(real_path string, reg_path string) (bool, regex.RE) {
start := time.now()
mut one_more_star_reg := regex.regex_opt(r'\*{2,}') or { panic(err) }
repl_on_e_more_star_fn := fn (re regex.RE, in_txt string, start int, end int) string {
if in_txt.ends_with('*') {
return r'(\w+/){0,}\w+'
}
return r'(\w+//){0,}\w+'
}
mut replaced_path := one_more_star_reg.replace_by_fn(reg_path, repl_on_e_more_star_fn)
replaced_path = replaced_path.replace('*', r'\w+')
mut pamam_reg := regex.regex_opt(r':\w+') or { panic(err) }
repl_fn := fn (re regex.RE, in_txt string, start int, end int) string {
match_str := in_txt[start..end]
return '(?P<${match_str[1..]}>\\w+)'
}
replaced_path = pamam_reg.replace_by_fn(replaced_path, repl_fn)
replaced_path = replaced_path + '$'
mut reg := regex.regex_opt(replaced_path) or { panic(err) }
end := time.now()
println('Regex compile time: ${end - start}')
return reg.matches_string(real_path), reg
}
fn (s ServerHanler) handle(req http.Request) http.Response {
mut res := http.Response{
status_code: 404
body: 'Not Found'
}
handlers := match req.method {
.get {
s.app.router.handlers.get
}
.post {
s.app.router.handlers.post
}
.put {
s.app.router.handlers.put
}
.delete {
s.app.router.handlers.delete
}
.patch {
s.app.router.handlers.patch
}
.head {
s.app.router.handlers.head
}
.options {
s.app.router.handlers.options
}
else {
[]
}
}
for handler in handlers {
url := urllib.parse(req.url) or {
urllib.URL{
path: '/'
}
}
match_result, mut replaced_path_reg := match_path_with_regex(url.path, handler.path)
if match_result {
mut param_map := map[string]string{}
mut pamam_reg := regex.regex_opt(r':\w+') or { panic(err) }
all_params := pamam_reg.find_all_str(handler.path)
for param in all_params {
param_name := param[1..]
group := replaced_path_reg.get_group_by_name(url.path, param_name)
param_map[param_name] = group
}
query_map := url.query().to_map()
res = handler.handler(Request{
url: url.path
param: param_map
query: query_map
})
break
}
}
return res
}
pub fn (mut app Hono) listen(port string) {
app.server.addr = port
app.server.handler = ServerHanler.new(app)
app.server.listen_and_serve()
}
+12
View File
@@ -0,0 +1,12 @@
module hono
import net.http
pub struct Request {
pub:
url string
header http.Header
query map[string][]string
param map[string]string
data map[string]string
}
+39
View File
@@ -0,0 +1,39 @@
module hono
import net.http
pub struct Response {
}
pub fn Response.json(body string) http.Response {
return http.Response{
header: http.new_header(http.HeaderConfig{
key: http.CommonHeader.content_type
value: 'application/json'
})
status_code: 200
body: body
}
}
pub fn Response.text(body string) http.Response {
return http.Response{
header: http.new_header(http.HeaderConfig{
key: http.CommonHeader.content_type
value: 'plain/text'
})
status_code: 200
body: body
}
}
pub fn Response.html(body string) http.Response {
return http.Response{
header: http.new_header(http.HeaderConfig{
key: http.CommonHeader.content_type
value: 'text/html'
})
status_code: 200
body: body
}
}
+13
View File
@@ -0,0 +1,13 @@
module hono
fn init() {
is_match, _ := match_path_with_regex('/api/v1/user', '/api/v1/:user_id')
assert is_match == true
is_match2, reg2 := match_path_with_regex('/api/v1/abc/user', '/api/**/user')
println('reg2: ${reg2.query}')
assert is_match2 == true
is_match3, reg := match_path_with_regex('/users/123/posts/456/comments', '/users/**')
println('reg: ${reg.query}')
assert is_match3 == true
println('all tests passed')
}
+78
View File
@@ -0,0 +1,78 @@
module pathlib
import regex
import os { File }
pub struct Path {
pub:
path string = os.abs_path ('.')
}
pub fn Path.pwd() Path {
pwd := os.abs_path ('./')
return Path {
path: pwd
}
}
pub fn (p Path) exists() bool {
return os.exists(p.path)
}
pub fn (p Path) absolute() Path {
return Path{
path: os.abs_path(p.path)
}
}
pub fn (p Path) is_dir() bool {
return os.is_dir(p.path)
}
pub fn (p Path) is_file() bool {
return os.is_file(p.path)
}
pub fn (p Path) walk() []Path {
entries := os.ls(p.path) or { [] }
mut result := [] Path {}
if !p.is_dir() {
return result
}
for entry in entries {
entry_path := Path{
path:entry
}
current_path := p / entry_path
result << current_path
if current_path.is_dir(){
result << current_path.walk()
}
}
return result
}
pub fn (p Path) str() string {
mut p_path := p.path
mut re := regex.regex_opt(r'^[/\\]+') or { panic(error)}
p_path = re.replace(p_path,'/')
return "Path(${p_path})"
}
pub fn (p Path) / (n Path) Path {
mut n_path := n.path
mut re := regex.regex_opt(r'[/\\]+') or { panic(error)}
n_path = re.replace(n_path,'')
return Path{
path: p.path + '\\' + n_path
}
}
pub fn (p Path) glob(pattern string) []Path {
match_results := os.glob(pattern) or { ["abc"] }
return match_results.map(fn (path string) Path {
return Path {
path: path
}
})
}
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Component A</h1>
</body>
</html>
+269882
View File
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
import net.ftp
fn main() {
mut instance := ftp.new()
println('instance')
defer {
instance.close() or { panic(err) }
}
connected := instance.connect('148.251.128.152:22')!
println('connected ${connected}')
login_status := instance.login('root', '6e5W3kqeTWKqQG')!
println('login_status ${login_status}')
}
+189
View File
@@ -0,0 +1,189 @@
// 一个使用通道和线程的V语言HTTP服务器示例
import net.http
import time
// 定义请求结构
struct Request {
id int
url string
method string
timestamp time.Time
}
// 定义响应结构
struct Response {
id int
status_code int
body string
processing_time time.Duration
}
// 请求处理器 - 模拟处理HTTP请求
fn request_processor(req_chan chan Request, resp_chan chan Response) {
for {
// 从请求通道获取请求,如果通道关闭则退出循环
req := <-req_chan or { break }
// 记录开始处理时间
start := time.now()
// 根据URL模拟不同的处理时间和结果
mut body := ''
mut status := 200
if req.url.contains('/api/') {
// API请求需要更长的处理时间
time.sleep(100 * time.millisecond)
body = '{"status": "success", "data": {"id": ${req.id}}}'
} else if req.url.contains('/slow/') {
// 慢速请求
time.sleep(300 * time.millisecond)
body = '<html><body>Slow page content</body></html>'
} else if req.url.contains('/error/') {
// 错误请求
time.sleep(50 * time.millisecond)
status = 500
body = 'Internal Server Error'
} else {
// 一般页面请求
time.sleep(80 * time.millisecond)
body = '<html><body>Regular page content</body></html>'
}
// 计算处理时间
processing_time := time.now() - start
// 将响应发送到响应通道
resp_chan <- Response{
id: req.id
status_code: status
body: body
processing_time: processing_time
}
}
}
// 日志器 - 记录请求和响应
fn logger(log_chan chan string) {
for {
log_msg := <-log_chan or { break }
println('[${time.now().format_ss_milli()}] $log_msg')
}
}
// 统计分析器 - 收集和分析响应数据
fn stats_analyzer(resp_chan chan Response, log_chan chan string) {
mut total_requests := 0
mut success_requests := 0
mut error_requests := 0
mut total_processing_time := time.Duration(0)
for {
resp := <-resp_chan or { break }
total_requests++
total_processing_time += resp.processing_time
if resp.status_code >= 200 && resp.status_code < 400 {
success_requests++
} else {
error_requests++
}
// 每10个请求生成一次统计报告
if total_requests % 10 == 0 {
avg_time := total_processing_time / total_requests
log_chan <- 'STATS: Total: $total_requests, Success: $success_requests, Errors: $error_requests, Avg Time: ${avg_time}ms'
}
}
}
// 模拟HTTP服务器处理请求的函数
fn handle_request(id int, url string, method string, req_chan chan Request, log_chan chan string) {
// 创建请求对象
req := Request{
id: id
url: url
method: method
timestamp: time.now()
}
// 记录请求信息
log_chan <- 'REQUEST #$id: $method $url'
// 发送请求到处理通道
req_chan <- req
}
fn main() {
// 创建通道
req_chan := chan Request{cap: 100} // 请求通道,缓冲容量100
resp_chan1 := chan Response{cap: 100} // 响应通道1,用于响应处理
resp_chan2 := chan Response{cap: 100} // 响应通道2,用于统计分析
log_chan := chan string{cap: 200} // 日志通道
// 启动日志记录器线程
spawn logger(log_chan)
// 启动多个请求处理器线程(工作池)
worker_count := 5
for i in 0..worker_count {
spawn request_processor(req_chan, resp_chan1)
}
// 启动统计分析器线程
spawn stats_analyzer(resp_chan2, log_chan)
// 模拟接收HTTP请求
request_count := 30
for i in 0..request_count {
// 随机生成不同类型的URL
url_types := ['/api/user', '/api/data', '/slow/page', '/error/test', '/home', '/about']
url := url_types[i % url_types.len]
method := if i % 5 == 0 { 'POST' } else { 'GET' }
// 处理请求
handle_request(i, url, method, req_chan, log_chan)
// 从响应通道1接收响应并复制到响应通道2用于统计
if i > 0 && i % 3 == 0 {
for j in 0..3 {
if resp := <-resp_chan1 {
log_chan <- 'RESPONSE #${resp.id}: Status ${resp.status_code}, Processed in ${resp.processing_time}ms'
resp_chan2 <- resp
}
}
}
// 模拟请求到达间隔
time.sleep(50 * time.millisecond)
}
// 等待所有响应处理完毕
time.sleep(1 * time.second)
// 处理剩余的响应
remaining := request_count - (request_count / 3 * 3)
for i in 0..remaining {
if resp := <-resp_chan1 {
log_chan <- 'RESPONSE #${resp.id}: Status ${resp.status_code}, Processed in ${resp.processing_time}ms'
resp_chan2 <- resp
}
}
// 最终统计
log_chan <- 'SERVER SHUTDOWN: All requests processed'
// 给统计分析器线程时间处理最后的数据
time.sleep(500 * time.millisecond)
// 关闭通道
req_chan.close()
resp_chan1.close()
resp_chan2.close()
log_chan.close()
// 给所有线程时间完成和退出
time.sleep(100 * time.millisecond)
println('Server shutdown complete')
}
+70
View File
@@ -0,0 +1,70 @@
import net.http
import hono
fn main() {
mut hono_app := hono.Hono{}
hono_app.get('/api/**/:folder/user', fn (req hono.Request) http.Response {
println('Request url: ${req.url}')
println('Query map: ${req.query}')
println('Param map: ${req.param}')
return hono.Response.text('Hello, GET request! id = ${req.param['id']}, q = ${req.param['q']}')
})
hono_app.post('/users/**', fn (req hono.Request) http.Response {
println('Request url: ${req.url}')
println('Query map: ${req.query}')
println('Param map: ${req.param}')
return hono.Response.text('Hello, GET request! id = ${req.param['id']}, q = ${req.param['q']}')
})
hono_app.get('/hello/:id/id/:q', fn (req hono.Request) http.Response {
println('Request url: ${req.url}')
println('Query map: ${req.query}')
println('Param map: ${req.param}')
return hono.Response.text('Hello, GET request! id = ${req.param['id']}, q = ${req.param['q']}')
})
hono_app.get('/html', fn (req hono.Request) http.Response {
println('Request url: ${req.url}')
println('Query map: ${req.query}')
println('Param map: ${req.param}')
result := '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script>
const socket = new WebSocket("ws://127.0.0.1:8888")
socket.onopen = function (event) {
console.log("WebSocket is open now.")
socket.send("hello")
}
</script>
</head>
<body>
<h1>Component A</h1>
</body>
</html>'
return hono.Response.html(result)
})
hono_app.get('/order/:id', fn (req hono.Request) http.Response {
println('Query map: ${req.query}')
println('Param map: ${req.param}')
result := '{}'
return hono.Response.json(result)
})
hono_app.put('/order/:id', fn (req hono.Request) http.Response {
println('Query map: ${req.query}')
println('Param map: ${req.param}')
result := '{}'
return hono.Response.json(result)
})
hono_app.post('/post/*', fn (req hono.Request) http.Response {
println('Received POST request: ${req.data}')
return http.Response{
body: 'Hello, POST request'
status_code: 200
}
})
port := '0.0.0.0:8585'
hono_app.listen(port)
println('Listening on ${port}')
}
+8
View File
@@ -0,0 +1,8 @@
import net.http.file;
import os
fn main() {
path := os.join_path(os.abs_path('./'),'/component/')
println("path ${path}")
file.serve(folder: "./src/component",on: "127.0.0.1:5002")
}
+28
View File
@@ -0,0 +1,28 @@
import net.websocket as socket
import net
fn main() {
opt := socket.ServerOpt{}
addr := net.AddrFamily.ip
mut server := socket.new_server(addr, 8888, '', opt)
server.on_connect(fn (mut client socket.ServerClient) !bool {
println('Client connected from')
conn := client.client.conn
peer_ip := conn.peer_ip() or { '0.0.0.0' }
println('IP: ${peer_ip}')
return true
})!
server.on_message(fn (mut client socket.Client, msg &socket.Message) ! {
payload := msg.payload
if payload.len > 0 {
println('payload: ${payload.bytestr()}')
client.write_string('hello from server')!
}
})
server.listen()!
}
+13
View File
@@ -0,0 +1,13 @@
import net.websocket as socket
fn main() {
opt := socket.ClientOpt{}
mut client := socket.new_client('ws://127.0.0.1:8888/hi', opt)!
client.connect()!
client.write_string('hi from client')!
client.listen()!
}
+53
View File
@@ -0,0 +1,53 @@
import json
import os
import time
type CategoryId = string | int
struct Asset {
asset_name string @[json: assetName]
asset_status struct {
alert ?string
abnormal []int
normal bool
} @[json: assetStatus]
asset_uuid string @[json: assetUuid]
category string
category_color string @[json: categoryColor]
category_id CategoryId @[json: categoryId]
category_logo_url string @[json: categoryLogoUrl]
external_asset_id string @[json: externalAssetId]
floor string
location struct {
city string
country string
lat string
lon string
postcode string
state string
street_name string @[json: streetName]
street_number string @[json: streetNumber]
}
logo_color string @[json: logoColor]
logo_url string @[json: logoUrl]
op_mode int @[json: opMode]
unit_name string @[json: unitName]
view_style int @[json: viewStyle]
}
fn main(){
text := os.read_file('./src/data.json') or {
println('Error reading file: $err')
return
}
start := time.now()
json_data := json.decode([]Asset, text) or {
println('Error decoding JSON: $err')
return
}
end := time.now()
println('Time taken to decode JSON: ${end - start}')
println('json_data: ${json_data[1000]}')
os.write_file('./src/result.json', json.encode(json_data)) or {
println('Error writing file: $err')
return
}
}
+44
View File
@@ -0,0 +1,44 @@
module main
import pathlib { Path }
import time
import rand
import crypto.md5
import os
fn async_fn(print_string string, delay f32) f32 {
return delay
}
fn main() {
p1 := Path{}
p2 := Path{
path: '//src'
}
println('p2 ${p2}')
p3 := p1 / p2
println('p3: ${p3}')
for path in p3.walk() {
println('${path}')
}
match_results := p3.glob('**.html')
println('match_results ${match_results}')
mut threads := []f32{}
start := time.now()
for _ in 1 .. 1000 {
threads << async_fn('1', rand.f32())
}
end := time.now()
println('spawn time ${end - start}')
println('done ${threads[0..10]}')
println('absolute path ${os.abs_path('.')}')
text := os.read_file(Path{ path: './src/component/maplabel.js' }.absolute().path) or {
eprintln('failed to read the file: ${err}')
return
}
hash_start := time.now()
println('hexhash ${md5.hexhash(text)}')
hash_end := time.now()
println('hash time ${hash_end - hash_start}')
}
+25
View File
@@ -0,0 +1,25 @@
module main
import os
fn get_file_default_content() string {
return 'Alala'
}
fn do_nothing() {
}
fn main() {
areas := ['game', 'web', 'tools', 'science', 'systems','embedded', 'drivers', 'GUI', 'mobile']
for area in areas {
println('Hello, ${area} developers!')
}
file_content := os.read_file('./src/main.v') or {
get_file_default_content()
}
println('file_content: ${file_content}')
os.write_file('./src/main_copy.v', file_content) or {
panic(err.str())
}
}
+78
View File
@@ -0,0 +1,78 @@
import net.http { get,Response }
import crypto.md5
import time
fn hexhash(text string) string {
return md5.hexhash(text)
}
fn get_content(url string) string {
content := get(url) or {
println("Error: $err")
Response{
body:"string"
}
}
result := content.bytestr()
println("get_content ${url} ${result[0..10]}")
return result
}
fn worker(id int, jobs chan int, results chan int) {
for {
job := <-jobs // 从jobs通道接收任务
println('工作者 ${id} 开始处理任务 ${job}')
// 模拟工作耗时
time.sleep(job * 100 * time.millisecond)
j1 := job * 2
results <- j1 // 将结果发送到results通道
}
}
fn main() {
jobs := chan int{cap: 10}
results := chan int{cap: 10}
// 启动3个工作者协程
for i in 0..3 {
spawn worker(i, jobs, results)
}
// 发送5个任务
for i in 1..6 {
jobs <- i
}
// 接收并处理结果
// for _ in 1..6 {
// result := <-results
// println('收到结果: ${result}')
// }
mut count := 0
for {
select {
result := <-results {
println('收到结果: ${result}')
count++
}
1000 * time.millisecond {
println('超时')
break
}
}
if count >= 6 {
break
}
}
start := time.now()
mut threads := []thread string{}
for _ in 1..20 {
time.sleep(0.01)
threads << spawn get_content("https://www.163.com")
}
// contents := threads.wait()
end := time.now()
println("get_content time ${end - start}")
// println("md5 init. ${contents[0][0]}")
}
+14
View File
@@ -0,0 +1,14 @@
import regex
fn main(){
path := "/order/**"
mut replaced_path := path.replace('**','[\\w+//?]+')
println("replaced_path: ${replaced_path}")
replaced_path = replaced_path.replace('*','\\w+')
println("replaced_path: ${replaced_path}")
replaced_path = replaced_path + '$'
println("replaced_path: ${replaced_path}")
mut reg := regex.regex_opt(replaced_path) or { panic(err) }
result := reg.matches_string("/order/33")
println("reg match result ${result}")
}
+1
View File
File diff suppressed because one or more lines are too long
+43
View File
@@ -0,0 +1,43 @@
import ui
struct App {
mut:
window &ui.Window = unsafe { nil }
first_name string
last_name string
}
fn main() {
mut app := &App{}
app.window = ui.window(
width: 600
height: 400
title: 'V UI Demo'
children: [
ui.row(
margin: ui.Margin{10, 10, 10, 10}
children: [
ui.column(
width: 200
spacing: 13
children: [
ui.textbox(
max_len: 20
width: 200
placeholder: 'First name'
text: &app.first_name
),
ui.textbox(
max_len: 50
width: 200
placeholder: 'Last name'
text: &app.last_name
),
]
),
]
),
]
)
ui.run(app.window)
}
+13
View File
@@ -0,0 +1,13 @@
from datetime import datetime
def async_fn (delay):
return delay
threads = []
start = datetime.now()
count = 0
while(count < 10000000):
count+=1
threads.append(async_fn(1))
end = datetime.now()
print(f"spawn time {end - start}")
print(f"done {threads[1:100]}")
+7
View File
@@ -0,0 +1,7 @@
Module {
name: 'v'
description: 'v new v-starting'
version: '0.0.0'
license: 'MIT'
dependencies: ["ui"]
}