pax_global_header00006660000000000000000000000064137650156770014533gustar00rootroot0000000000000052 comment=ea54b8a1190e194ca066570dceb8e40611455cf0 golang-github-valyala-quicktemplate-1.6.3+ds/000077500000000000000000000000001376501567700211705ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/.gitignore000066400000000000000000000000051376501567700231530ustar00rootroot00000000000000tags golang-github-valyala-quicktemplate-1.6.3+ds/.travis.yml000066400000000000000000000005201376501567700232760ustar00rootroot00000000000000language: go go: - 1.11.x - 1.12.x - 1.13.x - 1.14.x - tip before_install: - go get -u github.com/valyala/quicktemplate/qtc - go generate script: # build test for supported platforms - GOOS=linux go build - GOOS=darwin go build - GOOS=freebsd go build # run tests on a standard platform - go test -v ./... golang-github-valyala-quicktemplate-1.6.3+ds/LICENSE000066400000000000000000000021141376501567700221730ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Aliaksandr Valialkin, VertaMedia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-valyala-quicktemplate-1.6.3+ds/QuickTemplate.xml000066400000000000000000000016371376501567700244710ustar00rootroot00000000000000 golang-github-valyala-quicktemplate-1.6.3+ds/README.md000066400000000000000000000514101376501567700224500ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/valyala/quicktemplate.svg)](https://travis-ci.org/valyala/quicktemplate) [![GoDoc](https://godoc.org/github.com/valyala/quicktemplate?status.svg)](http://godoc.org/github.com/valyala/quicktemplate) [![Go Report Card](https://goreportcard.com/badge/github.com/valyala/quicktemplate)](https://goreportcard.com/report/github.com/valyala/quicktemplate) # quicktemplate A fast, powerful, yet easy to use template engine for Go. Inspired by the [Mako templates](http://www.makotemplates.org/) philosophy. # Features * [Extremely fast](#performance-comparison-with-htmltemplate). Templates are converted into Go code and then compiled. * Quicktemplate syntax is very close to Go - there is no need to learn yet another template language before starting to use quicktemplate. * Almost all bugs are caught during template compilation, so production suffers less from template-related bugs. * Easy to use. See [quickstart](#quick-start) and [examples](https://github.com/valyala/quicktemplate/tree/master/examples) for details. * Powerful. Arbitrary Go code may be embedded into and mixed with templates. Be careful with this power - do not query the database and/or external resources from templates unless you miss the PHP way in Go :) This power is mostly for arbitrary data transformations. * Easy to use template inheritance powered by [Go interfaces](https://golang.org/doc/effective_go.html#interfaces). See [this example](https://github.com/valyala/quicktemplate/tree/master/examples/basicserver) for details. * Templates are compiled into a single binary, so there is no need to copy template files to the server. # Drawbacks * Templates cannot be updated on the fly on the server, since they are compiled into a single binary. Take a look at [fasttemplate](https://github.com/valyala/fasttemplate) if you need a fast template engine for simple dynamically updated templates. [There are ways](https://www.reddit.com/r/golang/comments/f290ja/hot_reloading_with_quicktemplates_sqlc_and/) to dynamically update the templates during development. # Performance comparison with html/template Quicktemplate is more than 20x faster than [html/template](https://golang.org/pkg/html/template/). The following simple template is used in the benchmark: * [html/template version](https://github.com/valyala/quicktemplate/blob/master/testdata/templates/bench.tpl) * [quicktemplate version](https://github.com/valyala/quicktemplate/blob/master/testdata/templates/bench.qtpl) Benchmark results: ``` $ go test -bench='Benchmark(Quick|HTML)Template' -benchmem github.com/valyala/quicktemplate/tests BenchmarkQuickTemplate1-4 10000000 120 ns/op 0 B/op 0 allocs/op BenchmarkQuickTemplate10-4 3000000 441 ns/op 0 B/op 0 allocs/op BenchmarkQuickTemplate100-4 300000 3945 ns/op 0 B/op 0 allocs/op BenchmarkHTMLTemplate1-4 500000 2501 ns/op 752 B/op 23 allocs/op BenchmarkHTMLTemplate10-4 100000 12442 ns/op 3521 B/op 117 allocs/op BenchmarkHTMLTemplate100-4 10000 123392 ns/op 34498 B/op 1152 allocs/op ``` [goTemplateBenchmark](https://github.com/SlinSo/goTemplateBenchmark) compares QuickTemplate with numerous Go templating packages. QuickTemplate performs favorably. # Security * All template placeholders are HTML-escaped by default. * Template placeholders for JSON strings prevent from ``-based XSS attacks: ```qtpl {% func FailedXSS() %} {% endfunc %} ``` # Examples See [examples](https://github.com/valyala/quicktemplate/tree/master/examples). # Quick start First of all, install the `quicktemplate` package and [quicktemplate compiler](https://github.com/valyala/quicktemplate/tree/master/qtc) (`qtc`): ``` go get -u github.com/valyala/quicktemplate go get -u github.com/valyala/quicktemplate/qtc ``` If you using `go generate`, you just need put following into your `main.go` Important: please specify your own folder (-dir) to generate template file ``` //go:generate go get -u github.com/valyala/quicktemplate/qtc //go:generate qtc -dir=app/views ``` Let's start with a minimal template example: ```qtpl All text outside function templates is treated as comments, i.e. it is just ignored by quicktemplate compiler (`qtc`). It is for humans. Hello is a simple template function. {% func Hello(name string) %} Hello, {%s name %}! {% endfunc %} ``` Save this file into a `templates` folder under the name `hello.qtpl` and run `qtc` inside this folder. If everything went OK, `hello.qtpl.go` file should appear in the `templates` folder. This file contains Go code for `hello.qtpl`. Let's use it! Create a file main.go outside `templates` folder and put the following code there: ```go package main import ( "fmt" "./templates" ) func main() { fmt.Printf("%s\n", templates.Hello("Foo")) fmt.Printf("%s\n", templates.Hello("Bar")) } ``` Then issue `go run`. If everything went OK, you'll see something like this: ``` Hello, Foo! Hello, Bar! ``` Let's create more a complex template which calls other template functions, contains loops, conditions, breaks, continues and returns. Put the following template into `templates/greetings.qtpl`: ```qtpl Greetings greets up to 42 names. It also greets John differently comparing to others. {% func Greetings(names []string) %} {% if len(names) == 0 %} Nobody to greet :( {% return %} {% endif %} {% for i, name := range names %} {% if i == 42 %} I'm tired to greet so many people... {% break %} {% elseif name == "John" %} {%= sayHi("Mr. " + name) %} {% continue %} {% else %} {%= Hello(name) %} {% endif %} {% endfor %} {% endfunc %} sayHi is unexported, since it starts with lowercase letter. {% func sayHi(name string) %} Hi, {%s name %} {% endfunc %} Note that every template file may contain an arbitrary number of template functions. For instance, this file contains Greetings and sayHi functions. ``` Run `qtc` inside `templates` folder. Now the folder should contain two files with Go code: `hello.qtpl.go` and `greetings.qtpl.go`. These files form a single `templates` Go package. Template functions and other template stuff is shared between template files located in the same folder. So `Hello` template function may be used inside `greetings.qtpl` while it is defined in `hello.qtpl`. Moreover, the folder may contain ordinary Go files, so its contents may be used inside templates and vice versa. The package name inside template files may be overriden with `{% package packageName %}`. Now put the following code into `main.go`: ```go package main import ( "bytes" "fmt" "./templates" ) func main() { names := []string{"Kate", "Go", "John", "Brad"} // qtc creates Write* function for each template function. // Such functions accept io.Writer as first parameter: var buf bytes.Buffer templates.WriteGreetings(&buf, names) fmt.Printf("buf=\n%s", buf.Bytes()) } ``` Careful readers may notice different output tags were used in these templates: `{%s name %}` and `{%= Hello(name) %}`. What's the difference? The `{%s x %}` is used for printing HTML-safe strings, while `{%= F() %}` is used for embedding template function calls. Quicktemplate supports also other output tags: * `{%d int %}` and `{%dl int64 %}` `{%dul uint64 %}` for integers. * `{%f float %}` for float64. Floating point precision may be set via `{%f.precision float %}`. For example, `{%f.2 1.2345 %}` outputs `1.23`. * `{%z bytes %}` for byte slices. * `{%q str %}` and `{%qz bytes %}` for JSON-compatible quoted strings. * `{%j str %}` and `{%jz bytes %}` for embedding str into a JSON string. Unlike `{%q str %}`, it doesn't quote the string. * `{%u str %}` and `{%uz bytes %}` for [URL encoding](https://en.wikipedia.org/wiki/Percent-encoding) the given str. * `{%v anything %}` is equivalent to `%v` in [printf-like functions](https://golang.org/pkg/fmt/). All the output tags except `{%= F() %}` produce HTML-safe output, i.e. they escape `<` to `<`, `>` to `>`, etc. If you don't want HTML-safe output, then just put `=` after the tag. For example: `{%s= "

This h1 won't be escaped

" %}`. As you may notice `{%= F() %}` and `{%s= F() %}` produce the same output for `{% func F() %}`. But the first one is optimized for speed - it avoids memory allocations and copies. It is therefore recommended to stick to it when embedding template function calls. Additionally, the following extensions are supported for `{%= F() %}`: * `{%=h F() %}` produces html-escaped output. * `{%=u F() %}` produces [URL-encoded](https://en.wikipedia.org/wiki/Percent-encoding) output. * `{%=q F() %}` produces quoted json string. * `{%=j F() %}` produces json string without quotes. * `{%=uh F() %}` produces html-safe URL-encoded output. * `{%=qh F() %}` produces html-safe quoted json string. * `{%=jh F() %}` produces html-safe json string without quotes. All output tags except `{%= F() %}` family may contain arbitrary valid Go expressions instead of just an identifier. For example: ```qtpl Import fmt for fmt.Sprintf() {% import "fmt" %} FmtFunc uses fmt.Sprintf() inside output tag {% func FmtFunc(s string) %} {%s fmt.Sprintf("FmtFunc accepted %q string", s) %} {% endfunc %} ``` There are other useful tags supported by quicktemplate: * `{% comment %}` ```qtpl {% comment %} This is a comment. It won't trap into the output. It may contain {% arbitrary tags %}. They are just ignored. {% endcomment %} ``` * `{% plain %}` ```qtpl {% plain %} Tags will {% trap into %} the output {% unmodified %}. Plain block may contain invalid and {% incomplete tags. {% endplain %} ``` * `{% collapsespace %}` ```qtpl {% collapsespace %}
space between lines
and {%s "tags" %}
is collapsed into a single space unless{% newline %}or{% space %}is used
{% endcollapsespace %} ``` Is converted into: ```
space between lines
and tags
is collapsed into a single space unless or is used
``` * `{% stripspace %}` ```qtpl {% stripspace %}
space between lines
and {%s " tags" %}
is removed unless{% newline %}or{% space %}is used
{% endstripspace %} ``` Is converted into: ```
space between lines
and tags
is removed unless or is used
``` * It is possible removing whitespace before and after the tag by adding `-` after `{%` or prepending `%}` with `-`. For example: ```qtpl var sum int {%- for i := 1; i <= 3; i++ -%} sum += {%d i %} {%- endfor -%} return sum ``` Is converted into: ``` var sum int sum += 1 sum += 2 sum += 3 return sum ``` * `{% switch %}`, `{% case %}` and `{% default %}`: ```qtpl 1 + 1 = {% switch 1+1 %} {% case 2 %} 2? {% case 42 %} 42! {% default %} I don't know :( {% endswitch %} ``` * `{% code %}`: ```qtpl {% code // arbitrary Go code may be embedded here! type FooArg struct { Name string Age int } %} ``` * `{% package %}`: ```qtpl Override default package name with the custom name {% package customPackageName %} ``` * `{% import %}`: ```qtpl Import external packages. {% import "foo/bar" %} {% import ( "foo" bar "baz/baa" ) %} ``` * `{% cat "/path/to/file" %}`: ```qtpl Cat emits the given file contents as a plaintext: {% func passwords() %} /etc/passwd contents: {% cat "/etc/passwd" %} {% endfunc %} ``` * `{% interface %}`: ```qtpl Interfaces allow powerful templates' inheritance {% interface Page { Title() Body(s string, n int) Footer() } %} PrintPage prints Page {% func PrintPage(p Page) %} {%= p.Title() %}
{%= p.Body("foo", 42) %}
{%= p.Footer() %}
{% endfunc %} Base page implementation {% code type BasePage struct { TitleStr string FooterStr string } %} {% func (bp *BasePage) Title() %}{%s bp.TitleStr %}{% endfunc %} {% func (bp *BasePage) Body(s string, n int) %} s={%q s %}, n={%d n %} {% endfunc %} {% func (bp *BasePage) Footer() %}{%s bp.FooterStr %}{% endfunc %} Main page implementation {% code type MainPage struct { // inherit from BasePage BasePage // real body for main page BodyStr string } %} Override only Body Title and Footer are used from BasePage. {% func (mp *MainPage) Body(s string, n int) %}
main body: {%s mp.BodyStr %}
base body: {%= mp.BasePage.Body(s, n) %}
{% endfunc %} ``` See [basicserver example](https://github.com/valyala/quicktemplate/tree/master/examples/basicserver) for more details. # Performance optimization tips * Prefer calling `WriteFoo` instead of `Foo` when generating template output for `{% func Foo() %}`. This avoids unnesessary memory allocation and a copy for a `string` returned from `Foo()`. * Prefer `{%= Foo() %}` instead of `{%s= Foo() %}` when embedding a function template `{% func Foo() %}`. Though both approaches generate identical output, the first approach is optimized for speed. * Prefer using existing output tags instead of passing `fmt.Sprintf` to `{%s %}`. For instance, use `{%d num %}` instead of `{%s fmt.Sprintf("%d", num) %}`, because the first approach is optimized for speed. * Prefer using specific output tags instead of generic output tag `{%v %}`. For example, use `{%s str %}` instead of `{%v str %}`, since specific output tags are optimized for speed. * Prefer creating custom function templates instead of composing complex strings by hands before passing them to `{%s %}`. For instance, the first approach is slower than the second one: ```qtpl {% func Foo(n int) %} {% code // construct complex string complexStr := "" for i := 0; i < n; i++ { complexStr += fmt.Sprintf("num %d,", i) } %} complex string = {%s= complexStr %} {% endfunc %} ``` ```qtpl {% func Foo(n int) %} complex string = {%= complexStr(n) %} {% endfunc %} // Wrap complexStr func into stripspace for stripping unnesessary space // between tags and lines. {% stripspace %} {% func complexStr(n int) %} {% for i := 0; i < n; i++ %} num{% space %}{%d i %}{% newline %} {% endfor %} {% endfunc %} {% endstripspace %} ``` * Make sure that the `io.Writer` passed to `Write*` functions is [buffered](https://golang.org/pkg/bufio/#Writer). This will minimize the number of `write` [syscalls](https://en.wikipedia.org/wiki/System_call), which may be quite expensive. Note: There is no need to wrap [fasthttp.RequestCtx](https://godoc.org/github.com/valyala/fasthttp#RequestCtx) into [bufio.Writer](https://golang.org/pkg/bufio/#Writer), since it is already buffered. * [Profile](http://blog.golang.org/profiling-go-programs) your programs for memory allocations and fix the most demanding functions based on the output of `go tool pprof --alloc_objects`. # Use cases While the main quicktemplate purpose is generating HTML, it may be used for generating other data too. For example, JSON and XML marshalling may be easily implemented with quicktemplate: ```qtpl {% code type MarshalRow struct { Msg string N int } type MarshalData struct { Foo int Bar string Rows []MarshalRow } %} // JSON marshaling {% stripspace %} {% func (d *MarshalData) JSON() %} { "Foo": {%d d.Foo %}, "Bar": {%q= d.Bar %}, "Rows":[ {% for i, r := range d.Rows %} { "Msg": {%q= r.Msg %}, "N": {%d r.N %} } {% if i + 1 < len(d.Rows) %},{% endif %} {% endfor %} ] } {% endfunc %} {% endstripspace %} // XML marshalling {% stripspace %} {% func (d *MarshalData) XML() %} {%d d.Foo %} {%s d.Bar %} {% for _, r := range d.Rows %} {%s r.Msg %} {%d r.N %} {% endfor %} {% endfunc %} {% endstripspace %} ``` Usually, marshalling built with quicktemplate works faster than the marshalling implemented via standard [encoding/json](https://golang.org/pkg/encoding/json/) and [encoding/xml](https://golang.org/pkg/encoding/xml/). See the corresponding benchmark results: ``` go test -bench=Marshal -benchmem github.com/valyala/quicktemplate/tests BenchmarkMarshalJSONStd1-4 3000000 480 ns/op 8 B/op 1 allocs/op BenchmarkMarshalJSONStd10-4 1000000 1842 ns/op 8 B/op 1 allocs/op BenchmarkMarshalJSONStd100-4 100000 15820 ns/op 8 B/op 1 allocs/op BenchmarkMarshalJSONStd1000-4 10000 159327 ns/op 59 B/op 1 allocs/op BenchmarkMarshalJSONQuickTemplate1-4 10000000 162 ns/op 0 B/op 0 allocs/op BenchmarkMarshalJSONQuickTemplate10-4 2000000 748 ns/op 0 B/op 0 allocs/op BenchmarkMarshalJSONQuickTemplate100-4 200000 6572 ns/op 0 B/op 0 allocs/op BenchmarkMarshalJSONQuickTemplate1000-4 20000 66784 ns/op 29 B/op 0 allocs/op BenchmarkMarshalXMLStd1-4 1000000 1652 ns/op 2 B/op 2 allocs/op BenchmarkMarshalXMLStd10-4 200000 7533 ns/op 11 B/op 11 allocs/op BenchmarkMarshalXMLStd100-4 20000 65763 ns/op 195 B/op 101 allocs/op BenchmarkMarshalXMLStd1000-4 2000 663373 ns/op 3522 B/op 1002 allocs/op BenchmarkMarshalXMLQuickTemplate1-4 10000000 145 ns/op 0 B/op 0 allocs/op BenchmarkMarshalXMLQuickTemplate10-4 3000000 597 ns/op 0 B/op 0 allocs/op BenchmarkMarshalXMLQuickTemplate100-4 300000 5833 ns/op 0 B/op 0 allocs/op BenchmarkMarshalXMLQuickTemplate1000-4 30000 53000 ns/op 32 B/op 0 allocs/op ``` # FAQ * *Why is the quicktemplate syntax incompatible with [html/template](https://golang.org/pkg/html/template/)?* Because `html/template` syntax isn't expressive enough for `quicktemplate`. * *What's the difference between quicktemplate and [ego](https://github.com/benbjohnson/ego)?* `Ego` is similar to `quicktemplate` in the sense it converts templates into Go code. But it misses the following stuff, which makes `quicktemplate` so powerful and easy to use: * Defining multiple function templates in a single template file. * Embedding function templates inside other function templates. * Template interfaces, inheritance and overriding. See [this example](https://github.com/valyala/quicktemplate/tree/master/examples/basicserver) for details. * Top-level comments outside function templates. * Template packages. * Combining arbitrary Go files with template files in template packages. * Performance optimizations. * *What's the difference between quicktemplate and [gorazor](https://github.com/sipin/gorazor)?* `Gorazor` is similar to `quicktemplate` in the sense it converts templates into Go code. But it misses the following useful features: * Clear syntax instead of hard-to-understand magic stuff related to template arguments, template inheritance and embedding function templates into other templates. * *Is there a syntax highlighting for qtpl files?* Yes - see [this issue](https://github.com/valyala/quicktemplate/issues/19) for details. If you are using JetBrains products (syntax highlighting and autocomplete): * cd [JetBrains settings directory](https://intellij-support.jetbrains.com/hc/en-us/articles/206544519-Directories-used-by-the-IDE-to-store-settings-caches-plugins-and-logs) * mkdir -p filetypes && cd filetypes * curl https://raw.githubusercontent.com/valyala/quicktemplate/master/QuickTemplate.xml >> QuickTemplate.xml * Restart your IDE * *I didn't find an answer for my question here.* Try exploring [these questions](https://github.com/valyala/quicktemplate/issues?q=label%3Aquestion). golang-github-valyala-quicktemplate-1.6.3+ds/bytebuffer.go000066400000000000000000000022041376501567700236520ustar00rootroot00000000000000package quicktemplate import ( "github.com/valyala/bytebufferpool" ) // ByteBuffer implements io.Writer on top of byte slice. // // Recycle byte buffers via AcquireByteBuffer and ReleaseByteBuffer // in order to reduce memory allocations. // // Deprecated: use github.com/valyala/bytebufferpool instead. type ByteBuffer bytebufferpool.ByteBuffer // Write implements io.Writer. func (b *ByteBuffer) Write(p []byte) (int, error) { return bb(b).Write(p) } // Reset resets the byte buffer. func (b *ByteBuffer) Reset() { bb(b).Reset() } // AcquireByteBuffer returns new ByteBuffer from the pool. // // Return unneeded buffers to the pool by calling ReleaseByteBuffer // in order to reduce memory allocations. func AcquireByteBuffer() *ByteBuffer { return (*ByteBuffer)(byteBufferPool.Get()) } // ReleaseByteBuffer retruns byte buffer to the pool. // // Do not access byte buffer after returning it to the pool, // otherwise data races may occur. func ReleaseByteBuffer(b *ByteBuffer) { byteBufferPool.Put(bb(b)) } func bb(b *ByteBuffer) *bytebufferpool.ByteBuffer { return (*bytebufferpool.ByteBuffer)(b) } var byteBufferPool bytebufferpool.Pool golang-github-valyala-quicktemplate-1.6.3+ds/doc.go000066400000000000000000000002311376501567700222600ustar00rootroot00000000000000/* Package quicktemplate provides fast and powerful template engine. See https://github.com/valyala/quicktemplate for details. */ package quicktemplate golang-github-valyala-quicktemplate-1.6.3+ds/examples/000077500000000000000000000000001376501567700230065ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/examples/README.md000066400000000000000000000002401376501567700242610ustar00rootroot00000000000000[quicktemplate](https://github.com/valyala/quicktemplate) examples: * [basic usage](https://github.com/valyala/quicktemplate/tree/master/examples/basicserver) golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/000077500000000000000000000000001376501567700253165ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/.gitignore000066400000000000000000000000141376501567700273010ustar00rootroot00000000000000basicserver golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/Makefile000066400000000000000000000003411376501567700267540ustar00rootroot00000000000000run: build ./basicserver build: update go build -o basicserver update: go get -u github.com/valyala/fasthttp go get -u github.com/valyala/quicktemplate/qtc generate: update go generate all: update generate build run golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/README.md000066400000000000000000000005671376501567700266050ustar00rootroot00000000000000An example demonstrating basic usage of [quicktemplate](https://github.com/valyala/quicktemplate) as template engine and [fasthttp](https://github.com/valyala/fasthttp) as http server. Just run `make` to see the example in action. Run `make generate run` after modifying [template files](https://github.com/valyala/quicktemplate/tree/master/examples/basicserver/templates). golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/main.go000066400000000000000000000032711376501567700265740ustar00rootroot00000000000000// The following line is needed for generating go code from templates // with `go generate`. // See https://blog.golang.org/generate for more info. // Quicktemplate compiler (qtc) must be installed before running // `go generate`: // // go get -u github.com/valyala/quicktemplate/qtc // //go:generate qtc -dir=templates package main import ( "fmt" "log" "math/rand" "github.com/valyala/fasthttp" "github.com/valyala/quicktemplate/examples/basicserver/templates" ) func main() { log.Printf("starting the server at http://localhost:8080 ...") err := fasthttp.ListenAndServe("localhost:8080", requestHandler) if err != nil { log.Fatalf("unexpected error in server: %s", err) } } func requestHandler(ctx *fasthttp.RequestCtx) { switch string(ctx.Path()) { case "/": mainPageHandler(ctx) case "/table": tablePageHandler(ctx) default: errorPageHandler(ctx) } ctx.SetContentType("text/html; charset=utf-8") } func mainPageHandler(ctx *fasthttp.RequestCtx) { p := &templates.MainPage{ CTX: ctx, } templates.WritePageTemplate(ctx, p) } func tablePageHandler(ctx *fasthttp.RequestCtx) { rowsCount := ctx.QueryArgs().GetUintOrZero("rowsCount") if rowsCount == 0 { rowsCount = 10 } p := &templates.TablePage{ Rows: generateRows(rowsCount), } templates.WritePageTemplate(ctx, p) } func errorPageHandler(ctx *fasthttp.RequestCtx) { p := &templates.ErrorPage{ Path: ctx.Path(), } templates.WritePageTemplate(ctx, p) ctx.SetStatusCode(fasthttp.StatusBadRequest) } func generateRows(rowsCount int) []string { var rows []string for i := 0; i < rowsCount; i++ { r := fmt.Sprintf("row %d", i) if rand.Intn(20) == 0 { r = "bingo" } rows = append(rows, r) } return rows } golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/000077500000000000000000000000001376501567700273145ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/README.md000066400000000000000000000002301376501567700305660ustar00rootroot00000000000000Template files with the corresponding compiled files. Compiled files are generated by [qtc](https://github.com/valyala/quicktemplate/tree/master/qtc). golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/basepage.qtpl000066400000000000000000000012201376501567700317600ustar00rootroot00000000000000This is a base page template. All the other template pages implement this interface. {% interface Page { Title() Body() } %} Page prints a page implementing Page interface. {% func PageTemplate(p Page) %} {%= p.Title() %}
return to main page
{%= p.Body() %} {% endfunc %} Base page implementation. Other pages may inherit from it if they need overriding only certain Page methods {% code type BasePage struct {} %} {% func (p *BasePage) Title() %}This is a base title{% endfunc %} {% func (p *BasePage) Body() %}This is a base body{% endfunc %} golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/basepage.qtpl.go000066400000000000000000000131221376501567700323700ustar00rootroot00000000000000// Code generated by qtc from "basepage.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. // This is a base page template. All the other template pages implement this interface. // //line examples/basicserver/templates/basepage.qtpl:3 package templates //line examples/basicserver/templates/basepage.qtpl:3 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line examples/basicserver/templates/basepage.qtpl:3 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line examples/basicserver/templates/basepage.qtpl:4 type Page interface { //line examples/basicserver/templates/basepage.qtpl:4 Title() string //line examples/basicserver/templates/basepage.qtpl:4 StreamTitle(qw422016 *qt422016.Writer) //line examples/basicserver/templates/basepage.qtpl:4 WriteTitle(qq422016 qtio422016.Writer) //line examples/basicserver/templates/basepage.qtpl:4 Body() string //line examples/basicserver/templates/basepage.qtpl:4 StreamBody(qw422016 *qt422016.Writer) //line examples/basicserver/templates/basepage.qtpl:4 WriteBody(qq422016 qtio422016.Writer) //line examples/basicserver/templates/basepage.qtpl:4 } // Page prints a page implementing Page interface. //line examples/basicserver/templates/basepage.qtpl:12 func StreamPageTemplate(qw422016 *qt422016.Writer, p Page) { //line examples/basicserver/templates/basepage.qtpl:12 qw422016.N().S(` `) //line examples/basicserver/templates/basepage.qtpl:15 p.StreamTitle(qw422016) //line examples/basicserver/templates/basepage.qtpl:15 qw422016.N().S(`
return to main page
`) //line examples/basicserver/templates/basepage.qtpl:21 p.StreamBody(qw422016) //line examples/basicserver/templates/basepage.qtpl:21 qw422016.N().S(` `) //line examples/basicserver/templates/basepage.qtpl:24 } //line examples/basicserver/templates/basepage.qtpl:24 func WritePageTemplate(qq422016 qtio422016.Writer, p Page) { //line examples/basicserver/templates/basepage.qtpl:24 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/basepage.qtpl:24 StreamPageTemplate(qw422016, p) //line examples/basicserver/templates/basepage.qtpl:24 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/basepage.qtpl:24 } //line examples/basicserver/templates/basepage.qtpl:24 func PageTemplate(p Page) string { //line examples/basicserver/templates/basepage.qtpl:24 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/basepage.qtpl:24 WritePageTemplate(qb422016, p) //line examples/basicserver/templates/basepage.qtpl:24 qs422016 := string(qb422016.B) //line examples/basicserver/templates/basepage.qtpl:24 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/basepage.qtpl:24 return qs422016 //line examples/basicserver/templates/basepage.qtpl:24 } // Base page implementation. Other pages may inherit from it if they need // overriding only certain Page methods //line examples/basicserver/templates/basepage.qtpl:29 type BasePage struct{} //line examples/basicserver/templates/basepage.qtpl:30 func (p *BasePage) StreamTitle(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/basepage.qtpl:30 qw422016.N().S(`This is a base title`) //line examples/basicserver/templates/basepage.qtpl:30 } //line examples/basicserver/templates/basepage.qtpl:30 func (p *BasePage) WriteTitle(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/basepage.qtpl:30 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/basepage.qtpl:30 p.StreamTitle(qw422016) //line examples/basicserver/templates/basepage.qtpl:30 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/basepage.qtpl:30 } //line examples/basicserver/templates/basepage.qtpl:30 func (p *BasePage) Title() string { //line examples/basicserver/templates/basepage.qtpl:30 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/basepage.qtpl:30 p.WriteTitle(qb422016) //line examples/basicserver/templates/basepage.qtpl:30 qs422016 := string(qb422016.B) //line examples/basicserver/templates/basepage.qtpl:30 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/basepage.qtpl:30 return qs422016 //line examples/basicserver/templates/basepage.qtpl:30 } //line examples/basicserver/templates/basepage.qtpl:31 func (p *BasePage) StreamBody(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/basepage.qtpl:31 qw422016.N().S(`This is a base body`) //line examples/basicserver/templates/basepage.qtpl:31 } //line examples/basicserver/templates/basepage.qtpl:31 func (p *BasePage) WriteBody(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/basepage.qtpl:31 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/basepage.qtpl:31 p.StreamBody(qw422016) //line examples/basicserver/templates/basepage.qtpl:31 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/basepage.qtpl:31 } //line examples/basicserver/templates/basepage.qtpl:31 func (p *BasePage) Body() string { //line examples/basicserver/templates/basepage.qtpl:31 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/basepage.qtpl:31 p.WriteBody(qb422016) //line examples/basicserver/templates/basepage.qtpl:31 qs422016 := string(qb422016.B) //line examples/basicserver/templates/basepage.qtpl:31 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/basepage.qtpl:31 return qs422016 //line examples/basicserver/templates/basepage.qtpl:31 } golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/errorpage.qtpl000066400000000000000000000005541376501567700322100ustar00rootroot00000000000000// Error page template. Implements BasePage methods. {% code type ErrorPage struct { // inherit from base page, so its' title is used in error page. BasePage // error path Path []byte } %} {% func (p *ErrorPage) Body() %}

Error page

Unsupported path {%z p.Path %}. Base page body: {%= p.BasePage.Body() %} {% endfunc %} golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/errorpage.qtpl.go000066400000000000000000000045041376501567700326130ustar00rootroot00000000000000// Code generated by qtc from "errorpage.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. // Error page template. Implements BasePage methods. // //line examples/basicserver/templates/errorpage.qtpl:3 package templates //line examples/basicserver/templates/errorpage.qtpl:3 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line examples/basicserver/templates/errorpage.qtpl:3 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line examples/basicserver/templates/errorpage.qtpl:4 type ErrorPage struct { // inherit from base page, so its' title is used in error page. BasePage // error path Path []byte } //line examples/basicserver/templates/errorpage.qtpl:14 func (p *ErrorPage) StreamBody(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/errorpage.qtpl:14 qw422016.N().S(`

Error page

Unsupported path `) //line examples/basicserver/templates/errorpage.qtpl:17 qw422016.E().Z(p.Path) //line examples/basicserver/templates/errorpage.qtpl:17 qw422016.N().S(`. Base page body: `) //line examples/basicserver/templates/errorpage.qtpl:19 p.BasePage.StreamBody(qw422016) //line examples/basicserver/templates/errorpage.qtpl:19 qw422016.N().S(` `) //line examples/basicserver/templates/errorpage.qtpl:20 } //line examples/basicserver/templates/errorpage.qtpl:20 func (p *ErrorPage) WriteBody(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/errorpage.qtpl:20 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/errorpage.qtpl:20 p.StreamBody(qw422016) //line examples/basicserver/templates/errorpage.qtpl:20 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/errorpage.qtpl:20 } //line examples/basicserver/templates/errorpage.qtpl:20 func (p *ErrorPage) Body() string { //line examples/basicserver/templates/errorpage.qtpl:20 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/errorpage.qtpl:20 p.WriteBody(qb422016) //line examples/basicserver/templates/errorpage.qtpl:20 qs422016 := string(qb422016.B) //line examples/basicserver/templates/errorpage.qtpl:20 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/errorpage.qtpl:20 return qs422016 //line examples/basicserver/templates/errorpage.qtpl:20 } golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/mainpage.qtpl000066400000000000000000000011331376501567700317750ustar00rootroot00000000000000// Main page template. Implements BasePage methods. {% import "github.com/valyala/fasthttp" %} {% code type MainPage struct { CTX *fasthttp.RequestCtx } %} {% func (p *MainPage) Title() %} This is the main page {% endfunc %} {% func (p *MainPage) Body() %}

Main page

Click links below:
Some info about you:
IP: {%s p.CTX.RemoteIP().String() %}
User-Agent: {%z p.CTX.UserAgent() %}
{% endfunc %} golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/mainpage.qtpl.go000066400000000000000000000074171376501567700324140ustar00rootroot00000000000000// Code generated by qtc from "mainpage.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. // Main page template. Implements BasePage methods. // //line examples/basicserver/templates/mainpage.qtpl:3 package templates //line examples/basicserver/templates/mainpage.qtpl:3 import "github.com/valyala/fasthttp" //line examples/basicserver/templates/mainpage.qtpl:5 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line examples/basicserver/templates/mainpage.qtpl:5 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line examples/basicserver/templates/mainpage.qtpl:6 type MainPage struct { CTX *fasthttp.RequestCtx } //line examples/basicserver/templates/mainpage.qtpl:12 func (p *MainPage) StreamTitle(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/mainpage.qtpl:12 qw422016.N().S(` This is the main page `) //line examples/basicserver/templates/mainpage.qtpl:14 } //line examples/basicserver/templates/mainpage.qtpl:14 func (p *MainPage) WriteTitle(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/mainpage.qtpl:14 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/mainpage.qtpl:14 p.StreamTitle(qw422016) //line examples/basicserver/templates/mainpage.qtpl:14 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/mainpage.qtpl:14 } //line examples/basicserver/templates/mainpage.qtpl:14 func (p *MainPage) Title() string { //line examples/basicserver/templates/mainpage.qtpl:14 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/mainpage.qtpl:14 p.WriteTitle(qb422016) //line examples/basicserver/templates/mainpage.qtpl:14 qs422016 := string(qb422016.B) //line examples/basicserver/templates/mainpage.qtpl:14 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/mainpage.qtpl:14 return qs422016 //line examples/basicserver/templates/mainpage.qtpl:14 } //line examples/basicserver/templates/mainpage.qtpl:17 func (p *MainPage) StreamBody(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/mainpage.qtpl:17 qw422016.N().S(`

Main page

Click links below:
Some info about you:
IP: `) //line examples/basicserver/templates/mainpage.qtpl:28 qw422016.E().S(p.CTX.RemoteIP().String()) //line examples/basicserver/templates/mainpage.qtpl:28 qw422016.N().S(`
User-Agent: `) //line examples/basicserver/templates/mainpage.qtpl:29 qw422016.E().Z(p.CTX.UserAgent()) //line examples/basicserver/templates/mainpage.qtpl:29 qw422016.N().S(`
`) //line examples/basicserver/templates/mainpage.qtpl:31 } //line examples/basicserver/templates/mainpage.qtpl:31 func (p *MainPage) WriteBody(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/mainpage.qtpl:31 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/mainpage.qtpl:31 p.StreamBody(qw422016) //line examples/basicserver/templates/mainpage.qtpl:31 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/mainpage.qtpl:31 } //line examples/basicserver/templates/mainpage.qtpl:31 func (p *MainPage) Body() string { //line examples/basicserver/templates/mainpage.qtpl:31 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/mainpage.qtpl:31 p.WriteBody(qb422016) //line examples/basicserver/templates/mainpage.qtpl:31 qs422016 := string(qb422016.B) //line examples/basicserver/templates/mainpage.qtpl:31 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/mainpage.qtpl:31 return qs422016 //line examples/basicserver/templates/mainpage.qtpl:31 } golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/tablepage.qtpl000066400000000000000000000021401376501567700321370ustar00rootroot00000000000000// Table page template. Implements BasePage methods. {% code type TablePage struct { Rows []string } %} {% func (p *TablePage) Title() %} This is table page {% endfunc %} {% func (p *TablePage) Body() %}

Table page

{%= p.form() %} {% if len(p.Rows) == 0 %} No rows. Click here. {% else %} {%= emitRows(p.Rows) %}
{% endif %} {% endfunc %} {% func emitRows(rows []string) %} # value {% for n, r := range rows %} {% if r == "bingo" %}

BINGO!

{% return %} {% elseif n == 42 %} 42 rows already generated {% break %} {% endif %} {%d n+1 %} {%s r %} {% endfor %} No bingo found {% endfunc %} {% func (p *TablePage) form() %}
Rows:
{% endfunc %} golang-github-valyala-quicktemplate-1.6.3+ds/examples/basicserver/templates/tablepage.qtpl.go000066400000000000000000000211731376501567700325520ustar00rootroot00000000000000// Code generated by qtc from "tablepage.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. // Table page template. Implements BasePage methods. // //line examples/basicserver/templates/tablepage.qtpl:3 package templates //line examples/basicserver/templates/tablepage.qtpl:3 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line examples/basicserver/templates/tablepage.qtpl:3 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line examples/basicserver/templates/tablepage.qtpl:4 type TablePage struct { Rows []string } //line examples/basicserver/templates/tablepage.qtpl:10 func (p *TablePage) StreamTitle(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/tablepage.qtpl:10 qw422016.N().S(` This is table page `) //line examples/basicserver/templates/tablepage.qtpl:12 } //line examples/basicserver/templates/tablepage.qtpl:12 func (p *TablePage) WriteTitle(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/tablepage.qtpl:12 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/tablepage.qtpl:12 p.StreamTitle(qw422016) //line examples/basicserver/templates/tablepage.qtpl:12 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/tablepage.qtpl:12 } //line examples/basicserver/templates/tablepage.qtpl:12 func (p *TablePage) Title() string { //line examples/basicserver/templates/tablepage.qtpl:12 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/tablepage.qtpl:12 p.WriteTitle(qb422016) //line examples/basicserver/templates/tablepage.qtpl:12 qs422016 := string(qb422016.B) //line examples/basicserver/templates/tablepage.qtpl:12 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/tablepage.qtpl:12 return qs422016 //line examples/basicserver/templates/tablepage.qtpl:12 } //line examples/basicserver/templates/tablepage.qtpl:15 func (p *TablePage) StreamBody(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/tablepage.qtpl:15 qw422016.N().S(`

Table page

`) //line examples/basicserver/templates/tablepage.qtpl:18 p.streamform(qw422016) //line examples/basicserver/templates/tablepage.qtpl:18 qw422016.N().S(` `) //line examples/basicserver/templates/tablepage.qtpl:20 if len(p.Rows) == 0 { //line examples/basicserver/templates/tablepage.qtpl:20 qw422016.N().S(` No rows. Click here. `) //line examples/basicserver/templates/tablepage.qtpl:22 } else { //line examples/basicserver/templates/tablepage.qtpl:22 qw422016.N().S(` `) //line examples/basicserver/templates/tablepage.qtpl:24 streamemitRows(qw422016, p.Rows) //line examples/basicserver/templates/tablepage.qtpl:24 qw422016.N().S(`
`) //line examples/basicserver/templates/tablepage.qtpl:26 } //line examples/basicserver/templates/tablepage.qtpl:26 qw422016.N().S(` `) //line examples/basicserver/templates/tablepage.qtpl:27 } //line examples/basicserver/templates/tablepage.qtpl:27 func (p *TablePage) WriteBody(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/tablepage.qtpl:27 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/tablepage.qtpl:27 p.StreamBody(qw422016) //line examples/basicserver/templates/tablepage.qtpl:27 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/tablepage.qtpl:27 } //line examples/basicserver/templates/tablepage.qtpl:27 func (p *TablePage) Body() string { //line examples/basicserver/templates/tablepage.qtpl:27 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/tablepage.qtpl:27 p.WriteBody(qb422016) //line examples/basicserver/templates/tablepage.qtpl:27 qs422016 := string(qb422016.B) //line examples/basicserver/templates/tablepage.qtpl:27 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/tablepage.qtpl:27 return qs422016 //line examples/basicserver/templates/tablepage.qtpl:27 } //line examples/basicserver/templates/tablepage.qtpl:29 func streamemitRows(qw422016 *qt422016.Writer, rows []string) { //line examples/basicserver/templates/tablepage.qtpl:29 qw422016.N().S(` # value `) //line examples/basicserver/templates/tablepage.qtpl:35 for n, r := range rows { //line examples/basicserver/templates/tablepage.qtpl:35 qw422016.N().S(` `) //line examples/basicserver/templates/tablepage.qtpl:36 if r == "bingo" { //line examples/basicserver/templates/tablepage.qtpl:36 qw422016.N().S(`

BINGO!

`) //line examples/basicserver/templates/tablepage.qtpl:38 return //line examples/basicserver/templates/tablepage.qtpl:39 } else if n == 42 { //line examples/basicserver/templates/tablepage.qtpl:39 qw422016.N().S(` 42 rows already generated `) //line examples/basicserver/templates/tablepage.qtpl:41 break //line examples/basicserver/templates/tablepage.qtpl:42 } //line examples/basicserver/templates/tablepage.qtpl:42 qw422016.N().S(` `) //line examples/basicserver/templates/tablepage.qtpl:45 qw422016.N().D(n + 1) //line examples/basicserver/templates/tablepage.qtpl:45 qw422016.N().S(` `) //line examples/basicserver/templates/tablepage.qtpl:46 qw422016.E().S(r) //line examples/basicserver/templates/tablepage.qtpl:46 qw422016.N().S(` `) //line examples/basicserver/templates/tablepage.qtpl:48 } //line examples/basicserver/templates/tablepage.qtpl:48 qw422016.N().S(` No bingo found `) //line examples/basicserver/templates/tablepage.qtpl:51 } //line examples/basicserver/templates/tablepage.qtpl:51 func writeemitRows(qq422016 qtio422016.Writer, rows []string) { //line examples/basicserver/templates/tablepage.qtpl:51 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/tablepage.qtpl:51 streamemitRows(qw422016, rows) //line examples/basicserver/templates/tablepage.qtpl:51 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/tablepage.qtpl:51 } //line examples/basicserver/templates/tablepage.qtpl:51 func emitRows(rows []string) string { //line examples/basicserver/templates/tablepage.qtpl:51 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/tablepage.qtpl:51 writeemitRows(qb422016, rows) //line examples/basicserver/templates/tablepage.qtpl:51 qs422016 := string(qb422016.B) //line examples/basicserver/templates/tablepage.qtpl:51 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/tablepage.qtpl:51 return qs422016 //line examples/basicserver/templates/tablepage.qtpl:51 } //line examples/basicserver/templates/tablepage.qtpl:53 func (p *TablePage) streamform(qw422016 *qt422016.Writer) { //line examples/basicserver/templates/tablepage.qtpl:53 qw422016.N().S(`
Rows:
`) //line examples/basicserver/templates/tablepage.qtpl:58 } //line examples/basicserver/templates/tablepage.qtpl:58 func (p *TablePage) writeform(qq422016 qtio422016.Writer) { //line examples/basicserver/templates/tablepage.qtpl:58 qw422016 := qt422016.AcquireWriter(qq422016) //line examples/basicserver/templates/tablepage.qtpl:58 p.streamform(qw422016) //line examples/basicserver/templates/tablepage.qtpl:58 qt422016.ReleaseWriter(qw422016) //line examples/basicserver/templates/tablepage.qtpl:58 } //line examples/basicserver/templates/tablepage.qtpl:58 func (p *TablePage) form() string { //line examples/basicserver/templates/tablepage.qtpl:58 qb422016 := qt422016.AcquireByteBuffer() //line examples/basicserver/templates/tablepage.qtpl:58 p.writeform(qb422016) //line examples/basicserver/templates/tablepage.qtpl:58 qs422016 := string(qb422016.B) //line examples/basicserver/templates/tablepage.qtpl:58 qt422016.ReleaseByteBuffer(qb422016) //line examples/basicserver/templates/tablepage.qtpl:58 return qs422016 //line examples/basicserver/templates/tablepage.qtpl:58 } golang-github-valyala-quicktemplate-1.6.3+ds/go.mod000066400000000000000000000003001376501567700222670ustar00rootroot00000000000000module github.com/valyala/quicktemplate go 1.11 require ( github.com/klauspost/compress v1.11.0 // indirect github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.16.0 ) golang-github-valyala-quicktemplate-1.6.3+ds/go.sum000066400000000000000000000030121376501567700223170ustar00rootroot00000000000000github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.16.0 h1:9zAqOYLl8Tuy3E5R6ckzGDJ1g8+pw15oQp2iL9Jl6gQ= github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang-github-valyala-quicktemplate-1.6.3+ds/htmlescapewriter.go000066400000000000000000000017421376501567700251050ustar00rootroot00000000000000package quicktemplate import ( "bytes" "io" ) type htmlEscapeWriter struct { w io.Writer } func (w *htmlEscapeWriter) Write(b []byte) (int, error) { if bytes.IndexByte(b, '<') < 0 && bytes.IndexByte(b, '>') < 0 && bytes.IndexByte(b, '"') < 0 && bytes.IndexByte(b, '\'') < 0 && bytes.IndexByte(b, '&') < 0 { // fast path - nothing to escape return w.w.Write(b) } // slow path write := w.w.Write j := 0 for i, c := range b { switch c { case '<': write(b[j:i]) write(strLT) j = i + 1 case '>': write(b[j:i]) write(strGT) j = i + 1 case '"': write(b[j:i]) write(strQuot) j = i + 1 case '\'': write(b[j:i]) write(strApos) j = i + 1 case '&': write(b[j:i]) write(strAmp) j = i + 1 } } if n, err := write(b[j:]); err != nil { return j + n, err } return len(b), nil } var ( strLT = []byte("<") strGT = []byte(">") strQuot = []byte(""") strApos = []byte("'") strAmp = []byte("&") ) golang-github-valyala-quicktemplate-1.6.3+ds/htmlescapewriter_test.go000066400000000000000000000014511376501567700261410ustar00rootroot00000000000000package quicktemplate import ( "testing" ) func TestHTMLEscapeWriter(t *testing.T) { testHTMLEscapeWriter(t, "", "") testHTMLEscapeWriter(t, "foobar", "foobar") testHTMLEscapeWriter(t, `

fo'"bar&

`, "<h1>fo'"bar&</h1>") testHTMLEscapeWriter(t, "fobar привет\n\tbaz", "fo<b>bar привет\n\tbaz") } func testHTMLEscapeWriter(t *testing.T, s, expectedS string) { bb := AcquireByteBuffer() w := &htmlEscapeWriter{w: bb} n, err := w.Write([]byte(s)) if err != nil { t.Fatalf("unexpected error when writing %q: %s", s, err) } if n != len(s) { t.Fatalf("unexpected n returned: %d. Expecting %d. s=%q", n, len(s), s) } if string(bb.B) != expectedS { t.Fatalf("unexpected result: %q. Expecting %q", bb.B, expectedS) } ReleaseByteBuffer(bb) } golang-github-valyala-quicktemplate-1.6.3+ds/htmlescapewriter_timing_test.go000066400000000000000000000011721376501567700275100ustar00rootroot00000000000000package quicktemplate import ( "testing" ) func BenchmarkHTMLEscapeWriterNoHTML(b *testing.B) { s := "foobarbazabcdefghjkl" benchmarkHTMLEscapeWriter(b, s) } func BenchmarkHTMLEscapeWriterWithHTML(b *testing.B) { s := "foobazafghjkl" benchmarkHTMLEscapeWriter(b, s) } func benchmarkHTMLEscapeWriter(b *testing.B, s string) { sBytes := []byte(s) b.RunParallel(func(pb *testing.PB) { var err error bb := AcquireByteBuffer() w := &htmlEscapeWriter{w: bb} for pb.Next() { if _, err = w.Write(sBytes); err != nil { b.Fatalf("unexpected error: %s", err) } bb.Reset() } ReleaseByteBuffer(bb) }) } golang-github-valyala-quicktemplate-1.6.3+ds/jsonstring.go000066400000000000000000000024531376501567700237230ustar00rootroot00000000000000package quicktemplate import ( "fmt" "strings" ) func hasSpecialChars(s string) bool { if strings.IndexByte(s, '"') >= 0 || strings.IndexByte(s, '\\') >= 0 || strings.IndexByte(s, '<') >= 0 || strings.IndexByte(s, '\'') >= 0 { return true } for i := 0; i < len(s); i++ { if s[i] < 0x20 { return true } } return false } func appendJSONString(dst []byte, s string, addQuotes bool) []byte { if !hasSpecialChars(s) { // Fast path - nothing to escape. if !addQuotes { return append(dst, s...) } dst = append(dst, '"') dst = append(dst, s...) dst = append(dst, '"') return dst } // Slow path - there are chars to escape. if addQuotes { dst = append(dst, '"') } bb := AcquireByteBuffer() var tmp []byte tmp, bb.B = bb.B, dst _, err := jsonReplacer.WriteString(bb, s) if err != nil { panic(fmt.Errorf("BUG: unexpected error returned from jsonReplacer.WriteString: %s", err)) } dst, bb.B = bb.B, tmp ReleaseByteBuffer(bb) if addQuotes { dst = append(dst, '"') } return dst } var jsonReplacer = strings.NewReplacer(func() []string { a := []string{ "\n", `\n`, "\r", `\r`, "\t", `\t`, "\"", `\"`, "\\", `\\`, "<", `\u003c`, "'", `\u0027`, } for i := 0; i < 0x20; i++ { a = append(a, string([]byte{byte(i)}), fmt.Sprintf(`\u%04x`, i)) } return a }()...) golang-github-valyala-quicktemplate-1.6.3+ds/jsonstring_test.go000066400000000000000000000024651376501567700247650ustar00rootroot00000000000000package quicktemplate import ( "encoding/json" "strings" "testing" ) func TestAppendJSONString(t *testing.T) { testAppendJSONString(t, ``) testAppendJSONString(t, `f`) testAppendJSONString(t, `"`) testAppendJSONString(t, `<`) testAppendJSONString(t, "\x00\n\r\t\b\f"+`"\`) testAppendJSONString(t, `"foobar`) testAppendJSONString(t, `foobar"`) testAppendJSONString(t, `foo "bar" baz`) testAppendJSONString(t, `this is a "тест"`) testAppendJSONString(t, `привет test ыва`) testAppendJSONString(t, ``) testAppendJSONString(t, "\u001b") } func testAppendJSONString(t *testing.T, s string) { expectedResult, err := json.Marshal(s) if err != nil { t.Fatalf("unexpected error when encoding string %q: %s", s, err) } expectedResult = expectedResult[1 : len(expectedResult)-1] bb := AcquireByteBuffer() bb.B = appendJSONString(bb.B[:0], s, false) result := string(bb.B) ReleaseByteBuffer(bb) if strings.Contains(result, "'") { t.Fatalf("json string shouldn't contain single quote: %q, src %q", result, s) } result = strings.Replace(result, `\u0027`, "'", -1) result = strings.Replace(result, ">", `\u003e`, -1) if result != string(expectedResult) { t.Fatalf("unexpected result %q. Expecting %q. original string %q", result, expectedResult, s) } } golang-github-valyala-quicktemplate-1.6.3+ds/parser/000077500000000000000000000000001376501567700224645ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/parser/functype.go000066400000000000000000000113461376501567700246550ustar00rootroot00000000000000package parser import ( "fmt" "go/ast" goparser "go/parser" "strings" ) type funcType struct { name string defPrefix string callPrefix string argNames string args string } func parseFuncDef(b []byte) (*funcType, error) { defStr := string(b) // extract func name n := strings.Index(defStr, "(") if n < 0 { return nil, fmt.Errorf("cannot find '(' in function definition") } name := defStr[:n] defStr = defStr[n+1:] defPrefix := "" callPrefix := "" if len(name) == 0 { // Either empty func name or valid method definition. Let's check. // parse method receiver n = strings.Index(defStr, ")") if n < 0 { return nil, fmt.Errorf("cannot find ')' in func") } recvStr := defStr[:n] defStr = defStr[n+1:] exprStr := fmt.Sprintf("func (%s)", recvStr) expr, err := goparser.ParseExpr(exprStr) if err != nil { return nil, fmt.Errorf("invalid method definition: %s", err) } ft := expr.(*ast.FuncType) if len(ft.Params.List) != 1 || len(ft.Params.List[0].Names) != 1 { // method receiver must contain only one param return nil, fmt.Errorf("missing func or method name") } recvName := ft.Params.List[0].Names[0].Name defPrefix = fmt.Sprintf("(%s) ", recvStr) callPrefix = recvName + "." // extract method name n = strings.Index(defStr, "(") if n < 0 { return nil, fmt.Errorf("missing func name") } name = string(stripLeadingSpace([]byte(defStr[:n]))) if len(name) == 0 { return nil, fmt.Errorf("missing method name") } defStr = defStr[n+1:] } // validate and collect func args if len(defStr) == 0 || defStr[len(defStr)-1] != ')' { return nil, fmt.Errorf("missing ')' at the end of func") } args := defStr[:len(defStr)-1] exprStr := fmt.Sprintf("func (%s)", args) expr, err := goparser.ParseExpr(exprStr) if err != nil { return nil, fmt.Errorf("invalid func args: %s", err) } ft := expr.(*ast.FuncType) if ft.Results != nil { return nil, fmt.Errorf("func mustn't return any results") } // extract arg names var tmp []string for _, f := range ft.Params.List { if len(f.Names) == 0 { return nil, fmt.Errorf("func cannot contain untyped arguments") } for _, n := range f.Names { if n == nil { return nil, fmt.Errorf("func cannot contain untyped arguments") } if _, isVariadic := f.Type.(*ast.Ellipsis); isVariadic { tmp = append(tmp, n.Name+"...") } else { tmp = append(tmp, n.Name) } } } argNames := strings.Join(tmp, ", ") if len(args) > 0 { args = ", " + args } if len(argNames) > 0 { argNames = ", " + argNames } return &funcType{ name: name, defPrefix: defPrefix, callPrefix: callPrefix, argNames: argNames, args: args, }, nil } func parseFuncCall(b []byte) (*funcType, error) { exprStr := string(b) expr, err := goparser.ParseExpr(exprStr) if err != nil { return nil, err } ce, ok := expr.(*ast.CallExpr) if !ok { return nil, fmt.Errorf("missing function call") } callPrefix, name, err := getCallName(ce) if err != nil { return nil, err } argNames := exprStr[ce.Lparen : ce.Rparen-1] if len(argNames) > 0 { argNames = ", " + argNames } return &funcType{ name: name, callPrefix: callPrefix, argNames: argNames, }, nil } func (f *funcType) DefStream(dst string) string { return fmt.Sprintf("%s%s%s(%s *qt%s.Writer%s)", f.defPrefix, f.prefixStream(), f.name, dst, mangleSuffix, f.args) } func (f *funcType) CallStream(dst string) string { return fmt.Sprintf("%s%s%s(%s%s)", f.callPrefix, f.prefixStream(), f.name, dst, f.argNames) } func (f *funcType) DefWrite(dst string) string { return fmt.Sprintf("%s%s%s(%s qtio%s.Writer%s)", f.defPrefix, f.prefixWrite(), f.name, dst, mangleSuffix, f.args) } func (f *funcType) CallWrite(dst string) string { return fmt.Sprintf("%s%s%s(%s%s)", f.callPrefix, f.prefixWrite(), f.name, dst, f.argNames) } func (f *funcType) DefString() string { args := f.args if len(args) > 0 { // skip the first ', ' args = args[2:] } return fmt.Sprintf("%s%s(%s) string", f.defPrefix, f.name, args) } func (f *funcType) prefixWrite() string { s := "write" if isUpper(f.name[0]) { s = "Write" } return s } func (f *funcType) prefixStream() string { s := "stream" if isUpper(f.name[0]) { s = "Stream" } return s } func getCallName(ce *ast.CallExpr) (string, string, error) { callPrefix := "" name := "" expr := ce.Fun for { switch x := expr.(type) { case *ast.Ident: if len(callPrefix) == 0 && len(name) == 0 { return "", x.Name, nil } callPrefix = x.Name + "." + callPrefix return callPrefix, name, nil case *ast.SelectorExpr: if len(name) == 0 { name = x.Sel.Name } else { callPrefix = x.Sel.Name + "." + callPrefix } expr = x.X default: return "", "", fmt.Errorf("unexpected function name") } } } golang-github-valyala-quicktemplate-1.6.3+ds/parser/functype_test.go000066400000000000000000000127561376501567700257220ustar00rootroot00000000000000package parser import ( "testing" ) func TestParseFuncCallSuccess(t *testing.T) { // func without args testParseFuncCallSuccess(t, "f()", "streamf(qw422016)") // func with args testParseFuncCallSuccess(t, "Foo(a, b)", "StreamFoo(qw422016, a, b)") // method without args testParseFuncCallSuccess(t, "a.f()", "a.streamf(qw422016)") // method with args testParseFuncCallSuccess(t, "a.f(xx)", "a.streamf(qw422016, xx)") // chained method testParseFuncCallSuccess(t, "foo.bar.Baz(x, y)", "foo.bar.StreamBaz(qw422016, x, y)") // complex args testParseFuncCallSuccess(t, `as.ffs.SS( func(x int, y string) { panic("foobar") }, map[string]int{ "foo":1, "bar":2, }, qawe)`, `as.ffs.StreamSS(qw422016, func(x int, y string) { panic("foobar") }, map[string]int{ "foo":1, "bar":2, }, qawe)`) } func TestParseFuncCallFailure(t *testing.T) { testParseFuncCallFailure(t, "") // non-func testParseFuncCallFailure(t, "foobar") testParseFuncCallFailure(t, "a, b, c") testParseFuncCallFailure(t, "{}") testParseFuncCallFailure(t, "(a)") testParseFuncCallFailure(t, "(f())") // inline func testParseFuncCallFailure(t, "func() {}()") testParseFuncCallFailure(t, "func a() {}()") // nonempty tail after func call testParseFuncCallFailure(t, "f(); f1()") testParseFuncCallFailure(t, "f()\nf1()") testParseFuncCallFailure(t, "f()\n for {}") } func testParseFuncCallFailure(t *testing.T, s string) { _, err := parseFuncCall([]byte(s)) if err == nil { t.Fatalf("expecting non-nil error when parsing %q", s) } } func testParseFuncCallSuccess(t *testing.T, s, callStream string) { f, err := parseFuncCall([]byte(s)) if err != nil { t.Fatalf("unexpected error when parsing %q: %s", s, err) } cs := f.CallStream("qw422016") if cs != callStream { t.Fatalf("unexpected CallStream: %q. Expecting %q. s=%q", cs, callStream, s) } } func TestParseFuncDefSuccess(t *testing.T) { // private func without args testParseFuncDefSuccess(t, "xx()", "xx() string", "streamxx(qw422016 *qt422016.Writer)", "streamxx(qw422016)", "writexx(qq422016 qtio422016.Writer)", "writexx(qq422016)") // public func with a single arg testParseFuncDefSuccess(t, "F(a int)", "F(a int) string", "StreamF(qw422016 *qt422016.Writer, a int)", "StreamF(qw422016, a)", "WriteF(qq422016 qtio422016.Writer, a int)", "WriteF(qq422016, a)") // public method without args testParseFuncDefSuccess(t, "(f *foo) M()", "(f *foo) M() string", "(f *foo) StreamM(qw422016 *qt422016.Writer)", "f.StreamM(qw422016)", "(f *foo) WriteM(qq422016 qtio422016.Writer)", "f.WriteM(qq422016)") // private method with three args testParseFuncDefSuccess(t, "(f *Foo) bar(x, y string, z int)", "(f *Foo) bar(x, y string, z int) string", "(f *Foo) streambar(qw422016 *qt422016.Writer, x, y string, z int)", "f.streambar(qw422016, x, y, z)", "(f *Foo) writebar(qq422016 qtio422016.Writer, x, y string, z int)", "f.writebar(qq422016, x, y, z)") // method with complex args testParseFuncDefSuccess(t, "(t TPL) Head(h1, h2 func(x, y int), h3 map[int]struct{})", "(t TPL) Head(h1, h2 func(x, y int), h3 map[int]struct{}) string", "(t TPL) StreamHead(qw422016 *qt422016.Writer, h1, h2 func(x, y int), h3 map[int]struct{})", "t.StreamHead(qw422016, h1, h2, h3)", "(t TPL) WriteHead(qq422016 qtio422016.Writer, h1, h2 func(x, y int), h3 map[int]struct{})", "t.WriteHead(qq422016, h1, h2, h3)") // method with variadic arguments testParseFuncDefSuccess(t, "(t TPL) Head(name string, num int, otherNames ...string)", "(t TPL) Head(name string, num int, otherNames ...string) string", "(t TPL) StreamHead(qw422016 *qt422016.Writer, name string, num int, otherNames ...string)", "t.StreamHead(qw422016, name, num, otherNames...)", "(t TPL) WriteHead(qq422016 qtio422016.Writer, name string, num int, otherNames ...string)", "t.WriteHead(qq422016, name, num, otherNames...)") } func TestParseFuncDefFailure(t *testing.T) { testParseFuncDefFailure(t, "") // invalid syntax testParseFuncDefFailure(t, "foobar") testParseFuncDefFailure(t, "f() {") testParseFuncDefFailure(t, "for {}") // missing func name testParseFuncDefFailure(t, "()") testParseFuncDefFailure(t, "(a int, b string)") // missing method name testParseFuncDefFailure(t, "(x XX) ()") testParseFuncDefFailure(t, "(x XX) (y, z string)") // func with return values testParseFuncDefFailure(t, "f() string") testParseFuncDefFailure(t, "f() (int, string)") testParseFuncDefFailure(t, "(x XX) f() string") testParseFuncDefFailure(t, "(x XX) f(a int) (int, string)") } func testParseFuncDefFailure(t *testing.T, s string) { f, err := parseFuncDef([]byte(s)) if err == nil { t.Fatalf("expecting error when parsing %q. got %#v", s, f) } } func testParseFuncDefSuccess(t *testing.T, s, defString, defStream, callStream, defWrite, callWrite string) { f, err := parseFuncDef([]byte(s)) if err != nil { t.Fatalf("cannot parse %q: %s", s, err) } ds := f.DefString() if ds != defString { t.Fatalf("unexpected DefString: %q. Expecting %q. s=%q", ds, defString, s) } ds = f.DefStream("qw422016") if ds != defStream { t.Fatalf("unexpected DefStream: %q. Expecting %q. s=%q", ds, defStream, s) } cs := f.CallStream("qw422016") if cs != callStream { t.Fatalf("unexpected CallStream: %q. Expecting %q. s=%q", cs, callStream, s) } dw := f.DefWrite("qq422016") if dw != defWrite { t.Fatalf("unexpected DefWrite: %q. Expecting %q. s=%q", dw, defWrite, s) } cw := f.CallWrite("qq422016") if cw != callWrite { t.Fatalf("unexpected CallWrite: %q. Expecting %q. s=%q", cw, callWrite, s) } } golang-github-valyala-quicktemplate-1.6.3+ds/parser/parser.go000066400000000000000000000551201376501567700243120ustar00rootroot00000000000000package parser import ( "bytes" "fmt" "go/ast" goparser "go/parser" gotoken "go/token" "io" "path/filepath" "strconv" "strings" ) type parser struct { s *scanner w io.Writer packageName string skipLineComments bool prefix string forDepth int switchDepth int skipOutputDepth int importsUseEmitted bool packageNameEmitted bool } // Parse parses the contents of the supplied reader, writing generated code to // the supplied writer. Uses filename as the source file for line comments, and // pkg as the Go package name. func Parse(w io.Writer, r io.Reader, filename, pkg string) error { return parse(w, r, filename, pkg, false) } // ParseNoLineComments is the same as Parse, but does not write line comments. func ParseNoLineComments(w io.Writer, r io.Reader, filename, pkg string) error { return parse(w, r, filename, pkg, true) } func parse(w io.Writer, r io.Reader, filename, pkg string, skipLineComments bool) error { p := &parser{ s: newScanner(r, filename), w: w, packageName: pkg, skipLineComments: skipLineComments, } return p.parseTemplate() } func (p *parser) parseTemplate() error { s := p.s fmt.Fprintf(p.w, `// Code generated by qtc from %q. DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. `, filepath.Base(s.filePath)) for s.Next() { t := s.Token() switch t.ID { case text: p.emitComment(t.Value) case tagName: switch string(t.Value) { case "package": if p.packageNameEmitted { return fmt.Errorf("package name must be at the top of the template. Found at %s", s.Context()) } if err := p.parsePackageName(); err != nil { return err } case "import": p.emitPackageName() if p.importsUseEmitted { return fmt.Errorf("imports must be at the top of the template. Found at %s", s.Context()) } if err := p.parseImport(); err != nil { return err } default: p.emitPackageName() p.emitImportsUse() switch string(t.Value) { case "interface", "iface": if err := p.parseInterface(); err != nil { return err } case "code": if err := p.parseTemplateCode(); err != nil { return err } case "func": if err := p.parseFunc(); err != nil { return err } default: return fmt.Errorf("unexpected tag found outside func: %q at %s", t.Value, s.Context()) } } default: return fmt.Errorf("unexpected token found %s outside func at %s", t, s.Context()) } } p.emitImportsUse() if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse template: %s", err) } return nil } func (p *parser) emitPackageName() { if !p.packageNameEmitted { p.Printf("package %s\n", p.packageName) p.packageNameEmitted = true } } func (p *parser) emitComment(comment []byte) { isFirstNonemptyLine := false for len(comment) > 0 { n := bytes.IndexByte(comment, '\n') if n < 0 { n = len(comment) } line := stripTrailingSpace(comment[:n]) if bytes.HasPrefix(line, []byte("//")) { line = line[2:] if len(line) > 0 && isSpace(line[0]) { line = line[1:] } } if len(line) == 0 { if isFirstNonemptyLine { fmt.Fprintf(p.w, "//\n") } } else { fmt.Fprintf(p.w, "// %s\n", line) isFirstNonemptyLine = true } if n < len(comment) { comment = comment[n+1:] } else { comment = comment[n:] } } fmt.Fprintf(p.w, "\n") } func (p *parser) emitImportsUse() { if p.importsUseEmitted { return } p.Printf(`import ( qtio%s "io" qt%s "github.com/valyala/quicktemplate" ) `, mangleSuffix, mangleSuffix) p.Printf(`var ( _ = qtio%s.Copy _ = qt%s.AcquireByteBuffer ) `, mangleSuffix, mangleSuffix) p.importsUseEmitted = true } func (p *parser) parseFunc() error { s := p.s t, err := expectTagContents(s) if err != nil { return err } funcStr := "func " + string(t.Value) f, err := parseFuncDef(t.Value) if err != nil { return fmt.Errorf("error in %q at %s: %s", funcStr, s.Context(), err) } p.emitFuncStart(f) for s.Next() { t := s.Token() switch t.ID { case text: p.emitText(t.Value) case tagName: ok, err := p.tryParseCommonTags(t.Value) if err != nil { return fmt.Errorf("error in %q: %s", funcStr, err) } if ok { continue } switch string(t.Value) { case "endfunc": if err = skipTagContents(s); err != nil { return err } p.emitFuncEnd(f) return nil default: return fmt.Errorf("unexpected tag found in %q: %q at %s", funcStr, t.Value, s.Context()) } default: return fmt.Errorf("unexpected token found when parsing %q: %s at %s", funcStr, t, s.Context()) } } if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse %q: %s", funcStr, err) } return fmt.Errorf("cannot find endfunc tag for %q at %s", funcStr, s.Context()) } func (p *parser) parseFor() error { s := p.s t, err := expectTagContents(s) if err != nil { return err } forStr := "for " + string(t.Value) if err = validateForStmt(t.Value); err != nil { return fmt.Errorf("invalid statement %q at %s: %s", forStr, s.Context(), err) } p.Printf("for %s {", t.Value) p.prefix += "\t" p.forDepth++ for s.Next() { t := s.Token() switch t.ID { case text: p.emitText(t.Value) case tagName: ok, err := p.tryParseCommonTags(t.Value) if err != nil { return fmt.Errorf("error in %q: %s", forStr, err) } if ok { continue } switch string(t.Value) { case "endfor": if err = skipTagContents(s); err != nil { return err } p.forDepth-- p.prefix = p.prefix[1:] p.Printf("}") return nil default: return fmt.Errorf("unexpected tag found in %q: %q at %s", forStr, t.Value, s.Context()) } default: return fmt.Errorf("unexpected token found when parsing %q: %s at %s", forStr, t, s.Context()) } } if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse %q: %s", forStr, err) } return fmt.Errorf("cannot find endfor tag for %q at %s", forStr, s.Context()) } func (p *parser) parseDefault() error { s := p.s if err := skipTagContents(s); err != nil { return err } stmtStr := "default" p.Printf("default:") p.prefix += "\t" for s.Next() { t := s.Token() switch t.ID { case text: p.emitText(t.Value) case tagName: ok, err := p.tryParseCommonTags(t.Value) if err != nil { return fmt.Errorf("error in %q: %s", stmtStr, err) } if !ok { s.Rewind() p.prefix = p.prefix[1:] return nil } default: return fmt.Errorf("unexpected token found when parsing %q: %s at %s", stmtStr, t, s.Context()) } } if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse %q: %s", stmtStr, err) } return fmt.Errorf("cannot find end of %q at %s", stmtStr, s.Context()) } func (p *parser) parseCase(switchValue string) error { s := p.s t, err := expectTagContents(s) if err != nil { return err } caseStr := "case " + string(t.Value) if err = validateCaseStmt(switchValue, t.Value); err != nil { return fmt.Errorf("invalid statement %q at %s: %s", caseStr, s.Context(), err) } p.Printf("case %s:", t.Value) p.prefix += "\t" for s.Next() { t := s.Token() switch t.ID { case text: p.emitText(t.Value) case tagName: ok, err := p.tryParseCommonTags(t.Value) if err != nil { return fmt.Errorf("error in %q: %s", caseStr, err) } if !ok { s.Rewind() p.prefix = p.prefix[1:] return nil } default: return fmt.Errorf("unexpected token found when parsing %q: %s at %s", caseStr, t, s.Context()) } } if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse %q: %s", caseStr, err) } return fmt.Errorf("cannot find end of %q at %s", caseStr, s.Context()) } func (p *parser) parseCat() error { s := p.s t, err := expectTagContents(s) if err != nil { return err } filename, err := strconv.Unquote(string(t.Value)) if err != nil { return fmt.Errorf("invalid cat value %q at %s: %s", t.Value, s.Context(), err) } data, err := readFile(s.filePath, filename) if err != nil { return fmt.Errorf("cannot cat file %q at %s: %s", filename, s.Context(), err) } p.emitText(data) return nil } func (p *parser) parseSwitch() error { s := p.s t, err := expectTagContents(s) if err != nil { return err } switchStr := "switch " + string(t.Value) if err = validateSwitchStmt(t.Value); err != nil { return fmt.Errorf("invalid statement %q at %s: %s", switchStr, s.Context(), err) } p.Printf("switch %s {", t.Value) switchValue := string(t.Value) caseNum := 0 defaultFound := false p.switchDepth++ for s.Next() { t := s.Token() switch t.ID { case text: if caseNum == 0 { comment := stripLeadingSpace(t.Value) if len(comment) > 0 { p.emitComment(comment) } } else { p.emitText(t.Value) } case tagName: switch string(t.Value) { case "endswitch": if caseNum == 0 { return fmt.Errorf("empty statement %q found at %s", switchStr, s.Context()) } if err = skipTagContents(s); err != nil { return err } p.switchDepth-- p.Printf("}") return nil case "case": caseNum++ if err = p.parseCase(switchValue); err != nil { return err } case "default": if defaultFound { return fmt.Errorf("duplicate default tag found in %q at %s", switchStr, s.Context()) } defaultFound = true caseNum++ if err = p.parseDefault(); err != nil { return err } default: return fmt.Errorf("unexpected tag found in %q: %q at %s", switchStr, t.Value, s.Context()) } default: return fmt.Errorf("unexpected token found when parsing %q: %s at %s", switchStr, t, s.Context()) } } if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse %q: %s", switchStr, err) } return fmt.Errorf("cannot find endswitch tag for %q at %s", switchStr, s.Context()) } func (p *parser) parseIf() error { s := p.s t, err := expectTagContents(s) if err != nil { return err } if len(t.Value) == 0 { return fmt.Errorf("empty if condition at %s", s.Context()) } ifStr := "if " + string(t.Value) if err = validateIfStmt(t.Value); err != nil { return fmt.Errorf("invalid statement %q at %s: %s", ifStr, s.Context(), err) } p.Printf("if %s {", t.Value) p.prefix += "\t" elseUsed := false for s.Next() { t := s.Token() switch t.ID { case text: p.emitText(t.Value) case tagName: ok, err := p.tryParseCommonTags(t.Value) if err != nil { return fmt.Errorf("error in %q: %s", ifStr, err) } if ok { continue } switch string(t.Value) { case "endif": if err = skipTagContents(s); err != nil { return err } p.prefix = p.prefix[1:] p.Printf("}") return nil case "else": if elseUsed { return fmt.Errorf("duplicate else branch found for %q at %s", ifStr, s.Context()) } if err = skipTagContents(s); err != nil { return err } p.prefix = p.prefix[1:] p.Printf("} else {") p.prefix += "\t" elseUsed = true case "elseif": if elseUsed { return fmt.Errorf("unexpected elseif branch found after else branch for %q at %s", ifStr, s.Context()) } t, err = expectTagContents(s) if err != nil { return err } p.prefix = p.prefix[1:] p.Printf("} else if %s {", t.Value) p.prefix += "\t" default: return fmt.Errorf("unexpected tag found in %q: %q at %s", ifStr, t.Value, s.Context()) } default: return fmt.Errorf("unexpected token found when parsing %q: %s at %s", ifStr, t, s.Context()) } } if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse %q: %s", ifStr, err) } return fmt.Errorf("cannot find endif tag for %q at %s", ifStr, s.Context()) } func (p *parser) tryParseCommonTags(tagBytes []byte) (bool, error) { tagNameStr, prec := splitTagNamePrec(string(tagBytes)) switch tagNameStr { case "s", "v", "d", "dl", "dul", "f", "q", "z", "j", "u", "s=", "v=", "d=", "dl=", "dul=", "f=", "q=", "z=", "j=", "u=", "sz", "qz", "jz", "uz", "sz=", "qz=", "jz=", "uz=": if err := p.parseOutputTag(tagNameStr, prec); err != nil { return false, err } case "=", "=h", "=u", "=uh", "=q", "=qh", "=j", "=jh": if err := p.parseOutputFunc(tagNameStr); err != nil { return false, err } case "return": if err := p.skipAfterTag(tagNameStr); err != nil { return false, err } case "break": if p.forDepth <= 0 && p.switchDepth <= 0 { return false, fmt.Errorf("found break tag outside for loop and switch block") } if err := p.skipAfterTag(tagNameStr); err != nil { return false, err } case "continue": if p.forDepth <= 0 { return false, fmt.Errorf("found continue tag outside for loop") } if err := p.skipAfterTag(tagNameStr); err != nil { return false, err } case "code": if err := p.parseFuncCode(); err != nil { return false, err } case "for": if err := p.parseFor(); err != nil { return false, err } case "if": if err := p.parseIf(); err != nil { return false, err } case "switch": if err := p.parseSwitch(); err != nil { return false, err } case "cat": if err := p.parseCat(); err != nil { return false, err } default: return false, nil } return true, nil } func splitTagNamePrec(tagName string) (string, int) { parts := strings.Split(tagName, ".") if len(parts) == 2 && parts[0] == "f" { p := parts[1] if strings.HasSuffix(p, "=") { p = p[:len(p)-1] } if len(p) == 0 { return "f", 0 } prec, err := strconv.Atoi(p) if err == nil && prec >= 0 { return "f", prec } } return tagName, -1 } func (p *parser) skipAfterTag(tagStr string) error { s := p.s if err := skipTagContents(s); err != nil { return err } p.Printf("%s", tagStr) p.skipOutputDepth++ defer func() { p.skipOutputDepth-- }() for s.Next() { t := s.Token() switch t.ID { case text: // skip text case tagName: ok, err := p.tryParseCommonTags(t.Value) if err != nil { return fmt.Errorf("error when parsing contents after %q: %s", tagStr, err) } if ok { continue } switch string(t.Value) { case "endfunc", "endfor", "endif", "else", "elseif", "case", "default", "endswitch": s.Rewind() return nil default: return fmt.Errorf("unexpected tag found after %q: %q at %s", tagStr, t.Value, s.Context()) } default: return fmt.Errorf("unexpected token found when parsing contents after %q: %s at %s", tagStr, t, s.Context()) } } if err := s.LastError(); err != nil { return fmt.Errorf("cannot parse contents after %q: %s", tagStr, err) } return fmt.Errorf("cannot find closing tag after %q at %s", tagStr, s.Context()) } func (p *parser) parseInterface() error { s := p.s t, err := expectTagContents(s) if err != nil { return err } n := bytes.IndexByte(t.Value, '{') if n < 0 { return fmt.Errorf("missing '{' in interface at %s", s.Context()) } ifname := string(stripTrailingSpace(t.Value[:n])) if len(ifname) == 0 { return fmt.Errorf("missing interface name at %s", s.Context()) } p.Printf("type %s interface {", ifname) p.prefix = "\t" tail := t.Value[n:] exprStr := fmt.Sprintf("interface %s", tail) expr, err := goparser.ParseExpr(exprStr) if err != nil { return fmt.Errorf("error when parsing interface at %s: %s", s.Context(), err) } it, ok := expr.(*ast.InterfaceType) if !ok { return fmt.Errorf("unexpected interface type at %s: %T", s.Context(), expr) } methods := it.Methods.List if len(methods) == 0 { return fmt.Errorf("interface must contain at least one method at %s", s.Context()) } for _, m := range it.Methods.List { methodStr := exprStr[m.Pos()-1 : m.End()-1] f, err := parseFuncDef([]byte(methodStr)) if err != nil { return fmt.Errorf("when when parsing %q at %s: %s", methodStr, s.Context(), err) } p.Printf("%s string", methodStr) p.Printf("%s", f.DefStream("qw"+mangleSuffix)) p.Printf("%s", f.DefWrite("qq"+mangleSuffix)) } p.prefix = "" p.Printf("}") return nil } func (p *parser) parsePackageName() error { t, err := expectTagContents(p.s) if err != nil { return err } if len(t.Value) == 0 { return fmt.Errorf("empty package name found at %s", p.s.Context()) } if err = validatePackageName(t.Value); err != nil { return fmt.Errorf("invalid package name found at %s: %s", p.s.Context(), err) } p.packageName = string(t.Value) p.emitPackageName() return nil } func (p *parser) parseImport() error { t, err := expectTagContents(p.s) if err != nil { return err } if len(t.Value) == 0 { return fmt.Errorf("empty import found at %s", p.s.Context()) } if err = validateImport(t.Value); err != nil { return fmt.Errorf("invalid import found at %s: %s", p.s.Context(), err) } p.Printf("import %s\n", t.Value) return nil } func (p *parser) parseTemplateCode() error { t, err := expectTagContents(p.s) if err != nil { return err } if err = validateTemplateCode(t.Value); err != nil { return fmt.Errorf("invalid code at %s: %s", p.s.Context(), err) } p.Printf("%s\n", t.Value) return nil } func (p *parser) parseFuncCode() error { t, err := expectTagContents(p.s) if err != nil { return err } if err = validateFuncCode(t.Value); err != nil { return fmt.Errorf("invalid code at %s: %s", p.s.Context(), err) } p.Printf("%s\n", t.Value) return nil } func (p *parser) parseOutputTag(tagNameStr string, prec int) error { s := p.s t, err := expectTagContents(s) if err != nil { return err } if err = validateOutputTagValue(t.Value); err != nil { return fmt.Errorf("invalid output tag value at %s: %s", s.Context(), err) } filter := "N" switch tagNameStr { case "s", "v", "q", "z", "j", "sz", "qz", "jz": filter = "E" } if strings.HasSuffix(tagNameStr, "=") { tagNameStr = tagNameStr[:len(tagNameStr)-1] } if tagNameStr == "f" && prec >= 0 { p.Printf("qw%s.N().FPrec(%s, %d)", mangleSuffix, t.Value, prec) } else { tagNameStr = strings.ToUpper(tagNameStr) p.Printf("qw%s.%s().%s(%s)", mangleSuffix, filter, tagNameStr, t.Value) } return nil } func (p *parser) parseOutputFunc(tagNameStr string) error { s := p.s t, err := expectTagContents(s) if err != nil { return err } f, err := parseFuncCall(t.Value) if err != nil { return fmt.Errorf("error at %s: %s", s.Context(), err) } filter := "N" tagNameStr = tagNameStr[1:] if strings.HasSuffix(tagNameStr, "h") { tagNameStr = tagNameStr[:len(tagNameStr)-1] switch tagNameStr { case "", "q", "j": filter = "E" } } if len(tagNameStr) > 0 || filter == "E" { tagNameStr = strings.ToUpper(tagNameStr) p.Printf("{") p.Printf("qb%s := qt%s.AcquireByteBuffer()", mangleSuffix, mangleSuffix) p.Printf("%s", f.CallWrite("qb"+mangleSuffix)) p.Printf("qw%s.%s().%sZ(qb%s.B)", mangleSuffix, filter, tagNameStr, mangleSuffix) p.Printf("qt%s.ReleaseByteBuffer(qb%s)", mangleSuffix, mangleSuffix) p.Printf("}") } else { p.Printf("%s", f.CallStream("qw"+mangleSuffix)) } return nil } func (p *parser) emitText(text []byte) { for len(text) > 0 { n := bytes.IndexByte(text, '`') if n < 0 { p.Printf("qw%s.N().S(`%s`)", mangleSuffix, text) return } p.Printf("qw%s.N().S(`%s`)", mangleSuffix, text[:n]) p.Printf("qw%s.N().S(\"`\")", mangleSuffix) text = text[n+1:] } } func (p *parser) emitFuncStart(f *funcType) { p.Printf("func %s {", f.DefStream("qw"+mangleSuffix)) p.prefix = "\t" } func (p *parser) emitFuncEnd(f *funcType) { p.prefix = "" p.Printf("}\n") p.Printf("func %s {", f.DefWrite("qq"+mangleSuffix)) p.prefix = "\t" p.Printf("qw%s := qt%s.AcquireWriter(qq%s)", mangleSuffix, mangleSuffix, mangleSuffix) p.Printf("%s", f.CallStream("qw"+mangleSuffix)) p.Printf("qt%s.ReleaseWriter(qw%s)", mangleSuffix, mangleSuffix) p.prefix = "" p.Printf("}\n") p.Printf("func %s {", f.DefString()) p.prefix = "\t" p.Printf("qb%s := qt%s.AcquireByteBuffer()", mangleSuffix, mangleSuffix) p.Printf("%s", f.CallWrite("qb"+mangleSuffix)) p.Printf("qs%s := string(qb%s.B)", mangleSuffix, mangleSuffix) p.Printf("qt%s.ReleaseByteBuffer(qb%s)", mangleSuffix, mangleSuffix) p.Printf("return qs%s", mangleSuffix) p.prefix = "" p.Printf("}\n") } func (p *parser) Printf(format string, args ...interface{}) { if p.skipOutputDepth > 0 { return } w := p.w if !p.skipLineComments { // line comments are required to start at the beginning of the line p.s.WriteLineComment(w) } fmt.Fprintf(w, "%s", p.prefix) fmt.Fprintf(w, format, args...) fmt.Fprintf(w, "\n") } func skipTagContents(s *scanner) error { tagName := string(s.Token().Value) t, err := expectTagContents(s) if err != nil { return err } if len(t.Value) > 0 { return fmt.Errorf("unexpected extra value after %s: %q at %s", tagName, t.Value, s.Context()) } return err } func expectTagContents(s *scanner) (*token, error) { return expectToken(s, tagContents) } func expectToken(s *scanner, id int) (*token, error) { if !s.Next() { return nil, fmt.Errorf("cannot find token %s: %v", tokenIDToStr(id), s.LastError()) } t := s.Token() if t.ID != id { return nil, fmt.Errorf("unexpected token found %s. Expecting %s at %s", t, tokenIDToStr(id), s.Context()) } return t, nil } func validateOutputTagValue(stmt []byte) error { exprStr := string(stmt) _, err := goparser.ParseExpr(exprStr) return err } func validateForStmt(stmt []byte) error { exprStr := fmt.Sprintf("func () { for %s {} }", stmt) _, err := goparser.ParseExpr(exprStr) return err } func validateIfStmt(stmt []byte) error { exprStr := fmt.Sprintf("func () { if %s {} }", stmt) _, err := goparser.ParseExpr(exprStr) return err } func validateSwitchStmt(stmt []byte) error { exprStr := fmt.Sprintf("func () { switch %s {} }", stmt) _, err := goparser.ParseExpr(exprStr) return err } func validateCaseStmt(switchValue string, stmt []byte) error { exprStr := fmt.Sprintf("func () { switch %s {case %s:} }", switchValue, stmt) _, err := goparser.ParseExpr(exprStr) return err } func validateFuncCode(code []byte) error { exprStr := fmt.Sprintf("func () { for { %s\n } }", code) _, err := goparser.ParseExpr(exprStr) return err } func validateTemplateCode(code []byte) error { codeStr := fmt.Sprintf("package foo\nvar _ = a\n%s", code) fset := gotoken.NewFileSet() _, err := goparser.ParseFile(fset, "", codeStr, 0) return err } func validatePackageName(code []byte) error { codeStr := fmt.Sprintf("package %s", code) fset := gotoken.NewFileSet() _, err := goparser.ParseFile(fset, "", codeStr, 0) return err } func validateImport(code []byte) error { codeStr := fmt.Sprintf("package foo\nimport %s", code) fset := gotoken.NewFileSet() f, err := goparser.ParseFile(fset, "", codeStr, 0) if err != nil { return err } for _, d := range f.Decls { gd, ok := d.(*ast.GenDecl) if !ok { return fmt.Errorf("unexpected code found: %T. Expecting ast.GenDecl", d) } for _, s := range gd.Specs { if _, ok := s.(*ast.ImportSpec); !ok { return fmt.Errorf("unexpected code found: %T. Expecting ast.ImportSpec", s) } } } return nil } golang-github-valyala-quicktemplate-1.6.3+ds/parser/parser_test.go000066400000000000000000000445221376501567700253550ustar00rootroot00000000000000package parser import ( "bytes" "go/format" "io/ioutil" "os" "testing" "github.com/valyala/quicktemplate" ) func TestParsePackageName(t *testing.T) { // empty template testParseSuccess(t, ``) // No package name testParseSuccess(t, `foobar`) // explicit package name testParseSuccess(t, `{% package foobar %}`) // package name with imports testParseSuccess(t, `Package: {% package foobar %} import {% import "aa/bb/cc" %} yet another import {% import ( "xxx.com/aaa" ) %}`) // invalid package name testParseFailure(t, `{% package foo bar %}`) testParseFailure(t, `{% package "foobar" %}`) testParseFailure(t, `{% package x(foobar) %}`) // multiple package names testParseFailure(t, `{% package foo %}{% package bar %}`) // package name not at the top of the template testParseFailure(t, `{% import "foo" %}{% package bar %}`) testParseFailure(t, `{% func foo() %}{% endfunc %}{% package bar %}`) testParseFailure(t, `{% func foo() %}{% package bar %}{% endfunc %}`) } func TestParseOutputFunc(t *testing.T) { // func without args testParseSuccess(t, `{% func f() %}{%= f() %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%= x.y.f() %}{% endfunc %}`) // func with args testParseSuccess(t, `{% func f() %}{%= f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%= x.y.f(1, "foo", bar) %}{% endfunc %}`) // html modifier (=h) testParseSuccess(t, `{% func f() %}{%=h f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=h x.y.f(1, "foo", bar) %}{% endfunc %}`) // urlencode modifier (=u) testParseSuccess(t, `{% func f() %}{%=u f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=u x.y.f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=uh f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=uh x.y.f(1, "foo", bar) %}{% endfunc %}`) // quoted json string modifier (=q) testParseSuccess(t, `{% func f() %}{%=q f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=q x.y.f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=qh f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=qh x.y.f(1, "foo", bar) %}{% endfunc %}`) // unquoted json string modifier (=j) testParseSuccess(t, `{% func f() %}{%=j f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=j x.y.f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=jh f(1, "foo", bar) %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{%=jh x.y.f(1, "foo", bar) %}{% endfunc %}`) // unknown modifier testParseFailure(t, `{% func f() %}{%=w f(1, "foo", bar) %}{% endfunc %}`) testParseFailure(t, `{% func f() %}{%=ww x.y.f(1, "foo", bar) %}{% endfunc %}`) testParseFailure(t, `{% func f() %}{%=wwh f(1, "foo", bar) %}{% endfunc %}`) testParseFailure(t, `{% func f() %}{%=wh x.y.f(1, "foo", bar) %}{% endfunc %}`) } func TestParseCat(t *testing.T) { // relative paths testParseSuccess(t, `{% func a() %}{% cat "parser.go" %}{% endfunc %}`) testParseSuccess(t, `{% func a() %}{% cat "./parser.go" %}{% endfunc %}`) testParseSuccess(t, `{% func a() %}{% cat "../parser/parser.go" %}{% endfunc %}`) // multi-cat testParseSuccess(t, `{% func a() %}{% cat "parser.go" %}{% cat "./parser.go" %}{% endfunc %}`) // non-existing file testParseFailure(t, `{% func a() %}{% cat "non-existing-file.go" %}{% endfunc %}`) // non-const string testParseFailure(t, `{% func a() %}{% cat "foobar"+".baz" %}{% endfunc %}`) testParseFailure(t, `{% func a() %}{% cat foobar %}{% endfunc %}`) } func TestParseUnexpectedValueAfterTag(t *testing.T) { // endfunc testParseSuccess(t, "{% func a() %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% endfunc foo bar %}") // endfor testParseSuccess(t, "{% func a() %}{% for %}{% endfor %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% for %}{% endfor foo bar %}{% endfunc %}") // endif testParseSuccess(t, "{% func a() %}{% if true %}{% endif %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% if true %}{% endif foo bar %}{% endfunc %}") // endswitch testParseSuccess(t, "{% func a() %}{% switch %}{% case true %}{% endswitch %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% switch %}{% case true %}{% endswitch foobar %}{% endfunc %}") // else testParseSuccess(t, "{% func a() %}{% if true %}{% else %}{% endif %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% if true %}{% else foo bar %}{% endif %}{% endfunc %}") // return testParseSuccess(t, "{% func a() %}{% return %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% return foobar %}{% endfunc %}") // break testParseSuccess(t, "{% func a() %}{% for %}{% break %}{% endfor %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% for %}{% break foobar %}{% endfor %}{% endfunc %}") // default testParseSuccess(t, "{% func a() %}{% switch %}{% default %}{% endswitch %}{% endfunc %}") testParseFailure(t, "{% func a() %}{% switch %}{% default foobar %}{% endswitch %}{% endfunc %}") } func TestParseFPrecFailure(t *testing.T) { // negative precision testParseFailure(t, "{% func a()%}{%f.-1 1.2 %}{% endfunc %}") // non-numeric precision testParseFailure(t, "{% func a()%}{%f.foo 1.2 %}{% endfunc %}") // more than one dot testParseFailure(t, "{% func a()%}{%f.1.234 1.2 %}{% endfunc %}") testParseFailure(t, "{% func a()%}{%f.1.foo 1.2 %}{% endfunc %}") } func TestParseFPrecSuccess(t *testing.T) { // no precision testParseSuccess(t, "{% func a()%}{%f 1.2 %}{% endfunc %}") testParseSuccess(t, "{% func a()%}{%f= 1.2 %}{% endfunc %}") // precision set testParseSuccess(t, "{% func a()%}{%f.1 1.234 %}{% endfunc %}") testParseSuccess(t, "{% func a()%}{%f.10= 1.234 %}{% endfunc %}") // missing precision testParseSuccess(t, "{% func a()%}{%f. 1.234 %}{% endfunc %}") testParseSuccess(t, "{% func a()%}{%f.= 1.234 %}{% endfunc %}") } func TestParseSwitchCaseSuccess(t *testing.T) { // single-case switch testParseSuccess(t, "{%func a()%}{%switch n%}{%case 1%}aaa{%endswitch%}{%endfunc%}") // multi-case switch testParseSuccess(t, "{%func a()%}{%switch%}\n\t {%case foo()%}\nfoobar{%case bar()%}{%endswitch%}{%endfunc%}") // default statement testParseSuccess(t, "{%func a()%}{%switch%}{%default%}{%endswitch%}{%endfunc%}") // switch with break testParseSuccess(t, "{%func a()%}{%switch n%}{%case 1%}aaa{%break%}ignore{%endswitch%}{%endfunc%}") // switch on a type // See https://github.com/valyala/quicktemplate/issues/77 testParseSuccess(t, "{%func a()%}{%switch a.(type) %}{% case []string %}aaa{%case int%}bbb{%endswitch%}{%endfunc%}") // complex switch testParseSuccess(t, `{%func f()%}{% for %} {%switch foo() %} The text before the first case is converted into a comment {%case "foobar" %} {% switch %} {% case bar() %} aaaa{% break %} ignore this line {% case baz() %} bbbb {% endswitch %} {% case "aaa" %} {% for i := 0; i < 10; i++ %} foobar {% endfor %} {% case "qwe", "sdfdf" %} aaaa {% return %} {% case "www" %} break from the switch {% break %} {% default %} foobar {%endswitch%} {% if 42 == 2 %} break for the loop {% break %} ignore this {% endif %} {% endfor %}{%endfunc%}`) } func TestParseSwitchCaseFailure(t *testing.T) { // missing endswitch testParseFailure(t, "{%func a()%}{%switch%}{%endfunc%}") // empty switch testParseFailure(t, "{%func f()%}{%switch%}{%endswitch%}{%endfunc%}") // case outside switch testParseFailure(t, "{%func f()%}{%case%}{%endfunc%}") // the first tag inside switch is non-case testParseFailure(t, "{%func f()%}{%switch%}{%return%}{%endswitch%}{%endfunc%}") testParseFailure(t, "{%func F()%}{%switch%}{%break%}{%endswitch%}{%endfunc%}") testParseFailure(t, "{%func f()%}{%switch 1%}{%return%}{%case 1%}aaa{%endswitch%}{%endfunc%}") // empty case testParseFailure(t, "{%func f()%}{%switch%}{%case%}aaa{%endswitch%}{%endfunc%}") // multiple default statements testParseFailure(t, "{%func f()%}{%switch%}{%case%}aaa{%default%}bbb{%default%}{%endswitch%}{%endfunc%}") } func TestParseBreakContinueReturn(t *testing.T) { testParseSuccess(t, `{% func a() %}{% for %}{% continue %}{% break %}{% return %}{% endfor %}{% endfunc %}`) testParseSuccess(t, `{% func a() %}{% for %} {% if f1() %}{% continue %}skip this{%s "and this" %}{% endif %} {% if f2() %}{% break %}{% for %}{% endfor %}skip this{% endif %} {% if f3() %}{% return %}foo{% if f4() %}{% for %}noop{% endfor %}{% endif %}bar skip this{% endif %} text {% endfor %}{% endfunc %}`) } func TestParseOutputTagSuccess(t *testing.T) { // identifier testParseSuccess(t, "{%func a()%}{%s foobar %}{%endfunc%}") // method call testParseSuccess(t, "{%func a()%}{%s foo.bar.baz(a, b, &A{d:e}) %}{%endfunc%}") // inline function call testParseSuccess(t, "{%func f()%}{%s func() string { return foo.bar(baz, aaa) }() %}{%endfunc%}") // map testParseSuccess(t, `{%func f()%}{%v map[int]string{1:"foo", 2:"bar"} %}{%endfunc%}`) // jsons-safe string testParseSuccess(t, `{% func f() %}{%j "foo\nbar" %}{%endfunc%}`) // url-encoded string testParseSuccess(t, `{% func A() %}{%u "fooab" %}{%endfunc%}`) } func TestParseOutputTagFailure(t *testing.T) { // empty tag testParseFailure(t, "{%func f()%}{%s %}{%endfunc%}") // multiple arguments testParseFailure(t, "{%func f()%}{%s a, b %}{%endfunc%}") // invalid code testParseFailure(t, "{%func f()%}{%s f(a, %}{%endfunc%}") testParseFailure(t, "{%func f()%}{%s Foo{Bar:1 %}{%endfunc%}") // unsupported code testParseFailure(t, "{%func f()%}{%s if (a) {} %}{%endfunc%}") testParseFailure(t, "{%func f()%}{%s for {} %}{%endfunc%}") } func TestParseTemplateCodeSuccess(t *testing.T) { // empty code testParseSuccess(t, "{% code %}") testParseSuccess(t, "{% func f() %}{% code %}{% endfunc %}") // comment testParseSuccess(t, `{% code // foobar %}`) testParseSuccess(t, `{% func f() %}{% code // foobar %}{% endfunc %}`) testParseSuccess(t, `{% code // foo // bar %}`) testParseSuccess(t, `{% func f() %}{% code // foo // bar %}{% endfunc %}`) testParseSuccess(t, `{% code /* foo bar */ %}`) testParseSuccess(t, `{% func f() %}{% code /* foo bar */ %}{% endfunc %}`) testParseSuccess(t, `{% code var a int %}`) testParseSuccess(t, `{% func f() %}{% code var a int %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{% code a := 0 %}{% endfunc %}`) testParseSuccess(t, `{% func f() %}{% code type A struct{} %}{% endfunc %}`) // declarations testParseSuccess(t, `{%code // comment type Foo struct {} var b = &Foo{} func (f *Foo) Bar() {} // yet another comment func Bar(baz int) string { return fmt.Sprintf("%d", baz) } %}`) } func TestParseTemplateCodeFailure(t *testing.T) { // import inside the code testParseFailure(t, `{% code import "foo" %}`) // incomplete code testParseFailure(t, `{% code type A struct { %}`) testParseFailure(t, `{% code func F() { %}`) // invalid code testParseFailure(t, `{%code { %}`) testParseFailure(t, `{%code {} %}`) testParseFailure(t, `{%code ( %}`) testParseFailure(t, `{%code () %}`) } func TestParseImportSuccess(t *testing.T) { // single line import testParseSuccess(t, `{% import "github.com/foo/bar" %}`) // multiline import testParseSuccess(t, `{% import ( "foo" xxx "bar/baz/aaa" "yyy.com/zzz" ) %}`) // multiple imports testParseSuccess(t, `{% import "foo" %} baaas {% import ( "bar" "baasd" ) %} sddf `) } func TestParseImportFailure(t *testing.T) { // empty import testParseFailure(t, `{%import %}`) // invalid import testParseFailure(t, `{%import foo %}`) // non-import code testParseFailure(t, `{%import {"foo"} %}`) testParseFailure(t, `{%import "foo" type A struct {} %}`) testParseFailure(t, `{%import type a struct{} %}`) } func TestParseFailure(t *testing.T) { // unknown tag testParseFailure(t, "{% foobar %}") // unexpected tag outside func testParseFailure(t, "aaa{% for %}bbb{%endfor%}") testParseFailure(t, "{% return %}") testParseFailure(t, "{% break %}") testParseFailure(t, "{% if 1==1 %}aaa{%endif%}") testParseFailure(t, "{%s abc %}") testParseFailure(t, "{%v= aaaa(xx) %}") testParseFailure(t, "{%= a() %}") // import after func and/or code testParseFailure(t, `{%code var i = 0 %}{%import "fmt"%}`) testParseFailure(t, `{%func f()%}{%endfunc%}{%import "fmt"%}`) // missing endfunc testParseFailure(t, "{%func a() %}aaaa") // empty func name testParseFailure(t, "{% func () %}aaa{% endfunc %}") testParseFailure(t, "{% func (a int, b string) %}aaa{% endfunc %}") // empty func arguments testParseFailure(t, "{% func aaa %}aaa{% endfunc %}") // func with anonymous argument testParseFailure(t, "{% func a(x int, string) %}{%endfunc%}") // func with incorrect arguments' list testParseFailure(t, "{% func x(foo, bar) %}{%endfunc%}") testParseFailure(t, "{% func x(foo bar baz) %}{%endfunc%}") // empty if condition testParseFailure(t, "{% func a() %}{% if %}aaaa{% endif %}{% endfunc %}") // else with content testParseFailure(t, "{% func a() %}{% if 3 == 4%}aaaa{% else if 3 == 5 %}bug{% endif %}{% endfunc %}") // missing endif testParseFailure(t, "{%func a() %}{%if foo %}aaa{% endfunc %}") // missing endfor testParseFailure(t, "{%func a()%}{%for %}aaa{%endfunc%}") // break outside for testParseFailure(t, "{%func a()%}{%break%}{%endfunc%}") // invalid if condition testParseFailure(t, "{%func a()%}{%if a = b %}{%endif%}{%endfunc%}") testParseFailure(t, "{%func f()%}{%if a { %}{%endif%}{%endfunc%}") // invalid for testParseFailure(t, "{%func a()%}{%for a = b %}{%endfor%}{%endfunc%}") testParseFailure(t, "{%func f()%}{%for { %}{%endfor%}{%endfunc%}") // invalid code inside func testParseFailure(t, "{%func f()%}{%code } %}{%endfunc%}") testParseFailure(t, "{%func f()%}{%code { %}{%endfunc%}") // interface inside func testParseFailure(t, "{%func f()%}{%interface A { Foo() } %}{%endfunc%}") // interface without name testParseFailure(t, "{%interface { Foo() } %}") // empty interface testParseFailure(t, "{% interface Foo {} %}") // invalid interface testParseFailure(t, "{%interface aaaa %}") testParseFailure(t, "{%interface aa { Foo() %}") // unnamed method testParseFailure(t, "{%func (s *S) () %}{%endfunc%}") // empty method arguments testParseFailure(t, "{%func (s *S) Foo %}{%endfunc %}") // method with return values testParseFailure(t, "{%func (s *S) Foo() string %}{%endfunc%}") testParseFailure(t, "{%func (s *S) Bar() (int, string) %}{%endfunc%}") } func TestParserSuccess(t *testing.T) { // empty template testParseSuccess(t, "") // template without code and funcs testParseSuccess(t, "foobar\nbaz") // template with code testParseSuccess(t, "{%code var a struct {}\nconst n = 123%}") // import testParseSuccess(t, `{%import "foobar"%}`) testParseSuccess(t, `{% import ( "foo" "bar" )%}`) testParseSuccess(t, `{%import "foo"%}{%import "bar"%}`) // func testParseSuccess(t, "{%func a()%}{%endfunc%}") // func with with condition testParseSuccess(t, "{%func a(x bool)%}{%if x%}foobar{%endif%}{%endfunc%}") // func with complex arguments testParseSuccess(t, "{%func f(h1, h2 func(x, y int) string, d int)%}{%endfunc%}") // for testParseSuccess(t, "{%func a()%}{%for%}aaa{%endfor%}{%endfunc%}") // return testParseSuccess(t, "{%func a()%}{%return%}{%endfunc%}") // nested for testParseSuccess(t, "{%func a()%}{%for i := 0; i < 10; i++ %}{%for j := 0; j < i; j++%}aaa{%endfor%}{%endfor%}{%endfunc%}") // plain containing arbitrary tags testParseSuccess(t, "{%func f()%}{%plain%}This {%endfunc%} is ignored{%endplain%}{%endfunc%}") // comment with arbitrary tags testParseSuccess(t, "{%func f()%}{%comment%}This {%endfunc%} is ignored{%endcomment%}{%endfunc%}") // complex if testParseSuccess(t, "{%func a()%}{%if n, err := w.Write(p); err != nil %}{%endif%}{%endfunc%}") // complex for testParseSuccess(t, "{%func a()%}{%for i, n := 0, len(s); i < n && f(i); i++ %}{%endfor%}{%endfunc%}") // complex code inside func testParseSuccess(t, `{%func f()%}{%code type A struct{} var aa []A for i := 0; i < 10; i++ { aa = append(aa, &A{}) if i == 42 { break } } return %}{%endfunc%}`) // break inside for loop testParseSuccess(t, `{%func f()%}{%for%} {% if a() %} {% break %} {% else %} {% return %} {% endif %} {%endfor%}{%endfunc%}`) // interface testParseSuccess(t, "{%interface Foo { Bar()\nBaz() } %}") testParseSuccess(t, "{%iface Foo { Bar()\nBaz() } %}") // method testParseSuccess(t, "{%func (s *S) Foo(bar, baz string) %}{%endfunc%}") } func testParseFailure(t *testing.T, str string) { r := bytes.NewBufferString(str) w := &bytes.Buffer{} if err := Parse(w, r, "qtc-test.qtpl", "memory"); err == nil { t.Fatalf("expecting error when parsing %q", str) } } func testParseSuccess(t *testing.T, str string) { r := bytes.NewBufferString(str) w := &bytes.Buffer{} if err := Parse(w, r, "qtc-test.qtpl", "memory"); err != nil { t.Fatalf("unexpected error when parsing %q: %s", str, err) } } func TestParseFile(t *testing.T) { currDir, err := os.Getwd() if err != nil { t.Fatalf("cannot obtain current directory: %s", err) } if err := os.Chdir("../testdata/qtc"); err != nil { t.Fatalf("cannot change directory to `../testdata/qtc`: %s", err) } defer func() { if err := os.Chdir(currDir); err != nil { t.Fatalf("cannot change directory to %q: %s", currDir, err) } }() filename := "test.qtpl" f, err := os.Open(filename) if err != nil { t.Fatalf("cannot open file %q: %s", filename, err) } defer f.Close() w := quicktemplate.AcquireByteBuffer() if err := Parse(w, f, "test.qtpl", "qtc"); err != nil { t.Fatalf("unexpected error: %s", err) } code, err := format.Source(w.B) if err != nil { t.Fatalf("unexpected error: %s", err) } quicktemplate.ReleaseByteBuffer(w) expectedFilename := filename + ".expected" expectedCode, err := ioutil.ReadFile(expectedFilename) if err != nil { t.Fatalf("unexpected error: %s", err) } if !bytes.Equal(code, expectedCode) { if err := ioutil.WriteFile(filename+".generated", code, 0644); err != nil { t.Fatal(err) } t.Fatalf("unexpected code:\n%q\nexpected:\n%q", code, expectedCode) } } func TestParseNoLineComments(t *testing.T) { const str = "foobar\nbaz" r := bytes.NewBufferString(str) w := &bytes.Buffer{} if err := ParseNoLineComments(w, r, "qtc-test.qtpl", "memory"); err != nil { t.Fatalf("unexpected error when parsing %q: %s", str, err) } if bytes.Contains(w.Bytes(), []byte("//line")) { t.Fatal("unexpected line comment") } } golang-github-valyala-quicktemplate-1.6.3+ds/parser/scanner.go000066400000000000000000000210101376501567700244360ustar00rootroot00000000000000package parser import ( "bufio" "bytes" "fmt" "io" "regexp" "strings" ) // token ids const ( text = iota tagName tagContents ) var tokenStrMap = map[int]string{ text: "text", tagName: "tagName", tagContents: "tagContents", } func tokenIDToStr(id int) string { str := tokenStrMap[id] if str == "" { panic(fmt.Sprintf("unknown tokenID=%d", id)) } return str } type token struct { ID int Value []byte line int pos int } func (t *token) init(id, line, pos int) { t.ID = id t.Value = t.Value[:0] t.line = line t.pos = pos } func (t *token) String() string { return fmt.Sprintf("Token %q, value %q", tokenIDToStr(t.ID), t.Value) } type scanner struct { r *bufio.Reader t token c byte err error filePath string line int lineStr []byte nextTokenID int capture bool capturedValue []byte collapseSpaceDepth int stripSpaceDepth int stripToNewLine bool rewind bool } var tailOfLine = regexp.MustCompile(`^[[:blank:]]*(?:\r*\n)?`) var prevBlank = regexp.MustCompile(`[[:blank:]]+$`) func newScanner(r io.Reader, filePath string) *scanner { // Substitute backslashes with forward slashes in filePath // for the sake of consistency on different platforms (windows, linux). // See https://github.com/valyala/quicktemplate/issues/62. filePath = strings.Replace(filePath, "\\", "/", -1) return &scanner{ r: bufio.NewReader(r), filePath: filePath, } } func (s *scanner) Rewind() { if s.rewind { panic("BUG: duplicate Rewind call") } s.rewind = true } func (s *scanner) Next() bool { if s.rewind { s.rewind = false return true } for { if !s.scanToken() { return false } switch s.t.ID { case text: if s.stripToNewLine { s.t.Value = tailOfLine.ReplaceAll(s.t.Value, nil) s.stripToNewLine = false } if len(s.t.Value) == 0 { // skip empty text continue } case tagName: switch string(s.t.Value) { case "comment": if !s.skipComment() { return false } continue case "plain": if !s.readPlain() { return false } if len(s.t.Value) == 0 { // skip empty text continue } case "collapsespace": if !s.readTagContents() { return false } s.collapseSpaceDepth++ continue case "stripspace": if !s.readTagContents() { return false } s.stripSpaceDepth++ continue case "endcollapsespace": if s.collapseSpaceDepth == 0 { s.err = fmt.Errorf("endcollapsespace tag found without the corresponding collapsespace tag") return false } if !s.readTagContents() { return false } s.collapseSpaceDepth-- continue case "endstripspace": if s.stripSpaceDepth == 0 { s.err = fmt.Errorf("endstripspace tag found without the corresponding stripspace tag") return false } if !s.readTagContents() { return false } s.stripSpaceDepth-- continue case "space": if !s.readTagContents() { return false } s.t.init(text, s.t.line, s.t.pos) s.t.Value = append(s.t.Value[:0], ' ') return true case "newline": if !s.readTagContents() { return false } s.t.init(text, s.t.line, s.t.pos) s.t.Value = append(s.t.Value[:0], '\n') return true } } return true } } func (s *scanner) readPlain() bool { if !s.readTagContents() { return false } startLine := s.line startPos := s.pos() s.startCapture() ok := s.skipUntilTag("endplain") v := s.stopCapture() s.t.init(text, startLine, startPos) if ok { n := bytes.LastIndex(v, strTagOpen) v = v[:n] s.t.Value = append(s.t.Value[:0], v...) } return ok } var strTagOpen = []byte("{%") func (s *scanner) skipComment() bool { if !s.readTagContents() { return false } return s.skipUntilTag("endcomment") } func (s *scanner) skipUntilTag(tagName string) bool { ok := false for { if !s.nextByte() { break } if s.c != '{' { continue } if !s.nextByte() { break } if s.c != '%' { s.unreadByte('~') continue } ok = s.readTagName() s.nextTokenID = text if !ok { s.err = nil continue } if string(s.t.Value) == tagName { ok = s.readTagContents() break } } if !ok { s.err = fmt.Errorf("cannot find %q tag: %s", tagName, s.err) } return ok } func (s *scanner) scanToken() bool { switch s.nextTokenID { case text: return s.readText() case tagName: return s.readTagName() case tagContents: return s.readTagContents() default: panic(fmt.Sprintf("BUG: unknown nextTokenID %d", s.nextTokenID)) } } func (s *scanner) readText() bool { s.t.init(text, s.line, s.pos()) ok := false for { if !s.nextByte() { ok = (len(s.t.Value) > 0) break } if s.c != '{' { s.appendByte() continue } if !s.nextByte() { s.appendByte() ok = true break } if s.c == '%' { s.nextTokenID = tagName ok = true if !s.nextByte() { s.appendByte() break } if s.c != '-' { s.unreadByte(s.c) break } s.t.Value = prevBlank.ReplaceAll(s.t.Value, nil) break } s.unreadByte('{') s.appendByte() } if s.stripSpaceDepth > 0 { s.t.Value = stripSpace(s.t.Value) } else if s.collapseSpaceDepth > 0 { s.t.Value = collapseSpace(s.t.Value) } return ok } func (s *scanner) readTagName() bool { s.skipSpace() s.t.init(tagName, s.line, s.pos()) for { if s.isSpace() || s.c == '%' { if s.c == '%' { s.unreadByte('~') } s.nextTokenID = tagContents return true } if (s.c >= 'a' && s.c <= 'z') || (s.c >= 'A' && s.c <= 'Z') || (s.c >= '0' && s.c <= '9') || s.c == '=' || s.c == '.' { s.appendByte() if !s.nextByte() { return false } continue } if s.c == '-' { s.unreadByte(s.c) s.nextTokenID = tagContents return true } s.err = fmt.Errorf("unexpected character: '%c'", s.c) s.unreadByte('~') return false } } func (s *scanner) readTagContents() bool { s.skipSpace() s.t.init(tagContents, s.line, s.pos()) for { if s.c != '%' { s.appendByte() if !s.nextByte() { return false } continue } if !s.nextByte() { s.appendByte() return false } if s.c == '}' { if bytes.HasSuffix(s.t.Value, []byte("-")) { s.t.Value = s.t.Value[:len(s.t.Value)-1] s.stripToNewLine = true } s.nextTokenID = text s.t.Value = stripTrailingSpace(s.t.Value) return true } s.unreadByte('%') s.appendByte() if !s.nextByte() { return false } } } func (s *scanner) skipSpace() { for s.nextByte() && s.isSpace() { } } func (s *scanner) isSpace() bool { return isSpace(s.c) } func (s *scanner) nextByte() bool { if s.err != nil { return false } c, err := s.r.ReadByte() if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } s.err = err return false } if c == '\n' { s.line++ s.lineStr = s.lineStr[:0] } else { s.lineStr = append(s.lineStr, c) } s.c = c if s.capture { s.capturedValue = append(s.capturedValue, c) } return true } func (s *scanner) startCapture() { s.capture = true s.capturedValue = s.capturedValue[:0] } func (s *scanner) stopCapture() []byte { s.capture = false v := s.capturedValue s.capturedValue = s.capturedValue[:0] return v } func (s *scanner) Token() *token { return &s.t } func (s *scanner) LastError() error { if s.err == nil { return nil } if s.err == io.ErrUnexpectedEOF && s.t.ID == text { if s.collapseSpaceDepth > 0 { return fmt.Errorf("missing endcollapsespace tag at %s", s.Context()) } if s.stripSpaceDepth > 0 { return fmt.Errorf("missing endstripspace tag at %s", s.Context()) } return nil } return fmt.Errorf("error when reading %s at %s: %s", tokenIDToStr(s.t.ID), s.Context(), s.err) } func (s *scanner) appendByte() { s.t.Value = append(s.t.Value, s.c) } func (s *scanner) unreadByte(c byte) { if err := s.r.UnreadByte(); err != nil { panic(fmt.Sprintf("BUG: bufio.Reader.UnreadByte returned non-nil error: %s", err)) } if s.capture { s.capturedValue = s.capturedValue[:len(s.capturedValue)-1] } if s.c == '\n' { s.line-- s.lineStr = s.lineStr[:0] // TODO: use correct line } else { s.lineStr = s.lineStr[:len(s.lineStr)-1] } s.c = c } func (s *scanner) pos() int { return len(s.lineStr) } func (s *scanner) Context() string { t := s.Token() return fmt.Sprintf("file %q, line %d, pos %d, token %s, last line %s", s.filePath, t.line+1, t.pos, snippet(t.Value), snippet(s.lineStr)) } func (s *scanner) WriteLineComment(w io.Writer) { fmt.Fprintf(w, "//line %s:%d\n", s.filePath, s.t.line+1) } func snippet(s []byte) string { if len(s) <= 40 { return fmt.Sprintf("%q", s) } return fmt.Sprintf("%q ... %q", s[:20], s[len(s)-20:]) } golang-github-valyala-quicktemplate-1.6.3+ds/parser/scanner_test.go000066400000000000000000000235331376501567700255110ustar00rootroot00000000000000package parser import ( "bytes" "reflect" "testing" ) func TestScannerTagNameWithDotAndEqual(t *testing.T) { testScannerSuccess(t, "{%foo.bar.34 baz aaa%} awer{% aa= %}", []tt{ {ID: tagName, Value: "foo.bar.34"}, {ID: tagContents, Value: "baz aaa"}, {ID: text, Value: " awer"}, {ID: tagName, Value: "aa="}, {ID: tagContents, Value: ""}, }) } func TestScannerEndTagWithMinus(t *testing.T) { testScannerSuccess(t, "{%foo baz aaa-%} awer{% aa= -%}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: "baz aaa"}, {ID: text, Value: "awer"}, {ID: tagName, Value: "aa="}, {ID: tagContents, Value: ""}, }) testScannerSuccess(t, "{%foo baz aaa- %} awer{% aa= -% %}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: "baz aaa-"}, {ID: text, Value: " awer"}, {ID: tagName, Value: "aa="}, {ID: tagContents, Value: "-%"}, }) testScannerSuccess(t, "{%foo -%} \t\r\r\n\n\tawer{%aa= - %} \nbb", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: ""}, {ID: text, Value: "\n\tawer"}, {ID: tagName, Value: "aa="}, {ID: tagContents, Value: "-"}, {ID: text, Value: " \nbb"}, }) testScannerSuccess(t, "{%foo-%}\n awer{%bar -%}\n", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: ""}, {ID: text, Value: " awer"}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: ""}, }) testScannerSuccess(t, "{%foo-%}{%bar%} \n {%baz%}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: ""}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: ""}, {ID: text, Value: " \n "}, {ID: tagName, Value: "baz"}, {ID: tagContents, Value: ""}, }) } func TestScannerBeginTagWithMinus(t *testing.T) { testScannerSuccess(t, "x\n {%-foo%} {%- bar%}", []tt{ {ID: text, Value: "x\n"}, {ID: tagName, Value: "foo"}, {ID: tagContents, Value: ""}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: ""}, }) testScannerSuccess(t, "{%- foo%}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: ""}, }) testScannerSuccess(t, "{%-foo baz aaa%} awer\n\t {%-aa= %}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: "baz aaa"}, {ID: text, Value: " awer\n"}, {ID: tagName, Value: "aa="}, {ID: tagContents, Value: ""}, }) testScannerSuccess(t, "{%-foo baz aaa%} awer {- % aa= %}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: "baz aaa"}, {ID: text, Value: " awer {- % aa= %}"}, }) testScannerSuccess(t, "\n\n {%- foo baz aaa -%} awer {-{%- aa= xxx%}", []tt{ {ID: text, Value: "\n\n"}, {ID: tagName, Value: "foo"}, {ID: tagContents, Value: "baz aaa"}, {ID: text, Value: "awer {-"}, {ID: tagName, Value: "aa="}, {ID: tagContents, Value: "xxx"}, }) testScannerSuccess(t, "{%-foo%}{%bar%} \n {%-baz%}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: ""}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: ""}, {ID: text, Value: " \n"}, {ID: tagName, Value: "baz"}, {ID: tagContents, Value: ""}, }) } func TestScannerStripspaceSuccess(t *testing.T) { testScannerSuccess(t, " aa\n\t {%stripspace%} \t\n f\too \n b ar \n\r\t {% bar baz asd %}\n\nbaz \n\t \taaa \n{%endstripspace%} bb ", []tt{ {ID: text, Value: " aa\n\t "}, {ID: text, Value: "f\toob ar"}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: "baz asd"}, {ID: text, Value: "bazaaa"}, {ID: text, Value: " bb "}, }) testScannerSuccess(t, "{%stripspace %}{% stripspace fobar %} {%space%} a\taa\n\r\t bb b {%endstripspace %} {%endstripspace baz%}", []tt{ {ID: text, Value: " "}, {ID: text, Value: "a\taabb b"}, }) // sripspace wins over collapsespace testScannerSuccess(t, "{%stripspace%} {%collapsespace%}foo\n\t bar{%endcollapsespace%} \r\n\t {%endstripspace%}", []tt{ {ID: text, Value: "foobar"}, }) } func TestScannerStripspaceFailure(t *testing.T) { // incomplete stripspace tag testScannerFailure(t, "{%stripspace ") // incomplete endstripspace tag testScannerFailure(t, "{%stripspace%}aaa{%endstripspace") // missing endstripspace testScannerFailure(t, "{%stripspace%} foobar") // missing stripspace testScannerFailure(t, "aaa{%endstripspace%}") // missing the second endstripspace testScannerFailure(t, "{%stripspace%}{%stripspace%}aaaa{%endstripspace%}") } func TestScannerCollapsespaceSuccess(t *testing.T) { testScannerSuccess(t, " aa\n\t {%collapsespace%} \t\n foo \n bar{% bar baz asd %}\n\nbaz \n \n{%endcollapsespace%} bb ", []tt{ {ID: text, Value: " aa\n\t "}, {ID: text, Value: " foo bar"}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: "baz asd"}, {ID: text, Value: " baz "}, {ID: text, Value: " bb "}, }) testScannerSuccess(t, "{%collapsespace %}{% collapsespace fobar %} {%space%} aaa\n\r\t bbb {%endcollapsespace %} {%endcollapsespace baz%}", []tt{ {ID: text, Value: " "}, {ID: text, Value: " "}, {ID: text, Value: " aaa bbb "}, {ID: text, Value: " "}, }) } func TestScannerCollapsespaceFailure(t *testing.T) { // incomplete collapsespace tag testScannerFailure(t, "{%collapsespace ") // incomplete endcollapsespace tag testScannerFailure(t, "{%collapsespace%}aaa{%endcollapsespace") // missing endcollapsespace testScannerFailure(t, "{%collapsespace%} foobar") // missing collapsespace testScannerFailure(t, "aaa{%endcollapsespace%}") // missing the second endcollapsespace testScannerFailure(t, "{%collapsespace%}{%collapsespace%}aaaa{%endcollapsespace%}") } func TestScannerPlainSuccess(t *testing.T) { testScannerSuccess(t, "{%plain%}{%endplain%}", nil) testScannerSuccess(t, "{%plain%}{%foo bar%}asdf{%endplain%}", []tt{ {ID: text, Value: "{%foo bar%}asdf"}, }) testScannerSuccess(t, "{%plain%}{%foo{%endplain%}", []tt{ {ID: text, Value: "{%foo"}, }) testScannerSuccess(t, "aa{%plain%}bbb{%cc%}{%endplain%}{%plain%}dsff{%endplain%}", []tt{ {ID: text, Value: "aa"}, {ID: text, Value: "bbb{%cc%}"}, {ID: text, Value: "dsff"}, }) testScannerSuccess(t, "mmm{%plain%}aa{% bar {%%% }baz{%endplain%}nnn", []tt{ {ID: text, Value: "mmm"}, {ID: text, Value: "aa{% bar {%%% }baz"}, {ID: text, Value: "nnn"}, }) testScannerSuccess(t, "{% plain dsd %}0{%comment%}123{%endcomment%}45{% endplain aaa %}", []tt{ {ID: text, Value: "0{%comment%}123{%endcomment%}45"}, }) } func TestScannerPlainFailure(t *testing.T) { testScannerFailure(t, "{%plain%}sdfds") testScannerFailure(t, "{%plain%}aaaa%{%endplain") testScannerFailure(t, "{%plain%}{%endplain%") } func TestScannerCommentSuccess(t *testing.T) { testScannerSuccess(t, "{%comment%}{%endcomment%}", nil) testScannerSuccess(t, "{%comment%}foo{%endcomment%}", nil) testScannerSuccess(t, "{%comment%}foo{%endcomment%}{%comment%}sss{%endcomment%}", nil) testScannerSuccess(t, "{%comment%}foo{%bar%}{%endcomment%}", nil) testScannerSuccess(t, "{%comment%}foo{%bar {%endcomment%}", nil) testScannerSuccess(t, "{%comment%}foo{%bar&^{%endcomment%}", nil) testScannerSuccess(t, "{%comment%}foo{% bar\n\rs%{%endcomment%}", nil) testScannerSuccess(t, "xx{%x%}www{% comment aux data %}aaa{% comment %}{% endcomment %}yy", []tt{ {ID: text, Value: "xx"}, {ID: tagName, Value: "x"}, {ID: tagContents, Value: ""}, {ID: text, Value: "www"}, {ID: text, Value: "yy"}, }) } func TestScannerCommentFailure(t *testing.T) { testScannerFailure(t, "{%comment%}...no endcomment") testScannerFailure(t, "{% comment %}foobar{% endcomment") } func TestScannerSuccess(t *testing.T) { testScannerSuccess(t, "", nil) testScannerSuccess(t, "a%}{foo}bar", []tt{ {ID: text, Value: "a%}{foo}bar"}, }) testScannerSuccess(t, "{% foo bar baz(a, b, 123) %}", []tt{ {ID: tagName, Value: "foo"}, {ID: tagContents, Value: "bar baz(a, b, 123)"}, }) testScannerSuccess(t, "foo{%bar%}baz", []tt{ {ID: text, Value: "foo"}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: ""}, {ID: text, Value: "baz"}, }) testScannerSuccess(t, "{{{%\n\r\tfoo bar\n\rbaz%%\n \r %}}", []tt{ {ID: text, Value: "{{"}, {ID: tagName, Value: "foo"}, {ID: tagContents, Value: "bar\n\rbaz%%"}, {ID: text, Value: "}"}, }) testScannerSuccess(t, "{%%}", []tt{ {ID: tagName, Value: ""}, {ID: tagContents, Value: ""}, }) testScannerSuccess(t, "{%%aaa bb%}", []tt{ {ID: tagName, Value: ""}, {ID: tagContents, Value: "%aaa bb"}, }) testScannerSuccess(t, "foo{% bar %}{% baz aa (123)%}321", []tt{ {ID: text, Value: "foo"}, {ID: tagName, Value: "bar"}, {ID: tagContents, Value: ""}, {ID: tagName, Value: "baz"}, {ID: tagContents, Value: "aa (123)"}, {ID: text, Value: "321"}, }) } func TestScannerFailure(t *testing.T) { testScannerFailure(t, "a{%") testScannerFailure(t, "a{%foo") testScannerFailure(t, "a{%% }foo") testScannerFailure(t, "a{% foo %") testScannerFailure(t, "b{% fo() %}bar") testScannerFailure(t, "aa{% foo bar") testScannerFailure(t, "a{%-") testScannerFailure(t, "a{%-foo") testScannerFailure(t, "a{%-% }foo") testScannerFailure(t, "a{%- foo %") testScannerFailure(t, "b{%- fo() %}bar") testScannerFailure(t, "aa{%- foo bar") } func testScannerFailure(t *testing.T, str string) { r := bytes.NewBufferString(str) s := newScanner(r, "memory") var tokens []tt for s.Next() { tokens = append(tokens, tt{ ID: s.Token().ID, Value: string(s.Token().Value), }) } if err := s.LastError(); err == nil { t.Fatalf("expecting error when scanning %q. got tokens %v", str, tokens) } } func testScannerSuccess(t *testing.T, str string, expectedTokens []tt) { r := bytes.NewBufferString(str) s := newScanner(r, "memory") var tokens []tt for s.Next() { tokens = append(tokens, tt{ ID: s.Token().ID, Value: string(s.Token().Value), }) } if err := s.LastError(); err != nil { t.Fatalf("unexpected error: %s. str=%q", err, str) } if !reflect.DeepEqual(tokens, expectedTokens) { t.Fatalf("unexpected tokens %v. Expecting %v. str=%q", tokens, expectedTokens, str) } } type tt struct { ID int Value string } golang-github-valyala-quicktemplate-1.6.3+ds/parser/util.go000066400000000000000000000032731376501567700237750ustar00rootroot00000000000000package parser import ( "bytes" "errors" "io/ioutil" "path/filepath" "unicode" ) // mangleSuffix is used for mangling quicktemplate-specific names // in the generated code, so they don't clash with user-provided names. const mangleSuffix = "422016" func stripLeadingSpace(b []byte) []byte { for len(b) > 0 && isSpace(b[0]) { b = b[1:] } return b } func stripTrailingSpace(b []byte) []byte { for len(b) > 0 && isSpace(b[len(b)-1]) { b = b[:len(b)-1] } return b } func collapseSpace(b []byte) []byte { return stripSpaceExt(b, true) } func stripSpace(b []byte) []byte { return stripSpaceExt(b, false) } func stripSpaceExt(b []byte, isCollapse bool) []byte { if len(b) == 0 { return b } var dst []byte if isCollapse && isSpace(b[0]) { dst = append(dst, ' ') } isLastSpace := isSpace(b[len(b)-1]) for len(b) > 0 { n := bytes.IndexByte(b, '\n') if n < 0 { n = len(b) } z := b[:n] if n == len(b) { b = b[n:] } else { b = b[n+1:] } z = stripLeadingSpace(z) z = stripTrailingSpace(z) if len(z) == 0 { continue } dst = append(dst, z...) if isCollapse { dst = append(dst, ' ') } } if isCollapse && !isLastSpace && len(dst) > 0 { dst = dst[:len(dst)-1] } return dst } func isSpace(c byte) bool { return unicode.IsSpace(rune(c)) } func isUpper(c byte) bool { return unicode.IsUpper(rune(c)) } func readFile(cwd, filename string) ([]byte, error) { if len(filename) == 0 { return nil, errors.New("filename cannot be empty") } if filename[0] != '/' { cwdAbs, err := filepath.Abs(cwd) if err != nil { return nil, err } dir, _ := filepath.Split(cwdAbs) filename = filepath.Join(dir, filename) } return ioutil.ReadFile(filename) } golang-github-valyala-quicktemplate-1.6.3+ds/qtc/000077500000000000000000000000001376501567700217575ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/qtc/LICENSE000066400000000000000000000021141376501567700227620ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Aliaksandr Valialkin, VertaMedia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-valyala-quicktemplate-1.6.3+ds/qtc/README.md000066400000000000000000000017031376501567700232370ustar00rootroot00000000000000# qtc Template compiler (converter) for [quicktemplate](https://github.com/valyala/quicktemplate). Converts quicktemplate files into Go code. By default these files have `.qtpl` extension. # Usage ``` $ go get -u github.com/valyala/quicktemplate/qtc $ qtc -h ``` `qtc` may be called either directly or via [go generate](https://blog.golang.org/generate). The latter case is preffered. Just put the following line near the `main` function: ```go package main //go:generate qtc -dir=path/to/directory/with/templates func main() { // main code here } ``` Then run `go generate` whenever you need re-generating template code. Directory with templates may contain arbirary number of subdirectories - `qtc` generates template code recursively for each subdirectory. Directories with templates may also contain arbitrary `.go` files - contents of these files may be used inside templates. Such Go files usually contain various helper functions and structs. golang-github-valyala-quicktemplate-1.6.3+ds/qtc/main.go000066400000000000000000000103721376501567700232350ustar00rootroot00000000000000// Command qtc is a compiler for quicktemplate files. // // See https://github.com/valyala/quicktemplate/qtc for details. package main import ( "flag" "go/format" "io/ioutil" "log" "os" "path/filepath" "sort" "strings" "github.com/valyala/quicktemplate/parser" ) var ( dir = flag.String("dir", ".", "Path to directory with template files to compile. "+ "Only files with ext extension are compiled. See ext flag for details.\n"+ "The compiler recursively processes all the subdirectories.\n"+ "Compiled template files are placed near the original file with .go extension added.") file = flag.String("file", "", "Path to template file to compile.\n"+ "Flags -dir and -ext are ignored if file is set.\n"+ "The compiled file will be placed near the original file with .go extension added.") ext = flag.String("ext", "qtpl", "Only files with this extension are compiled") skipLineComments = flag.Bool("skipLineComments", false, "Don't write line comments") ) var logger = log.New(os.Stderr, "qtc: ", log.LstdFlags) var filesCompiled int func main() { flag.Parse() if len(*file) > 0 { compileSingleFile(*file) return } if len(*ext) == 0 { logger.Fatalf("ext cannot be empty") } if len(*dir) == 0 { *dir = "." } if (*ext)[0] != '.' { *ext = "." + *ext } logger.Printf("Compiling *%s template files in directory %q", *ext, *dir) compileDir(*dir) logger.Printf("Total files compiled: %d", filesCompiled) } func compileSingleFile(filename string) { fi, err := os.Stat(filename) if err != nil { logger.Fatalf("cannot stat file %q: %s", filename, err) } if fi.IsDir() { logger.Fatalf("cannot compile directory %q. Use -dir flag", filename) } compileFile(filename) } func compileDir(path string) { fi, err := os.Stat(path) if err != nil { logger.Fatalf("cannot compile files in %q: %s", path, err) } if !fi.IsDir() { logger.Fatalf("cannot compile files in %q: it is not directory", path) } d, err := os.Open(path) if err != nil { logger.Fatalf("cannot compile files in %q: %s", path, err) } defer d.Close() fis, err := d.Readdir(-1) if err != nil { logger.Fatalf("cannot read files in %q: %s", path, err) } var names []string for _, fi = range fis { name := fi.Name() if name == "." || name == ".." { continue } if !fi.IsDir() { names = append(names, name) } else { subPath := filepath.Join(path, name) compileDir(subPath) } } sort.Strings(names) for _, name := range names { if strings.HasSuffix(name, *ext) { filename := filepath.Join(path, name) compileFile(filename) } } } func compileFile(infile string) { outfile := infile + ".go" logger.Printf("Compiling %q to %q...", infile, outfile) inf, err := os.Open(infile) if err != nil { logger.Fatalf("cannot open file %q: %s", infile, err) } tmpfile := outfile + ".tmp" outf, err := os.Create(tmpfile) if err != nil { logger.Fatalf("cannot create file %q: %s", tmpfile, err) } packageName, err := getPackageName(infile) if err != nil { logger.Fatalf("cannot determine package name for %q: %s", infile, err) } parseFunc := parser.Parse if *skipLineComments { parseFunc = parser.ParseNoLineComments } if err = parseFunc(outf, inf, infile, packageName); err != nil { logger.Fatalf("error when parsing file %q: %s", infile, err) } if err = outf.Close(); err != nil { logger.Fatalf("error when closing file %q: %s", tmpfile, err) } if err = inf.Close(); err != nil { logger.Fatalf("error when closing file %q: %s", infile, err) } // prettify the output file uglyCode, err := ioutil.ReadFile(tmpfile) if err != nil { logger.Fatalf("cannot read file %q: %s", tmpfile, err) } prettyCode, err := format.Source(uglyCode) if err != nil { logger.Fatalf("error when formatting compiled code for %q: %s. See %q for details", infile, err, tmpfile) } if err = ioutil.WriteFile(outfile, prettyCode, 0666); err != nil { logger.Fatalf("error when writing file %q: %s", outfile, err) } if err = os.Remove(tmpfile); err != nil { logger.Fatalf("error when removing file %q: %s", tmpfile, err) } filesCompiled++ } func getPackageName(filename string) (string, error) { filenameAbs, err := filepath.Abs(filename) if err != nil { return "", err } dir, _ := filepath.Split(filenameAbs) return filepath.Base(dir), nil } golang-github-valyala-quicktemplate-1.6.3+ds/testdata/000077500000000000000000000000001376501567700230015ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/testdata/qtc/000077500000000000000000000000001376501567700235705ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/testdata/qtc/.gitignore000066400000000000000000000000141376501567700255530ustar00rootroot00000000000000*.generated golang-github-valyala-quicktemplate-1.6.3+ds/testdata/qtc/test.qtpl000066400000000000000000000102471376501567700254550ustar00rootroot00000000000000This is a test template file. All the lines outside func and code are just comments. Optional package name must be at the top of template. By default package name is identical to the current folder name. {% package templates %} Optional imports must be at the top of template {% import ( "fmt" "strconv" ) %} // Arbitrary go code may be inserted here. For instance, type definition: {% code type FooArgs struct { S string N int } %} Now define an exported function template {% func Foo(a []FooArgs) %}

Hello, I'm Foo!

My args are: {% if len(a) == 0 %} no args! {% elseif len(a) == 1 %} a single arg: {%= printArgs(0, &a[0]) %} {% else %} {% endif %}
{% plain %} Arbitrary tags are treated as plaintext inside plain. For instance, {% foo %} {% bar %} {% for %} {% func %} {% code %} {% return %} {% break %} {% comment %} and even {% unclosed tag {% endplain %} {% collapsespace %} Leading and trailing space between lines and tags is collapsed inside collapsespace unless {%space%} or {%newline%} is used {% endcollapsespace %} {% stripspace %} Leading and trailing space between lines and tags is completely removed unless {%space%} or {%newline%} is used {% endstripspace %} {% cat "test.qtpl" %} {% endfunc %} {%plain%} Now define private printArgs, which is called in Foo via {%= %} tag {%endplain%} {% func printArgs(i int, a *FooArgs) %} {% if i == 0 %} Hide args for i = 0 {% return %} All the stuff after return is ignored: {% if 123 %}this{% endif %} {% for %}And this: {% break %} {% return %}{% endfor %} {% endif %}
  • a[{%d i %}] = {S: {%q a.S %}, SS: {%qz []byte(a.S) %}, N: {%dl int64(a.N) %}, NN: {%dul uint64(a.N) %}}
    {%s a.S %}, {%z []byte(a.S) %}, {%sz []byte(a.S) %} {%f 1.234 %}, {%f.1 1.234 %}, {% f.2= 1.234 %} alert("foo {%j "bar\naaa" %} baz {%jz []byte("aaa") %}")
    \"'&1"%}">test1 test2
  • Switch statement: {% stripspace %} a.S = {% switch a.S %} {% case "foo" %} foo {%break%} ignore this {% case "bar" %} bar {% default %} {%q a.S %} {% endswitch %} {% endstripspace %} {% endfunc %} Now create page template interface. {% iface Page { // Must print template head Head() // Must print template body Body(title string) } %} This function prints arbitrary page. {% func PrintPage(p Page, title string) %} {%= p.Head() %} {%= p.Body(title) %} {% endfunc %} Implement contacts page {% code type ContactsPage struct {} %} {% func (b *ContactsPage) Head() %}Contacts!{% endfunc %} {% func (b *ContactsPage) Body(title string) %}Put here contact info{% endfunc %} Implement HomePage {% code type Homepage struct {} %} {% func (h *Homepage) Head() %}Homepage{% endfunc %} {% func (h *Homepage) Body(title string) %} Title: {%s= title %} Homepage body {% endfunc %} unused code may be commented: {% comment %} {% func UnusedFunc(n int) %} foobar {% endfunc %} {% endcomment %} variadic function {% func Variadic(a int, b ...string) %} a = {%d a %} {% for i, s := range b %} {%d i %}: {%s s %} {% endfor %} {% endfunc %} golang-github-valyala-quicktemplate-1.6.3+ds/testdata/qtc/test.qtpl.expected000066400000000000000000000473071376501567700272640ustar00rootroot00000000000000// Code generated by qtc from "test.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. // This is a test template file. // All the lines outside func and code are just comments. // // Optional package name must be at the top of template. // By default package name is identical to the current folder name. //line test.qtpl:6 package templates // Optional imports must be at the top of template //line test.qtpl:9 import ( "fmt" "strconv" ) // Arbitrary go code may be inserted here. For instance, type definition: //line test.qtpl:16 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line test.qtpl:16 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line test.qtpl:17 type FooArgs struct { S string N int } // Now define an exported function template //line test.qtpl:24 func StreamFoo(qw422016 *qt422016.Writer, a []FooArgs) { //line test.qtpl:24 qw422016.N().S(`

    Hello, I'm Foo!

    My args are: `) //line test.qtpl:28 if len(a) == 0 { //line test.qtpl:28 qw422016.N().S(` no args! `) //line test.qtpl:30 } else if len(a) == 1 { //line test.qtpl:30 qw422016.N().S(` a single arg: `) //line test.qtpl:31 streamprintArgs(qw422016, 0, &a[0]) //line test.qtpl:31 qw422016.N().S(` `) //line test.qtpl:32 } else { //line test.qtpl:32 qw422016.N().S(` `) //line test.qtpl:58 } //line test.qtpl:58 qw422016.N().S(`
    `) //line test.qtpl:60 qw422016.N().S(` Arbitrary tags are treated as plaintext inside plain. For instance, {% foo %} {% bar %} {% for %} {% func %} {% code %} {% return %} {% break %} {% comment %} and even {% unclosed tag `) //line test.qtpl:65 qw422016.N().S(` `) //line test.qtpl:66 qw422016.N().S(` Leading and trailing space between lines and tags is collapsed inside collapsespace unless `) //line test.qtpl:68 qw422016.N().S(` `) //line test.qtpl:68 qw422016.N().S(` or `) //line test.qtpl:68 qw422016.N().S(` `) //line test.qtpl:68 qw422016.N().S(` is used `) //line test.qtpl:69 qw422016.N().S(` `) //line test.qtpl:70 qw422016.N().S(`Leading and trailing space between lines and tags is completelyremoved unless`) //line test.qtpl:72 qw422016.N().S(` `) //line test.qtpl:72 qw422016.N().S(`or`) //line test.qtpl:72 qw422016.N().S(` `) //line test.qtpl:72 qw422016.N().S(`is used`) //line test.qtpl:73 qw422016.N().S(` `) //line test.qtpl:74 qw422016.N().S(`This is a test template file. All the lines outside func and code are just comments. Optional package name must be at the top of template. By default package name is identical to the current folder name. {% package templates %} Optional imports must be at the top of template {% import ( "fmt" "strconv" ) %} // Arbitrary go code may be inserted here. For instance, type definition: {% code type FooArgs struct { S string N int } %} Now define an exported function template {% func Foo(a []FooArgs) %}

    Hello, I'm Foo!

    My args are: {% if len(a) == 0 %} no args! {% elseif len(a) == 1 %} a single arg: {%= printArgs(0, &a[0]) %} {% else %} {% endif %}
    {% plain %} Arbitrary tags are treated as plaintext inside plain. For instance, {% foo %} {% bar %} {% for %} {% func %} {% code %} {% return %} {% break %} {% comment %} and even {% unclosed tag {% endplain %} {% collapsespace %} Leading and trailing space between lines and tags is collapsed inside collapsespace unless {%space%} or {%newline%} is used {% endcollapsespace %} {% stripspace %} Leading and trailing space between lines and tags is completely removed unless {%space%} or {%newline%} is used {% endstripspace %} {% cat "test.qtpl" %} {% endfunc %} {%plain%} Now define private printArgs, which is called in Foo via {%= %} tag {%endplain%} {% func printArgs(i int, a *FooArgs) %} {% if i == 0 %} Hide args for i = 0 {% return %} All the stuff after return is ignored: {% if 123 %}this{% endif %} {% for %}And this: {% break %} {% return %}{% endfor %} {% endif %}
  • a[{%d i %}] = {S: {%q a.S %}, SS: {%qz []byte(a.S) %}, N: {%dl int64(a.N) %}, NN: {%dul uint64(a.N) %}}
    {%s a.S %}, {%z []byte(a.S) %}, {%sz []byte(a.S) %} {%f 1.234 %}, {%f.1 1.234 %}, {% f.2= 1.234 %} alert("foo {%j "bar\naaa" %} baz {%jz []byte("aaa") %}")
    \"'&1"%}">test1 test2
  • Switch statement: {% stripspace %} a.S = {% switch a.S %} {% case "foo" %} foo {%break%} ignore this {% case "bar" %} bar {% default %} {%q a.S %} {% endswitch %} {% endstripspace %} {% endfunc %} Now create page template interface. {% iface Page { // Must print template head Head() // Must print template body Body(title string) } %} This function prints arbitrary page. {% func PrintPage(p Page, title string) %} {%= p.Head() %} {%= p.Body(title) %} {% endfunc %} Implement contacts page {% code type ContactsPage struct {} %} {% func (b *ContactsPage) Head() %}Contacts!{% endfunc %} {% func (b *ContactsPage) Body(title string) %}Put here contact info{% endfunc %} Implement HomePage {% code type Homepage struct {} %} {% func (h *Homepage) Head() %}Homepage{% endfunc %} {% func (h *Homepage) Body(title string) %} Title: {%s= title %} Homepage body {% endfunc %} unused code may be commented: {% comment %} {% func UnusedFunc(n int) %} foobar {% endfunc %} {% endcomment %} variadic function {% func Variadic(a int, b ...string) %} a = {%d a %} {% for i, s := range b %} {%d i %}: {%s s %} {% endfor %} {% endfunc %} `) //line test.qtpl:74 qw422016.N().S(` `) //line test.qtpl:75 } //line test.qtpl:75 func WriteFoo(qq422016 qtio422016.Writer, a []FooArgs) { //line test.qtpl:75 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:75 StreamFoo(qw422016, a) //line test.qtpl:75 qt422016.ReleaseWriter(qw422016) //line test.qtpl:75 } //line test.qtpl:75 func Foo(a []FooArgs) string { //line test.qtpl:75 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:75 WriteFoo(qb422016, a) //line test.qtpl:75 qs422016 := string(qb422016.B) //line test.qtpl:75 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:75 return qs422016 //line test.qtpl:75 } // Now define private printArgs, which is called in Foo via {%= %} tag //line test.qtpl:80 func streamprintArgs(qw422016 *qt422016.Writer, i int, a *FooArgs) { //line test.qtpl:80 qw422016.N().S(` `) //line test.qtpl:81 if i == 0 { //line test.qtpl:81 qw422016.N().S(` Hide args for i = 0 `) //line test.qtpl:83 return //line test.qtpl:87 } //line test.qtpl:87 qw422016.N().S(`
  • a[`) //line test.qtpl:89 qw422016.N().D(i) //line test.qtpl:89 qw422016.N().S(`] = {S: `) //line test.qtpl:89 qw422016.E().Q(a.S) //line test.qtpl:89 qw422016.N().S(`, SS: `) //line test.qtpl:89 qw422016.E().QZ([]byte(a.S)) //line test.qtpl:89 qw422016.N().S(`, N: `) //line test.qtpl:89 qw422016.N().DL(int64(a.N)) //line test.qtpl:89 qw422016.N().S(`, NN: `) //line test.qtpl:89 qw422016.N().DUL(uint64(a.N)) //line test.qtpl:89 qw422016.N().S(`}
    `) //line test.qtpl:90 qw422016.E().S(a.S) //line test.qtpl:90 qw422016.N().S(`, `) //line test.qtpl:90 qw422016.E().Z([]byte(a.S)) //line test.qtpl:90 qw422016.N().S(`, `) //line test.qtpl:90 qw422016.E().SZ([]byte(a.S)) //line test.qtpl:90 qw422016.N().S(` `) //line test.qtpl:91 qw422016.N().F(1.234) //line test.qtpl:91 qw422016.N().S(`, `) //line test.qtpl:91 qw422016.N().FPrec(1.234, 1) //line test.qtpl:91 qw422016.N().S(`, `) //line test.qtpl:91 qw422016.N().FPrec(1.234, 2) //line test.qtpl:91 qw422016.N().S(` alert("foo `) //line test.qtpl:92 qw422016.E().J("bar\naaa") //line test.qtpl:92 qw422016.N().S(` baz `) //line test.qtpl:92 qw422016.E().JZ([]byte("aaa")) //line test.qtpl:92 qw422016.N().S(`")
    \"'&1") //line test.qtpl:93 qw422016.N().S(`">test1 test2
  • Switch statement: `) //line test.qtpl:98 qw422016.N().S(`a.S =`) //line test.qtpl:100 switch a.S { //line test.qtpl:101 case "foo": //line test.qtpl:101 qw422016.N().S(`foo`) //line test.qtpl:103 break //line test.qtpl:105 case "bar": //line test.qtpl:105 qw422016.N().S(`bar`) //line test.qtpl:107 default: //line test.qtpl:108 qw422016.E().Q(a.S) //line test.qtpl:109 } //line test.qtpl:110 qw422016.N().S(` `) //line test.qtpl:111 } //line test.qtpl:111 func writeprintArgs(qq422016 qtio422016.Writer, i int, a *FooArgs) { //line test.qtpl:111 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:111 streamprintArgs(qw422016, i, a) //line test.qtpl:111 qt422016.ReleaseWriter(qw422016) //line test.qtpl:111 } //line test.qtpl:111 func printArgs(i int, a *FooArgs) string { //line test.qtpl:111 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:111 writeprintArgs(qb422016, i, a) //line test.qtpl:111 qs422016 := string(qb422016.B) //line test.qtpl:111 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:111 return qs422016 //line test.qtpl:111 } // Now create page template interface. //line test.qtpl:115 type Page interface { //line test.qtpl:115 Head() string //line test.qtpl:115 StreamHead(qw422016 *qt422016.Writer) //line test.qtpl:115 WriteHead(qq422016 qtio422016.Writer) //line test.qtpl:115 Body(title string) string //line test.qtpl:115 StreamBody(qw422016 *qt422016.Writer, title string) //line test.qtpl:115 WriteBody(qq422016 qtio422016.Writer, title string) //line test.qtpl:115 } // This function prints arbitrary page. //line test.qtpl:125 func StreamPrintPage(qw422016 *qt422016.Writer, p Page, title string) { //line test.qtpl:125 qw422016.N().S(` `) //line test.qtpl:127 p.StreamHead(qw422016) //line test.qtpl:127 qw422016.N().S(` `) //line test.qtpl:128 p.StreamBody(qw422016, title) //line test.qtpl:128 qw422016.N().S(` `) //line test.qtpl:130 } //line test.qtpl:130 func WritePrintPage(qq422016 qtio422016.Writer, p Page, title string) { //line test.qtpl:130 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:130 StreamPrintPage(qw422016, p, title) //line test.qtpl:130 qt422016.ReleaseWriter(qw422016) //line test.qtpl:130 } //line test.qtpl:130 func PrintPage(p Page, title string) string { //line test.qtpl:130 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:130 WritePrintPage(qb422016, p, title) //line test.qtpl:130 qs422016 := string(qb422016.B) //line test.qtpl:130 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:130 return qs422016 //line test.qtpl:130 } // Implement contacts page //line test.qtpl:133 type ContactsPage struct{} //line test.qtpl:134 func (b *ContactsPage) StreamHead(qw422016 *qt422016.Writer) { //line test.qtpl:134 qw422016.N().S(`Contacts!`) //line test.qtpl:134 } //line test.qtpl:134 func (b *ContactsPage) WriteHead(qq422016 qtio422016.Writer) { //line test.qtpl:134 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:134 b.StreamHead(qw422016) //line test.qtpl:134 qt422016.ReleaseWriter(qw422016) //line test.qtpl:134 } //line test.qtpl:134 func (b *ContactsPage) Head() string { //line test.qtpl:134 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:134 b.WriteHead(qb422016) //line test.qtpl:134 qs422016 := string(qb422016.B) //line test.qtpl:134 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:134 return qs422016 //line test.qtpl:134 } //line test.qtpl:135 func (b *ContactsPage) StreamBody(qw422016 *qt422016.Writer, title string) { //line test.qtpl:135 qw422016.N().S(`Put here contact info`) //line test.qtpl:135 } //line test.qtpl:135 func (b *ContactsPage) WriteBody(qq422016 qtio422016.Writer, title string) { //line test.qtpl:135 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:135 b.StreamBody(qw422016, title) //line test.qtpl:135 qt422016.ReleaseWriter(qw422016) //line test.qtpl:135 } //line test.qtpl:135 func (b *ContactsPage) Body(title string) string { //line test.qtpl:135 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:135 b.WriteBody(qb422016, title) //line test.qtpl:135 qs422016 := string(qb422016.B) //line test.qtpl:135 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:135 return qs422016 //line test.qtpl:135 } // Implement HomePage //line test.qtpl:138 type Homepage struct{} //line test.qtpl:139 func (h *Homepage) StreamHead(qw422016 *qt422016.Writer) { //line test.qtpl:139 qw422016.N().S(`Homepage`) //line test.qtpl:139 } //line test.qtpl:139 func (h *Homepage) WriteHead(qq422016 qtio422016.Writer) { //line test.qtpl:139 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:139 h.StreamHead(qw422016) //line test.qtpl:139 qt422016.ReleaseWriter(qw422016) //line test.qtpl:139 } //line test.qtpl:139 func (h *Homepage) Head() string { //line test.qtpl:139 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:139 h.WriteHead(qb422016) //line test.qtpl:139 qs422016 := string(qb422016.B) //line test.qtpl:139 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:139 return qs422016 //line test.qtpl:139 } //line test.qtpl:140 func (h *Homepage) StreamBody(qw422016 *qt422016.Writer, title string) { //line test.qtpl:140 qw422016.N().S(` Title: `) //line test.qtpl:141 qw422016.N().S(title) //line test.qtpl:141 qw422016.N().S(` Homepage body `) //line test.qtpl:143 } //line test.qtpl:143 func (h *Homepage) WriteBody(qq422016 qtio422016.Writer, title string) { //line test.qtpl:143 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:143 h.StreamBody(qw422016, title) //line test.qtpl:143 qt422016.ReleaseWriter(qw422016) //line test.qtpl:143 } //line test.qtpl:143 func (h *Homepage) Body(title string) string { //line test.qtpl:143 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:143 h.WriteBody(qb422016, title) //line test.qtpl:143 qs422016 := string(qb422016.B) //line test.qtpl:143 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:143 return qs422016 //line test.qtpl:143 } // unused code may be commented: // variadic function //line test.qtpl:153 func StreamVariadic(qw422016 *qt422016.Writer, a int, b ...string) { //line test.qtpl:153 qw422016.N().S(` a = `) //line test.qtpl:154 qw422016.N().D(a) //line test.qtpl:154 qw422016.N().S(` `) //line test.qtpl:155 for i, s := range b { //line test.qtpl:155 qw422016.N().S(` `) //line test.qtpl:156 qw422016.N().D(i) //line test.qtpl:156 qw422016.N().S(`: `) //line test.qtpl:156 qw422016.E().S(s) //line test.qtpl:156 qw422016.N().S(` `) //line test.qtpl:157 } //line test.qtpl:157 qw422016.N().S(` `) //line test.qtpl:158 } //line test.qtpl:158 func WriteVariadic(qq422016 qtio422016.Writer, a int, b ...string) { //line test.qtpl:158 qw422016 := qt422016.AcquireWriter(qq422016) //line test.qtpl:158 StreamVariadic(qw422016, a, b...) //line test.qtpl:158 qt422016.ReleaseWriter(qw422016) //line test.qtpl:158 } //line test.qtpl:158 func Variadic(a int, b ...string) string { //line test.qtpl:158 qb422016 := qt422016.AcquireByteBuffer() //line test.qtpl:158 WriteVariadic(qb422016, a, b...) //line test.qtpl:158 qs422016 := string(qb422016.B) //line test.qtpl:158 qt422016.ReleaseByteBuffer(qb422016) //line test.qtpl:158 return qs422016 //line test.qtpl:158 } golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/000077500000000000000000000000001376501567700247775ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/bench.qtpl000066400000000000000000000005361376501567700267640ustar00rootroot00000000000000{% code type BenchRow struct { ID int Message string Print bool } %} {% func BenchPage(rows []BenchRow) %} test {% endfunc %} golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/bench.qtpl.go000066400000000000000000000045601376501567700273710ustar00rootroot00000000000000// Code generated by qtc from "bench.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. //line testdata/templates/bench.qtpl:1 package templates //line testdata/templates/bench.qtpl:1 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line testdata/templates/bench.qtpl:1 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line testdata/templates/bench.qtpl:3 type BenchRow struct { ID int Message string Print bool } //line testdata/templates/bench.qtpl:11 func StreamBenchPage(qw422016 *qt422016.Writer, rows []BenchRow) { //line testdata/templates/bench.qtpl:11 qw422016.N().S(` test `) //line testdata/templates/bench.qtpl:23 } //line testdata/templates/bench.qtpl:23 func WriteBenchPage(qq422016 qtio422016.Writer, rows []BenchRow) { //line testdata/templates/bench.qtpl:23 qw422016 := qt422016.AcquireWriter(qq422016) //line testdata/templates/bench.qtpl:23 StreamBenchPage(qw422016, rows) //line testdata/templates/bench.qtpl:23 qt422016.ReleaseWriter(qw422016) //line testdata/templates/bench.qtpl:23 } //line testdata/templates/bench.qtpl:23 func BenchPage(rows []BenchRow) string { //line testdata/templates/bench.qtpl:23 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/bench.qtpl:23 WriteBenchPage(qb422016, rows) //line testdata/templates/bench.qtpl:23 qs422016 := string(qb422016.B) //line testdata/templates/bench.qtpl:23 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/bench.qtpl:23 return qs422016 //line testdata/templates/bench.qtpl:23 } golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/bench.tpl000066400000000000000000000002631376501567700266000ustar00rootroot00000000000000 test golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/integration.qtpl000066400000000000000000000057711376501567700302360ustar00rootroot00000000000000This is a template for integration test. It should contains all the quicktemplate stuff. {% import "fmt" %} {% func Integration() %} Output tags` verification. {% code p := &integrationPage{ S: "foobar", } %} Embedded func template: plain: {%= embeddedFunc(p) %} html-escaped: {%=h embeddedFunc(p) %} url-escaped: {%=u embeddedFunc(p) %} quoted json string: {%=q embeddedFunc(p) %} unquoted json string: {%=j embeddedFunc(p) %} html-escaped url-escaped: {%=uh embeddedFunc(p) %} html-escaped quoted json string: {%=qh embeddedFunc(p) %} html-escaped unquoted json string: {%=jh embeddedFunc(p) %} Html-escaped output tags: Output tags without html escaping {% stripspace %} Strip space {%space%} between lines and tags {%plain%} Tags aren't parsed {%inside %} plain {%endplain%} {% code // one-liner comment %} {% code // multi-line // comment %} {% code /* yet another multi-line comment */ %} {% endstripspace %} {% collapsespace %} Collapse space {%space %} between {%newline%} lines and tags {%comment%} Comments {%are%} ignored {%endcomment%} {% for _, s := range []string{"foo","bar","baz"} %} {% if s == "bar" %} Bar {% elseif s == "baz" %} Baz {% break %} {% else %} {% if s == "never" %} {% return %} {% endif %} {% switch s %} {% case "foobar" %} s = foobar {% case "barbaz" %} s = barbaz {% default %} s = {%s s %} {% endswitch %} {% continue %} {% endif %} {% endfor %} {% endcollapsespace %} {% cat "integration.qtpl" %} Strip leading and trailing space: {%- for i := 0; i < 4; i++ -%} x += {%d i -%} + {%-d i+1 %} {%- endfor -%} end tail of the func {% endfunc %} {% interface Page { Header() Body() } %} {% func embeddedFunc(p Page) %} Page's header: {%= p.Header() %} Body: {%s= fmt.Sprintf("%s", p.Body()) %} {% endfunc %} {% code type integrationPage struct { S string } %} {% func (p *integrationPage) Header() %}Header{% endfunc %} {% func (p *integrationPage) Body() %} S={%q p.S %} {% endfunc %} golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/integration.qtpl.go000066400000000000000000000556141376501567700306430ustar00rootroot00000000000000// Code generated by qtc from "integration.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. // This is a template for integration test. // It should contains all the quicktemplate stuff. // //line testdata/templates/integration.qtpl:4 package templates //line testdata/templates/integration.qtpl:4 import "fmt" //line testdata/templates/integration.qtpl:6 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line testdata/templates/integration.qtpl:6 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line testdata/templates/integration.qtpl:6 func StreamIntegration(qw422016 *qt422016.Writer) { //line testdata/templates/integration.qtpl:6 qw422016.N().S(` Output tags`) //line testdata/templates/integration.qtpl:6 qw422016.N().S("`") //line testdata/templates/integration.qtpl:6 qw422016.N().S(` verification. `) //line testdata/templates/integration.qtpl:10 p := &integrationPage{ S: "foobar", } //line testdata/templates/integration.qtpl:13 qw422016.N().S(` Embedded func template: plain: `) //line testdata/templates/integration.qtpl:15 streamembeddedFunc(qw422016, p) //line testdata/templates/integration.qtpl:15 qw422016.N().S(` html-escaped: `) //line testdata/templates/integration.qtpl:16 { //line testdata/templates/integration.qtpl:16 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:16 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:16 qw422016.E().Z(qb422016.B) //line testdata/templates/integration.qtpl:16 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:16 } //line testdata/templates/integration.qtpl:16 qw422016.N().S(` url-escaped: `) //line testdata/templates/integration.qtpl:17 { //line testdata/templates/integration.qtpl:17 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:17 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:17 qw422016.N().UZ(qb422016.B) //line testdata/templates/integration.qtpl:17 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:17 } //line testdata/templates/integration.qtpl:17 qw422016.N().S(` quoted json string: `) //line testdata/templates/integration.qtpl:18 { //line testdata/templates/integration.qtpl:18 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:18 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:18 qw422016.N().QZ(qb422016.B) //line testdata/templates/integration.qtpl:18 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:18 } //line testdata/templates/integration.qtpl:18 qw422016.N().S(` unquoted json string: `) //line testdata/templates/integration.qtpl:19 { //line testdata/templates/integration.qtpl:19 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:19 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:19 qw422016.N().JZ(qb422016.B) //line testdata/templates/integration.qtpl:19 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:19 } //line testdata/templates/integration.qtpl:19 qw422016.N().S(` html-escaped url-escaped: `) //line testdata/templates/integration.qtpl:20 { //line testdata/templates/integration.qtpl:20 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:20 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:20 qw422016.N().UZ(qb422016.B) //line testdata/templates/integration.qtpl:20 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:20 } //line testdata/templates/integration.qtpl:20 qw422016.N().S(` html-escaped quoted json string: `) //line testdata/templates/integration.qtpl:21 { //line testdata/templates/integration.qtpl:21 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:21 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:21 qw422016.E().QZ(qb422016.B) //line testdata/templates/integration.qtpl:21 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:21 } //line testdata/templates/integration.qtpl:21 qw422016.N().S(` html-escaped unquoted json string: `) //line testdata/templates/integration.qtpl:22 { //line testdata/templates/integration.qtpl:22 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:22 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:22 qw422016.E().JZ(qb422016.B) //line testdata/templates/integration.qtpl:22 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:22 } //line testdata/templates/integration.qtpl:22 qw422016.N().S(` Html-escaped output tags: Output tags without html escaping `) //line testdata/templates/integration.qtpl:52 qw422016.N().S(`Strip space`) //line testdata/templates/integration.qtpl:53 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:53 qw422016.N().S(`between lines and tags`) //line testdata/templates/integration.qtpl:55 qw422016.N().S(` Tags aren't parsed {%inside %} plain `) //line testdata/templates/integration.qtpl:59 // one-liner comment //line testdata/templates/integration.qtpl:61 // multi-line // comment //line testdata/templates/integration.qtpl:65 /* yet another multi-line comment */ //line testdata/templates/integration.qtpl:70 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:72 qw422016.N().S(` Collapse space `) //line testdata/templates/integration.qtpl:73 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:73 qw422016.N().S(` between `) //line testdata/templates/integration.qtpl:74 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:74 qw422016.N().S(` lines and tags `) //line testdata/templates/integration.qtpl:78 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:80 for _, s := range []string{"foo", "bar", "baz"} { //line testdata/templates/integration.qtpl:80 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:81 if s == "bar" { //line testdata/templates/integration.qtpl:81 qw422016.N().S(` Bar `) //line testdata/templates/integration.qtpl:83 } else if s == "baz" { //line testdata/templates/integration.qtpl:83 qw422016.N().S(` Baz `) //line testdata/templates/integration.qtpl:85 break //line testdata/templates/integration.qtpl:86 } else { //line testdata/templates/integration.qtpl:86 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:87 if s == "never" { //line testdata/templates/integration.qtpl:87 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:88 return //line testdata/templates/integration.qtpl:89 } //line testdata/templates/integration.qtpl:89 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:91 switch s { //line testdata/templates/integration.qtpl:92 case "foobar": //line testdata/templates/integration.qtpl:92 qw422016.N().S(` s = foobar `) //line testdata/templates/integration.qtpl:94 case "barbaz": //line testdata/templates/integration.qtpl:94 qw422016.N().S(` s = barbaz `) //line testdata/templates/integration.qtpl:96 default: //line testdata/templates/integration.qtpl:96 qw422016.N().S(` s = `) //line testdata/templates/integration.qtpl:97 qw422016.E().S(s) //line testdata/templates/integration.qtpl:97 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:98 } //line testdata/templates/integration.qtpl:98 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:100 continue //line testdata/templates/integration.qtpl:101 } //line testdata/templates/integration.qtpl:101 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:102 } //line testdata/templates/integration.qtpl:102 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:103 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:105 qw422016.N().S(`This is a template for integration test. It should contains all the quicktemplate stuff. {% import "fmt" %} {% func Integration() %} Output tags`) //line testdata/templates/integration.qtpl:105 qw422016.N().S("`") //line testdata/templates/integration.qtpl:105 qw422016.N().S(` verification. {% code p := &integrationPage{ S: "foobar", } %} Embedded func template: plain: {%= embeddedFunc(p) %} html-escaped: {%=h embeddedFunc(p) %} url-escaped: {%=u embeddedFunc(p) %} quoted json string: {%=q embeddedFunc(p) %} unquoted json string: {%=j embeddedFunc(p) %} html-escaped url-escaped: {%=uh embeddedFunc(p) %} html-escaped quoted json string: {%=qh embeddedFunc(p) %} html-escaped unquoted json string: {%=jh embeddedFunc(p) %} Html-escaped output tags: Output tags without html escaping {% stripspace %} Strip space {%space%} between lines and tags {%plain%} Tags aren't parsed {%inside %} plain {%endplain%} {% code // one-liner comment %} {% code // multi-line // comment %} {% code /* yet another multi-line comment */ %} {% endstripspace %} {% collapsespace %} Collapse space {%space %} between {%newline%} lines and tags {%comment%} Comments {%are%} ignored {%endcomment%} {% for _, s := range []string{"foo","bar","baz"} %} {% if s == "bar" %} Bar {% elseif s == "baz" %} Baz {% break %} {% else %} {% if s == "never" %} {% return %} {% endif %} {% switch s %} {% case "foobar" %} s = foobar {% case "barbaz" %} s = barbaz {% default %} s = {%s s %} {% endswitch %} {% continue %} {% endif %} {% endfor %} {% endcollapsespace %} {% cat "integration.qtpl" %} Strip leading and trailing space: {%- for i := 0; i < 4; i++ -%} x += {%d i -%} + {%-d i+1 %} {%- endfor -%} end tail of the func {% endfunc %} {% interface Page { Header() Body() } %} {% func embeddedFunc(p Page) %} Page's header: {%= p.Header() %} Body: {%s= fmt.Sprintf("%s", p.Body()) %} {% endfunc %} {% code type integrationPage struct { S string } %} {% func (p *integrationPage) Header() %}Header{% endfunc %} {% func (p *integrationPage) Body() %} S={%q p.S %} {% endfunc %} `) //line testdata/templates/integration.qtpl:105 qw422016.N().S(` Strip leading and trailing space: `) //line testdata/templates/integration.qtpl:108 for i := 0; i < 4; i++ { //line testdata/templates/integration.qtpl:108 qw422016.N().S(` x += `) //line testdata/templates/integration.qtpl:109 qw422016.N().D(i) //line testdata/templates/integration.qtpl:109 qw422016.N().S(`+`) //line testdata/templates/integration.qtpl:109 qw422016.N().D(i + 1) //line testdata/templates/integration.qtpl:109 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:110 } //line testdata/templates/integration.qtpl:110 qw422016.N().S(` end tail of the func `) //line testdata/templates/integration.qtpl:114 } //line testdata/templates/integration.qtpl:114 func WriteIntegration(qq422016 qtio422016.Writer) { //line testdata/templates/integration.qtpl:114 qw422016 := qt422016.AcquireWriter(qq422016) //line testdata/templates/integration.qtpl:114 StreamIntegration(qw422016) //line testdata/templates/integration.qtpl:114 qt422016.ReleaseWriter(qw422016) //line testdata/templates/integration.qtpl:114 } //line testdata/templates/integration.qtpl:114 func Integration() string { //line testdata/templates/integration.qtpl:114 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:114 WriteIntegration(qb422016) //line testdata/templates/integration.qtpl:114 qs422016 := string(qb422016.B) //line testdata/templates/integration.qtpl:114 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:114 return qs422016 //line testdata/templates/integration.qtpl:114 } //line testdata/templates/integration.qtpl:117 type Page interface { //line testdata/templates/integration.qtpl:117 Header() string //line testdata/templates/integration.qtpl:117 StreamHeader(qw422016 *qt422016.Writer) //line testdata/templates/integration.qtpl:117 WriteHeader(qq422016 qtio422016.Writer) //line testdata/templates/integration.qtpl:117 Body() string //line testdata/templates/integration.qtpl:117 StreamBody(qw422016 *qt422016.Writer) //line testdata/templates/integration.qtpl:117 WriteBody(qq422016 qtio422016.Writer) //line testdata/templates/integration.qtpl:117 } //line testdata/templates/integration.qtpl:123 func streamembeddedFunc(qw422016 *qt422016.Writer, p Page) { //line testdata/templates/integration.qtpl:123 qw422016.N().S(` Page's header: `) //line testdata/templates/integration.qtpl:124 p.StreamHeader(qw422016) //line testdata/templates/integration.qtpl:124 qw422016.N().S(` Body: `) //line testdata/templates/integration.qtpl:125 qw422016.N().S(fmt.Sprintf("%s", p.Body())) //line testdata/templates/integration.qtpl:125 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:126 } //line testdata/templates/integration.qtpl:126 func writeembeddedFunc(qq422016 qtio422016.Writer, p Page) { //line testdata/templates/integration.qtpl:126 qw422016 := qt422016.AcquireWriter(qq422016) //line testdata/templates/integration.qtpl:126 streamembeddedFunc(qw422016, p) //line testdata/templates/integration.qtpl:126 qt422016.ReleaseWriter(qw422016) //line testdata/templates/integration.qtpl:126 } //line testdata/templates/integration.qtpl:126 func embeddedFunc(p Page) string { //line testdata/templates/integration.qtpl:126 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:126 writeembeddedFunc(qb422016, p) //line testdata/templates/integration.qtpl:126 qs422016 := string(qb422016.B) //line testdata/templates/integration.qtpl:126 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:126 return qs422016 //line testdata/templates/integration.qtpl:126 } //line testdata/templates/integration.qtpl:129 type integrationPage struct { S string } //line testdata/templates/integration.qtpl:134 func (p *integrationPage) StreamHeader(qw422016 *qt422016.Writer) { //line testdata/templates/integration.qtpl:134 qw422016.N().S(`Header`) //line testdata/templates/integration.qtpl:134 } //line testdata/templates/integration.qtpl:134 func (p *integrationPage) WriteHeader(qq422016 qtio422016.Writer) { //line testdata/templates/integration.qtpl:134 qw422016 := qt422016.AcquireWriter(qq422016) //line testdata/templates/integration.qtpl:134 p.StreamHeader(qw422016) //line testdata/templates/integration.qtpl:134 qt422016.ReleaseWriter(qw422016) //line testdata/templates/integration.qtpl:134 } //line testdata/templates/integration.qtpl:134 func (p *integrationPage) Header() string { //line testdata/templates/integration.qtpl:134 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:134 p.WriteHeader(qb422016) //line testdata/templates/integration.qtpl:134 qs422016 := string(qb422016.B) //line testdata/templates/integration.qtpl:134 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:134 return qs422016 //line testdata/templates/integration.qtpl:134 } //line testdata/templates/integration.qtpl:136 func (p *integrationPage) StreamBody(qw422016 *qt422016.Writer) { //line testdata/templates/integration.qtpl:136 qw422016.N().S(` S=`) //line testdata/templates/integration.qtpl:137 qw422016.E().Q(p.S) //line testdata/templates/integration.qtpl:137 qw422016.N().S(` `) //line testdata/templates/integration.qtpl:138 } //line testdata/templates/integration.qtpl:138 func (p *integrationPage) WriteBody(qq422016 qtio422016.Writer) { //line testdata/templates/integration.qtpl:138 qw422016 := qt422016.AcquireWriter(qq422016) //line testdata/templates/integration.qtpl:138 p.StreamBody(qw422016) //line testdata/templates/integration.qtpl:138 qt422016.ReleaseWriter(qw422016) //line testdata/templates/integration.qtpl:138 } //line testdata/templates/integration.qtpl:138 func (p *integrationPage) Body() string { //line testdata/templates/integration.qtpl:138 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/integration.qtpl:138 p.WriteBody(qb422016) //line testdata/templates/integration.qtpl:138 qs422016 := string(qb422016.B) //line testdata/templates/integration.qtpl:138 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/integration.qtpl:138 return qs422016 //line testdata/templates/integration.qtpl:138 } golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/integration.qtpl.out000066400000000000000000000123451376501567700310370ustar00rootroot00000000000000 Output tags` verification. Embedded func template: plain: Page's header: Header Body: S="foobar" html-escaped: Page's header: Header Body: <b> S=&quot;foobar&quot; </b> url-escaped: %0A%09Page%27s+header%3A+Header%0A%09Body%3A+%3Cb%3E%0A%09S%3D%26quot%3Bfoobar%26quot%3B%0A%3C%2Fb%3E%0A quoted json string: "\n\tPage\u0027s header: Header\n\tBody: \u003cb>\n\tS="foobar"\n\u003c/b>\n" unquoted json string: \n\tPage\u0027s header: Header\n\tBody: \u003cb>\n\tS="foobar"\n\u003c/b>\n html-escaped url-escaped: %0A%09Page%27s+header%3A+Header%0A%09Body%3A+%3Cb%3E%0A%09S%3D%26quot%3Bfoobar%26quot%3B%0A%3C%2Fb%3E%0A html-escaped quoted json string: "\n\tPage\u0027s header: Header\n\tBody: \u003cb>\n\tS=&quot;foobar&quot;\n\u003c/b>\n" html-escaped unquoted json string: \n\tPage\u0027s header: Header\n\tBody: \u003cb>\n\tS=&quot;foobar&quot;\n\u003c/b>\n Html-escaped output tags: Output tags without html escaping Strip space between lines and tags Tags aren't parsed {%inside %} plain Collapse space between lines and tags s = foo Bar Baz This is a template for integration test. It should contains all the quicktemplate stuff. {% import "fmt" %} {% func Integration() %} Output tags` verification. {% code p := &integrationPage{ S: "foobar", } %} Embedded func template: plain: {%= embeddedFunc(p) %} html-escaped: {%=h embeddedFunc(p) %} url-escaped: {%=u embeddedFunc(p) %} quoted json string: {%=q embeddedFunc(p) %} unquoted json string: {%=j embeddedFunc(p) %} html-escaped url-escaped: {%=uh embeddedFunc(p) %} html-escaped quoted json string: {%=qh embeddedFunc(p) %} html-escaped unquoted json string: {%=jh embeddedFunc(p) %} Html-escaped output tags: Output tags without html escaping {% stripspace %} Strip space {%space%} between lines and tags {%plain%} Tags aren't parsed {%inside %} plain {%endplain%} {% code // one-liner comment %} {% code // multi-line // comment %} {% code /* yet another multi-line comment */ %} {% endstripspace %} {% collapsespace %} Collapse space {%space %} between {%newline%} lines and tags {%comment%} Comments {%are%} ignored {%endcomment%} {% for _, s := range []string{"foo","bar","baz"} %} {% if s == "bar" %} Bar {% elseif s == "baz" %} Baz {% break %} {% else %} {% if s == "never" %} {% return %} {% endif %} {% switch s %} {% case "foobar" %} s = foobar {% case "barbaz" %} s = barbaz {% default %} s = {%s s %} {% endswitch %} {% continue %} {% endif %} {% endfor %} {% endcollapsespace %} {% cat "integration.qtpl" %} Strip leading and trailing space: {%- for i := 0; i < 4; i++ -%} x += {%d i -%} + {%-d i+1 %} {%- endfor -%} end tail of the func {% endfunc %} {% interface Page { Header() Body() } %} {% func embeddedFunc(p Page) %} Page's header: {%= p.Header() %} Body: {%s= fmt.Sprintf("%s", p.Body()) %} {% endfunc %} {% code type integrationPage struct { S string } %} {% func (p *integrationPage) Header() %}Header{% endfunc %} {% func (p *integrationPage) Body() %} S={%q p.S %} {% endfunc %} Strip leading and trailing space: x += 0+1 x += 1+2 x += 2+3 x += 3+4 end tail of the func golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/marshal.qtpl000066400000000000000000000014101376501567700273240ustar00rootroot00000000000000Templates for marshal_timing_test.go {% code type MarshalRow struct { Msg string N int } type MarshalData struct { Foo int Bar string Rows []MarshalRow } %} // JSON marshaling {% stripspace %} {% func (d *MarshalData) JSON() %} { "Foo": {%d d.Foo %}, "Bar": {%q= d.Bar %}, "Rows":[ {% for i, r := range d.Rows %} { "Msg": {%q= r.Msg %}, "N": {%d r.N %} } {% if i + 1 < len(d.Rows) %},{% endif %} {% endfor %} ] } {% endfunc %} {% endstripspace %} // XML marshaling {% stripspace %} {% func (d *MarshalData) XML() %} {%d d.Foo %} {%s d.Bar %} {% for _, r := range d.Rows %} {%s r.Msg %} {%d r.N %} {% endfor %} {% endfunc %} {% endstripspace %} golang-github-valyala-quicktemplate-1.6.3+ds/testdata/templates/marshal.qtpl.go000066400000000000000000000107541376501567700277430ustar00rootroot00000000000000// Code generated by qtc from "marshal.qtpl". DO NOT EDIT. // See https://github.com/valyala/quicktemplate for details. // Templates for marshal_timing_test.go // //line testdata/templates/marshal.qtpl:3 package templates //line testdata/templates/marshal.qtpl:3 import ( qtio422016 "io" qt422016 "github.com/valyala/quicktemplate" ) //line testdata/templates/marshal.qtpl:3 var ( _ = qtio422016.Copy _ = qt422016.AcquireByteBuffer ) //line testdata/templates/marshal.qtpl:4 type MarshalRow struct { Msg string N int } type MarshalData struct { Foo int Bar string Rows []MarshalRow } // JSON marshaling //line testdata/templates/marshal.qtpl:18 func (d *MarshalData) StreamJSON(qw422016 *qt422016.Writer) { //line testdata/templates/marshal.qtpl:18 qw422016.N().S(`{"Foo":`) //line testdata/templates/marshal.qtpl:20 qw422016.N().D(d.Foo) //line testdata/templates/marshal.qtpl:20 qw422016.N().S(`,"Bar":`) //line testdata/templates/marshal.qtpl:21 qw422016.N().Q(d.Bar) //line testdata/templates/marshal.qtpl:21 qw422016.N().S(`,"Rows":[`) //line testdata/templates/marshal.qtpl:23 for i, r := range d.Rows { //line testdata/templates/marshal.qtpl:23 qw422016.N().S(`{"Msg":`) //line testdata/templates/marshal.qtpl:25 qw422016.N().Q(r.Msg) //line testdata/templates/marshal.qtpl:25 qw422016.N().S(`,"N":`) //line testdata/templates/marshal.qtpl:26 qw422016.N().D(r.N) //line testdata/templates/marshal.qtpl:26 qw422016.N().S(`}`) //line testdata/templates/marshal.qtpl:28 if i+1 < len(d.Rows) { //line testdata/templates/marshal.qtpl:28 qw422016.N().S(`,`) //line testdata/templates/marshal.qtpl:28 } //line testdata/templates/marshal.qtpl:29 } //line testdata/templates/marshal.qtpl:29 qw422016.N().S(`]}`) //line testdata/templates/marshal.qtpl:32 } //line testdata/templates/marshal.qtpl:32 func (d *MarshalData) WriteJSON(qq422016 qtio422016.Writer) { //line testdata/templates/marshal.qtpl:32 qw422016 := qt422016.AcquireWriter(qq422016) //line testdata/templates/marshal.qtpl:32 d.StreamJSON(qw422016) //line testdata/templates/marshal.qtpl:32 qt422016.ReleaseWriter(qw422016) //line testdata/templates/marshal.qtpl:32 } //line testdata/templates/marshal.qtpl:32 func (d *MarshalData) JSON() string { //line testdata/templates/marshal.qtpl:32 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/marshal.qtpl:32 d.WriteJSON(qb422016) //line testdata/templates/marshal.qtpl:32 qs422016 := string(qb422016.B) //line testdata/templates/marshal.qtpl:32 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/marshal.qtpl:32 return qs422016 //line testdata/templates/marshal.qtpl:32 } // XML marshaling //line testdata/templates/marshal.qtpl:37 func (d *MarshalData) StreamXML(qw422016 *qt422016.Writer) { //line testdata/templates/marshal.qtpl:37 qw422016.N().S(``) //line testdata/templates/marshal.qtpl:39 qw422016.N().D(d.Foo) //line testdata/templates/marshal.qtpl:39 qw422016.N().S(``) //line testdata/templates/marshal.qtpl:40 qw422016.E().S(d.Bar) //line testdata/templates/marshal.qtpl:40 qw422016.N().S(``) //line testdata/templates/marshal.qtpl:41 for _, r := range d.Rows { //line testdata/templates/marshal.qtpl:41 qw422016.N().S(``) //line testdata/templates/marshal.qtpl:43 qw422016.E().S(r.Msg) //line testdata/templates/marshal.qtpl:43 qw422016.N().S(``) //line testdata/templates/marshal.qtpl:44 qw422016.N().D(r.N) //line testdata/templates/marshal.qtpl:44 qw422016.N().S(``) //line testdata/templates/marshal.qtpl:46 } //line testdata/templates/marshal.qtpl:46 qw422016.N().S(``) //line testdata/templates/marshal.qtpl:48 } //line testdata/templates/marshal.qtpl:48 func (d *MarshalData) WriteXML(qq422016 qtio422016.Writer) { //line testdata/templates/marshal.qtpl:48 qw422016 := qt422016.AcquireWriter(qq422016) //line testdata/templates/marshal.qtpl:48 d.StreamXML(qw422016) //line testdata/templates/marshal.qtpl:48 qt422016.ReleaseWriter(qw422016) //line testdata/templates/marshal.qtpl:48 } //line testdata/templates/marshal.qtpl:48 func (d *MarshalData) XML() string { //line testdata/templates/marshal.qtpl:48 qb422016 := qt422016.AcquireByteBuffer() //line testdata/templates/marshal.qtpl:48 d.WriteXML(qb422016) //line testdata/templates/marshal.qtpl:48 qs422016 := string(qb422016.B) //line testdata/templates/marshal.qtpl:48 qt422016.ReleaseByteBuffer(qb422016) //line testdata/templates/marshal.qtpl:48 return qs422016 //line testdata/templates/marshal.qtpl:48 } golang-github-valyala-quicktemplate-1.6.3+ds/tests/000077500000000000000000000000001376501567700223325ustar00rootroot00000000000000golang-github-valyala-quicktemplate-1.6.3+ds/tests/doc.go000066400000000000000000000001001376501567700234150ustar00rootroot00000000000000// Package tests contains tests for quicktemplate package tests golang-github-valyala-quicktemplate-1.6.3+ds/tests/integration_test.go000066400000000000000000000006401376501567700262430ustar00rootroot00000000000000package tests import ( "io/ioutil" "testing" "github.com/valyala/quicktemplate/testdata/templates" ) func TestIntegration(t *testing.T) { expectedS, err := ioutil.ReadFile("../testdata/templates/integration.qtpl.out") if err != nil { t.Fatalf("unexpected error: %s", err) } s := templates.Integration() if s != string(expectedS) { t.Fatalf("unexpected output\n%q\nExpecting\n%q\n", s, expectedS) } } golang-github-valyala-quicktemplate-1.6.3+ds/tests/marshal_timing_test.go000066400000000000000000000107251376501567700267230ustar00rootroot00000000000000package tests import ( "bytes" "encoding/json" "encoding/xml" "fmt" "log" "testing" "github.com/valyala/quicktemplate" "github.com/valyala/quicktemplate/testdata/templates" ) func init() { // Make sure both encoding/json and templates generate identical output. d := newTemplatesData(3) bb := quicktemplate.AcquireByteBuffer() expectedData, err := json.Marshal(d) if err != nil { log.Fatalf("unexpected error: %s", err) } e := json.NewEncoder(bb) if err := e.Encode(d); err != nil { log.Fatalf("unexpected error: %s", err) } bb.B = bytes.TrimSpace(bb.B) if !bytes.Equal(bb.B, expectedData) { log.Fatalf("unexpected data generated with encoding/json:\n%q\n. Expecting\n%q\n", bb.B, expectedData) } bb.Reset() d.WriteJSON(bb) if !bytes.Equal(bb.B, expectedData) { log.Fatalf("unexpected data generated with quicktemplate:\n%q\n. Expecting\n%q\n", bb.B, expectedData) } // make sure both encoding/xml and templates generate identical output. expectedData, err = xml.Marshal(d) if err != nil { log.Fatalf("unexpected error: %s", err) } bb.Reset() xe := xml.NewEncoder(bb) if err := xe.Encode(d); err != nil { log.Fatalf("unexpected error: %s", err) } if !bytes.Equal(bb.B, expectedData) { log.Fatalf("unexpected data generated with encoding/xml:\n%q\n. Expecting\n%q\n", bb.B, expectedData) } bb.Reset() d.WriteXML(bb) if !bytes.Equal(bb.B, expectedData) { log.Fatalf("unexpected data generated with quicktemplate:\n%q\n. Expecting\n%q\n", bb.B, expectedData) } quicktemplate.ReleaseByteBuffer(bb) } func BenchmarkMarshalJSONStd1(b *testing.B) { benchmarkMarshalJSONStd(b, 1) } func BenchmarkMarshalJSONStd10(b *testing.B) { benchmarkMarshalJSONStd(b, 10) } func BenchmarkMarshalJSONStd100(b *testing.B) { benchmarkMarshalJSONStd(b, 100) } func BenchmarkMarshalJSONStd1000(b *testing.B) { benchmarkMarshalJSONStd(b, 1000) } func benchmarkMarshalJSONStd(b *testing.B, n int) { d := newTemplatesData(n) b.RunParallel(func(pb *testing.PB) { bb := quicktemplate.AcquireByteBuffer() e := json.NewEncoder(bb) for pb.Next() { if err := e.Encode(d); err != nil { b.Fatalf("unexpected error: %s", err) } bb.Reset() } quicktemplate.ReleaseByteBuffer(bb) }) } func BenchmarkMarshalJSONQuickTemplate1(b *testing.B) { benchmarkMarshalJSONQuickTemplate(b, 1) } func BenchmarkMarshalJSONQuickTemplate10(b *testing.B) { benchmarkMarshalJSONQuickTemplate(b, 10) } func BenchmarkMarshalJSONQuickTemplate100(b *testing.B) { benchmarkMarshalJSONQuickTemplate(b, 100) } func BenchmarkMarshalJSONQuickTemplate1000(b *testing.B) { benchmarkMarshalJSONQuickTemplate(b, 1000) } func benchmarkMarshalJSONQuickTemplate(b *testing.B, n int) { d := newTemplatesData(n) b.RunParallel(func(pb *testing.PB) { bb := quicktemplate.AcquireByteBuffer() for pb.Next() { d.WriteJSON(bb) bb.Reset() } quicktemplate.ReleaseByteBuffer(bb) }) } func BenchmarkMarshalXMLStd1(b *testing.B) { benchmarkMarshalXMLStd(b, 1) } func BenchmarkMarshalXMLStd10(b *testing.B) { benchmarkMarshalXMLStd(b, 10) } func BenchmarkMarshalXMLStd100(b *testing.B) { benchmarkMarshalXMLStd(b, 100) } func BenchmarkMarshalXMLStd1000(b *testing.B) { benchmarkMarshalXMLStd(b, 1000) } func benchmarkMarshalXMLStd(b *testing.B, n int) { d := newTemplatesData(n) b.RunParallel(func(pb *testing.PB) { bb := quicktemplate.AcquireByteBuffer() e := xml.NewEncoder(bb) for pb.Next() { if err := e.Encode(d); err != nil { b.Fatalf("unexpected error: %s", err) } bb.Reset() } quicktemplate.ReleaseByteBuffer(bb) }) } func BenchmarkMarshalXMLQuickTemplate1(b *testing.B) { benchmarkMarshalXMLQuickTemplate(b, 1) } func BenchmarkMarshalXMLQuickTemplate10(b *testing.B) { benchmarkMarshalXMLQuickTemplate(b, 10) } func BenchmarkMarshalXMLQuickTemplate100(b *testing.B) { benchmarkMarshalXMLQuickTemplate(b, 100) } func BenchmarkMarshalXMLQuickTemplate1000(b *testing.B) { benchmarkMarshalXMLQuickTemplate(b, 1000) } func benchmarkMarshalXMLQuickTemplate(b *testing.B, n int) { d := newTemplatesData(n) b.RunParallel(func(pb *testing.PB) { bb := quicktemplate.AcquireByteBuffer() for pb.Next() { d.WriteXML(bb) bb.Reset() } quicktemplate.ReleaseByteBuffer(bb) }) } func newTemplatesData(n int) *templates.MarshalData { var rows []templates.MarshalRow for i := 0; i < n; i++ { rows = append(rows, templates.MarshalRow{ Msg: fmt.Sprintf("тест %d", i), N: i, }) } return &templates.MarshalData{ Foo: 1, Bar: "foobar", Rows: rows, } } golang-github-valyala-quicktemplate-1.6.3+ds/tests/templates_timing_test.go000066400000000000000000000052451376501567700272730ustar00rootroot00000000000000package tests import ( "bytes" "fmt" "html/template" "log" "testing" texttemplate "text/template" "github.com/valyala/quicktemplate" "github.com/valyala/quicktemplate/testdata/templates" ) var tpl = template.Must(template.ParseFiles("../testdata/templates/bench.tpl")) var textTpl = texttemplate.Must(texttemplate.ParseFiles("../testdata/templates/bench.tpl")) func init() { // make sure that both template engines generate the same result rows := getBenchRows(3) bb1 := &quicktemplate.ByteBuffer{} if err := tpl.Execute(bb1, rows); err != nil { log.Fatalf("unexpected error: %s", err) } bb2 := &quicktemplate.ByteBuffer{} templates.WriteBenchPage(bb2, rows) if !bytes.Equal(bb1.B, bb2.B) { log.Fatalf("results mismatch:\n%q\n%q", bb1, bb2) } } func BenchmarkQuickTemplate1(b *testing.B) { benchmarkQuickTemplate(b, 1) } func BenchmarkQuickTemplate10(b *testing.B) { benchmarkQuickTemplate(b, 10) } func BenchmarkQuickTemplate100(b *testing.B) { benchmarkQuickTemplate(b, 100) } func benchmarkQuickTemplate(b *testing.B, rowsCount int) { rows := getBenchRows(rowsCount) b.RunParallel(func(pb *testing.PB) { bb := quicktemplate.AcquireByteBuffer() for pb.Next() { templates.WriteBenchPage(bb, rows) bb.Reset() } quicktemplate.ReleaseByteBuffer(bb) }) } func BenchmarkHTMLTemplate1(b *testing.B) { benchmarkHTMLTemplate(b, 1) } func BenchmarkHTMLTemplate10(b *testing.B) { benchmarkHTMLTemplate(b, 10) } func BenchmarkHTMLTemplate100(b *testing.B) { benchmarkHTMLTemplate(b, 100) } func benchmarkHTMLTemplate(b *testing.B, rowsCount int) { rows := getBenchRows(rowsCount) b.RunParallel(func(pb *testing.PB) { bb := quicktemplate.AcquireByteBuffer() for pb.Next() { if err := tpl.Execute(bb, rows); err != nil { b.Fatalf("unexpected error: %s", err) } bb.Reset() } quicktemplate.ReleaseByteBuffer(bb) }) } func BenchmarkTextTemplate1(b *testing.B) { benchmarkTextTemplate(b, 1) } func BenchmarkTextTemplate10(b *testing.B) { benchmarkTextTemplate(b, 10) } func BenchmarkTextTemplate100(b *testing.B) { benchmarkTextTemplate(b, 100) } func benchmarkTextTemplate(b *testing.B, rowsCount int) { rows := getBenchRows(rowsCount) b.RunParallel(func(pb *testing.PB) { bb := quicktemplate.AcquireByteBuffer() for pb.Next() { if err := textTpl.Execute(bb, rows); err != nil { b.Fatalf("unexpected error: %s", err) } bb.Reset() } quicktemplate.ReleaseByteBuffer(bb) }) } func getBenchRows(n int) []templates.BenchRow { rows := make([]templates.BenchRow, n) for i := 0; i < n; i++ { rows[i] = templates.BenchRow{ ID: i, Message: fmt.Sprintf("message %d", i), Print: ((i & 1) == 0), } } return rows } golang-github-valyala-quicktemplate-1.6.3+ds/urlencode.go000066400000000000000000000012631376501567700235010ustar00rootroot00000000000000package quicktemplate func appendURLEncode(dst []byte, src string) []byte { n := len(src) if n > 0 { // Hint the compiler to remove bounds checks in the loop below. _ = src[n-1] } for i := 0; i < n; i++ { c := src[i] // See http://www.w3.org/TR/html5/forms.html#form-submission-algorithm if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '.' || c == '_' { dst = append(dst, c) } else { if c == ' ' { dst = append(dst, '+') } else { dst = append(dst, '%', hexCharUpper(c>>4), hexCharUpper(c&15)) } } } return dst } func hexCharUpper(c byte) byte { if c < 10 { return '0' + c } return c - 10 + 'A' } golang-github-valyala-quicktemplate-1.6.3+ds/urlencode_test.go000066400000000000000000000010151376501567700245330ustar00rootroot00000000000000package quicktemplate import ( "net/url" "testing" ) func TestAppendURLEncode(t *testing.T) { testAppendURLEncode(t, "") testAppendURLEncode(t, "f") testAppendURLEncode(t, " ") testAppendURLEncode(t, ".-_") testAppendURLEncode(t, "тест+this/&=;?\n\t\rabc") } func testAppendURLEncode(t *testing.T, s string) { expectedResult := url.QueryEscape(s) result := appendURLEncode(nil, s) if string(result) != expectedResult { t.Fatalf("unexpected result %q. Expecting %q. str=%q", result, expectedResult, s) } } golang-github-valyala-quicktemplate-1.6.3+ds/util.go000066400000000000000000000001011376501567700224640ustar00rootroot00000000000000package quicktemplate //go:generate qtc -dir=testdata/templates golang-github-valyala-quicktemplate-1.6.3+ds/util_appengine.go000066400000000000000000000002631376501567700245230ustar00rootroot00000000000000// +build appengine appenginevm package quicktemplate func unsafeStrToBytes(s string) []byte { return []byte(s) } func unsafeBytesToStr(z []byte) string { return string(z) } golang-github-valyala-quicktemplate-1.6.3+ds/util_noappengine.go000066400000000000000000000006011376501567700250540ustar00rootroot00000000000000// +build !appengine,!appenginevm package quicktemplate import ( "reflect" "unsafe" ) func unsafeStrToBytes(s string) (b []byte) { sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) bh.Data = sh.Data bh.Len = sh.Len bh.Cap = sh.Len return b } func unsafeBytesToStr(z []byte) string { return *(*string)(unsafe.Pointer(&z)) } golang-github-valyala-quicktemplate-1.6.3+ds/writer.go000066400000000000000000000077311376501567700230430ustar00rootroot00000000000000package quicktemplate import ( "fmt" "io" "strconv" "sync" ) // Writer implements auxiliary writer used by quicktemplate functions. // // Use AcquireWriter for creating new writers. type Writer struct { e QWriter n QWriter } // W returns the underlying writer passed to AcquireWriter. func (qw *Writer) W() io.Writer { return qw.n.w } // E returns QWriter with enabled html escaping. func (qw *Writer) E() *QWriter { return &qw.e } // N returns QWriter without html escaping. func (qw *Writer) N() *QWriter { return &qw.n } // AcquireWriter returns new writer from the pool. // // Return unneeded writer to the pool by calling ReleaseWriter // in order to reduce memory allocations. func AcquireWriter(w io.Writer) *Writer { v := writerPool.Get() if v == nil { qw := &Writer{} qw.e.w = &htmlEscapeWriter{} v = qw } qw := v.(*Writer) qw.e.w.(*htmlEscapeWriter).w = w qw.n.w = w return qw } // ReleaseWriter returns the writer to the pool. // // Do not access released writer, otherwise data races may occur. func ReleaseWriter(qw *Writer) { hw := qw.e.w.(*htmlEscapeWriter) hw.w = nil qw.e.Reset() qw.e.w = hw qw.n.Reset() writerPool.Put(qw) } var writerPool sync.Pool // QWriter is auxiliary writer used by Writer. type QWriter struct { w io.Writer err error b []byte } // Write implements io.Writer. func (w *QWriter) Write(p []byte) (int, error) { if w.err != nil { return 0, w.err } n, err := w.w.Write(p) if err != nil { w.err = err } return n, err } // Reset resets QWriter to the original state. func (w *QWriter) Reset() { w.w = nil w.err = nil } // S writes s to w. func (w *QWriter) S(s string) { w.Write(unsafeStrToBytes(s)) } // Z writes z to w. func (w *QWriter) Z(z []byte) { w.Write(z) } // SZ is a synonym to Z. func (w *QWriter) SZ(z []byte) { w.Write(z) } // D writes n to w. func (w *QWriter) D(n int) { bb, ok := w.w.(*ByteBuffer) if ok { bb.B = strconv.AppendInt(bb.B, int64(n), 10) } else { w.b = strconv.AppendInt(w.b[:0], int64(n), 10) w.Write(w.b) } } // DL writes n to w func (w *QWriter) DL(n int64) { bb, ok := w.w.(*ByteBuffer) if ok { bb.B = strconv.AppendInt(bb.B, n, 10) } else { w.b = strconv.AppendInt(w.b[:0], n, 10) w.Write(w.b) } } // DUL writes n to w func (w *QWriter) DUL(n uint64) { bb, ok := w.w.(*ByteBuffer) if ok { bb.B = strconv.AppendUint(bb.B, n, 10) } else { w.b = strconv.AppendUint(w.b[:0], n, 10) w.Write(w.b) } } // F writes f to w. func (w *QWriter) F(f float64) { n := int(f) if float64(n) == f { // Fast path - just int. w.D(n) return } // Slow path. w.FPrec(f, -1) } // FPrec writes f to w using the given floating point precision. func (w *QWriter) FPrec(f float64, prec int) { bb, ok := w.w.(*ByteBuffer) if ok { bb.B = strconv.AppendFloat(bb.B, f, 'f', prec, 64) } else { w.b = strconv.AppendFloat(w.b[:0], f, 'f', prec, 64) w.Write(w.b) } } // Q writes quoted json-safe s to w. func (w *QWriter) Q(s string) { bb, ok := w.w.(*ByteBuffer) if ok { bb.B = appendJSONString(bb.B, s, true) } else { w.b = appendJSONString(w.b[:0], s, true) w.Write(w.b) } } var strQuote = []byte(`"`) // QZ writes quoted json-safe z to w. func (w *QWriter) QZ(z []byte) { w.Q(unsafeBytesToStr(z)) } // J writes json-safe s to w. // // Unlike Q it doesn't qoute resulting s. func (w *QWriter) J(s string) { bb, ok := w.w.(*ByteBuffer) if ok { bb.B = appendJSONString(bb.B, s, false) } else { w.b = appendJSONString(w.b[:0], s, false) w.Write(w.b) } } // JZ writes json-safe z to w. // // Unlike Q it doesn't qoute resulting z. func (w *QWriter) JZ(z []byte) { w.J(unsafeBytesToStr(z)) } // V writes v to w. func (w *QWriter) V(v interface{}) { fmt.Fprintf(w, "%v", v) } // U writes url-encoded s to w. func (w *QWriter) U(s string) { bb, ok := w.w.(*ByteBuffer) if ok { bb.B = appendURLEncode(bb.B, s) } else { w.b = appendURLEncode(w.b[:0], s) w.Write(w.b) } } // UZ writes url-encoded z to w. func (w *QWriter) UZ(z []byte) { w.U(unsafeBytesToStr(z)) } golang-github-valyala-quicktemplate-1.6.3+ds/writer_test.go000066400000000000000000000125041376501567700240740ustar00rootroot00000000000000package quicktemplate import ( "testing" ) func TestWriter(t *testing.T) { bb := AcquireByteBuffer() qw := AcquireWriter(bb) w := qw.W() bbNew, ok := w.(*ByteBuffer) if !ok { t.Fatalf("W() must return ByteBuffer, not %T", w) } if bbNew != bb { t.Fatalf("unexpected ByteBuffer returned: %p. Expecting %p", bbNew, bb) } wn := qw.N() we := qw.E() wn.S("") wn.D(123) we.DUL(18446744073709551615) wn.Z([]byte("'")) wn.Q("foo") wn.J("ds") wn.F(1.23) wn.U("абв") wn.V(struct{}{}) wn.SZ([]byte("aaa")) wn.QZ([]byte("asadf")) wn.JZ([]byte("asd")) wn.UZ([]byte("abc")) we.S("") we.D(321) we.DUL(18446744073709551615) we.Z([]byte("'")) we.Q("foo") we.J("ds") we.F(1.23) we.U("абв") we.V(struct{}{}) we.SZ([]byte("aaa")) we.QZ([]byte("asadf")) we.JZ([]byte("asd")) we.UZ([]byte("abc")) ReleaseWriter(qw) expectedS := "12318446744073709551615'\"foo\"ds1.23%D0%B0%D0%B1%D0%B2{}aaa\"asadf\"asdabc" + "<a></a>32118446744073709551615'"foo"ds1.23%D0%B0%D0%B1%D0%B2{}aaa"asadf"asdabc" if string(bb.B) != expectedS { t.Fatalf("unexpected output:\n%q\nExpecting\n%q", bb.B, expectedS) } ReleaseByteBuffer(bb) } func TestQWriterS(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "\x00foo<>&'\" bar\n\t=;\\/+%йцу\x00foo<>&'" bar\n\t</script>=;\\/+%йцу" wn.S(s) we.S(s) return expectedS }) } func TestQWriterZ(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "\x00foo<>&'\" bar\n\t=;\\/+%йцу\x00foo<>&'" bar\n\t</script>=;\\/+%йцу" wn.Z([]byte(s)) we.Z([]byte(s)) return expectedS }) } func TestQWriterSZ(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "\x00foo<>&'\" bar\n\t=;\\/+%йцу\x00foo<>&'" bar\n\t</script>=;\\/+%йцу" wn.SZ([]byte(s)) we.SZ([]byte(s)) return expectedS }) } func TestQWriterQ(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "\"\\u0000foo\\u003c>&\\u0027\\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу\""\\u0000foo\\u003c>&\\u0027\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу"" wn.Q(s) we.Q(s) return expectedS }) } func TestQWriterQZ(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "\"\\u0000foo\\u003c>&\\u0027\\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу\""\\u0000foo\\u003c>&\\u0027\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу"" wn.QZ([]byte(s)) we.QZ([]byte(s)) return expectedS }) } func TestQWriterJ(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "\\u0000foo\\u003c>&\\u0027\\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу\\u0000foo\\u003c>&\\u0027\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу" wn.J(s) we.J(s) return expectedS }) } func TestQWriterJZ(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "\\u0000foo\\u003c>&\\u0027\\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу\\u0000foo\\u003c>&\\u0027\\" bar\\n\\t\\u003c/script>=;\\\\/+%йцу" wn.JZ([]byte(s)) we.JZ([]byte(s)) return expectedS }) } func TestQWriterU(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "%00foo%3C%3E%26%27%22+bar%0A%09%3C%2Fscript%3E%3D%3B%5C%2F%2B%25%D0%B9%D1%86%D1%83%00foo%3C%3E%26%27%22+bar%0A%09%3C%2Fscript%3E%3D%3B%5C%2F%2B%25%D0%B9%D1%86%D1%83" wn.U(s) we.U(s) return expectedS }) } func TestQWriterUZ(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "%00foo%3C%3E%26%27%22+bar%0A%09%3C%2Fscript%3E%3D%3B%5C%2F%2B%25%D0%B9%D1%86%D1%83%00foo%3C%3E%26%27%22+bar%0A%09%3C%2Fscript%3E%3D%3B%5C%2F%2B%25%D0%B9%D1%86%D1%83" wn.UZ([]byte(s)) we.UZ([]byte(s)) return expectedS }) } func TestQWriterV(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { s := "\u0000" + `foo<>&'" bar =;\/+%йцу` expectedS := "{\x00foo<>&'\" bar\n\t=;\\/+%йцу}{\x00foo<>&'" bar\n\t</script>=;\\/+%йцу}" ss := struct{ S string }{s} wn.V(ss) we.V(ss) return expectedS }) } func TestQWriterF(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { f := 1.9234 wn.F(f) we.F(f) return "1.92341.9234" }) } func TestQWriterFPrec(t *testing.T) { testQWriter(t, func(wn, we *QWriter) string { f := 1.9254 wn.FPrec(f, 2) we.FPrec(f, 3) wn.FPrec(f, 0) we.FPrec(f, 1) return "1.931.92521.9" }) } func testQWriter(t *testing.T, f func(wn, we *QWriter) (expectedS string)) { bb := AcquireByteBuffer() qw := AcquireWriter(bb) wn := qw.N() we := qw.E() expectedS := f(wn, we) ReleaseWriter(qw) if string(bb.B) != expectedS { t.Fatalf("unexpected output:\n%q\nExpecting\n%q", bb.B, expectedS) } ReleaseByteBuffer(bb) } golang-github-valyala-quicktemplate-1.6.3+ds/writer_timing_test.go000066400000000000000000000110051376501567700254360ustar00rootroot00000000000000package quicktemplate import ( "testing" ) func BenchmarkQWriterVString(b *testing.B) { v := createTestS(100) b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.V(v) bb.Reset() } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterVInt(b *testing.B) { v := 1233455 b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.V(v) bb.Reset() } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterQ1(b *testing.B) { benchmarkQWriterQ(b, 1) } func BenchmarkQWriterQ10(b *testing.B) { benchmarkQWriterQ(b, 10) } func BenchmarkQWriterQ100(b *testing.B) { benchmarkQWriterQ(b, 100) } func BenchmarkQWriterQ1K(b *testing.B) { benchmarkQWriterQ(b, 1000) } func BenchmarkQWriterQ10K(b *testing.B) { benchmarkQWriterQ(b, 10000) } func benchmarkQWriterQ(b *testing.B, size int) { s := createTestS(size) b.SetBytes(int64(size)) b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.Q(s) bb.Reset() } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterJ1(b *testing.B) { benchmarkQWriterJ(b, 1) } func BenchmarkQWriterJ10(b *testing.B) { benchmarkQWriterJ(b, 10) } func BenchmarkQWriterJ100(b *testing.B) { benchmarkQWriterJ(b, 100) } func BenchmarkQWriterJ1K(b *testing.B) { benchmarkQWriterJ(b, 1000) } func BenchmarkQWriterJ10K(b *testing.B) { benchmarkQWriterJ(b, 10000) } func benchmarkQWriterJ(b *testing.B, size int) { s := createTestS(size) b.SetBytes(int64(size)) b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.J(s) bb.Reset() } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterU1(b *testing.B) { benchmarkQWriterU(b, 1) } func BenchmarkQWriterU10(b *testing.B) { benchmarkQWriterU(b, 10) } func BenchmarkQWriterU100(b *testing.B) { benchmarkQWriterU(b, 100) } func BenchmarkQWriterU1K(b *testing.B) { benchmarkQWriterU(b, 1000) } func BenchmarkQWriterU10K(b *testing.B) { benchmarkQWriterU(b, 10000) } func benchmarkQWriterU(b *testing.B, size int) { s := createTestS(size) b.SetBytes(int64(size)) b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.U(s) bb.Reset() } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterFfloat(b *testing.B) { b.RunParallel(func(pb *testing.PB) { f := 123.456 var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.F(f) bb.Reset() f++ } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterFint(b *testing.B) { b.RunParallel(func(pb *testing.PB) { f := 123.0 var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.F(f) bb.Reset() f++ } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterD(b *testing.B) { n := 123456 b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.D(n) bb.Reset() } ReleaseByteBuffer(bb) }) } func BenchmarkQWriterZ1(b *testing.B) { benchmarkQWriterZ(b, 1) } func BenchmarkQWriterZ10(b *testing.B) { benchmarkQWriterZ(b, 10) } func BenchmarkQWriterZ100(b *testing.B) { benchmarkQWriterZ(b, 100) } func BenchmarkQWriterZ1K(b *testing.B) { benchmarkQWriterZ(b, 1000) } func BenchmarkQWriterZ10K(b *testing.B) { benchmarkQWriterZ(b, 10000) } func BenchmarkQWriterS1(b *testing.B) { benchmarkQWriterS(b, 1) } func BenchmarkQWriterS10(b *testing.B) { benchmarkQWriterS(b, 10) } func BenchmarkQWriterS100(b *testing.B) { benchmarkQWriterS(b, 100) } func BenchmarkQWriterS1K(b *testing.B) { benchmarkQWriterS(b, 1000) } func BenchmarkQWriterS10K(b *testing.B) { benchmarkQWriterS(b, 10000) } func benchmarkQWriterZ(b *testing.B, size int) { z := createTestZ(size) b.SetBytes(int64(size)) b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.Z(z) bb.Reset() } ReleaseByteBuffer(bb) }) } func benchmarkQWriterS(b *testing.B, size int) { s := createTestS(size) b.SetBytes(int64(size)) b.RunParallel(func(pb *testing.PB) { var w QWriter bb := AcquireByteBuffer() w.w = bb for pb.Next() { w.S(s) bb.Reset() } ReleaseByteBuffer(bb) }) } func createTestS(size int) string { return string(createTestZ(size)) } var sample = []byte(`0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM`) func createTestZ(size int) []byte { var b []byte for i := 0; i < size; i++ { b = append(b, sample[i%len(sample)]) } return b }