diff --git a/httpx/handler.go b/httpx/handler.go deleted file mode 100644 index 9358f3e..0000000 --- a/httpx/handler.go +++ /dev/null @@ -1,191 +0,0 @@ -package httpx - -import ( - //"github.com/d5/tengo/v2" - "surdeus.su/util/tpp/mdx" - "surdeus.su/util/tpp/htmlx" - "surdeus.su/util/tpp" - "path/filepath" - "net/http" - "context" - "path" - "mime" - "log" - "os" - "io" -) - -var _ = http.Handler(&Handler{}) - -// The type describes behaviour -// of handling stuff like in PHP. -type Handler struct { - // THe field represents - // where we store site's - // source files and request handlers. - sourcePath string - // Preprocessor must be set by user - // to be able to bring custom features in. - pp *tpp.Preprocessor - // Aditional extension. ".tpp" by default. - // For example "file.html.tpp" will be - // first preprocessed TPP and sent back as simple HTML. - ext string - - // The global map accessable from all the - // request so you can keep the states - // between requests. - global any - md *mdx.Markdown - html *htmlx.HTML -} - -// Returns the new Handler with -// specified preprocessor, source path, -// preprocessor extension and the global value. -func NewHandler( - pp *tpp.Preprocessor, - src, ext string, - global any, -) *Handler { - ret := &Handler{} - ret.sourcePath = src - ret.ext = ext - ret.pp = pp - ret.global = global - return ret -} - -// Returns the default tpp.Preprocessor suitable for -// the most needs. -func DefaultPP(mod string) *tpp.Preprocessor { - t := tpp.NewTengo().SetPreCompile(func( - ctx context.Context, - s *tpp.Script, - ) { - s.SetImportDir(mod) - s.SetImports(&ModuleGetter{}) - s.EnableFileImport(true) - - s.Add("__http_request__", ctx.Value(KeyRequest)) - s.Add("__global__", ctx.Value(KeyGlobal)) - s.Add("__markdown__", ctx.Value(KeyMarkdown)) - s.Add("__html__", ctx.Value(KeyHTML)) - }).SetPreCode(func(ctx context.Context) []byte { - return []byte(` - markdown := func(...args) { - pp.write_raw(__markdown__(args...)) - } - http := immutable({ - request : __http_request__ - }) - html := __html__ - context.http = http - context.pp = pp - context.global = __global__ - context.html = html - import("./pre")(context) - `) - }).SetPostCode(func(ctx context.Context) []byte { - return []byte(` - import("./post")(context) - `) - }) - - return tpp.New(t) -} - -func (h *Handler) SetMD(md *mdx.Markdown) *Handler { - h.md = md - return h -} -func (h *Handler) SetHTML(html *htmlx.HTML) *Handler { - h.html = html - return h -} - -func (h *Handler) ServeHTTP( - w http.ResponseWriter, - r *http.Request, -) { - shouldProcess := true - urlPath := r.URL.Path - // Cleaning URL path to prevent injections. - urlPath = path.Clean(urlPath) - urlExt := path.Ext(urlPath) - - filePath := filepath.Join( - filepath.FromSlash(h.sourcePath), - filepath.FromSlash(urlPath), - ) - filePathTpp := filePath + h.ext - - //log.Println("pth:", urlPath, filePathTpp) - file, err := os.Open(filePathTpp) - if err != nil { - shouldProcess = false - file, err = os.Open(filePath) - if err != nil { - http.NotFound(w, r) - return - } - } - - //process := true - fileData, err := io.ReadAll(file) - if err != nil { - http.NotFound(w, r) - return - } - - ctx := context.WithValue( - r.Context(), - KeyRequest, - &Request{ - Request: r, - }, - ) - - ctx = context.WithValue( - ctx, - KeyGlobal, - h.global, - ) - - ctx = context.WithValue( - ctx, - KeyMarkdown, - h.md, - ) - - ctx = context.WithValue( - ctx, - KeyHTML, - h.html, - ) - - // Setting before the code to let it change own - // content type. - contentType := mime.TypeByExtension(urlExt) - w.Header().Set("Content-Type", contentType) - - processedData := fileData - if shouldProcess { - processedData, err = h.pp.Process( - ctx, - true, - filePathTpp, - fileData, - ) - if err != nil { - http.NotFound(w, r) - log.Printf( - "Error: pp.Process(...): %s\n", - err, - ) - return - } - } - - w.Write(processedData) -} diff --git a/httpx/module.go b/httpx/module.go index f7a0765..0e441e6 100644 --- a/httpx/module.go +++ b/httpx/module.go @@ -1,24 +1,2 @@ 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) -} diff --git a/httpx/tool.go b/httpx/tool.go deleted file mode 100644 index 08a60f9..0000000 --- a/httpx/tool.go +++ /dev/null @@ -1,52 +0,0 @@ -package httpx - -import ( - - "github.com/d5/tengo/v2" - //"github.com/d5/tengo/v2/stdlib" - "surdeus.su/util/tpp/mdx" - "surdeus.su/core/cli/mtool" - "net/http" - "log" - //"context" -) - -// Context key type for internal usage. -type CKey string - -const ( - KeyRequest CKey = "http-request" - KeyGlobal = "global" - KeyMarkdown = "markdown" - KeyHTML = "html" -) - -// Simple PHP-like server implementation. -var Tool = mtool.T("tht").Func(func(flags *mtool.Flags) { - var ( - addr, ext, src, mod string - ) - - flags.StringVar(&addr, "addr", ":3000", "address to serve at") - flags.StringVar(&mod, "mod", "./mod", "path to store Tengo modules") - flags.StringVar(&src, "src", "./src", "directory with source files") - flags.StringVar(&ext, "ext", ".tpp", "extension for TPP files") - - flags.Parse() - - - srv := &http.Server{ - Addr: addr, - Handler: NewHandler( - DefaultPP(mod), - src, ext, - map[string] tengo.Object{}, - ).SetMD(mdx.MakeDefaultMarkdown()), - } - - log.Printf("Listening on %q\n", addr) - err := srv.ListenAndServe() - if err != nil { - log.Printf("Error: srv.ListenAndServe(...): %s\n", err) - } -}) diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..78050c5 --- /dev/null +++ b/install.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +go install diff --git a/paths/main.go b/paths/main.go new file mode 100644 index 0000000..8b75c7e --- /dev/null +++ b/paths/main.go @@ -0,0 +1,47 @@ +package paths + +import ( + "github.com/d5/tengo/v2" + "path" +) + +var Module = 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 + } +} + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..bee0218 --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +# xgo + +The Tengo extended modules. + +The module implements Tengo modules and interpreter +with them to make more complicated scripts. + diff --git a/xtool/main.go b/xtool/main.go index 83a3195..62efbc8 100644 --- a/xtool/main.go +++ b/xtool/main.go @@ -21,6 +21,7 @@ import ( import "surdeus.su/core/cli/mtool" import "surdeus.su/core/xgo/cjson" import "surdeus.su/core/xgo/logx" +import "surdeus.su/core/xgo/paths" const ( sourceFileExt = ".xgo" @@ -56,6 +57,7 @@ func Run(flags *mtool.Flags) { modules := stdlib.GetModuleMap(stdlib.AllModuleNames()...) modules.AddBuiltinModule("cjson", cjson.Module) modules.AddBuiltinModule("log", logx.Module) + modules.AddBuiltinModule("paths", paths.Module) if len(iargs) == 0 { // REPL RunREPL(modules, os.Stdin, os.Stdout)