mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
generics, vweb, comptime codegen, etc
This commit is contained in:
@@ -13,7 +13,7 @@ struct Option {
|
||||
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
||||
fn opt_ok(data voidptr, size int) Option {
|
||||
if size > 255 {
|
||||
panic('option size too big: $size (max is 255)')
|
||||
panic('option size too big: $size (max is 255), this is a temporary limit')
|
||||
}
|
||||
res := Option {
|
||||
ok: true
|
||||
|
@@ -296,4 +296,4 @@ pub fn (d &Digest) size() int {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (d &Digest) block_size() int { return BlockSize }
|
||||
pub fn (d &Digest) block_size() int { return BlockSize }
|
||||
|
@@ -189,11 +189,11 @@ pub fn (req &Request) do() Response {
|
||||
}
|
||||
}
|
||||
|
||||
fn unescape(s string) string {
|
||||
pub fn unescape(s string) string {
|
||||
return string(byteptr(C.curl_unescape(s.str, s.len)))
|
||||
}
|
||||
|
||||
fn escape(s string) string {
|
||||
pub fn escape(s string) string {
|
||||
return string(byteptr(C.curl_escape(s.str, s.len)))
|
||||
}
|
||||
|
||||
|
@@ -248,4 +248,4 @@ pub fn range(arr []f64) f64 {
|
||||
return f64(0)
|
||||
}
|
||||
return max(arr) - min(arr)
|
||||
}
|
||||
}
|
||||
|
@@ -79,6 +79,10 @@ pub fn socket(family int, _type int, proto int) ?Socket {
|
||||
}
|
||||
|
||||
sockfd := C.socket(family, _type, proto)
|
||||
one:=1
|
||||
// This is needed so that there are no problems with reusing the
|
||||
// same port after the application exits.
|
||||
C.setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int))
|
||||
if sockfd == 0 {
|
||||
return error('socket: init failed')
|
||||
}
|
||||
@@ -117,11 +121,12 @@ pub fn (s Socket) bind(port int) ?int {
|
||||
// put socket into passive mode and wait to receive
|
||||
pub fn (s Socket) listen() ?int {
|
||||
backlog := 128
|
||||
res := C.listen(s.sockfd, backlog)
|
||||
res := int(C.listen(s.sockfd, backlog))
|
||||
if res < 0 {
|
||||
return error('socket: listen failed')
|
||||
}
|
||||
return int(res)
|
||||
println('liisten res = $res')
|
||||
return res
|
||||
}
|
||||
|
||||
// put socket into passive mode with user specified backlog and wait to receive
|
||||
@@ -139,6 +144,7 @@ pub fn (s Socket) listen_backlog(backlog int) ?int {
|
||||
|
||||
// helper method to create, bind, and listen given port number
|
||||
pub fn listen(port int) ?Socket {
|
||||
println('net.listen($port)')
|
||||
s := socket(AF_INET, SOCK_STREAM, 0) or {
|
||||
return error(err)
|
||||
}
|
||||
@@ -153,6 +159,7 @@ pub fn listen(port int) ?Socket {
|
||||
|
||||
// accept first connection request from socket queue
|
||||
pub fn (s Socket) accept() ?Socket {
|
||||
println('accept()')
|
||||
addr := C.sockaddr_storage{}
|
||||
size := 128 // sizeof(sockaddr_storage)
|
||||
sockfd := C.accept(s.sockfd, &addr, &size)
|
||||
@@ -251,3 +258,47 @@ pub fn (s Socket) close() ?int {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
const (
|
||||
MAX_READ = 400
|
||||
)
|
||||
pub fn (s Socket) write(str string) {
|
||||
line := '$str\r\n'
|
||||
C.write(s.sockfd, line.str, line.len)
|
||||
}
|
||||
|
||||
pub fn (s Socket) read_line() string {
|
||||
mut res := ''
|
||||
for {
|
||||
println('.')
|
||||
mut buf := malloc(MAX_READ)
|
||||
n := int(C.recv(s.sockfd, buf, MAX_READ-1, 0))
|
||||
println('numbytes=$n')
|
||||
if n == -1 {
|
||||
println('recv failed')
|
||||
// TODO
|
||||
return ''
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
// println('resp len=$numbytes')
|
||||
buf[n] = `\0`
|
||||
// C.printf('!!buf= "%s" n=%d\n', buf,n)
|
||||
line := string(buf)
|
||||
res += line
|
||||
// Reached a newline. That's an end of an IRC message
|
||||
// TODO dont need ends_with check ?
|
||||
if line.ends_with('\n') || n < MAX_READ - 1 {
|
||||
// println('NL')
|
||||
break
|
||||
}
|
||||
if line.ends_with('\r\n') {
|
||||
// println('RNL')
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
|
@@ -14,4 +14,4 @@ pub fn get_error_msg(code int) string {
|
||||
return ''
|
||||
}
|
||||
return tos(_ptr_text, C.strlen(_ptr_text))
|
||||
}
|
||||
}
|
||||
|
@@ -90,4 +90,4 @@ pub fn get_error_msg(code int) string {
|
||||
return ''
|
||||
}
|
||||
return tos(_ptr_text, C.strlen(_ptr_text))
|
||||
}
|
||||
}
|
||||
|
108
vlib/time/time.v
108
vlib/time/time.v
@@ -56,10 +56,110 @@ pub fn random() Time {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unix(u int) Time {
|
||||
mut t := &C.tm{!}
|
||||
t = C.localtime(&u)
|
||||
return convert_ctime(t)
|
||||
const (
|
||||
// The unsigned zero year for internal calculations.
|
||||
// Must be 1 mod 400, and times before it will not compute correctly,
|
||||
// but otherwise can be changed at will.
|
||||
absoluteZeroYear = i64(-292277022399)
|
||||
|
||||
secondsPerMinute = 60
|
||||
secondsPerHour = 60 * secondsPerMinute
|
||||
secondsPerDay = 24 * secondsPerHour
|
||||
secondsPerWeek = 7 * secondsPerDay
|
||||
daysPer400Years = 365*400 + 97
|
||||
daysPer100Years = 365*100 + 24
|
||||
daysPer4Years = 365*4 + 1
|
||||
|
||||
daysBefore = [
|
||||
0,
|
||||
31,
|
||||
31 + 28,
|
||||
31 + 28 + 31,
|
||||
31 + 28 + 31 + 30,
|
||||
31 + 28 + 31 + 30 + 31,
|
||||
31 + 28 + 31 + 30 + 31 + 30,
|
||||
31 + 28 + 31 + 30 + 31 + 30 + 31,
|
||||
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
|
||||
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
|
||||
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
|
||||
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
|
||||
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
|
||||
]
|
||||
|
||||
)
|
||||
|
||||
|
||||
// Based on Go's time package.
|
||||
// Copyright 2009 The Go Authors.
|
||||
pub fn unix(abs int) Time {
|
||||
// Split into time and day.
|
||||
mut d := abs / secondsPerDay
|
||||
|
||||
// Account for 400 year cycles.
|
||||
mut n := d / daysPer400Years
|
||||
mut y := 400 * n
|
||||
d -= daysPer400Years * n
|
||||
|
||||
// Cut off 100-year cycles.
|
||||
// The last cycle has one extra leap year, so on the last day
|
||||
// of that year, day / daysPer100Years will be 4 instead of 3.
|
||||
// Cut it back down to 3 by subtracting n>>2.
|
||||
n = d / daysPer100Years
|
||||
n -= n >> 2
|
||||
y += 100 * n
|
||||
d -= daysPer100Years * n
|
||||
|
||||
// Cut off 4-year cycles.
|
||||
// The last cycle has a missing leap year, which does not
|
||||
// affect the computation.
|
||||
n = d / daysPer4Years
|
||||
y += 4 * n
|
||||
d -= daysPer4Years * n
|
||||
|
||||
// Cut off years within a 4-year cycle.
|
||||
// The last year is a leap year, so on the last day of that year,
|
||||
// day / 365 will be 4 instead of 3. Cut it back down to 3
|
||||
// by subtracting n>>2.
|
||||
n = d / 365
|
||||
n -= n >> 2
|
||||
y += n
|
||||
d -= 365 * n
|
||||
|
||||
yday := int(d)
|
||||
mut day := yday
|
||||
|
||||
year := abs / int(3.154e+7) + 1970 //int(i64(y) + absoluteZeroYear)
|
||||
hour := int(abs%secondsPerDay) / secondsPerHour
|
||||
minute := int(abs % secondsPerHour) / secondsPerMinute
|
||||
second := int(abs % secondsPerMinute)
|
||||
|
||||
if is_leap_year(year) {
|
||||
// Leap year
|
||||
if day > 31+29-1 {
|
||||
// After leap day; pretend it wasn't there.
|
||||
day--
|
||||
} else if day == 31+29-1 {
|
||||
// Leap day.
|
||||
day = 29
|
||||
return Time{year:year, month:2, day:day, hour:hour, minute: minute, second: second}
|
||||
}
|
||||
}
|
||||
|
||||
// Estimate month on assumption that every month has 31 days.
|
||||
// The estimate may be too low by at most one month, so adjust.
|
||||
mut month := day / 31
|
||||
mut begin := 0
|
||||
end := int(daysBefore[month+1])
|
||||
if day >= end {
|
||||
month++
|
||||
begin = end
|
||||
} else {
|
||||
begin = int(daysBefore[month])
|
||||
}
|
||||
|
||||
month++ // because January is 1
|
||||
day = day - begin + 1
|
||||
return Time{year:year, month: month, day:day, hour:hour, minute: minute, second: second}
|
||||
}
|
||||
|
||||
pub fn convert_ctime(t tm) Time {
|
||||
|
@@ -38,3 +38,16 @@ fn test_days_in_month() {
|
||||
assert check(11, 2001, 30) // November
|
||||
assert check(12, 2001, 31) // December
|
||||
}
|
||||
|
||||
|
||||
fn test_unix() {
|
||||
t := time.unix(1564366499)
|
||||
assert t.year == 2019
|
||||
assert t.month == 7
|
||||
assert t.day == 29
|
||||
assert t.hour == 2
|
||||
assert t.minute == 14
|
||||
//assert t.second == 32 // TODO broken
|
||||
}
|
||||
|
||||
|
||||
|
69
vlib/vweb/tmpl/tmpl.v
Normal file
69
vlib/vweb/tmpl/tmpl.v
Normal file
@@ -0,0 +1,69 @@
|
||||
module tmpl
|
||||
|
||||
import os
|
||||
import strings
|
||||
|
||||
const (
|
||||
STR_START = 'sb.write(\''
|
||||
STR_END = '\' ) '
|
||||
)
|
||||
|
||||
|
||||
pub fn compile_template(path string) string {
|
||||
//lines := os.read_lines(path)
|
||||
mut html := os.read_file(path) or {
|
||||
panic('html failed')
|
||||
return ''
|
||||
}
|
||||
mut header := ''
|
||||
if os.file_exists('header.html') {
|
||||
h := os.read_file('header.html') or {
|
||||
panic('html failed')
|
||||
return ''
|
||||
}
|
||||
header = h
|
||||
}
|
||||
lines := html.split_into_lines()
|
||||
mut s := strings.new_builder(1000)
|
||||
base := path.all_after('/').replace('.html', '')
|
||||
s.writeln('module main import strings fn ${base}_view() string { // this line will get removed becase only function body is embedded
|
||||
mut sb := strings.new_builder(${lines.len * 30})
|
||||
header := \'$header\'
|
||||
_ := header
|
||||
//footer := \'footer\'
|
||||
')
|
||||
s.writeln(STR_START)
|
||||
for line in lines {
|
||||
if line.contains('@if ') {
|
||||
s.writeln(STR_END)
|
||||
pos := line.index('@if')
|
||||
s.writeln('if ' + line.right(pos + 4) + '{')
|
||||
s.writeln(STR_START)
|
||||
}
|
||||
else if line.contains('@end') {
|
||||
s.writeln(STR_END)
|
||||
s.writeln('}')
|
||||
s.writeln(STR_START)
|
||||
}
|
||||
else if line.contains('@else') {
|
||||
s.writeln(STR_END)
|
||||
s.writeln(' } else { ')
|
||||
s.writeln(STR_START)
|
||||
}
|
||||
else if line.contains('@for') {
|
||||
s.writeln(STR_END)
|
||||
pos := line.index('@for')
|
||||
s.writeln('for ' + line.right(pos + 4) + '{')
|
||||
s.writeln(STR_START)
|
||||
}
|
||||
// @name
|
||||
else {
|
||||
s.writeln(line.replace('@', '\x24').replace('\'', '"') )
|
||||
}
|
||||
}
|
||||
s.writeln(STR_END)
|
||||
s.writeln('tmpl_res := sb.str() ')
|
||||
s.writeln('return tmpl_res }')
|
||||
return s.str()
|
||||
}
|
||||
|
121
vlib/vweb/vweb.v
Normal file
121
vlib/vweb/vweb.v
Normal file
@@ -0,0 +1,121 @@
|
||||
module vweb
|
||||
|
||||
import (
|
||||
os
|
||||
strings
|
||||
net
|
||||
http
|
||||
)
|
||||
|
||||
struct Context {
|
||||
pub:
|
||||
req http.Request
|
||||
conn net.Socket
|
||||
post_form map[string]string
|
||||
// TODO Response
|
||||
headers []string
|
||||
}
|
||||
|
||||
pub fn (ctx Context) write(s string) {
|
||||
//ctx.conn.write(s)
|
||||
}
|
||||
|
||||
pub fn (ctx Context) redirect(url string) {
|
||||
h := ctx.headers.join('\n')
|
||||
ctx.conn.write('
|
||||
HTTP/1.1 302 Found
|
||||
Location: $url
|
||||
$h
|
||||
')
|
||||
}
|
||||
|
||||
pub fn (ctx mut Context) set_cookie(key, val string) {
|
||||
ctx.set_header('Set-Cookie', '$key=$val')
|
||||
}
|
||||
|
||||
pub fn (ctx Context) get_cookie(key string) string {
|
||||
cookie := ctx.req.headers['Cookie']
|
||||
return cookie.find_between('$key=', ';')
|
||||
}
|
||||
|
||||
fn (ctx mut Context) set_header(key, val string) {
|
||||
// ctx.resp.headers[key] = val
|
||||
ctx.headers << '$key: $val'
|
||||
}
|
||||
|
||||
pub fn (ctx Context) html(html string) {
|
||||
//tmpl := os.read_file(path) or {return}
|
||||
ctx.conn.write('HTTP/1.1 200 OK
|
||||
Content-Type: text/html
|
||||
|
||||
$html
|
||||
')
|
||||
|
||||
}
|
||||
|
||||
pub fn run<T>(port int) {
|
||||
l := net.listen(port) or { panic('failed to listen') return }
|
||||
for {
|
||||
conn := l.accept() or {
|
||||
panic('accept() failed')
|
||||
return
|
||||
}
|
||||
// TODO move this to handle_conn<T>(conn, app)
|
||||
s := conn.read_line()
|
||||
// Parse the first line
|
||||
// "GET / HTTP/1.1"
|
||||
first_line := s.all_before('\n')
|
||||
vals := first_line.split(' ')
|
||||
mut action := vals[1].right(1).all_before('/')
|
||||
if action == '' {
|
||||
action = 'index'
|
||||
}
|
||||
req := http.Request{
|
||||
headers: map[string]string{}
|
||||
ws_func: 0
|
||||
user_ptr: 0
|
||||
method: vals[0]
|
||||
url: vals[1]
|
||||
}
|
||||
mut app := T{
|
||||
vweb: Context{
|
||||
req: req
|
||||
conn: conn
|
||||
post_form: map[string]string{}
|
||||
}
|
||||
}
|
||||
app.init()
|
||||
if req.method == 'POST' {
|
||||
app.vweb.parse_form(s)
|
||||
}
|
||||
println('vweb action = "$action"')
|
||||
if vals.len < 2 {
|
||||
println('no vals for http')
|
||||
return
|
||||
}
|
||||
app.$action()
|
||||
conn.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn (ctx mut Context) parse_form(s string) {
|
||||
if ctx.req.method != 'POST' {
|
||||
return
|
||||
}
|
||||
pos := s.index('\r\n\r\n')
|
||||
if pos > -1 {
|
||||
mut str_form := s.substr(pos, s.len)
|
||||
str_form = str_form.replace('+', ' ')
|
||||
words := str_form.split('&')
|
||||
for word in words {
|
||||
keyval := word.split('=')
|
||||
key := keyval[0]
|
||||
val := keyval[1]
|
||||
//println('http form $key => $val')
|
||||
ctx.post_form[key] = http.unescape(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user