feat: made better interfaces and more generalized approach to use THT.
This commit is contained in:
parent
8ac7b3bb31
commit
502cfbe390
12 changed files with 259 additions and 117 deletions
|
@ -1,11 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"vultras.su/util/tpp/httpx"
|
"surdeus.su/util/tpp/httpx"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
httpx.Tool.Run(os.Args[1:])
|
httpx.Tool.Run(os.Args[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"vultras.su/util/tpp"
|
"surdeus.su/util/tpp"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module vultras.su/util/tpp
|
module surdeus.su/util/tpp
|
||||||
|
|
||||||
go 1.21.7
|
go 1.21.7
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package httpx
|
package httpx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"vultras.su/util/tpp"
|
"surdeus.su/util/tpp"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"net/http"
|
"net/http"
|
||||||
"context"
|
"context"
|
||||||
|
@ -20,14 +20,28 @@ type Handler struct {
|
||||||
// THe field represents
|
// THe field represents
|
||||||
// where we store site's
|
// where we store site's
|
||||||
// source files and request handlers.
|
// source files and request handlers.
|
||||||
SourcePath string
|
sourcePath string
|
||||||
// Preprocessor must be set by user
|
// Preprocessor must be set by user
|
||||||
// to be able to bring custom features in.
|
// to be able to bring custom features in.
|
||||||
PP *tpp.Preprocessor
|
pp *tpp.Preprocessor
|
||||||
// Aditional extension. ".tpp" by default.
|
// Aditional extension. ".tpp" by default.
|
||||||
// For example "file.html.tpp" will be
|
// For example "file.html.tpp" will be
|
||||||
// first preprocessed TPP and sent back as simple HTML.
|
// first preprocessed TPP and sent back as simple HTML.
|
||||||
Ext string
|
ext string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the new Handler with
|
||||||
|
// specified preprocessor, source path and
|
||||||
|
// extension.
|
||||||
|
func NewHandler(
|
||||||
|
pp *tpp.Preprocessor,
|
||||||
|
src, ext string,
|
||||||
|
) *Handler {
|
||||||
|
ret := &Handler{}
|
||||||
|
ret.sourcePath = src
|
||||||
|
ret.ext = ext
|
||||||
|
ret.pp = pp
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) ServeHTTP(
|
func (h *Handler) ServeHTTP(
|
||||||
|
@ -40,10 +54,10 @@ func (h *Handler) ServeHTTP(
|
||||||
urlExt := path.Ext(urlPath)
|
urlExt := path.Ext(urlPath)
|
||||||
|
|
||||||
filePath := filepath.Join(
|
filePath := filepath.Join(
|
||||||
filepath.FromSlash(h.SourcePath),
|
filepath.FromSlash(h.sourcePath),
|
||||||
filepath.FromSlash(urlPath),
|
filepath.FromSlash(urlPath),
|
||||||
)
|
)
|
||||||
filePathTpp := filePath + h.Ext
|
filePathTpp := filePath + h.ext
|
||||||
|
|
||||||
//log.Println("pth:", urlPath, filePathTpp)
|
//log.Println("pth:", urlPath, filePathTpp)
|
||||||
file, err := os.Open(filePathTpp)
|
file, err := os.Open(filePathTpp)
|
||||||
|
@ -62,7 +76,13 @@ func (h *Handler) ServeHTTP(
|
||||||
ctx := context.WithValue(r.Context(), KeyRequest, &Request{
|
ctx := context.WithValue(r.Context(), KeyRequest, &Request{
|
||||||
Request: r,
|
Request: r,
|
||||||
})
|
})
|
||||||
processedData, err := h.PP.Process(
|
|
||||||
|
// Setting before the code to let it change own
|
||||||
|
// content type.
|
||||||
|
contentType := mime.TypeByExtension(urlExt)
|
||||||
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
processedData, err := h.pp.Process(
|
||||||
ctx,
|
ctx,
|
||||||
true,
|
true,
|
||||||
filePathTpp,
|
filePathTpp,
|
||||||
|
@ -74,7 +94,5 @@ func (h *Handler) ServeHTTP(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := mime.TypeByExtension(urlExt)
|
|
||||||
w.Header().Set("Content-Type", contentType)
|
|
||||||
w.Write(processedData)
|
w.Write(processedData)
|
||||||
}
|
}
|
||||||
|
|
24
httpx/module.go
Normal file
24
httpx/module.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package httpx
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/v2"
|
||||||
|
import "github.com/d5/tengo/v2/stdlib"
|
||||||
|
import "surdeus.su/util/tpp/paths"
|
||||||
|
|
||||||
|
var Modules = map[string]tengo.Importable{
|
||||||
|
"paths": paths.Module,
|
||||||
|
}
|
||||||
|
|
||||||
|
var Stdlib = stdlib.GetModuleMap(stdlib.AllModuleNames()...)
|
||||||
|
|
||||||
|
type ModuleGetter struct{}
|
||||||
|
|
||||||
|
func (m *ModuleGetter) Get(
|
||||||
|
name string,
|
||||||
|
) tengo.Importable {
|
||||||
|
module, exist := Modules[name]
|
||||||
|
if exist {
|
||||||
|
return module
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stdlib.Get(name)
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package httpx
|
||||||
import (
|
import (
|
||||||
"github.com/d5/tengo/v2"
|
"github.com/d5/tengo/v2"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"io"
|
||||||
//"log"
|
//"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,6 +33,14 @@ func (r *Request) IndexGet(
|
||||||
return &URL{
|
return &URL{
|
||||||
URL: r.URL,
|
URL: r.URL,
|
||||||
}, nil
|
}, nil
|
||||||
|
case "body" :
|
||||||
|
bts, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &tengo.Bytes{
|
||||||
|
Value: bts,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing found.
|
// Nothing found.
|
||||||
|
|
|
@ -2,8 +2,8 @@ package httpx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"github.com/d5/tengo/v2"
|
//"github.com/d5/tengo/v2"
|
||||||
"github.com/d5/tengo/v2/stdlib"
|
//"github.com/d5/tengo/v2/stdlib"
|
||||||
"vultras.su/util/tpp"
|
"surdeus.su/util/tpp"
|
||||||
"vultras.su/core/cli/mtool"
|
"vultras.su/core/cli/mtool"
|
||||||
"net/http"
|
"net/http"
|
||||||
"log"
|
"log"
|
||||||
|
@ -12,47 +12,53 @@ import (
|
||||||
|
|
||||||
// Context key type for internal usage.
|
// Context key type for internal usage.
|
||||||
type CKey string
|
type CKey string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KeyRequest CKey = "http-request"
|
KeyRequest CKey = "http-request"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Simple PHP-like server implementation.
|
// Simple PHP-like server implementation.
|
||||||
var Tool = mtool.T("tht").Func(func(flags *mtool.Flags){
|
var Tool = mtool.T("tht").Func(func(flags *mtool.Flags) {
|
||||||
var (
|
var (
|
||||||
addr string
|
addr, ext, src, mod string
|
||||||
handler Handler
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flags.StringVar(&addr, "addr", ":3000", "address to serve at")
|
flags.StringVar(&addr, "addr", ":3000", "address to serve at")
|
||||||
flags.StringVar(&handler.SourcePath, "src", "./src", "directory with source files")
|
flags.StringVar(&mod, "mod", "./mod", "path to store Tengo modules")
|
||||||
flags.StringVar(&handler.Ext, "ext", ".tpp", "extension for TPP files")
|
flags.StringVar(&src, "src", "./src", "directory with source files")
|
||||||
|
flags.StringVar(&ext, "ext", ".tpp", "extension for TPP files")
|
||||||
|
|
||||||
flags.Parse()
|
flags.Parse()
|
||||||
|
|
||||||
t := tpp.NewTengo().SetPreCompile(func(
|
t := tpp.NewTengo().SetPreCompile(func(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
s *tpp.Script,
|
s *tpp.Script,
|
||||||
){
|
) {
|
||||||
s.SetImportDir(handler.SourcePath)
|
s.SetImportDir(mod)
|
||||||
s.SetImports(stdlib.GetModuleMap(
|
s.SetImports(&ModuleGetter{})
|
||||||
stdlib.AllModuleNames()...,
|
|
||||||
))
|
|
||||||
s.EnableFileImport(true)
|
s.EnableFileImport(true)
|
||||||
|
|
||||||
s.Add("__http_request__", ctx.Value(KeyRequest))
|
s.Add("__http_request__", ctx.Value(KeyRequest))
|
||||||
}).SetPreCode(func(ctx context.Context) []byte {
|
}).SetPreCode(func(ctx context.Context) []byte {
|
||||||
return []byte(`
|
return []byte(`
|
||||||
http := {
|
http := immutable({
|
||||||
request : __http_request__
|
request : __http_request__
|
||||||
|
})
|
||||||
|
__context__ := {
|
||||||
|
http: http,
|
||||||
|
pp: pp
|
||||||
}
|
}
|
||||||
|
import("./pre")(__context__)
|
||||||
|
`)
|
||||||
|
}).SetPostCode(func(ctx context.Context) []byte {
|
||||||
|
return []byte(`
|
||||||
|
import("./post")(__context__)
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
handler.PP = tpp.New(t)
|
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: &handler,
|
Handler: NewHandler(tpp.New(t), src, ext),
|
||||||
}
|
}
|
||||||
err := srv.ListenAndServe()
|
err := srv.ListenAndServe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
22
mod/post.tengo
Normal file
22
mod/post.tengo
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
fmt := import("fmt")
|
||||||
|
paths := import("paths")
|
||||||
|
htmExt := func(c){
|
||||||
|
c.pp.print(`
|
||||||
|
</body></html>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
exts := {
|
||||||
|
".htm": htmExt,
|
||||||
|
".html": htmExt
|
||||||
|
}
|
||||||
|
|
||||||
|
export func(c){
|
||||||
|
url_path := c.http.request.url.path
|
||||||
|
ext := paths.ext(url_path)
|
||||||
|
|
||||||
|
cl := exts[ext]
|
||||||
|
if !is_undefined(cl) {
|
||||||
|
cl(c)
|
||||||
|
}
|
||||||
|
}
|
26
mod/pre.tengo
Normal file
26
mod/pre.tengo
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
fmt := import("fmt")
|
||||||
|
paths := import("paths")
|
||||||
|
|
||||||
|
htmExt := func(c){
|
||||||
|
c.pp.print(`
|
||||||
|
<!doctype html>
|
||||||
|
<html><head>
|
||||||
|
<title>Check shit</title>
|
||||||
|
</head><body>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
exts := {
|
||||||
|
".htm": htmExt,
|
||||||
|
".html": htmExt
|
||||||
|
}
|
||||||
|
|
||||||
|
export func(c){
|
||||||
|
url_path := c.http.request.url.path
|
||||||
|
ext := paths.ext(url_path)
|
||||||
|
|
||||||
|
cl := exts[ext]
|
||||||
|
if !is_undefined(cl) {
|
||||||
|
cl(c)
|
||||||
|
}
|
||||||
|
}
|
51
paths/main.go
Normal file
51
paths/main.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package paths
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/v2"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Module = &tengo.BuiltinModule {
|
||||||
|
Attrs: moduleMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
var moduleMap = map[string] tengo.Object {
|
||||||
|
"base" : &tengo.UserFunction{
|
||||||
|
Name: "base",
|
||||||
|
Value: wrapOneArg(path.Base),
|
||||||
|
},
|
||||||
|
"ext" : &tengo.UserFunction{
|
||||||
|
Name: "ext",
|
||||||
|
Value: wrapOneArg(path.Ext),
|
||||||
|
},
|
||||||
|
"dir" : &tengo.UserFunction{
|
||||||
|
Name: "dir",
|
||||||
|
Value: wrapOneArg(path.Dir),
|
||||||
|
},
|
||||||
|
"clean" : &tengo.UserFunction{
|
||||||
|
Name: "clean",
|
||||||
|
Value: wrapOneArg(path.Clean),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapOneArg(
|
||||||
|
fn func(string) string,
|
||||||
|
) func(...tengo.Object) (tengo.Object, error) {
|
||||||
|
return func(args ...tengo.Object) (tengo.Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, tengo.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
str, ok := tengo.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
|
Name: "first",
|
||||||
|
Expected: "string(compatible)",
|
||||||
|
Found: args[0].TypeName(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &tengo.String{
|
||||||
|
Value: fn(str),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,26 +4,27 @@
|
||||||
q := req.url.query
|
q := req.url.query
|
||||||
// List checking.
|
// List checking.
|
||||||
list := [1, 2, 3, 4, 123]
|
list := [1, 2, 3, 4, 123]
|
||||||
|
body := req.body
|
||||||
|
if body {
|
||||||
|
fmt.println(string(body))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
}}<!doctype html>
|
{{
|
||||||
<html><head>
|
if q.name {
|
||||||
</head><body>
|
pp.print("<div id=\"name\">", q.name[0], "</div>")
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
<div>
|
||||||
|
Hello, Cock!
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
{{
|
{{
|
||||||
if q.name {
|
pp.print(req.url.path)
|
||||||
pp.print("<div id=\"name\">", q.name[0], "</div>")
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
<div>
|
</div>
|
||||||
Hello, Cock!
|
<ul>{{
|
||||||
</div>
|
for v in list {
|
||||||
<div>
|
pp.print("<li>", v, "</li>")
|
||||||
{{
|
}
|
||||||
pp.print(req.url.path)
|
}}</ul>
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<ul>{{
|
|
||||||
for v in list {
|
|
||||||
pp.print("<li>", v, "</li>")
|
|
||||||
}
|
|
||||||
}}</ul>
|
|
||||||
</body></html>
|
|
||||||
|
|
120
tengo.go
120
tengo.go
|
@ -53,7 +53,8 @@ func (pp *Tengo) Eval(
|
||||||
var fullCodeBuf bytes.Buffer
|
var fullCodeBuf bytes.Buffer
|
||||||
const retHead = `
|
const retHead = `
|
||||||
__ret_one__ := bytes("")
|
__ret_one__ := bytes("")
|
||||||
pp := {
|
pp := immutable({
|
||||||
|
filepath: __filepath__,
|
||||||
printf : func(format, ...vals) {
|
printf : func(format, ...vals) {
|
||||||
__ret_one__ += __sprintf__(format, vals...)
|
__ret_one__ += __sprintf__(format, vals...)
|
||||||
},
|
},
|
||||||
|
@ -67,7 +68,7 @@ func (pp *Tengo) Eval(
|
||||||
write_raw : func(bts) {
|
write_raw : func(bts) {
|
||||||
__ret_one__ += bytes(bts)
|
__ret_one__ += bytes(bts)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -94,78 +95,63 @@ func (pp *Tengo) Eval(
|
||||||
pp.preCompile(ctx, script)
|
pp.preCompile(ctx, script)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := script.Add("__sprintf__", &tengo.UserFunction{
|
script.Add("__filepath__", filePath)
|
||||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
script.Add("__sprintf__", &tengo.UserFunction{
|
||||||
if len(args) < 1 {
|
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||||
return nil, tengo.ErrWrongNumArguments
|
if len(args) < 1 {
|
||||||
|
return nil, tengo.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
format, ok := tengo.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
|
Expected: "string",
|
||||||
}
|
}
|
||||||
format, ok := tengo.ToString(args[0])
|
}
|
||||||
if !ok {
|
gargs := make([]any, len(args) - 1)
|
||||||
return nil, tengo.ErrInvalidArgumentType{
|
for i := range gargs {
|
||||||
Expected: "string",
|
gargs[i] = tengo.ToInterface(args[i+1])
|
||||||
}
|
//fmt.Printf("shit: %q\n", gargs[i])
|
||||||
}
|
}
|
||||||
gargs := make([]any, len(args) - 1)
|
str := fmt.Sprintf(format, gargs...)
|
||||||
for i := range gargs {
|
return tengo.FromInterface([]byte(str))
|
||||||
gargs[i] = tengo.ToInterface(args[i+1])
|
|
||||||
//fmt.Printf("shit: %q\n", gargs[i])
|
|
||||||
}
|
|
||||||
str := fmt.Sprintf(format, gargs...)
|
|
||||||
return tengo.FromInterface([]byte(str))
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
if err != nil {
|
script.Add("__sprint__", &tengo.UserFunction{
|
||||||
return nil, err
|
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||||
}
|
gargs := make([]any, len(args))
|
||||||
err = script.Add("__sprint__", &tengo.UserFunction{
|
for i := range gargs {
|
||||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
gargs[i] = tengo.ToInterface(args[i])
|
||||||
gargs := make([]any, len(args))
|
}
|
||||||
for i := range gargs {
|
str := fmt.Sprint(gargs...)
|
||||||
gargs[i] = tengo.ToInterface(args[i])
|
return tengo.FromInterface([]byte(str))
|
||||||
}
|
|
||||||
str := fmt.Sprint(gargs...)
|
|
||||||
return tengo.FromInterface([]byte(str))
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
if err != nil {
|
script.Add("__sprintln__", &tengo.UserFunction{
|
||||||
return nil, err
|
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||||
}
|
gargs := make([]any, len(args))
|
||||||
err = script.Add("__sprintln__", &tengo.UserFunction{
|
for i := range gargs {
|
||||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
gargs[i] = tengo.ToInterface(args[i])
|
||||||
gargs := make([]any, len(args))
|
}
|
||||||
for i := range gargs {
|
str := fmt.Sprintln(gargs...)
|
||||||
gargs[i] = tengo.ToInterface(args[i])
|
return tengo.FromInterface([]byte(str))
|
||||||
}
|
|
||||||
str := fmt.Sprintln(gargs...)
|
|
||||||
return tengo.FromInterface([]byte(str))
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
if err != nil {
|
script.Add("__separate__", &tengo.UserFunction{
|
||||||
return nil, err
|
Value: func(args ...tengo.Object) (tengo.Object, error){
|
||||||
}
|
if len(args) < 1 {
|
||||||
err = script.Add("__separate__", &tengo.UserFunction{
|
return nil, tengo.ErrWrongNumArguments
|
||||||
Value: func(args ...tengo.Object) (tengo.Object, error){
|
}
|
||||||
if len(args) < 1 {
|
bts, ok := tengo.ToByteSlice(args[0])
|
||||||
return nil, tengo.ErrWrongNumArguments
|
if !ok {
|
||||||
|
return nil, tengo.ErrInvalidArgumentType{
|
||||||
}
|
}
|
||||||
bts, ok := tengo.ToByteSlice(args[0])
|
}
|
||||||
if !ok {
|
rets = append(rets, bts)
|
||||||
return nil, tengo.ErrInvalidArgumentType{
|
return nil, nil
|
||||||
}
|
|
||||||
}
|
|
||||||
rets = append(rets, bts)
|
|
||||||
return nil, nil
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = script.RunContext(ctx)
|
_, err := script.RunContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue