mirror of
https://github.com/thegeeklab/wp-matrix.git
synced 2024-09-19 15:12:47 +02:00
Merge pull request #8 from drone-plugins/restructure
Updated to current build process
This commit is contained in:
commit
d6bf193ae0
|
@ -1,11 +1,12 @@
|
||||||
version: '{build}'
|
version: '{build}'
|
||||||
image: 'Visual Studio 2017'
|
image: 'Visual Studio 2017'
|
||||||
platform: x64
|
platform: 'x64'
|
||||||
|
|
||||||
clone_folder: 'c:\go\src\github.com\drone-plugins\drone-matrix'
|
clone_folder: 'c:\gopath\src\github.com\drone-plugins\drone-matrix'
|
||||||
max_jobs: 1
|
max_jobs: 1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
GOPATH: c:\gopath
|
||||||
DOCKER_USERNAME:
|
DOCKER_USERNAME:
|
||||||
secure: '4YzzahbEiMZQJpOCOd1LAw=='
|
secure: '4YzzahbEiMZQJpOCOd1LAw=='
|
||||||
DOCKER_PASSWORD:
|
DOCKER_PASSWORD:
|
||||||
|
@ -15,14 +16,19 @@ install:
|
||||||
- ps: |
|
- ps: |
|
||||||
docker version
|
docker version
|
||||||
go version
|
go version
|
||||||
|
- ps: |
|
||||||
|
$env:Path = "c:\gopath\bin;$env:Path"
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- ps: |
|
- ps: |
|
||||||
|
go get -u github.com/golang/dep/cmd/dep
|
||||||
|
dep ensure
|
||||||
|
|
||||||
if ( $env:APPVEYOR_REPO_TAG -eq 'false' ) {
|
if ( $env:APPVEYOR_REPO_TAG -eq 'false' ) {
|
||||||
go build -ldflags "-X main.build=$env:APPVEYOR_BUILD_VERSION" -a -o drone-matrix.exe
|
go build -ldflags "-X main.build=$env:APPVEYOR_BUILD_VERSION" -a -o release/drone-matrix.exe
|
||||||
} else {
|
} else {
|
||||||
$version = $env:APPVEYOR_REPO_TAG_NAME.substring(1)
|
$version = $env:APPVEYOR_REPO_TAG_NAME.substring(1)
|
||||||
go build -ldflags "-X main.version=$version -X main.build=$env:APPVEYOR_BUILD_VERSION" -a -o drone-matrix.exe
|
go build -ldflags "-X main.version=$version -X main.build=$env:APPVEYOR_BUILD_VERSION" -a -o release/drone-matrix.exe
|
||||||
}
|
}
|
||||||
|
|
||||||
docker pull microsoft/nanoserver:10.0.14393.1593
|
docker pull microsoft/nanoserver:10.0.14393.1593
|
||||||
|
|
34
.drone.yml
34
.drone.yml
|
@ -3,18 +3,22 @@ workspace:
|
||||||
path: src/github.com/drone-plugins/drone-matrix
|
path: src/github.com/drone-plugins/drone-matrix
|
||||||
|
|
||||||
pipeline:
|
pipeline:
|
||||||
test:
|
deps:
|
||||||
image: golang:1.9
|
image: golang:1.10
|
||||||
pull: true
|
pull: true
|
||||||
commands:
|
commands:
|
||||||
- go vet
|
- go get -u github.com/golang/dep/cmd/dep
|
||||||
- |
|
- dep ensure
|
||||||
for PKG in $(go list ./... | grep -v /vendor/); do
|
|
||||||
go test -cover -coverprofile $GOPATH/src/$PKG/coverage.out $PKG
|
test:
|
||||||
done
|
image: golang:1.10
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- go vet ./...
|
||||||
|
- go test -cover ./...
|
||||||
|
|
||||||
build_linux_amd64:
|
build_linux_amd64:
|
||||||
image: golang:1.9
|
image: golang:1.10
|
||||||
pull: true
|
pull: true
|
||||||
group: build
|
group: build
|
||||||
environment:
|
environment:
|
||||||
|
@ -30,7 +34,7 @@ pipeline:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
build_linux_i386:
|
build_linux_i386:
|
||||||
image: golang:1.9
|
image: golang:1.10
|
||||||
pull: true
|
pull: true
|
||||||
group: build
|
group: build
|
||||||
environment:
|
environment:
|
||||||
|
@ -46,7 +50,7 @@ pipeline:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
build_linux_arm64:
|
build_linux_arm64:
|
||||||
image: golang:1.9
|
image: golang:1.10
|
||||||
pull: true
|
pull: true
|
||||||
group: build
|
group: build
|
||||||
environment:
|
environment:
|
||||||
|
@ -62,7 +66,7 @@ pipeline:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
build_linux_arm:
|
build_linux_arm:
|
||||||
image: golang:1.9
|
image: golang:1.10
|
||||||
pull: true
|
pull: true
|
||||||
group: build
|
group: build
|
||||||
environment:
|
environment:
|
||||||
|
@ -79,7 +83,7 @@ pipeline:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
publish_linux_amd64:
|
publish_linux_amd64:
|
||||||
image: plugins/docker:17.05
|
image: plugins/docker:17.12
|
||||||
pull: true
|
pull: true
|
||||||
secrets: [ docker_username, docker_password ]
|
secrets: [ docker_username, docker_password ]
|
||||||
group: docker
|
group: docker
|
||||||
|
@ -91,7 +95,7 @@ pipeline:
|
||||||
event: [ push, tag ]
|
event: [ push, tag ]
|
||||||
|
|
||||||
publish_linux_i386:
|
publish_linux_i386:
|
||||||
image: plugins/docker:17.05
|
image: plugins/docker:17.12
|
||||||
pull: true
|
pull: true
|
||||||
secrets: [ docker_username, docker_password ]
|
secrets: [ docker_username, docker_password ]
|
||||||
group: docker
|
group: docker
|
||||||
|
@ -103,7 +107,7 @@ pipeline:
|
||||||
event: [ push, tag ]
|
event: [ push, tag ]
|
||||||
|
|
||||||
publish_linux_arm64:
|
publish_linux_arm64:
|
||||||
image: plugins/docker:17.05
|
image: plugins/docker:17.12
|
||||||
pull: true
|
pull: true
|
||||||
secrets: [ docker_username, docker_password ]
|
secrets: [ docker_username, docker_password ]
|
||||||
group: docker
|
group: docker
|
||||||
|
@ -115,7 +119,7 @@ pipeline:
|
||||||
event: [ push, tag ]
|
event: [ push, tag ]
|
||||||
|
|
||||||
publish_linux_arm:
|
publish_linux_arm:
|
||||||
image: plugins/docker:17.05
|
image: plugins/docker:17.12
|
||||||
pull: true
|
pull: true
|
||||||
secrets: [ docker_username, docker_password ]
|
secrets: [ docker_username, docker_password ]
|
||||||
group: docker
|
group: docker
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -24,5 +24,7 @@ _testmain.go
|
||||||
*.prof
|
*.prof
|
||||||
|
|
||||||
release/
|
release/
|
||||||
|
vendor/
|
||||||
|
|
||||||
coverage.out
|
coverage.out
|
||||||
drone-matrix
|
drone-matrix
|
||||||
|
|
|
@ -6,5 +6,7 @@ LABEL maintainer="Drone.IO Community <drone-dev@googlegroups.com>" `
|
||||||
org.label-schema.vendor="Drone.IO Community" `
|
org.label-schema.vendor="Drone.IO Community" `
|
||||||
org.label-schema.schema-version="1.0"
|
org.label-schema.schema-version="1.0"
|
||||||
|
|
||||||
ADD drone-matrix.exe c:\drone-matrix.exe
|
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||||
|
|
||||||
|
ADD release\drone-matrix.exe c:\drone-matrix.exe
|
||||||
ENTRYPOINT [ "c:\\drone-matrix.exe" ]
|
ENTRYPOINT [ "c:\\drone-matrix.exe" ]
|
||||||
|
|
23
Gopkg.toml
23
Gopkg.toml
|
@ -1,26 +1,3 @@
|
||||||
|
|
||||||
# Gopkg.toml example
|
|
||||||
#
|
|
||||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
|
||||||
# for detailed Gopkg.toml documentation.
|
|
||||||
#
|
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
|
||||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project"
|
|
||||||
# version = "1.0.0"
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project2"
|
|
||||||
# branch = "dev"
|
|
||||||
# source = "github.com/myfork/project2"
|
|
||||||
#
|
|
||||||
# [[override]]
|
|
||||||
# name = "github.com/x/y"
|
|
||||||
# version = "2.4.0"
|
|
||||||
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/aymerick/raymond"
|
name = "github.com/aymerick/raymond"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
|
|
3
vendor/github.com/aymerick/raymond/.gitmodules
generated
vendored
3
vendor/github.com/aymerick/raymond/.gitmodules
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "mustache"]
|
|
||||||
path = mustache
|
|
||||||
url = git://github.com/mustache/spec.git
|
|
6
vendor/github.com/aymerick/raymond/.travis.yml
generated
vendored
6
vendor/github.com/aymerick/raymond/.travis.yml
generated
vendored
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.3
|
|
||||||
- tip
|
|
46
vendor/github.com/aymerick/raymond/BENCHMARKS.md
generated
vendored
46
vendor/github.com/aymerick/raymond/BENCHMARKS.md
generated
vendored
|
@ -1,46 +0,0 @@
|
||||||
# Benchmarks
|
|
||||||
|
|
||||||
Hardware: MacBookPro11,1 - Intel Core i5 - 2,6 GHz - 8 Go RAM
|
|
||||||
|
|
||||||
With:
|
|
||||||
|
|
||||||
- handlebars.js #8cba84df119c317fcebc49fb285518542ca9c2d0
|
|
||||||
- raymond #7bbaaf50ed03c96b56687d7fa6c6e04e02375a98
|
|
||||||
|
|
||||||
|
|
||||||
## handlebars.js (ops/ms)
|
|
||||||
|
|
||||||
arguments 198 ±4 (5)
|
|
||||||
array-each 568 ±23 (5)
|
|
||||||
array-mustache 522 ±18 (4)
|
|
||||||
complex 71 ±7 (3)
|
|
||||||
data 67 ±2 (3)
|
|
||||||
depth-1 47 ±2 (3)
|
|
||||||
depth-2 14 ±1 (2)
|
|
||||||
object-mustache 1099 ±47 (5)
|
|
||||||
object 907 ±58 (4)
|
|
||||||
partial-recursion 46 ±3 (4)
|
|
||||||
partial 68 ±3 (3)
|
|
||||||
paths 1650 ±50 (3)
|
|
||||||
string 2552 ±157 (3)
|
|
||||||
subexpression 141 ±2 (4)
|
|
||||||
variables 2671 ±83 (4)
|
|
||||||
|
|
||||||
|
|
||||||
## raymond
|
|
||||||
|
|
||||||
BenchmarkArguments 200000 6642 ns/op 151 ops/ms
|
|
||||||
BenchmarkArrayEach 100000 19584 ns/op 51 ops/ms
|
|
||||||
BenchmarkArrayMustache 100000 17305 ns/op 58 ops/ms
|
|
||||||
BenchmarkComplex 30000 50270 ns/op 20 ops/ms
|
|
||||||
BenchmarkData 50000 25551 ns/op 39 ops/ms
|
|
||||||
BenchmarkDepth1 100000 20162 ns/op 50 ops/ms
|
|
||||||
BenchmarkDepth2 30000 47782 ns/op 21 ops/ms
|
|
||||||
BenchmarkObjectMustache 200000 7668 ns/op 130 ops/ms
|
|
||||||
BenchmarkObject 200000 8843 ns/op 113 ops/ms
|
|
||||||
BenchmarkPartialRecursion 50000 23139 ns/op 43 ops/ms
|
|
||||||
BenchmarkPartial 50000 31015 ns/op 32 ops/ms
|
|
||||||
BenchmarkPath 200000 8997 ns/op 111 ops/ms
|
|
||||||
BenchmarkString 1000000 1879 ns/op 532 ops/ms
|
|
||||||
BenchmarkSubExpression 300000 4935 ns/op 203 ops/ms
|
|
||||||
BenchmarkVariables 200000 6478 ns/op 154 ops/ms
|
|
33
vendor/github.com/aymerick/raymond/CHANGELOG.md
generated
vendored
33
vendor/github.com/aymerick/raymond/CHANGELOG.md
generated
vendored
|
@ -1,33 +0,0 @@
|
||||||
# Raymond Changelog
|
|
||||||
|
|
||||||
### Raymond 2.0.1 _(June 01, 2016)_
|
|
||||||
|
|
||||||
- [BUGFIX] Removes data races [#3](https://github.com/aymerick/raymond/issues/3) - Thanks [@markbates](https://github.com/markbates)
|
|
||||||
|
|
||||||
### Raymond 2.0.0 _(May 01, 2016)_
|
|
||||||
|
|
||||||
- [BUGFIX] Fixes passing of context in helper options [#2](https://github.com/aymerick/raymond/issues/2) - Thanks [@GhostRussia](https://github.com/GhostRussia)
|
|
||||||
- [BREAKING] Renames and unexports constants:
|
|
||||||
|
|
||||||
- `handlebars.DUMP_TPL`
|
|
||||||
- `lexer.ESCAPED_ESCAPED_OPEN_MUSTACHE`
|
|
||||||
- `lexer.ESCAPED_OPEN_MUSTACHE`
|
|
||||||
- `lexer.OPEN_MUSTACHE`
|
|
||||||
- `lexer.CLOSE_MUSTACHE`
|
|
||||||
- `lexer.CLOSE_STRIP_MUSTACHE`
|
|
||||||
- `lexer.CLOSE_UNESCAPED_STRIP_MUSTACHE`
|
|
||||||
- `lexer.DUMP_TOKEN_POS`
|
|
||||||
- `lexer.DUMP_ALL_TOKENS_VAL`
|
|
||||||
|
|
||||||
|
|
||||||
### Raymond 1.1.0 _(June 15, 2015)_
|
|
||||||
|
|
||||||
- Permits templates references with lowercase versions of struct fields.
|
|
||||||
- Adds `ParseFile()` function.
|
|
||||||
- Adds `RegisterPartialFile()`, `RegisterPartialFiles()` and `Clone()` methods on `Template`.
|
|
||||||
- Helpers can now be struct methods.
|
|
||||||
- Ensures safe concurrent access to helpers and partials.
|
|
||||||
|
|
||||||
### Raymond 1.0.0 _(June 09, 2015)_
|
|
||||||
|
|
||||||
- This is the first release. Raymond supports almost all handlebars features. See https://github.com/aymerick/raymond#limitations for a list of differences with the javascript implementation.
|
|
22
vendor/github.com/aymerick/raymond/LICENSE
generated
vendored
22
vendor/github.com/aymerick/raymond/LICENSE
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Aymerick JEHANNE
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
1382
vendor/github.com/aymerick/raymond/README.md
generated
vendored
1382
vendor/github.com/aymerick/raymond/README.md
generated
vendored
File diff suppressed because it is too large
Load Diff
1
vendor/github.com/aymerick/raymond/VERSION
generated
vendored
1
vendor/github.com/aymerick/raymond/VERSION
generated
vendored
|
@ -1 +0,0 @@
|
||||||
2.0.1
|
|
785
vendor/github.com/aymerick/raymond/ast/node.go
generated
vendored
785
vendor/github.com/aymerick/raymond/ast/node.go
generated
vendored
|
@ -1,785 +0,0 @@
|
||||||
// Package ast provides structures to represent a handlebars Abstract Syntax Tree, and a Visitor interface to visit that tree.
|
|
||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// References:
|
|
||||||
// - https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/compiler/ast.js
|
|
||||||
// - https://github.com/wycats/handlebars.js/blob/master/docs/compiler-api.md
|
|
||||||
// - https://github.com/golang/go/blob/master/src/text/template/parse/node.go
|
|
||||||
|
|
||||||
// Node is an element in the AST.
|
|
||||||
type Node interface {
|
|
||||||
// node type
|
|
||||||
Type() NodeType
|
|
||||||
|
|
||||||
// location of node in original input string
|
|
||||||
Location() Loc
|
|
||||||
|
|
||||||
// string representation, used for debugging
|
|
||||||
String() string
|
|
||||||
|
|
||||||
// accepts visitor
|
|
||||||
Accept(Visitor) interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visitor is the interface to visit an AST.
|
|
||||||
type Visitor interface {
|
|
||||||
VisitProgram(*Program) interface{}
|
|
||||||
|
|
||||||
// statements
|
|
||||||
VisitMustache(*MustacheStatement) interface{}
|
|
||||||
VisitBlock(*BlockStatement) interface{}
|
|
||||||
VisitPartial(*PartialStatement) interface{}
|
|
||||||
VisitContent(*ContentStatement) interface{}
|
|
||||||
VisitComment(*CommentStatement) interface{}
|
|
||||||
|
|
||||||
// expressions
|
|
||||||
VisitExpression(*Expression) interface{}
|
|
||||||
VisitSubExpression(*SubExpression) interface{}
|
|
||||||
VisitPath(*PathExpression) interface{}
|
|
||||||
|
|
||||||
// literals
|
|
||||||
VisitString(*StringLiteral) interface{}
|
|
||||||
VisitBoolean(*BooleanLiteral) interface{}
|
|
||||||
VisitNumber(*NumberLiteral) interface{}
|
|
||||||
|
|
||||||
// miscellaneous
|
|
||||||
VisitHash(*Hash) interface{}
|
|
||||||
VisitHashPair(*HashPair) interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeType represents an AST Node type.
|
|
||||||
type NodeType int
|
|
||||||
|
|
||||||
// Type returns itself, and permits struct includers to satisfy that part of Node interface.
|
|
||||||
func (t NodeType) Type() NodeType {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// NodeProgram is the program node
|
|
||||||
NodeProgram NodeType = iota
|
|
||||||
|
|
||||||
// NodeMustache is the mustache statement node
|
|
||||||
NodeMustache
|
|
||||||
|
|
||||||
// NodeBlock is the block statement node
|
|
||||||
NodeBlock
|
|
||||||
|
|
||||||
// NodePartial is the partial statement node
|
|
||||||
NodePartial
|
|
||||||
|
|
||||||
// NodeContent is the content statement node
|
|
||||||
NodeContent
|
|
||||||
|
|
||||||
// NodeComment is the comment statement node
|
|
||||||
NodeComment
|
|
||||||
|
|
||||||
// NodeExpression is the expression node
|
|
||||||
NodeExpression
|
|
||||||
|
|
||||||
// NodeSubExpression is the subexpression node
|
|
||||||
NodeSubExpression
|
|
||||||
|
|
||||||
// NodePath is the expression path node
|
|
||||||
NodePath
|
|
||||||
|
|
||||||
// NodeBoolean is the literal boolean node
|
|
||||||
NodeBoolean
|
|
||||||
|
|
||||||
// NodeNumber is the literal number node
|
|
||||||
NodeNumber
|
|
||||||
|
|
||||||
// NodeString is the literal string node
|
|
||||||
NodeString
|
|
||||||
|
|
||||||
// NodeHash is the hash node
|
|
||||||
NodeHash
|
|
||||||
|
|
||||||
// NodeHashPair is the hash pair node
|
|
||||||
NodeHashPair
|
|
||||||
)
|
|
||||||
|
|
||||||
// Loc represents the position of a parsed node in source file.
|
|
||||||
type Loc struct {
|
|
||||||
Pos int // Byte position
|
|
||||||
Line int // Line number
|
|
||||||
}
|
|
||||||
|
|
||||||
// Location returns itself, and permits struct includers to satisfy that part of Node interface.
|
|
||||||
func (l Loc) Location() Loc {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip describes node whitespace management.
|
|
||||||
type Strip struct {
|
|
||||||
Open bool
|
|
||||||
Close bool
|
|
||||||
|
|
||||||
OpenStandalone bool
|
|
||||||
CloseStandalone bool
|
|
||||||
InlineStandalone bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStrip instanciates a Strip for given open and close mustaches.
|
|
||||||
func NewStrip(openStr, closeStr string) *Strip {
|
|
||||||
return &Strip{
|
|
||||||
Open: (len(openStr) > 2) && openStr[2] == '~',
|
|
||||||
Close: (len(closeStr) > 2) && closeStr[len(closeStr)-3] == '~',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStripForStr instanciates a Strip for given tag.
|
|
||||||
func NewStripForStr(str string) *Strip {
|
|
||||||
return &Strip{
|
|
||||||
Open: (len(str) > 2) && str[2] == '~',
|
|
||||||
Close: (len(str) > 2) && str[len(str)-3] == '~',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (s *Strip) String() string {
|
|
||||||
return fmt.Sprintf("Open: %t, Close: %t, OpenStandalone: %t, CloseStandalone: %t, InlineStandalone: %t", s.Open, s.Close, s.OpenStandalone, s.CloseStandalone, s.InlineStandalone)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Program
|
|
||||||
//
|
|
||||||
|
|
||||||
// Program represents a program node.
|
|
||||||
type Program struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Body []Node // [ Statement ... ]
|
|
||||||
BlockParams []string
|
|
||||||
Chained bool
|
|
||||||
|
|
||||||
// whitespace management
|
|
||||||
Strip *Strip
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewProgram instanciates a new program node.
|
|
||||||
func NewProgram(pos int, line int) *Program {
|
|
||||||
return &Program{
|
|
||||||
NodeType: NodeProgram,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *Program) String() string {
|
|
||||||
return fmt.Sprintf("Program{Pos: %d}", node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *Program) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitProgram(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddStatement adds given statement to program.
|
|
||||||
func (node *Program) AddStatement(statement Node) {
|
|
||||||
node.Body = append(node.Body, statement)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Mustache Statement
|
|
||||||
//
|
|
||||||
|
|
||||||
// MustacheStatement represents a mustache node.
|
|
||||||
type MustacheStatement struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Unescaped bool
|
|
||||||
Expression *Expression
|
|
||||||
|
|
||||||
// whitespace management
|
|
||||||
Strip *Strip
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMustacheStatement instanciates a new mustache node.
|
|
||||||
func NewMustacheStatement(pos int, line int, unescaped bool) *MustacheStatement {
|
|
||||||
return &MustacheStatement{
|
|
||||||
NodeType: NodeMustache,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
Unescaped: unescaped,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *MustacheStatement) String() string {
|
|
||||||
return fmt.Sprintf("Mustache{Pos: %d}", node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *MustacheStatement) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitMustache(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Block Statement
|
|
||||||
//
|
|
||||||
|
|
||||||
// BlockStatement represents a block node.
|
|
||||||
type BlockStatement struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Expression *Expression
|
|
||||||
|
|
||||||
Program *Program
|
|
||||||
Inverse *Program
|
|
||||||
|
|
||||||
// whitespace management
|
|
||||||
OpenStrip *Strip
|
|
||||||
InverseStrip *Strip
|
|
||||||
CloseStrip *Strip
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBlockStatement instanciates a new block node.
|
|
||||||
func NewBlockStatement(pos int, line int) *BlockStatement {
|
|
||||||
return &BlockStatement{
|
|
||||||
NodeType: NodeBlock,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *BlockStatement) String() string {
|
|
||||||
return fmt.Sprintf("Block{Pos: %d}", node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *BlockStatement) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitBlock(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Partial Statement
|
|
||||||
//
|
|
||||||
|
|
||||||
// PartialStatement represents a partial node.
|
|
||||||
type PartialStatement struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Name Node // PathExpression | SubExpression
|
|
||||||
Params []Node // [ Expression ... ]
|
|
||||||
Hash *Hash
|
|
||||||
|
|
||||||
// whitespace management
|
|
||||||
Strip *Strip
|
|
||||||
Indent string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPartialStatement instanciates a new partial node.
|
|
||||||
func NewPartialStatement(pos int, line int) *PartialStatement {
|
|
||||||
return &PartialStatement{
|
|
||||||
NodeType: NodePartial,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *PartialStatement) String() string {
|
|
||||||
return fmt.Sprintf("Partial{Name:%s, Pos:%d}", node.Name, node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *PartialStatement) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitPartial(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Content Statement
|
|
||||||
//
|
|
||||||
|
|
||||||
// ContentStatement represents a content node.
|
|
||||||
type ContentStatement struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Value string
|
|
||||||
Original string
|
|
||||||
|
|
||||||
// whitespace management
|
|
||||||
RightStripped bool
|
|
||||||
LeftStripped bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContentStatement instanciates a new content node.
|
|
||||||
func NewContentStatement(pos int, line int, val string) *ContentStatement {
|
|
||||||
return &ContentStatement{
|
|
||||||
NodeType: NodeContent,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
|
|
||||||
Value: val,
|
|
||||||
Original: val,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *ContentStatement) String() string {
|
|
||||||
return fmt.Sprintf("Content{Value:'%s', Pos:%d}", node.Value, node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *ContentStatement) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitContent(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Comment Statement
|
|
||||||
//
|
|
||||||
|
|
||||||
// CommentStatement represents a comment node.
|
|
||||||
type CommentStatement struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Value string
|
|
||||||
|
|
||||||
// whitespace management
|
|
||||||
Strip *Strip
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCommentStatement instanciates a new comment node.
|
|
||||||
func NewCommentStatement(pos int, line int, val string) *CommentStatement {
|
|
||||||
return &CommentStatement{
|
|
||||||
NodeType: NodeComment,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
|
|
||||||
Value: val,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *CommentStatement) String() string {
|
|
||||||
return fmt.Sprintf("Comment{Value:'%s', Pos:%d}", node.Value, node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *CommentStatement) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitComment(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Expression
|
|
||||||
//
|
|
||||||
|
|
||||||
// Expression represents an expression node.
|
|
||||||
type Expression struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Path Node // PathExpression | StringLiteral | BooleanLiteral | NumberLiteral
|
|
||||||
Params []Node // [ Expression ... ]
|
|
||||||
Hash *Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExpression instanciates a new expression node.
|
|
||||||
func NewExpression(pos int, line int) *Expression {
|
|
||||||
return &Expression{
|
|
||||||
NodeType: NodeExpression,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *Expression) String() string {
|
|
||||||
return fmt.Sprintf("Expr{Path:%s, Pos:%d}", node.Path, node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *Expression) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitExpression(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelperName returns helper name, or an empty string if this expression can't be a helper.
|
|
||||||
func (node *Expression) HelperName() string {
|
|
||||||
path, ok := node.Path.(*PathExpression)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.Data || (len(path.Parts) != 1) || (path.Depth > 0) || path.Scoped {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.Parts[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// FieldPath returns path expression representing a field path, or nil if this is not a field path.
|
|
||||||
func (node *Expression) FieldPath() *PathExpression {
|
|
||||||
path, ok := node.Path.(*PathExpression)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
// LiteralStr returns the string representation of literal value, with a boolean set to false if this is not a literal.
|
|
||||||
func (node *Expression) LiteralStr() (string, bool) {
|
|
||||||
return LiteralStr(node.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonical returns the canonical form of expression node as a string.
|
|
||||||
func (node *Expression) Canonical() string {
|
|
||||||
if str, ok := HelperNameStr(node.Path); ok {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelperNameStr returns the string representation of a helper name, with a boolean set to false if this is not a valid helper name.
|
|
||||||
//
|
|
||||||
// helperName : path | dataName | STRING | NUMBER | BOOLEAN | UNDEFINED | NULL
|
|
||||||
func HelperNameStr(node Node) (string, bool) {
|
|
||||||
// PathExpression
|
|
||||||
if str, ok := PathExpressionStr(node); ok {
|
|
||||||
return str, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Literal
|
|
||||||
if str, ok := LiteralStr(node); ok {
|
|
||||||
return str, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathExpressionStr returns the string representation of path expression value, with a boolean set to false if this is not a path expression.
|
|
||||||
func PathExpressionStr(node Node) (string, bool) {
|
|
||||||
if path, ok := node.(*PathExpression); ok {
|
|
||||||
result := path.Original
|
|
||||||
|
|
||||||
// "[foo bar]"" => "foo bar"
|
|
||||||
if (len(result) >= 2) && (result[0] == '[') && (result[len(result)-1] == ']') {
|
|
||||||
result = result[1 : len(result)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// LiteralStr returns the string representation of literal value, with a boolean set to false if this is not a literal.
|
|
||||||
func LiteralStr(node Node) (string, bool) {
|
|
||||||
if lit, ok := node.(*StringLiteral); ok {
|
|
||||||
return lit.Value, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if lit, ok := node.(*BooleanLiteral); ok {
|
|
||||||
return lit.Canonical(), true
|
|
||||||
}
|
|
||||||
|
|
||||||
if lit, ok := node.(*NumberLiteral); ok {
|
|
||||||
return lit.Canonical(), true
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// SubExpression
|
|
||||||
//
|
|
||||||
|
|
||||||
// SubExpression represents a subexpression node.
|
|
||||||
type SubExpression struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Expression *Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSubExpression instanciates a new subexpression node.
|
|
||||||
func NewSubExpression(pos int, line int) *SubExpression {
|
|
||||||
return &SubExpression{
|
|
||||||
NodeType: NodeSubExpression,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *SubExpression) String() string {
|
|
||||||
return fmt.Sprintf("Sexp{Path:%s, Pos:%d}", node.Expression.Path, node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *SubExpression) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitSubExpression(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Path Expression
|
|
||||||
//
|
|
||||||
|
|
||||||
// PathExpression represents a path expression node.
|
|
||||||
type PathExpression struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Original string
|
|
||||||
Depth int
|
|
||||||
Parts []string
|
|
||||||
Data bool
|
|
||||||
Scoped bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPathExpression instanciates a new path expression node.
|
|
||||||
func NewPathExpression(pos int, line int, data bool) *PathExpression {
|
|
||||||
result := &PathExpression{
|
|
||||||
NodeType: NodePath,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
|
|
||||||
if data {
|
|
||||||
result.Original = "@"
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *PathExpression) String() string {
|
|
||||||
return fmt.Sprintf("Path{Original:'%s', Pos:%d}", node.Original, node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *PathExpression) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitPath(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Part adds path part.
|
|
||||||
func (node *PathExpression) Part(part string) {
|
|
||||||
node.Original += part
|
|
||||||
|
|
||||||
switch part {
|
|
||||||
case "..":
|
|
||||||
node.Depth++
|
|
||||||
node.Scoped = true
|
|
||||||
case ".", "this":
|
|
||||||
node.Scoped = true
|
|
||||||
default:
|
|
||||||
node.Parts = append(node.Parts, part)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sep adds path separator.
|
|
||||||
func (node *PathExpression) Sep(separator string) {
|
|
||||||
node.Original += separator
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDataRoot returns true if path expression is @root.
|
|
||||||
func (node *PathExpression) IsDataRoot() bool {
|
|
||||||
return node.Data && (node.Parts[0] == "root")
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// String Literal
|
|
||||||
//
|
|
||||||
|
|
||||||
// StringLiteral represents a string node.
|
|
||||||
type StringLiteral struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringLiteral instanciates a new string node.
|
|
||||||
func NewStringLiteral(pos int, line int, val string) *StringLiteral {
|
|
||||||
return &StringLiteral{
|
|
||||||
NodeType: NodeString,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
|
|
||||||
Value: val,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *StringLiteral) String() string {
|
|
||||||
return fmt.Sprintf("String{Value:'%s', Pos:%d}", node.Value, node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *StringLiteral) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitString(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Boolean Literal
|
|
||||||
//
|
|
||||||
|
|
||||||
// BooleanLiteral represents a boolean node.
|
|
||||||
type BooleanLiteral struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Value bool
|
|
||||||
Original string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBooleanLiteral instanciates a new boolean node.
|
|
||||||
func NewBooleanLiteral(pos int, line int, val bool, original string) *BooleanLiteral {
|
|
||||||
return &BooleanLiteral{
|
|
||||||
NodeType: NodeBoolean,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
|
|
||||||
Value: val,
|
|
||||||
Original: original,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *BooleanLiteral) String() string {
|
|
||||||
return fmt.Sprintf("Boolean{Value:%s, Pos:%d}", node.Canonical(), node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *BooleanLiteral) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitBoolean(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonical returns the canonical form of boolean node as a string (ie. "true" | "false").
|
|
||||||
func (node *BooleanLiteral) Canonical() string {
|
|
||||||
if node.Value {
|
|
||||||
return "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Number Literal
|
|
||||||
//
|
|
||||||
|
|
||||||
// NumberLiteral represents a number node.
|
|
||||||
type NumberLiteral struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Value float64
|
|
||||||
IsInt bool
|
|
||||||
Original string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNumberLiteral instanciates a new number node.
|
|
||||||
func NewNumberLiteral(pos int, line int, val float64, isInt bool, original string) *NumberLiteral {
|
|
||||||
return &NumberLiteral{
|
|
||||||
NodeType: NodeNumber,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
|
|
||||||
Value: val,
|
|
||||||
IsInt: isInt,
|
|
||||||
Original: original,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *NumberLiteral) String() string {
|
|
||||||
return fmt.Sprintf("Number{Value:%s, Pos:%d}", node.Canonical(), node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *NumberLiteral) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitNumber(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonical returns the canonical form of number node as a string (eg: "12", "-1.51").
|
|
||||||
func (node *NumberLiteral) Canonical() string {
|
|
||||||
prec := -1
|
|
||||||
if node.IsInt {
|
|
||||||
prec = 0
|
|
||||||
}
|
|
||||||
return strconv.FormatFloat(node.Value, 'f', prec, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number returns an integer or a float.
|
|
||||||
func (node *NumberLiteral) Number() interface{} {
|
|
||||||
if node.IsInt {
|
|
||||||
return int(node.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return node.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Hash
|
|
||||||
//
|
|
||||||
|
|
||||||
// Hash represents a hash node.
|
|
||||||
type Hash struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Pairs []*HashPair
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHash instanciates a new hash node.
|
|
||||||
func NewHash(pos int, line int) *Hash {
|
|
||||||
return &Hash{
|
|
||||||
NodeType: NodeHash,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *Hash) String() string {
|
|
||||||
result := fmt.Sprintf("Hash{[%d", node.Loc.Pos)
|
|
||||||
|
|
||||||
for i, p := range node.Pairs {
|
|
||||||
if i > 0 {
|
|
||||||
result += ", "
|
|
||||||
}
|
|
||||||
result += p.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result + fmt.Sprintf("], Pos:%d}", node.Loc.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *Hash) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitHash(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// HashPair
|
|
||||||
//
|
|
||||||
|
|
||||||
// HashPair represents a hash pair node.
|
|
||||||
type HashPair struct {
|
|
||||||
NodeType
|
|
||||||
Loc
|
|
||||||
|
|
||||||
Key string
|
|
||||||
Val Node // Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHashPair instanciates a new hash pair node.
|
|
||||||
func NewHashPair(pos int, line int) *HashPair {
|
|
||||||
return &HashPair{
|
|
||||||
NodeType: NodeHashPair,
|
|
||||||
Loc: Loc{pos, line},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of receiver that can be used for debugging.
|
|
||||||
func (node *HashPair) String() string {
|
|
||||||
return node.Key + "=" + node.Val.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept is the receiver entry point for visitors.
|
|
||||||
func (node *HashPair) Accept(visitor Visitor) interface{} {
|
|
||||||
return visitor.VisitHashPair(node)
|
|
||||||
}
|
|
279
vendor/github.com/aymerick/raymond/ast/print.go
generated
vendored
279
vendor/github.com/aymerick/raymond/ast/print.go
generated
vendored
|
@ -1,279 +0,0 @@
|
||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// printVisitor implements the Visitor interface to print a AST.
|
|
||||||
type printVisitor struct {
|
|
||||||
buf string
|
|
||||||
depth int
|
|
||||||
|
|
||||||
original bool
|
|
||||||
inBlock bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPrintVisitor() *printVisitor {
|
|
||||||
return &printVisitor{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print returns a string representation of given AST, that can be used for debugging purpose.
|
|
||||||
func Print(node Node) string {
|
|
||||||
visitor := newPrintVisitor()
|
|
||||||
node.Accept(visitor)
|
|
||||||
return visitor.output()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *printVisitor) output() string {
|
|
||||||
return v.buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *printVisitor) indent() {
|
|
||||||
for i := 0; i < v.depth; {
|
|
||||||
v.buf += " "
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *printVisitor) str(val string) {
|
|
||||||
v.buf += val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *printVisitor) nl() {
|
|
||||||
v.str("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *printVisitor) line(val string) {
|
|
||||||
v.indent()
|
|
||||||
v.str(val)
|
|
||||||
v.nl()
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Visitor interface
|
|
||||||
//
|
|
||||||
|
|
||||||
// Statements
|
|
||||||
|
|
||||||
// VisitProgram implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitProgram(node *Program) interface{} {
|
|
||||||
if len(node.BlockParams) > 0 {
|
|
||||||
v.line("BLOCK PARAMS: [ " + strings.Join(node.BlockParams, " ") + " ]")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, n := range node.Body {
|
|
||||||
n.Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitMustache implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitMustache(node *MustacheStatement) interface{} {
|
|
||||||
v.indent()
|
|
||||||
v.str("{{ ")
|
|
||||||
|
|
||||||
node.Expression.Accept(v)
|
|
||||||
|
|
||||||
v.str(" }}")
|
|
||||||
v.nl()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitBlock implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitBlock(node *BlockStatement) interface{} {
|
|
||||||
v.inBlock = true
|
|
||||||
|
|
||||||
v.line("BLOCK:")
|
|
||||||
v.depth++
|
|
||||||
|
|
||||||
node.Expression.Accept(v)
|
|
||||||
|
|
||||||
if node.Program != nil {
|
|
||||||
v.line("PROGRAM:")
|
|
||||||
v.depth++
|
|
||||||
node.Program.Accept(v)
|
|
||||||
v.depth--
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Inverse != nil {
|
|
||||||
// if node.Program != nil {
|
|
||||||
// v.depth++
|
|
||||||
// }
|
|
||||||
|
|
||||||
v.line("{{^}}")
|
|
||||||
v.depth++
|
|
||||||
node.Inverse.Accept(v)
|
|
||||||
v.depth--
|
|
||||||
|
|
||||||
// if node.Program != nil {
|
|
||||||
// v.depth--
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
v.inBlock = false
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitPartial implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitPartial(node *PartialStatement) interface{} {
|
|
||||||
v.indent()
|
|
||||||
v.str("{{> PARTIAL:")
|
|
||||||
|
|
||||||
v.original = true
|
|
||||||
node.Name.Accept(v)
|
|
||||||
v.original = false
|
|
||||||
|
|
||||||
if len(node.Params) > 0 {
|
|
||||||
v.str(" ")
|
|
||||||
node.Params[0].Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash
|
|
||||||
if node.Hash != nil {
|
|
||||||
v.str(" ")
|
|
||||||
node.Hash.Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.str(" }}")
|
|
||||||
v.nl()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitContent implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitContent(node *ContentStatement) interface{} {
|
|
||||||
v.line("CONTENT[ '" + node.Value + "' ]")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitComment implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitComment(node *CommentStatement) interface{} {
|
|
||||||
v.line("{{! '" + node.Value + "' }}")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expressions
|
|
||||||
|
|
||||||
// VisitExpression implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitExpression(node *Expression) interface{} {
|
|
||||||
if v.inBlock {
|
|
||||||
v.indent()
|
|
||||||
}
|
|
||||||
|
|
||||||
// path
|
|
||||||
node.Path.Accept(v)
|
|
||||||
|
|
||||||
// params
|
|
||||||
v.str(" [")
|
|
||||||
for i, n := range node.Params {
|
|
||||||
if i > 0 {
|
|
||||||
v.str(", ")
|
|
||||||
}
|
|
||||||
n.Accept(v)
|
|
||||||
}
|
|
||||||
v.str("]")
|
|
||||||
|
|
||||||
// hash
|
|
||||||
if node.Hash != nil {
|
|
||||||
v.str(" ")
|
|
||||||
node.Hash.Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.inBlock {
|
|
||||||
v.nl()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitSubExpression implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitSubExpression(node *SubExpression) interface{} {
|
|
||||||
node.Expression.Accept(v)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitPath implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitPath(node *PathExpression) interface{} {
|
|
||||||
if v.original {
|
|
||||||
v.str(node.Original)
|
|
||||||
} else {
|
|
||||||
path := strings.Join(node.Parts, "/")
|
|
||||||
|
|
||||||
result := ""
|
|
||||||
if node.Data {
|
|
||||||
result += "@"
|
|
||||||
}
|
|
||||||
|
|
||||||
v.str(result + "PATH:" + path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Literals
|
|
||||||
|
|
||||||
// VisitString implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitString(node *StringLiteral) interface{} {
|
|
||||||
if v.original {
|
|
||||||
v.str(node.Value)
|
|
||||||
} else {
|
|
||||||
v.str("\"" + node.Value + "\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitBoolean implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitBoolean(node *BooleanLiteral) interface{} {
|
|
||||||
if v.original {
|
|
||||||
v.str(node.Original)
|
|
||||||
} else {
|
|
||||||
v.str(fmt.Sprintf("BOOLEAN{%s}", node.Canonical()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitNumber implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitNumber(node *NumberLiteral) interface{} {
|
|
||||||
if v.original {
|
|
||||||
v.str(node.Original)
|
|
||||||
} else {
|
|
||||||
v.str(fmt.Sprintf("NUMBER{%s}", node.Canonical()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Miscellaneous
|
|
||||||
|
|
||||||
// VisitHash implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitHash(node *Hash) interface{} {
|
|
||||||
v.str("HASH{")
|
|
||||||
|
|
||||||
for i, p := range node.Pairs {
|
|
||||||
if i > 0 {
|
|
||||||
v.str(", ")
|
|
||||||
}
|
|
||||||
p.Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.str("}")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitHashPair implements corresponding Visitor interface method
|
|
||||||
func (v *printVisitor) VisitHashPair(node *HashPair) interface{} {
|
|
||||||
v.str(node.Key + "=")
|
|
||||||
node.Val.Accept(v)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
167
vendor/github.com/aymerick/raymond/base_test.go
generated
vendored
167
vendor/github.com/aymerick/raymond/base_test.go
generated
vendored
|
@ -1,167 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Test struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
data interface{}
|
|
||||||
privData map[string]interface{}
|
|
||||||
helpers map[string]interface{}
|
|
||||||
partials map[string]string
|
|
||||||
output interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func launchTests(t *testing.T, tests []Test) {
|
|
||||||
// NOTE: TestMustache() makes Parallel testing fail
|
|
||||||
// t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
var err error
|
|
||||||
var tpl *Template
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl, err = Parse(test.input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test '%s' failed - Failed to parse template\ninput:\n\t'%s'\nerror:\n\t%s", test.name, test.input, err)
|
|
||||||
} else {
|
|
||||||
if len(test.helpers) > 0 {
|
|
||||||
// register helpers
|
|
||||||
tpl.RegisterHelpers(test.helpers)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(test.partials) > 0 {
|
|
||||||
// register partials
|
|
||||||
tpl.RegisterPartials(test.partials)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup private data frame
|
|
||||||
var privData *DataFrame
|
|
||||||
if test.privData != nil {
|
|
||||||
privData = NewDataFrame()
|
|
||||||
for k, v := range test.privData {
|
|
||||||
privData.Set(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// render template
|
|
||||||
output, err := tpl.ExecWith(test.data, privData)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\ndata:\n\t%s\nerror:\n\t%s\nAST:\n\t%s", test.name, test.input, Str(test.data), err, tpl.PrintAST())
|
|
||||||
} else {
|
|
||||||
// check output
|
|
||||||
var expectedArr []string
|
|
||||||
expectedArr, ok := test.output.([]string)
|
|
||||||
if ok {
|
|
||||||
match := false
|
|
||||||
for _, expectedStr := range expectedArr {
|
|
||||||
if expectedStr == output {
|
|
||||||
match = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !match {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\ndata:\n\t%s\npartials:\n\t%s\nexpected\n\t%q\ngot\n\t%q\nAST:\n%s", test.name, test.input, Str(test.data), Str(test.partials), expectedArr, output, tpl.PrintAST())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expectedStr, ok := test.output.(string)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Errorf("Erroneous test output description: %q", test.output))
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedStr != output {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\ndata:\n\t%s\npartials:\n\t%s\nexpected\n\t%q\ngot\n\t%q\nAST:\n%s", test.name, test.input, Str(test.data), Str(test.partials), expectedStr, output, tpl.PrintAST())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func launchErrorTests(t *testing.T, tests []Test) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
var err error
|
|
||||||
var tpl *Template
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl, err = Parse(test.input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test '%s' failed - Failed to parse template\ninput:\n\t'%s'\nerror:\n\t%s", test.name, test.input, err)
|
|
||||||
} else {
|
|
||||||
if len(test.helpers) > 0 {
|
|
||||||
// register helpers
|
|
||||||
tpl.RegisterHelpers(test.helpers)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(test.partials) > 0 {
|
|
||||||
// register partials
|
|
||||||
tpl.RegisterPartials(test.partials)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup private data frame
|
|
||||||
var privData *DataFrame
|
|
||||||
if test.privData != nil {
|
|
||||||
privData := NewDataFrame()
|
|
||||||
for k, v := range test.privData {
|
|
||||||
privData.Set(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// render template
|
|
||||||
output, err := tpl.ExecWith(test.data, privData)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Test '%s' failed - Error expected\ninput:\n\t'%s'\ngot\n\t%q\nAST:\n%q", test.name, test.input, output, tpl.PrintAST())
|
|
||||||
} else {
|
|
||||||
var errMatch error
|
|
||||||
match := false
|
|
||||||
|
|
||||||
// check output
|
|
||||||
var expectedArr []string
|
|
||||||
expectedArr, ok := test.output.([]string)
|
|
||||||
if ok {
|
|
||||||
if len(expectedArr) > 0 {
|
|
||||||
for _, expectedStr := range expectedArr {
|
|
||||||
match, errMatch = regexp.MatchString(regexp.QuoteMeta(expectedStr), fmt.Sprint(err))
|
|
||||||
if errMatch != nil {
|
|
||||||
panic("Failed to match regexp")
|
|
||||||
}
|
|
||||||
|
|
||||||
if match {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// nothing to test
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expectedStr, ok := test.output.(string)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Errorf("Erroneous test output description: %q", test.output))
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedStr != "" {
|
|
||||||
match, errMatch = regexp.MatchString(regexp.QuoteMeta(expectedStr), fmt.Sprint(err))
|
|
||||||
if errMatch != nil {
|
|
||||||
panic("Failed to match regexp")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// nothing to test
|
|
||||||
match = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !match {
|
|
||||||
t.Errorf("Test '%s' failed - Incorrect error returned\ninput:\n\t'%s'\ndata:\n\t%s\nexpected\n\t%q\ngot\n\t%q", test.name, test.input, Str(test.data), test.output, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
316
vendor/github.com/aymerick/raymond/benchmark_test.go
generated
vendored
316
vendor/github.com/aymerick/raymond/benchmark_test.go
generated
vendored
|
@ -1,316 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/bench/
|
|
||||||
//
|
|
||||||
// Note that handlebars.js does NOT benchmark template compilation, it only benchmarks evaluation.
|
|
||||||
//
|
|
||||||
|
|
||||||
func BenchmarkArguments(b *testing.B) {
|
|
||||||
source := `{{foo person "person" 1 true foo=bar foo="person" foo=1 foo=true}}`
|
|
||||||
|
|
||||||
ctx := map[string]bool{
|
|
||||||
"bar": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
tpl.RegisterHelper("foo", func(a, b, c, d interface{}) string { return "" })
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkArrayEach(b *testing.B) {
|
|
||||||
source := `{{#each names}}{{name}}{{/each}}`
|
|
||||||
|
|
||||||
ctx := map[string][]map[string]string{
|
|
||||||
"names": {
|
|
||||||
{"name": "Moe"},
|
|
||||||
{"name": "Larry"},
|
|
||||||
{"name": "Curly"},
|
|
||||||
{"name": "Shemp"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkArrayMustache(b *testing.B) {
|
|
||||||
source := `{{#names}}{{name}}{{/names}}`
|
|
||||||
|
|
||||||
ctx := map[string][]map[string]string{
|
|
||||||
"names": {
|
|
||||||
{"name": "Moe"},
|
|
||||||
{"name": "Larry"},
|
|
||||||
{"name": "Curly"},
|
|
||||||
{"name": "Shemp"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkComplex(b *testing.B) {
|
|
||||||
source := `<h1>{{header}}</h1>
|
|
||||||
{{#if items}}
|
|
||||||
<ul>
|
|
||||||
{{#each items}}
|
|
||||||
{{#if current}}
|
|
||||||
<li><strong>{{name}}</strong></li>
|
|
||||||
{{^}}
|
|
||||||
<li><a href="{{url}}">{{name}}</a></li>
|
|
||||||
{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
{{^}}
|
|
||||||
<p>The list is empty.</p>
|
|
||||||
{{/if}}
|
|
||||||
`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"header": func() string { return "Colors" },
|
|
||||||
"hasItems": true,
|
|
||||||
"items": []map[string]interface{}{
|
|
||||||
{"name": "red", "current": true, "url": "#Red"},
|
|
||||||
{"name": "green", "current": false, "url": "#Green"},
|
|
||||||
{"name": "blue", "current": false, "url": "#Blue"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkData(b *testing.B) {
|
|
||||||
source := `{{#each names}}{{@index}}{{name}}{{/each}}`
|
|
||||||
|
|
||||||
ctx := map[string][]map[string]string{
|
|
||||||
"names": {
|
|
||||||
{"name": "Moe"},
|
|
||||||
{"name": "Larry"},
|
|
||||||
{"name": "Curly"},
|
|
||||||
{"name": "Shemp"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDepth1(b *testing.B) {
|
|
||||||
source := `{{#each names}}{{../foo}}{{/each}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"names": []map[string]string{
|
|
||||||
{"name": "Moe"},
|
|
||||||
{"name": "Larry"},
|
|
||||||
{"name": "Curly"},
|
|
||||||
{"name": "Shemp"},
|
|
||||||
},
|
|
||||||
"foo": "bar",
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDepth2(b *testing.B) {
|
|
||||||
source := `{{#each names}}{{#each name}}{{../bat}}{{../../foo}}{{/each}}{{/each}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"names": []map[string]interface{}{
|
|
||||||
{"bat": "foo", "name": []string{"Moe"}},
|
|
||||||
{"bat": "foo", "name": []string{"Larry"}},
|
|
||||||
{"bat": "foo", "name": []string{"Curly"}},
|
|
||||||
{"bat": "foo", "name": []string{"Shemp"}},
|
|
||||||
},
|
|
||||||
"foo": "bar",
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkObjectMustache(b *testing.B) {
|
|
||||||
source := `{{#person}}{{name}}{{age}}{{/person}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"person": map[string]interface{}{
|
|
||||||
"name": "Larry",
|
|
||||||
"age": 45,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkObject(b *testing.B) {
|
|
||||||
source := `{{#with person}}{{name}}{{age}}{{/with}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"person": map[string]interface{}{
|
|
||||||
"name": "Larry",
|
|
||||||
"age": 45,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkPartialRecursion(b *testing.B) {
|
|
||||||
source := `{{name}}{{#each kids}}{{>recursion}}{{/each}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"name": 1,
|
|
||||||
"kids": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"name": "1.1",
|
|
||||||
"kids": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"name": "1.1.1",
|
|
||||||
"kids": []map[string]interface{}{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
partial := MustParse(`{{name}}{{#each kids}}{{>recursion}}{{/each}}`)
|
|
||||||
tpl.RegisterPartialTemplate("recursion", partial)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkPartial(b *testing.B) {
|
|
||||||
source := `{{#each peeps}}{{>variables}}{{/each}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"peeps": []map[string]interface{}{
|
|
||||||
{"name": "Moe", "count": 15},
|
|
||||||
{"name": "Moe", "count": 5},
|
|
||||||
{"name": "Curly", "count": 1},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
partial := MustParse(`Hello {{name}}! You have {{count}} new messages.`)
|
|
||||||
tpl.RegisterPartialTemplate("variables", partial)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkPath(b *testing.B) {
|
|
||||||
source := `{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"person": map[string]interface{}{
|
|
||||||
"name": map[string]interface{}{
|
|
||||||
"bar": map[string]string{
|
|
||||||
"baz": "Larry",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"age": 45,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkString(b *testing.B) {
|
|
||||||
source := `Hello world`
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSubExpression(b *testing.B) {
|
|
||||||
source := `{{echo (header)}}`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
tpl.RegisterHelpers(map[string]interface{}{
|
|
||||||
"echo": func(v string) string { return "foo " + v },
|
|
||||||
"header": func() string { return "Colors" },
|
|
||||||
})
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkVariables(b *testing.B) {
|
|
||||||
source := `Hello {{name}}! You have {{count}} new messages.`
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"name": "Mick",
|
|
||||||
"count": 30,
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
tpl.MustExec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
95
vendor/github.com/aymerick/raymond/data_frame.go
generated
vendored
95
vendor/github.com/aymerick/raymond/data_frame.go
generated
vendored
|
@ -1,95 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import "reflect"
|
|
||||||
|
|
||||||
// DataFrame represents a private data frame.
|
|
||||||
//
|
|
||||||
// Cf. private variables documentation at: http://handlebarsjs.com/block_helpers.html
|
|
||||||
type DataFrame struct {
|
|
||||||
parent *DataFrame
|
|
||||||
data map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDataFrame instanciates a new private data frame.
|
|
||||||
func NewDataFrame() *DataFrame {
|
|
||||||
return &DataFrame{
|
|
||||||
data: make(map[string]interface{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy instanciates a new private data frame with receiver as parent.
|
|
||||||
func (p *DataFrame) Copy() *DataFrame {
|
|
||||||
result := NewDataFrame()
|
|
||||||
|
|
||||||
for k, v := range p.data {
|
|
||||||
result.data[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
result.parent = p
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// newIterDataFrame instanciates a new private data frame with receiver as parent and with iteration data set (@index, @key, @first, @last)
|
|
||||||
func (p *DataFrame) newIterDataFrame(length int, i int, key interface{}) *DataFrame {
|
|
||||||
result := p.Copy()
|
|
||||||
|
|
||||||
result.Set("index", i)
|
|
||||||
result.Set("key", key)
|
|
||||||
result.Set("first", i == 0)
|
|
||||||
result.Set("last", i == length-1)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets a data value.
|
|
||||||
func (p *DataFrame) Set(key string, val interface{}) {
|
|
||||||
p.data[key] = val
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets a data value.
|
|
||||||
func (p *DataFrame) Get(key string) interface{} {
|
|
||||||
return p.find([]string{key})
|
|
||||||
}
|
|
||||||
|
|
||||||
// find gets a deep data value
|
|
||||||
//
|
|
||||||
// @todo This is NOT consistent with the way we resolve data in template (cf. `evalDataPathExpression()`) ! FIX THAT !
|
|
||||||
func (p *DataFrame) find(parts []string) interface{} {
|
|
||||||
data := p.data
|
|
||||||
|
|
||||||
for i, part := range parts {
|
|
||||||
val := data[part]
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == len(parts)-1 {
|
|
||||||
// found
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
valValue := reflect.ValueOf(val)
|
|
||||||
if valValue.Kind() != reflect.Map {
|
|
||||||
// not found
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// continue
|
|
||||||
data = mapStringInterface(valValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// not found
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// mapStringInterface converts any `map` to `map[string]interface{}`
|
|
||||||
func mapStringInterface(value reflect.Value) map[string]interface{} {
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
|
|
||||||
for _, key := range value.MapKeys() {
|
|
||||||
result[strValue(key)] = value.MapIndex(key).Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
65
vendor/github.com/aymerick/raymond/escape.go
generated
vendored
65
vendor/github.com/aymerick/raymond/escape.go
generated
vendored
|
@ -1,65 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// That whole file is borrowed from https://github.com/golang/go/tree/master/src/html/escape.go
|
|
||||||
//
|
|
||||||
// With changes:
|
|
||||||
// ' => '
|
|
||||||
// " => "
|
|
||||||
//
|
|
||||||
// To stay in sync with JS implementation, and make mustache tests pass.
|
|
||||||
//
|
|
||||||
|
|
||||||
type writer interface {
|
|
||||||
WriteString(string) (int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const escapedChars = `&'<>"`
|
|
||||||
|
|
||||||
func escape(w writer, s string) error {
|
|
||||||
i := strings.IndexAny(s, escapedChars)
|
|
||||||
for i != -1 {
|
|
||||||
if _, err := w.WriteString(s[:i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var esc string
|
|
||||||
switch s[i] {
|
|
||||||
case '&':
|
|
||||||
esc = "&"
|
|
||||||
case '\'':
|
|
||||||
esc = "'"
|
|
||||||
case '<':
|
|
||||||
esc = "<"
|
|
||||||
case '>':
|
|
||||||
esc = ">"
|
|
||||||
case '"':
|
|
||||||
esc = """
|
|
||||||
default:
|
|
||||||
panic("unrecognized escape character")
|
|
||||||
}
|
|
||||||
s = s[i+1:]
|
|
||||||
if _, err := w.WriteString(esc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i = strings.IndexAny(s, escapedChars)
|
|
||||||
}
|
|
||||||
_, err := w.WriteString(s)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape escapes special HTML characters.
|
|
||||||
//
|
|
||||||
// It can be used by helpers that return a SafeString and that need to escape some content by themselves.
|
|
||||||
func Escape(s string) string {
|
|
||||||
if strings.IndexAny(s, escapedChars) == -1 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
escape(&buf, s)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
20
vendor/github.com/aymerick/raymond/escape_test.go
generated
vendored
20
vendor/github.com/aymerick/raymond/escape_test.go
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func ExampleEscape() {
|
|
||||||
tpl := MustParse("{{link url text}}")
|
|
||||||
|
|
||||||
tpl.RegisterHelper("link", func(url string, text string) SafeString {
|
|
||||||
return SafeString("<a href='" + Escape(url) + "'>" + Escape(text) + "</a>")
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx := map[string]string{
|
|
||||||
"url": "http://www.aymerick.com/",
|
|
||||||
"text": "This is a <em>cool</em> website",
|
|
||||||
}
|
|
||||||
|
|
||||||
result := tpl.MustExec(ctx)
|
|
||||||
fmt.Print(result)
|
|
||||||
// Output: <a href='http://www.aymerick.com/'>This is a <em>cool</em> website</a>
|
|
||||||
}
|
|
984
vendor/github.com/aymerick/raymond/eval.go
generated
vendored
984
vendor/github.com/aymerick/raymond/eval.go
generated
vendored
|
@ -1,984 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// @note borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go
|
|
||||||
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
||||||
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
|
||||||
|
|
||||||
zero reflect.Value
|
|
||||||
)
|
|
||||||
|
|
||||||
// evalVisitor evaluates a handlebars template with context
|
|
||||||
type evalVisitor struct {
|
|
||||||
tpl *Template
|
|
||||||
|
|
||||||
// contexts stack
|
|
||||||
ctx []reflect.Value
|
|
||||||
|
|
||||||
// current data frame (chained with parent)
|
|
||||||
dataFrame *DataFrame
|
|
||||||
|
|
||||||
// block parameters stack
|
|
||||||
blockParams []map[string]interface{}
|
|
||||||
|
|
||||||
// block statements stack
|
|
||||||
blocks []*ast.BlockStatement
|
|
||||||
|
|
||||||
// expressions stack
|
|
||||||
exprs []*ast.Expression
|
|
||||||
|
|
||||||
// memoize expressions that were function calls
|
|
||||||
exprFunc map[*ast.Expression]bool
|
|
||||||
|
|
||||||
// used for info on panic
|
|
||||||
curNode ast.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEvalVisitor instanciate a new evaluation visitor with given context and initial private data frame
|
|
||||||
//
|
|
||||||
// If privData is nil, then a default data frame is created
|
|
||||||
func newEvalVisitor(tpl *Template, ctx interface{}, privData *DataFrame) *evalVisitor {
|
|
||||||
frame := privData
|
|
||||||
if frame == nil {
|
|
||||||
frame = NewDataFrame()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &evalVisitor{
|
|
||||||
tpl: tpl,
|
|
||||||
ctx: []reflect.Value{reflect.ValueOf(ctx)},
|
|
||||||
dataFrame: frame,
|
|
||||||
exprFunc: make(map[*ast.Expression]bool),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// at sets current node
|
|
||||||
func (v *evalVisitor) at(node ast.Node) {
|
|
||||||
v.curNode = node
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Contexts stack
|
|
||||||
//
|
|
||||||
|
|
||||||
// pushCtx pushes new context to the stack
|
|
||||||
func (v *evalVisitor) pushCtx(ctx reflect.Value) {
|
|
||||||
v.ctx = append(v.ctx, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// popCtx pops last context from stack
|
|
||||||
func (v *evalVisitor) popCtx() reflect.Value {
|
|
||||||
if len(v.ctx) == 0 {
|
|
||||||
return zero
|
|
||||||
}
|
|
||||||
|
|
||||||
var result reflect.Value
|
|
||||||
result, v.ctx = v.ctx[len(v.ctx)-1], v.ctx[:len(v.ctx)-1]
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// rootCtx returns root context
|
|
||||||
func (v *evalVisitor) rootCtx() reflect.Value {
|
|
||||||
return v.ctx[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// curCtx returns current context
|
|
||||||
func (v *evalVisitor) curCtx() reflect.Value {
|
|
||||||
return v.ancestorCtx(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ancestorCtx returns ancestor context
|
|
||||||
func (v *evalVisitor) ancestorCtx(depth int) reflect.Value {
|
|
||||||
index := len(v.ctx) - 1 - depth
|
|
||||||
if index < 0 {
|
|
||||||
return zero
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.ctx[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Private data frame
|
|
||||||
//
|
|
||||||
|
|
||||||
// setDataFrame sets new data frame
|
|
||||||
func (v *evalVisitor) setDataFrame(frame *DataFrame) {
|
|
||||||
v.dataFrame = frame
|
|
||||||
}
|
|
||||||
|
|
||||||
// popDataFrame sets back parent data frame
|
|
||||||
func (v *evalVisitor) popDataFrame() {
|
|
||||||
v.dataFrame = v.dataFrame.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Block Parameters stack
|
|
||||||
//
|
|
||||||
|
|
||||||
// pushBlockParams pushes new block params to the stack
|
|
||||||
func (v *evalVisitor) pushBlockParams(params map[string]interface{}) {
|
|
||||||
v.blockParams = append(v.blockParams, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// popBlockParams pops last block params from stack
|
|
||||||
func (v *evalVisitor) popBlockParams() map[string]interface{} {
|
|
||||||
var result map[string]interface{}
|
|
||||||
|
|
||||||
if len(v.blockParams) == 0 {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
result, v.blockParams = v.blockParams[len(v.blockParams)-1], v.blockParams[:len(v.blockParams)-1]
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockParam iterates on stack to find given block parameter, and returns its value or nil if not founc
|
|
||||||
func (v *evalVisitor) blockParam(name string) interface{} {
|
|
||||||
for i := len(v.blockParams) - 1; i >= 0; i-- {
|
|
||||||
for k, v := range v.blockParams[i] {
|
|
||||||
if name == k {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Blocks stack
|
|
||||||
//
|
|
||||||
|
|
||||||
// pushBlock pushes new block statement to stack
|
|
||||||
func (v *evalVisitor) pushBlock(block *ast.BlockStatement) {
|
|
||||||
v.blocks = append(v.blocks, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
// popBlock pops last block statement from stack
|
|
||||||
func (v *evalVisitor) popBlock() *ast.BlockStatement {
|
|
||||||
if len(v.blocks) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var result *ast.BlockStatement
|
|
||||||
result, v.blocks = v.blocks[len(v.blocks)-1], v.blocks[:len(v.blocks)-1]
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// curBlock returns current block statement
|
|
||||||
func (v *evalVisitor) curBlock() *ast.BlockStatement {
|
|
||||||
if len(v.blocks) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.blocks[len(v.blocks)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Expressions stack
|
|
||||||
//
|
|
||||||
|
|
||||||
// pushExpr pushes new expression to stack
|
|
||||||
func (v *evalVisitor) pushExpr(expression *ast.Expression) {
|
|
||||||
v.exprs = append(v.exprs, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
// popExpr pops last expression from stack
|
|
||||||
func (v *evalVisitor) popExpr() *ast.Expression {
|
|
||||||
if len(v.exprs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var result *ast.Expression
|
|
||||||
result, v.exprs = v.exprs[len(v.exprs)-1], v.exprs[:len(v.exprs)-1]
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// curExpr returns current expression
|
|
||||||
func (v *evalVisitor) curExpr() *ast.Expression {
|
|
||||||
if len(v.exprs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.exprs[len(v.exprs)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Error functions
|
|
||||||
//
|
|
||||||
|
|
||||||
// errPanic panics
|
|
||||||
func (v *evalVisitor) errPanic(err error) {
|
|
||||||
panic(fmt.Errorf("Evaluation error: %s\nCurrent node:\n\t%s", err, v.curNode))
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf panics with a custom message
|
|
||||||
func (v *evalVisitor) errorf(format string, args ...interface{}) {
|
|
||||||
v.errPanic(fmt.Errorf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Evaluation
|
|
||||||
//
|
|
||||||
|
|
||||||
// evalProgram eEvaluates program with given context and returns string result
|
|
||||||
func (v *evalVisitor) evalProgram(program *ast.Program, ctx interface{}, data *DataFrame, key interface{}) string {
|
|
||||||
blockParams := make(map[string]interface{})
|
|
||||||
|
|
||||||
// compute block params
|
|
||||||
if len(program.BlockParams) > 0 {
|
|
||||||
blockParams[program.BlockParams[0]] = ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len(program.BlockParams) > 1) && (key != nil) {
|
|
||||||
blockParams[program.BlockParams[1]] = key
|
|
||||||
}
|
|
||||||
|
|
||||||
// push contexts
|
|
||||||
if len(blockParams) > 0 {
|
|
||||||
v.pushBlockParams(blockParams)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctxVal := reflect.ValueOf(ctx)
|
|
||||||
if ctxVal.IsValid() {
|
|
||||||
v.pushCtx(ctxVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
if data != nil {
|
|
||||||
v.setDataFrame(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate program
|
|
||||||
result, _ := program.Accept(v).(string)
|
|
||||||
|
|
||||||
// pop contexts
|
|
||||||
if data != nil {
|
|
||||||
v.popDataFrame()
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctxVal.IsValid() {
|
|
||||||
v.popCtx()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(blockParams) > 0 {
|
|
||||||
v.popBlockParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalPath evaluates all path parts with given context
|
|
||||||
func (v *evalVisitor) evalPath(ctx reflect.Value, parts []string, exprRoot bool) (reflect.Value, bool) {
|
|
||||||
partResolved := false
|
|
||||||
|
|
||||||
for i := 0; i < len(parts); i++ {
|
|
||||||
part := parts[i]
|
|
||||||
|
|
||||||
// "[foo bar]"" => "foo bar"
|
|
||||||
if (len(part) >= 2) && (part[0] == '[') && (part[len(part)-1] == ']') {
|
|
||||||
part = part[1 : len(part)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = v.evalField(ctx, part, exprRoot)
|
|
||||||
if !ctx.IsValid() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// we resolved at least one part of path
|
|
||||||
partResolved = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx, partResolved
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalField evaluates field with given context
|
|
||||||
func (v *evalVisitor) evalField(ctx reflect.Value, fieldName string, exprRoot bool) reflect.Value {
|
|
||||||
result := zero
|
|
||||||
|
|
||||||
ctx, _ = indirect(ctx)
|
|
||||||
if !ctx.IsValid() {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if this is a method call
|
|
||||||
result, isMeth := v.evalMethod(ctx, fieldName, exprRoot)
|
|
||||||
if !isMeth {
|
|
||||||
switch ctx.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
// example: firstName => FirstName
|
|
||||||
expFieldName := strings.Title(fieldName)
|
|
||||||
|
|
||||||
// check if struct have this field and that it is exported
|
|
||||||
if tField, ok := ctx.Type().FieldByName(expFieldName); ok && (tField.PkgPath == "") {
|
|
||||||
// struct field
|
|
||||||
result = ctx.FieldByIndex(tField.Index)
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
nameVal := reflect.ValueOf(fieldName)
|
|
||||||
if nameVal.Type().AssignableTo(ctx.Type().Key()) {
|
|
||||||
// map key
|
|
||||||
result = ctx.MapIndex(nameVal)
|
|
||||||
}
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if i, err := strconv.Atoi(fieldName); (err == nil) && (i < ctx.Len()) {
|
|
||||||
result = ctx.Index(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if result is a function
|
|
||||||
result, _ = indirect(result)
|
|
||||||
if result.Kind() == reflect.Func {
|
|
||||||
result = v.evalFieldFunc(fieldName, result, exprRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalFieldFunc tries to evaluate given method name, and a boolean to indicate if this was a method call
|
|
||||||
func (v *evalVisitor) evalMethod(ctx reflect.Value, name string, exprRoot bool) (reflect.Value, bool) {
|
|
||||||
if ctx.Kind() != reflect.Interface && ctx.CanAddr() {
|
|
||||||
ctx = ctx.Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
method := ctx.MethodByName(name)
|
|
||||||
if !method.IsValid() {
|
|
||||||
// example: subject() => Subject()
|
|
||||||
method = ctx.MethodByName(strings.Title(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !method.IsValid() {
|
|
||||||
return zero, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.evalFieldFunc(name, method, exprRoot), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalFieldFunc evaluates given function
|
|
||||||
func (v *evalVisitor) evalFieldFunc(name string, funcVal reflect.Value, exprRoot bool) reflect.Value {
|
|
||||||
ensureValidHelper(name, funcVal)
|
|
||||||
|
|
||||||
var options *Options
|
|
||||||
if exprRoot {
|
|
||||||
// create function arg with all params/hash
|
|
||||||
expr := v.curExpr()
|
|
||||||
options = v.helperOptions(expr)
|
|
||||||
|
|
||||||
// ok, that expression was a function call
|
|
||||||
v.exprFunc[expr] = true
|
|
||||||
} else {
|
|
||||||
// we are not at root of expression, so we are a parameter... and we don't like
|
|
||||||
// infinite loops caused by trying to parse ourself forever
|
|
||||||
options = newEmptyOptions(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.callFunc(name, funcVal, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// findBlockParam returns node's block parameter
|
|
||||||
func (v *evalVisitor) findBlockParam(node *ast.PathExpression) (string, interface{}) {
|
|
||||||
if len(node.Parts) > 0 {
|
|
||||||
name := node.Parts[0]
|
|
||||||
if value := v.blockParam(name); value != nil {
|
|
||||||
return name, value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalPathExpression evaluates a path expression
|
|
||||||
func (v *evalVisitor) evalPathExpression(node *ast.PathExpression, exprRoot bool) interface{} {
|
|
||||||
var result interface{}
|
|
||||||
|
|
||||||
if name, value := v.findBlockParam(node); value != nil {
|
|
||||||
// block parameter value
|
|
||||||
|
|
||||||
// We push a new context so we can evaluate the path expression (note: this may be a bad idea).
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// {{#foo as |bar|}}
|
|
||||||
// {{bar.baz}}
|
|
||||||
// {{/foo}}
|
|
||||||
//
|
|
||||||
// With data:
|
|
||||||
// {"foo": {"baz": "bat"}}
|
|
||||||
newCtx := map[string]interface{}{name: value}
|
|
||||||
|
|
||||||
v.pushCtx(reflect.ValueOf(newCtx))
|
|
||||||
result = v.evalCtxPathExpression(node, exprRoot)
|
|
||||||
v.popCtx()
|
|
||||||
} else {
|
|
||||||
ctxTried := false
|
|
||||||
|
|
||||||
if node.IsDataRoot() {
|
|
||||||
// context path
|
|
||||||
result = v.evalCtxPathExpression(node, exprRoot)
|
|
||||||
|
|
||||||
ctxTried = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == nil) && node.Data {
|
|
||||||
// if it is @root, then we tried to evaluate with root context but nothing was found
|
|
||||||
// so let's try with private data
|
|
||||||
|
|
||||||
// private data
|
|
||||||
result = v.evalDataPathExpression(node, exprRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == nil) && !ctxTried {
|
|
||||||
// context path
|
|
||||||
result = v.evalCtxPathExpression(node, exprRoot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalDataPathExpression evaluates a private data path expression
|
|
||||||
func (v *evalVisitor) evalDataPathExpression(node *ast.PathExpression, exprRoot bool) interface{} {
|
|
||||||
// find data frame
|
|
||||||
frame := v.dataFrame
|
|
||||||
for i := node.Depth; i > 0; i-- {
|
|
||||||
if frame.parent == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
frame = frame.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve data
|
|
||||||
// @note Can be changed to v.evalCtx() as context can't be an array
|
|
||||||
result, _ := v.evalCtxPath(reflect.ValueOf(frame.data), node.Parts, exprRoot)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalCtxPathExpression evaluates a context path expression
|
|
||||||
func (v *evalVisitor) evalCtxPathExpression(node *ast.PathExpression, exprRoot bool) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
if node.IsDataRoot() {
|
|
||||||
// `@root` - remove the first part
|
|
||||||
parts := node.Parts[1:len(node.Parts)]
|
|
||||||
|
|
||||||
result, _ := v.evalCtxPath(v.rootCtx(), parts, exprRoot)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.evalDepthPath(node.Depth, node.Parts, exprRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalDepthPath iterates on contexts, starting at given depth, until there is one that resolve given path parts
|
|
||||||
func (v *evalVisitor) evalDepthPath(depth int, parts []string, exprRoot bool) interface{} {
|
|
||||||
var result interface{}
|
|
||||||
partResolved := false
|
|
||||||
|
|
||||||
ctx := v.ancestorCtx(depth)
|
|
||||||
|
|
||||||
for (result == nil) && ctx.IsValid() && (depth <= len(v.ctx) && !partResolved) {
|
|
||||||
// try with context
|
|
||||||
result, partResolved = v.evalCtxPath(ctx, parts, exprRoot)
|
|
||||||
|
|
||||||
// As soon as we find the first part of a path, we must not try to resolve with parent context if result is finally `nil`
|
|
||||||
// Reference: "Dotted Names - Context Precedence" mustache test
|
|
||||||
if !partResolved && (result == nil) {
|
|
||||||
// try with previous context
|
|
||||||
depth++
|
|
||||||
ctx = v.ancestorCtx(depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalCtxPath evaluates path with given context
|
|
||||||
func (v *evalVisitor) evalCtxPath(ctx reflect.Value, parts []string, exprRoot bool) (interface{}, bool) {
|
|
||||||
var result interface{}
|
|
||||||
partResolved := false
|
|
||||||
|
|
||||||
switch ctx.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
// Array context
|
|
||||||
var results []interface{}
|
|
||||||
|
|
||||||
for i := 0; i < ctx.Len(); i++ {
|
|
||||||
value, _ := v.evalPath(ctx.Index(i), parts, exprRoot)
|
|
||||||
if value.IsValid() {
|
|
||||||
results = append(results, value.Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = results
|
|
||||||
default:
|
|
||||||
// NOT array context
|
|
||||||
var value reflect.Value
|
|
||||||
|
|
||||||
value, partResolved = v.evalPath(ctx, parts, exprRoot)
|
|
||||||
if value.IsValid() {
|
|
||||||
result = value.Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, partResolved
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Helpers
|
|
||||||
//
|
|
||||||
|
|
||||||
// isHelperCall returns true if given expression is a helper call
|
|
||||||
func (v *evalVisitor) isHelperCall(node *ast.Expression) bool {
|
|
||||||
if helperName := node.HelperName(); helperName != "" {
|
|
||||||
return v.findHelper(helperName) != zero
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// findHelper finds given helper
|
|
||||||
func (v *evalVisitor) findHelper(name string) reflect.Value {
|
|
||||||
// check template helpers
|
|
||||||
if h := v.tpl.findHelper(name); h != zero {
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
// check global helpers
|
|
||||||
return findHelper(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// callFunc calls function with given options
|
|
||||||
func (v *evalVisitor) callFunc(name string, funcVal reflect.Value, options *Options) reflect.Value {
|
|
||||||
params := options.Params()
|
|
||||||
|
|
||||||
funcType := funcVal.Type()
|
|
||||||
|
|
||||||
// @todo Is there a better way to do that ?
|
|
||||||
strType := reflect.TypeOf("")
|
|
||||||
boolType := reflect.TypeOf(true)
|
|
||||||
|
|
||||||
// check parameters number
|
|
||||||
addOptions := false
|
|
||||||
numIn := funcType.NumIn()
|
|
||||||
|
|
||||||
if numIn == len(params)+1 {
|
|
||||||
lastArgType := funcType.In(numIn - 1)
|
|
||||||
if reflect.TypeOf(options).AssignableTo(lastArgType) {
|
|
||||||
addOptions = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !addOptions && (len(params) != numIn) {
|
|
||||||
v.errorf("Helper '%s' called with wrong number of arguments, needed %d but got %d", name, numIn, len(params))
|
|
||||||
}
|
|
||||||
|
|
||||||
// check and collect arguments
|
|
||||||
args := make([]reflect.Value, numIn)
|
|
||||||
for i, param := range params {
|
|
||||||
arg := reflect.ValueOf(param)
|
|
||||||
argType := funcType.In(i)
|
|
||||||
|
|
||||||
if !arg.IsValid() {
|
|
||||||
if canBeNil(argType) {
|
|
||||||
arg = reflect.Zero(argType)
|
|
||||||
} else if argType.Kind() == reflect.String {
|
|
||||||
arg = reflect.ValueOf("")
|
|
||||||
} else {
|
|
||||||
// @todo Maybe we can panic on that
|
|
||||||
return reflect.Zero(strType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !arg.Type().AssignableTo(argType) {
|
|
||||||
if strType.AssignableTo(argType) {
|
|
||||||
// convert parameter to string
|
|
||||||
arg = reflect.ValueOf(strValue(arg))
|
|
||||||
} else if boolType.AssignableTo(argType) {
|
|
||||||
// convert parameter to bool
|
|
||||||
val, _ := isTrueValue(arg)
|
|
||||||
arg = reflect.ValueOf(val)
|
|
||||||
} else {
|
|
||||||
v.errorf("Helper %s called with argument %d with type %s but it should be %s", name, i, arg.Type(), argType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args[i] = arg
|
|
||||||
}
|
|
||||||
|
|
||||||
if addOptions {
|
|
||||||
args[numIn-1] = reflect.ValueOf(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := funcVal.Call(args)
|
|
||||||
|
|
||||||
return result[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// callHelper invoqs helper function for given expression node
|
|
||||||
func (v *evalVisitor) callHelper(name string, helper reflect.Value, node *ast.Expression) interface{} {
|
|
||||||
result := v.callFunc(name, helper, v.helperOptions(node))
|
|
||||||
if !result.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo We maybe want to ensure here that helper returned a string or a SafeString
|
|
||||||
return result.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperOptions computes helper options argument from an expression
|
|
||||||
func (v *evalVisitor) helperOptions(node *ast.Expression) *Options {
|
|
||||||
var params []interface{}
|
|
||||||
var hash map[string]interface{}
|
|
||||||
|
|
||||||
for _, paramNode := range node.Params {
|
|
||||||
param := paramNode.Accept(v)
|
|
||||||
params = append(params, param)
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Hash != nil {
|
|
||||||
hash, _ = node.Hash.Accept(v).(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
return newOptions(v, params, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Partials
|
|
||||||
//
|
|
||||||
|
|
||||||
// findPartial finds given partial
|
|
||||||
func (v *evalVisitor) findPartial(name string) *partial {
|
|
||||||
// check template partials
|
|
||||||
if p := v.tpl.findPartial(name); p != nil {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// check global partials
|
|
||||||
return findPartial(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// partialContext computes partial context
|
|
||||||
func (v *evalVisitor) partialContext(node *ast.PartialStatement) reflect.Value {
|
|
||||||
if nb := len(node.Params); nb > 1 {
|
|
||||||
v.errorf("Unsupported number of partial arguments: %d", nb)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len(node.Params) > 0) && (node.Hash != nil) {
|
|
||||||
v.errorf("Passing both context and named parameters to a partial is not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(node.Params) == 1 {
|
|
||||||
return reflect.ValueOf(node.Params[0].Accept(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Hash != nil {
|
|
||||||
hash, _ := node.Hash.Accept(v).(map[string]interface{})
|
|
||||||
return reflect.ValueOf(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
return zero
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalPartial evaluates a partial
|
|
||||||
func (v *evalVisitor) evalPartial(p *partial, node *ast.PartialStatement) string {
|
|
||||||
// get partial template
|
|
||||||
partialTpl, err := p.template()
|
|
||||||
if err != nil {
|
|
||||||
v.errPanic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// push partial context
|
|
||||||
ctx := v.partialContext(node)
|
|
||||||
if ctx.IsValid() {
|
|
||||||
v.pushCtx(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluate partial template
|
|
||||||
result, _ := partialTpl.program.Accept(v).(string)
|
|
||||||
|
|
||||||
// ident partial
|
|
||||||
result = indentLines(result, node.Indent)
|
|
||||||
|
|
||||||
if ctx.IsValid() {
|
|
||||||
v.popCtx()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// indentLines indents all lines of given string
|
|
||||||
func indentLines(str string, indent string) string {
|
|
||||||
if indent == "" {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
var indented []string
|
|
||||||
|
|
||||||
lines := strings.Split(str, "\n")
|
|
||||||
for i, line := range lines {
|
|
||||||
if (i == (len(lines) - 1)) && (line == "") {
|
|
||||||
// input string ends with a new line
|
|
||||||
indented = append(indented, line)
|
|
||||||
} else {
|
|
||||||
indented = append(indented, indent+line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(indented, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Functions
|
|
||||||
//
|
|
||||||
|
|
||||||
// wasFuncCall returns true if given expression was a function call
|
|
||||||
func (v *evalVisitor) wasFuncCall(node *ast.Expression) bool {
|
|
||||||
// check if expression was tagged as a function call
|
|
||||||
return v.exprFunc[node]
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Visitor interface
|
|
||||||
//
|
|
||||||
|
|
||||||
// Statements
|
|
||||||
|
|
||||||
// VisitProgram implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitProgram(node *ast.Program) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
for _, n := range node.Body {
|
|
||||||
if str := Str(n.Accept(v)); str != "" {
|
|
||||||
if _, err := buf.Write([]byte(str)); err != nil {
|
|
||||||
v.errPanic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitMustache implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitMustache(node *ast.MustacheStatement) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
// evaluate expression
|
|
||||||
expr := node.Expression.Accept(v)
|
|
||||||
|
|
||||||
// check if this is a safe string
|
|
||||||
isSafe := isSafeString(expr)
|
|
||||||
|
|
||||||
// get string value
|
|
||||||
str := Str(expr)
|
|
||||||
if !isSafe && !node.Unescaped {
|
|
||||||
// escape html
|
|
||||||
str = Escape(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitBlock implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitBlock(node *ast.BlockStatement) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
v.pushBlock(node)
|
|
||||||
|
|
||||||
var result interface{}
|
|
||||||
|
|
||||||
// evaluate expression
|
|
||||||
expr := node.Expression.Accept(v)
|
|
||||||
|
|
||||||
if v.isHelperCall(node.Expression) || v.wasFuncCall(node.Expression) {
|
|
||||||
// it is the responsability of the helper/function to evaluate block
|
|
||||||
result = expr
|
|
||||||
} else {
|
|
||||||
val := reflect.ValueOf(expr)
|
|
||||||
|
|
||||||
truth, _ := isTrueValue(val)
|
|
||||||
if truth {
|
|
||||||
if node.Program != nil {
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
concat := ""
|
|
||||||
|
|
||||||
// Array context
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
// Computes new private data frame
|
|
||||||
frame := v.dataFrame.newIterDataFrame(val.Len(), i, nil)
|
|
||||||
|
|
||||||
// Evaluate program
|
|
||||||
concat += v.evalProgram(node.Program, val.Index(i).Interface(), frame, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
result = concat
|
|
||||||
default:
|
|
||||||
// NOT array
|
|
||||||
result = v.evalProgram(node.Program, expr, nil, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if node.Inverse != nil {
|
|
||||||
result, _ = node.Inverse.Accept(v).(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v.popBlock()
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitPartial implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitPartial(node *ast.PartialStatement) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
// partialName: helperName | sexpr
|
|
||||||
name, ok := ast.HelperNameStr(node.Name)
|
|
||||||
if !ok {
|
|
||||||
if subExpr, ok := node.Name.(*ast.SubExpression); ok {
|
|
||||||
name, _ = subExpr.Accept(v).(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == "" {
|
|
||||||
v.errorf("Unexpected partial name: %q", node.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
partial := v.findPartial(name)
|
|
||||||
if partial == nil {
|
|
||||||
v.errorf("Partial not found: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.evalPartial(partial, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitContent implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitContent(node *ast.ContentStatement) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
// write content as is
|
|
||||||
return node.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitComment implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitComment(node *ast.CommentStatement) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
// ignore comments
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expressions
|
|
||||||
|
|
||||||
// VisitExpression implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitExpression(node *ast.Expression) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
var result interface{}
|
|
||||||
done := false
|
|
||||||
|
|
||||||
v.pushExpr(node)
|
|
||||||
|
|
||||||
// helper call
|
|
||||||
if helperName := node.HelperName(); helperName != "" {
|
|
||||||
if helper := v.findHelper(helperName); helper != zero {
|
|
||||||
result = v.callHelper(helperName, helper, node)
|
|
||||||
done = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !done {
|
|
||||||
// literal
|
|
||||||
if literal, ok := node.LiteralStr(); ok {
|
|
||||||
if val := v.evalField(v.curCtx(), literal, true); val.IsValid() {
|
|
||||||
result = val.Interface()
|
|
||||||
done = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !done {
|
|
||||||
// field path
|
|
||||||
if path := node.FieldPath(); path != nil {
|
|
||||||
// @todo Find a cleaner way ! Don't break the pattern !
|
|
||||||
// this is an exception to visitor pattern, because we need to pass the info
|
|
||||||
// that this path is at root of current expression
|
|
||||||
if val := v.evalPathExpression(path, true); val != nil {
|
|
||||||
result = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v.popExpr()
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitSubExpression implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitSubExpression(node *ast.SubExpression) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
return node.Expression.Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitPath implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitPath(node *ast.PathExpression) interface{} {
|
|
||||||
return v.evalPathExpression(node, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Literals
|
|
||||||
|
|
||||||
// VisitString implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitString(node *ast.StringLiteral) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
return node.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitBoolean implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitBoolean(node *ast.BooleanLiteral) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
return node.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitNumber implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitNumber(node *ast.NumberLiteral) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
return node.Number()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Miscellaneous
|
|
||||||
|
|
||||||
// VisitHash implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitHash(node *ast.Hash) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
result := make(map[string]interface{})
|
|
||||||
|
|
||||||
for _, pair := range node.Pairs {
|
|
||||||
if value := pair.Accept(v); value != nil {
|
|
||||||
result[pair.Key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisitHashPair implements corresponding Visitor interface method
|
|
||||||
func (v *evalVisitor) VisitHashPair(node *ast.HashPair) interface{} {
|
|
||||||
v.at(node)
|
|
||||||
|
|
||||||
return node.Val.Accept(v)
|
|
||||||
}
|
|
215
vendor/github.com/aymerick/raymond/eval_test.go
generated
vendored
215
vendor/github.com/aymerick/raymond/eval_test.go
generated
vendored
|
@ -1,215 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
var evalTests = []Test{
|
|
||||||
{
|
|
||||||
"only content",
|
|
||||||
"this is content",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"this is content",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checks path in parent contexts",
|
|
||||||
"{{#a}}{{one}}{{#b}}{{one}}{{two}}{{one}}{{/b}}{{/a}}",
|
|
||||||
map[string]interface{}{"a": map[string]int{"one": 1}, "b": map[string]int{"two": 2}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"1121",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block params",
|
|
||||||
"{{#foo as |bar|}}{{bar}}{{/foo}}{{bar}}",
|
|
||||||
map[string]string{"foo": "baz", "bar": "bat"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bazbat",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block params on array",
|
|
||||||
"{{#foo as |bar i|}}{{i}}.{{bar}} {{/foo}}",
|
|
||||||
map[string][]string{"foo": {"baz", "bar", "bat"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"0.baz 1.bar 2.bat ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested block params",
|
|
||||||
"{{#foos as |foo iFoo|}}{{#wats as |wat iWat|}}{{iFoo}}.{{iWat}}.{{foo}}-{{wat}} {{/wats}}{{/foos}}",
|
|
||||||
map[string][]string{"foos": {"baz", "bar"}, "wats": {"the", "phoque"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"0.0.baz-the 0.1.baz-phoque 1.0.bar-the 1.1.bar-phoque ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block params with path reference",
|
|
||||||
"{{#foo as |bar|}}{{bar.baz}}{{/foo}}",
|
|
||||||
map[string]map[string]string{"foo": {"baz": "bat"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bat",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"falsy block evaluation",
|
|
||||||
"{{#foo}}bar{{/foo}} baz",
|
|
||||||
map[string]interface{}{"foo": false},
|
|
||||||
nil, nil, nil,
|
|
||||||
" baz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper returns a SafeString",
|
|
||||||
"{{title}} - {{#bold}}{{body}}{{/bold}}",
|
|
||||||
map[string]string{
|
|
||||||
"title": "My new blog post",
|
|
||||||
"body": "I have so many things to say!",
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"bold": func(options *Options) SafeString {
|
|
||||||
return SafeString(`<div class="mybold">` + options.Fn() + "</div>")
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
`My new blog post - <div class="mybold">I have so many things to say!</div>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"chained blocks",
|
|
||||||
"{{#if a}}A{{else if b}}B{{else}}C{{/if}}",
|
|
||||||
map[string]interface{}{"b": false},
|
|
||||||
nil, nil, nil,
|
|
||||||
"C",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo Test with a "../../path" (depth 2 path) while context is only depth 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEval(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
launchTests(t, evalTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
var evalErrors = []Test{
|
|
||||||
{
|
|
||||||
"functions with wrong number of arguments",
|
|
||||||
`{{foo "bar"}}`,
|
|
||||||
map[string]interface{}{"foo": func(a string, b string) string { return "foo" }},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Helper 'foo' called with wrong number of arguments, needed 2 but got 1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"functions with wrong number of returned values (1)",
|
|
||||||
"{{foo}}",
|
|
||||||
map[string]interface{}{"foo": func() {}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Helper function must return a string or a SafeString",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"functions with wrong number of returned values (2)",
|
|
||||||
"{{foo}}",
|
|
||||||
map[string]interface{}{"foo": func() (string, bool, string) { return "foo", true, "bar" }},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Helper function must return a string or a SafeString",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalErrors(t *testing.T) {
|
|
||||||
launchErrorTests(t, evalErrors)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalStruct(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
source := `<div class="post">
|
|
||||||
<h1>By {{author.FirstName}} {{Author.lastName}}</h1>
|
|
||||||
<div class="body">{{Body}}</div>
|
|
||||||
|
|
||||||
<h1>Comments</h1>
|
|
||||||
|
|
||||||
{{#each comments}}
|
|
||||||
<h2>By {{Author.FirstName}} {{author.LastName}}</h2>
|
|
||||||
<div class="body">{{body}}</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>`
|
|
||||||
|
|
||||||
expected := `<div class="post">
|
|
||||||
<h1>By Jean Valjean</h1>
|
|
||||||
<div class="body">Life is difficult</div>
|
|
||||||
|
|
||||||
<h1>Comments</h1>
|
|
||||||
|
|
||||||
<h2>By Marcel Beliveau</h2>
|
|
||||||
<div class="body">LOL!</div>
|
|
||||||
</div>`
|
|
||||||
|
|
||||||
type Person struct {
|
|
||||||
FirstName string
|
|
||||||
LastName string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Comment struct {
|
|
||||||
Author Person
|
|
||||||
Body string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Post struct {
|
|
||||||
Author Person
|
|
||||||
Body string
|
|
||||||
Comments []Comment
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := Post{
|
|
||||||
Person{"Jean", "Valjean"},
|
|
||||||
"Life is difficult",
|
|
||||||
[]Comment{
|
|
||||||
Comment{
|
|
||||||
Person{"Marcel", "Beliveau"},
|
|
||||||
"LOL!",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
output := MustRender(source, ctx)
|
|
||||||
if output != expected {
|
|
||||||
t.Errorf("Failed to evaluate with struct context")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestFoo struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TestFoo) Subject() string {
|
|
||||||
return "foo"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalMethod(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
source := `Subject is {{subject}}! YES I SAID {{Subject}}!`
|
|
||||||
expected := `Subject is foo! YES I SAID foo!`
|
|
||||||
|
|
||||||
ctx := &TestFoo{}
|
|
||||||
|
|
||||||
output := MustRender(source, ctx)
|
|
||||||
if output != expected {
|
|
||||||
t.Errorf("Failed to evaluate struct method: %s", output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestBar struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TestBar) Subject() interface{} {
|
|
||||||
return testBar
|
|
||||||
}
|
|
||||||
|
|
||||||
func testBar() string {
|
|
||||||
return "bar"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEvalMethodReturningFunc(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
source := `Subject is {{subject}}! YES I SAID {{Subject}}!`
|
|
||||||
expected := `Subject is bar! YES I SAID bar!`
|
|
||||||
|
|
||||||
ctx := &TestBar{}
|
|
||||||
|
|
||||||
output := MustRender(source, ctx)
|
|
||||||
if output != expected {
|
|
||||||
t.Errorf("Failed to evaluate struct method: %s", output)
|
|
||||||
}
|
|
||||||
}
|
|
100
vendor/github.com/aymerick/raymond/handlebars/base_test.go
generated
vendored
100
vendor/github.com/aymerick/raymond/handlebars/base_test.go
generated
vendored
|
@ -1,100 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond"
|
|
||||||
)
|
|
||||||
|
|
||||||
// cf. https://github.com/aymerick/go-fuzz-tests/raymond
|
|
||||||
const dumpTpl = false
|
|
||||||
|
|
||||||
var dumpTplNb = 0
|
|
||||||
|
|
||||||
type Test struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
data interface{}
|
|
||||||
privData map[string]interface{}
|
|
||||||
helpers map[string]interface{}
|
|
||||||
partials map[string]string
|
|
||||||
output interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func launchTests(t *testing.T, tests []Test) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
var err error
|
|
||||||
var tpl *raymond.Template
|
|
||||||
|
|
||||||
if dumpTpl {
|
|
||||||
filename := strconv.Itoa(dumpTplNb)
|
|
||||||
if err := ioutil.WriteFile(path.Join(".", "dump_tpl", filename), []byte(test.input), 0644); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
dumpTplNb++
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl, err = raymond.Parse(test.input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test '%s' failed - Failed to parse template\ninput:\n\t'%s'\nerror:\n\t%s", test.name, test.input, err)
|
|
||||||
} else {
|
|
||||||
if len(test.helpers) > 0 {
|
|
||||||
// register helpers
|
|
||||||
tpl.RegisterHelpers(test.helpers)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(test.partials) > 0 {
|
|
||||||
// register partials
|
|
||||||
tpl.RegisterPartials(test.partials)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup private data frame
|
|
||||||
var privData *raymond.DataFrame
|
|
||||||
if test.privData != nil {
|
|
||||||
privData = raymond.NewDataFrame()
|
|
||||||
for k, v := range test.privData {
|
|
||||||
privData.Set(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// render template
|
|
||||||
output, err := tpl.ExecWith(test.data, privData)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\ndata:\n\t%s\nerror:\n\t%s\nAST:\n\t%s", test.name, test.input, raymond.Str(test.data), err, tpl.PrintAST())
|
|
||||||
} else {
|
|
||||||
// check output
|
|
||||||
var expectedArr []string
|
|
||||||
expectedArr, ok := test.output.([]string)
|
|
||||||
if ok {
|
|
||||||
match := false
|
|
||||||
for _, expectedStr := range expectedArr {
|
|
||||||
if expectedStr == output {
|
|
||||||
match = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !match {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\ndata:\n\t%s\npartials:\n\t%s\nexpected\n\t%q\ngot\n\t%q\nAST:\n%s", test.name, test.input, raymond.Str(test.data), raymond.Str(test.partials), expectedArr, output, tpl.PrintAST())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expectedStr, ok := test.output.(string)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Errorf("Erroneous test output description: %q", test.output))
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedStr != output {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\ndata:\n\t%s\npartials:\n\t%s\nexpected\n\t%q\ngot\n\t%q\nAST:\n%s", test.name, test.input, raymond.Str(test.data), raymond.Str(test.partials), expectedStr, output, tpl.PrintAST())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
650
vendor/github.com/aymerick/raymond/handlebars/basic_test.go
generated
vendored
650
vendor/github.com/aymerick/raymond/handlebars/basic_test.go
generated
vendored
|
@ -1,650 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/basic.js
|
|
||||||
//
|
|
||||||
var basicTests = []Test{
|
|
||||||
{
|
|
||||||
"most basic",
|
|
||||||
"{{foo}}",
|
|
||||||
map[string]string{"foo": "foo"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping (1)",
|
|
||||||
"\\{{foo}}",
|
|
||||||
map[string]string{"foo": "food"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"{{foo}}",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping (2)",
|
|
||||||
"content \\{{foo}}",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"content {{foo}}",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping (3)",
|
|
||||||
"\\\\{{foo}}",
|
|
||||||
map[string]string{"foo": "food"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"\\food",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping (4)",
|
|
||||||
"content \\\\{{foo}}",
|
|
||||||
map[string]string{"foo": "food"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"content \\food",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping (5)",
|
|
||||||
"\\\\ {{foo}}",
|
|
||||||
map[string]string{"foo": "food"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"\\\\ food",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"compiling with a basic context",
|
|
||||||
"Goodbye\n{{cruel}}\n{{world}}!",
|
|
||||||
map[string]string{"cruel": "cruel", "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye\ncruel\nworld!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"compiling with an undefined context (1)",
|
|
||||||
"Goodbye\n{{cruel}}\n{{world.bar}}!",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"Goodbye\n\n!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"compiling with an undefined context (2)",
|
|
||||||
"{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"Goodbye",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comments (1)",
|
|
||||||
"{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
|
|
||||||
map[string]string{"cruel": "cruel", "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye\ncruel\nworld!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comments (2)",
|
|
||||||
" {{~! comment ~}} blah",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"blah",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comments (3)",
|
|
||||||
" {{~!-- long-comment --~}} blah",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"blah",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comments (4)",
|
|
||||||
" {{! comment ~}} blah",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" blah",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comments (5)",
|
|
||||||
" {{!-- long-comment --~}} blah",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" blah",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comments (6)",
|
|
||||||
" {{~! comment}} blah",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" blah",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comments (7)",
|
|
||||||
" {{~!-- long-comment --}} blah",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" blah",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"boolean (1)",
|
|
||||||
"{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": true, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"boolean (2)",
|
|
||||||
"{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": false, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"zeros (1)",
|
|
||||||
"num1: {{num1}}, num2: {{num2}}",
|
|
||||||
map[string]interface{}{"num1": 42, "num2": 0},
|
|
||||||
nil, nil, nil,
|
|
||||||
"num1: 42, num2: 0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"zeros (2)",
|
|
||||||
"num: {{.}}",
|
|
||||||
0,
|
|
||||||
nil, nil, nil,
|
|
||||||
"num: 0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"zeros (3)",
|
|
||||||
"num: {{num1/num2}}",
|
|
||||||
map[string]map[string]interface{}{"num1": {"num2": 0}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"num: 0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"false (1)",
|
|
||||||
"val1: {{val1}}, val2: {{val2}}",
|
|
||||||
map[string]interface{}{"val1": false, "val2": false},
|
|
||||||
nil, nil, nil,
|
|
||||||
"val1: false, val2: false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"false (2)",
|
|
||||||
"val: {{.}}",
|
|
||||||
false,
|
|
||||||
nil, nil, nil,
|
|
||||||
"val: false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"false (3)",
|
|
||||||
"val: {{val1/val2}}",
|
|
||||||
map[string]map[string]interface{}{"val1": {"val2": false}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"val: false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"false (4)",
|
|
||||||
"val1: {{{val1}}}, val2: {{{val2}}}",
|
|
||||||
map[string]interface{}{"val1": false, "val2": false},
|
|
||||||
nil, nil, nil,
|
|
||||||
"val1: false, val2: false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"false (5)",
|
|
||||||
"val: {{{val1/val2}}}",
|
|
||||||
map[string]map[string]interface{}{"val1": {"val2": false}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"val: false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"newlines (1)",
|
|
||||||
"Alan's\nTest",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"Alan's\nTest",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"newlines (2)",
|
|
||||||
"Alan's\rTest",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"Alan's\rTest",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping text (1)",
|
|
||||||
"Awesome's",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Awesome's",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping text (2)",
|
|
||||||
"Awesome\\",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Awesome\\",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping text (3)",
|
|
||||||
"Awesome\\\\ foo",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Awesome\\\\ foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping text (4)",
|
|
||||||
"Awesome {{foo}}",
|
|
||||||
map[string]string{"foo": "\\"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Awesome \\",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping text (5)",
|
|
||||||
" ' ' ",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
" ' ' ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping expressions (6)",
|
|
||||||
"{{{awesome}}}",
|
|
||||||
map[string]string{"awesome": "&'\\<>"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"&'\\<>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping expressions (7)",
|
|
||||||
"{{&awesome}}",
|
|
||||||
map[string]string{"awesome": "&'\\<>"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"&'\\<>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping expressions (8)",
|
|
||||||
"{{awesome}}",
|
|
||||||
map[string]string{"awesome": "&\"'`\\<>"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"&"'`\\<>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"escaping expressions (9)",
|
|
||||||
"{{awesome}}",
|
|
||||||
map[string]string{"awesome": "Escaped, <b> looks like: <b>"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Escaped, <b> looks like: &lt;b&gt;",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"functions returning safestrings shouldn't be escaped",
|
|
||||||
"{{awesome}}",
|
|
||||||
map[string]interface{}{"awesome": func() raymond.SafeString { return raymond.SafeString("&'\\<>") }},
|
|
||||||
nil, nil, nil,
|
|
||||||
"&'\\<>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"functions (1)",
|
|
||||||
"{{awesome}}",
|
|
||||||
map[string]interface{}{"awesome": func() string { return "Awesome" }},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Awesome",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"functions (2)",
|
|
||||||
"{{awesome}}",
|
|
||||||
map[string]interface{}{"awesome": func(options *raymond.Options) string {
|
|
||||||
return options.ValueStr("more")
|
|
||||||
}, "more": "More awesome"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"More awesome",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"functions with context argument",
|
|
||||||
"{{awesome frank}}",
|
|
||||||
map[string]interface{}{"awesome": func(context string) string {
|
|
||||||
return context
|
|
||||||
}, "frank": "Frank"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Frank",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pathed functions with context argument",
|
|
||||||
"{{bar.awesome frank}}",
|
|
||||||
map[string]interface{}{"bar": map[string]interface{}{"awesome": func(context string) string {
|
|
||||||
return context
|
|
||||||
}}, "frank": "Frank"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Frank",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"depthed functions with context argument",
|
|
||||||
"{{#with frank}}{{../awesome .}}{{/with}}",
|
|
||||||
map[string]interface{}{"awesome": func(context string) string {
|
|
||||||
return context
|
|
||||||
}, "frank": "Frank"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Frank",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block functions with context argument",
|
|
||||||
"{{#awesome 1}}inner {{.}}{{/awesome}}",
|
|
||||||
map[string]interface{}{"awesome": func(context interface{}, options *raymond.Options) string {
|
|
||||||
return options.FnWith(context)
|
|
||||||
}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"inner 1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"depthed block functions with context argument",
|
|
||||||
"{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}",
|
|
||||||
map[string]interface{}{
|
|
||||||
"awesome": func(context interface{}, options *raymond.Options) string {
|
|
||||||
return options.FnWith(context)
|
|
||||||
},
|
|
||||||
"value": true,
|
|
||||||
},
|
|
||||||
nil, nil, nil,
|
|
||||||
"inner 1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block functions without context argument",
|
|
||||||
"{{#awesome}}inner{{/awesome}}",
|
|
||||||
map[string]interface{}{
|
|
||||||
"awesome": func(options *raymond.Options) string {
|
|
||||||
return options.Fn()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil, nil, nil,
|
|
||||||
"inner",
|
|
||||||
},
|
|
||||||
// // @note I don't even understand why this test passes with the JS implementation... it should be
|
|
||||||
// // the responsability of the function to evaluate the block
|
|
||||||
// {
|
|
||||||
// "pathed block functions without context argument",
|
|
||||||
// "{{#foo.awesome}}inner{{/foo.awesome}}",
|
|
||||||
// map[string]map[string]interface{}{
|
|
||||||
// "foo": {
|
|
||||||
// "awesome": func(options *raymond.Options) interface{} {
|
|
||||||
// return options.Ctx()
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// nil, nil, nil,
|
|
||||||
// "inner",
|
|
||||||
// },
|
|
||||||
// // @note I don't even understand why this test passes with the JS implementation... it should be
|
|
||||||
// // the responsability of the function to evaluate the block
|
|
||||||
// {
|
|
||||||
// "depthed block functions without context argument",
|
|
||||||
// "{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}",
|
|
||||||
// map[string]interface{}{
|
|
||||||
// "value": true,
|
|
||||||
// "awesome": func(options *raymond.Options) interface{} {
|
|
||||||
// return options.Ctx()
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// nil, nil, nil,
|
|
||||||
// "inner",
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
"paths with hyphens (1)",
|
|
||||||
"{{foo-bar}}",
|
|
||||||
map[string]string{"foo-bar": "baz"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"baz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"paths with hyphens (2)",
|
|
||||||
"{{foo.foo-bar}}",
|
|
||||||
map[string]map[string]string{"foo": {"foo-bar": "baz"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"baz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"paths with hyphens (3)",
|
|
||||||
"{{foo/foo-bar}}",
|
|
||||||
map[string]map[string]string{"foo": {"foo-bar": "baz"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"baz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested paths",
|
|
||||||
"Goodbye {{alan/expression}} world!",
|
|
||||||
map[string]map[string]string{"alan": {"expression": "beautiful"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye beautiful world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested paths with empty string value",
|
|
||||||
"Goodbye {{alan/expression}} world!",
|
|
||||||
map[string]map[string]string{"alan": {"expression": ""}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"literal paths (1)",
|
|
||||||
"Goodbye {{[@alan]/expression}} world!",
|
|
||||||
map[string]map[string]string{"@alan": {"expression": "beautiful"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye beautiful world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"literal paths (2)",
|
|
||||||
"Goodbye {{[foo bar]/expression}} world!",
|
|
||||||
map[string]map[string]string{"foo bar": {"expression": "beautiful"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye beautiful world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"literal references",
|
|
||||||
"Goodbye {{[foo bar]}} world!",
|
|
||||||
map[string]string{"foo bar": "beautiful"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye beautiful world!",
|
|
||||||
},
|
|
||||||
// @note MMm ok, well... no... I don't see the purpose of that test
|
|
||||||
{
|
|
||||||
"that current context path ({{.}}) doesn't hit helpers",
|
|
||||||
"test: {{.}}",
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"helper": func() string {
|
|
||||||
panic("fail")
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"test: ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"complex but empty paths (1)",
|
|
||||||
"{{person/name}}",
|
|
||||||
map[string]map[string]interface{}{"person": {"name": nil}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"complex but empty paths (2)",
|
|
||||||
"{{person/name}}",
|
|
||||||
map[string]map[string]string{"person": {}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword in paths (1)",
|
|
||||||
"{{#goodbyes}}{{this}}{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"goodbyes": []string{"goodbye", "Goodbye", "GOODBYE"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"goodbyeGoodbyeGOODBYE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword in paths (2)",
|
|
||||||
"{{#hellos}}{{this/text}}{{/hellos}}",
|
|
||||||
map[string]interface{}{"hellos": []interface{}{
|
|
||||||
map[string]string{"text": "hello"},
|
|
||||||
map[string]string{"text": "Hello"},
|
|
||||||
map[string]string{"text": "HELLO"},
|
|
||||||
}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"helloHelloHELLO",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword nested inside path' (1)",
|
|
||||||
"{{[this]}}",
|
|
||||||
map[string]string{"this": "bar"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword nested inside path' (2)",
|
|
||||||
"{{text/[this]}}",
|
|
||||||
map[string]map[string]string{"text": {"this": "bar"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword in helpers (1)",
|
|
||||||
"{{#goodbyes}}{{foo this}}{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"goodbyes": []string{"goodbye", "Goodbye", "GOODBYE"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"foo": barSuffixHelper},
|
|
||||||
nil,
|
|
||||||
"bar goodbyebar Goodbyebar GOODBYE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword in helpers (2)",
|
|
||||||
"{{#hellos}}{{foo this/text}}{{/hellos}}",
|
|
||||||
map[string]interface{}{"hellos": []map[string]string{{"text": "hello"}, {"text": "Hello"}, {"text": "HELLO"}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"foo": barSuffixHelper},
|
|
||||||
nil,
|
|
||||||
"bar hellobar Hellobar HELLO",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword nested inside helpers param (1)",
|
|
||||||
"{{foo [this]}}",
|
|
||||||
map[string]interface{}{"this": "bar"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"foo": echoHelper},
|
|
||||||
nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"this keyword nested inside helpers param (2)",
|
|
||||||
"{{foo text/[this]}}",
|
|
||||||
map[string]map[string]string{"text": {"this": "bar"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"foo": echoHelper},
|
|
||||||
nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass string literals (1)",
|
|
||||||
`{{"foo"}}`,
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass string literals (2)",
|
|
||||||
`{{"foo"}}`,
|
|
||||||
map[string]string{"foo": "bar"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass string literals (3)",
|
|
||||||
`{{#"foo"}}{{.}}{{/"foo"}}`,
|
|
||||||
map[string]interface{}{"foo": []string{"bar", "baz"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"barbaz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass number literals (1)",
|
|
||||||
"{{12}}",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass number literals (2)",
|
|
||||||
"{{12}}",
|
|
||||||
map[string]string{"12": "bar"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass number literals (3)",
|
|
||||||
"{{12.34}}",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass number literals (4)",
|
|
||||||
"{{12.34}}",
|
|
||||||
map[string]string{"12.34": "bar"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass number literals (5)",
|
|
||||||
"{{12.34 1}}",
|
|
||||||
map[string]interface{}{"12.34": func(context string) string {
|
|
||||||
return "bar" + context
|
|
||||||
}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass boolean literals (1)",
|
|
||||||
"{{true}}",
|
|
||||||
map[string]string{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass boolean literals (2)",
|
|
||||||
"{{true}}",
|
|
||||||
map[string]string{"": "foo"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pass boolean literals (3)",
|
|
||||||
"{{false}}",
|
|
||||||
map[string]string{"false": "foo"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should handle literals in subexpression",
|
|
||||||
"{{foo (false)}}",
|
|
||||||
map[string]interface{}{"false": func() string { return "bar" }},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"foo": func(context string) string {
|
|
||||||
return context
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
|
||||||
launchTests(t, basicTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBasicErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
inputs := []string{
|
|
||||||
// this keyword nested inside path
|
|
||||||
"{{#hellos}}{{text/this/foo}}{{/hellos}}",
|
|
||||||
// this keyword nested inside helpers param
|
|
||||||
"{{#hellos}}{{foo text/this/foo}}{{/hellos}}",
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedError := regexp.QuoteMeta("Invalid path: text/this")
|
|
||||||
|
|
||||||
for _, input := range inputs {
|
|
||||||
_, err = raymond.Parse(input)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Test failed - Error expected")
|
|
||||||
}
|
|
||||||
|
|
||||||
match, errMatch := regexp.MatchString(expectedError, fmt.Sprint(err))
|
|
||||||
if errMatch != nil {
|
|
||||||
panic("Failed to match regexp")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !match {
|
|
||||||
t.Errorf("Test failed - Expected error:\n\t%s\n\nGot:\n\t%s", expectedError, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
208
vendor/github.com/aymerick/raymond/handlebars/blocks_test.go
generated
vendored
208
vendor/github.com/aymerick/raymond/handlebars/blocks_test.go
generated
vendored
|
@ -1,208 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/blocks.js
|
|
||||||
//
|
|
||||||
var blocksTests = []Test{
|
|
||||||
{
|
|
||||||
"array (1) - Arrays iterate over the contents when not empty",
|
|
||||||
"{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"goodbye! Goodbye! GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"array (2) - Arrays ignore the contents when empty",
|
|
||||||
"{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"array without data",
|
|
||||||
"{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"array with @index - The @index variable is used",
|
|
||||||
"{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"empty block (1) - Arrays iterate over the contents when not empty",
|
|
||||||
"{{#goodbyes}}{{/goodbyes}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"empty block (1) - Arrays ignore the contents when empty",
|
|
||||||
"{{#goodbyes}}{{/goodbyes}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block with complex lookup - Templates can access variables in contexts up the stack with relative path syntax",
|
|
||||||
"{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "name": "Alan"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"multiple blocks with complex lookup",
|
|
||||||
"{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "name": "Alan"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"AlanAlanAlanAlanAlanAlan",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}" should throw error
|
|
||||||
|
|
||||||
{
|
|
||||||
"block with deep nested complex lookup",
|
|
||||||
"{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}",
|
|
||||||
map[string]interface{}{"omg": "OMG!", "outer": []map[string]interface{}{{"sibling": "sad", "inner": []map[string]string{{"text": "goodbye"}}}}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye cruel sad OMG!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inverted sections with unset value - Inverted section rendered when value isn't set.",
|
|
||||||
"{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Right On!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inverted sections with false value - Inverted section rendered when value is false.",
|
|
||||||
"{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"goodbyes": false},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Right On!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inverted section with empty set - Inverted section rendered when value is empty set.",
|
|
||||||
"{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"goodbyes": []interface{}{}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Right On!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block inverted sections",
|
|
||||||
"{{#people}}{{name}}{{^}}{{none}}{{/people}}",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"chained inverted sections (1)",
|
|
||||||
"{{#people}}{{name}}{{else if none}}{{none}}{{/people}}",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"chained inverted sections (2)",
|
|
||||||
"{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"chained inverted sections (3)",
|
|
||||||
"{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "{{#people}}{{name}}{{else if none}}{{none}}{{/if}}" should throw error
|
|
||||||
|
|
||||||
{
|
|
||||||
"block inverted sections with empty arrays",
|
|
||||||
"{{#people}}{{name}}{{^}}{{none}}{{/people}}",
|
|
||||||
map[string]interface{}{"none": "No people", "people": map[string]interface{}{}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block standalone else sections (1)",
|
|
||||||
"{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block standalone else sections (2)",
|
|
||||||
"{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block standalone else sections (3)",
|
|
||||||
"{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block standalone chained else sections (1)",
|
|
||||||
"{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block standalone chained else sections (2)",
|
|
||||||
"{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n",
|
|
||||||
map[string]interface{}{"none": "No people"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"No people\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should handle nesting",
|
|
||||||
"{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.",
|
|
||||||
map[string]interface{}{"data": []int{1, 3, 5}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"1\n3\n5\nOK.",
|
|
||||||
},
|
|
||||||
// // @todo compat mode
|
|
||||||
// {
|
|
||||||
// "block with deep recursive lookup lookup",
|
|
||||||
// "{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}",
|
|
||||||
// map[string]interface{}{"omg": "OMG!", "outer": []map[string]interface{}{{"inner": []map[string]string{{"text": "goodbye"}}}}},
|
|
||||||
// nil,
|
|
||||||
// nil,
|
|
||||||
// nil,
|
|
||||||
// "Goodbye cruel OMG!",
|
|
||||||
// },
|
|
||||||
// // @todo compat mode
|
|
||||||
// {
|
|
||||||
// "block with deep recursive pathed lookup",
|
|
||||||
// "{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}",
|
|
||||||
// map[string]interface{}{"omg": map[string]string{"yes": "OMG!"}, "outer": []map[string]interface{}{{"inner": []map[string]string{{"yes": "no", "text": "goodbye"}}}}},
|
|
||||||
// nil,
|
|
||||||
// nil,
|
|
||||||
// nil,
|
|
||||||
// "Goodbye cruel OMG!",
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
"block with missed recursive lookup",
|
|
||||||
"{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}",
|
|
||||||
map[string]interface{}{"omg": map[string]string{"no": "OMG!"}, "outer": []map[string]interface{}{{"inner": []map[string]string{{"yes": "no", "text": "goodbye"}}}}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Goodbye cruel ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlocks(t *testing.T) {
|
|
||||||
launchTests(t, blocksTests)
|
|
||||||
}
|
|
341
vendor/github.com/aymerick/raymond/handlebars/builtins_test.go
generated
vendored
341
vendor/github.com/aymerick/raymond/handlebars/builtins_test.go
generated
vendored
|
@ -1,341 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/builtin.js
|
|
||||||
//
|
|
||||||
var builtinsTests = []Test{
|
|
||||||
{
|
|
||||||
"#if - if with boolean argument shows the contents when true",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": true, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with string argument shows the contents",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": "dummy", "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with boolean argument does not show the contents when false",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": false, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with undefined does not show the contents",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with non-empty array shows the contents",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": []string{"foo"}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with empty array does not show the contents",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": []string{}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with zero does not show the contents",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": 0, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with zero and includeZero option shows the contents",
|
|
||||||
"{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbye": 0, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with function shows the contents when function returns true",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func() bool { return true },
|
|
||||||
"world": "world",
|
|
||||||
},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with function shows the contents when function returns string",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func() string { return "world" },
|
|
||||||
"world": "world",
|
|
||||||
},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with function does not show the contents when returns false",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func() bool { return false },
|
|
||||||
"world": "world",
|
|
||||||
},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if - if with function does not show the contents when returns undefined",
|
|
||||||
"{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func() interface{} { return nil },
|
|
||||||
"world": "world",
|
|
||||||
},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#with",
|
|
||||||
"{{#with person}}{{first}} {{last}}{{/with}}",
|
|
||||||
map[string]interface{}{"person": map[string]string{"first": "Alan", "last": "Johnson"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Alan Johnson",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#with - with with function argument",
|
|
||||||
"{{#with person}}{{first}} {{last}}{{/with}}",
|
|
||||||
map[string]interface{}{
|
|
||||||
"person": func() map[string]string { return map[string]string{"first": "Alan", "last": "Johnson"} },
|
|
||||||
}, nil, nil, nil,
|
|
||||||
"Alan Johnson",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#with - with with else",
|
|
||||||
"{{#with person}}Person is present{{else}}Person is not present{{/with}}",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Person is not present",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"#each - each with array argument iterates over the contents when not empty",
|
|
||||||
"{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"goodbye! Goodbye! GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with array argument ignores the contents when empty",
|
|
||||||
"{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each without data (1)",
|
|
||||||
"{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"goodbye! Goodbye! GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each without data (2)",
|
|
||||||
"{{#each .}}{{.}}{{/each}}",
|
|
||||||
map[string]interface{}{"goodbyes": "cruel", "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
// note: a go hash is not ordered, so result may vary, this behaviour differs from the JS implementation
|
|
||||||
[]string{"cruelworld", "worldcruel"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each without context",
|
|
||||||
"{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"cruel !",
|
|
||||||
},
|
|
||||||
|
|
||||||
// NOTE: we test with a map instead of an object
|
|
||||||
{
|
|
||||||
"#each - each with an object and @key (map)",
|
|
||||||
"{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": map[interface{}]map[string]string{"<b>#1</b>": {"text": "goodbye"}, 2: {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
[]string{"<b>#1</b>. goodbye! 2. GOODBYE! cruel world!", "2. GOODBYE! <b>#1</b>. goodbye! cruel world!"},
|
|
||||||
},
|
|
||||||
// NOTE: An additional test with a struct, but without an html stuff for the key, because it is impossible
|
|
||||||
{
|
|
||||||
"#each - each with an object and @key (struct)",
|
|
||||||
"{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbyes": struct {
|
|
||||||
Foo map[string]string
|
|
||||||
Bar map[string]int
|
|
||||||
}{map[string]string{"text": "baz"}, map[string]int{"text": 10}},
|
|
||||||
"world": "world",
|
|
||||||
},
|
|
||||||
nil, nil, nil,
|
|
||||||
[]string{"Foo. baz! Bar. 10! cruel world!", "Bar. 10! Foo. baz! cruel world!"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with @index",
|
|
||||||
"{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with nested @index",
|
|
||||||
"{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with block params",
|
|
||||||
"{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!",
|
|
||||||
},
|
|
||||||
// @note: That test differs from JS impl because maps and structs are not ordered in go
|
|
||||||
{
|
|
||||||
"#each - each object with @index",
|
|
||||||
"{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": map[string]map[string]string{"a": {"text": "goodbye"}, "b": {"text": "Goodbye"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
[]string{"0. goodbye! 1. Goodbye! cruel world!", "0. Goodbye! 1. goodbye! cruel world!"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with nested @first",
|
|
||||||
"{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!",
|
|
||||||
},
|
|
||||||
// @note: That test differs from JS impl because maps and structs are not ordered in go
|
|
||||||
{
|
|
||||||
"#each - each object with @first",
|
|
||||||
"{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": map[string]map[string]string{"foo": {"text": "goodbye"}, "bar": {"text": "Goodbye"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
[]string{"goodbye! cruel world!", "Goodbye! cruel world!"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with @last",
|
|
||||||
"{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
// @note: That test differs from JS impl because maps and structs are not ordered in go
|
|
||||||
{
|
|
||||||
"#each - each object with @last",
|
|
||||||
"{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": map[string]map[string]string{"foo": {"text": "goodbye"}, "bar": {"text": "Goodbye"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
[]string{"goodbye! cruel world!", "Goodbye! cruel world!"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with nested @last",
|
|
||||||
"{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"#each - each with function argument (1)",
|
|
||||||
"{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": func() []map[string]string {
|
|
||||||
return []map[string]string{{"text": "goodbye"}, {"text": "Goodbye"}, {"text": "GOODBYE"}}
|
|
||||||
}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"goodbye! Goodbye! GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - each with function argument (2)",
|
|
||||||
"{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"goodbyes": []map[string]string{}, "world": "world"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#each - data passed to helpers",
|
|
||||||
"{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}",
|
|
||||||
map[string][]string{"letters": {"a", "b", "c"}},
|
|
||||||
map[string]interface{}{"exclaim": "!"},
|
|
||||||
map[string]interface{}{"detectDataInsideEach": detectDataHelper},
|
|
||||||
nil,
|
|
||||||
"a!b!c!",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "each on implicit context" should throw error
|
|
||||||
|
|
||||||
// SKIP: #log - "should call logger at default level"
|
|
||||||
// SKIP: #log - "should call logger at data level"
|
|
||||||
// SKIP: #log - "should output to info"
|
|
||||||
// SKIP: #log - "should log at data level"
|
|
||||||
// SKIP: #log - "should handle missing logger"
|
|
||||||
|
|
||||||
// @note Test added
|
|
||||||
// @todo Check log output
|
|
||||||
{
|
|
||||||
"#log",
|
|
||||||
"{{log blah}}",
|
|
||||||
map[string]string{"blah": "whee"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @note Test added
|
|
||||||
{
|
|
||||||
"#lookup - should lookup array element",
|
|
||||||
"{{#each goodbyes}}{{lookup ../data @index}}{{/each}}",
|
|
||||||
map[string]interface{}{"goodbyes": []int{0, 1}, "data": []string{"foo", "bar"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"foobar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#lookup - should lookup map element",
|
|
||||||
"{{#each goodbyes}}{{lookup ../data .}}{{/each}}",
|
|
||||||
map[string]interface{}{"goodbyes": []string{"foo", "bar"}, "data": map[string]string{"foo": "baz", "bar": "bat"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bazbat",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#lookup - should lookup struct field",
|
|
||||||
"{{#each goodbyes}}{{lookup ../data .}}{{/each}}",
|
|
||||||
map[string]interface{}{"goodbyes": []string{"Foo", "Bar"}, "data": struct {
|
|
||||||
Foo string
|
|
||||||
Bar string
|
|
||||||
}{"baz", "bat"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bazbat",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#lookup - should lookup arbitrary content",
|
|
||||||
"{{#each goodbyes}}{{lookup ../data .}}{{/each}}",
|
|
||||||
map[string]interface{}{"goodbyes": []int{0, 1}, "data": []string{"foo", "bar"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"foobar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#lookup - should not fail on undefined value",
|
|
||||||
"{{#each goodbyes}}{{lookup ../bar .}}{{/each}}",
|
|
||||||
map[string]interface{}{"goodbyes": []int{0, 1}, "data": []string{"foo", "bar"}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuiltins(t *testing.T) {
|
|
||||||
launchTests(t, builtinsTests)
|
|
||||||
}
|
|
300
vendor/github.com/aymerick/raymond/handlebars/data_test.go
generated
vendored
300
vendor/github.com/aymerick/raymond/handlebars/data_test.go
generated
vendored
|
@ -1,300 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/data.js
|
|
||||||
//
|
|
||||||
var dataTests = []Test{
|
|
||||||
{
|
|
||||||
"passing in data to a compiled function that expects data - works with helpers",
|
|
||||||
"{{hello}}",
|
|
||||||
map[string]string{"noun": "cat"},
|
|
||||||
map[string]interface{}{"adjective": "happy"},
|
|
||||||
map[string]interface{}{"hello": func(options *raymond.Options) string {
|
|
||||||
return options.DataStr("adjective") + " " + options.ValueStr("noun")
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"happy cat",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"data can be looked up via @foo",
|
|
||||||
"{{@hello}}",
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"hello": "hello"},
|
|
||||||
nil, nil,
|
|
||||||
"hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"deep @foo triggers automatic top-level data",
|
|
||||||
`{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}`,
|
|
||||||
map[string]bool{"foo": true},
|
|
||||||
map[string]interface{}{"hello": "hello"},
|
|
||||||
map[string]interface{}{"let": func(options *raymond.Options) string {
|
|
||||||
frame := options.NewDataFrame()
|
|
||||||
|
|
||||||
for k, v := range options.Hash() {
|
|
||||||
frame.Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.FnData(frame)
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Hello world",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameter data can be looked up via @foo",
|
|
||||||
`{{hello @world}}`,
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"world": "world"},
|
|
||||||
map[string]interface{}{"hello": func(context string) string {
|
|
||||||
return "Hello " + context
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Hello world",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hash values can be looked up via @foo",
|
|
||||||
`{{hello noun=@world}}`,
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"world": "world"},
|
|
||||||
map[string]interface{}{"hello": func(options *raymond.Options) string {
|
|
||||||
return "Hello " + options.HashStr("noun")
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Hello world",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested parameter data can be looked up via @foo.bar",
|
|
||||||
`{{hello @world.bar}}`,
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"world": map[string]string{"bar": "world"}},
|
|
||||||
map[string]interface{}{"hello": func(context string) string {
|
|
||||||
return "Hello " + context
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Hello world",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested parameter data does not fail with @world.bar",
|
|
||||||
`{{hello @world.bar}}`,
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"foo": map[string]string{"bar": "world"}},
|
|
||||||
map[string]interface{}{"hello": func(context string) string {
|
|
||||||
return "Hello " + context
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
// @todo Test differs with JS implementation: we don't output `undefined`
|
|
||||||
"Hello ",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "parameter data throws when using complex scope references",
|
|
||||||
|
|
||||||
{
|
|
||||||
"data can be functions",
|
|
||||||
`{{@hello}}`,
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"hello": func() string { return "hello" }},
|
|
||||||
nil, nil,
|
|
||||||
"hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"data can be functions with params",
|
|
||||||
`{{@hello "hello"}}`,
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"hello": func(context string) string { return context }},
|
|
||||||
nil, nil,
|
|
||||||
"hello",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"data is inherited downstream",
|
|
||||||
`{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}`,
|
|
||||||
map[string]map[string]string{"bar": {"baz": "hello world"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"let": func(options *raymond.Options) string {
|
|
||||||
frame := options.NewDataFrame()
|
|
||||||
|
|
||||||
for k, v := range options.Hash() {
|
|
||||||
frame.Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.FnData(frame)
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"2hello world1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"passing in data to a compiled function that expects data - works with helpers in partials",
|
|
||||||
`{{>myPartial}}`,
|
|
||||||
map[string]string{"noun": "cat"},
|
|
||||||
map[string]interface{}{"adjective": "happy"},
|
|
||||||
map[string]interface{}{"hello": func(options *raymond.Options) string {
|
|
||||||
return options.DataStr("adjective") + " " + options.ValueStr("noun")
|
|
||||||
}},
|
|
||||||
map[string]string{
|
|
||||||
"myPartial": "{{hello}}",
|
|
||||||
},
|
|
||||||
"happy cat",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"passing in data to a compiled function that expects data - works with helpers and parameters",
|
|
||||||
`{{hello world}}`,
|
|
||||||
map[string]interface{}{"exclaim": true, "world": "world"},
|
|
||||||
map[string]interface{}{"adjective": "happy"},
|
|
||||||
map[string]interface{}{"hello": func(context string, options *raymond.Options) string {
|
|
||||||
str := "error"
|
|
||||||
if b, ok := options.Value("exclaim").(bool); ok {
|
|
||||||
if b {
|
|
||||||
str = "!"
|
|
||||||
} else {
|
|
||||||
str = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.DataStr("adjective") + " " + context + str
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"happy world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"passing in data to a compiled function that expects data - works with block helpers",
|
|
||||||
`{{#hello}}{{world}}{{/hello}}`,
|
|
||||||
map[string]bool{"exclaim": true},
|
|
||||||
map[string]interface{}{"adjective": "happy"},
|
|
||||||
map[string]interface{}{
|
|
||||||
"hello": func(options *raymond.Options) string {
|
|
||||||
return options.Fn()
|
|
||||||
},
|
|
||||||
"world": func(options *raymond.Options) string {
|
|
||||||
str := "error"
|
|
||||||
if b, ok := options.Value("exclaim").(bool); ok {
|
|
||||||
if b {
|
|
||||||
str = "!"
|
|
||||||
} else {
|
|
||||||
str = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.DataStr("adjective") + " world" + str
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"happy world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"passing in data to a compiled function that expects data - works with block helpers that use ..",
|
|
||||||
`{{#hello}}{{world ../zomg}}{{/hello}}`,
|
|
||||||
map[string]interface{}{"exclaim": true, "zomg": "world"},
|
|
||||||
map[string]interface{}{"adjective": "happy"},
|
|
||||||
map[string]interface{}{
|
|
||||||
"hello": func(options *raymond.Options) string {
|
|
||||||
return options.FnWith(map[string]string{"exclaim": "?"})
|
|
||||||
},
|
|
||||||
"world": func(context string, options *raymond.Options) string {
|
|
||||||
return options.DataStr("adjective") + " " + context + options.ValueStr("exclaim")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"happy world?",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..",
|
|
||||||
`{{#hello}}{{world ../zomg}}{{/hello}}`,
|
|
||||||
map[string]interface{}{"exclaim": true, "zomg": "world"},
|
|
||||||
map[string]interface{}{"adjective": "happy", "accessData": "#win"},
|
|
||||||
map[string]interface{}{
|
|
||||||
"hello": func(options *raymond.Options) string {
|
|
||||||
return options.DataStr("accessData") + " " + options.FnWith(map[string]string{"exclaim": "?"})
|
|
||||||
},
|
|
||||||
"world": func(context string, options *raymond.Options) string {
|
|
||||||
return options.DataStr("adjective") + " " + context + options.ValueStr("exclaim")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"#win happy world?",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"you can override inherited data when invoking a helper",
|
|
||||||
`{{#hello}}{{world zomg}}{{/hello}}`,
|
|
||||||
map[string]interface{}{"exclaim": true, "zomg": "planet"},
|
|
||||||
map[string]interface{}{"adjective": "happy"},
|
|
||||||
map[string]interface{}{
|
|
||||||
"hello": func(options *raymond.Options) string {
|
|
||||||
ctx := map[string]string{"exclaim": "?", "zomg": "world"}
|
|
||||||
data := options.NewDataFrame()
|
|
||||||
data.Set("adjective", "sad")
|
|
||||||
|
|
||||||
return options.FnCtxData(ctx, data)
|
|
||||||
},
|
|
||||||
"world": func(context string, options *raymond.Options) string {
|
|
||||||
return options.DataStr("adjective") + " " + context + options.ValueStr("exclaim")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"sad world?",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"you can override inherited data when invoking a helper with depth",
|
|
||||||
`{{#hello}}{{world ../zomg}}{{/hello}}`,
|
|
||||||
map[string]interface{}{"exclaim": true, "zomg": "world"},
|
|
||||||
map[string]interface{}{"adjective": "happy"},
|
|
||||||
map[string]interface{}{
|
|
||||||
"hello": func(options *raymond.Options) string {
|
|
||||||
ctx := map[string]string{"exclaim": "?"}
|
|
||||||
data := options.NewDataFrame()
|
|
||||||
data.Set("adjective", "sad")
|
|
||||||
|
|
||||||
return options.FnCtxData(ctx, data)
|
|
||||||
},
|
|
||||||
"world": func(context string, options *raymond.Options) string {
|
|
||||||
return options.DataStr("adjective") + " " + context + options.ValueStr("exclaim")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"sad world?",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@root - the root context can be looked up via @root",
|
|
||||||
`{{@root.foo}}`,
|
|
||||||
map[string]interface{}{"foo": "hello"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@root - passed root values take priority",
|
|
||||||
`{{@root.foo}}`,
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"root": map[string]string{"foo": "hello"}},
|
|
||||||
nil, nil,
|
|
||||||
"hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nesting - the root context can be looked up via @root",
|
|
||||||
`{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}`,
|
|
||||||
map[string]interface{}{"foo": "hello"},
|
|
||||||
map[string]interface{}{"depth": 0},
|
|
||||||
map[string]interface{}{
|
|
||||||
"helper": func(options *raymond.Options) string {
|
|
||||||
data := options.NewDataFrame()
|
|
||||||
|
|
||||||
if depth, ok := options.Data("depth").(int); ok {
|
|
||||||
data.Set("depth", depth+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.FnData(data)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"2 1 0",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestData(t *testing.T) {
|
|
||||||
launchTests(t, dataTests)
|
|
||||||
}
|
|
2
vendor/github.com/aymerick/raymond/handlebars/doc.go
generated
vendored
2
vendor/github.com/aymerick/raymond/handlebars/doc.go
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
// Package handlebars contains all the tests that come from handlebars.js project.
|
|
||||||
package handlebars
|
|
665
vendor/github.com/aymerick/raymond/handlebars/helpers_test.go
generated
vendored
665
vendor/github.com/aymerick/raymond/handlebars/helpers_test.go
generated
vendored
|
@ -1,665 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Helpers
|
|
||||||
//
|
|
||||||
|
|
||||||
func barSuffixHelper(context string) string {
|
|
||||||
return "bar " + context
|
|
||||||
}
|
|
||||||
|
|
||||||
func echoHelper(str string) string {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
func echoNbHelper(str string, nb int) string {
|
|
||||||
result := ""
|
|
||||||
for i := 0; i < nb; i++ {
|
|
||||||
result += str
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func linkHelper(prefix string, options *raymond.Options) string {
|
|
||||||
return fmt.Sprintf(`<a href="%s/%s">%s</a>`, prefix, options.ValueStr("url"), options.ValueStr("text"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func rawHelper(options *raymond.Options) string {
|
|
||||||
return options.Fn()
|
|
||||||
}
|
|
||||||
|
|
||||||
func rawThreeHelper(a, b, c string, options *raymond.Options) string {
|
|
||||||
return options.Fn() + a + b + c
|
|
||||||
}
|
|
||||||
|
|
||||||
func formHelper(options *raymond.Options) string {
|
|
||||||
return "<form>" + options.Fn() + "</form>"
|
|
||||||
}
|
|
||||||
|
|
||||||
func formCtxHelper(context interface{}, options *raymond.Options) string {
|
|
||||||
return "<form>" + options.FnWith(context) + "</form>"
|
|
||||||
}
|
|
||||||
|
|
||||||
func listHelper(context interface{}, options *raymond.Options) string {
|
|
||||||
val := reflect.ValueOf(context)
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if val.Len() > 0 {
|
|
||||||
result := "<ul>"
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
result += "<li>"
|
|
||||||
result += options.FnWith(val.Index(i).Interface())
|
|
||||||
result += "</li>"
|
|
||||||
}
|
|
||||||
result += "</ul>"
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "<p>" + options.Inverse() + "</p>"
|
|
||||||
}
|
|
||||||
|
|
||||||
func blogHelper(val string) string {
|
|
||||||
return "val is " + val
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalHelper(a, b string) string {
|
|
||||||
return raymond.Str(a == b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dashHelper(a, b string) string {
|
|
||||||
return a + "-" + b
|
|
||||||
}
|
|
||||||
|
|
||||||
func concatHelper(a, b string) string {
|
|
||||||
return a + b
|
|
||||||
}
|
|
||||||
|
|
||||||
func detectDataHelper(options *raymond.Options) string {
|
|
||||||
if val, ok := options.DataFrame().Get("exclaim").(string); ok {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/helper.js
|
|
||||||
//
|
|
||||||
var helpersTests = []Test{
|
|
||||||
{
|
|
||||||
"helper with complex lookup",
|
|
||||||
"{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"prefix": "/root", "goodbyes": []map[string]string{{"text": "Goodbye", "url": "goodbye"}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"link": linkHelper},
|
|
||||||
nil,
|
|
||||||
`<a href="/root/goodbye">Goodbye</a>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper for raw block gets raw content",
|
|
||||||
"{{{{raw}}}} {{test}} {{{{/raw}}}}",
|
|
||||||
map[string]interface{}{"test": "hello"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"raw": rawHelper},
|
|
||||||
nil,
|
|
||||||
" {{test}} ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper for raw block gets parameters",
|
|
||||||
"{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}",
|
|
||||||
map[string]interface{}{"test": "hello"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"raw": rawThreeHelper},
|
|
||||||
nil,
|
|
||||||
" {{test}} 123",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper block with complex lookup expression",
|
|
||||||
"{{#goodbyes}}{{../name}}{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"name": "Alan"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"goodbyes": func(options *raymond.Options) string {
|
|
||||||
out := ""
|
|
||||||
for _, str := range []string{"Goodbye", "goodbye", "GOODBYE"} {
|
|
||||||
out += str + " " + options.FnWith(str) + "! "
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Goodbye Alan! goodbye Alan! GOODBYE Alan! ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper with complex lookup and nested template",
|
|
||||||
"{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}",
|
|
||||||
map[string]interface{}{"prefix": "/root", "goodbyes": []map[string]string{{"text": "Goodbye", "url": "goodbye"}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"link": linkHelper},
|
|
||||||
nil,
|
|
||||||
`<a href="/root/goodbye">Goodbye</a>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// note: The JS implementation returns undefined, we return empty string
|
|
||||||
"helper returning undefined value (1)",
|
|
||||||
" {{nothere}}",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"nothere": func() string {
|
|
||||||
return ""
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
" ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// note: The JS implementation returns undefined, we return empty string
|
|
||||||
"helper returning undefined value (2)",
|
|
||||||
" {{#nothere}}{{/nothere}}",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"nothere": func() string {
|
|
||||||
return ""
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
" ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper",
|
|
||||||
"{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!",
|
|
||||||
map[string]interface{}{"world": "world"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"goodbyes": func(options *raymond.Options) string {
|
|
||||||
return options.FnWith(map[string]string{"text": "GOODBYE"})
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"GOODBYE! cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper staying in the same context",
|
|
||||||
"{{#form}}<p>{{name}}</p>{{/form}}",
|
|
||||||
map[string]interface{}{"name": "Yehuda"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"form": formHelper},
|
|
||||||
nil,
|
|
||||||
"<form><p>Yehuda</p></form>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper should have context in this",
|
|
||||||
"<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>",
|
|
||||||
map[string]interface{}{"people": []map[string]interface{}{{"name": "Alan", "id": 1}, {"name": "Yehuda", "id": 2}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"link": func(options *raymond.Options) string {
|
|
||||||
return fmt.Sprintf("<a href=\"/people/%s\">%s</a>", options.ValueStr("id"), options.Fn())
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
`<ul><li><a href="/people/1">Alan</a></li><li><a href="/people/2">Yehuda</a></li></ul>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper for undefined value",
|
|
||||||
"{{#empty}}shouldn't render{{/empty}}",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper passing a new context",
|
|
||||||
"{{#form yehuda}}<p>{{name}}</p>{{/form}}",
|
|
||||||
map[string]map[string]string{"yehuda": {"name": "Yehuda"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"form": formCtxHelper},
|
|
||||||
nil,
|
|
||||||
"<form><p>Yehuda</p></form>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper passing a complex path context",
|
|
||||||
"{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}",
|
|
||||||
map[string]map[string]interface{}{"yehuda": {"name": "Yehuda", "cat": map[string]string{"name": "Harold"}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"form": formCtxHelper},
|
|
||||||
nil,
|
|
||||||
"<form><p>Harold</p></form>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested block helpers",
|
|
||||||
"{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}",
|
|
||||||
map[string]map[string]string{"yehuda": {"name": "Yehuda"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"link": func(options *raymond.Options) string {
|
|
||||||
return fmt.Sprintf("<a href=\"%s\">%s</a>", options.ValueStr("name"), options.Fn())
|
|
||||||
}, "form": formCtxHelper},
|
|
||||||
nil,
|
|
||||||
`<form><p>Yehuda</p><a href="Yehuda">Hello</a></form>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper inverted sections (1) - an inverse wrapper is passed in as a new context",
|
|
||||||
"{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}",
|
|
||||||
map[string][]map[string]string{"people": {{"name": "Alan"}, {"name": "Yehuda"}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"list": listHelper},
|
|
||||||
nil,
|
|
||||||
`<ul><li>Alan</li><li>Yehuda</li></ul>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper inverted sections (2) - an inverse wrapper can be optionally called",
|
|
||||||
"{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}",
|
|
||||||
map[string][]map[string]string{"people": {}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"list": listHelper},
|
|
||||||
nil,
|
|
||||||
`<p><em>Nobody's here</em></p>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helper inverted sections (3) - the context of an inverse is the parent of the block",
|
|
||||||
"{{#list people}}Hello{{^}}{{message}}{{/list}}",
|
|
||||||
map[string]interface{}{"people": []interface{}{}, "message": "Nobody's here"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"list": listHelper},
|
|
||||||
nil,
|
|
||||||
`<p>Nobody's here</p>`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"pathed lambdas with parameters (1)",
|
|
||||||
"{{./helper 1}}",
|
|
||||||
map[string]interface{}{
|
|
||||||
"helper": func(param int) string { return "winning" },
|
|
||||||
"hash": map[string]interface{}{
|
|
||||||
"helper": func(param int) string { return "winning" },
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"./helper": func(param int) string { return "fail" }},
|
|
||||||
nil,
|
|
||||||
"winning",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pathed lambdas with parameters (2)",
|
|
||||||
"{{hash/helper 1}}",
|
|
||||||
map[string]interface{}{
|
|
||||||
"helper": func(param int) string { return "winning" },
|
|
||||||
"hash": map[string]interface{}{
|
|
||||||
"helper": func(param int) string { return "winning" },
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"./helper": func(param int) string { return "fail" }},
|
|
||||||
nil,
|
|
||||||
"winning",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"helpers hash - providing a helpers hash (1)",
|
|
||||||
"Goodbye {{cruel}} {{world}}!",
|
|
||||||
map[string]interface{}{"cruel": "cruel"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"world": func() string { return "world" }},
|
|
||||||
nil,
|
|
||||||
"Goodbye cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helpers hash - providing a helpers hash (2)",
|
|
||||||
"Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!",
|
|
||||||
map[string]interface{}{"iter": []map[string]string{{"cruel": "cruel"}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"world": func() string { return "world" }},
|
|
||||||
nil,
|
|
||||||
"Goodbye cruel world!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helpers hash - in cases of conflict, helpers win (1)",
|
|
||||||
"{{{lookup}}}",
|
|
||||||
map[string]interface{}{"lookup": "Explicit"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"lookup": func() string { return "helpers" }},
|
|
||||||
nil,
|
|
||||||
"helpers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helpers hash - in cases of conflict, helpers win (2)",
|
|
||||||
"{{lookup}}",
|
|
||||||
map[string]interface{}{"lookup": "Explicit"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"lookup": func() string { return "helpers" }},
|
|
||||||
nil,
|
|
||||||
"helpers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helpers hash - the helpers hash is available is nested contexts",
|
|
||||||
"{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}",
|
|
||||||
map[string]interface{}{"outer": map[string]interface{}{"inner": map[string]interface{}{"unused": []string{}}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"helper": func() string { return "helper" }},
|
|
||||||
nil,
|
|
||||||
"helper",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "helpers hash - the helper hash should augment the global hash"
|
|
||||||
|
|
||||||
// @todo "registration"
|
|
||||||
|
|
||||||
{
|
|
||||||
"decimal number literals work",
|
|
||||||
"Message: {{hello -1.2 1.2}}",
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"hello": func(times, times2 interface{}) string {
|
|
||||||
ts, t2s := "NaN", "NaN"
|
|
||||||
|
|
||||||
if v, ok := times.(float64); ok {
|
|
||||||
ts = raymond.Str(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := times2.(float64); ok {
|
|
||||||
t2s = raymond.Str(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Hello " + ts + " " + t2s + " times"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Message: Hello -1.2 1.2 times",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"negative number literals work",
|
|
||||||
"Message: {{hello -12}}",
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"hello": func(times interface{}) string {
|
|
||||||
ts := "NaN"
|
|
||||||
|
|
||||||
if v, ok := times.(int); ok {
|
|
||||||
ts = raymond.Str(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Hello " + ts + " times"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Message: Hello -12 times",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"String literal parameters - simple literals work",
|
|
||||||
`Message: {{hello "world" 12 true false}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"hello": func(p, t, b, b2 interface{}) string {
|
|
||||||
times, bool1, bool2 := "NaN", "NaB", "NaB"
|
|
||||||
|
|
||||||
param, ok := p.(string)
|
|
||||||
if !ok {
|
|
||||||
param = "NaN"
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := t.(int); ok {
|
|
||||||
times = raymond.Str(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := b.(bool); ok {
|
|
||||||
bool1 = raymond.Str(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := b2.(bool); ok {
|
|
||||||
bool2 = raymond.Str(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Hello " + param + " " + times + " times: " + bool1 + " " + bool2
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Message: Hello world 12 times: true false",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "using a quote in the middle of a parameter raises an error"
|
|
||||||
|
|
||||||
{
|
|
||||||
"String literal parameters - escaping a String is possible",
|
|
||||||
"Message: {{{hello \"\\\"world\\\"\"}}}",
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"hello": func(param string) string {
|
|
||||||
return "Hello " + param
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
`Message: Hello "world"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"String literal parameters - it works with ' marks",
|
|
||||||
"Message: {{{hello \"Alan's world\"}}}",
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"hello": func(param string) string {
|
|
||||||
return "Hello " + param
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
`Message: Hello Alan's world`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"multiple parameters - simple multi-params work",
|
|
||||||
"Message: {{goodbye cruel world}}",
|
|
||||||
map[string]string{"cruel": "cruel", "world": "world"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"goodbye": func(cruel, world string) string {
|
|
||||||
return "Goodbye " + cruel + " " + world
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Message: Goodbye cruel world",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"multiple parameters - block multi-params work",
|
|
||||||
"Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}",
|
|
||||||
map[string]string{"cruel": "cruel", "world": "world"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"goodbye": func(cruel, world string, options *raymond.Options) string {
|
|
||||||
return options.FnWith(map[string]interface{}{"greeting": "Goodbye", "adj": cruel, "noun": world})
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"Message: Goodbye cruel world",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash - helpers can take an optional hash",
|
|
||||||
`{{goodbye cruel="CRUEL" world="WORLD" times=12}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.HashStr("world") + " " + options.HashStr("times") + " TIMES"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"GOODBYE CRUEL WORLD 12 TIMES",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hash - helpers can take an optional hash with booleans (1)",
|
|
||||||
`{{goodbye cruel="CRUEL" world="WORLD" print=true}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
p, ok := options.HashProp("print").(bool)
|
|
||||||
if ok {
|
|
||||||
if p {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.HashStr("world")
|
|
||||||
}
|
|
||||||
return "NOT PRINTING"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "THIS SHOULD NOT HAPPEN"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"GOODBYE CRUEL WORLD",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hash - helpers can take an optional hash with booleans (2)",
|
|
||||||
`{{goodbye cruel="CRUEL" world="WORLD" print=false}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
p, ok := options.HashProp("print").(bool)
|
|
||||||
if ok {
|
|
||||||
if p {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.HashStr("world")
|
|
||||||
}
|
|
||||||
return "NOT PRINTING"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "THIS SHOULD NOT HAPPEN"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"NOT PRINTING",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helpers can take an optional hash",
|
|
||||||
`{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.Fn() + " " + options.HashStr("times") + " TIMES"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"GOODBYE CRUEL world 12 TIMES",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helpers can take an optional hash with single quoted stings",
|
|
||||||
`{{#goodbye cruel='CRUEL' times=12}}world{{/goodbye}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.Fn() + " " + options.HashStr("times") + " TIMES"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"GOODBYE CRUEL world 12 TIMES",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helpers can take an optional hash with booleans (1)",
|
|
||||||
`{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
p, ok := options.HashProp("print").(bool)
|
|
||||||
if ok {
|
|
||||||
if p {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.Fn()
|
|
||||||
}
|
|
||||||
return "NOT PRINTING"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "THIS SHOULD NOT HAPPEN"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"GOODBYE CRUEL world",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"block helpers can take an optional hash with booleans (1)",
|
|
||||||
`{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
p, ok := options.HashProp("print").(bool)
|
|
||||||
if ok {
|
|
||||||
if p {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.Fn()
|
|
||||||
}
|
|
||||||
return "NOT PRINTING"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "THIS SHOULD NOT HAPPEN"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"NOT PRINTING",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "helperMissing - if a context is not found, helperMissing is used" throw error
|
|
||||||
|
|
||||||
// @todo "helperMissing - if a context is not found, custom helperMissing is used"
|
|
||||||
|
|
||||||
// @todo "helperMissing - if a value is not found, custom helperMissing is used"
|
|
||||||
|
|
||||||
{
|
|
||||||
"block helpers can take an optional hash with booleans (1)",
|
|
||||||
`{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"goodbye": func(options *raymond.Options) string {
|
|
||||||
p, ok := options.HashProp("print").(bool)
|
|
||||||
if ok {
|
|
||||||
if p {
|
|
||||||
return "GOODBYE " + options.HashStr("cruel") + " " + options.Fn()
|
|
||||||
}
|
|
||||||
return "NOT PRINTING"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "THIS SHOULD NOT HAPPEN"
|
|
||||||
}},
|
|
||||||
nil,
|
|
||||||
"NOT PRINTING",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "knownHelpers/knownHelpersOnly" tests
|
|
||||||
|
|
||||||
// @todo "blockHelperMissing" tests
|
|
||||||
|
|
||||||
// @todo "name field" tests
|
|
||||||
|
|
||||||
{
|
|
||||||
"name conflicts - helpers take precedence over same-named context properties",
|
|
||||||
`{{goodbye}} {{cruel world}}`,
|
|
||||||
map[string]string{"goodbye": "goodbye", "world": "world"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func(options *raymond.Options) string {
|
|
||||||
return strings.ToUpper(options.ValueStr("goodbye"))
|
|
||||||
},
|
|
||||||
"cruel": func(world string) string {
|
|
||||||
return "cruel " + strings.ToUpper(world)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"GOODBYE cruel WORLD",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name conflicts - helpers take precedence over same-named context properties",
|
|
||||||
`{{#goodbye}} {{cruel world}}{{/goodbye}}`,
|
|
||||||
map[string]string{"goodbye": "goodbye", "world": "world"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func(options *raymond.Options) string {
|
|
||||||
return strings.ToUpper(options.ValueStr("goodbye")) + options.Fn()
|
|
||||||
},
|
|
||||||
"cruel": func(world string) string {
|
|
||||||
return "cruel " + strings.ToUpper(world)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"GOODBYE cruel WORLD",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name conflicts - Scoped names take precedence over helpers",
|
|
||||||
`{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}`,
|
|
||||||
map[string]string{"goodbye": "goodbye", "world": "world"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func(options *raymond.Options) string {
|
|
||||||
return strings.ToUpper(options.ValueStr("goodbye"))
|
|
||||||
},
|
|
||||||
"cruel": func(world string) string {
|
|
||||||
return "cruel " + strings.ToUpper(world)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"goodbye cruel WORLD cruel GOODBYE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name conflicts - Scoped names take precedence over block helpers",
|
|
||||||
`{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}`,
|
|
||||||
map[string]string{"goodbye": "goodbye", "world": "world"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"goodbye": func(options *raymond.Options) string {
|
|
||||||
return strings.ToUpper(options.ValueStr("goodbye")) + options.Fn()
|
|
||||||
},
|
|
||||||
"cruel": func(world string) string {
|
|
||||||
return "cruel " + strings.ToUpper(world)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"GOODBYE cruel WORLD goodbye",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "block params" tests
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHelpers(t *testing.T) {
|
|
||||||
launchTests(t, helpersTests)
|
|
||||||
}
|
|
182
vendor/github.com/aymerick/raymond/handlebars/partials_test.go
generated
vendored
182
vendor/github.com/aymerick/raymond/handlebars/partials_test.go
generated
vendored
|
@ -1,182 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/partials.js
|
|
||||||
//
|
|
||||||
var partialsTests = []Test{
|
|
||||||
{
|
|
||||||
"basic partials",
|
|
||||||
"Dudes: {{#dudes}}{{> dude}}{{/dudes}}",
|
|
||||||
map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{name}} ({{url}}) "},
|
|
||||||
"Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dynamic partials",
|
|
||||||
"Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}",
|
|
||||||
map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"partial": func() string {
|
|
||||||
return "dude"
|
|
||||||
}},
|
|
||||||
map[string]string{"dude": "{{name}} ({{url}}) "},
|
|
||||||
"Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "failing dynamic partials"
|
|
||||||
|
|
||||||
{
|
|
||||||
"partials with context",
|
|
||||||
"Dudes: {{>dude dudes}}",
|
|
||||||
map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{#this}}{{name}} ({{url}}) {{/this}}"},
|
|
||||||
"Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partials with undefined context",
|
|
||||||
"Dudes: {{>dude dudes}}",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{foo}} Empty"},
|
|
||||||
"Dudes: Empty",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "partials with duplicate parameters"
|
|
||||||
|
|
||||||
{
|
|
||||||
"partials with parameters",
|
|
||||||
"Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}",
|
|
||||||
map[string]interface{}{"foo": "bar", "dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{others.foo}}{{name}} ({{url}}) "},
|
|
||||||
"Dudes: barYehuda (http://yehuda) barAlan (http://alan) ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partial in a partial",
|
|
||||||
"Dudes: {{#dudes}}{{>dude}}{{/dudes}}",
|
|
||||||
map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{name}} {{> url}} ", "url": `<a href="{{url}}">{{url}}</a>`},
|
|
||||||
`Dudes: Yehuda <a href="http://yehuda">http://yehuda</a> Alan <a href="http://alan">http://alan</a> `,
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "rendering undefined partial throws an exception"
|
|
||||||
|
|
||||||
// @todo "registering undefined partial throws an exception"
|
|
||||||
|
|
||||||
// SKIP: "rendering template partial in vm mode throws an exception"
|
|
||||||
// SKIP: "rendering function partial in vm mode"
|
|
||||||
|
|
||||||
{
|
|
||||||
"GH-14: a partial preceding a selector",
|
|
||||||
"Dudes: {{>dude}} {{anotherDude}}",
|
|
||||||
map[string]string{"name": "Jeepers", "anotherDude": "Creepers"},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{name}}"},
|
|
||||||
"Dudes: Jeepers Creepers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Partials with slash paths",
|
|
||||||
"Dudes: {{> shared/dude}}",
|
|
||||||
map[string]string{"name": "Jeepers", "anotherDude": "Creepers"},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"shared/dude": "{{name}}"},
|
|
||||||
"Dudes: Jeepers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Partials with slash and point paths",
|
|
||||||
"Dudes: {{> shared/dude.thing}}",
|
|
||||||
map[string]string{"name": "Jeepers", "anotherDude": "Creepers"},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"shared/dude.thing": "{{name}}"},
|
|
||||||
"Dudes: Jeepers",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "Global Partials"
|
|
||||||
|
|
||||||
// @todo "Multiple partial registration"
|
|
||||||
|
|
||||||
{
|
|
||||||
"Partials with integer path",
|
|
||||||
"Dudes: {{> 404}}",
|
|
||||||
map[string]string{"name": "Jeepers", "anotherDude": "Creepers"},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"404": "{{name}}"}, // @note Difference with JS test: partial name is a string
|
|
||||||
"Dudes: Jeepers",
|
|
||||||
},
|
|
||||||
// @note This is not supported by our implementation. But really... who cares ?
|
|
||||||
// {
|
|
||||||
// "Partials with complex path",
|
|
||||||
// "Dudes: {{> 404/asdf?.bar}}",
|
|
||||||
// map[string]string{"name": "Jeepers", "anotherDude": "Creepers"},
|
|
||||||
// nil, nil,
|
|
||||||
// map[string]string{"404/asdf?.bar": "{{name}}"},
|
|
||||||
// "Dudes: Jeepers",
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
"Partials with escaped",
|
|
||||||
"Dudes: {{> [+404/asdf?.bar]}}",
|
|
||||||
map[string]string{"name": "Jeepers", "anotherDude": "Creepers"},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"+404/asdf?.bar": "{{name}}"},
|
|
||||||
"Dudes: Jeepers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Partials with string",
|
|
||||||
"Dudes: {{> '+404/asdf?.bar'}}",
|
|
||||||
map[string]string{"name": "Jeepers", "anotherDude": "Creepers"},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"+404/asdf?.bar": "{{name}}"},
|
|
||||||
"Dudes: Jeepers",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should handle empty partial",
|
|
||||||
"Dudes: {{#dudes}}{{> dude}}{{/dudes}}",
|
|
||||||
map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": ""},
|
|
||||||
"Dudes: ",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "throw on missing partial"
|
|
||||||
|
|
||||||
// SKIP: "should pass compiler flags"
|
|
||||||
|
|
||||||
{
|
|
||||||
"standalone partials (1) - indented partials",
|
|
||||||
"Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}",
|
|
||||||
map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{name}}\n"},
|
|
||||||
"Dudes:\n Yehuda\n Alan\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"standalone partials (2) - nested indented partials",
|
|
||||||
"Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}",
|
|
||||||
map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
nil, nil,
|
|
||||||
map[string]string{"dude": "{{name}}\n {{> url}}", "url": "{{url}}!\n"},
|
|
||||||
"Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n",
|
|
||||||
},
|
|
||||||
|
|
||||||
// // @todo preventIndent option
|
|
||||||
// {
|
|
||||||
// "standalone partials (3) - prevent nested indented partials",
|
|
||||||
// "Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}",
|
|
||||||
// map[string]interface{}{"dudes": []map[string]string{{"name": "Yehuda", "url": "http://yehuda"}, {"name": "Alan", "url": "http://alan"}}},
|
|
||||||
// nil, nil,
|
|
||||||
// map[string]string{"dude": "{{name}}\n {{> url}}", "url": "{{url}}!\n"},
|
|
||||||
// "Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n",
|
|
||||||
// },
|
|
||||||
|
|
||||||
// @todo "compat mode"
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPartials(t *testing.T) {
|
|
||||||
launchTests(t, partialsTests)
|
|
||||||
}
|
|
209
vendor/github.com/aymerick/raymond/handlebars/subexpressions_test.go
generated
vendored
209
vendor/github.com/aymerick/raymond/handlebars/subexpressions_test.go
generated
vendored
|
@ -1,209 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/subexpression.js
|
|
||||||
//
|
|
||||||
var subexpressionsTests = []Test{
|
|
||||||
{
|
|
||||||
"arg-less helper",
|
|
||||||
"{{foo (bar)}}!",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": func(val string) string {
|
|
||||||
return val + val
|
|
||||||
},
|
|
||||||
"bar": func() string {
|
|
||||||
return "LOL"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"LOLLOL!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper w args",
|
|
||||||
"{{blog (equal a b)}}",
|
|
||||||
map[string]interface{}{"bar": "LOL"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"blog": blogHelper,
|
|
||||||
"equal": equalHelper,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"val is true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mixed paths and helpers",
|
|
||||||
"{{blog baz.bat (equal a b) baz.bar}}",
|
|
||||||
map[string]interface{}{"bar": "LOL", "baz": map[string]string{"bat": "foo!", "bar": "bar!"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"blog": func(p, p2, p3 string) string {
|
|
||||||
return "val is " + p + ", " + p2 + " and " + p3
|
|
||||||
},
|
|
||||||
"equal": equalHelper,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"val is foo!, true and bar!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supports much nesting",
|
|
||||||
"{{blog (equal (equal true true) true)}}",
|
|
||||||
map[string]interface{}{"bar": "LOL"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"blog": blogHelper,
|
|
||||||
"equal": equalHelper,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"val is true",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"GH-800 : Complex subexpressions (1)",
|
|
||||||
"{{dash 'abc' (concat a b)}}",
|
|
||||||
map[string]interface{}{"a": "a", "b": "b", "c": map[string]string{"c": "c"}, "d": "d", "e": map[string]string{"e": "e"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"dash": dashHelper, "concat": concatHelper},
|
|
||||||
nil,
|
|
||||||
"abc-ab",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"GH-800 : Complex subexpressions (2)",
|
|
||||||
"{{dash d (concat a b)}}",
|
|
||||||
map[string]interface{}{"a": "a", "b": "b", "c": map[string]string{"c": "c"}, "d": "d", "e": map[string]string{"e": "e"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"dash": dashHelper, "concat": concatHelper},
|
|
||||||
nil,
|
|
||||||
"d-ab",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"GH-800 : Complex subexpressions (3)",
|
|
||||||
"{{dash c.c (concat a b)}}",
|
|
||||||
map[string]interface{}{"a": "a", "b": "b", "c": map[string]string{"c": "c"}, "d": "d", "e": map[string]string{"e": "e"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"dash": dashHelper, "concat": concatHelper},
|
|
||||||
nil,
|
|
||||||
"c-ab",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"GH-800 : Complex subexpressions (4)",
|
|
||||||
"{{dash (concat a b) c.c}}",
|
|
||||||
map[string]interface{}{"a": "a", "b": "b", "c": map[string]string{"c": "c"}, "d": "d", "e": map[string]string{"e": "e"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"dash": dashHelper, "concat": concatHelper},
|
|
||||||
nil,
|
|
||||||
"ab-c",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"GH-800 : Complex subexpressions (5)",
|
|
||||||
"{{dash (concat a e.e) c.c}}",
|
|
||||||
map[string]interface{}{"a": "a", "b": "b", "c": map[string]string{"c": "c"}, "d": "d", "e": map[string]string{"e": "e"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"dash": dashHelper, "concat": concatHelper},
|
|
||||||
nil,
|
|
||||||
"ae-c",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
// note: test not relevant
|
|
||||||
"provides each nested helper invocation its own options hash",
|
|
||||||
"{{equal (equal true true) true}}",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"equal": equalHelper,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"with hashes",
|
|
||||||
"{{blog (equal (equal true true) true fun='yes')}}",
|
|
||||||
map[string]interface{}{"bar": "LOL"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"blog": blogHelper,
|
|
||||||
"equal": equalHelper,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"val is true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"as hashes",
|
|
||||||
"{{blog fun=(equal (blog fun=1) 'val is 1')}}",
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"blog": func(options *raymond.Options) string {
|
|
||||||
return "val is " + options.HashStr("fun")
|
|
||||||
},
|
|
||||||
"equal": equalHelper,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"val is true",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"multiple subexpressions in a hash",
|
|
||||||
`{{input aria-label=(t "Name") placeholder=(t "Example User")}}`,
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"input": func(options *raymond.Options) raymond.SafeString {
|
|
||||||
return raymond.SafeString(`<input aria-label="` + options.HashStr("aria-label") + `" placeholder="` + options.HashStr("placeholder") + `" />`)
|
|
||||||
},
|
|
||||||
"t": func(param string) raymond.SafeString {
|
|
||||||
return raymond.SafeString(param)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
`<input aria-label="Name" placeholder="Example User" />`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"multiple subexpressions in a hash with context",
|
|
||||||
`{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}`,
|
|
||||||
map[string]map[string]string{"item": {"field": "Name", "placeholder": "Example User"}},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"input": func(options *raymond.Options) raymond.SafeString {
|
|
||||||
return raymond.SafeString(`<input aria-label="` + options.HashStr("aria-label") + `" placeholder="` + options.HashStr("placeholder") + `" />`)
|
|
||||||
},
|
|
||||||
"t": func(param string) raymond.SafeString {
|
|
||||||
return raymond.SafeString(param)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
`<input aria-label="Name" placeholder="Example User" />`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "in string params mode"
|
|
||||||
|
|
||||||
// @todo "as hashes in string params mode"
|
|
||||||
|
|
||||||
{
|
|
||||||
"subexpression functions on the context",
|
|
||||||
"{{foo (bar)}}!",
|
|
||||||
map[string]interface{}{"bar": func() string { return "LOL" }},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": func(val string) string {
|
|
||||||
return val + val
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"LOLLOL!",
|
|
||||||
},
|
|
||||||
|
|
||||||
// @todo "subexpressions can't just be property lookups" should raise error
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubexpressions(t *testing.T) {
|
|
||||||
launchTests(t, subexpressionsTests)
|
|
||||||
}
|
|
259
vendor/github.com/aymerick/raymond/handlebars/whitespace_test.go
generated
vendored
259
vendor/github.com/aymerick/raymond/handlebars/whitespace_test.go
generated
vendored
|
@ -1,259 +0,0 @@
|
||||||
package handlebars
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
//
|
|
||||||
// Those tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/whitespace-control.js
|
|
||||||
//
|
|
||||||
var whitespaceControlTests = []Test{
|
|
||||||
{
|
|
||||||
"should strip whitespace around mustache calls (1)",
|
|
||||||
" {{~foo~}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar<",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around mustache calls (2)",
|
|
||||||
" {{~foo}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar< ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around mustache calls (3)",
|
|
||||||
" {{foo~}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
" bar<",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around mustache calls (4)",
|
|
||||||
" {{~&foo~}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar<",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around mustache calls (5)",
|
|
||||||
" {{~{foo}~}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar<",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around mustache calls (6)",
|
|
||||||
"1\n{{foo~}} \n\n 23\n{{bar}}4",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"1\n23\n4",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"blocks - should strip whitespace around simple block calls (1)",
|
|
||||||
" {{~#if foo~}} bar {{~/if~}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blocks - should strip whitespace around simple block calls (2)",
|
|
||||||
" {{#if foo~}} bar {{/if~}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
" bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blocks - should strip whitespace around simple block calls (3)",
|
|
||||||
" {{~#if foo}} bar {{~/if}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
" bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blocks - should strip whitespace around simple block calls (4)",
|
|
||||||
" {{#if foo}} bar {{/if}} ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
" bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blocks - should strip whitespace around simple block calls (5)",
|
|
||||||
" \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blocks - should strip whitespace around simple block calls (6)",
|
|
||||||
" a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
" abara ",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"should strip whitespace around inverse block calls (1)",
|
|
||||||
" {{~^if foo~}} bar {{~/if~}} ",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around inverse block calls (2)",
|
|
||||||
" {{^if foo~}} bar {{/if~}} ",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around inverse block calls (3)",
|
|
||||||
" {{~^if foo}} bar {{~/if}} ",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around inverse block calls (4)",
|
|
||||||
" {{^if foo}} bar {{/if}} ",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around inverse block calls (5)",
|
|
||||||
" \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (1)",
|
|
||||||
"{{#if foo~}} bar {{~^~}} baz {{~/if}}",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (2)",
|
|
||||||
"{{#if foo~}} bar {{^~}} baz {{/if}}",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (3)",
|
|
||||||
"{{#if foo}} bar {{~^~}} baz {{~/if}}",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
" bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (4)",
|
|
||||||
"{{#if foo}} bar {{^~}} baz {{/if}}",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
" bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (5)",
|
|
||||||
"{{#if foo~}} bar {{~else~}} baz {{~/if}}",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (6)",
|
|
||||||
"\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (7)",
|
|
||||||
"\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n",
|
|
||||||
map[string]string{"foo": "bar<"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"bar<",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (8)",
|
|
||||||
"{{#if foo~}} bar {{~^~}} baz {{~/if}}",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"baz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (9)",
|
|
||||||
"{{#if foo}} bar {{~^~}} baz {{/if}}",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"baz ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (10)",
|
|
||||||
"{{#if foo~}} bar {{~^}} baz {{~/if}}",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" baz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (11)",
|
|
||||||
"{{#if foo~}} bar {{~^}} baz {{/if}}",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
" baz ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (12)",
|
|
||||||
"{{#if foo~}} bar {{~else~}} baz {{~/if}}",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"baz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around complex block calls (13)",
|
|
||||||
"\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n",
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
"baz",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"should strip whitespace around partials (1)",
|
|
||||||
"foo {{~> dude~}} ",
|
|
||||||
nil, nil, nil,
|
|
||||||
map[string]string{"dude": "bar"},
|
|
||||||
"foobar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around partials (2)",
|
|
||||||
"foo {{> dude~}} ",
|
|
||||||
nil, nil, nil,
|
|
||||||
map[string]string{"dude": "bar"},
|
|
||||||
"foo bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around partials (3)",
|
|
||||||
"foo {{> dude}} ",
|
|
||||||
nil, nil, nil,
|
|
||||||
map[string]string{"dude": "bar"},
|
|
||||||
"foo bar ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around partials (4)",
|
|
||||||
"foo\n {{~> dude}} ",
|
|
||||||
nil, nil, nil,
|
|
||||||
map[string]string{"dude": "bar"},
|
|
||||||
"foobar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"should strip whitespace around partials (5)",
|
|
||||||
"foo\n {{> dude}} ",
|
|
||||||
nil, nil, nil,
|
|
||||||
map[string]string{"dude": "bar"},
|
|
||||||
"foo\n bar",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"should only strip whitespace once",
|
|
||||||
" {{~foo~}} {{foo}} {{foo}} ",
|
|
||||||
map[string]string{"foo": "bar"},
|
|
||||||
nil, nil, nil,
|
|
||||||
"barbar bar ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWhitespaceControl(t *testing.T) {
|
|
||||||
launchTests(t, whitespaceControlTests)
|
|
||||||
}
|
|
371
vendor/github.com/aymerick/raymond/helper.go
generated
vendored
371
vendor/github.com/aymerick/raymond/helper.go
generated
vendored
|
@ -1,371 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options represents the options argument provided to helpers and context functions.
|
|
||||||
type Options struct {
|
|
||||||
// evaluation visitor
|
|
||||||
eval *evalVisitor
|
|
||||||
|
|
||||||
// params
|
|
||||||
params []interface{}
|
|
||||||
hash map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helpers stores all globally registered helpers
|
|
||||||
var helpers = make(map[string]reflect.Value)
|
|
||||||
|
|
||||||
// protects global helpers
|
|
||||||
var helpersMutex sync.RWMutex
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// register builtin helpers
|
|
||||||
RegisterHelper("if", ifHelper)
|
|
||||||
RegisterHelper("unless", unlessHelper)
|
|
||||||
RegisterHelper("with", withHelper)
|
|
||||||
RegisterHelper("each", eachHelper)
|
|
||||||
RegisterHelper("log", logHelper)
|
|
||||||
RegisterHelper("lookup", lookupHelper)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterHelper registers a global helper. That helper will be available to all templates.
|
|
||||||
func RegisterHelper(name string, helper interface{}) {
|
|
||||||
helpersMutex.Lock()
|
|
||||||
defer helpersMutex.Unlock()
|
|
||||||
|
|
||||||
if helpers[name] != zero {
|
|
||||||
panic(fmt.Errorf("Helper already registered: %s", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.ValueOf(helper)
|
|
||||||
ensureValidHelper(name, val)
|
|
||||||
|
|
||||||
helpers[name] = val
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterHelpers registers several global helpers. Those helpers will be available to all templates.
|
|
||||||
func RegisterHelpers(helpers map[string]interface{}) {
|
|
||||||
for name, helper := range helpers {
|
|
||||||
RegisterHelper(name, helper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensureValidHelper panics if given helper is not valid
|
|
||||||
func ensureValidHelper(name string, funcValue reflect.Value) {
|
|
||||||
if funcValue.Kind() != reflect.Func {
|
|
||||||
panic(fmt.Errorf("Helper must be a function: %s", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
funcType := funcValue.Type()
|
|
||||||
|
|
||||||
if funcType.NumOut() != 1 {
|
|
||||||
panic(fmt.Errorf("Helper function must return a string or a SafeString: %s", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo Check if first returned value is a string, SafeString or interface{} ?
|
|
||||||
}
|
|
||||||
|
|
||||||
// findHelper finds a globally registered helper
|
|
||||||
func findHelper(name string) reflect.Value {
|
|
||||||
helpersMutex.RLock()
|
|
||||||
defer helpersMutex.RUnlock()
|
|
||||||
|
|
||||||
return helpers[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// newOptions instanciates a new Options
|
|
||||||
func newOptions(eval *evalVisitor, params []interface{}, hash map[string]interface{}) *Options {
|
|
||||||
return &Options{
|
|
||||||
eval: eval,
|
|
||||||
params: params,
|
|
||||||
hash: hash,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newEmptyOptions instanciates a new empty Options
|
|
||||||
func newEmptyOptions(eval *evalVisitor) *Options {
|
|
||||||
return &Options{
|
|
||||||
eval: eval,
|
|
||||||
hash: make(map[string]interface{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Context Values
|
|
||||||
//
|
|
||||||
|
|
||||||
// Value returns field value from current context.
|
|
||||||
func (options *Options) Value(name string) interface{} {
|
|
||||||
value := options.eval.evalField(options.eval.curCtx(), name, false)
|
|
||||||
if !value.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValueStr returns string representation of field value from current context.
|
|
||||||
func (options *Options) ValueStr(name string) string {
|
|
||||||
return Str(options.Value(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ctx returns current evaluation context.
|
|
||||||
func (options *Options) Ctx() interface{} {
|
|
||||||
return options.eval.curCtx().Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Hash Arguments
|
|
||||||
//
|
|
||||||
|
|
||||||
// HashProp returns hash property.
|
|
||||||
func (options *Options) HashProp(name string) interface{} {
|
|
||||||
return options.hash[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashStr returns string representation of hash property.
|
|
||||||
func (options *Options) HashStr(name string) string {
|
|
||||||
return Str(options.hash[name])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash returns entire hash.
|
|
||||||
func (options *Options) Hash() map[string]interface{} {
|
|
||||||
return options.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Parameters
|
|
||||||
//
|
|
||||||
|
|
||||||
// Param returns parameter at given position.
|
|
||||||
func (options *Options) Param(pos int) interface{} {
|
|
||||||
if len(options.params) > pos {
|
|
||||||
return options.params[pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParamStr returns string representation of parameter at given position.
|
|
||||||
func (options *Options) ParamStr(pos int) string {
|
|
||||||
return Str(options.Param(pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Params returns all parameters.
|
|
||||||
func (options *Options) Params() []interface{} {
|
|
||||||
return options.params
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Private data
|
|
||||||
//
|
|
||||||
|
|
||||||
// Data returns private data value.
|
|
||||||
func (options *Options) Data(name string) interface{} {
|
|
||||||
return options.eval.dataFrame.Get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataStr returns string representation of private data value.
|
|
||||||
func (options *Options) DataStr(name string) string {
|
|
||||||
return Str(options.eval.dataFrame.Get(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataFrame returns current private data frame.
|
|
||||||
func (options *Options) DataFrame() *DataFrame {
|
|
||||||
return options.eval.dataFrame
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDataFrame instanciates a new data frame that is a copy of current evaluation data frame.
|
|
||||||
//
|
|
||||||
// Parent of returned data frame is set to current evaluation data frame.
|
|
||||||
func (options *Options) NewDataFrame() *DataFrame {
|
|
||||||
return options.eval.dataFrame.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newIterDataFrame instanciates a new data frame and set iteration specific vars
|
|
||||||
func (options *Options) newIterDataFrame(length int, i int, key interface{}) *DataFrame {
|
|
||||||
return options.eval.dataFrame.newIterDataFrame(length, i, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Evaluation
|
|
||||||
//
|
|
||||||
|
|
||||||
// evalBlock evaluates block with given context, private data and iteration key
|
|
||||||
func (options *Options) evalBlock(ctx interface{}, data *DataFrame, key interface{}) string {
|
|
||||||
result := ""
|
|
||||||
|
|
||||||
if block := options.eval.curBlock(); (block != nil) && (block.Program != nil) {
|
|
||||||
result = options.eval.evalProgram(block.Program, ctx, data, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fn evaluates block with current evaluation context.
|
|
||||||
func (options *Options) Fn() string {
|
|
||||||
return options.evalBlock(nil, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FnCtxData evaluates block with given context and private data frame.
|
|
||||||
func (options *Options) FnCtxData(ctx interface{}, data *DataFrame) string {
|
|
||||||
return options.evalBlock(ctx, data, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FnWith evaluates block with given context.
|
|
||||||
func (options *Options) FnWith(ctx interface{}) string {
|
|
||||||
return options.evalBlock(ctx, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FnData evaluates block with given private data frame.
|
|
||||||
func (options *Options) FnData(data *DataFrame) string {
|
|
||||||
return options.evalBlock(nil, data, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inverse evaluates "else block".
|
|
||||||
func (options *Options) Inverse() string {
|
|
||||||
result := ""
|
|
||||||
if block := options.eval.curBlock(); (block != nil) && (block.Inverse != nil) {
|
|
||||||
result, _ = block.Inverse.Accept(options.eval).(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eval evaluates field for given context.
|
|
||||||
func (options *Options) Eval(ctx interface{}, field string) interface{} {
|
|
||||||
if ctx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if field == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
val := options.eval.evalField(reflect.ValueOf(ctx), field, false)
|
|
||||||
if !val.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return val.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Misc
|
|
||||||
//
|
|
||||||
|
|
||||||
// isIncludableZero returns true if 'includeZero' option is set and first param is the number 0
|
|
||||||
func (options *Options) isIncludableZero() bool {
|
|
||||||
b, ok := options.HashProp("includeZero").(bool)
|
|
||||||
if ok && b {
|
|
||||||
nb, ok := options.Param(0).(int)
|
|
||||||
if ok && nb == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Builtin helpers
|
|
||||||
//
|
|
||||||
|
|
||||||
// #if block helper
|
|
||||||
func ifHelper(conditional interface{}, options *Options) interface{} {
|
|
||||||
if options.isIncludableZero() || IsTrue(conditional) {
|
|
||||||
return options.Fn()
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.Inverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
// #unless block helper
|
|
||||||
func unlessHelper(conditional interface{}, options *Options) interface{} {
|
|
||||||
if options.isIncludableZero() || IsTrue(conditional) {
|
|
||||||
return options.Inverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.Fn()
|
|
||||||
}
|
|
||||||
|
|
||||||
// #with block helper
|
|
||||||
func withHelper(context interface{}, options *Options) interface{} {
|
|
||||||
if IsTrue(context) {
|
|
||||||
return options.FnWith(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options.Inverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
// #each block helper
|
|
||||||
func eachHelper(context interface{}, options *Options) interface{} {
|
|
||||||
if !IsTrue(context) {
|
|
||||||
return options.Inverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
result := ""
|
|
||||||
|
|
||||||
val := reflect.ValueOf(context)
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
// computes private data
|
|
||||||
data := options.newIterDataFrame(val.Len(), i, nil)
|
|
||||||
|
|
||||||
// evaluates block
|
|
||||||
result += options.evalBlock(val.Index(i).Interface(), data, i)
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
// note: a go hash is not ordered, so result may vary, this behaviour differs from the JS implementation
|
|
||||||
keys := val.MapKeys()
|
|
||||||
for i := 0; i < len(keys); i++ {
|
|
||||||
key := keys[i].Interface()
|
|
||||||
ctx := val.MapIndex(keys[i]).Interface()
|
|
||||||
|
|
||||||
// computes private data
|
|
||||||
data := options.newIterDataFrame(len(keys), i, key)
|
|
||||||
|
|
||||||
// evaluates block
|
|
||||||
result += options.evalBlock(ctx, data, key)
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
var exportedFields []int
|
|
||||||
|
|
||||||
// collect exported fields only
|
|
||||||
for i := 0; i < val.NumField(); i++ {
|
|
||||||
if tField := val.Type().Field(i); tField.PkgPath == "" {
|
|
||||||
exportedFields = append(exportedFields, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, fieldIndex := range exportedFields {
|
|
||||||
key := val.Type().Field(fieldIndex).Name
|
|
||||||
ctx := val.Field(fieldIndex).Interface()
|
|
||||||
|
|
||||||
// computes private data
|
|
||||||
data := options.newIterDataFrame(len(exportedFields), i, key)
|
|
||||||
|
|
||||||
// evaluates block
|
|
||||||
result += options.evalBlock(ctx, data, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// #log helper
|
|
||||||
func logHelper(message string) interface{} {
|
|
||||||
log.Print(message)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// #lookup helper
|
|
||||||
func lookupHelper(obj interface{}, field string, options *Options) interface{} {
|
|
||||||
return Str(options.Eval(obj, field))
|
|
||||||
}
|
|
193
vendor/github.com/aymerick/raymond/helper_test.go
generated
vendored
193
vendor/github.com/aymerick/raymond/helper_test.go
generated
vendored
|
@ -1,193 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
const (
|
|
||||||
VERBOSE = false
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Helpers
|
|
||||||
//
|
|
||||||
|
|
||||||
func barHelper(options *Options) string { return "bar" }
|
|
||||||
|
|
||||||
func echoHelper(str string, nb int) string {
|
|
||||||
result := ""
|
|
||||||
for i := 0; i < nb; i++ {
|
|
||||||
result += str
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolHelper(b bool) string {
|
|
||||||
if b {
|
|
||||||
return "yes it is"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "absolutely not"
|
|
||||||
}
|
|
||||||
|
|
||||||
func gnakHelper(nb int) string {
|
|
||||||
result := ""
|
|
||||||
for i := 0; i < nb; i++ {
|
|
||||||
result += "GnAK!"
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Tests
|
|
||||||
//
|
|
||||||
|
|
||||||
var helperTests = []Test{
|
|
||||||
{
|
|
||||||
"simple helper",
|
|
||||||
`{{foo}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"foo": barHelper},
|
|
||||||
nil,
|
|
||||||
`bar`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper with literal string param",
|
|
||||||
`{{echo "foo" 1}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"echo": echoHelper},
|
|
||||||
nil,
|
|
||||||
`foo`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper with identifier param",
|
|
||||||
`{{echo foo 1}}`,
|
|
||||||
map[string]interface{}{"foo": "bar"},
|
|
||||||
nil,
|
|
||||||
map[string]interface{}{"echo": echoHelper},
|
|
||||||
nil,
|
|
||||||
`bar`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper with literal boolean param",
|
|
||||||
`{{bool true}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"bool": boolHelper},
|
|
||||||
nil,
|
|
||||||
`yes it is`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper with literal boolean param",
|
|
||||||
`{{bool false}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"bool": boolHelper},
|
|
||||||
nil,
|
|
||||||
`absolutely not`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper with literal boolean param",
|
|
||||||
`{{gnak 5}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"gnak": gnakHelper},
|
|
||||||
nil,
|
|
||||||
`GnAK!GnAK!GnAK!GnAK!GnAK!`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"helper with several parameters",
|
|
||||||
`{{echo "GnAK!" 3}}`,
|
|
||||||
nil, nil,
|
|
||||||
map[string]interface{}{"echo": echoHelper},
|
|
||||||
nil,
|
|
||||||
`GnAK!GnAK!GnAK!`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if helper with true literal",
|
|
||||||
`{{#if true}}YES MAN{{/if}}`,
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
`YES MAN`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if helper with false literal",
|
|
||||||
`{{#if false}}YES MAN{{/if}}`,
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
``,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if helper with truthy identifier",
|
|
||||||
`{{#if ok}}YES MAN{{/if}}`,
|
|
||||||
map[string]interface{}{"ok": true},
|
|
||||||
nil, nil, nil,
|
|
||||||
`YES MAN`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#if helper with falsy identifier",
|
|
||||||
`{{#if ok}}YES MAN{{/if}}`,
|
|
||||||
map[string]interface{}{"ok": false},
|
|
||||||
nil, nil, nil,
|
|
||||||
``,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#unless helper with true literal",
|
|
||||||
`{{#unless true}}YES MAN{{/unless}}`,
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
``,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#unless helper with false literal",
|
|
||||||
`{{#unless false}}YES MAN{{/unless}}`,
|
|
||||||
nil, nil, nil, nil,
|
|
||||||
`YES MAN`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#unless helper with truthy identifier",
|
|
||||||
`{{#unless ok}}YES MAN{{/unless}}`,
|
|
||||||
map[string]interface{}{"ok": true},
|
|
||||||
nil, nil, nil,
|
|
||||||
``,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"#unless helper with falsy identifier",
|
|
||||||
`{{#unless ok}}YES MAN{{/unless}}`,
|
|
||||||
map[string]interface{}{"ok": false},
|
|
||||||
nil, nil, nil,
|
|
||||||
`YES MAN`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Let's go
|
|
||||||
//
|
|
||||||
|
|
||||||
func TestHelper(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
launchTests(t, helperTests)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Fixes: https://github.com/aymerick/raymond/issues/2
|
|
||||||
//
|
|
||||||
|
|
||||||
type Author struct {
|
|
||||||
FirstName string
|
|
||||||
LastName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHelperCtx(t *testing.T) {
|
|
||||||
RegisterHelper("template", func(name string, options *Options) SafeString {
|
|
||||||
context := options.Ctx()
|
|
||||||
|
|
||||||
template := name + " - {{ firstName }} {{ lastName }}"
|
|
||||||
result, _ := Render(template, context)
|
|
||||||
|
|
||||||
return SafeString(result)
|
|
||||||
})
|
|
||||||
|
|
||||||
template := `By {{ template "namefile" }}`
|
|
||||||
context := Author{"Alan", "Johnson"}
|
|
||||||
|
|
||||||
result, _ := Render(template, context)
|
|
||||||
if result != "By namefile - Alan Johnson" {
|
|
||||||
t.Errorf("Failed to render template in helper: %q", result)
|
|
||||||
}
|
|
||||||
}
|
|
639
vendor/github.com/aymerick/raymond/lexer/lexer.go
generated
vendored
639
vendor/github.com/aymerick/raymond/lexer/lexer.go
generated
vendored
|
@ -1,639 +0,0 @@
|
||||||
// Package lexer provides a handlebars tokenizer.
|
|
||||||
package lexer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// References:
|
|
||||||
// - https://github.com/wycats/handlebars.js/blob/master/src/handlebars.l
|
|
||||||
// - https://github.com/golang/go/blob/master/src/text/template/parse/lex.go
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Mustaches detection
|
|
||||||
escapedEscapedOpenMustache = "\\\\{{"
|
|
||||||
escapedOpenMustache = "\\{{"
|
|
||||||
openMustache = "{{"
|
|
||||||
closeMustache = "}}"
|
|
||||||
closeStripMustache = "~}}"
|
|
||||||
closeUnescapedStripMustache = "}~}}"
|
|
||||||
)
|
|
||||||
|
|
||||||
const eof = -1
|
|
||||||
|
|
||||||
// lexFunc represents a function that returns the next lexer function.
|
|
||||||
type lexFunc func(*Lexer) lexFunc
|
|
||||||
|
|
||||||
// Lexer is a lexical analyzer.
|
|
||||||
type Lexer struct {
|
|
||||||
input string // input to scan
|
|
||||||
name string // lexer name, used for testing purpose
|
|
||||||
tokens chan Token // channel of scanned tokens
|
|
||||||
nextFunc lexFunc // the next function to execute
|
|
||||||
|
|
||||||
pos int // current byte position in input string
|
|
||||||
line int // current line position in input string
|
|
||||||
width int // size of last rune scanned from input string
|
|
||||||
start int // start position of the token we are scanning
|
|
||||||
|
|
||||||
// the shameful contextual properties needed because `nextFunc` is not enough
|
|
||||||
closeComment *regexp.Regexp // regexp to scan close of current comment
|
|
||||||
rawBlock bool // are we parsing a raw block content ?
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
lookheadChars = `[\s` + regexp.QuoteMeta("=~}/)|") + `]`
|
|
||||||
literalLookheadChars = `[\s` + regexp.QuoteMeta("~})") + `]`
|
|
||||||
|
|
||||||
// characters not allowed in an identifier
|
|
||||||
unallowedIDChars = " \n\t!\"#%&'()*+,./;<=>@[\\]^`{|}~"
|
|
||||||
|
|
||||||
// regular expressions
|
|
||||||
rID = regexp.MustCompile(`^[^` + regexp.QuoteMeta(unallowedIDChars) + `]+`)
|
|
||||||
rDotID = regexp.MustCompile(`^\.` + lookheadChars)
|
|
||||||
rTrue = regexp.MustCompile(`^true` + literalLookheadChars)
|
|
||||||
rFalse = regexp.MustCompile(`^false` + literalLookheadChars)
|
|
||||||
rOpenRaw = regexp.MustCompile(`^\{\{\{\{`)
|
|
||||||
rCloseRaw = regexp.MustCompile(`^\}\}\}\}`)
|
|
||||||
rOpenEndRaw = regexp.MustCompile(`^\{\{\{\{/`)
|
|
||||||
rOpenEndRawLookAhead = regexp.MustCompile(`\{\{\{\{/`)
|
|
||||||
rOpenUnescaped = regexp.MustCompile(`^\{\{~?\{`)
|
|
||||||
rCloseUnescaped = regexp.MustCompile(`^\}~?\}\}`)
|
|
||||||
rOpenBlock = regexp.MustCompile(`^\{\{~?#`)
|
|
||||||
rOpenEndBlock = regexp.MustCompile(`^\{\{~?/`)
|
|
||||||
rOpenPartial = regexp.MustCompile(`^\{\{~?>`)
|
|
||||||
// {{^}} or {{else}}
|
|
||||||
rInverse = regexp.MustCompile(`^(\{\{~?\^\s*~?\}\}|\{\{~?\s*else\s*~?\}\})`)
|
|
||||||
rOpenInverse = regexp.MustCompile(`^\{\{~?\^`)
|
|
||||||
rOpenInverseChain = regexp.MustCompile(`^\{\{~?\s*else`)
|
|
||||||
// {{ or {{&
|
|
||||||
rOpen = regexp.MustCompile(`^\{\{~?&?`)
|
|
||||||
rClose = regexp.MustCompile(`^~?\}\}`)
|
|
||||||
rOpenBlockParams = regexp.MustCompile(`^as\s+\|`)
|
|
||||||
// {{!-- ... --}}
|
|
||||||
rOpenCommentDash = regexp.MustCompile(`^\{\{~?!--\s*`)
|
|
||||||
rCloseCommentDash = regexp.MustCompile(`^\s*--~?\}\}`)
|
|
||||||
// {{! ... }}
|
|
||||||
rOpenComment = regexp.MustCompile(`^\{\{~?!\s*`)
|
|
||||||
rCloseComment = regexp.MustCompile(`^\s*~?\}\}`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scan scans given input.
|
|
||||||
//
|
|
||||||
// Tokens can then be fetched sequentially thanks to NextToken() function on returned lexer.
|
|
||||||
func Scan(input string) *Lexer {
|
|
||||||
return scanWithName(input, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// scanWithName scans given input, with a name used for testing
|
|
||||||
//
|
|
||||||
// Tokens can then be fetched sequentially thanks to NextToken() function on returned lexer.
|
|
||||||
func scanWithName(input string, name string) *Lexer {
|
|
||||||
result := &Lexer{
|
|
||||||
input: input,
|
|
||||||
name: name,
|
|
||||||
tokens: make(chan Token),
|
|
||||||
line: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
go result.run()
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect scans and collect all tokens.
|
|
||||||
//
|
|
||||||
// This should be used for debugging purpose only. You should use Scan() and lexer.NextToken() functions instead.
|
|
||||||
func Collect(input string) []Token {
|
|
||||||
var result []Token
|
|
||||||
|
|
||||||
l := Scan(input)
|
|
||||||
for {
|
|
||||||
token := l.NextToken()
|
|
||||||
result = append(result, token)
|
|
||||||
|
|
||||||
if token.Kind == TokenEOF || token.Kind == TokenError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextToken returns the next scanned token.
|
|
||||||
func (l *Lexer) NextToken() Token {
|
|
||||||
result := <-l.tokens
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// run starts lexical analysis
|
|
||||||
func (l *Lexer) run() {
|
|
||||||
for l.nextFunc = lexContent; l.nextFunc != nil; {
|
|
||||||
l.nextFunc = l.nextFunc(l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// next returns next character from input, or eof of there is nothing left to scan
|
|
||||||
func (l *Lexer) next() rune {
|
|
||||||
if l.pos >= len(l.input) {
|
|
||||||
l.width = 0
|
|
||||||
return eof
|
|
||||||
}
|
|
||||||
|
|
||||||
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
|
|
||||||
l.width = w
|
|
||||||
l.pos += l.width
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lexer) produce(kind TokenKind, val string) {
|
|
||||||
l.tokens <- Token{kind, val, l.start, l.line}
|
|
||||||
|
|
||||||
// scanning a new token
|
|
||||||
l.start = l.pos
|
|
||||||
|
|
||||||
// update line number
|
|
||||||
l.line += strings.Count(val, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit emits a new scanned token
|
|
||||||
func (l *Lexer) emit(kind TokenKind) {
|
|
||||||
l.produce(kind, l.input[l.start:l.pos])
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitContent emits scanned content
|
|
||||||
func (l *Lexer) emitContent() {
|
|
||||||
if l.pos > l.start {
|
|
||||||
l.emit(TokenContent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitString emits a scanned string
|
|
||||||
func (l *Lexer) emitString(delimiter rune) {
|
|
||||||
str := l.input[l.start:l.pos]
|
|
||||||
|
|
||||||
// replace escaped delimiters
|
|
||||||
str = strings.Replace(str, "\\"+string(delimiter), string(delimiter), -1)
|
|
||||||
|
|
||||||
l.produce(TokenString, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek returns but does not consume the next character in the input
|
|
||||||
func (l *Lexer) peek() rune {
|
|
||||||
r := l.next()
|
|
||||||
l.backup()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup steps back one character
|
|
||||||
//
|
|
||||||
// WARNING: Can only be called once per call of next
|
|
||||||
func (l *Lexer) backup() {
|
|
||||||
l.pos -= l.width
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignoreskips all characters that have been scanned up to current position
|
|
||||||
func (l *Lexer) ignore() {
|
|
||||||
l.start = l.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// accept scans the next character if it is included in given string
|
|
||||||
func (l *Lexer) accept(valid string) bool {
|
|
||||||
if strings.IndexRune(valid, l.next()) >= 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
l.backup()
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// acceptRun scans all following characters that are part of given string
|
|
||||||
func (l *Lexer) acceptRun(valid string) {
|
|
||||||
for strings.IndexRune(valid, l.next()) >= 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
l.backup()
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf emits an error token
|
|
||||||
func (l *Lexer) errorf(format string, args ...interface{}) lexFunc {
|
|
||||||
l.tokens <- Token{TokenError, fmt.Sprintf(format, args...), l.start, l.line}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isString returns true if content at current scanning position starts with given string
|
|
||||||
func (l *Lexer) isString(str string) bool {
|
|
||||||
return strings.HasPrefix(l.input[l.pos:], str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// findRegexp returns the first string from current scanning position that matches given regular expression
|
|
||||||
func (l *Lexer) findRegexp(r *regexp.Regexp) string {
|
|
||||||
return r.FindString(l.input[l.pos:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// indexRegexp returns the index of the first string from current scanning position that matches given regular expression
|
|
||||||
//
|
|
||||||
// It returns -1 if not found
|
|
||||||
func (l *Lexer) indexRegexp(r *regexp.Regexp) int {
|
|
||||||
loc := r.FindStringIndex(l.input[l.pos:])
|
|
||||||
if loc == nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return loc[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexContent scans content (ie: not between mustaches)
|
|
||||||
func lexContent(l *Lexer) lexFunc {
|
|
||||||
var next lexFunc
|
|
||||||
|
|
||||||
if l.rawBlock {
|
|
||||||
if i := l.indexRegexp(rOpenEndRawLookAhead); i != -1 {
|
|
||||||
// {{{{/
|
|
||||||
l.rawBlock = false
|
|
||||||
l.pos += i
|
|
||||||
|
|
||||||
next = lexOpenMustache
|
|
||||||
} else {
|
|
||||||
return l.errorf("Unclosed raw block")
|
|
||||||
}
|
|
||||||
} else if l.isString(escapedEscapedOpenMustache) {
|
|
||||||
// \\{{
|
|
||||||
|
|
||||||
// emit content with only one escaped escape
|
|
||||||
l.next()
|
|
||||||
l.emitContent()
|
|
||||||
|
|
||||||
// ignore second escaped escape
|
|
||||||
l.next()
|
|
||||||
l.ignore()
|
|
||||||
|
|
||||||
next = lexContent
|
|
||||||
} else if l.isString(escapedOpenMustache) {
|
|
||||||
// \{{
|
|
||||||
next = lexEscapedOpenMustache
|
|
||||||
} else if str := l.findRegexp(rOpenCommentDash); str != "" {
|
|
||||||
// {{!--
|
|
||||||
l.closeComment = rCloseCommentDash
|
|
||||||
|
|
||||||
next = lexComment
|
|
||||||
} else if str := l.findRegexp(rOpenComment); str != "" {
|
|
||||||
// {{!
|
|
||||||
l.closeComment = rCloseComment
|
|
||||||
|
|
||||||
next = lexComment
|
|
||||||
} else if l.isString(openMustache) {
|
|
||||||
// {{
|
|
||||||
next = lexOpenMustache
|
|
||||||
}
|
|
||||||
|
|
||||||
if next != nil {
|
|
||||||
// emit scanned content
|
|
||||||
l.emitContent()
|
|
||||||
|
|
||||||
// scan next token
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan next rune
|
|
||||||
if l.next() == eof {
|
|
||||||
// emit scanned content
|
|
||||||
l.emitContent()
|
|
||||||
|
|
||||||
// this is over
|
|
||||||
l.emit(TokenEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// continue content scanning
|
|
||||||
return lexContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexEscapedOpenMustache scans \{{
|
|
||||||
func lexEscapedOpenMustache(l *Lexer) lexFunc {
|
|
||||||
// ignore escape character
|
|
||||||
l.next()
|
|
||||||
l.ignore()
|
|
||||||
|
|
||||||
// scan mustaches
|
|
||||||
for l.peek() == '{' {
|
|
||||||
l.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexOpenMustache scans {{
|
|
||||||
func lexOpenMustache(l *Lexer) lexFunc {
|
|
||||||
var str string
|
|
||||||
var tok TokenKind
|
|
||||||
|
|
||||||
nextFunc := lexExpression
|
|
||||||
|
|
||||||
if str = l.findRegexp(rOpenEndRaw); str != "" {
|
|
||||||
tok = TokenOpenEndRawBlock
|
|
||||||
} else if str = l.findRegexp(rOpenRaw); str != "" {
|
|
||||||
tok = TokenOpenRawBlock
|
|
||||||
l.rawBlock = true
|
|
||||||
} else if str = l.findRegexp(rOpenUnescaped); str != "" {
|
|
||||||
tok = TokenOpenUnescaped
|
|
||||||
} else if str = l.findRegexp(rOpenBlock); str != "" {
|
|
||||||
tok = TokenOpenBlock
|
|
||||||
} else if str = l.findRegexp(rOpenEndBlock); str != "" {
|
|
||||||
tok = TokenOpenEndBlock
|
|
||||||
} else if str = l.findRegexp(rOpenPartial); str != "" {
|
|
||||||
tok = TokenOpenPartial
|
|
||||||
} else if str = l.findRegexp(rInverse); str != "" {
|
|
||||||
tok = TokenInverse
|
|
||||||
nextFunc = lexContent
|
|
||||||
} else if str = l.findRegexp(rOpenInverse); str != "" {
|
|
||||||
tok = TokenOpenInverse
|
|
||||||
} else if str = l.findRegexp(rOpenInverseChain); str != "" {
|
|
||||||
tok = TokenOpenInverseChain
|
|
||||||
} else if str = l.findRegexp(rOpen); str != "" {
|
|
||||||
tok = TokenOpen
|
|
||||||
} else {
|
|
||||||
// this is rotten
|
|
||||||
panic("Current pos MUST be an opening mustache")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.pos += len(str)
|
|
||||||
l.emit(tok)
|
|
||||||
|
|
||||||
return nextFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexCloseMustache scans }} or ~}}
|
|
||||||
func lexCloseMustache(l *Lexer) lexFunc {
|
|
||||||
var str string
|
|
||||||
var tok TokenKind
|
|
||||||
|
|
||||||
if str = l.findRegexp(rCloseRaw); str != "" {
|
|
||||||
// }}}}
|
|
||||||
tok = TokenCloseRawBlock
|
|
||||||
} else if str = l.findRegexp(rCloseUnescaped); str != "" {
|
|
||||||
// }}}
|
|
||||||
tok = TokenCloseUnescaped
|
|
||||||
} else if str = l.findRegexp(rClose); str != "" {
|
|
||||||
// }}
|
|
||||||
tok = TokenClose
|
|
||||||
} else {
|
|
||||||
// this is rotten
|
|
||||||
panic("Current pos MUST be a closing mustache")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.pos += len(str)
|
|
||||||
l.emit(tok)
|
|
||||||
|
|
||||||
return lexContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexExpression scans inside mustaches
|
|
||||||
func lexExpression(l *Lexer) lexFunc {
|
|
||||||
// search close mustache delimiter
|
|
||||||
if l.isString(closeMustache) || l.isString(closeStripMustache) || l.isString(closeUnescapedStripMustache) {
|
|
||||||
return lexCloseMustache
|
|
||||||
}
|
|
||||||
|
|
||||||
// search some patterns before advancing scanning position
|
|
||||||
|
|
||||||
// "as |"
|
|
||||||
if str := l.findRegexp(rOpenBlockParams); str != "" {
|
|
||||||
l.pos += len(str)
|
|
||||||
l.emit(TokenOpenBlockParams)
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// ..
|
|
||||||
if l.isString("..") {
|
|
||||||
l.pos += len("..")
|
|
||||||
l.emit(TokenID)
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// .
|
|
||||||
if str := l.findRegexp(rDotID); str != "" {
|
|
||||||
l.pos += len(".")
|
|
||||||
l.emit(TokenID)
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// true
|
|
||||||
if str := l.findRegexp(rTrue); str != "" {
|
|
||||||
l.pos += len("true")
|
|
||||||
l.emit(TokenBoolean)
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// false
|
|
||||||
if str := l.findRegexp(rFalse); str != "" {
|
|
||||||
l.pos += len("false")
|
|
||||||
l.emit(TokenBoolean)
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's scan next character
|
|
||||||
switch r := l.next(); {
|
|
||||||
case r == eof:
|
|
||||||
return l.errorf("Unclosed expression")
|
|
||||||
case isIgnorable(r):
|
|
||||||
return lexIgnorable
|
|
||||||
case r == '(':
|
|
||||||
l.emit(TokenOpenSexpr)
|
|
||||||
case r == ')':
|
|
||||||
l.emit(TokenCloseSexpr)
|
|
||||||
case r == '=':
|
|
||||||
l.emit(TokenEquals)
|
|
||||||
case r == '@':
|
|
||||||
l.emit(TokenData)
|
|
||||||
case r == '"' || r == '\'':
|
|
||||||
l.backup()
|
|
||||||
return lexString
|
|
||||||
case r == '/' || r == '.':
|
|
||||||
l.emit(TokenSep)
|
|
||||||
case r == '|':
|
|
||||||
l.emit(TokenCloseBlockParams)
|
|
||||||
case r == '+' || r == '-' || (r >= '0' && r <= '9'):
|
|
||||||
l.backup()
|
|
||||||
return lexNumber
|
|
||||||
case r == '[':
|
|
||||||
return lexPathLiteral
|
|
||||||
case strings.IndexRune(unallowedIDChars, r) < 0:
|
|
||||||
l.backup()
|
|
||||||
return lexIdentifier
|
|
||||||
default:
|
|
||||||
return l.errorf("Unexpected character in expression: '%c'", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexComment scans {{!-- or {{!
|
|
||||||
func lexComment(l *Lexer) lexFunc {
|
|
||||||
if str := l.findRegexp(l.closeComment); str != "" {
|
|
||||||
l.pos += len(str)
|
|
||||||
l.emit(TokenComment)
|
|
||||||
|
|
||||||
return lexContent
|
|
||||||
}
|
|
||||||
|
|
||||||
if r := l.next(); r == eof {
|
|
||||||
return l.errorf("Unclosed comment")
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexIgnorable scans all following ignorable characters
|
|
||||||
func lexIgnorable(l *Lexer) lexFunc {
|
|
||||||
for isIgnorable(l.peek()) {
|
|
||||||
l.next()
|
|
||||||
}
|
|
||||||
l.ignore()
|
|
||||||
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexString scans a string
|
|
||||||
func lexString(l *Lexer) lexFunc {
|
|
||||||
// get string delimiter
|
|
||||||
delim := l.next()
|
|
||||||
var prev rune
|
|
||||||
|
|
||||||
// ignore delimiter
|
|
||||||
l.ignore()
|
|
||||||
|
|
||||||
for {
|
|
||||||
r := l.next()
|
|
||||||
if r == eof || r == '\n' {
|
|
||||||
return l.errorf("Unterminated string")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r == delim) && (prev != '\\') {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = r
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove end delimiter
|
|
||||||
l.backup()
|
|
||||||
|
|
||||||
// emit string
|
|
||||||
l.emitString(delim)
|
|
||||||
|
|
||||||
// skip end delimiter
|
|
||||||
l.next()
|
|
||||||
l.ignore()
|
|
||||||
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
|
|
||||||
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
|
|
||||||
// and "089" - but when it's wrong the input is invalid and the parser (via
|
|
||||||
// strconv) will notice.
|
|
||||||
//
|
|
||||||
// NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/parse/lex.go
|
|
||||||
func lexNumber(l *Lexer) lexFunc {
|
|
||||||
if !l.scanNumber() {
|
|
||||||
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
|
||||||
}
|
|
||||||
if sign := l.peek(); sign == '+' || sign == '-' {
|
|
||||||
// Complex: 1+2i. No spaces, must end in 'i'.
|
|
||||||
if !l.scanNumber() || l.input[l.pos-1] != 'i' {
|
|
||||||
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
|
||||||
}
|
|
||||||
l.emit(TokenNumber)
|
|
||||||
} else {
|
|
||||||
l.emit(TokenNumber)
|
|
||||||
}
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// scanNumber scans a number
|
|
||||||
//
|
|
||||||
// NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/parse/lex.go
|
|
||||||
func (l *Lexer) scanNumber() bool {
|
|
||||||
// Optional leading sign.
|
|
||||||
l.accept("+-")
|
|
||||||
|
|
||||||
// Is it hex?
|
|
||||||
digits := "0123456789"
|
|
||||||
|
|
||||||
if l.accept("0") && l.accept("xX") {
|
|
||||||
digits = "0123456789abcdefABCDEF"
|
|
||||||
}
|
|
||||||
|
|
||||||
l.acceptRun(digits)
|
|
||||||
|
|
||||||
if l.accept(".") {
|
|
||||||
l.acceptRun(digits)
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.accept("eE") {
|
|
||||||
l.accept("+-")
|
|
||||||
l.acceptRun("0123456789")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is it imaginary?
|
|
||||||
l.accept("i")
|
|
||||||
|
|
||||||
// Next thing mustn't be alphanumeric.
|
|
||||||
if isAlphaNumeric(l.peek()) {
|
|
||||||
l.next()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexIdentifier scans an ID
|
|
||||||
func lexIdentifier(l *Lexer) lexFunc {
|
|
||||||
str := l.findRegexp(rID)
|
|
||||||
if len(str) == 0 {
|
|
||||||
// this is rotten
|
|
||||||
panic("Identifier expected")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.pos += len(str)
|
|
||||||
l.emit(TokenID)
|
|
||||||
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexPathLiteral scans an [ID]
|
|
||||||
func lexPathLiteral(l *Lexer) lexFunc {
|
|
||||||
for {
|
|
||||||
r := l.next()
|
|
||||||
if r == eof || r == '\n' {
|
|
||||||
return l.errorf("Unterminated path literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == ']' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.emit(TokenID)
|
|
||||||
|
|
||||||
return lexExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// isIgnorable returns true if given character is ignorable (ie. whitespace of line feed)
|
|
||||||
func isIgnorable(r rune) bool {
|
|
||||||
return r == ' ' || r == '\t' || r == '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
|
|
||||||
//
|
|
||||||
// NOTE borrowed from https://github.com/golang/go/tree/master/src/text/template/parse/lex.go
|
|
||||||
func isAlphaNumeric(r rune) bool {
|
|
||||||
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
|
|
||||||
}
|
|
541
vendor/github.com/aymerick/raymond/lexer/lexer_test.go
generated
vendored
541
vendor/github.com/aymerick/raymond/lexer/lexer_test.go
generated
vendored
|
@ -1,541 +0,0 @@
|
||||||
package lexer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type lexTest struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
tokens []Token
|
|
||||||
}
|
|
||||||
|
|
||||||
// helpers
|
|
||||||
func tokContent(val string) Token { return Token{TokenContent, val, 0, 1} }
|
|
||||||
func tokID(val string) Token { return Token{TokenID, val, 0, 1} }
|
|
||||||
func tokSep(val string) Token { return Token{TokenSep, val, 0, 1} }
|
|
||||||
func tokString(val string) Token { return Token{TokenString, val, 0, 1} }
|
|
||||||
func tokNumber(val string) Token { return Token{TokenNumber, val, 0, 1} }
|
|
||||||
func tokInverse(val string) Token { return Token{TokenInverse, val, 0, 1} }
|
|
||||||
func tokBool(val string) Token { return Token{TokenBoolean, val, 0, 1} }
|
|
||||||
func tokError(val string) Token { return Token{TokenError, val, 0, 1} }
|
|
||||||
func tokComment(val string) Token { return Token{TokenComment, val, 0, 1} }
|
|
||||||
|
|
||||||
var tokEOF = Token{TokenEOF, "", 0, 1}
|
|
||||||
var tokEquals = Token{TokenEquals, "=", 0, 1}
|
|
||||||
var tokData = Token{TokenData, "@", 0, 1}
|
|
||||||
var tokOpen = Token{TokenOpen, "{{", 0, 1}
|
|
||||||
var tokOpenAmp = Token{TokenOpen, "{{&", 0, 1}
|
|
||||||
var tokOpenPartial = Token{TokenOpenPartial, "{{>", 0, 1}
|
|
||||||
var tokClose = Token{TokenClose, "}}", 0, 1}
|
|
||||||
var tokOpenStrip = Token{TokenOpen, "{{~", 0, 1}
|
|
||||||
var tokCloseStrip = Token{TokenClose, "~}}", 0, 1}
|
|
||||||
var tokOpenUnescaped = Token{TokenOpenUnescaped, "{{{", 0, 1}
|
|
||||||
var tokCloseUnescaped = Token{TokenCloseUnescaped, "}}}", 0, 1}
|
|
||||||
var tokOpenUnescapedStrip = Token{TokenOpenUnescaped, "{{~{", 0, 1}
|
|
||||||
var tokCloseUnescapedStrip = Token{TokenCloseUnescaped, "}~}}", 0, 1}
|
|
||||||
var tokOpenBlock = Token{TokenOpenBlock, "{{#", 0, 1}
|
|
||||||
var tokOpenEndBlock = Token{TokenOpenEndBlock, "{{/", 0, 1}
|
|
||||||
var tokOpenInverse = Token{TokenOpenInverse, "{{^", 0, 1}
|
|
||||||
var tokOpenInverseChain = Token{TokenOpenInverseChain, "{{else", 0, 1}
|
|
||||||
var tokOpenSexpr = Token{TokenOpenSexpr, "(", 0, 1}
|
|
||||||
var tokCloseSexpr = Token{TokenCloseSexpr, ")", 0, 1}
|
|
||||||
var tokOpenBlockParams = Token{TokenOpenBlockParams, "as |", 0, 1}
|
|
||||||
var tokCloseBlockParams = Token{TokenCloseBlockParams, "|", 0, 1}
|
|
||||||
var tokOpenRawBlock = Token{TokenOpenRawBlock, "{{{{", 0, 1}
|
|
||||||
var tokCloseRawBlock = Token{TokenCloseRawBlock, "}}}}", 0, 1}
|
|
||||||
var tokOpenEndRawBlock = Token{TokenOpenEndRawBlock, "{{{{/", 0, 1}
|
|
||||||
|
|
||||||
var lexTests = []lexTest{
|
|
||||||
{"empty", "", []Token{tokEOF}},
|
|
||||||
{"spaces", " \t\n", []Token{tokContent(" \t\n"), tokEOF}},
|
|
||||||
{"content", `now is the time`, []Token{tokContent(`now is the time`), tokEOF}},
|
|
||||||
|
|
||||||
{
|
|
||||||
`does not tokenizes identifier starting with true as boolean`,
|
|
||||||
`{{ foo truebar }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("truebar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`does not tokenizes identifier starting with false as boolean`,
|
|
||||||
`{{ foo falsebar }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("falsebar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes raw block`,
|
|
||||||
`{{{{foo}}}} {{{{/foo}}}}`,
|
|
||||||
[]Token{tokOpenRawBlock, tokID("foo"), tokCloseRawBlock, tokContent(" "), tokOpenEndRawBlock, tokID("foo"), tokCloseRawBlock, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes raw block with mustaches in content`,
|
|
||||||
`{{{{foo}}}}{{bar}}{{{{/foo}}}}`,
|
|
||||||
[]Token{tokOpenRawBlock, tokID("foo"), tokCloseRawBlock, tokContent("{{bar}}"), tokOpenEndRawBlock, tokID("foo"), tokCloseRawBlock, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes @../foo`,
|
|
||||||
`{{@../foo}}`,
|
|
||||||
[]Token{tokOpen, tokData, tokID(".."), tokSep("/"), tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes escaped mustaches`,
|
|
||||||
"\\{{bar}}",
|
|
||||||
[]Token{tokContent("{{bar}}"), tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes strip mustaches`,
|
|
||||||
`{{~ foo ~}}`,
|
|
||||||
[]Token{tokOpenStrip, tokID("foo"), tokCloseStrip, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes unescaped strip mustaches`,
|
|
||||||
`{{~{ foo }~}}`,
|
|
||||||
[]Token{tokOpenUnescapedStrip, tokID("foo"), tokCloseUnescapedStrip, tokEOF},
|
|
||||||
},
|
|
||||||
|
|
||||||
//
|
|
||||||
// Next tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/tokenizer.js
|
|
||||||
//
|
|
||||||
{
|
|
||||||
`tokenizes a simple mustache as "OPEN ID CLOSE"`,
|
|
||||||
`{{foo}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports unescaping with &`,
|
|
||||||
`{{&bar}}`,
|
|
||||||
[]Token{tokOpenAmp, tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports unescaping with {{{`,
|
|
||||||
`{{{bar}}}`,
|
|
||||||
[]Token{tokOpenUnescaped, tokID("bar"), tokCloseUnescaped, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaping delimiters`,
|
|
||||||
"{{foo}} \\{{bar}} {{baz}}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" "), tokContent("{{bar}} "), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaping multiple delimiters`,
|
|
||||||
"{{foo}} \\{{bar}} \\{{baz}}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" "), tokContent("{{bar}} "), tokContent("{{baz}}"), tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaping a triple stash`,
|
|
||||||
"{{foo}} \\{{{bar}}} {{baz}}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" "), tokContent("{{{bar}}} "), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaping escape character`,
|
|
||||||
"{{foo}} \\\\{{bar}} {{baz}}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" \\"), tokOpen, tokID("bar"), tokClose, tokContent(" "), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaping multiple escape characters`,
|
|
||||||
"{{foo}} \\\\{{bar}} \\\\{{baz}}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" \\"), tokOpen, tokID("bar"), tokClose, tokContent(" \\"), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaped mustaches after escaped escape characters`,
|
|
||||||
"{{foo}} \\\\{{bar}} \\{{baz}}",
|
|
||||||
// NOTE: JS implementation returns:
|
|
||||||
// ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT'],
|
|
||||||
// WTF is the last CONTENT ?
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" \\"), tokOpen, tokID("bar"), tokClose, tokContent(" "), tokContent("{{baz}}"), tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaped escape characters after escaped mustaches`,
|
|
||||||
"{{foo}} \\{{bar}} \\\\{{baz}}",
|
|
||||||
// NOTE: JS implementation returns:
|
|
||||||
// []Token{tokOpen, tokID("foo"), tokClose, tokContent(" "), tokContent("{{bar}} "), tokContent("\\"), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" "), tokContent("{{bar}} \\"), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`supports escaped escape character on a triple stash`,
|
|
||||||
"{{foo}} \\\\{{{bar}}} {{baz}}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokContent(" \\"), tokOpenUnescaped, tokID("bar"), tokCloseUnescaped, tokContent(" "), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a simple path`,
|
|
||||||
`{{foo/bar}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokSep("/"), tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`allows dot notation (1)`,
|
|
||||||
`{{foo.bar}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokSep("."), tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`allows dot notation (2)`,
|
|
||||||
`{{foo.bar.baz}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokSep("."), tokID("bar"), tokSep("."), tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`allows path literals with []`,
|
|
||||||
`{{foo.[bar]}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokSep("."), tokID("[bar]"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`allows multiple path literals on a line with []`,
|
|
||||||
`{{foo.[bar]}}{{foo.[baz]}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokSep("."), tokID("[bar]"), tokClose, tokOpen, tokID("foo"), tokSep("."), tokID("[baz]"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes {{.}} as OPEN ID CLOSE`,
|
|
||||||
`{{.}}`,
|
|
||||||
[]Token{tokOpen, tokID("."), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a path as "OPEN (ID SEP)* ID CLOSE"`,
|
|
||||||
`{{../foo/bar}}`,
|
|
||||||
[]Token{tokOpen, tokID(".."), tokSep("/"), tokID("foo"), tokSep("/"), tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a path with .. as a parent path`,
|
|
||||||
`{{../foo.bar}}`,
|
|
||||||
[]Token{tokOpen, tokID(".."), tokSep("/"), tokID("foo"), tokSep("."), tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a path with this/foo as OPEN ID SEP ID CLOSE`,
|
|
||||||
`{{this/foo}}`,
|
|
||||||
[]Token{tokOpen, tokID("this"), tokSep("/"), tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a simple mustache with spaces as "OPEN ID CLOSE"`,
|
|
||||||
`{{ foo }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a simple mustache with line breaks as "OPEN ID ID CLOSE"`,
|
|
||||||
"{{ foo \n bar }}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes raw content as "CONTENT"`,
|
|
||||||
`foo {{ bar }} baz`,
|
|
||||||
[]Token{tokContent("foo "), tokOpen, tokID("bar"), tokClose, tokContent(" baz"), tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a partial as "OPEN_PARTIAL ID CLOSE"`,
|
|
||||||
`{{> foo}}`,
|
|
||||||
[]Token{tokOpenPartial, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a partial with context as "OPEN_PARTIAL ID ID CLOSE"`,
|
|
||||||
`{{> foo bar }}`,
|
|
||||||
[]Token{tokOpenPartial, tokID("foo"), tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a partial without spaces as "OPEN_PARTIAL ID CLOSE"`,
|
|
||||||
`{{>foo}}`,
|
|
||||||
[]Token{tokOpenPartial, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"`,
|
|
||||||
`{{>foo }}`,
|
|
||||||
[]Token{tokOpenPartial, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"`,
|
|
||||||
`{{>foo/bar.baz }}`,
|
|
||||||
[]Token{tokOpenPartial, tokID("foo"), tokSep("/"), tokID("bar"), tokSep("."), tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a comment as "COMMENT"`,
|
|
||||||
`foo {{! this is a comment }} bar {{ baz }}`,
|
|
||||||
[]Token{tokContent("foo "), tokComment("{{! this is a comment }}"), tokContent(" bar "), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a block comment as "COMMENT"`,
|
|
||||||
`foo {{!-- this is a {{comment}} --}} bar {{ baz }}`,
|
|
||||||
[]Token{tokContent("foo "), tokComment("{{!-- this is a {{comment}} --}}"), tokContent(" bar "), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes a block comment with whitespace as "COMMENT"`,
|
|
||||||
"foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}",
|
|
||||||
[]Token{tokContent("foo "), tokComment("{{!-- this is a\n{{comment}}\n--}}"), tokContent(" bar "), tokOpen, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE`,
|
|
||||||
`{{#foo}}content{{/foo}}`,
|
|
||||||
[]Token{tokOpenBlock, tokID("foo"), tokClose, tokContent("content"), tokOpenEndBlock, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes inverse sections as "INVERSE"`,
|
|
||||||
`{{^}}`,
|
|
||||||
[]Token{tokInverse("{{^}}"), tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes inverse sections as "INVERSE" with alternate format`,
|
|
||||||
`{{else}}`,
|
|
||||||
[]Token{tokInverse("{{else}}"), tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes inverse sections as "INVERSE" with spaces`,
|
|
||||||
`{{ else }}`,
|
|
||||||
[]Token{tokInverse("{{ else }}"), tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes inverse sections with ID as "OPEN_INVERSE ID CLOSE"`,
|
|
||||||
`{{^foo}}`,
|
|
||||||
[]Token{tokOpenInverse, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes inverse sections with ID and spaces as "OPEN_INVERSE ID CLOSE"`,
|
|
||||||
`{{^ foo }}`,
|
|
||||||
[]Token{tokOpenInverse, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes mustaches with params as "OPEN ID ID ID CLOSE"`,
|
|
||||||
`{{ foo bar baz }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes mustaches with String params as "OPEN ID ID STRING CLOSE"`,
|
|
||||||
`{{ foo bar "baz" }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokString("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes mustaches with String params using single quotes as "OPEN ID ID STRING CLOSE"`,
|
|
||||||
`{{ foo bar 'baz' }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokString("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes String params with spaces inside as "STRING"`,
|
|
||||||
`{{ foo bar "baz bat" }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokString("baz bat"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes String params with escapes quotes as STRING`,
|
|
||||||
`{{ foo "bar\"baz" }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokString(`bar"baz`), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes String params using single quotes with escapes quotes as STRING`,
|
|
||||||
`{{ foo 'bar\'baz' }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokString(`bar'baz`), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes numbers`,
|
|
||||||
`{{ foo 1 }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokNumber("1"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes floats`,
|
|
||||||
`{{ foo 1.1 }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokNumber("1.1"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes negative numbers`,
|
|
||||||
`{{ foo -1 }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokNumber("-1"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes negative floats`,
|
|
||||||
`{{ foo -1.1 }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokNumber("-1.1"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes boolean true`,
|
|
||||||
`{{ foo true }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokBool("true"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes boolean false`,
|
|
||||||
`{{ foo false }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokBool("false"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
// SKIP: 'tokenizes undefined and null'
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (1)`,
|
|
||||||
`{{ foo bar=baz }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokEquals, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (2)`,
|
|
||||||
`{{ foo bar baz=bat }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokEquals, tokID("bat"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (3)`,
|
|
||||||
`{{ foo bar baz=1 }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokEquals, tokNumber("1"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (4)`,
|
|
||||||
`{{ foo bar baz=true }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokEquals, tokBool("true"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (5)`,
|
|
||||||
`{{ foo bar baz=false }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokEquals, tokBool("false"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (6)`,
|
|
||||||
"{{ foo bar\n baz=bat }}",
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokEquals, tokID("bat"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (7)`,
|
|
||||||
`{{ foo bar baz="bat" }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokEquals, tokString("bat"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (8)`,
|
|
||||||
`{{ foo bar baz="bat" bam=wot }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokID("baz"), tokEquals, tokString("bat"), tokID("bam"), tokEquals, tokID("wot"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes hash arguments (9)`,
|
|
||||||
`{{foo omg bar=baz bat="bam"}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("omg"), tokID("bar"), tokEquals, tokID("baz"), tokID("bat"), tokEquals, tokString("bam"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes special @ identifiers (1)`,
|
|
||||||
`{{ @foo }}`,
|
|
||||||
[]Token{tokOpen, tokData, tokID("foo"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes special @ identifiers (2)`,
|
|
||||||
`{{ foo @bar }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokData, tokID("bar"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes special @ identifiers (3)`,
|
|
||||||
`{{ foo bar=@baz }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokID("bar"), tokEquals, tokData, tokID("baz"), tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`does not time out in a mustache with a single } followed by EOF`,
|
|
||||||
`{{foo}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokError("Unexpected character in expression: '}'")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`does not time out in a mustache when invalid ID characters are used`,
|
|
||||||
`{{foo & }}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokError("Unexpected character in expression: '&'")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes subexpressions (1)`,
|
|
||||||
`{{foo (bar)}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokOpenSexpr, tokID("bar"), tokCloseSexpr, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes subexpressions (2)`,
|
|
||||||
`{{foo (a-x b-y)}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokOpenSexpr, tokID("a-x"), tokID("b-y"), tokCloseSexpr, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes nested subexpressions`,
|
|
||||||
`{{foo (bar (lol rofl)) (baz)}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokOpenSexpr, tokID("bar"), tokOpenSexpr, tokID("lol"), tokID("rofl"), tokCloseSexpr, tokCloseSexpr, tokOpenSexpr, tokID("baz"), tokCloseSexpr, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes nested subexpressions: literals`,
|
|
||||||
`{{foo (bar (lol true) false) (baz 1) (blah 'b') (blorg "c")}}`,
|
|
||||||
[]Token{tokOpen, tokID("foo"), tokOpenSexpr, tokID("bar"), tokOpenSexpr, tokID("lol"), tokBool("true"), tokCloseSexpr, tokBool("false"), tokCloseSexpr, tokOpenSexpr, tokID("baz"), tokNumber("1"), tokCloseSexpr, tokOpenSexpr, tokID("blah"), tokString("b"), tokCloseSexpr, tokOpenSexpr, tokID("blorg"), tokString("c"), tokCloseSexpr, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes block params (1)`,
|
|
||||||
`{{#foo as |bar|}}`,
|
|
||||||
[]Token{tokOpenBlock, tokID("foo"), tokOpenBlockParams, tokID("bar"), tokCloseBlockParams, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes block params (2)`,
|
|
||||||
`{{#foo as |bar baz|}}`,
|
|
||||||
[]Token{tokOpenBlock, tokID("foo"), tokOpenBlockParams, tokID("bar"), tokID("baz"), tokCloseBlockParams, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes block params (3)`,
|
|
||||||
`{{#foo as | bar baz |}}`,
|
|
||||||
[]Token{tokOpenBlock, tokID("foo"), tokOpenBlockParams, tokID("bar"), tokID("baz"), tokCloseBlockParams, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes block params (4)`,
|
|
||||||
`{{#foo as as | bar baz |}}`,
|
|
||||||
[]Token{tokOpenBlock, tokID("foo"), tokID("as"), tokOpenBlockParams, tokID("bar"), tokID("baz"), tokCloseBlockParams, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`tokenizes block params (5)`,
|
|
||||||
`{{else foo as |bar baz|}}`,
|
|
||||||
[]Token{tokOpenInverseChain, tokID("foo"), tokOpenBlockParams, tokID("bar"), tokID("baz"), tokCloseBlockParams, tokClose, tokEOF},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func collect(t *lexTest) []Token {
|
|
||||||
var result []Token
|
|
||||||
|
|
||||||
l := scanWithName(t.input, t.name)
|
|
||||||
for {
|
|
||||||
token := l.NextToken()
|
|
||||||
result = append(result, token)
|
|
||||||
|
|
||||||
if token.Kind == TokenEOF || token.Kind == TokenError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func equal(i1, i2 []Token, checkPos bool) bool {
|
|
||||||
if len(i1) != len(i2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := range i1 {
|
|
||||||
if i1[k].Kind != i2[k].Kind {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkPos && i1[k].Pos != i2[k].Pos {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if i1[k].Val != i2[k].Val {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLexer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range lexTests {
|
|
||||||
tokens := collect(&test)
|
|
||||||
if !equal(tokens, test.tokens, false) {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\nexpected\n\t%v\ngot\n\t%+v\n", test.name, test.input, test.tokens, tokens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo Test errors:
|
|
||||||
// `{{{{raw foo`
|
|
||||||
|
|
||||||
// package example
|
|
||||||
func Example() {
|
|
||||||
source := "You know {{nothing}} John Snow"
|
|
||||||
|
|
||||||
output := ""
|
|
||||||
|
|
||||||
lex := Scan(source)
|
|
||||||
for {
|
|
||||||
// consume next token
|
|
||||||
token := lex.NextToken()
|
|
||||||
|
|
||||||
output += fmt.Sprintf(" %s", token)
|
|
||||||
|
|
||||||
// stops when all tokens have been consumed, or on error
|
|
||||||
if token.Kind == TokenEOF || token.Kind == TokenError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: Content{"You know "} Open{"{{"} ID{"nothing"} Close{"}}"} Content{" John Snow"} EOF
|
|
||||||
}
|
|
183
vendor/github.com/aymerick/raymond/lexer/token.go
generated
vendored
183
vendor/github.com/aymerick/raymond/lexer/token.go
generated
vendored
|
@ -1,183 +0,0 @@
|
||||||
package lexer
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
const (
|
|
||||||
// TokenError represents an error
|
|
||||||
TokenError TokenKind = iota
|
|
||||||
|
|
||||||
// TokenEOF represents an End Of File
|
|
||||||
TokenEOF
|
|
||||||
|
|
||||||
//
|
|
||||||
// Mustache delimiters
|
|
||||||
//
|
|
||||||
|
|
||||||
// TokenOpen is the OPEN token
|
|
||||||
TokenOpen
|
|
||||||
|
|
||||||
// TokenClose is the CLOSE token
|
|
||||||
TokenClose
|
|
||||||
|
|
||||||
// TokenOpenRawBlock is the OPEN_RAW_BLOCK token
|
|
||||||
TokenOpenRawBlock
|
|
||||||
|
|
||||||
// TokenCloseRawBlock is the CLOSE_RAW_BLOCK token
|
|
||||||
TokenCloseRawBlock
|
|
||||||
|
|
||||||
// TokenOpenEndRawBlock is the END_RAW_BLOCK token
|
|
||||||
TokenOpenEndRawBlock
|
|
||||||
|
|
||||||
// TokenOpenUnescaped is the OPEN_UNESCAPED token
|
|
||||||
TokenOpenUnescaped
|
|
||||||
|
|
||||||
// TokenCloseUnescaped is the CLOSE_UNESCAPED token
|
|
||||||
TokenCloseUnescaped
|
|
||||||
|
|
||||||
// TokenOpenBlock is the OPEN_BLOCK token
|
|
||||||
TokenOpenBlock
|
|
||||||
|
|
||||||
// TokenOpenEndBlock is the OPEN_ENDBLOCK token
|
|
||||||
TokenOpenEndBlock
|
|
||||||
|
|
||||||
// TokenInverse is the INVERSE token
|
|
||||||
TokenInverse
|
|
||||||
|
|
||||||
// TokenOpenInverse is the OPEN_INVERSE token
|
|
||||||
TokenOpenInverse
|
|
||||||
|
|
||||||
// TokenOpenInverseChain is the OPEN_INVERSE_CHAIN token
|
|
||||||
TokenOpenInverseChain
|
|
||||||
|
|
||||||
// TokenOpenPartial is the OPEN_PARTIAL token
|
|
||||||
TokenOpenPartial
|
|
||||||
|
|
||||||
// TokenComment is the COMMENT token
|
|
||||||
TokenComment
|
|
||||||
|
|
||||||
//
|
|
||||||
// Inside mustaches
|
|
||||||
//
|
|
||||||
|
|
||||||
// TokenOpenSexpr is the OPEN_SEXPR token
|
|
||||||
TokenOpenSexpr
|
|
||||||
|
|
||||||
// TokenCloseSexpr is the CLOSE_SEXPR token
|
|
||||||
TokenCloseSexpr
|
|
||||||
|
|
||||||
// TokenEquals is the EQUALS token
|
|
||||||
TokenEquals
|
|
||||||
|
|
||||||
// TokenData is the DATA token
|
|
||||||
TokenData
|
|
||||||
|
|
||||||
// TokenSep is the SEP token
|
|
||||||
TokenSep
|
|
||||||
|
|
||||||
// TokenOpenBlockParams is the OPEN_BLOCK_PARAMS token
|
|
||||||
TokenOpenBlockParams
|
|
||||||
|
|
||||||
// TokenCloseBlockParams is the CLOSE_BLOCK_PARAMS token
|
|
||||||
TokenCloseBlockParams
|
|
||||||
|
|
||||||
//
|
|
||||||
// Tokens with content
|
|
||||||
//
|
|
||||||
|
|
||||||
// TokenContent is the CONTENT token
|
|
||||||
TokenContent
|
|
||||||
|
|
||||||
// TokenID is the ID token
|
|
||||||
TokenID
|
|
||||||
|
|
||||||
// TokenString is the STRING token
|
|
||||||
TokenString
|
|
||||||
|
|
||||||
// TokenNumber is the NUMBER token
|
|
||||||
TokenNumber
|
|
||||||
|
|
||||||
// TokenBoolean is the BOOLEAN token
|
|
||||||
TokenBoolean
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Option to generate token position in its string representation
|
|
||||||
dumpTokenPos = false
|
|
||||||
|
|
||||||
// Option to generate values for all token kinds for their string representations
|
|
||||||
dumpAllTokensVal = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// TokenKind represents a Token type.
|
|
||||||
type TokenKind int
|
|
||||||
|
|
||||||
// Token represents a scanned token.
|
|
||||||
type Token struct {
|
|
||||||
Kind TokenKind // Token kind
|
|
||||||
Val string // Token value
|
|
||||||
|
|
||||||
Pos int // Byte position in input string
|
|
||||||
Line int // Line number in input string
|
|
||||||
}
|
|
||||||
|
|
||||||
// tokenName permits to display token name given token type
|
|
||||||
var tokenName = map[TokenKind]string{
|
|
||||||
TokenError: "Error",
|
|
||||||
TokenEOF: "EOF",
|
|
||||||
TokenContent: "Content",
|
|
||||||
TokenComment: "Comment",
|
|
||||||
TokenOpen: "Open",
|
|
||||||
TokenClose: "Close",
|
|
||||||
TokenOpenUnescaped: "OpenUnescaped",
|
|
||||||
TokenCloseUnescaped: "CloseUnescaped",
|
|
||||||
TokenOpenBlock: "OpenBlock",
|
|
||||||
TokenOpenEndBlock: "OpenEndBlock",
|
|
||||||
TokenOpenRawBlock: "OpenRawBlock",
|
|
||||||
TokenCloseRawBlock: "CloseRawBlock",
|
|
||||||
TokenOpenEndRawBlock: "OpenEndRawBlock",
|
|
||||||
TokenOpenBlockParams: "OpenBlockParams",
|
|
||||||
TokenCloseBlockParams: "CloseBlockParams",
|
|
||||||
TokenInverse: "Inverse",
|
|
||||||
TokenOpenInverse: "OpenInverse",
|
|
||||||
TokenOpenInverseChain: "OpenInverseChain",
|
|
||||||
TokenOpenPartial: "OpenPartial",
|
|
||||||
TokenOpenSexpr: "OpenSexpr",
|
|
||||||
TokenCloseSexpr: "CloseSexpr",
|
|
||||||
TokenID: "ID",
|
|
||||||
TokenEquals: "Equals",
|
|
||||||
TokenString: "String",
|
|
||||||
TokenNumber: "Number",
|
|
||||||
TokenBoolean: "Boolean",
|
|
||||||
TokenData: "Data",
|
|
||||||
TokenSep: "Sep",
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the token kind string representation for debugging.
|
|
||||||
func (k TokenKind) String() string {
|
|
||||||
s := tokenName[k]
|
|
||||||
if s == "" {
|
|
||||||
return fmt.Sprintf("Token-%d", int(k))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the token string representation for debugging.
|
|
||||||
func (t Token) String() string {
|
|
||||||
result := ""
|
|
||||||
|
|
||||||
if dumpTokenPos {
|
|
||||||
result += fmt.Sprintf("%d:", t.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
result += fmt.Sprintf("%s", t.Kind)
|
|
||||||
|
|
||||||
if (dumpAllTokensVal || (t.Kind >= TokenContent)) && len(t.Val) > 0 {
|
|
||||||
if len(t.Val) > 100 {
|
|
||||||
result += fmt.Sprintf("{%.20q...}", t.Val)
|
|
||||||
} else {
|
|
||||||
result += fmt.Sprintf("{%q}", t.Val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
234
vendor/github.com/aymerick/raymond/mustache_test.go
generated
vendored
234
vendor/github.com/aymerick/raymond/mustache_test.go
generated
vendored
|
@ -1,234 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Note, as the JS implementation, the divergences from mustache spec:
|
|
||||||
// - we don't support alternative delimeters
|
|
||||||
// - the mustache lambda spec differs
|
|
||||||
//
|
|
||||||
|
|
||||||
type mustacheTest struct {
|
|
||||||
Name string
|
|
||||||
Desc string
|
|
||||||
Data interface{}
|
|
||||||
Template string
|
|
||||||
Expected string
|
|
||||||
Partials map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type mustacheTestFile struct {
|
|
||||||
Overview string
|
|
||||||
Tests []mustacheTest
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
rAltDelim = regexp.MustCompile(regexp.QuoteMeta("{{="))
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
musTestLambdaInterMult = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMustache(t *testing.T) {
|
|
||||||
skipFiles := map[string]bool{
|
|
||||||
// mustache lambdas differ from handlebars lambdas
|
|
||||||
"~lambdas.yml": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fileName := range mustacheTestFiles() {
|
|
||||||
if skipFiles[fileName] {
|
|
||||||
// fmt.Printf("Skipped file: %s\n", fileName)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
launchTests(t, testsFromMustacheFile(fileName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testsFromMustacheFile(fileName string) []Test {
|
|
||||||
result := []Test{}
|
|
||||||
|
|
||||||
fileData, err := ioutil.ReadFile(path.Join("mustache", "specs", fileName))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var testFile mustacheTestFile
|
|
||||||
if err := yaml.Unmarshal(fileData, &testFile); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, mustacheTest := range testFile.Tests {
|
|
||||||
if mustBeSkipped(mustacheTest, fileName) {
|
|
||||||
// fmt.Printf("Skipped test: %s\n", mustacheTest.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
test := Test{
|
|
||||||
name: mustacheTest.Name,
|
|
||||||
input: mustacheTest.Template,
|
|
||||||
data: mustacheTest.Data,
|
|
||||||
partials: mustacheTest.Partials,
|
|
||||||
output: mustacheTest.Expected,
|
|
||||||
}
|
|
||||||
|
|
||||||
result = append(result, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if test must be skipped
|
|
||||||
func mustBeSkipped(test mustacheTest, fileName string) bool {
|
|
||||||
// handlebars does not support alternative delimiters
|
|
||||||
return haveAltDelimiter(test) ||
|
|
||||||
// the JS implementation skips those tests
|
|
||||||
fileName == "partials.yml" && (test.Name == "Failed Lookup" || test.Name == "Standalone Indentation")
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if test have alternative delimeter in template or in partials
|
|
||||||
func haveAltDelimiter(test mustacheTest) bool {
|
|
||||||
// check template
|
|
||||||
if rAltDelim.MatchString(test.Template) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// check partials
|
|
||||||
for _, partial := range test.Partials {
|
|
||||||
if rAltDelim.MatchString(partial) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustacheTestFiles() []string {
|
|
||||||
var result []string
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(path.Join("mustache", "specs"))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
fileName := file.Name()
|
|
||||||
|
|
||||||
if !file.IsDir() && strings.HasSuffix(fileName, ".yml") {
|
|
||||||
result = append(result, fileName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Following tests come fron ~lambdas.yml
|
|
||||||
//
|
|
||||||
|
|
||||||
var mustacheLambdasTests = []Test{
|
|
||||||
{
|
|
||||||
"Interpolation",
|
|
||||||
"Hello, {{lambda}}!",
|
|
||||||
map[string]interface{}{"lambda": func() string { return "world" }},
|
|
||||||
nil, nil, nil,
|
|
||||||
"Hello, world!",
|
|
||||||
},
|
|
||||||
|
|
||||||
// // SKIP: lambda return value is not parsed
|
|
||||||
// {
|
|
||||||
// "Interpolation - Expansion",
|
|
||||||
// "Hello, {{lambda}}!",
|
|
||||||
// map[string]interface{}{"lambda": func() string { return "{{planet}}" }},
|
|
||||||
// nil, nil, nil,
|
|
||||||
// "Hello, world!",
|
|
||||||
// },
|
|
||||||
|
|
||||||
// SKIP "Interpolation - Alternate Delimiters"
|
|
||||||
|
|
||||||
{
|
|
||||||
"Interpolation - Multiple Calls",
|
|
||||||
"{{lambda}} == {{{lambda}}} == {{lambda}}",
|
|
||||||
map[string]interface{}{"lambda": func() string {
|
|
||||||
musTestLambdaInterMult++
|
|
||||||
return Str(musTestLambdaInterMult)
|
|
||||||
}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"1 == 2 == 3",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"Escaping",
|
|
||||||
"<{{lambda}}{{{lambda}}}",
|
|
||||||
map[string]interface{}{"lambda": func() string { return ">" }},
|
|
||||||
nil, nil, nil,
|
|
||||||
"<>>",
|
|
||||||
},
|
|
||||||
|
|
||||||
// // SKIP: "Lambdas used for sections should receive the raw section string."
|
|
||||||
// {
|
|
||||||
// "Section",
|
|
||||||
// "<{{#lambda}}{{x}}{{/lambda}}>",
|
|
||||||
// map[string]interface{}{"lambda": func(param string) string {
|
|
||||||
// if param == "{{x}}" {
|
|
||||||
// return "yes"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return "false"
|
|
||||||
// }, "x": "Error!"},
|
|
||||||
// nil, nil, nil,
|
|
||||||
// "<yes>",
|
|
||||||
// },
|
|
||||||
|
|
||||||
// // SKIP: lambda return value is not parsed
|
|
||||||
// {
|
|
||||||
// "Section - Expansion",
|
|
||||||
// "<{{#lambda}}-{{/lambda}}>",
|
|
||||||
// map[string]interface{}{"lambda": func(param string) string {
|
|
||||||
// return param + "{{planet}}" + param
|
|
||||||
// }, "planet": "Earth"},
|
|
||||||
// nil, nil, nil,
|
|
||||||
// "<-Earth->",
|
|
||||||
// },
|
|
||||||
|
|
||||||
// SKIP: "Section - Alternate Delimiters"
|
|
||||||
|
|
||||||
{
|
|
||||||
"Section - Multiple Calls",
|
|
||||||
"{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}",
|
|
||||||
map[string]interface{}{"lambda": func(options *Options) string {
|
|
||||||
return "__" + options.Fn() + "__"
|
|
||||||
}},
|
|
||||||
nil, nil, nil,
|
|
||||||
"__FILE__ != __LINE__",
|
|
||||||
},
|
|
||||||
|
|
||||||
// // SKIP: "Lambdas used for inverted sections should be considered truthy."
|
|
||||||
// {
|
|
||||||
// "Inverted Section",
|
|
||||||
// "<{{^lambda}}{{static}}{{/lambda}}>",
|
|
||||||
// map[string]interface{}{
|
|
||||||
// "lambda": func() interface{} {
|
|
||||||
// return false
|
|
||||||
// },
|
|
||||||
// "static": "static",
|
|
||||||
// },
|
|
||||||
// nil, nil, nil,
|
|
||||||
// "<>",
|
|
||||||
// },
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMustacheLambdas(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
launchTests(t, mustacheLambdasTests)
|
|
||||||
}
|
|
846
vendor/github.com/aymerick/raymond/parser/parser.go
generated
vendored
846
vendor/github.com/aymerick/raymond/parser/parser.go
generated
vendored
|
@ -1,846 +0,0 @@
|
||||||
// Package parser provides a handlebars syntax analyser. It consumes the tokens provided by the lexer to build an AST.
|
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond/ast"
|
|
||||||
"github.com/aymerick/raymond/lexer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// References:
|
|
||||||
// - https://github.com/wycats/handlebars.js/blob/master/src/handlebars.yy
|
|
||||||
// - https://github.com/golang/go/blob/master/src/text/template/parse/parse.go
|
|
||||||
|
|
||||||
// parser is a syntax analyzer.
|
|
||||||
type parser struct {
|
|
||||||
// Lexer
|
|
||||||
lex *lexer.Lexer
|
|
||||||
|
|
||||||
// Root node
|
|
||||||
root ast.Node
|
|
||||||
|
|
||||||
// Tokens parsed but not consumed yet
|
|
||||||
tokens []*lexer.Token
|
|
||||||
|
|
||||||
// All tokens have been retreieved from lexer
|
|
||||||
lexOver bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
rOpenComment = regexp.MustCompile(`^\{\{~?!-?-?`)
|
|
||||||
rCloseComment = regexp.MustCompile(`-?-?~?\}\}$`)
|
|
||||||
rOpenAmp = regexp.MustCompile(`^\{\{~?&`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// new instanciates a new parser
|
|
||||||
func new(input string) *parser {
|
|
||||||
return &parser{
|
|
||||||
lex: lexer.Scan(input),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse analyzes given input and returns the AST root node.
|
|
||||||
func Parse(input string) (result *ast.Program, err error) {
|
|
||||||
// recover error
|
|
||||||
defer errRecover(&err)
|
|
||||||
|
|
||||||
parser := new(input)
|
|
||||||
|
|
||||||
// parse
|
|
||||||
result = parser.parseProgram()
|
|
||||||
|
|
||||||
// check last token
|
|
||||||
token := parser.shift()
|
|
||||||
if token.Kind != lexer.TokenEOF {
|
|
||||||
// Parsing ended before EOF
|
|
||||||
errToken(token, "Syntax error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix whitespaces
|
|
||||||
processWhitespaces(result)
|
|
||||||
|
|
||||||
// named returned values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// errRecover recovers parsing panic
|
|
||||||
func errRecover(errp *error) {
|
|
||||||
e := recover()
|
|
||||||
if e != nil {
|
|
||||||
switch err := e.(type) {
|
|
||||||
case runtime.Error:
|
|
||||||
panic(e)
|
|
||||||
case error:
|
|
||||||
*errp = err
|
|
||||||
default:
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// errPanic panics
|
|
||||||
func errPanic(err error, line int) {
|
|
||||||
panic(fmt.Errorf("Parse error on line %d:\n%s", line, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// errNode panics with given node infos
|
|
||||||
func errNode(node ast.Node, msg string) {
|
|
||||||
errPanic(fmt.Errorf("%s\nNode: %s", msg, node), node.Location().Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
// errNode panics with given Token infos
|
|
||||||
func errToken(tok *lexer.Token, msg string) {
|
|
||||||
errPanic(fmt.Errorf("%s\nToken: %s", msg, tok), tok.Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
// errNode panics because of an unexpected Token kind
|
|
||||||
func errExpected(expect lexer.TokenKind, tok *lexer.Token) {
|
|
||||||
errPanic(fmt.Errorf("Expecting %s, got: '%s'", expect, tok), tok.Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
// program : statement*
|
|
||||||
func (p *parser) parseProgram() *ast.Program {
|
|
||||||
result := ast.NewProgram(p.next().Pos, p.next().Line)
|
|
||||||
|
|
||||||
for p.isStatement() {
|
|
||||||
result.AddStatement(p.parseStatement())
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// statement : mustache | block | rawBlock | partial | content | COMMENT
|
|
||||||
func (p *parser) parseStatement() ast.Node {
|
|
||||||
var result ast.Node
|
|
||||||
|
|
||||||
tok := p.next()
|
|
||||||
|
|
||||||
switch tok.Kind {
|
|
||||||
case lexer.TokenOpen, lexer.TokenOpenUnescaped:
|
|
||||||
// mustache
|
|
||||||
result = p.parseMustache()
|
|
||||||
case lexer.TokenOpenBlock:
|
|
||||||
// block
|
|
||||||
result = p.parseBlock()
|
|
||||||
case lexer.TokenOpenInverse:
|
|
||||||
// block
|
|
||||||
result = p.parseInverse()
|
|
||||||
case lexer.TokenOpenRawBlock:
|
|
||||||
// rawBlock
|
|
||||||
result = p.parseRawBlock()
|
|
||||||
case lexer.TokenOpenPartial:
|
|
||||||
// partial
|
|
||||||
result = p.parsePartial()
|
|
||||||
case lexer.TokenContent:
|
|
||||||
// content
|
|
||||||
result = p.parseContent()
|
|
||||||
case lexer.TokenComment:
|
|
||||||
// COMMENT
|
|
||||||
result = p.parseComment()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// isStatement returns true if next token starts a statement
|
|
||||||
func (p *parser) isStatement() bool {
|
|
||||||
if !p.have(1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch p.next().Kind {
|
|
||||||
case lexer.TokenOpen, lexer.TokenOpenUnescaped, lexer.TokenOpenBlock,
|
|
||||||
lexer.TokenOpenInverse, lexer.TokenOpenRawBlock, lexer.TokenOpenPartial,
|
|
||||||
lexer.TokenContent, lexer.TokenComment:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// content : CONTENT
|
|
||||||
func (p *parser) parseContent() *ast.ContentStatement {
|
|
||||||
// CONTENT
|
|
||||||
tok := p.shift()
|
|
||||||
if tok.Kind != lexer.TokenContent {
|
|
||||||
// @todo This check can be removed if content is optional in a raw block
|
|
||||||
errExpected(lexer.TokenContent, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ast.NewContentStatement(tok.Pos, tok.Line, tok.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// COMMENT
|
|
||||||
func (p *parser) parseComment() *ast.CommentStatement {
|
|
||||||
// COMMENT
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
value := rOpenComment.ReplaceAllString(tok.Val, "")
|
|
||||||
value = rCloseComment.ReplaceAllString(value, "")
|
|
||||||
|
|
||||||
result := ast.NewCommentStatement(tok.Pos, tok.Line, value)
|
|
||||||
result.Strip = ast.NewStripForStr(tok.Val)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// param* hash?
|
|
||||||
func (p *parser) parseExpressionParamsHash() ([]ast.Node, *ast.Hash) {
|
|
||||||
var params []ast.Node
|
|
||||||
var hash *ast.Hash
|
|
||||||
|
|
||||||
// params*
|
|
||||||
if p.isParam() {
|
|
||||||
params = p.parseParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash?
|
|
||||||
if p.isHashSegment() {
|
|
||||||
hash = p.parseHash()
|
|
||||||
}
|
|
||||||
|
|
||||||
return params, hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperName param* hash?
|
|
||||||
func (p *parser) parseExpression(tok *lexer.Token) *ast.Expression {
|
|
||||||
result := ast.NewExpression(tok.Pos, tok.Line)
|
|
||||||
|
|
||||||
// helperName
|
|
||||||
result.Path = p.parseHelperName()
|
|
||||||
|
|
||||||
// param* hash?
|
|
||||||
result.Params, result.Hash = p.parseExpressionParamsHash()
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawBlock : openRawBlock content endRawBlock
|
|
||||||
// openRawBlock : OPEN_RAW_BLOCK helperName param* hash? CLOSE_RAW_BLOCK
|
|
||||||
// endRawBlock : OPEN_END_RAW_BLOCK helperName CLOSE_RAW_BLOCK
|
|
||||||
func (p *parser) parseRawBlock() *ast.BlockStatement {
|
|
||||||
// OPEN_RAW_BLOCK
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
result := ast.NewBlockStatement(tok.Pos, tok.Line)
|
|
||||||
|
|
||||||
// helperName param* hash?
|
|
||||||
result.Expression = p.parseExpression(tok)
|
|
||||||
|
|
||||||
openName := result.Expression.Canonical()
|
|
||||||
|
|
||||||
// CLOSE_RAW_BLOCK
|
|
||||||
tok = p.shift()
|
|
||||||
if tok.Kind != lexer.TokenCloseRawBlock {
|
|
||||||
errExpected(lexer.TokenCloseRawBlock, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
// content
|
|
||||||
// @todo Is content mandatory in a raw block ?
|
|
||||||
content := p.parseContent()
|
|
||||||
|
|
||||||
program := ast.NewProgram(tok.Pos, tok.Line)
|
|
||||||
program.AddStatement(content)
|
|
||||||
|
|
||||||
result.Program = program
|
|
||||||
|
|
||||||
// OPEN_END_RAW_BLOCK
|
|
||||||
tok = p.shift()
|
|
||||||
if tok.Kind != lexer.TokenOpenEndRawBlock {
|
|
||||||
// should never happen as it is caught by lexer
|
|
||||||
errExpected(lexer.TokenOpenEndRawBlock, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperName
|
|
||||||
endID := p.parseHelperName()
|
|
||||||
|
|
||||||
closeName, ok := ast.HelperNameStr(endID)
|
|
||||||
if !ok {
|
|
||||||
errNode(endID, "Erroneous closing expression")
|
|
||||||
}
|
|
||||||
|
|
||||||
if openName != closeName {
|
|
||||||
errNode(endID, fmt.Sprintf("%s doesn't match %s", openName, closeName))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CLOSE_RAW_BLOCK
|
|
||||||
tok = p.shift()
|
|
||||||
if tok.Kind != lexer.TokenCloseRawBlock {
|
|
||||||
errExpected(lexer.TokenCloseRawBlock, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// block : openBlock program inverseChain? closeBlock
|
|
||||||
func (p *parser) parseBlock() *ast.BlockStatement {
|
|
||||||
// openBlock
|
|
||||||
result, blockParams := p.parseOpenBlock()
|
|
||||||
|
|
||||||
// program
|
|
||||||
program := p.parseProgram()
|
|
||||||
program.BlockParams = blockParams
|
|
||||||
result.Program = program
|
|
||||||
|
|
||||||
// inverseChain?
|
|
||||||
if p.isInverseChain() {
|
|
||||||
result.Inverse = p.parseInverseChain()
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeBlock
|
|
||||||
p.parseCloseBlock(result)
|
|
||||||
|
|
||||||
setBlockInverseStrip(result)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// setBlockInverseStrip is called when parsing `block` (openBlock | openInverse) and `inverseChain`
|
|
||||||
//
|
|
||||||
// TODO: This was totally cargo culted ! CHECK THAT !
|
|
||||||
//
|
|
||||||
// cf. prepareBlock() in:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/compiler/helper.js
|
|
||||||
func setBlockInverseStrip(block *ast.BlockStatement) {
|
|
||||||
if block.Inverse == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.Inverse.Chained {
|
|
||||||
b, _ := block.Inverse.Body[0].(*ast.BlockStatement)
|
|
||||||
b.CloseStrip = block.CloseStrip
|
|
||||||
}
|
|
||||||
|
|
||||||
block.InverseStrip = block.Inverse.Strip
|
|
||||||
}
|
|
||||||
|
|
||||||
// block : openInverse program inverseAndProgram? closeBlock
|
|
||||||
func (p *parser) parseInverse() *ast.BlockStatement {
|
|
||||||
// openInverse
|
|
||||||
result, blockParams := p.parseOpenBlock()
|
|
||||||
|
|
||||||
// program
|
|
||||||
program := p.parseProgram()
|
|
||||||
|
|
||||||
program.BlockParams = blockParams
|
|
||||||
result.Inverse = program
|
|
||||||
|
|
||||||
// inverseAndProgram?
|
|
||||||
if p.isInverse() {
|
|
||||||
result.Program = p.parseInverseAndProgram()
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeBlock
|
|
||||||
p.parseCloseBlock(result)
|
|
||||||
|
|
||||||
setBlockInverseStrip(result)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperName param* hash? blockParams?
|
|
||||||
func (p *parser) parseOpenBlockExpression(tok *lexer.Token) (*ast.BlockStatement, []string) {
|
|
||||||
var blockParams []string
|
|
||||||
|
|
||||||
result := ast.NewBlockStatement(tok.Pos, tok.Line)
|
|
||||||
|
|
||||||
// helperName param* hash?
|
|
||||||
result.Expression = p.parseExpression(tok)
|
|
||||||
|
|
||||||
// blockParams?
|
|
||||||
if p.isBlockParams() {
|
|
||||||
blockParams = p.parseBlockParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
// named returned values
|
|
||||||
return result, blockParams
|
|
||||||
}
|
|
||||||
|
|
||||||
// inverseChain : openInverseChain program inverseChain?
|
|
||||||
// | inverseAndProgram
|
|
||||||
func (p *parser) parseInverseChain() *ast.Program {
|
|
||||||
if p.isInverse() {
|
|
||||||
// inverseAndProgram
|
|
||||||
return p.parseInverseAndProgram()
|
|
||||||
}
|
|
||||||
|
|
||||||
result := ast.NewProgram(p.next().Pos, p.next().Line)
|
|
||||||
|
|
||||||
// openInverseChain
|
|
||||||
block, blockParams := p.parseOpenBlock()
|
|
||||||
|
|
||||||
// program
|
|
||||||
program := p.parseProgram()
|
|
||||||
|
|
||||||
program.BlockParams = blockParams
|
|
||||||
block.Program = program
|
|
||||||
|
|
||||||
// inverseChain?
|
|
||||||
if p.isInverseChain() {
|
|
||||||
block.Inverse = p.parseInverseChain()
|
|
||||||
}
|
|
||||||
|
|
||||||
setBlockInverseStrip(block)
|
|
||||||
|
|
||||||
result.Chained = true
|
|
||||||
result.AddStatement(block)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if current token starts an inverse chain
|
|
||||||
func (p *parser) isInverseChain() bool {
|
|
||||||
return p.isOpenInverseChain() || p.isInverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
// inverseAndProgram : INVERSE program
|
|
||||||
func (p *parser) parseInverseAndProgram() *ast.Program {
|
|
||||||
// INVERSE
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
// program
|
|
||||||
result := p.parseProgram()
|
|
||||||
result.Strip = ast.NewStripForStr(tok.Val)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// openBlock : OPEN_BLOCK helperName param* hash? blockParams? CLOSE
|
|
||||||
// openInverse : OPEN_INVERSE helperName param* hash? blockParams? CLOSE
|
|
||||||
// openInverseChain: OPEN_INVERSE_CHAIN helperName param* hash? blockParams? CLOSE
|
|
||||||
func (p *parser) parseOpenBlock() (*ast.BlockStatement, []string) {
|
|
||||||
// OPEN_BLOCK | OPEN_INVERSE | OPEN_INVERSE_CHAIN
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
// helperName param* hash? blockParams?
|
|
||||||
result, blockParams := p.parseOpenBlockExpression(tok)
|
|
||||||
|
|
||||||
// CLOSE
|
|
||||||
tokClose := p.shift()
|
|
||||||
if tokClose.Kind != lexer.TokenClose {
|
|
||||||
errExpected(lexer.TokenClose, tokClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.OpenStrip = ast.NewStrip(tok.Val, tokClose.Val)
|
|
||||||
|
|
||||||
// named returned values
|
|
||||||
return result, blockParams
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeBlock : OPEN_ENDBLOCK helperName CLOSE
|
|
||||||
func (p *parser) parseCloseBlock(block *ast.BlockStatement) {
|
|
||||||
// OPEN_ENDBLOCK
|
|
||||||
tok := p.shift()
|
|
||||||
if tok.Kind != lexer.TokenOpenEndBlock {
|
|
||||||
errExpected(lexer.TokenOpenEndBlock, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperName
|
|
||||||
endID := p.parseHelperName()
|
|
||||||
|
|
||||||
closeName, ok := ast.HelperNameStr(endID)
|
|
||||||
if !ok {
|
|
||||||
errNode(endID, "Erroneous closing expression")
|
|
||||||
}
|
|
||||||
|
|
||||||
openName := block.Expression.Canonical()
|
|
||||||
if openName != closeName {
|
|
||||||
errNode(endID, fmt.Sprintf("%s doesn't match %s", openName, closeName))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CLOSE
|
|
||||||
tokClose := p.shift()
|
|
||||||
if tokClose.Kind != lexer.TokenClose {
|
|
||||||
errExpected(lexer.TokenClose, tokClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
block.CloseStrip = ast.NewStrip(tok.Val, tokClose.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mustache : OPEN helperName param* hash? CLOSE
|
|
||||||
// | OPEN_UNESCAPED helperName param* hash? CLOSE_UNESCAPED
|
|
||||||
func (p *parser) parseMustache() *ast.MustacheStatement {
|
|
||||||
// OPEN | OPEN_UNESCAPED
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
closeToken := lexer.TokenClose
|
|
||||||
if tok.Kind == lexer.TokenOpenUnescaped {
|
|
||||||
closeToken = lexer.TokenCloseUnescaped
|
|
||||||
}
|
|
||||||
|
|
||||||
unescaped := false
|
|
||||||
if (tok.Kind == lexer.TokenOpenUnescaped) || (rOpenAmp.MatchString(tok.Val)) {
|
|
||||||
unescaped = true
|
|
||||||
}
|
|
||||||
|
|
||||||
result := ast.NewMustacheStatement(tok.Pos, tok.Line, unescaped)
|
|
||||||
|
|
||||||
// helperName param* hash?
|
|
||||||
result.Expression = p.parseExpression(tok)
|
|
||||||
|
|
||||||
// CLOSE | CLOSE_UNESCAPED
|
|
||||||
tokClose := p.shift()
|
|
||||||
if tokClose.Kind != closeToken {
|
|
||||||
errExpected(closeToken, tokClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Strip = ast.NewStrip(tok.Val, tokClose.Val)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// partial : OPEN_PARTIAL partialName param* hash? CLOSE
|
|
||||||
func (p *parser) parsePartial() *ast.PartialStatement {
|
|
||||||
// OPEN_PARTIAL
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
result := ast.NewPartialStatement(tok.Pos, tok.Line)
|
|
||||||
|
|
||||||
// partialName
|
|
||||||
result.Name = p.parsePartialName()
|
|
||||||
|
|
||||||
// param* hash?
|
|
||||||
result.Params, result.Hash = p.parseExpressionParamsHash()
|
|
||||||
|
|
||||||
// CLOSE
|
|
||||||
tokClose := p.shift()
|
|
||||||
if tokClose.Kind != lexer.TokenClose {
|
|
||||||
errExpected(lexer.TokenClose, tokClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Strip = ast.NewStrip(tok.Val, tokClose.Val)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperName | sexpr
|
|
||||||
func (p *parser) parseHelperNameOrSexpr() ast.Node {
|
|
||||||
if p.isSexpr() {
|
|
||||||
// sexpr
|
|
||||||
return p.parseSexpr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperName
|
|
||||||
return p.parseHelperName()
|
|
||||||
}
|
|
||||||
|
|
||||||
// param : helperName | sexpr
|
|
||||||
func (p *parser) parseParam() ast.Node {
|
|
||||||
return p.parseHelperNameOrSexpr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if next tokens represent a `param`
|
|
||||||
func (p *parser) isParam() bool {
|
|
||||||
return (p.isSexpr() || p.isHelperName()) && !p.isHashSegment()
|
|
||||||
}
|
|
||||||
|
|
||||||
// param*
|
|
||||||
func (p *parser) parseParams() []ast.Node {
|
|
||||||
var result []ast.Node
|
|
||||||
|
|
||||||
for p.isParam() {
|
|
||||||
result = append(result, p.parseParam())
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// sexpr : OPEN_SEXPR helperName param* hash? CLOSE_SEXPR
|
|
||||||
func (p *parser) parseSexpr() *ast.SubExpression {
|
|
||||||
// OPEN_SEXPR
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
result := ast.NewSubExpression(tok.Pos, tok.Line)
|
|
||||||
|
|
||||||
// helperName param* hash?
|
|
||||||
result.Expression = p.parseExpression(tok)
|
|
||||||
|
|
||||||
// CLOSE_SEXPR
|
|
||||||
tok = p.shift()
|
|
||||||
if tok.Kind != lexer.TokenCloseSexpr {
|
|
||||||
errExpected(lexer.TokenCloseSexpr, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash : hashSegment+
|
|
||||||
func (p *parser) parseHash() *ast.Hash {
|
|
||||||
var pairs []*ast.HashPair
|
|
||||||
|
|
||||||
for p.isHashSegment() {
|
|
||||||
pairs = append(pairs, p.parseHashSegment())
|
|
||||||
}
|
|
||||||
|
|
||||||
firstLoc := pairs[0].Location()
|
|
||||||
|
|
||||||
result := ast.NewHash(firstLoc.Pos, firstLoc.Line)
|
|
||||||
result.Pairs = pairs
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if next tokens represents a `hashSegment`
|
|
||||||
func (p *parser) isHashSegment() bool {
|
|
||||||
return p.have(2) && (p.next().Kind == lexer.TokenID) && (p.nextAt(1).Kind == lexer.TokenEquals)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashSegment : ID EQUALS param
|
|
||||||
func (p *parser) parseHashSegment() *ast.HashPair {
|
|
||||||
// ID
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
// EQUALS
|
|
||||||
p.shift()
|
|
||||||
|
|
||||||
// param
|
|
||||||
param := p.parseParam()
|
|
||||||
|
|
||||||
result := ast.NewHashPair(tok.Pos, tok.Line)
|
|
||||||
result.Key = tok.Val
|
|
||||||
result.Val = param
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockParams : OPEN_BLOCK_PARAMS ID+ CLOSE_BLOCK_PARAMS
|
|
||||||
func (p *parser) parseBlockParams() []string {
|
|
||||||
var result []string
|
|
||||||
|
|
||||||
// OPEN_BLOCK_PARAMS
|
|
||||||
tok := p.shift()
|
|
||||||
|
|
||||||
// ID+
|
|
||||||
for p.isID() {
|
|
||||||
result = append(result, p.shift().Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(result) == 0 {
|
|
||||||
errExpected(lexer.TokenID, p.next())
|
|
||||||
}
|
|
||||||
|
|
||||||
// CLOSE_BLOCK_PARAMS
|
|
||||||
tok = p.shift()
|
|
||||||
if tok.Kind != lexer.TokenCloseBlockParams {
|
|
||||||
errExpected(lexer.TokenCloseBlockParams, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// helperName : path | dataName | STRING | NUMBER | BOOLEAN | UNDEFINED | NULL
|
|
||||||
func (p *parser) parseHelperName() ast.Node {
|
|
||||||
var result ast.Node
|
|
||||||
|
|
||||||
tok := p.next()
|
|
||||||
|
|
||||||
switch tok.Kind {
|
|
||||||
case lexer.TokenBoolean:
|
|
||||||
// BOOLEAN
|
|
||||||
p.shift()
|
|
||||||
result = ast.NewBooleanLiteral(tok.Pos, tok.Line, (tok.Val == "true"), tok.Val)
|
|
||||||
case lexer.TokenNumber:
|
|
||||||
// NUMBER
|
|
||||||
p.shift()
|
|
||||||
|
|
||||||
val, isInt := parseNumber(tok)
|
|
||||||
result = ast.NewNumberLiteral(tok.Pos, tok.Line, val, isInt, tok.Val)
|
|
||||||
case lexer.TokenString:
|
|
||||||
// STRING
|
|
||||||
p.shift()
|
|
||||||
result = ast.NewStringLiteral(tok.Pos, tok.Line, tok.Val)
|
|
||||||
case lexer.TokenData:
|
|
||||||
// dataName
|
|
||||||
result = p.parseDataName()
|
|
||||||
default:
|
|
||||||
// path
|
|
||||||
result = p.parsePath(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseNumber parses a number
|
|
||||||
func parseNumber(tok *lexer.Token) (result float64, isInt bool) {
|
|
||||||
var valInt int
|
|
||||||
var err error
|
|
||||||
|
|
||||||
valInt, err = strconv.Atoi(tok.Val)
|
|
||||||
if err == nil {
|
|
||||||
isInt = true
|
|
||||||
|
|
||||||
result = float64(valInt)
|
|
||||||
} else {
|
|
||||||
isInt = false
|
|
||||||
|
|
||||||
result, err = strconv.ParseFloat(tok.Val, 64)
|
|
||||||
if err != nil {
|
|
||||||
errToken(tok, fmt.Sprintf("Failed to parse number: %s", tok.Val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// named returned values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if next tokens represent a `helperName`
|
|
||||||
func (p *parser) isHelperName() bool {
|
|
||||||
switch p.next().Kind {
|
|
||||||
case lexer.TokenBoolean, lexer.TokenNumber, lexer.TokenString, lexer.TokenData, lexer.TokenID:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// partialName : helperName | sexpr
|
|
||||||
func (p *parser) parsePartialName() ast.Node {
|
|
||||||
return p.parseHelperNameOrSexpr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// dataName : DATA pathSegments
|
|
||||||
func (p *parser) parseDataName() *ast.PathExpression {
|
|
||||||
// DATA
|
|
||||||
p.shift()
|
|
||||||
|
|
||||||
// pathSegments
|
|
||||||
return p.parsePath(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// path : pathSegments
|
|
||||||
// pathSegments : pathSegments SEP ID
|
|
||||||
// | ID
|
|
||||||
func (p *parser) parsePath(data bool) *ast.PathExpression {
|
|
||||||
var tok *lexer.Token
|
|
||||||
|
|
||||||
// ID
|
|
||||||
tok = p.shift()
|
|
||||||
if tok.Kind != lexer.TokenID {
|
|
||||||
errExpected(lexer.TokenID, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := ast.NewPathExpression(tok.Pos, tok.Line, data)
|
|
||||||
result.Part(tok.Val)
|
|
||||||
|
|
||||||
for p.isPathSep() {
|
|
||||||
// SEP
|
|
||||||
tok = p.shift()
|
|
||||||
result.Sep(tok.Val)
|
|
||||||
|
|
||||||
// ID
|
|
||||||
tok = p.shift()
|
|
||||||
if tok.Kind != lexer.TokenID {
|
|
||||||
errExpected(lexer.TokenID, tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Part(tok.Val)
|
|
||||||
|
|
||||||
if len(result.Parts) > 0 {
|
|
||||||
switch tok.Val {
|
|
||||||
case "..", ".", "this":
|
|
||||||
errToken(tok, "Invalid path: "+result.Original)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensures there is token to parse at given index
|
|
||||||
func (p *parser) ensure(index int) {
|
|
||||||
if p.lexOver {
|
|
||||||
// nothing more to grab
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nb := index + 1
|
|
||||||
|
|
||||||
for len(p.tokens) < nb {
|
|
||||||
// fetch next token
|
|
||||||
tok := p.lex.NextToken()
|
|
||||||
|
|
||||||
// queue it
|
|
||||||
p.tokens = append(p.tokens, &tok)
|
|
||||||
|
|
||||||
if (tok.Kind == lexer.TokenEOF) || (tok.Kind == lexer.TokenError) {
|
|
||||||
p.lexOver = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// have returns true is there are a list given number of tokens to consume left
|
|
||||||
func (p *parser) have(nb int) bool {
|
|
||||||
p.ensure(nb - 1)
|
|
||||||
|
|
||||||
return len(p.tokens) >= nb
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextAt returns next token at given index, without consuming it
|
|
||||||
func (p *parser) nextAt(index int) *lexer.Token {
|
|
||||||
p.ensure(index)
|
|
||||||
|
|
||||||
return p.tokens[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
// next returns next token without consuming it
|
|
||||||
func (p *parser) next() *lexer.Token {
|
|
||||||
return p.nextAt(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// shift returns next token and remove it from the tokens buffer
|
|
||||||
//
|
|
||||||
// Panics if next token is `TokenError`
|
|
||||||
func (p *parser) shift() *lexer.Token {
|
|
||||||
var result *lexer.Token
|
|
||||||
|
|
||||||
p.ensure(0)
|
|
||||||
|
|
||||||
result, p.tokens = p.tokens[0], p.tokens[1:]
|
|
||||||
|
|
||||||
// check error token
|
|
||||||
if result.Kind == lexer.TokenError {
|
|
||||||
errToken(result, "Lexer error")
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// isToken returns true if next token is of given type
|
|
||||||
func (p *parser) isToken(kind lexer.TokenKind) bool {
|
|
||||||
return p.have(1) && p.next().Kind == kind
|
|
||||||
}
|
|
||||||
|
|
||||||
// isSexpr returns true if next token starts a sexpr
|
|
||||||
func (p *parser) isSexpr() bool {
|
|
||||||
return p.isToken(lexer.TokenOpenSexpr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isPathSep returns true if next token is a path separator
|
|
||||||
func (p *parser) isPathSep() bool {
|
|
||||||
return p.isToken(lexer.TokenSep)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isID returns true if next token is an ID
|
|
||||||
func (p *parser) isID() bool {
|
|
||||||
return p.isToken(lexer.TokenID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isBlockParams returns true if next token starts a block params
|
|
||||||
func (p *parser) isBlockParams() bool {
|
|
||||||
return p.isToken(lexer.TokenOpenBlockParams)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isInverse returns true if next token starts an INVERSE sequence
|
|
||||||
func (p *parser) isInverse() bool {
|
|
||||||
return p.isToken(lexer.TokenInverse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isOpenInverseChain returns true if next token is OPEN_INVERSE_CHAIN
|
|
||||||
func (p *parser) isOpenInverseChain() bool {
|
|
||||||
return p.isToken(lexer.TokenOpenInverseChain)
|
|
||||||
}
|
|
200
vendor/github.com/aymerick/raymond/parser/parser_test.go
generated
vendored
200
vendor/github.com/aymerick/raymond/parser/parser_test.go
generated
vendored
|
@ -1,200 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond/ast"
|
|
||||||
"github.com/aymerick/raymond/lexer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type parserTest struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
output string
|
|
||||||
}
|
|
||||||
|
|
||||||
var parserTests = []parserTest{
|
|
||||||
//
|
|
||||||
// Next tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/parser.js
|
|
||||||
//
|
|
||||||
{"parses simple mustaches (1)", `{{123}}`, "{{ NUMBER{123} [] }}\n"},
|
|
||||||
{"parses simple mustaches (2)", `{{"foo"}}`, "{{ \"foo\" [] }}\n"},
|
|
||||||
{"parses simple mustaches (3)", `{{false}}`, "{{ BOOLEAN{false} [] }}\n"},
|
|
||||||
{"parses simple mustaches (4)", `{{true}}`, "{{ BOOLEAN{true} [] }}\n"},
|
|
||||||
{"parses simple mustaches (5)", `{{foo}}`, "{{ PATH:foo [] }}\n"},
|
|
||||||
{"parses simple mustaches (6)", `{{foo?}}`, "{{ PATH:foo? [] }}\n"},
|
|
||||||
{"parses simple mustaches (7)", `{{foo_}}`, "{{ PATH:foo_ [] }}\n"},
|
|
||||||
{"parses simple mustaches (8)", `{{foo-}}`, "{{ PATH:foo- [] }}\n"},
|
|
||||||
{"parses simple mustaches (9)", `{{foo:}}`, "{{ PATH:foo: [] }}\n"},
|
|
||||||
|
|
||||||
{"parses simple mustaches with data", `{{@foo}}`, "{{ @PATH:foo [] }}\n"},
|
|
||||||
{"parses simple mustaches with data paths", `{{@../foo}}`, "{{ @PATH:foo [] }}\n"},
|
|
||||||
{"parses mustaches with paths", `{{foo/bar}}`, "{{ PATH:foo/bar [] }}\n"},
|
|
||||||
{"parses mustaches with this/foo", `{{this/foo}}`, "{{ PATH:foo [] }}\n"},
|
|
||||||
{"parses mustaches with - in a path", `{{foo-bar}}`, "{{ PATH:foo-bar [] }}\n"},
|
|
||||||
{"parses mustaches with parameters", `{{foo bar}}`, "{{ PATH:foo [PATH:bar] }}\n"},
|
|
||||||
{"parses mustaches with string parameters", `{{foo bar "baz" }}`, "{{ PATH:foo [PATH:bar, \"baz\"] }}\n"},
|
|
||||||
{"parses mustaches with NUMBER parameters", `{{foo 1}}`, "{{ PATH:foo [NUMBER{1}] }}\n"},
|
|
||||||
{"parses mustaches with BOOLEAN parameters (1)", `{{foo true}}`, "{{ PATH:foo [BOOLEAN{true}] }}\n"},
|
|
||||||
{"parses mustaches with BOOLEAN parameters (2)", `{{foo false}}`, "{{ PATH:foo [BOOLEAN{false}] }}\n"},
|
|
||||||
{"parses mustaches with DATA parameters", `{{foo @bar}}`, "{{ PATH:foo [@PATH:bar] }}\n"},
|
|
||||||
|
|
||||||
{"parses mustaches with hash arguments (01)", `{{foo bar=baz}}`, "{{ PATH:foo [] HASH{bar=PATH:baz} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (02)", `{{foo bar=1}}`, "{{ PATH:foo [] HASH{bar=NUMBER{1}} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (03)", `{{foo bar=true}}`, "{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (04)", `{{foo bar=false}}`, "{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (05)", `{{foo bar=@baz}}`, "{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (06)", `{{foo bar=baz bat=bam}}`, "{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (07)", `{{foo bar=baz bat="bam"}}`, "{{ PATH:foo [] HASH{bar=PATH:baz, bat=\"bam\"} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (08)", `{{foo bat='bam'}}`, "{{ PATH:foo [] HASH{bat=\"bam\"} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (09)", `{{foo omg bar=baz bat="bam"}}`, "{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat=\"bam\"} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (10)", `{{foo omg bar=baz bat="bam" baz=1}}`, "{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat=\"bam\", baz=NUMBER{1}} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (11)", `{{foo omg bar=baz bat="bam" baz=true}}`, "{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat=\"bam\", baz=BOOLEAN{true}} }}\n"},
|
|
||||||
{"parses mustaches with hash arguments (12)", `{{foo omg bar=baz bat="bam" baz=false}}`, "{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat=\"bam\", baz=BOOLEAN{false}} }}\n"},
|
|
||||||
|
|
||||||
{"parses contents followed by a mustache", `foo bar {{baz}}`, "CONTENT[ 'foo bar ' ]\n{{ PATH:baz [] }}\n"},
|
|
||||||
|
|
||||||
{"parses a partial (1)", `{{> foo }}`, "{{> PARTIAL:foo }}\n"},
|
|
||||||
{"parses a partial (2)", `{{> "foo" }}`, "{{> PARTIAL:foo }}\n"},
|
|
||||||
{"parses a partial (3)", `{{> 1 }}`, "{{> PARTIAL:1 }}\n"},
|
|
||||||
{"parses a partial with context", `{{> foo bar}}`, "{{> PARTIAL:foo PATH:bar }}\n"},
|
|
||||||
{"parses a partial with hash", `{{> foo bar=bat}}`, "{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n"},
|
|
||||||
{"parses a partial with context and hash", `{{> foo bar bat=baz}}`, "{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n"},
|
|
||||||
{"parses a partial with a complex name", `{{> shared/partial?.bar}}`, "{{> PARTIAL:shared/partial?.bar }}\n"},
|
|
||||||
|
|
||||||
{"parses a comment", `{{! this is a comment }}`, "{{! ' this is a comment ' }}\n"},
|
|
||||||
{"parses a multi-line comment", "{{!\nthis is a multi-line comment\n}}", "{{! '\nthis is a multi-line comment\n' }}\n"},
|
|
||||||
|
|
||||||
{"parses an inverse section", `{{#foo}} bar {{^}} baz {{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"},
|
|
||||||
{"parses an inverse (else-style) section", `{{#foo}} bar {{else}} baz {{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"},
|
|
||||||
{"parses multiple inverse sections", `{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n"},
|
|
||||||
{"parses empty blocks", `{{#foo}}{{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n"},
|
|
||||||
{"parses empty blocks with empty inverse section", `{{#foo}}{{^}}{{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n"},
|
|
||||||
{"parses empty blocks with empty inverse (else-style) section", `{{#foo}}{{else}}{{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n"},
|
|
||||||
{"parses non-empty blocks with empty inverse section", `{{#foo}} bar {{^}}{{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"},
|
|
||||||
{"parses non-empty blocks with empty inverse (else-style) section", `{{#foo}} bar {{else}}{{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"},
|
|
||||||
{"parses empty blocks with non-empty inverse section", `{{#foo}}{{^}} bar {{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"},
|
|
||||||
{"parses empty blocks with non-empty inverse (else-style) section", `{{#foo}}{{else}} bar {{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"},
|
|
||||||
{"parses a standalone inverse section", `{{^foo}}bar{{/foo}}`, "BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n"},
|
|
||||||
{"parses block with block params", `{{#foo as |bar baz|}}content{{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"},
|
|
||||||
{"parses inverse block with block params", `{{^foo as |bar baz|}}content{{/foo}}`, "BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"},
|
|
||||||
{"parses chained inverse block with block params", `{{#foo}}{{else foo as |bar baz|}}content{{/foo}}`, "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParser(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range parserTests {
|
|
||||||
output := ""
|
|
||||||
|
|
||||||
node, err := Parse(test.input)
|
|
||||||
if err == nil {
|
|
||||||
output = ast.Print(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err != nil) || (test.output != output) {
|
|
||||||
t.Errorf("Test '%s' failed\ninput:\n\t'%s'\nexpected\n\t%q\ngot\n\t%q\nerror:\n\t%s", test.name, test.input, test.output, output, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var parserErrorTests = []parserTest{
|
|
||||||
{"lexer error", `{{! unclosed comment`, "Lexer error"},
|
|
||||||
{"syntax error", `foo{{^}}`, "Syntax error"},
|
|
||||||
|
|
||||||
{"open raw block must be closed", `{{{{raw foo}} bar {{{{/raw}}}}`, "Expecting CloseRawBlock"},
|
|
||||||
{"end raw block must be closed", `{{{{raw foo}}}} bar {{{{/raw}}`, "Expecting CloseRawBlock"},
|
|
||||||
|
|
||||||
{"raw block names must match (1)", `{{{{1}}}}{{foo}}{{{{/raw}}}}`, "1 doesn't match raw"},
|
|
||||||
{"raw block names must match (2)", `{{{{raw}}}}{{foo}}{{{{/1}}}}`, "raw doesn't match 1"},
|
|
||||||
{"raw block names must match (3)", `{{{{goodbyes}}}}test{{{{/hellos}}}}`, "goodbyes doesn't match hellos"},
|
|
||||||
|
|
||||||
{"open block must be closed", `{{#foo bar}}}{{/foo}}`, "Expecting Close"},
|
|
||||||
{"end block must be closed", `{{#foo bar}}{{/foo}}}`, "Expecting Close"},
|
|
||||||
{"an open block must have a end block", `{{#foo}}test`, "Expecting OpenEndBlock"},
|
|
||||||
|
|
||||||
{"block names must match (1)", `{{#1 bar}}{{/foo}}`, "1 doesn't match foo"},
|
|
||||||
{"block names must match (2)", `{{#foo bar}}{{/1}}`, "foo doesn't match 1"},
|
|
||||||
{"block names must match (3)", `{{#foo}}test{{/bar}}`, "foo doesn't match bar"},
|
|
||||||
|
|
||||||
{"an mustache must terminate with a close mustache", `{{foo}}}`, "Expecting Close"},
|
|
||||||
{"an unescaped mustache must terminate with a close unescaped mustache", `{{{foo}}`, "Expecting CloseUnescaped"},
|
|
||||||
|
|
||||||
{"an partial must terminate with a close mustache", `{{> foo}}}`, "Expecting Close"},
|
|
||||||
{"a subexpression must terminate with a close subexpression", `{{foo (false}}`, "Expecting CloseSexpr"},
|
|
||||||
|
|
||||||
{"raises on missing hash value (1)", `{{foo bar=}}`, "Parse error on line 1"},
|
|
||||||
{"raises on missing hash value (2)", `{{foo bar=baz bim=}}`, "Parse error on line 1"},
|
|
||||||
|
|
||||||
{"block param must have at least one param", `{{#foo as ||}}content{{/foo}}`, "Expecting ID"},
|
|
||||||
{"open block params must be closed", `{{#foo as |}}content{{/foo}}`, "Expecting ID"},
|
|
||||||
|
|
||||||
{"a path must start with an ID", `{{#/}}content{{/foo}}`, "Expecting ID"},
|
|
||||||
{"a path must end with an ID", `{{foo/bar/}}`, "Expecting ID"},
|
|
||||||
|
|
||||||
//
|
|
||||||
// Next tests come from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/spec/parser.js
|
|
||||||
//
|
|
||||||
{"throws on old inverse section", `{{else foo}}bar{{/foo}}`, ""},
|
|
||||||
|
|
||||||
{"raises if there's a parser error (1)", `foo{{^}}bar`, "Parse error on line 1"},
|
|
||||||
{"raises if there's a parser error (2)", `{{foo}`, "Parse error on line 1"},
|
|
||||||
{"raises if there's a parser error (3)", `{{foo &}}`, "Parse error on line 1"},
|
|
||||||
{"raises if there's a parser error (4)", `{{#goodbyes}}{{/hellos}}`, "Parse error on line 1"},
|
|
||||||
{"raises if there's a parser error (5)", `{{#goodbyes}}{{/hellos}}`, "goodbyes doesn't match hellos"},
|
|
||||||
|
|
||||||
{"should handle invalid paths (1)", `{{foo/../bar}}`, `Invalid path: foo/..`},
|
|
||||||
{"should handle invalid paths (2)", `{{foo/./bar}}`, `Invalid path: foo/.`},
|
|
||||||
{"should handle invalid paths (3)", `{{foo/this/bar}}`, `Invalid path: foo/this`},
|
|
||||||
|
|
||||||
{"knows how to report the correct line number in errors (1)", "hello\nmy\n{{foo}", "Parse error on line 3"},
|
|
||||||
{"knows how to report the correct line number in errors (2)", "hello\n\nmy\n\n{{foo}", "Parse error on line 5"},
|
|
||||||
|
|
||||||
{"knows how to report the correct line number in errors when the first character is a newline", "\n\nhello\n\nmy\n\n{{foo}", "Parse error on line 7"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range parserErrorTests {
|
|
||||||
node, err := Parse(test.input)
|
|
||||||
if err == nil {
|
|
||||||
output := ast.Print(node)
|
|
||||||
tokens := lexer.Collect(test.input)
|
|
||||||
|
|
||||||
t.Errorf("Test '%s' failed - Error expected\ninput:\n\t'%s'\ngot\n\t%q\ntokens:\n\t%q", test.name, test.input, output, tokens)
|
|
||||||
} else if test.output != "" {
|
|
||||||
matched, errMatch := regexp.MatchString(regexp.QuoteMeta(test.output), fmt.Sprint(err))
|
|
||||||
if errMatch != nil {
|
|
||||||
panic("Failed to match regexp")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !matched {
|
|
||||||
t.Errorf("Test '%s' failed - Incorrect error returned\ninput:\n\t'%s'\nexpected\n\t%q\ngot\n\t%q", test.name, test.input, test.output, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// package example
|
|
||||||
func Example() {
|
|
||||||
source := "You know {{nothing}} John Snow"
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
program, err := Parse(source)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// print AST
|
|
||||||
output := ast.Print(program)
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// CONTENT[ 'You know ' ]
|
|
||||||
// {{ PATH:nothing [] }}
|
|
||||||
// CONTENT[ ' John Snow' ]
|
|
||||||
}
|
|
360
vendor/github.com/aymerick/raymond/parser/whitespace.go
generated
vendored
360
vendor/github.com/aymerick/raymond/parser/whitespace.go
generated
vendored
|
@ -1,360 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
// whitespaceVisitor walks through the AST to perform whitespace control
|
|
||||||
//
|
|
||||||
// The logic was shamelessly borrowed from:
|
|
||||||
// https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/compiler/whitespace-control.js
|
|
||||||
type whitespaceVisitor struct {
|
|
||||||
isRootSeen bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
rTrimLeft = regexp.MustCompile(`^[ \t]*\r?\n?`)
|
|
||||||
rTrimLeftMultiple = regexp.MustCompile(`^\s+`)
|
|
||||||
|
|
||||||
rTrimRight = regexp.MustCompile(`[ \t]+$`)
|
|
||||||
rTrimRightMultiple = regexp.MustCompile(`\s+$`)
|
|
||||||
|
|
||||||
rPrevWhitespace = regexp.MustCompile(`\r?\n\s*?$`)
|
|
||||||
rPrevWhitespaceStart = regexp.MustCompile(`(^|\r?\n)\s*?$`)
|
|
||||||
|
|
||||||
rNextWhitespace = regexp.MustCompile(`^\s*?\r?\n`)
|
|
||||||
rNextWhitespaceEnd = regexp.MustCompile(`^\s*?(\r?\n|$)`)
|
|
||||||
|
|
||||||
rPartialIndent = regexp.MustCompile(`([ \t]+$)`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// newWhitespaceVisitor instanciates a new whitespaceVisitor
|
|
||||||
func newWhitespaceVisitor() *whitespaceVisitor {
|
|
||||||
return &whitespaceVisitor{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// processWhitespaces performs whitespace control on given AST
|
|
||||||
//
|
|
||||||
// WARNING: It must be called only once on AST.
|
|
||||||
func processWhitespaces(node ast.Node) {
|
|
||||||
node.Accept(newWhitespaceVisitor())
|
|
||||||
}
|
|
||||||
|
|
||||||
func omitRightFirst(body []ast.Node, multiple bool) {
|
|
||||||
omitRight(body, -1, multiple)
|
|
||||||
}
|
|
||||||
|
|
||||||
func omitRight(body []ast.Node, i int, multiple bool) {
|
|
||||||
if i+1 >= len(body) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
current := body[i+1]
|
|
||||||
|
|
||||||
node, ok := current.(*ast.ContentStatement)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !multiple && node.RightStripped {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
original := node.Value
|
|
||||||
|
|
||||||
r := rTrimLeft
|
|
||||||
if multiple {
|
|
||||||
r = rTrimLeftMultiple
|
|
||||||
}
|
|
||||||
|
|
||||||
node.Value = r.ReplaceAllString(node.Value, "")
|
|
||||||
|
|
||||||
node.RightStripped = (original != node.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func omitLeftLast(body []ast.Node, multiple bool) {
|
|
||||||
omitLeft(body, len(body), multiple)
|
|
||||||
}
|
|
||||||
|
|
||||||
func omitLeft(body []ast.Node, i int, multiple bool) bool {
|
|
||||||
if i-1 < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
current := body[i-1]
|
|
||||||
|
|
||||||
node, ok := current.(*ast.ContentStatement)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !multiple && node.LeftStripped {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
original := node.Value
|
|
||||||
|
|
||||||
r := rTrimRight
|
|
||||||
if multiple {
|
|
||||||
r = rTrimRightMultiple
|
|
||||||
}
|
|
||||||
|
|
||||||
node.Value = r.ReplaceAllString(node.Value, "")
|
|
||||||
|
|
||||||
node.LeftStripped = (original != node.Value)
|
|
||||||
|
|
||||||
return node.LeftStripped
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPrevWhitespace(body []ast.Node) bool {
|
|
||||||
return isPrevWhitespaceProgram(body, len(body), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPrevWhitespaceProgram(body []ast.Node, i int, isRoot bool) bool {
|
|
||||||
if i < 1 {
|
|
||||||
return isRoot
|
|
||||||
}
|
|
||||||
|
|
||||||
prev := body[i-1]
|
|
||||||
|
|
||||||
if node, ok := prev.(*ast.ContentStatement); ok {
|
|
||||||
if (node.Value == "") && node.RightStripped {
|
|
||||||
// already stripped, so it may be an empty string not catched by regexp
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
r := rPrevWhitespaceStart
|
|
||||||
if (i > 1) || !isRoot {
|
|
||||||
r = rPrevWhitespace
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.MatchString(node.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNextWhitespace(body []ast.Node) bool {
|
|
||||||
return isNextWhitespaceProgram(body, -1, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNextWhitespaceProgram(body []ast.Node, i int, isRoot bool) bool {
|
|
||||||
if i+1 >= len(body) {
|
|
||||||
return isRoot
|
|
||||||
}
|
|
||||||
|
|
||||||
next := body[i+1]
|
|
||||||
|
|
||||||
if node, ok := next.(*ast.ContentStatement); ok {
|
|
||||||
if (node.Value == "") && node.LeftStripped {
|
|
||||||
// already stripped, so it may be an empty string not catched by regexp
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
r := rNextWhitespaceEnd
|
|
||||||
if (i+2 > len(body)) || !isRoot {
|
|
||||||
r = rNextWhitespace
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.MatchString(node.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Visitor interface
|
|
||||||
//
|
|
||||||
|
|
||||||
func (v *whitespaceVisitor) VisitProgram(program *ast.Program) interface{} {
|
|
||||||
isRoot := !v.isRootSeen
|
|
||||||
v.isRootSeen = true
|
|
||||||
|
|
||||||
body := program.Body
|
|
||||||
for i, current := range body {
|
|
||||||
strip, _ := current.Accept(v).(*ast.Strip)
|
|
||||||
if strip == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
_isPrevWhitespace := isPrevWhitespaceProgram(body, i, isRoot)
|
|
||||||
_isNextWhitespace := isNextWhitespaceProgram(body, i, isRoot)
|
|
||||||
|
|
||||||
openStandalone := strip.OpenStandalone && _isPrevWhitespace
|
|
||||||
closeStandalone := strip.CloseStandalone && _isNextWhitespace
|
|
||||||
inlineStandalone := strip.InlineStandalone && _isPrevWhitespace && _isNextWhitespace
|
|
||||||
|
|
||||||
if strip.Close {
|
|
||||||
omitRight(body, i, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strip.Open && (i > 0) {
|
|
||||||
omitLeft(body, i, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if inlineStandalone {
|
|
||||||
omitRight(body, i, false)
|
|
||||||
|
|
||||||
if omitLeft(body, i, false) {
|
|
||||||
// If we are on a standalone node, save the indent info for partials
|
|
||||||
if partial, ok := current.(*ast.PartialStatement); ok {
|
|
||||||
// Pull out the whitespace from the final line
|
|
||||||
if i > 0 {
|
|
||||||
if prevContent, ok := body[i-1].(*ast.ContentStatement); ok {
|
|
||||||
partial.Indent = rPartialIndent.FindString(prevContent.Original)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := current.(*ast.BlockStatement); ok {
|
|
||||||
if openStandalone {
|
|
||||||
prog := b.Program
|
|
||||||
if prog == nil {
|
|
||||||
prog = b.Inverse
|
|
||||||
}
|
|
||||||
|
|
||||||
omitRightFirst(prog.Body, false)
|
|
||||||
|
|
||||||
// Strip out the previous content node if it's whitespace only
|
|
||||||
omitLeft(body, i, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if closeStandalone {
|
|
||||||
prog := b.Inverse
|
|
||||||
if prog == nil {
|
|
||||||
prog = b.Program
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always strip the next node
|
|
||||||
omitRight(body, i, false)
|
|
||||||
|
|
||||||
omitLeftLast(prog.Body, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *whitespaceVisitor) VisitBlock(block *ast.BlockStatement) interface{} {
|
|
||||||
if block.Program != nil {
|
|
||||||
block.Program.Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.Inverse != nil {
|
|
||||||
block.Inverse.Accept(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
program := block.Program
|
|
||||||
inverse := block.Inverse
|
|
||||||
|
|
||||||
if program == nil {
|
|
||||||
program = inverse
|
|
||||||
inverse = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
firstInverse := inverse
|
|
||||||
lastInverse := inverse
|
|
||||||
|
|
||||||
if (inverse != nil) && inverse.Chained {
|
|
||||||
b, _ := inverse.Body[0].(*ast.BlockStatement)
|
|
||||||
firstInverse = b.Program
|
|
||||||
|
|
||||||
for lastInverse.Chained {
|
|
||||||
b, _ := lastInverse.Body[len(lastInverse.Body)-1].(*ast.BlockStatement)
|
|
||||||
lastInverse = b.Program
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeProg := firstInverse
|
|
||||||
if closeProg == nil {
|
|
||||||
closeProg = program
|
|
||||||
}
|
|
||||||
|
|
||||||
strip := &ast.Strip{
|
|
||||||
Open: (block.OpenStrip != nil) && block.OpenStrip.Open,
|
|
||||||
Close: (block.CloseStrip != nil) && block.CloseStrip.Close,
|
|
||||||
|
|
||||||
OpenStandalone: isNextWhitespace(program.Body),
|
|
||||||
CloseStandalone: isPrevWhitespace(closeProg.Body),
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.OpenStrip != nil) && block.OpenStrip.Close {
|
|
||||||
omitRightFirst(program.Body, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if inverse != nil {
|
|
||||||
if block.InverseStrip != nil {
|
|
||||||
inverseStrip := block.InverseStrip
|
|
||||||
|
|
||||||
if inverseStrip.Open {
|
|
||||||
omitLeftLast(program.Body, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if inverseStrip.Close {
|
|
||||||
omitRightFirst(firstInverse.Body, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block.CloseStrip != nil) && block.CloseStrip.Open {
|
|
||||||
omitLeftLast(lastInverse.Body, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find standalone else statements
|
|
||||||
if isPrevWhitespace(program.Body) && isNextWhitespace(firstInverse.Body) {
|
|
||||||
omitLeftLast(program.Body, false)
|
|
||||||
|
|
||||||
omitRightFirst(firstInverse.Body, false)
|
|
||||||
}
|
|
||||||
} else if (block.CloseStrip != nil) && block.CloseStrip.Open {
|
|
||||||
omitLeftLast(program.Body, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strip
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *whitespaceVisitor) VisitMustache(mustache *ast.MustacheStatement) interface{} {
|
|
||||||
return mustache.Strip
|
|
||||||
}
|
|
||||||
|
|
||||||
func _inlineStandalone(strip *ast.Strip) interface{} {
|
|
||||||
return &ast.Strip{
|
|
||||||
Open: strip.Open,
|
|
||||||
Close: strip.Close,
|
|
||||||
InlineStandalone: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *whitespaceVisitor) VisitPartial(node *ast.PartialStatement) interface{} {
|
|
||||||
strip := node.Strip
|
|
||||||
if strip == nil {
|
|
||||||
strip = &ast.Strip{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _inlineStandalone(strip)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *whitespaceVisitor) VisitComment(node *ast.CommentStatement) interface{} {
|
|
||||||
strip := node.Strip
|
|
||||||
if strip == nil {
|
|
||||||
strip = &ast.Strip{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _inlineStandalone(strip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOOP
|
|
||||||
func (v *whitespaceVisitor) VisitContent(node *ast.ContentStatement) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitExpression(node *ast.Expression) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitSubExpression(node *ast.SubExpression) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitPath(node *ast.PathExpression) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitString(node *ast.StringLiteral) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitBoolean(node *ast.BooleanLiteral) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitNumber(node *ast.NumberLiteral) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitHash(node *ast.Hash) interface{} { return nil }
|
|
||||||
func (v *whitespaceVisitor) VisitHashPair(node *ast.HashPair) interface{} { return nil }
|
|
85
vendor/github.com/aymerick/raymond/partial.go
generated
vendored
85
vendor/github.com/aymerick/raymond/partial.go
generated
vendored
|
@ -1,85 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// partial represents a partial template
|
|
||||||
type partial struct {
|
|
||||||
name string
|
|
||||||
source string
|
|
||||||
tpl *Template
|
|
||||||
}
|
|
||||||
|
|
||||||
// partials stores all global partials
|
|
||||||
var partials map[string]*partial
|
|
||||||
|
|
||||||
// protects global partials
|
|
||||||
var partialsMutex sync.RWMutex
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
partials = make(map[string]*partial)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newPartial instanciates a new partial
|
|
||||||
func newPartial(name string, source string, tpl *Template) *partial {
|
|
||||||
return &partial{
|
|
||||||
name: name,
|
|
||||||
source: source,
|
|
||||||
tpl: tpl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartial registers a global partial. That partial will be available to all templates.
|
|
||||||
func RegisterPartial(name string, source string) {
|
|
||||||
partialsMutex.Lock()
|
|
||||||
defer partialsMutex.Unlock()
|
|
||||||
|
|
||||||
if partials[name] != nil {
|
|
||||||
panic(fmt.Errorf("Partial already registered: %s", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
partials[name] = newPartial(name, source, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartials registers several global partials. Those partials will be available to all templates.
|
|
||||||
func RegisterPartials(partials map[string]string) {
|
|
||||||
for name, p := range partials {
|
|
||||||
RegisterPartial(name, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartialTemplate registers a global partial with given parsed template. That partial will be available to all templates.
|
|
||||||
func RegisterPartialTemplate(name string, tpl *Template) {
|
|
||||||
partialsMutex.Lock()
|
|
||||||
defer partialsMutex.Unlock()
|
|
||||||
|
|
||||||
if partials[name] != nil {
|
|
||||||
panic(fmt.Errorf("Partial already registered: %s", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
partials[name] = newPartial(name, "", tpl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// findPartial finds a registered global partial
|
|
||||||
func findPartial(name string) *partial {
|
|
||||||
partialsMutex.RLock()
|
|
||||||
defer partialsMutex.RUnlock()
|
|
||||||
|
|
||||||
return partials[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// template returns parsed partial template
|
|
||||||
func (p *partial) template() (*Template, error) {
|
|
||||||
if p.tpl == nil {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
p.tpl, err = Parse(p.source)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.tpl, nil
|
|
||||||
}
|
|
28
vendor/github.com/aymerick/raymond/raymond.go
generated
vendored
28
vendor/github.com/aymerick/raymond/raymond.go
generated
vendored
|
@ -1,28 +0,0 @@
|
||||||
// Package raymond provides handlebars evaluation
|
|
||||||
package raymond
|
|
||||||
|
|
||||||
// Render parses a template and evaluates it with given context
|
|
||||||
//
|
|
||||||
// Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead.
|
|
||||||
func Render(source string, ctx interface{}) (string, error) {
|
|
||||||
// parse template
|
|
||||||
tpl, err := Parse(source)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// renders template
|
|
||||||
str, err := tpl.Exec(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return str, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustRender parses a template and evaluates it with given context. It panics on error.
|
|
||||||
//
|
|
||||||
// Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead.
|
|
||||||
func MustRender(source string, ctx interface{}) string {
|
|
||||||
return MustParse(source).MustExec(ctx)
|
|
||||||
}
|
|
BIN
vendor/github.com/aymerick/raymond/raymond.png
generated
vendored
BIN
vendor/github.com/aymerick/raymond/raymond.png
generated
vendored
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
115
vendor/github.com/aymerick/raymond/raymond_test.go
generated
vendored
115
vendor/github.com/aymerick/raymond/raymond_test.go
generated
vendored
|
@ -1,115 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func Example() {
|
|
||||||
source := "<h1>{{title}}</h1><p>{{body.content}}</p>"
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"title": "foo",
|
|
||||||
"body": map[string]string{"content": "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
// evaluate template with context
|
|
||||||
output := tpl.MustExec(ctx)
|
|
||||||
|
|
||||||
// alternatively, for one shots:
|
|
||||||
// output := MustRender(source, ctx)
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: <h1>foo</h1><p>bar</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example_struct() {
|
|
||||||
source := `<div class="post">
|
|
||||||
<h1>By {{fullName author}}</h1>
|
|
||||||
<div class="body">{{body}}</div>
|
|
||||||
|
|
||||||
<h1>Comments</h1>
|
|
||||||
|
|
||||||
{{#each comments}}
|
|
||||||
<h2>By {{fullName author}}</h2>
|
|
||||||
<div class="body">{{body}}</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>`
|
|
||||||
|
|
||||||
type Person struct {
|
|
||||||
FirstName string
|
|
||||||
LastName string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Comment struct {
|
|
||||||
Author Person
|
|
||||||
Body string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Post struct {
|
|
||||||
Author Person
|
|
||||||
Body string
|
|
||||||
Comments []Comment
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := Post{
|
|
||||||
Person{"Jean", "Valjean"},
|
|
||||||
"Life is difficult",
|
|
||||||
[]Comment{
|
|
||||||
Comment{
|
|
||||||
Person{"Marcel", "Beliveau"},
|
|
||||||
"LOL!",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterHelper("fullName", func(person Person) string {
|
|
||||||
return person.FirstName + " " + person.LastName
|
|
||||||
})
|
|
||||||
|
|
||||||
output := MustRender(source, ctx)
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: <div class="post">
|
|
||||||
// <h1>By Jean Valjean</h1>
|
|
||||||
// <div class="body">Life is difficult</div>
|
|
||||||
//
|
|
||||||
// <h1>Comments</h1>
|
|
||||||
//
|
|
||||||
// <h2>By Marcel Beliveau</h2>
|
|
||||||
// <div class="body">LOL!</div>
|
|
||||||
// </div>
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleRender() {
|
|
||||||
tpl := "<h1>{{title}}</h1><p>{{body.content}}</p>"
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"title": "foo",
|
|
||||||
"body": map[string]string{"content": "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// render template with context
|
|
||||||
output, err := Render(tpl, ctx)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: <h1>foo</h1><p>bar</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleMustRender() {
|
|
||||||
tpl := "<h1>{{title}}</h1><p>{{body.content}}</p>"
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"title": "foo",
|
|
||||||
"body": map[string]string{"content": "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// render template with context
|
|
||||||
output := MustRender(tpl, ctx)
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: <h1>foo</h1><p>bar</p>
|
|
||||||
}
|
|
84
vendor/github.com/aymerick/raymond/string.go
generated
vendored
84
vendor/github.com/aymerick/raymond/string.go
generated
vendored
|
@ -1,84 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SafeString represents a string that must not be escaped.
|
|
||||||
//
|
|
||||||
// A SafeString can be returned by helpers to disable escaping.
|
|
||||||
type SafeString string
|
|
||||||
|
|
||||||
// isSafeString returns true if argument is a SafeString
|
|
||||||
func isSafeString(value interface{}) bool {
|
|
||||||
if _, ok := value.(SafeString); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Str returns string representation of any basic type value.
|
|
||||||
func Str(value interface{}) string {
|
|
||||||
return strValue(reflect.ValueOf(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
// strValue returns string representation of a reflect.Value
|
|
||||||
func strValue(value reflect.Value) string {
|
|
||||||
result := ""
|
|
||||||
|
|
||||||
ival, ok := printableValue(value)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Errorf("Can't print value: %q", value))
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.ValueOf(ival)
|
|
||||||
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
result += strValue(val.Index(i))
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
result = "false"
|
|
||||||
if val.Bool() {
|
|
||||||
result = "true"
|
|
||||||
}
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
result = fmt.Sprintf("%d", ival)
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
result = strconv.FormatFloat(val.Float(), 'f', -1, 64)
|
|
||||||
case reflect.Invalid:
|
|
||||||
result = ""
|
|
||||||
default:
|
|
||||||
result = fmt.Sprintf("%s", ival)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// printableValue returns the, possibly indirected, interface value inside v that
|
|
||||||
// is best for a call to formatted printer.
|
|
||||||
//
|
|
||||||
// NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go
|
|
||||||
func printableValue(v reflect.Value) (interface{}, bool) {
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v, _ = indirect(v) // fmt.Fprint handles nil.
|
|
||||||
}
|
|
||||||
if !v.IsValid() {
|
|
||||||
return "", true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {
|
|
||||||
if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) {
|
|
||||||
v = v.Addr()
|
|
||||||
} else {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Chan, reflect.Func:
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v.Interface(), true
|
|
||||||
}
|
|
59
vendor/github.com/aymerick/raymond/string_test.go
generated
vendored
59
vendor/github.com/aymerick/raymond/string_test.go
generated
vendored
|
@ -1,59 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type strTest struct {
|
|
||||||
name string
|
|
||||||
input interface{}
|
|
||||||
output string
|
|
||||||
}
|
|
||||||
|
|
||||||
var strTests = []strTest{
|
|
||||||
{"String", "foo", "foo"},
|
|
||||||
{"Boolean true", true, "true"},
|
|
||||||
{"Boolean false", false, "false"},
|
|
||||||
{"Integer", 25, "25"},
|
|
||||||
{"Float", 25.75, "25.75"},
|
|
||||||
{"Nil", nil, ""},
|
|
||||||
{"[]string", []string{"foo", "bar"}, "foobar"},
|
|
||||||
{"[]interface{} (strings)", []interface{}{"foo", "bar"}, "foobar"},
|
|
||||||
{"[]Boolean", []bool{true, false}, "truefalse"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStr(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range strTests {
|
|
||||||
if res := Str(test.input); res != test.output {
|
|
||||||
t.Errorf("Failed to stringify: %s\nexpected:\n\t'%s'got:\n\t%q", test.name, test.output, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleStr() {
|
|
||||||
output := Str(3) + " foos are " + Str(true) + " and " + Str(-1.25) + " bars are " + Str(false) + "\n"
|
|
||||||
output += "But you know '" + Str(nil) + "' John Snow\n"
|
|
||||||
output += "map: " + Str(map[string]string{"foo": "bar"}) + "\n"
|
|
||||||
output += "array: " + Str([]interface{}{true, 10, "foo", 5, "bar"})
|
|
||||||
|
|
||||||
fmt.Println(output)
|
|
||||||
// Output: 3 foos are true and -1.25 bars are false
|
|
||||||
// But you know '' John Snow
|
|
||||||
// map: map[foo:bar]
|
|
||||||
// array: true10foo5bar
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSafeString() {
|
|
||||||
RegisterHelper("em", func() SafeString {
|
|
||||||
return SafeString("<em>FOO BAR</em>")
|
|
||||||
})
|
|
||||||
|
|
||||||
tpl := MustParse("{{em}}")
|
|
||||||
|
|
||||||
result := tpl.MustExec(nil)
|
|
||||||
fmt.Print(result)
|
|
||||||
// Output: <em>FOO BAR</em>
|
|
||||||
}
|
|
248
vendor/github.com/aymerick/raymond/template.go
generated
vendored
248
vendor/github.com/aymerick/raymond/template.go
generated
vendored
|
@ -1,248 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/aymerick/raymond/ast"
|
|
||||||
"github.com/aymerick/raymond/parser"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Template represents a handlebars template.
|
|
||||||
type Template struct {
|
|
||||||
source string
|
|
||||||
program *ast.Program
|
|
||||||
helpers map[string]reflect.Value
|
|
||||||
partials map[string]*partial
|
|
||||||
mutex sync.RWMutex // protects helpers and partials
|
|
||||||
}
|
|
||||||
|
|
||||||
// newTemplate instanciate a new template without parsing it
|
|
||||||
func newTemplate(source string) *Template {
|
|
||||||
return &Template{
|
|
||||||
source: source,
|
|
||||||
helpers: make(map[string]reflect.Value),
|
|
||||||
partials: make(map[string]*partial),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse instanciates a template by parsing given source.
|
|
||||||
func Parse(source string) (*Template, error) {
|
|
||||||
tpl := newTemplate(source)
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
if err := tpl.parse(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tpl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustParse instanciates a template by parsing given source. It panics on error.
|
|
||||||
func MustParse(source string) *Template {
|
|
||||||
result, err := Parse(source)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFile reads given file and returns parsed template.
|
|
||||||
func ParseFile(filePath string) (*Template, error) {
|
|
||||||
b, err := ioutil.ReadFile(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Parse(string(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse parses the template
|
|
||||||
//
|
|
||||||
// It can be called several times, the parsing will be done only once.
|
|
||||||
func (tpl *Template) parse() error {
|
|
||||||
if tpl.program == nil {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
tpl.program, err = parser.Parse(tpl.source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a copy of that template.
|
|
||||||
func (tpl *Template) Clone() *Template {
|
|
||||||
result := newTemplate(tpl.source)
|
|
||||||
|
|
||||||
result.program = tpl.program
|
|
||||||
|
|
||||||
tpl.mutex.RLock()
|
|
||||||
defer tpl.mutex.RUnlock()
|
|
||||||
|
|
||||||
for name, helper := range tpl.helpers {
|
|
||||||
result.RegisterHelper(name, helper.Interface())
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, partial := range tpl.partials {
|
|
||||||
result.addPartial(name, partial.source, partial.tpl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tpl *Template) findHelper(name string) reflect.Value {
|
|
||||||
tpl.mutex.RLock()
|
|
||||||
defer tpl.mutex.RUnlock()
|
|
||||||
|
|
||||||
return tpl.helpers[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterHelper registers a helper for that template.
|
|
||||||
func (tpl *Template) RegisterHelper(name string, helper interface{}) {
|
|
||||||
tpl.mutex.Lock()
|
|
||||||
defer tpl.mutex.Unlock()
|
|
||||||
|
|
||||||
if tpl.helpers[name] != zero {
|
|
||||||
panic(fmt.Sprintf("Helper %s already registered", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.ValueOf(helper)
|
|
||||||
ensureValidHelper(name, val)
|
|
||||||
|
|
||||||
tpl.helpers[name] = val
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterHelpers registers several helpers for that template.
|
|
||||||
func (tpl *Template) RegisterHelpers(helpers map[string]interface{}) {
|
|
||||||
for name, helper := range helpers {
|
|
||||||
tpl.RegisterHelper(name, helper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tpl *Template) addPartial(name string, source string, template *Template) {
|
|
||||||
tpl.mutex.Lock()
|
|
||||||
defer tpl.mutex.Unlock()
|
|
||||||
|
|
||||||
if tpl.partials[name] != nil {
|
|
||||||
panic(fmt.Sprintf("Partial %s already registered", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl.partials[name] = newPartial(name, source, template)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tpl *Template) findPartial(name string) *partial {
|
|
||||||
tpl.mutex.RLock()
|
|
||||||
defer tpl.mutex.RUnlock()
|
|
||||||
|
|
||||||
return tpl.partials[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartial registers a partial for that template.
|
|
||||||
func (tpl *Template) RegisterPartial(name string, source string) {
|
|
||||||
tpl.addPartial(name, source, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartials registers several partials for that template.
|
|
||||||
func (tpl *Template) RegisterPartials(partials map[string]string) {
|
|
||||||
for name, partial := range partials {
|
|
||||||
tpl.RegisterPartial(name, partial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartialFile reads given file and registers its content as a partial with given name.
|
|
||||||
func (tpl *Template) RegisterPartialFile(filePath string, name string) error {
|
|
||||||
b, err := ioutil.ReadFile(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tpl.RegisterPartial(name, string(b))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartialFiles reads several files and registers them as partials, the filename base is used as the partial name.
|
|
||||||
func (tpl *Template) RegisterPartialFiles(filePaths ...string) error {
|
|
||||||
if len(filePaths) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, filePath := range filePaths {
|
|
||||||
name := fileBase(filePath)
|
|
||||||
|
|
||||||
if err := tpl.RegisterPartialFile(filePath, name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterPartialTemplate registers an already parsed partial for that template.
|
|
||||||
func (tpl *Template) RegisterPartialTemplate(name string, template *Template) {
|
|
||||||
tpl.addPartial(name, "", template)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exec evaluates template with given context.
|
|
||||||
func (tpl *Template) Exec(ctx interface{}) (result string, err error) {
|
|
||||||
return tpl.ExecWith(ctx, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustExec evaluates template with given context. It panics on error.
|
|
||||||
func (tpl *Template) MustExec(ctx interface{}) string {
|
|
||||||
result, err := tpl.Exec(ctx)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecWith evaluates template with given context and private data frame.
|
|
||||||
func (tpl *Template) ExecWith(ctx interface{}, privData *DataFrame) (result string, err error) {
|
|
||||||
defer errRecover(&err)
|
|
||||||
|
|
||||||
// parses template if necessary
|
|
||||||
err = tpl.parse()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup visitor
|
|
||||||
v := newEvalVisitor(tpl, ctx, privData)
|
|
||||||
|
|
||||||
// visit AST
|
|
||||||
result, _ = tpl.program.Accept(v).(string)
|
|
||||||
|
|
||||||
// named return values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// errRecover recovers evaluation panic
|
|
||||||
func errRecover(errp *error) {
|
|
||||||
e := recover()
|
|
||||||
if e != nil {
|
|
||||||
switch err := e.(type) {
|
|
||||||
case runtime.Error:
|
|
||||||
panic(e)
|
|
||||||
case error:
|
|
||||||
*errp = err
|
|
||||||
default:
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintAST returns string representation of parsed template.
|
|
||||||
func (tpl *Template) PrintAST() string {
|
|
||||||
if err := tpl.parse(); err != nil {
|
|
||||||
return fmt.Sprintf("PARSER ERROR: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ast.Print(tpl.program)
|
|
||||||
}
|
|
166
vendor/github.com/aymerick/raymond/template_test.go
generated
vendored
166
vendor/github.com/aymerick/raymond/template_test.go
generated
vendored
|
@ -1,166 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var sourceBasic = `<div class="entry">
|
|
||||||
<h1>{{title}}</h1>
|
|
||||||
<div class="body">
|
|
||||||
{{body}}
|
|
||||||
</div>
|
|
||||||
</div>`
|
|
||||||
|
|
||||||
var basicAST = `CONTENT[ '<div class="entry">
|
|
||||||
<h1>' ]
|
|
||||||
{{ PATH:title [] }}
|
|
||||||
CONTENT[ '</h1>
|
|
||||||
<div class="body">
|
|
||||||
' ]
|
|
||||||
{{ PATH:body [] }}
|
|
||||||
CONTENT[ '
|
|
||||||
</div>
|
|
||||||
</div>' ]
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestNewTemplate(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tpl := newTemplate(sourceBasic)
|
|
||||||
if tpl.source != sourceBasic {
|
|
||||||
t.Errorf("Failed to instantiate template")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tpl, err := Parse(sourceBasic)
|
|
||||||
if err != nil || (tpl.source != sourceBasic) {
|
|
||||||
t.Errorf("Failed to parse template")
|
|
||||||
}
|
|
||||||
|
|
||||||
if str := tpl.PrintAST(); str != basicAST {
|
|
||||||
t.Errorf("Template parsing incorrect: %s", str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClone(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
sourcePartial := `I am a {{wat}} partial`
|
|
||||||
sourcePartial2 := `Partial for the {{wat}}`
|
|
||||||
|
|
||||||
tpl := MustParse(sourceBasic)
|
|
||||||
tpl.RegisterPartial("p", sourcePartial)
|
|
||||||
|
|
||||||
if (len(tpl.partials) != 1) || (tpl.partials["p"] == nil) {
|
|
||||||
t.Errorf("What?")
|
|
||||||
}
|
|
||||||
|
|
||||||
cloned := tpl.Clone()
|
|
||||||
|
|
||||||
if (len(cloned.partials) != 1) || (cloned.partials["p"] == nil) {
|
|
||||||
t.Errorf("Template partials must be cloned")
|
|
||||||
}
|
|
||||||
|
|
||||||
cloned.RegisterPartial("p2", sourcePartial2)
|
|
||||||
|
|
||||||
if (len(cloned.partials) != 2) || (cloned.partials["p"] == nil) || (cloned.partials["p2"] == nil) {
|
|
||||||
t.Errorf("Failed to register a partial on cloned template")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len(tpl.partials) != 1) || (tpl.partials["p"] == nil) {
|
|
||||||
t.Errorf("Modification of a cloned template MUST NOT affect original template")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleTemplate_Exec() {
|
|
||||||
source := "<h1>{{title}}</h1><p>{{body.content}}</p>"
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"title": "foo",
|
|
||||||
"body": map[string]string{"content": "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
// evaluate template with context
|
|
||||||
output, err := tpl.Exec(ctx)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: <h1>foo</h1><p>bar</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleTemplate_MustExec() {
|
|
||||||
source := "<h1>{{title}}</h1><p>{{body.content}}</p>"
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"title": "foo",
|
|
||||||
"body": map[string]string{"content": "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
// evaluate template with context
|
|
||||||
output := tpl.MustExec(ctx)
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: <h1>foo</h1><p>bar</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleTemplate_ExecWith() {
|
|
||||||
source := "<h1>{{title}}</h1><p>{{#body}}{{content}} and {{@baz.bat}}{{/body}}</p>"
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"title": "foo",
|
|
||||||
"body": map[string]string{"content": "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
// computes private data frame
|
|
||||||
frame := NewDataFrame()
|
|
||||||
frame.Set("baz", map[string]string{"bat": "unicorns"})
|
|
||||||
|
|
||||||
// evaluate template
|
|
||||||
output, err := tpl.ExecWith(ctx, frame)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: <h1>foo</h1><p>bar and unicorns</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleTemplate_PrintAST() {
|
|
||||||
source := "<h1>{{title}}</h1><p>{{#body}}{{content}} and {{@baz.bat}}{{/body}}</p>"
|
|
||||||
|
|
||||||
// parse template
|
|
||||||
tpl := MustParse(source)
|
|
||||||
|
|
||||||
// print AST
|
|
||||||
output := tpl.PrintAST()
|
|
||||||
|
|
||||||
fmt.Print(output)
|
|
||||||
// Output: CONTENT[ '<h1>' ]
|
|
||||||
// {{ PATH:title [] }}
|
|
||||||
// CONTENT[ '</h1><p>' ]
|
|
||||||
// BLOCK:
|
|
||||||
// PATH:body []
|
|
||||||
// PROGRAM:
|
|
||||||
// {{ PATH:content []
|
|
||||||
// }}
|
|
||||||
// CONTENT[ ' and ' ]
|
|
||||||
// {{ @PATH:baz/bat []
|
|
||||||
// }}
|
|
||||||
// CONTENT[ '</p>' ]
|
|
||||||
//
|
|
||||||
}
|
|
85
vendor/github.com/aymerick/raymond/utils.go
generated
vendored
85
vendor/github.com/aymerick/raymond/utils.go
generated
vendored
|
@ -1,85 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
|
|
||||||
// We indirect through pointers and empty interfaces (only) because
|
|
||||||
// non-empty interfaces have methods we might need.
|
|
||||||
//
|
|
||||||
// NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go
|
|
||||||
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
|
|
||||||
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
|
|
||||||
if v.IsNil() {
|
|
||||||
return v, true
|
|
||||||
}
|
|
||||||
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTrue returns true if obj is a truthy value.
|
|
||||||
func IsTrue(obj interface{}) bool {
|
|
||||||
thruth, ok := isTrueValue(reflect.ValueOf(obj))
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return thruth
|
|
||||||
}
|
|
||||||
|
|
||||||
// isTrueValue reports whether the value is 'true', in the sense of not the zero of its type,
|
|
||||||
// and whether the value has a meaningful truth value
|
|
||||||
//
|
|
||||||
// NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go
|
|
||||||
func isTrueValue(val reflect.Value) (truth, ok bool) {
|
|
||||||
if !val.IsValid() {
|
|
||||||
// Something like var x interface{}, never set. It's a form of nil.
|
|
||||||
return false, true
|
|
||||||
}
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
||||||
truth = val.Len() > 0
|
|
||||||
case reflect.Bool:
|
|
||||||
truth = val.Bool()
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
truth = val.Complex() != 0
|
|
||||||
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
|
|
||||||
truth = !val.IsNil()
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
truth = val.Int() != 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
truth = val.Float() != 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
truth = val.Uint() != 0
|
|
||||||
case reflect.Struct:
|
|
||||||
truth = true // Struct values are always true.
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return truth, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
|
|
||||||
//
|
|
||||||
// NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go
|
|
||||||
func canBeNil(typ reflect.Type) bool {
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// fileBase returns base file name
|
|
||||||
//
|
|
||||||
// example: /foo/bar/baz.png => baz
|
|
||||||
func fileBase(filePath string) string {
|
|
||||||
fileName := path.Base(filePath)
|
|
||||||
fileExt := path.Ext(filePath)
|
|
||||||
|
|
||||||
return fileName[:len(fileName)-len(fileExt)]
|
|
||||||
}
|
|
51
vendor/github.com/aymerick/raymond/utils_test.go
generated
vendored
51
vendor/github.com/aymerick/raymond/utils_test.go
generated
vendored
|
@ -1,51 +0,0 @@
|
||||||
package raymond
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func ExampleIsTrue() {
|
|
||||||
output := "Empty array: " + Str(IsTrue([0]string{})) + "\n"
|
|
||||||
output += "Non empty array: " + Str(IsTrue([1]string{"foo"})) + "\n"
|
|
||||||
|
|
||||||
output += "Empty slice: " + Str(IsTrue([]string{})) + "\n"
|
|
||||||
output += "Non empty slice: " + Str(IsTrue([]string{"foo"})) + "\n"
|
|
||||||
|
|
||||||
output += "Empty map: " + Str(IsTrue(map[string]string{})) + "\n"
|
|
||||||
output += "Non empty map: " + Str(IsTrue(map[string]string{"foo": "bar"})) + "\n"
|
|
||||||
|
|
||||||
output += "Empty string: " + Str(IsTrue("")) + "\n"
|
|
||||||
output += "Non empty string: " + Str(IsTrue("foo")) + "\n"
|
|
||||||
|
|
||||||
output += "true bool: " + Str(IsTrue(true)) + "\n"
|
|
||||||
output += "false bool: " + Str(IsTrue(false)) + "\n"
|
|
||||||
|
|
||||||
output += "0 integer: " + Str(IsTrue(0)) + "\n"
|
|
||||||
output += "positive integer: " + Str(IsTrue(10)) + "\n"
|
|
||||||
output += "negative integer: " + Str(IsTrue(-10)) + "\n"
|
|
||||||
|
|
||||||
output += "0 float: " + Str(IsTrue(0.0)) + "\n"
|
|
||||||
output += "positive float: " + Str(IsTrue(10.0)) + "\n"
|
|
||||||
output += "negative integer: " + Str(IsTrue(-10.0)) + "\n"
|
|
||||||
|
|
||||||
output += "struct: " + Str(IsTrue(struct{}{})) + "\n"
|
|
||||||
output += "nil: " + Str(IsTrue(nil)) + "\n"
|
|
||||||
|
|
||||||
fmt.Println(output)
|
|
||||||
// Output: Empty array: false
|
|
||||||
// Non empty array: true
|
|
||||||
// Empty slice: false
|
|
||||||
// Non empty slice: true
|
|
||||||
// Empty map: false
|
|
||||||
// Non empty map: true
|
|
||||||
// Empty string: false
|
|
||||||
// Non empty string: true
|
|
||||||
// true bool: true
|
|
||||||
// false bool: false
|
|
||||||
// 0 integer: false
|
|
||||||
// positive integer: true
|
|
||||||
// negative integer: true
|
|
||||||
// 0 float: false
|
|
||||||
// positive float: true
|
|
||||||
// negative integer: true
|
|
||||||
// struct: true
|
|
||||||
// nil: false
|
|
||||||
}
|
|
24
vendor/github.com/matrix-org/gomatrix/.gitignore
generated
vendored
24
vendor/github.com/matrix-org/gomatrix/.gitignore
generated
vendored
|
@ -1,24 +0,0 @@
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
*.test
|
|
||||||
*.prof
|
|
9
vendor/github.com/matrix-org/gomatrix/.travis.yml
generated
vendored
9
vendor/github.com/matrix-org/gomatrix/.travis.yml
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.8
|
|
||||||
install:
|
|
||||||
- go get github.com/golang/lint/golint
|
|
||||||
- go get github.com/fzipp/gocyclo
|
|
||||||
- go get github.com/client9/misspell/...
|
|
||||||
- go get github.com/gordonklaus/ineffassign
|
|
||||||
script: ./hooks/pre-commit
|
|
201
vendor/github.com/matrix-org/gomatrix/LICENSE
generated
vendored
201
vendor/github.com/matrix-org/gomatrix/LICENSE
generated
vendored
|
@ -1,201 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
6
vendor/github.com/matrix-org/gomatrix/README.md
generated
vendored
6
vendor/github.com/matrix-org/gomatrix/README.md
generated
vendored
|
@ -1,6 +0,0 @@
|
||||||
# gomatrix
|
|
||||||
[![GoDoc](https://godoc.org/github.com/matrix-org/gomatrix?status.svg)](https://godoc.org/github.com/matrix-org/gomatrix)
|
|
||||||
|
|
||||||
A Golang Matrix client.
|
|
||||||
|
|
||||||
**THIS IS UNDER ACTIVE DEVELOPMENT: BREAKING CHANGES ARE FREQUENT.**
|
|
703
vendor/github.com/matrix-org/gomatrix/client.go
generated
vendored
703
vendor/github.com/matrix-org/gomatrix/client.go
generated
vendored
|
@ -1,703 +0,0 @@
|
||||||
// Package gomatrix implements the Matrix Client-Server API.
|
|
||||||
//
|
|
||||||
// Specification can be found at http://matrix.org/docs/spec/client_server/r0.2.0.html
|
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client represents a Matrix client.
|
|
||||||
type Client struct {
|
|
||||||
HomeserverURL *url.URL // The base homeserver URL
|
|
||||||
Prefix string // The API prefix eg '/_matrix/client/r0'
|
|
||||||
UserID string // The user ID of the client. Used for forming HTTP paths which use the client's user ID.
|
|
||||||
AccessToken string // The access_token for the client.
|
|
||||||
Client *http.Client // The underlying HTTP client which will be used to make HTTP requests.
|
|
||||||
Syncer Syncer // The thing which can process /sync responses
|
|
||||||
Store Storer // The thing which can store rooms/tokens/ids
|
|
||||||
|
|
||||||
// The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty,
|
|
||||||
// no user_id parameter will be sent.
|
|
||||||
// See http://matrix.org/docs/spec/application_service/unstable.html#identity-assertion
|
|
||||||
AppServiceUserID string
|
|
||||||
|
|
||||||
syncingMutex sync.Mutex // protects syncingID
|
|
||||||
syncingID uint32 // Identifies the current Sync. Only one Sync can be active at any given time.
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
|
|
||||||
type HTTPError struct {
|
|
||||||
WrappedError error
|
|
||||||
Message string
|
|
||||||
Code int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e HTTPError) Error() string {
|
|
||||||
var wrappedErrMsg string
|
|
||||||
if e.WrappedError != nil {
|
|
||||||
wrappedErrMsg = e.WrappedError.Error()
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("msg=%s code=%d wrapped=%s", e.Message, e.Code, wrappedErrMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildURL builds a URL with the Client's homserver/prefix/access_token set already.
|
|
||||||
func (cli *Client) BuildURL(urlPath ...string) string {
|
|
||||||
ps := []string{cli.Prefix}
|
|
||||||
for _, p := range urlPath {
|
|
||||||
ps = append(ps, p)
|
|
||||||
}
|
|
||||||
return cli.BuildBaseURL(ps...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildBaseURL builds a URL with the Client's homeserver/access_token set already. You must
|
|
||||||
// supply the prefix in the path.
|
|
||||||
func (cli *Client) BuildBaseURL(urlPath ...string) string {
|
|
||||||
// copy the URL. Purposefully ignore error as the input is from a valid URL already
|
|
||||||
hsURL, _ := url.Parse(cli.HomeserverURL.String())
|
|
||||||
parts := []string{hsURL.Path}
|
|
||||||
parts = append(parts, urlPath...)
|
|
||||||
hsURL.Path = path.Join(parts...)
|
|
||||||
query := hsURL.Query()
|
|
||||||
if cli.AccessToken != "" {
|
|
||||||
query.Set("access_token", cli.AccessToken)
|
|
||||||
}
|
|
||||||
if cli.AppServiceUserID != "" {
|
|
||||||
query.Set("user_id", cli.AppServiceUserID)
|
|
||||||
}
|
|
||||||
hsURL.RawQuery = query.Encode()
|
|
||||||
return hsURL.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already.
|
|
||||||
func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
|
|
||||||
u, _ := url.Parse(cli.BuildURL(urlPath...))
|
|
||||||
q := u.Query()
|
|
||||||
for k, v := range urlQuery {
|
|
||||||
q.Set(k, v)
|
|
||||||
}
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
return u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCredentials sets the user ID and access token on this client instance.
|
|
||||||
func (cli *Client) SetCredentials(userID, accessToken string) {
|
|
||||||
cli.AccessToken = accessToken
|
|
||||||
cli.UserID = userID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearCredentials removes the user ID and access token on this client instance.
|
|
||||||
func (cli *Client) ClearCredentials() {
|
|
||||||
cli.AccessToken = ""
|
|
||||||
cli.UserID = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync starts syncing with the provided Homeserver. If Sync() is called twice then the first sync will be stopped and the
|
|
||||||
// error will be nil.
|
|
||||||
//
|
|
||||||
// This function will block until a fatal /sync error occurs, so it should almost always be started as a new goroutine.
|
|
||||||
// Fatal sync errors can be caused by:
|
|
||||||
// - The failure to create a filter.
|
|
||||||
// - Client.Syncer.OnFailedSync returning an error in response to a failed sync.
|
|
||||||
// - Client.Syncer.ProcessResponse returning an error.
|
|
||||||
// If you wish to continue retrying in spite of these fatal errors, call Sync() again.
|
|
||||||
func (cli *Client) Sync() error {
|
|
||||||
// Mark the client as syncing.
|
|
||||||
// We will keep syncing until the syncing state changes. Either because
|
|
||||||
// Sync is called or StopSync is called.
|
|
||||||
syncingID := cli.incrementSyncingID()
|
|
||||||
nextBatch := cli.Store.LoadNextBatch(cli.UserID)
|
|
||||||
filterID := cli.Store.LoadFilterID(cli.UserID)
|
|
||||||
if filterID == "" {
|
|
||||||
filterJSON := cli.Syncer.GetFilterJSON(cli.UserID)
|
|
||||||
resFilter, err := cli.CreateFilter(filterJSON)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
filterID = resFilter.FilterID
|
|
||||||
cli.Store.SaveFilterID(cli.UserID, filterID)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
resSync, err := cli.SyncRequest(30000, nextBatch, filterID, false, "")
|
|
||||||
if err != nil {
|
|
||||||
duration, err2 := cli.Syncer.OnFailedSync(resSync, err)
|
|
||||||
if err2 != nil {
|
|
||||||
return err2
|
|
||||||
}
|
|
||||||
time.Sleep(duration)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the syncing state hasn't changed
|
|
||||||
// Either because we've stopped syncing or another sync has been started.
|
|
||||||
// We discard the response from our sync.
|
|
||||||
if cli.getSyncingID() != syncingID {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the token now *before* processing it. This means it's possible
|
|
||||||
// to not process some events, but it means that we won't get constantly stuck processing
|
|
||||||
// a malformed/buggy event which keeps making us panic.
|
|
||||||
cli.Store.SaveNextBatch(cli.UserID, resSync.NextBatch)
|
|
||||||
if err = cli.Syncer.ProcessResponse(resSync, nextBatch); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nextBatch = resSync.NextBatch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) incrementSyncingID() uint32 {
|
|
||||||
cli.syncingMutex.Lock()
|
|
||||||
defer cli.syncingMutex.Unlock()
|
|
||||||
cli.syncingID++
|
|
||||||
return cli.syncingID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) getSyncingID() uint32 {
|
|
||||||
cli.syncingMutex.Lock()
|
|
||||||
defer cli.syncingMutex.Unlock()
|
|
||||||
return cli.syncingID
|
|
||||||
}
|
|
||||||
|
|
||||||
// StopSync stops the ongoing sync started by Sync.
|
|
||||||
func (cli *Client) StopSync() {
|
|
||||||
// Advance the syncing state so that any running Syncs will terminate.
|
|
||||||
cli.incrementSyncingID()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRequest makes a JSON HTTP request to the given URL.
|
|
||||||
// If "resBody" is not nil, the response body will be json.Unmarshalled into it.
|
|
||||||
//
|
|
||||||
// Returns the HTTP body as bytes on 2xx with a nil error. Returns an error if the response is not 2xx along
|
|
||||||
// with the HTTP body bytes if it got that far. This error is an HTTPError which includes the returned
|
|
||||||
// HTTP status code and possibly a RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
|
|
||||||
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) {
|
|
||||||
var req *http.Request
|
|
||||||
var err error
|
|
||||||
if reqBody != nil {
|
|
||||||
var jsonStr []byte
|
|
||||||
jsonStr, err = json.Marshal(reqBody)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr))
|
|
||||||
} else {
|
|
||||||
req, err = http.NewRequest(method, httpURL, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
res, err := cli.Client.Do(req)
|
|
||||||
if res != nil {
|
|
||||||
defer res.Body.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
contents, err := ioutil.ReadAll(res.Body)
|
|
||||||
if res.StatusCode/100 != 2 { // not 2xx
|
|
||||||
var wrap error
|
|
||||||
var respErr RespError
|
|
||||||
if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" {
|
|
||||||
wrap = respErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we failed to decode as RespError, don't just drop the HTTP body, include it in the
|
|
||||||
// HTTP error instead (e.g proxy errors which return HTML).
|
|
||||||
msg := "Failed to " + method + " JSON to " + req.URL.Path
|
|
||||||
if wrap == nil {
|
|
||||||
msg = msg + ": " + string(contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
return contents, HTTPError{
|
|
||||||
Code: res.StatusCode,
|
|
||||||
Message: msg,
|
|
||||||
WrappedError: wrap,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resBody != nil {
|
|
||||||
if err = json.Unmarshal(contents, &resBody); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return contents, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateFilter makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
|
|
||||||
func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) {
|
|
||||||
urlPath := cli.BuildURL("user", cli.UserID, "filter")
|
|
||||||
_, err = cli.MakeRequest("POST", urlPath, &filter, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncRequest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
|
|
||||||
func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bool, setPresence string) (resp *RespSync, err error) {
|
|
||||||
query := map[string]string{
|
|
||||||
"timeout": strconv.Itoa(timeout),
|
|
||||||
}
|
|
||||||
if since != "" {
|
|
||||||
query["since"] = since
|
|
||||||
}
|
|
||||||
if filterID != "" {
|
|
||||||
query["filter"] = filterID
|
|
||||||
}
|
|
||||||
if setPresence != "" {
|
|
||||||
query["set_presence"] = setPresence
|
|
||||||
}
|
|
||||||
if fullState {
|
|
||||||
query["full_state"] = "true"
|
|
||||||
}
|
|
||||||
urlPath := cli.BuildURLWithQuery([]string{"sync"}, query)
|
|
||||||
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) {
|
|
||||||
var bodyBytes []byte
|
|
||||||
bodyBytes, err = cli.MakeRequest("POST", u, req, nil)
|
|
||||||
if err != nil {
|
|
||||||
httpErr, ok := err.(HTTPError)
|
|
||||||
if !ok { // network error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if httpErr.Code == 401 {
|
|
||||||
// body should be RespUserInteractive, if it isn't, fail with the error
|
|
||||||
err = json.Unmarshal(bodyBytes, &uiaResp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// body should be RespRegister
|
|
||||||
err = json.Unmarshal(bodyBytes, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
|
|
||||||
//
|
|
||||||
// Registers with kind=user. For kind=guest, see RegisterGuest.
|
|
||||||
func (cli *Client) Register(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
|
|
||||||
u := cli.BuildURL("register")
|
|
||||||
return cli.register(u, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterGuest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
|
|
||||||
// with kind=guest.
|
|
||||||
//
|
|
||||||
// For kind=user, see Register.
|
|
||||||
func (cli *Client) RegisterGuest(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
|
|
||||||
query := map[string]string{
|
|
||||||
"kind": "guest",
|
|
||||||
}
|
|
||||||
u := cli.BuildURLWithQuery([]string{"register"}, query)
|
|
||||||
return cli.register(u, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterDummy performs m.login.dummy registration according to https://matrix.org/docs/spec/client_server/r0.2.0.html#dummy-auth
|
|
||||||
//
|
|
||||||
// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration
|
|
||||||
// this way. If the homeserver does not, an error is returned.
|
|
||||||
//
|
|
||||||
// This does not set credentials on the client instance. See SetCredentials() instead.
|
|
||||||
//
|
|
||||||
// res, err := cli.RegisterDummy(&gomatrix.ReqRegister{
|
|
||||||
// Username: "alice",
|
|
||||||
// Password: "wonderland",
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// token := res.AccessToken
|
|
||||||
func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
|
|
||||||
res, uia, err := cli.Register(req)
|
|
||||||
if err != nil && uia == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if uia != nil && uia.HasSingleStageFlow("m.login.dummy") {
|
|
||||||
req.Auth = struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Session string `json:"session,omitempty"`
|
|
||||||
}{"m.login.dummy", uia.Session}
|
|
||||||
res, _, err = cli.Register(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if res == nil {
|
|
||||||
return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?")
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login a user to the homeserver according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
|
|
||||||
// This does not set credentials on this client instance. See SetCredentials() instead.
|
|
||||||
func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) {
|
|
||||||
urlPath := cli.BuildURL("login")
|
|
||||||
_, err = cli.MakeRequest("POST", urlPath, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout
|
|
||||||
// This does not clear the credentials from the client instance. See ClearCredentials() instead.
|
|
||||||
func (cli *Client) Logout() (resp *RespLogout, err error) {
|
|
||||||
urlPath := cli.BuildURL("logout")
|
|
||||||
_, err = cli.MakeRequest("POST", urlPath, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
|
|
||||||
func (cli *Client) Versions() (resp *RespVersions, err error) {
|
|
||||||
urlPath := cli.BuildBaseURL("_matrix", "client", "versions")
|
|
||||||
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias
|
|
||||||
//
|
|
||||||
// If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will
|
|
||||||
// be JSON encoded and used as the request body.
|
|
||||||
func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{}) (resp *RespJoinRoom, err error) {
|
|
||||||
var urlPath string
|
|
||||||
if serverName != "" {
|
|
||||||
urlPath = cli.BuildURLWithQuery([]string{"join", roomIDorAlias}, map[string]string{
|
|
||||||
"server_name": serverName,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
urlPath = cli.BuildURL("join", roomIDorAlias)
|
|
||||||
}
|
|
||||||
_, err = cli.MakeRequest("POST", urlPath, content, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
|
|
||||||
func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
|
|
||||||
urlPath := cli.BuildURL("profile", mxid, "displayname")
|
|
||||||
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
|
|
||||||
func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
|
|
||||||
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
|
|
||||||
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
|
|
||||||
func (cli *Client) SetDisplayName(displayName string) (err error) {
|
|
||||||
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
|
|
||||||
s := struct {
|
|
||||||
DisplayName string `json:"displayname"`
|
|
||||||
}{displayName}
|
|
||||||
_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAvatarURL gets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url
|
|
||||||
func (cli *Client) GetAvatarURL() (url string, err error) {
|
|
||||||
urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
|
|
||||||
s := struct {
|
|
||||||
AvatarURL string `json:"avatar_url"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
_, err = cli.MakeRequest("GET", urlPath, nil, &s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.AvatarURL, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAvatarURL sets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url
|
|
||||||
func (cli *Client) SetAvatarURL(url string) (err error) {
|
|
||||||
urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
|
|
||||||
s := struct {
|
|
||||||
AvatarURL string `json:"avatar_url"`
|
|
||||||
}{url}
|
|
||||||
_, err = cli.MakeRequest("PUT", urlPath, &s, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
|
|
||||||
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
|
|
||||||
func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) {
|
|
||||||
txnID := txnID()
|
|
||||||
urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID)
|
|
||||||
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey
|
|
||||||
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
|
|
||||||
func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
|
|
||||||
urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
|
|
||||||
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendText sends an m.room.message event into the given room with a msgtype of m.text
|
|
||||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text
|
|
||||||
func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
|
|
||||||
return cli.SendMessageEvent(roomID, "m.room.message",
|
|
||||||
TextMessage{"m.text", text})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendImage sends an m.room.message event into the given room with a msgtype of m.image
|
|
||||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
|
|
||||||
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
|
|
||||||
return cli.SendMessageEvent(roomID, "m.room.message",
|
|
||||||
ImageMessage{
|
|
||||||
MsgType: "m.image",
|
|
||||||
Body: body,
|
|
||||||
URL: url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video
|
|
||||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
|
|
||||||
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
|
|
||||||
return cli.SendMessageEvent(roomID, "m.room.message",
|
|
||||||
VideoMessage{
|
|
||||||
MsgType: "m.video",
|
|
||||||
Body: body,
|
|
||||||
URL: url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
|
|
||||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
|
|
||||||
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
|
|
||||||
return cli.SendMessageEvent(roomID, "m.room.message",
|
|
||||||
TextMessage{"m.notice", text})
|
|
||||||
}
|
|
||||||
|
|
||||||
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
|
|
||||||
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
|
|
||||||
txnID := txnID()
|
|
||||||
urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID)
|
|
||||||
_, err = cli.MakeRequest("PUT", urlPath, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRoom creates a new Matrix room. See https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
|
|
||||||
// resp, err := cli.CreateRoom(&gomatrix.ReqCreateRoom{
|
|
||||||
// Preset: "public_chat",
|
|
||||||
// })
|
|
||||||
// fmt.Println("Room:", resp.RoomID)
|
|
||||||
func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) {
|
|
||||||
urlPath := cli.BuildURL("createRoom")
|
|
||||||
_, err = cli.MakeRequest("POST", urlPath, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
|
|
||||||
func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "leave")
|
|
||||||
_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
|
|
||||||
func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "forget")
|
|
||||||
_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// InviteUser invites a user to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
|
|
||||||
func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "invite")
|
|
||||||
_, err = cli.MakeRequest("POST", u, struct{}{}, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// InviteUserByThirdParty invites a third-party identifier to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#invite-by-third-party-id-endpoint
|
|
||||||
func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "invite")
|
|
||||||
_, err = cli.MakeRequest("POST", u, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// KickUser kicks a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
|
|
||||||
func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "kick")
|
|
||||||
_, err = cli.MakeRequest("POST", u, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BanUser bans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
|
|
||||||
func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "ban")
|
|
||||||
_, err = cli.MakeRequest("POST", u, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnbanUser unbans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
|
|
||||||
func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "unban")
|
|
||||||
_, err = cli.MakeRequest("POST", u, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
|
|
||||||
func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
|
|
||||||
req := ReqTyping{Typing: typing, Timeout: timeout}
|
|
||||||
u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
|
|
||||||
_, err = cli.MakeRequest("PUT", u, req, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
|
|
||||||
// the HTTP response body, or return an error.
|
|
||||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
|
|
||||||
func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
|
|
||||||
_, err = cli.MakeRequest("GET", u, nil, outContent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadLink uploads an HTTP URL and then returns an MXC URI.
|
|
||||||
func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) {
|
|
||||||
res, err := cli.Client.Get(link)
|
|
||||||
if res != nil {
|
|
||||||
defer res.Body.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI.
|
|
||||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
|
|
||||||
func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) {
|
|
||||||
req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", contentType)
|
|
||||||
req.ContentLength = contentLength
|
|
||||||
res, err := cli.Client.Do(req)
|
|
||||||
if res != nil {
|
|
||||||
defer res.Body.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if res.StatusCode != 200 {
|
|
||||||
contents, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, HTTPError{
|
|
||||||
Message: "Upload request failed - Failed to read response body: " + err.Error(),
|
|
||||||
Code: res.StatusCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, HTTPError{
|
|
||||||
Message: "Upload request failed: " + string(contents),
|
|
||||||
Code: res.StatusCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var m RespMediaUpload
|
|
||||||
if err := json.NewDecoder(res.Body).Decode(&m); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinedMembers returns a map of joined room members. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
|
|
||||||
//
|
|
||||||
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
|
|
||||||
// This API is primarily designed for application services which may want to efficiently look up joined members in a room.
|
|
||||||
func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) {
|
|
||||||
u := cli.BuildURL("rooms", roomID, "joined_members")
|
|
||||||
_, err = cli.MakeRequest("GET", u, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinedRooms returns a list of rooms which the client is joined to. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
|
|
||||||
//
|
|
||||||
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
|
|
||||||
// This API is primarily designed for application services which may want to efficiently look up joined rooms.
|
|
||||||
func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
|
|
||||||
u := cli.BuildURL("joined_rooms")
|
|
||||||
_, err = cli.MakeRequest("GET", u, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Messages returns a list of message and state events for a room. It uses
|
|
||||||
// pagination query parameters to paginate history in the room.
|
|
||||||
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
|
|
||||||
func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) {
|
|
||||||
query := map[string]string{
|
|
||||||
"from": from,
|
|
||||||
"dir": string(dir),
|
|
||||||
}
|
|
||||||
if to != "" {
|
|
||||||
query["to"] = to
|
|
||||||
}
|
|
||||||
if limit != 0 {
|
|
||||||
query["limit"] = strconv.Itoa(limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
|
|
||||||
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TurnServer returns turn server details and credentials for the client to use when initiating calls.
|
|
||||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
|
|
||||||
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
|
|
||||||
urlPath := cli.BuildURL("voip", "turnServer")
|
|
||||||
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func txnID() string {
|
|
||||||
return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient creates a new Matrix Client ready for syncing
|
|
||||||
func NewClient(homeserverURL, userID, accessToken string) (*Client, error) {
|
|
||||||
hsURL, err := url.Parse(homeserverURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// By default, use an in-memory store which will never save filter ids / next batch tokens to disk.
|
|
||||||
// The client will work with this storer: it just won't remember across restarts.
|
|
||||||
// In practice, a database backend should be used.
|
|
||||||
store := NewInMemoryStore()
|
|
||||||
cli := Client{
|
|
||||||
AccessToken: accessToken,
|
|
||||||
HomeserverURL: hsURL,
|
|
||||||
UserID: userID,
|
|
||||||
Prefix: "/_matrix/client/r0",
|
|
||||||
Syncer: NewDefaultSyncer(userID, store),
|
|
||||||
Store: store,
|
|
||||||
}
|
|
||||||
// By default, use the default HTTP client.
|
|
||||||
cli.Client = http.DefaultClient
|
|
||||||
|
|
||||||
return &cli, nil
|
|
||||||
}
|
|
119
vendor/github.com/matrix-org/gomatrix/client_examples_test.go
generated
vendored
119
vendor/github.com/matrix-org/gomatrix/client_examples_test.go
generated
vendored
|
@ -1,119 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Example_sync() {
|
|
||||||
cli, _ := NewClient("https://matrix.org", "@example:matrix.org", "MDAefhiuwehfuiwe")
|
|
||||||
cli.Store.SaveFilterID("@example:matrix.org", "2") // Optional: if you know it already
|
|
||||||
cli.Store.SaveNextBatch("@example:matrix.org", "111_222_333_444") // Optional: if you know it already
|
|
||||||
syncer := cli.Syncer.(*DefaultSyncer)
|
|
||||||
syncer.OnEventType("m.room.message", func(ev *Event) {
|
|
||||||
fmt.Println("Message: ", ev)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Blocking version
|
|
||||||
if err := cli.Sync(); err != nil {
|
|
||||||
fmt.Println("Sync() returned ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-blocking version
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if err := cli.Sync(); err != nil {
|
|
||||||
fmt.Println("Sync() returned ", err)
|
|
||||||
}
|
|
||||||
// Optional: Wait a period of time before trying to sync again.
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Example_customInterfaces() {
|
|
||||||
// Custom interfaces must be set prior to calling functions on the client.
|
|
||||||
cli, _ := NewClient("https://matrix.org", "@example:matrix.org", "MDAefhiuwehfuiwe")
|
|
||||||
|
|
||||||
// anything which implements the Storer interface
|
|
||||||
customStore := NewInMemoryStore()
|
|
||||||
cli.Store = customStore
|
|
||||||
|
|
||||||
// anything which implements the Syncer interface
|
|
||||||
customSyncer := NewDefaultSyncer("@example:matrix.org", customStore)
|
|
||||||
cli.Syncer = customSyncer
|
|
||||||
|
|
||||||
// any http.Client
|
|
||||||
cli.Client = http.DefaultClient
|
|
||||||
|
|
||||||
// Once you call a function, you can't safely change the interfaces.
|
|
||||||
cli.SendText("!foo:bar", "Down the rabbit hole")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleClient_BuildURLWithQuery() {
|
|
||||||
cli, _ := NewClient("https://matrix.org", "@example:matrix.org", "abcdef123456")
|
|
||||||
out := cli.BuildURLWithQuery([]string{"sync"}, map[string]string{
|
|
||||||
"filter_id": "5",
|
|
||||||
})
|
|
||||||
fmt.Println(out)
|
|
||||||
// Output: https://matrix.org/_matrix/client/r0/sync?access_token=abcdef123456&filter_id=5
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleClient_BuildURL() {
|
|
||||||
userID := "@example:matrix.org"
|
|
||||||
cli, _ := NewClient("https://matrix.org", userID, "abcdef123456")
|
|
||||||
out := cli.BuildURL("user", userID, "filter")
|
|
||||||
fmt.Println(out)
|
|
||||||
// Output: https://matrix.org/_matrix/client/r0/user/@example:matrix.org/filter?access_token=abcdef123456
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleClient_BuildBaseURL() {
|
|
||||||
userID := "@example:matrix.org"
|
|
||||||
cli, _ := NewClient("https://matrix.org", userID, "abcdef123456")
|
|
||||||
out := cli.BuildBaseURL("_matrix", "client", "r0", "directory", "room", "#matrix:matrix.org")
|
|
||||||
fmt.Println(out)
|
|
||||||
// Output: https://matrix.org/_matrix/client/r0/directory/room/%23matrix:matrix.org?access_token=abcdef123456
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the content of a m.room.name state event.
|
|
||||||
func ExampleClient_StateEvent() {
|
|
||||||
content := struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}{}
|
|
||||||
cli, _ := NewClient("https://matrix.org", "@example:matrix.org", "abcdef123456")
|
|
||||||
if err := cli.StateEvent("!foo:bar", "m.room.name", "", &content); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join a room by ID.
|
|
||||||
func ExampleClient_JoinRoom_id() {
|
|
||||||
cli, _ := NewClient("http://localhost:8008", "@example:localhost", "abcdef123456")
|
|
||||||
if _, err := cli.JoinRoom("!uOILRrqxnsYgQdUzar:localhost", "", nil); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join a room by alias.
|
|
||||||
func ExampleClient_JoinRoom_alias() {
|
|
||||||
cli, _ := NewClient("http://localhost:8008", "@example:localhost", "abcdef123456")
|
|
||||||
if resp, err := cli.JoinRoom("#test:localhost", "", nil); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
// Use room ID for something.
|
|
||||||
_ = resp.RoomID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login to a local homeserver and set the user ID and access token on success.
|
|
||||||
func ExampleClient_Login() {
|
|
||||||
cli, _ := NewClient("http://localhost:8008", "", "")
|
|
||||||
resp, err := cli.Login(&ReqLogin{
|
|
||||||
Type: "m.login.password",
|
|
||||||
User: "alice",
|
|
||||||
Password: "wonderland",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
cli.SetCredentials(resp.UserID, resp.AccessToken)
|
|
||||||
}
|
|
105
vendor/github.com/matrix-org/gomatrix/client_test.go
generated
vendored
105
vendor/github.com/matrix-org/gomatrix/client_test.go
generated
vendored
|
@ -1,105 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestClient_LeaveRoom(t *testing.T) {
|
|
||||||
cli := mockClient(func(req *http.Request) (*http.Response, error) {
|
|
||||||
if req.Method == "POST" && req.URL.Path == "/_matrix/client/r0/rooms/!foo:bar/leave" {
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{}`)),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unhandled URL: %s", req.URL.Path)
|
|
||||||
})
|
|
||||||
|
|
||||||
if _, err := cli.LeaveRoom("!foo:bar"); err != nil {
|
|
||||||
t.Fatalf("LeaveRoom: error, got %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClient_GetAvatarUrl(t *testing.T) {
|
|
||||||
cli := mockClient(func(req *http.Request) (*http.Response, error) {
|
|
||||||
if req.Method == "GET" && req.URL.Path == "/_matrix/client/r0/profile/@user:test.gomatrix.org/avatar_url" {
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{"avatar_url":"mxc://matrix.org/iJaUjkshgdfsdkjfn"}`)),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unhandled URL: %s", req.URL.Path)
|
|
||||||
})
|
|
||||||
|
|
||||||
if response, err := cli.GetAvatarURL(); err != nil {
|
|
||||||
t.Fatalf("GetAvatarURL: Got error: %s", err.Error())
|
|
||||||
} else if response == "" {
|
|
||||||
t.Fatal("GetAvatarURL: Got empty response")
|
|
||||||
} else if response != "mxc://matrix.org/iJaUjkshgdfsdkjfn" {
|
|
||||||
t.Fatalf("Unexpected response URL: %s", response)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClient_SetAvatarUrl(t *testing.T) {
|
|
||||||
cli := mockClient(func(req *http.Request) (*http.Response, error) {
|
|
||||||
if req.Method == "PUT" && req.URL.Path == "/_matrix/client/r0/profile/@user:test.gomatrix.org/avatar_url" {
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{}`)),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unhandled URL: %s", req.URL.Path)
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := cli.SetAvatarURL("https://foo.com/bar.png"); err != nil {
|
|
||||||
t.Fatalf("GetAvatarURL: Got error: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClient_StateEvent(t *testing.T) {
|
|
||||||
cli := mockClient(func(req *http.Request) (*http.Response, error) {
|
|
||||||
if req.Method == "GET" && req.URL.Path == "/_matrix/client/r0/rooms/!foo:bar/state/m.room.name" {
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: 200,
|
|
||||||
Body: ioutil.NopCloser(bytes.NewBufferString(`{"name":"Room Name Goes Here"}`)),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unhandled URL: %s", req.URL.Path)
|
|
||||||
})
|
|
||||||
|
|
||||||
content := struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
if err := cli.StateEvent("!foo:bar", "m.room.name", "", &content); err != nil {
|
|
||||||
t.Fatalf("StateEvent: error, got %s", err.Error())
|
|
||||||
}
|
|
||||||
if content.Name != "Room Name Goes Here" {
|
|
||||||
t.Fatalf("StateEvent: got %s, want %s", content.Name, "Room Name Goes Here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockClient(fn func(*http.Request) (*http.Response, error)) *Client {
|
|
||||||
mrt := MockRoundTripper{
|
|
||||||
RT: fn,
|
|
||||||
}
|
|
||||||
|
|
||||||
cli, _ := NewClient("https://test.gomatrix.org", "@user:test.gomatrix.org", "abcdef")
|
|
||||||
cli.Client = &http.Client{
|
|
||||||
Transport: mrt,
|
|
||||||
}
|
|
||||||
return cli
|
|
||||||
}
|
|
||||||
|
|
||||||
type MockRoundTripper struct {
|
|
||||||
RT func(*http.Request) (*http.Response, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t MockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
return t.RT(req)
|
|
||||||
}
|
|
101
vendor/github.com/matrix-org/gomatrix/events.go
generated
vendored
101
vendor/github.com/matrix-org/gomatrix/events.go
generated
vendored
|
@ -1,101 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event represents a single Matrix event.
|
|
||||||
type Event struct {
|
|
||||||
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
|
|
||||||
Sender string `json:"sender"` // The user ID of the sender of the event
|
|
||||||
Type string `json:"type"` // The event type
|
|
||||||
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
|
|
||||||
ID string `json:"event_id"` // The unique ID of this event
|
|
||||||
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
|
|
||||||
Content map[string]interface{} `json:"content"` // The JSON content of the event.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Body returns the value of the "body" key in the event content if it is
|
|
||||||
// present and is a string.
|
|
||||||
func (event *Event) Body() (body string, ok bool) {
|
|
||||||
value, exists := event.Content["body"]
|
|
||||||
if !exists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
body, ok = value.(string)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageType returns the value of the "msgtype" key in the event content if
|
|
||||||
// it is present and is a string.
|
|
||||||
func (event *Event) MessageType() (msgtype string, ok bool) {
|
|
||||||
value, exists := event.Content["msgtype"]
|
|
||||||
if !exists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msgtype, ok = value.(string)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextMessage is the contents of a Matrix formated message event.
|
|
||||||
type TextMessage struct {
|
|
||||||
MsgType string `json:"msgtype"`
|
|
||||||
Body string `json:"body"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
|
|
||||||
type ImageInfo struct {
|
|
||||||
Height uint `json:"h,omitempty"`
|
|
||||||
Width uint `json:"w,omitempty"`
|
|
||||||
Mimetype string `json:"mimetype,omitempty"`
|
|
||||||
Size uint `json:"size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
|
|
||||||
type VideoInfo struct {
|
|
||||||
Mimetype string `json:"mimetype,omitempty"`
|
|
||||||
ThumbnailInfo ImageInfo `json:"thumbnail_info"`
|
|
||||||
ThumbnailURL string `json:"thumbnail_url,omitempty"`
|
|
||||||
Height uint `json:"h,omitempty"`
|
|
||||||
Width uint `json:"w,omitempty"`
|
|
||||||
Duration uint `json:"duration,omitempty"`
|
|
||||||
Size uint `json:"size,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
|
|
||||||
type VideoMessage struct {
|
|
||||||
MsgType string `json:"msgtype"`
|
|
||||||
Body string `json:"body"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
Info VideoInfo `json:"info"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageMessage is an m.image event
|
|
||||||
type ImageMessage struct {
|
|
||||||
MsgType string `json:"msgtype"`
|
|
||||||
Body string `json:"body"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
Info ImageInfo `json:"info"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// An HTMLMessage is the contents of a Matrix HTML formated message event.
|
|
||||||
type HTMLMessage struct {
|
|
||||||
Body string `json:"body"`
|
|
||||||
MsgType string `json:"msgtype"`
|
|
||||||
Format string `json:"format"`
|
|
||||||
FormattedBody string `json:"formatted_body"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var htmlRegex = regexp.MustCompile("<[^<]+?>")
|
|
||||||
|
|
||||||
// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition
|
|
||||||
// to the provided HTML.
|
|
||||||
func GetHTMLMessage(msgtype, htmlText string) HTMLMessage {
|
|
||||||
return HTMLMessage{
|
|
||||||
Body: html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")),
|
|
||||||
MsgType: msgtype,
|
|
||||||
Format: "org.matrix.custom.html",
|
|
||||||
FormattedBody: htmlText,
|
|
||||||
}
|
|
||||||
}
|
|
43
vendor/github.com/matrix-org/gomatrix/filter.go
generated
vendored
43
vendor/github.com/matrix-org/gomatrix/filter.go
generated
vendored
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2017 Jan Christian Grünhage
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
|
|
||||||
//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering
|
|
||||||
type Filter struct {
|
|
||||||
AccountData FilterPart `json:"account_data,omitempty"`
|
|
||||||
EventFields []string `json:"event_fields,omitempty"`
|
|
||||||
EventFormat string `json:"event_format,omitempty"`
|
|
||||||
Presence FilterPart `json:"presence,omitempty"`
|
|
||||||
Room struct {
|
|
||||||
AccountData FilterPart `json:"account_data,omitempty"`
|
|
||||||
Ephemeral FilterPart `json:"ephemeral,omitempty"`
|
|
||||||
IncludeLeave bool `json:"include_leave,omitempty"`
|
|
||||||
NotRooms []string `json:"not_rooms,omitempty"`
|
|
||||||
Rooms []string `json:"rooms,omitempty"`
|
|
||||||
State FilterPart `json:"state,omitempty"`
|
|
||||||
Timeline FilterPart `json:"timeline,omitempty"`
|
|
||||||
} `json:"room,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FilterPart struct {
|
|
||||||
NotRooms []string `json:"not_rooms,omitempty"`
|
|
||||||
Rooms []string `json:"rooms,omitempty"`
|
|
||||||
Limit *int `json:"limit,omitempty"`
|
|
||||||
NotSenders []string `json:"not_senders,omitempty"`
|
|
||||||
NotTypes []string `json:"not_types,omitempty"`
|
|
||||||
Senders []string `json:"senders,omitempty"`
|
|
||||||
Types []string `json:"types,omitempty"`
|
|
||||||
}
|
|
5
vendor/github.com/matrix-org/gomatrix/hooks/install.sh
generated
vendored
5
vendor/github.com/matrix-org/gomatrix/hooks/install.sh
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
#! /bin/bash
|
|
||||||
|
|
||||||
DOT_GIT="$(dirname $0)/../.git"
|
|
||||||
|
|
||||||
ln -s "../../hooks/pre-commit" "$DOT_GIT/hooks/pre-commit"
|
|
26
vendor/github.com/matrix-org/gomatrix/hooks/pre-commit
generated
vendored
26
vendor/github.com/matrix-org/gomatrix/hooks/pre-commit
generated
vendored
|
@ -1,26 +0,0 @@
|
||||||
#! /bin/bash
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
golint
|
|
||||||
misspell --error .
|
|
||||||
|
|
||||||
# gofmt doesn't exit with an error code if the files don't match the expected
|
|
||||||
# format. So we have to run it and see if it outputs anything.
|
|
||||||
if gofmt -l -s . 2>&1 | read
|
|
||||||
then
|
|
||||||
echo "Error: not all code had been formatted with gofmt."
|
|
||||||
echo "Fixing the following files"
|
|
||||||
gofmt -s -w -l .
|
|
||||||
echo
|
|
||||||
echo "Please add them to the commit"
|
|
||||||
git status --short
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ineffassign .
|
|
||||||
|
|
||||||
go fmt
|
|
||||||
go tool vet --all --shadow .
|
|
||||||
gocyclo -over 12 .
|
|
||||||
go test -timeout 5s -test.v
|
|
78
vendor/github.com/matrix-org/gomatrix/requests.go
generated
vendored
78
vendor/github.com/matrix-org/gomatrix/requests.go
generated
vendored
|
@ -1,78 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
// ReqRegister is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
|
|
||||||
type ReqRegister struct {
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
BindEmail bool `json:"bind_email,omitempty"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
DeviceID string `json:"device_id,omitempty"`
|
|
||||||
InitialDeviceDisplayName string `json:"initial_device_display_name"`
|
|
||||||
Auth interface{} `json:"auth,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
|
|
||||||
type ReqLogin struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
Medium string `json:"medium,omitempty"`
|
|
||||||
User string `json:"user,omitempty"`
|
|
||||||
Address string `json:"address,omitempty"`
|
|
||||||
Token string `json:"token,omitempty"`
|
|
||||||
DeviceID string `json:"device_id,omitempty"`
|
|
||||||
InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
|
|
||||||
type ReqCreateRoom struct {
|
|
||||||
Visibility string `json:"visibility,omitempty"`
|
|
||||||
RoomAliasName string `json:"room_alias_name,omitempty"`
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Topic string `json:"topic,omitempty"`
|
|
||||||
Invite []string `json:"invite,omitempty"`
|
|
||||||
Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"`
|
|
||||||
CreationContent map[string]interface{} `json:"creation_content,omitempty"`
|
|
||||||
InitialState []Event `json:"initial_state,omitempty"`
|
|
||||||
Preset string `json:"preset,omitempty"`
|
|
||||||
IsDirect bool `json:"is_direct,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqRedact is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
|
|
||||||
type ReqRedact struct {
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqInvite3PID is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#id57
|
|
||||||
// It is also a JSON object used in https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
|
|
||||||
type ReqInvite3PID struct {
|
|
||||||
IDServer string `json:"id_server"`
|
|
||||||
Medium string `json:"medium"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqInviteUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
|
|
||||||
type ReqInviteUser struct {
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqKickUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
|
|
||||||
type ReqKickUser struct {
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqBanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
|
|
||||||
type ReqBanUser struct {
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqUnbanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
|
|
||||||
type ReqUnbanUser struct {
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
|
|
||||||
type ReqTyping struct {
|
|
||||||
Typing bool `json:"typing"`
|
|
||||||
Timeout int64 `json:"timeout"`
|
|
||||||
}
|
|
176
vendor/github.com/matrix-org/gomatrix/responses.go
generated
vendored
176
vendor/github.com/matrix-org/gomatrix/responses.go
generated
vendored
|
@ -1,176 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
|
|
||||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards
|
|
||||||
type RespError struct {
|
|
||||||
ErrCode string `json:"errcode"`
|
|
||||||
Err string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the errcode and error message.
|
|
||||||
func (e RespError) Error() string {
|
|
||||||
return e.ErrCode + ": " + e.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespCreateFilter is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
|
|
||||||
type RespCreateFilter struct {
|
|
||||||
FilterID string `json:"filter_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespVersions is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
|
|
||||||
type RespVersions struct {
|
|
||||||
Versions []string `json:"versions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join
|
|
||||||
type RespJoinRoom struct {
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespLeaveRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
|
|
||||||
type RespLeaveRoom struct{}
|
|
||||||
|
|
||||||
// RespForgetRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
|
|
||||||
type RespForgetRoom struct{}
|
|
||||||
|
|
||||||
// RespInviteUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
|
|
||||||
type RespInviteUser struct{}
|
|
||||||
|
|
||||||
// RespKickUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
|
|
||||||
type RespKickUser struct{}
|
|
||||||
|
|
||||||
// RespBanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
|
|
||||||
type RespBanUser struct{}
|
|
||||||
|
|
||||||
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
|
|
||||||
type RespUnbanUser struct{}
|
|
||||||
|
|
||||||
// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
|
|
||||||
type RespTyping struct{}
|
|
||||||
|
|
||||||
// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
|
|
||||||
type RespJoinedRooms struct {
|
|
||||||
JoinedRooms []string `json:"joined_rooms"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespJoinedMembers is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
|
|
||||||
type RespJoinedMembers struct {
|
|
||||||
Joined map[string]struct {
|
|
||||||
DisplayName *string `json:"display_name"`
|
|
||||||
AvatarURL *string `json:"avatar_url"`
|
|
||||||
} `json:"joined"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
|
|
||||||
type RespMessages struct {
|
|
||||||
Start string `json:"start"`
|
|
||||||
Chunk []Event `json:"chunk"`
|
|
||||||
End string `json:"end"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
|
|
||||||
type RespSendEvent struct {
|
|
||||||
EventID string `json:"event_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespMediaUpload is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
|
|
||||||
type RespMediaUpload struct {
|
|
||||||
ContentURI string `json:"content_uri"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api
|
|
||||||
type RespUserInteractive struct {
|
|
||||||
Flows []struct {
|
|
||||||
Stages []string `json:"stages"`
|
|
||||||
} `json:"flows"`
|
|
||||||
Params map[string]interface{} `json:"params"`
|
|
||||||
Session string `json:"string"`
|
|
||||||
Completed []string `json:"completed"`
|
|
||||||
ErrCode string `json:"errcode"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
|
|
||||||
func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
|
|
||||||
for _, f := range r.Flows {
|
|
||||||
if len(f.Stages) == 1 && f.Stages[0] == stageName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
|
|
||||||
type RespUserDisplayName struct {
|
|
||||||
DisplayName string `json:"displayname"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
|
|
||||||
type RespRegister struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
DeviceID string `json:"device_id"`
|
|
||||||
HomeServer string `json:"home_server"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
|
|
||||||
type RespLogin struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
DeviceID string `json:"device_id"`
|
|
||||||
HomeServer string `json:"home_server"`
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout
|
|
||||||
type RespLogout struct{}
|
|
||||||
|
|
||||||
// RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
|
|
||||||
type RespCreateRoom struct {
|
|
||||||
RoomID string `json:"room_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
|
|
||||||
type RespSync struct {
|
|
||||||
NextBatch string `json:"next_batch"`
|
|
||||||
AccountData struct {
|
|
||||||
Events []Event `json:"events"`
|
|
||||||
} `json:"account_data"`
|
|
||||||
Presence struct {
|
|
||||||
Events []Event `json:"events"`
|
|
||||||
} `json:"presence"`
|
|
||||||
Rooms struct {
|
|
||||||
Leave map[string]struct {
|
|
||||||
State struct {
|
|
||||||
Events []Event `json:"events"`
|
|
||||||
} `json:"state"`
|
|
||||||
Timeline struct {
|
|
||||||
Events []Event `json:"events"`
|
|
||||||
Limited bool `json:"limited"`
|
|
||||||
PrevBatch string `json:"prev_batch"`
|
|
||||||
} `json:"timeline"`
|
|
||||||
} `json:"leave"`
|
|
||||||
Join map[string]struct {
|
|
||||||
State struct {
|
|
||||||
Events []Event `json:"events"`
|
|
||||||
} `json:"state"`
|
|
||||||
Timeline struct {
|
|
||||||
Events []Event `json:"events"`
|
|
||||||
Limited bool `json:"limited"`
|
|
||||||
PrevBatch string `json:"prev_batch"`
|
|
||||||
} `json:"timeline"`
|
|
||||||
} `json:"join"`
|
|
||||||
Invite map[string]struct {
|
|
||||||
State struct {
|
|
||||||
Events []Event
|
|
||||||
} `json:"invite_state"`
|
|
||||||
} `json:"invite"`
|
|
||||||
} `json:"rooms"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RespTurnServer struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
TTL int `json:"ttl"`
|
|
||||||
URIs []string `json:"uris"`
|
|
||||||
}
|
|
50
vendor/github.com/matrix-org/gomatrix/room.go
generated
vendored
50
vendor/github.com/matrix-org/gomatrix/room.go
generated
vendored
|
@ -1,50 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
// Room represents a single Matrix room.
|
|
||||||
type Room struct {
|
|
||||||
ID string
|
|
||||||
State map[string]map[string]*Event
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateState updates the room's current state with the given Event. This will clobber events based
|
|
||||||
// on the type/state_key combination.
|
|
||||||
func (room Room) UpdateState(event *Event) {
|
|
||||||
_, exists := room.State[event.Type]
|
|
||||||
if !exists {
|
|
||||||
room.State[event.Type] = make(map[string]*Event)
|
|
||||||
}
|
|
||||||
room.State[event.Type][*event.StateKey] = event
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
|
|
||||||
func (room Room) GetStateEvent(eventType string, stateKey string) *Event {
|
|
||||||
stateEventMap, _ := room.State[eventType]
|
|
||||||
event, _ := stateEventMap[stateKey]
|
|
||||||
return event
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMembershipState returns the membership state of the given user ID in this room. If there is
|
|
||||||
// no entry for this member, 'leave' is returned for consistency with left users.
|
|
||||||
func (room Room) GetMembershipState(userID string) string {
|
|
||||||
state := "leave"
|
|
||||||
event := room.GetStateEvent("m.room.member", userID)
|
|
||||||
if event != nil {
|
|
||||||
membershipState, found := event.Content["membership"]
|
|
||||||
if found {
|
|
||||||
mState, isString := membershipState.(string)
|
|
||||||
if isString {
|
|
||||||
state = mState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRoom creates a new Room with the given ID
|
|
||||||
func NewRoom(roomID string) *Room {
|
|
||||||
// Init the State map and return a pointer to the Room
|
|
||||||
return &Room{
|
|
||||||
ID: roomID,
|
|
||||||
State: make(map[string]map[string]*Event),
|
|
||||||
}
|
|
||||||
}
|
|
65
vendor/github.com/matrix-org/gomatrix/store.go
generated
vendored
65
vendor/github.com/matrix-org/gomatrix/store.go
generated
vendored
|
@ -1,65 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
// Storer is an interface which must be satisfied to store client data.
|
|
||||||
//
|
|
||||||
// You can either write a struct which persists this data to disk, or you can use the
|
|
||||||
// provided "InMemoryStore" which just keeps data around in-memory which is lost on
|
|
||||||
// restarts.
|
|
||||||
type Storer interface {
|
|
||||||
SaveFilterID(userID, filterID string)
|
|
||||||
LoadFilterID(userID string) string
|
|
||||||
SaveNextBatch(userID, nextBatchToken string)
|
|
||||||
LoadNextBatch(userID string) string
|
|
||||||
SaveRoom(room *Room)
|
|
||||||
LoadRoom(roomID string) *Room
|
|
||||||
}
|
|
||||||
|
|
||||||
// InMemoryStore implements the Storer interface.
|
|
||||||
//
|
|
||||||
// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
|
|
||||||
// or next batch tokens on any goroutine other than the syncing goroutine: the one
|
|
||||||
// which called Client.Sync().
|
|
||||||
type InMemoryStore struct {
|
|
||||||
Filters map[string]string
|
|
||||||
NextBatch map[string]string
|
|
||||||
Rooms map[string]*Room
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveFilterID to memory.
|
|
||||||
func (s *InMemoryStore) SaveFilterID(userID, filterID string) {
|
|
||||||
s.Filters[userID] = filterID
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadFilterID from memory.
|
|
||||||
func (s *InMemoryStore) LoadFilterID(userID string) string {
|
|
||||||
return s.Filters[userID]
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveNextBatch to memory.
|
|
||||||
func (s *InMemoryStore) SaveNextBatch(userID, nextBatchToken string) {
|
|
||||||
s.NextBatch[userID] = nextBatchToken
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadNextBatch from memory.
|
|
||||||
func (s *InMemoryStore) LoadNextBatch(userID string) string {
|
|
||||||
return s.NextBatch[userID]
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveRoom to memory.
|
|
||||||
func (s *InMemoryStore) SaveRoom(room *Room) {
|
|
||||||
s.Rooms[room.ID] = room
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadRoom from memory.
|
|
||||||
func (s *InMemoryStore) LoadRoom(roomID string) *Room {
|
|
||||||
return s.Rooms[roomID]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInMemoryStore constructs a new InMemoryStore.
|
|
||||||
func NewInMemoryStore() *InMemoryStore {
|
|
||||||
return &InMemoryStore{
|
|
||||||
Filters: make(map[string]string),
|
|
||||||
NextBatch: make(map[string]string),
|
|
||||||
Rooms: make(map[string]*Room),
|
|
||||||
}
|
|
||||||
}
|
|
164
vendor/github.com/matrix-org/gomatrix/sync.go
generated
vendored
164
vendor/github.com/matrix-org/gomatrix/sync.go
generated
vendored
|
@ -1,164 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"runtime/debug"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Syncer represents an interface that must be satisfied in order to do /sync requests on a client.
|
|
||||||
type Syncer interface {
|
|
||||||
// Process the /sync response. The since parameter is the since= value that was used to produce the response.
|
|
||||||
// This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped
|
|
||||||
// permanently.
|
|
||||||
ProcessResponse(resp *RespSync, since string) error
|
|
||||||
// OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently.
|
|
||||||
OnFailedSync(res *RespSync, err error) (time.Duration, error)
|
|
||||||
// GetFilterJSON for the given user ID. NOT the filter ID.
|
|
||||||
GetFilterJSON(userID string) json.RawMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively
|
|
||||||
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
|
|
||||||
// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information.
|
|
||||||
type DefaultSyncer struct {
|
|
||||||
UserID string
|
|
||||||
Store Storer
|
|
||||||
listeners map[string][]OnEventListener // event type to listeners array
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events.
|
|
||||||
type OnEventListener func(*Event)
|
|
||||||
|
|
||||||
// NewDefaultSyncer returns an instantiated DefaultSyncer
|
|
||||||
func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer {
|
|
||||||
return &DefaultSyncer{
|
|
||||||
UserID: userID,
|
|
||||||
Store: store,
|
|
||||||
listeners: make(map[string][]OnEventListener),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
|
|
||||||
// unrepeating events. Returns a fatal error if a listener panics.
|
|
||||||
func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) {
|
|
||||||
if !s.shouldProcessResponse(res, since) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.UserID, since, r, debug.Stack())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for roomID, roomData := range res.Rooms.Join {
|
|
||||||
room := s.getOrCreateRoom(roomID)
|
|
||||||
for _, event := range roomData.State.Events {
|
|
||||||
event.RoomID = roomID
|
|
||||||
room.UpdateState(&event)
|
|
||||||
s.notifyListeners(&event)
|
|
||||||
}
|
|
||||||
for _, event := range roomData.Timeline.Events {
|
|
||||||
event.RoomID = roomID
|
|
||||||
s.notifyListeners(&event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for roomID, roomData := range res.Rooms.Invite {
|
|
||||||
room := s.getOrCreateRoom(roomID)
|
|
||||||
for _, event := range roomData.State.Events {
|
|
||||||
event.RoomID = roomID
|
|
||||||
room.UpdateState(&event)
|
|
||||||
s.notifyListeners(&event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for roomID, roomData := range res.Rooms.Leave {
|
|
||||||
room := s.getOrCreateRoom(roomID)
|
|
||||||
for _, event := range roomData.Timeline.Events {
|
|
||||||
if event.StateKey != nil {
|
|
||||||
event.RoomID = roomID
|
|
||||||
room.UpdateState(&event)
|
|
||||||
s.notifyListeners(&event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnEventType allows callers to be notified when there are new events for the given event type.
|
|
||||||
// There are no duplicate checks.
|
|
||||||
func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) {
|
|
||||||
_, exists := s.listeners[eventType]
|
|
||||||
if !exists {
|
|
||||||
s.listeners[eventType] = []OnEventListener{}
|
|
||||||
}
|
|
||||||
s.listeners[eventType] = append(s.listeners[eventType], callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
|
|
||||||
// stuff that shouldn't be processed.
|
|
||||||
func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool {
|
|
||||||
if since == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// This is a horrible hack because /sync will return the most recent messages for a room
|
|
||||||
// as soon as you /join it. We do NOT want to process those events in that particular room
|
|
||||||
// because they may have already been processed (if you toggle the bot in/out of the room).
|
|
||||||
//
|
|
||||||
// Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
|
|
||||||
// exists and is "join" and then discard processing that room entirely if so.
|
|
||||||
// TODO: We probably want to process messages from after the last join event in the timeline.
|
|
||||||
for roomID, roomData := range resp.Rooms.Join {
|
|
||||||
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
|
|
||||||
e := roomData.Timeline.Events[i]
|
|
||||||
if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
|
|
||||||
m := e.Content["membership"]
|
|
||||||
mship, ok := m.(string)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if mship == "join" {
|
|
||||||
_, ok := resp.Rooms.Join[roomID]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
delete(resp.Rooms.Join, roomID) // don't re-process messages
|
|
||||||
delete(resp.Rooms.Invite, roomID) // don't re-process invites
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// getOrCreateRoom must only be called by the Sync() goroutine which calls ProcessResponse()
|
|
||||||
func (s *DefaultSyncer) getOrCreateRoom(roomID string) *Room {
|
|
||||||
room := s.Store.LoadRoom(roomID)
|
|
||||||
if room == nil { // create a new Room
|
|
||||||
room = NewRoom(roomID)
|
|
||||||
s.Store.SaveRoom(room)
|
|
||||||
}
|
|
||||||
return room
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultSyncer) notifyListeners(event *Event) {
|
|
||||||
listeners, exists := s.listeners[event.Type]
|
|
||||||
if !exists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, fn := range listeners {
|
|
||||||
fn(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
|
|
||||||
func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) {
|
|
||||||
return 10 * time.Second, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFilterJSON returns a filter with a timeline limit of 50.
|
|
||||||
func (s *DefaultSyncer) GetFilterJSON(userID string) json.RawMessage {
|
|
||||||
return json.RawMessage(`{"room":{"timeline":{"limit":50}}}`)
|
|
||||||
}
|
|
130
vendor/github.com/matrix-org/gomatrix/userids.go
generated
vendored
130
vendor/github.com/matrix-org/gomatrix/userids.go
generated
vendored
|
@ -1,130 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const lowerhex = "0123456789abcdef"
|
|
||||||
|
|
||||||
// encode the given byte using quoted-printable encoding (e.g "=2f")
|
|
||||||
// and writes it to the buffer
|
|
||||||
// See https://golang.org/src/mime/quotedprintable/writer.go
|
|
||||||
func encode(buf *bytes.Buffer, b byte) {
|
|
||||||
buf.WriteByte('=')
|
|
||||||
buf.WriteByte(lowerhex[b>>4])
|
|
||||||
buf.WriteByte(lowerhex[b&0x0f])
|
|
||||||
}
|
|
||||||
|
|
||||||
// escape the given alpha character and writes it to the buffer
|
|
||||||
func escape(buf *bytes.Buffer, b byte) {
|
|
||||||
buf.WriteByte('_')
|
|
||||||
if b == '_' {
|
|
||||||
buf.WriteByte('_') // another _
|
|
||||||
} else {
|
|
||||||
buf.WriteByte(b + 0x20) // ASCII shift A-Z to a-z
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldEncode(b byte) bool {
|
|
||||||
return b != '-' && b != '.' && b != '_' && !(b >= '0' && b <= '9') && !(b >= 'a' && b <= 'z') && !(b >= 'A' && b <= 'Z')
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldEscape(b byte) bool {
|
|
||||||
return (b >= 'A' && b <= 'Z') || b == '_'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidByte(b byte) bool {
|
|
||||||
return isValidEscapedChar(b) || (b >= '0' && b <= '9') || b == '.' || b == '=' || b == '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidEscapedChar(b byte) bool {
|
|
||||||
return b == '_' || (b >= 'a' && b <= 'z')
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeUserLocalpart encodes the given string into Matrix-compliant user ID localpart form.
|
|
||||||
// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
|
|
||||||
//
|
|
||||||
// This returns a string with only the characters "a-z0-9._=-". The uppercase range A-Z
|
|
||||||
// are encoded using leading underscores ("_"). Characters outside the aforementioned ranges
|
|
||||||
// (including literal underscores ("_") and equals ("=")) are encoded as UTF8 code points (NOT NCRs)
|
|
||||||
// and converted to lower-case hex with a leading "=". For example:
|
|
||||||
// Alph@Bet_50up => _alph=40_bet=5f50up
|
|
||||||
func EncodeUserLocalpart(str string) string {
|
|
||||||
strBytes := []byte(str)
|
|
||||||
var outputBuffer bytes.Buffer
|
|
||||||
for _, b := range strBytes {
|
|
||||||
if shouldEncode(b) {
|
|
||||||
encode(&outputBuffer, b)
|
|
||||||
} else if shouldEscape(b) {
|
|
||||||
escape(&outputBuffer, b)
|
|
||||||
} else {
|
|
||||||
outputBuffer.WriteByte(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outputBuffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeUserLocalpart decodes the given string back into the original input string.
|
|
||||||
// Returns an error if the given string is not a valid user ID localpart encoding.
|
|
||||||
// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
|
|
||||||
//
|
|
||||||
// This decodes quoted-printable bytes back into UTF8, and unescapes casing. For
|
|
||||||
// example:
|
|
||||||
// _alph=40_bet=5f50up => Alph@Bet_50up
|
|
||||||
// Returns an error if the input string contains characters outside the
|
|
||||||
// range "a-z0-9._=-", has an invalid quote-printable byte (e.g. not hex), or has
|
|
||||||
// an invalid _ escaped byte (e.g. "_5").
|
|
||||||
func DecodeUserLocalpart(str string) (string, error) {
|
|
||||||
strBytes := []byte(str)
|
|
||||||
var outputBuffer bytes.Buffer
|
|
||||||
for i := 0; i < len(strBytes); i++ {
|
|
||||||
b := strBytes[i]
|
|
||||||
if !isValidByte(b) {
|
|
||||||
return "", fmt.Errorf("Byte pos %d: Invalid byte", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _
|
|
||||||
if i+1 >= len(strBytes) {
|
|
||||||
return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i)
|
|
||||||
}
|
|
||||||
if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping
|
|
||||||
return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i)
|
|
||||||
}
|
|
||||||
if strBytes[i+1] == '_' {
|
|
||||||
outputBuffer.WriteByte('_')
|
|
||||||
} else {
|
|
||||||
outputBuffer.WriteByte(strBytes[i+1] - 0x20) // ASCII shift a-z to A-Z
|
|
||||||
}
|
|
||||||
i++ // skip next byte since we just handled it
|
|
||||||
} else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8
|
|
||||||
if i+2 >= len(strBytes) {
|
|
||||||
return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i)
|
|
||||||
}
|
|
||||||
dst := make([]byte, 1)
|
|
||||||
_, err := hex.Decode(dst, strBytes[i+1:i+3])
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
outputBuffer.WriteByte(dst[0])
|
|
||||||
i += 2 // skip next 2 bytes since we just handled it
|
|
||||||
} else { // pass through
|
|
||||||
outputBuffer.WriteByte(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outputBuffer.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractUserLocalpart extracts the localpart portion of a user ID.
|
|
||||||
// See http://matrix.org/docs/spec/intro.html#user-identifiers
|
|
||||||
func ExtractUserLocalpart(userID string) (string, error) {
|
|
||||||
if len(userID) == 0 || userID[0] != '@' {
|
|
||||||
return "", fmt.Errorf("%s is not a valid user id", userID)
|
|
||||||
}
|
|
||||||
return strings.TrimPrefix(
|
|
||||||
strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ]
|
|
||||||
"@", // remove "@" prefix
|
|
||||||
), nil
|
|
||||||
}
|
|
27
vendor/github.com/matrix-org/gomatrix/userids_examples_test.go
generated
vendored
27
vendor/github.com/matrix-org/gomatrix/userids_examples_test.go
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func ExampleEncodeUserLocalpart() {
|
|
||||||
localpart := EncodeUserLocalpart("Alph@Bet_50up")
|
|
||||||
fmt.Println(localpart)
|
|
||||||
// Output: _alph=40_bet__50up
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleDecodeUserLocalpart() {
|
|
||||||
localpart, err := DecodeUserLocalpart("_alph=40_bet__50up")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Println(localpart)
|
|
||||||
// Output: Alph@Bet_50up
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleExtractUserLocalpart() {
|
|
||||||
localpart, err := ExtractUserLocalpart("@alice:matrix.org")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Println(localpart)
|
|
||||||
// Output: alice
|
|
||||||
}
|
|
86
vendor/github.com/matrix-org/gomatrix/userids_test.go
generated
vendored
86
vendor/github.com/matrix-org/gomatrix/userids_test.go
generated
vendored
|
@ -1,86 +0,0 @@
|
||||||
package gomatrix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var useridtests = []struct {
|
|
||||||
Input string
|
|
||||||
Output string
|
|
||||||
}{
|
|
||||||
{"Alph@Bet_50up", "_alph=40_bet__50up"}, // The doc example
|
|
||||||
{"abcdef", "abcdef"}, // no-op
|
|
||||||
{"i_like_pie_", "i__like__pie__"}, // double underscore escaping
|
|
||||||
{"ABCDEF", "_a_b_c_d_e_f"}, // all-caps
|
|
||||||
{"!£", "=21=c2=a3"}, // punctuation and outside ascii range (U+00A3 => c2 a3)
|
|
||||||
{"___", "______"}, // literal underscores
|
|
||||||
{"hello-world.", "hello-world."}, // allowed punctuation
|
|
||||||
{"5+5=10", "5=2b5=3d10"}, // equals sign
|
|
||||||
{"東方Project", "=e6=9d=b1=e6=96=b9_project"}, // CJK mixed
|
|
||||||
{" foo bar", "=09foo=20bar"}, // whitespace (tab and space)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeUserLocalpart(t *testing.T) {
|
|
||||||
for _, u := range useridtests {
|
|
||||||
out := EncodeUserLocalpart(u.Input)
|
|
||||||
if out != u.Output {
|
|
||||||
t.Fatalf("TestEncodeUserLocalpart(%s) => Got: %s Expected: %s", u.Input, out, u.Output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeUserLocalpart(t *testing.T) {
|
|
||||||
for _, u := range useridtests {
|
|
||||||
in, _ := DecodeUserLocalpart(u.Output)
|
|
||||||
if in != u.Input {
|
|
||||||
t.Fatalf("TestDecodeUserLocalpart(%s) => Got: %s Expected: %s", u.Output, in, u.Input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var errtests = []struct {
|
|
||||||
Input string
|
|
||||||
}{
|
|
||||||
{"foo@bar"}, // invalid character @
|
|
||||||
{"foo_5bar"}, // invalid character after _
|
|
||||||
{"foo_._-bar"}, // multiple invalid characters after _
|
|
||||||
{"foo=2Hbar"}, // invalid hex after =
|
|
||||||
{"foo=2hbar"}, // invalid hex after = (lower-case)
|
|
||||||
{"foo=======2fbar"}, // multiple invalid hex after =
|
|
||||||
{"foo=2"}, // end of string after =
|
|
||||||
{"foo_"}, // end of string after _
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeUserLocalpartErrors(t *testing.T) {
|
|
||||||
for _, u := range errtests {
|
|
||||||
out, err := DecodeUserLocalpart(u.Input)
|
|
||||||
if out != "" {
|
|
||||||
t.Fatalf("TestDecodeUserLocalpartErrors(%s) => Got: %s Expected: empty string", u.Input, out)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("TestDecodeUserLocalpartErrors(%s) => Got: nil error Expected: error", u.Input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var localparttests = []struct {
|
|
||||||
Input string
|
|
||||||
ExpectOutput string
|
|
||||||
}{
|
|
||||||
{"@foo:bar", "foo"},
|
|
||||||
{"@foo:bar:8448", "foo"},
|
|
||||||
{"@foo.bar:baz.quuz", "foo.bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtractUserLocalpart(t *testing.T) {
|
|
||||||
for _, u := range localparttests {
|
|
||||||
out, err := ExtractUserLocalpart(u.Input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("TestExtractUserLocalpart(%s) => Error: %s", u.Input, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if out != u.ExpectOutput {
|
|
||||||
t.Errorf("TestExtractUserLocalpart(%s) => Got: %s, Want %s", u.Input, out, u.ExpectOutput)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
vendor/github.com/urfave/cli/.flake8
generated
vendored
2
vendor/github.com/urfave/cli/.flake8
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
[flake8]
|
|
||||||
max-line-length = 120
|
|
2
vendor/github.com/urfave/cli/.gitignore
generated
vendored
2
vendor/github.com/urfave/cli/.gitignore
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
*.coverprofile
|
|
||||||
node_modules/
|
|
27
vendor/github.com/urfave/cli/.travis.yml
generated
vendored
27
vendor/github.com/urfave/cli/.travis.yml
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
dist: trusty
|
|
||||||
osx_image: xcode8.3
|
|
||||||
go: 1.8.x
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- go get github.com/urfave/gfmrun/... || true
|
|
||||||
- go get golang.org/x/tools/cmd/goimports
|
|
||||||
- if [ ! -f node_modules/.bin/markdown-toc ] ; then
|
|
||||||
npm install markdown-toc ;
|
|
||||||
fi
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./runtests gen
|
|
||||||
- ./runtests vet
|
|
||||||
- ./runtests test
|
|
||||||
- ./runtests gfmrun
|
|
||||||
- ./runtests toc
|
|
435
vendor/github.com/urfave/cli/CHANGELOG.md
generated
vendored
435
vendor/github.com/urfave/cli/CHANGELOG.md
generated
vendored
|
@ -1,435 +0,0 @@
|
||||||
# Change Log
|
|
||||||
|
|
||||||
**ATTN**: This project uses [semantic versioning](http://semver.org/).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## 1.20.0 - 2017-08-10
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* `HandleExitCoder` is now correctly iterates over all errors in
|
|
||||||
a `MultiError`. The exit code is the exit code of the last error or `1` if
|
|
||||||
there are no `ExitCoder`s in the `MultiError`.
|
|
||||||
* Fixed YAML file loading on Windows (previously would fail validate the file path)
|
|
||||||
* Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly
|
|
||||||
propogated
|
|
||||||
* `ErrWriter` is now passed downwards through command structure to avoid the
|
|
||||||
need to redefine it
|
|
||||||
* Pass `Command` context into `OnUsageError` rather than parent context so that
|
|
||||||
all fields are avaiable
|
|
||||||
* Errors occuring in `Before` funcs are no longer double printed
|
|
||||||
* Use `UsageText` in the help templates for commands and subcommands if
|
|
||||||
defined; otherwise build the usage as before (was previously ignoring this
|
|
||||||
field)
|
|
||||||
* `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if
|
|
||||||
a program calls `Set` or `GlobalSet` directly after flag parsing (would
|
|
||||||
previously only return `true` if the flag was set during parsing)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* No longer exit the program on command/subcommand error if the error raised is
|
|
||||||
not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was
|
|
||||||
determined to be a regression in functionality. See [the
|
|
||||||
PR](https://github.com/urfave/cli/pull/595) for discussion.
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* `CommandsByName` type was added to make it easy to sort `Command`s by name,
|
|
||||||
alphabetically
|
|
||||||
* `altsrc` now handles loading of string and int arrays from TOML
|
|
||||||
* Support for definition of custom help templates for `App` via
|
|
||||||
`CustomAppHelpTemplate`
|
|
||||||
* Support for arbitrary key/value fields on `App` to be used with
|
|
||||||
`CustomAppHelpTemplate` via `ExtraInfo`
|
|
||||||
* `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be
|
|
||||||
`cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag`
|
|
||||||
interface to be used.
|
|
||||||
|
|
||||||
|
|
||||||
## [1.19.1] - 2016-11-21
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fixes regression introduced in 1.19.0 where using an `ActionFunc` as
|
|
||||||
the `Action` for a command would cause it to error rather than calling the
|
|
||||||
function. Should not have a affected declarative cases using `func(c
|
|
||||||
*cli.Context) err)`.
|
|
||||||
- Shell completion now handles the case where the user specifies
|
|
||||||
`--generate-bash-completion` immediately after a flag that takes an argument.
|
|
||||||
Previously it call the application with `--generate-bash-completion` as the
|
|
||||||
flag value.
|
|
||||||
|
|
||||||
## [1.19.0] - 2016-11-19
|
|
||||||
### Added
|
|
||||||
- `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`)
|
|
||||||
- A `Description` field was added to `App` for a more detailed description of
|
|
||||||
the application (similar to the existing `Description` field on `Command`)
|
|
||||||
- Flag type code generation via `go generate`
|
|
||||||
- Write to stderr and exit 1 if action returns non-nil error
|
|
||||||
- Added support for TOML to the `altsrc` loader
|
|
||||||
- `SkipArgReorder` was added to allow users to skip the argument reordering.
|
|
||||||
This is useful if you want to consider all "flags" after an argument as
|
|
||||||
arguments rather than flags (the default behavior of the stdlib `flag`
|
|
||||||
library). This is backported functionality from the [removal of the flag
|
|
||||||
reordering](https://github.com/urfave/cli/pull/398) in the unreleased version
|
|
||||||
2
|
|
||||||
- For formatted errors (those implementing `ErrorFormatter`), the errors will
|
|
||||||
be formatted during output. Compatible with `pkg/errors`.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Raise minimum tested/supported Go version to 1.2+
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Consider empty environment variables as set (previously environment variables
|
|
||||||
with the equivalent of `""` would be skipped rather than their value used).
|
|
||||||
- Return an error if the value in a given environment variable cannot be parsed
|
|
||||||
as the flag type. Previously these errors were silently swallowed.
|
|
||||||
- Print full error when an invalid flag is specified (which includes the invalid flag)
|
|
||||||
- `App.Writer` defaults to `stdout` when `nil`
|
|
||||||
- If no action is specified on a command or app, the help is now printed instead of `panic`ing
|
|
||||||
- `App.Metadata` is initialized automatically now (previously was `nil` unless initialized)
|
|
||||||
- Correctly show help message if `-h` is provided to a subcommand
|
|
||||||
- `context.(Global)IsSet` now respects environment variables. Previously it
|
|
||||||
would return `false` if a flag was specified in the environment rather than
|
|
||||||
as an argument
|
|
||||||
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
|
|
||||||
- `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This
|
|
||||||
fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well
|
|
||||||
as `altsrc` where Go would complain that the types didn't match
|
|
||||||
|
|
||||||
## [1.18.1] - 2016-08-28
|
|
||||||
### Fixed
|
|
||||||
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported)
|
|
||||||
|
|
||||||
## [1.18.0] - 2016-06-27
|
|
||||||
### Added
|
|
||||||
- `./runtests` test runner with coverage tracking by default
|
|
||||||
- testing on OS X
|
|
||||||
- testing on Windows
|
|
||||||
- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Use spaces for alignment in help/usage output instead of tabs, making the
|
|
||||||
output alignment consistent regardless of tab width
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Printing of command aliases in help text
|
|
||||||
- Printing of visible flags for both struct and struct pointer flags
|
|
||||||
- Display the `help` subcommand when using `CommandCategories`
|
|
||||||
- No longer swallows `panic`s that occur within the `Action`s themselves when
|
|
||||||
detecting the signature of the `Action` field
|
|
||||||
|
|
||||||
## [1.17.1] - 2016-08-28
|
|
||||||
### Fixed
|
|
||||||
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
|
|
||||||
|
|
||||||
## [1.17.0] - 2016-05-09
|
|
||||||
### Added
|
|
||||||
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc`
|
|
||||||
- `context.GlobalBoolT` was added as an analogue to `context.GlobalBool`
|
|
||||||
- Support for hiding commands by setting `Hidden: true` -- this will hide the
|
|
||||||
commands in help output
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer
|
|
||||||
quoted in help text output.
|
|
||||||
- All flag types now include `(default: {value})` strings following usage when a
|
|
||||||
default value can be (reasonably) detected.
|
|
||||||
- `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent
|
|
||||||
with non-slice flag types
|
|
||||||
- Apps now exit with a code of 3 if an unknown subcommand is specified
|
|
||||||
(previously they printed "No help topic for...", but still exited 0. This
|
|
||||||
makes it easier to script around apps built using `cli` since they can trust
|
|
||||||
that a 0 exit code indicated a successful execution.
|
|
||||||
- cleanups based on [Go Report Card
|
|
||||||
feedback](https://goreportcard.com/report/github.com/urfave/cli)
|
|
||||||
|
|
||||||
## [1.16.1] - 2016-08-28
|
|
||||||
### Fixed
|
|
||||||
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
|
|
||||||
|
|
||||||
## [1.16.0] - 2016-05-02
|
|
||||||
### Added
|
|
||||||
- `Hidden` field on all flag struct types to omit from generated help text
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from
|
|
||||||
generated help text via the `Hidden` field
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- handling of error values in `HandleAction` and `HandleExitCoder`
|
|
||||||
|
|
||||||
## [1.15.0] - 2016-04-30
|
|
||||||
### Added
|
|
||||||
- This file!
|
|
||||||
- Support for placeholders in flag usage strings
|
|
||||||
- `App.Metadata` map for arbitrary data/state management
|
|
||||||
- `Set` and `GlobalSet` methods on `*cli.Context` for altering values after
|
|
||||||
parsing.
|
|
||||||
- Support for nested lookup of dot-delimited keys in structures loaded from
|
|
||||||
YAML.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- The `App.Action` and `Command.Action` now prefer a return signature of
|
|
||||||
`func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil
|
|
||||||
`error` is returned, there may be two outcomes:
|
|
||||||
- If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called
|
|
||||||
automatically
|
|
||||||
- Else the error is bubbled up and returned from `App.Run`
|
|
||||||
- Specifying an `Action` with the legacy return signature of
|
|
||||||
`func(*cli.Context)` will produce a deprecation message to stderr
|
|
||||||
- Specifying an `Action` that is not a `func` type will produce a non-zero exit
|
|
||||||
from `App.Run`
|
|
||||||
- Specifying an `Action` func that has an invalid (input) signature will
|
|
||||||
produce a non-zero exit from `App.Run`
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- <a name="deprecated-cli-app-runandexitonerror"></a>
|
|
||||||
`cli.App.RunAndExitOnError`, which should now be done by returning an error
|
|
||||||
that fulfills `cli.ExitCoder` to `cli.App.Run`.
|
|
||||||
- <a name="deprecated-cli-app-action-signature"></a> the legacy signature for
|
|
||||||
`cli.App.Action` of `func(*cli.Context)`, which should now have a return
|
|
||||||
signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Added missing `*cli.Context.GlobalFloat64` method
|
|
||||||
|
|
||||||
## [1.14.0] - 2016-04-03 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Codebeat badge
|
|
||||||
- Support for categorization via `CategorizedHelp` and `Categories` on app.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Ensure version is not shown in help text when `HideVersion` set.
|
|
||||||
|
|
||||||
## [1.13.0] - 2016-03-06 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- YAML file input support.
|
|
||||||
- `NArg` method on context.
|
|
||||||
|
|
||||||
## [1.12.0] - 2016-02-17 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Custom usage error handling.
|
|
||||||
- Custom text support in `USAGE` section of help output.
|
|
||||||
- Improved help messages for empty strings.
|
|
||||||
- AppVeyor CI configuration.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Removed `panic` from default help printer func.
|
|
||||||
- De-duping and optimizations.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Correctly handle `Before`/`After` at command level when no subcommands.
|
|
||||||
- Case of literal `-` argument causing flag reordering.
|
|
||||||
- Environment variable hints on Windows.
|
|
||||||
- Docs updates.
|
|
||||||
|
|
||||||
## [1.11.1] - 2015-12-21 (backfilled 2016-04-25)
|
|
||||||
### Changed
|
|
||||||
- Use `path.Base` in `Name` and `HelpName`
|
|
||||||
- Export `GetName` on flag types.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Flag parsing when skipping is enabled.
|
|
||||||
- Test output cleanup.
|
|
||||||
- Move completion check to account for empty input case.
|
|
||||||
|
|
||||||
## [1.11.0] - 2015-11-15 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Destination scan support for flags.
|
|
||||||
- Testing against `tip` in Travis CI config.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Go version in Travis CI config.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Removed redundant tests.
|
|
||||||
- Use correct example naming in tests.
|
|
||||||
|
|
||||||
## [1.10.2] - 2015-10-29 (backfilled 2016-04-25)
|
|
||||||
### Fixed
|
|
||||||
- Remove unused var in bash completion.
|
|
||||||
|
|
||||||
## [1.10.1] - 2015-10-21 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Coverage and reference logos in README.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Use specified values in help and version parsing.
|
|
||||||
- Only display app version and help message once.
|
|
||||||
|
|
||||||
## [1.10.0] - 2015-10-06 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- More tests for existing functionality.
|
|
||||||
- `ArgsUsage` at app and command level for help text flexibility.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Honor `HideHelp` and `HideVersion` in `App.Run`.
|
|
||||||
- Remove juvenile word from README.
|
|
||||||
|
|
||||||
## [1.9.0] - 2015-09-08 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- `FullName` on command with accompanying help output update.
|
|
||||||
- Set default `$PROG` in bash completion.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Docs formatting.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Removed self-referential imports in tests.
|
|
||||||
|
|
||||||
## [1.8.0] - 2015-06-30 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Support for `Copyright` at app level.
|
|
||||||
- `Parent` func at context level to walk up context lineage.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Global flag processing at top level.
|
|
||||||
|
|
||||||
## [1.7.1] - 2015-06-11 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Aggregate errors from `Before`/`After` funcs.
|
|
||||||
- Doc comments on flag structs.
|
|
||||||
- Include non-global flags when checking version and help.
|
|
||||||
- Travis CI config updates.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Ensure slice type flags have non-nil values.
|
|
||||||
- Collect global flags from the full command hierarchy.
|
|
||||||
- Docs prose.
|
|
||||||
|
|
||||||
## [1.7.0] - 2015-05-03 (backfilled 2016-04-25)
|
|
||||||
### Changed
|
|
||||||
- `HelpPrinter` signature includes output writer.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Specify go 1.1+ in docs.
|
|
||||||
- Set `Writer` when running command as app.
|
|
||||||
|
|
||||||
## [1.6.0] - 2015-03-23 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Multiple author support.
|
|
||||||
- `NumFlags` at context level.
|
|
||||||
- `Aliases` at command level.
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
- `ShortName` at command level.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Subcommand help output.
|
|
||||||
- Backward compatible support for deprecated `Author` and `Email` fields.
|
|
||||||
- Docs regarding `Names`/`Aliases`.
|
|
||||||
|
|
||||||
## [1.5.0] - 2015-02-20 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- `After` hook func support at app and command level.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Use parsed context when running command as subcommand.
|
|
||||||
- Docs prose.
|
|
||||||
|
|
||||||
## [1.4.1] - 2015-01-09 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Support for hiding `-h / --help` flags, but not `help` subcommand.
|
|
||||||
- Stop flag parsing after `--`.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Help text for generic flags to specify single value.
|
|
||||||
- Use double quotes in output for defaults.
|
|
||||||
- Use `ParseInt` instead of `ParseUint` for int environment var values.
|
|
||||||
- Use `0` as base when parsing int environment var values.
|
|
||||||
|
|
||||||
## [1.4.0] - 2014-12-12 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Support for environment variable lookup "cascade".
|
|
||||||
- Support for `Stdout` on app for output redirection.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Print command help instead of app help in `ShowCommandHelp`.
|
|
||||||
|
|
||||||
## [1.3.1] - 2014-11-13 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- Docs and example code updates.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Default `-v / --version` flag made optional.
|
|
||||||
|
|
||||||
## [1.3.0] - 2014-08-10 (backfilled 2016-04-25)
|
|
||||||
### Added
|
|
||||||
- `FlagNames` at context level.
|
|
||||||
- Exposed `VersionPrinter` var for more control over version output.
|
|
||||||
- Zsh completion hook.
|
|
||||||
- `AUTHOR` section in default app help template.
|
|
||||||
- Contribution guidelines.
|
|
||||||
- `DurationFlag` type.
|
|
||||||
|
|
||||||
## [1.2.0] - 2014-08-02
|
|
||||||
### Added
|
|
||||||
- Support for environment variable defaults on flags plus tests.
|
|
||||||
|
|
||||||
## [1.1.0] - 2014-07-15
|
|
||||||
### Added
|
|
||||||
- Bash completion.
|
|
||||||
- Optional hiding of built-in help command.
|
|
||||||
- Optional skipping of flag parsing at command level.
|
|
||||||
- `Author`, `Email`, and `Compiled` metadata on app.
|
|
||||||
- `Before` hook func support at app and command level.
|
|
||||||
- `CommandNotFound` func support at app level.
|
|
||||||
- Command reference available on context.
|
|
||||||
- `GenericFlag` type.
|
|
||||||
- `Float64Flag` type.
|
|
||||||
- `BoolTFlag` type.
|
|
||||||
- `IsSet` flag helper on context.
|
|
||||||
- More flag lookup funcs at context level.
|
|
||||||
- More tests & docs.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Help template updates to account for presence/absence of flags.
|
|
||||||
- Separated subcommand help template.
|
|
||||||
- Exposed `HelpPrinter` var for more control over help output.
|
|
||||||
|
|
||||||
## [1.0.0] - 2013-11-01
|
|
||||||
### Added
|
|
||||||
- `help` flag in default app flag set and each command flag set.
|
|
||||||
- Custom handling of argument parsing errors.
|
|
||||||
- Command lookup by name at app level.
|
|
||||||
- `StringSliceFlag` type and supporting `StringSlice` type.
|
|
||||||
- `IntSliceFlag` type and supporting `IntSlice` type.
|
|
||||||
- Slice type flag lookups by name at context level.
|
|
||||||
- Export of app and command help functions.
|
|
||||||
- More tests & docs.
|
|
||||||
|
|
||||||
## 0.1.0 - 2013-07-22
|
|
||||||
### Added
|
|
||||||
- Initial implementation.
|
|
||||||
|
|
||||||
[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD
|
|
||||||
[1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0
|
|
||||||
[1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0
|
|
||||||
[1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0
|
|
||||||
[1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0
|
|
||||||
[1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0
|
|
||||||
[1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0
|
|
||||||
[1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0
|
|
||||||
[1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1
|
|
||||||
[1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0
|
|
||||||
[1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2
|
|
||||||
[1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1
|
|
||||||
[1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0
|
|
||||||
[1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0
|
|
||||||
[1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0
|
|
||||||
[1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1
|
|
||||||
[1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0
|
|
||||||
[1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0
|
|
||||||
[1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0
|
|
||||||
[1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1
|
|
||||||
[1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0
|
|
||||||
[1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1
|
|
||||||
[1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0
|
|
||||||
[1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0
|
|
||||||
[1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0
|
|
||||||
[1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0
|
|
21
vendor/github.com/urfave/cli/LICENSE
generated
vendored
21
vendor/github.com/urfave/cli/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2016 Jeremy Saenz & Contributors
|
|
||||||
|
|
||||||
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.
|
|
1381
vendor/github.com/urfave/cli/README.md
generated
vendored
1381
vendor/github.com/urfave/cli/README.md
generated
vendored
File diff suppressed because it is too large
Load Diff
3
vendor/github.com/urfave/cli/altsrc/altsrc.go
generated
vendored
3
vendor/github.com/urfave/cli/altsrc/altsrc.go
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
package altsrc
|
|
||||||
|
|
||||||
//go:generate python ../generate-flag-types altsrc -i ../flag-types.json -o flag_generated.go
|
|
261
vendor/github.com/urfave/cli/altsrc/flag.go
generated
vendored
261
vendor/github.com/urfave/cli/altsrc/flag.go
generated
vendored
|
@ -1,261 +0,0 @@
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FlagInputSourceExtension is an extension interface of cli.Flag that
|
|
||||||
// allows a value to be set on the existing parsed flags.
|
|
||||||
type FlagInputSourceExtension interface {
|
|
||||||
cli.Flag
|
|
||||||
ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValues iterates over all provided flags and
|
|
||||||
// executes ApplyInputSourceValue on flags implementing the
|
|
||||||
// FlagInputSourceExtension interface to initialize these flags
|
|
||||||
// to an alternate input source.
|
|
||||||
func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error {
|
|
||||||
for _, f := range flags {
|
|
||||||
inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension)
|
|
||||||
if isType {
|
|
||||||
err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
|
|
||||||
// input source based on the func provided. If there is no error it will then apply the new input source to any flags
|
|
||||||
// that are supported by the input source
|
|
||||||
func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc {
|
|
||||||
return func(context *cli.Context) error {
|
|
||||||
inputSource, err := createInputSource()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApplyInputSourceValues(context, inputSource, flags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
|
|
||||||
// input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is
|
|
||||||
// no error it will then apply the new input source to any flags that are supported by the input source
|
|
||||||
func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFunc {
|
|
||||||
return func(context *cli.Context) error {
|
|
||||||
inputSource, err := createInputSource(context)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApplyInputSourceValues(context, inputSource, flags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a generic value to the flagSet if required
|
|
||||||
func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
|
||||||
value, err := isc.Generic(f.GenericFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value != nil {
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
f.set.Set(f.Name, value.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a StringSlice value to the flagSet if required
|
|
||||||
func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
|
||||||
value, err := isc.StringSlice(f.StringSliceFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value != nil {
|
|
||||||
var sliceValue cli.StringSlice = value
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
underlyingFlag := f.set.Lookup(f.Name)
|
|
||||||
if underlyingFlag != nil {
|
|
||||||
underlyingFlag.Value = &sliceValue
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a IntSlice value if required
|
|
||||||
func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
|
||||||
value, err := isc.IntSlice(f.IntSliceFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value != nil {
|
|
||||||
var sliceValue cli.IntSlice = value
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
underlyingFlag := f.set.Lookup(f.Name)
|
|
||||||
if underlyingFlag != nil {
|
|
||||||
underlyingFlag.Value = &sliceValue
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a Bool value to the flagSet if required
|
|
||||||
func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
|
||||||
value, err := isc.Bool(f.BoolFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value {
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
f.set.Set(f.Name, strconv.FormatBool(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a BoolT value to the flagSet if required
|
|
||||||
func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) {
|
|
||||||
value, err := isc.BoolT(f.BoolTFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !value {
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
f.set.Set(f.Name, strconv.FormatBool(value))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a String value to the flagSet if required
|
|
||||||
func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
|
||||||
value, err := isc.String(f.StringFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value != "" {
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
f.set.Set(f.Name, value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a int value to the flagSet if required
|
|
||||||
func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
|
||||||
value, err := isc.Int(f.IntFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value > 0 {
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
f.set.Set(f.Name, strconv.FormatInt(int64(value), 10))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a Duration value to the flagSet if required
|
|
||||||
func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
|
||||||
value, err := isc.Duration(f.DurationFlag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value > 0 {
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
f.set.Set(f.Name, value.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyInputSourceValue applies a Float64 value to the flagSet if required
|
|
||||||
func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error {
|
|
||||||
if f.set != nil {
|
|
||||||
if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) {
|
|
||||||
value, err := isc.Float64(f.Float64Flag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if value > 0 {
|
|
||||||
floatStr := float64ToString(value)
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
f.set.Set(f.Name, floatStr)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEnvVarSet(envVars string) bool {
|
|
||||||
for _, envVar := range strings.Split(envVars, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if _, ok := syscall.Getenv(envVar); ok {
|
|
||||||
// TODO: Can't use this for bools as
|
|
||||||
// set means that it was true or false based on
|
|
||||||
// Bool flag type, should work for other types
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func float64ToString(f float64) string {
|
|
||||||
return fmt.Sprintf("%v", f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func eachName(longName string, fn func(string)) {
|
|
||||||
parts := strings.Split(longName, ",")
|
|
||||||
for _, name := range parts {
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
fn(name)
|
|
||||||
}
|
|
||||||
}
|
|
347
vendor/github.com/urfave/cli/altsrc/flag_generated.go
generated
vendored
347
vendor/github.com/urfave/cli/altsrc/flag_generated.go
generated
vendored
|
@ -1,347 +0,0 @@
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WARNING: This file is generated!
|
|
||||||
|
|
||||||
// BoolFlag is the flag type that wraps cli.BoolFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type BoolFlag struct {
|
|
||||||
cli.BoolFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBoolFlag creates a new BoolFlag
|
|
||||||
func NewBoolFlag(fl cli.BoolFlag) *BoolFlag {
|
|
||||||
return &BoolFlag{BoolFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped BoolFlag.Apply
|
|
||||||
func (f *BoolFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.BoolFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped BoolFlag.ApplyWithError
|
|
||||||
func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.BoolFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolTFlag is the flag type that wraps cli.BoolTFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type BoolTFlag struct {
|
|
||||||
cli.BoolTFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBoolTFlag creates a new BoolTFlag
|
|
||||||
func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag {
|
|
||||||
return &BoolTFlag{BoolTFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped BoolTFlag.Apply
|
|
||||||
func (f *BoolTFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.BoolTFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped BoolTFlag.ApplyWithError
|
|
||||||
func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.BoolTFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DurationFlag is the flag type that wraps cli.DurationFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type DurationFlag struct {
|
|
||||||
cli.DurationFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDurationFlag creates a new DurationFlag
|
|
||||||
func NewDurationFlag(fl cli.DurationFlag) *DurationFlag {
|
|
||||||
return &DurationFlag{DurationFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped DurationFlag.Apply
|
|
||||||
func (f *DurationFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.DurationFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped DurationFlag.ApplyWithError
|
|
||||||
func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.DurationFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64Flag is the flag type that wraps cli.Float64Flag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type Float64Flag struct {
|
|
||||||
cli.Float64Flag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFloat64Flag creates a new Float64Flag
|
|
||||||
func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag {
|
|
||||||
return &Float64Flag{Float64Flag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Float64Flag.Apply
|
|
||||||
func (f *Float64Flag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.Float64Flag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Float64Flag.ApplyWithError
|
|
||||||
func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.Float64Flag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenericFlag is the flag type that wraps cli.GenericFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type GenericFlag struct {
|
|
||||||
cli.GenericFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGenericFlag creates a new GenericFlag
|
|
||||||
func NewGenericFlag(fl cli.GenericFlag) *GenericFlag {
|
|
||||||
return &GenericFlag{GenericFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped GenericFlag.Apply
|
|
||||||
func (f *GenericFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.GenericFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped GenericFlag.ApplyWithError
|
|
||||||
func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.GenericFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64Flag is the flag type that wraps cli.Int64Flag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type Int64Flag struct {
|
|
||||||
cli.Int64Flag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt64Flag creates a new Int64Flag
|
|
||||||
func NewInt64Flag(fl cli.Int64Flag) *Int64Flag {
|
|
||||||
return &Int64Flag{Int64Flag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Int64Flag.Apply
|
|
||||||
func (f *Int64Flag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.Int64Flag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Int64Flag.ApplyWithError
|
|
||||||
func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.Int64Flag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntFlag is the flag type that wraps cli.IntFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type IntFlag struct {
|
|
||||||
cli.IntFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIntFlag creates a new IntFlag
|
|
||||||
func NewIntFlag(fl cli.IntFlag) *IntFlag {
|
|
||||||
return &IntFlag{IntFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped IntFlag.Apply
|
|
||||||
func (f *IntFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.IntFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped IntFlag.ApplyWithError
|
|
||||||
func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.IntFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type IntSliceFlag struct {
|
|
||||||
cli.IntSliceFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIntSliceFlag creates a new IntSliceFlag
|
|
||||||
func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag {
|
|
||||||
return &IntSliceFlag{IntSliceFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped IntSliceFlag.Apply
|
|
||||||
func (f *IntSliceFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.IntSliceFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped IntSliceFlag.ApplyWithError
|
|
||||||
func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.IntSliceFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type Int64SliceFlag struct {
|
|
||||||
cli.Int64SliceFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInt64SliceFlag creates a new Int64SliceFlag
|
|
||||||
func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag {
|
|
||||||
return &Int64SliceFlag{Int64SliceFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Int64SliceFlag.Apply
|
|
||||||
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.Int64SliceFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Int64SliceFlag.ApplyWithError
|
|
||||||
func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.Int64SliceFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringFlag is the flag type that wraps cli.StringFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type StringFlag struct {
|
|
||||||
cli.StringFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringFlag creates a new StringFlag
|
|
||||||
func NewStringFlag(fl cli.StringFlag) *StringFlag {
|
|
||||||
return &StringFlag{StringFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped StringFlag.Apply
|
|
||||||
func (f *StringFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.StringFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped StringFlag.ApplyWithError
|
|
||||||
func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.StringFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type StringSliceFlag struct {
|
|
||||||
cli.StringSliceFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringSliceFlag creates a new StringSliceFlag
|
|
||||||
func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag {
|
|
||||||
return &StringSliceFlag{StringSliceFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped StringSliceFlag.Apply
|
|
||||||
func (f *StringSliceFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.StringSliceFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped StringSliceFlag.ApplyWithError
|
|
||||||
func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.StringSliceFlag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64Flag is the flag type that wraps cli.Uint64Flag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type Uint64Flag struct {
|
|
||||||
cli.Uint64Flag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUint64Flag creates a new Uint64Flag
|
|
||||||
func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag {
|
|
||||||
return &Uint64Flag{Uint64Flag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Uint64Flag.Apply
|
|
||||||
func (f *Uint64Flag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.Uint64Flag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped Uint64Flag.ApplyWithError
|
|
||||||
func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.Uint64Flag.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UintFlag is the flag type that wraps cli.UintFlag to allow
|
|
||||||
// for other values to be specified
|
|
||||||
type UintFlag struct {
|
|
||||||
cli.UintFlag
|
|
||||||
set *flag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUintFlag creates a new UintFlag
|
|
||||||
func NewUintFlag(fl cli.UintFlag) *UintFlag {
|
|
||||||
return &UintFlag{UintFlag: fl, set: nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped UintFlag.Apply
|
|
||||||
func (f *UintFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.set = set
|
|
||||||
f.UintFlag.Apply(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
||||||
// wrapped UintFlag.ApplyWithError
|
|
||||||
func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
f.set = set
|
|
||||||
return f.UintFlag.ApplyWithError(set)
|
|
||||||
}
|
|
336
vendor/github.com/urfave/cli/altsrc/flag_test.go
generated
vendored
336
vendor/github.com/urfave/cli/altsrc/flag_test.go
generated
vendored
|
@ -1,336 +0,0 @@
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testApplyInputSource struct {
|
|
||||||
Flag FlagInputSourceExtension
|
|
||||||
FlagName string
|
|
||||||
FlagSetName string
|
|
||||||
Expected string
|
|
||||||
ContextValueString string
|
|
||||||
ContextValue flag.Value
|
|
||||||
EnvVarValue string
|
|
||||||
EnvVarName string
|
|
||||||
MapValue interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenericApplyInputSourceValue(t *testing.T) {
|
|
||||||
v := &Parser{"abc", "def"}
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: v,
|
|
||||||
})
|
|
||||||
expect(t, v, c.Generic("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenericApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
p := &Parser{"abc", "def"}
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: &Parser{"efg", "hig"},
|
|
||||||
ContextValueString: p.String(),
|
|
||||||
})
|
|
||||||
expect(t, p, c.Generic("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}, EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: &Parser{"efg", "hij"},
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: "abc,def",
|
|
||||||
})
|
|
||||||
expect(t, &Parser{"abc", "def"}, c.Generic("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringSliceApplyInputSourceValue(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: []interface{}{"hello", "world"},
|
|
||||||
})
|
|
||||||
expect(t, c.StringSlice("test"), []string{"hello", "world"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: []interface{}{"hello", "world"},
|
|
||||||
ContextValueString: "ohno",
|
|
||||||
})
|
|
||||||
expect(t, c.StringSlice("test"), []string{"ohno"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: []interface{}{"hello", "world"},
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: "oh,no",
|
|
||||||
})
|
|
||||||
expect(t, c.StringSlice("test"), []string{"oh", "no"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntSliceApplyInputSourceValue(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: []interface{}{1, 2},
|
|
||||||
})
|
|
||||||
expect(t, c.IntSlice("test"), []int{1, 2})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: []interface{}{1, 2},
|
|
||||||
ContextValueString: "3",
|
|
||||||
})
|
|
||||||
expect(t, c.IntSlice("test"), []int{3})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: []interface{}{1, 2},
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: "3,4",
|
|
||||||
})
|
|
||||||
expect(t, c.IntSlice("test"), []int{3, 4})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolApplyInputSourceMethodSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: true,
|
|
||||||
})
|
|
||||||
expect(t, true, c.Bool("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: false,
|
|
||||||
ContextValueString: "true",
|
|
||||||
})
|
|
||||||
expect(t, true, c.Bool("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: false,
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: "true",
|
|
||||||
})
|
|
||||||
expect(t, true, c.Bool("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolTApplyInputSourceMethodSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: false,
|
|
||||||
})
|
|
||||||
expect(t, false, c.BoolT("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolTApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: true,
|
|
||||||
ContextValueString: "false",
|
|
||||||
})
|
|
||||||
expect(t, false, c.BoolT("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBoolTApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: true,
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: "false",
|
|
||||||
})
|
|
||||||
expect(t, false, c.BoolT("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringApplyInputSourceMethodSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewStringFlag(cli.StringFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: "hello",
|
|
||||||
})
|
|
||||||
expect(t, "hello", c.String("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewStringFlag(cli.StringFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: "hello",
|
|
||||||
ContextValueString: "goodbye",
|
|
||||||
})
|
|
||||||
expect(t, "goodbye", c.String("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: "hello",
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: "goodbye",
|
|
||||||
})
|
|
||||||
expect(t, "goodbye", c.String("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntApplyInputSourceMethodSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewIntFlag(cli.IntFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: 15,
|
|
||||||
})
|
|
||||||
expect(t, 15, c.Int("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewIntFlag(cli.IntFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: 15,
|
|
||||||
ContextValueString: "7",
|
|
||||||
})
|
|
||||||
expect(t, 7, c.Int("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: 15,
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: "12",
|
|
||||||
})
|
|
||||||
expect(t, 12, c.Int("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDurationApplyInputSourceMethodSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: time.Duration(30 * time.Second),
|
|
||||||
})
|
|
||||||
expect(t, time.Duration(30*time.Second), c.Duration("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDurationApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: time.Duration(30 * time.Second),
|
|
||||||
ContextValueString: time.Duration(15 * time.Second).String(),
|
|
||||||
})
|
|
||||||
expect(t, time.Duration(15*time.Second), c.Duration("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: time.Duration(30 * time.Second),
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: time.Duration(15 * time.Second).String(),
|
|
||||||
})
|
|
||||||
expect(t, time.Duration(15*time.Second), c.Duration("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFloat64ApplyInputSourceMethodSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: 1.3,
|
|
||||||
})
|
|
||||||
expect(t, 1.3, c.Float64("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: 1.3,
|
|
||||||
ContextValueString: fmt.Sprintf("%v", 1.4),
|
|
||||||
})
|
|
||||||
expect(t, 1.4, c.Float64("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|
||||||
c := runTest(t, testApplyInputSource{
|
|
||||||
Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVar: "TEST"}),
|
|
||||||
FlagName: "test",
|
|
||||||
MapValue: 1.3,
|
|
||||||
EnvVarName: "TEST",
|
|
||||||
EnvVarValue: fmt.Sprintf("%v", 1.4),
|
|
||||||
})
|
|
||||||
expect(t, 1.4, c.Float64("test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func runTest(t *testing.T, test testApplyInputSource) *cli.Context {
|
|
||||||
inputSource := &MapInputSource{valueMap: map[interface{}]interface{}{test.FlagName: test.MapValue}}
|
|
||||||
set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError)
|
|
||||||
c := cli.NewContext(nil, set, nil)
|
|
||||||
if test.EnvVarName != "" && test.EnvVarValue != "" {
|
|
||||||
os.Setenv(test.EnvVarName, test.EnvVarValue)
|
|
||||||
defer os.Setenv(test.EnvVarName, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
test.Flag.Apply(set)
|
|
||||||
if test.ContextValue != nil {
|
|
||||||
flag := set.Lookup(test.FlagName)
|
|
||||||
flag.Value = test.ContextValue
|
|
||||||
}
|
|
||||||
if test.ContextValueString != "" {
|
|
||||||
set.Set(test.FlagName, test.ContextValueString)
|
|
||||||
}
|
|
||||||
test.Flag.ApplyInputSourceValue(c, inputSource)
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
type Parser [2]string
|
|
||||||
|
|
||||||
func (p *Parser) Set(value string) error {
|
|
||||||
parts := strings.Split(value, ",")
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return fmt.Errorf("invalid format")
|
|
||||||
}
|
|
||||||
|
|
||||||
(*p)[0] = parts[0]
|
|
||||||
(*p)[1] = parts[1]
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) String() string {
|
|
||||||
return fmt.Sprintf("%s,%s", p[0], p[1])
|
|
||||||
}
|
|
18
vendor/github.com/urfave/cli/altsrc/helpers_test.go
generated
vendored
18
vendor/github.com/urfave/cli/altsrc/helpers_test.go
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
|
||||||
if !reflect.DeepEqual(b, a) {
|
|
||||||
t.Errorf("Expected %#v (type %v) - Got %#v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
|
||||||
if a == b {
|
|
||||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
|
||||||
}
|
|
||||||
}
|
|
21
vendor/github.com/urfave/cli/altsrc/input_source_context.go
generated
vendored
21
vendor/github.com/urfave/cli/altsrc/input_source_context.go
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InputSourceContext is an interface used to allow
|
|
||||||
// other input sources to be implemented as needed.
|
|
||||||
type InputSourceContext interface {
|
|
||||||
Int(name string) (int, error)
|
|
||||||
Duration(name string) (time.Duration, error)
|
|
||||||
Float64(name string) (float64, error)
|
|
||||||
String(name string) (string, error)
|
|
||||||
StringSlice(name string) ([]string, error)
|
|
||||||
IntSlice(name string) ([]int, error)
|
|
||||||
Generic(name string) (cli.Generic, error)
|
|
||||||
Bool(name string) (bool, error)
|
|
||||||
BoolT(name string) (bool, error)
|
|
||||||
}
|
|
262
vendor/github.com/urfave/cli/altsrc/map_input_source.go
generated
vendored
262
vendor/github.com/urfave/cli/altsrc/map_input_source.go
generated
vendored
|
@ -1,262 +0,0 @@
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MapInputSource implements InputSourceContext to return
|
|
||||||
// data from the map that is loaded.
|
|
||||||
type MapInputSource struct {
|
|
||||||
valueMap map[interface{}]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nestedVal checks if the name has '.' delimiters.
|
|
||||||
// If so, it tries to traverse the tree by the '.' delimited sections to find
|
|
||||||
// a nested value for the key.
|
|
||||||
func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool) {
|
|
||||||
if sections := strings.Split(name, "."); len(sections) > 1 {
|
|
||||||
node := tree
|
|
||||||
for _, section := range sections[:len(sections)-1] {
|
|
||||||
if child, ok := node[section]; !ok {
|
|
||||||
return nil, false
|
|
||||||
} else {
|
|
||||||
if ctype, ok := child.(map[interface{}]interface{}); !ok {
|
|
||||||
return nil, false
|
|
||||||
} else {
|
|
||||||
node = ctype
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if val, ok := node[sections[len(sections)-1]]; ok {
|
|
||||||
return val, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int returns an int from the map if it exists otherwise returns 0
|
|
||||||
func (fsm *MapInputSource) Int(name string) (int, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := otherGenericValue.(int)
|
|
||||||
if !isType {
|
|
||||||
return 0, incorrectTypeForFlagError(name, "int", otherGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.(int)
|
|
||||||
if !isType {
|
|
||||||
return 0, incorrectTypeForFlagError(name, "int", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duration returns a duration from the map if it exists otherwise returns 0
|
|
||||||
func (fsm *MapInputSource) Duration(name string) (time.Duration, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := otherGenericValue.(time.Duration)
|
|
||||||
if !isType {
|
|
||||||
return 0, incorrectTypeForFlagError(name, "duration", otherGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.(time.Duration)
|
|
||||||
if !isType {
|
|
||||||
return 0, incorrectTypeForFlagError(name, "duration", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64 returns an float64 from the map if it exists otherwise returns 0
|
|
||||||
func (fsm *MapInputSource) Float64(name string) (float64, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := otherGenericValue.(float64)
|
|
||||||
if !isType {
|
|
||||||
return 0, incorrectTypeForFlagError(name, "float64", otherGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.(float64)
|
|
||||||
if !isType {
|
|
||||||
return 0, incorrectTypeForFlagError(name, "float64", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string from the map if it exists otherwise returns an empty string
|
|
||||||
func (fsm *MapInputSource) String(name string) (string, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := otherGenericValue.(string)
|
|
||||||
if !isType {
|
|
||||||
return "", incorrectTypeForFlagError(name, "string", otherGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.(string)
|
|
||||||
if !isType {
|
|
||||||
return "", incorrectTypeForFlagError(name, "string", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice returns an []string from the map if it exists otherwise returns nil
|
|
||||||
func (fsm *MapInputSource) StringSlice(name string) ([]string, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if !exists {
|
|
||||||
otherGenericValue, exists = nestedVal(name, fsm.valueMap)
|
|
||||||
if !exists {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
otherValue, isType := otherGenericValue.([]interface{})
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
var stringSlice = make([]string, 0, len(otherValue))
|
|
||||||
for i, v := range otherValue {
|
|
||||||
stringValue, isType := v.(string)
|
|
||||||
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "string", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
stringSlice = append(stringSlice, stringValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringSlice, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntSlice returns an []int from the map if it exists otherwise returns nil
|
|
||||||
func (fsm *MapInputSource) IntSlice(name string) ([]int, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if !exists {
|
|
||||||
otherGenericValue, exists = nestedVal(name, fsm.valueMap)
|
|
||||||
if !exists {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
otherValue, isType := otherGenericValue.([]interface{})
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
var intSlice = make([]int, 0, len(otherValue))
|
|
||||||
for i, v := range otherValue {
|
|
||||||
intValue, isType := v.(int)
|
|
||||||
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "int", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
intSlice = append(intSlice, intValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
return intSlice, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic returns an cli.Generic from the map if it exists otherwise returns nil
|
|
||||||
func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := otherGenericValue.(cli.Generic)
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(name, "cli.Generic", otherGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.(cli.Generic)
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(name, "cli.Generic", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool returns an bool from the map otherwise returns false
|
|
||||||
func (fsm *MapInputSource) Bool(name string) (bool, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := otherGenericValue.(bool)
|
|
||||||
if !isType {
|
|
||||||
return false, incorrectTypeForFlagError(name, "bool", otherGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.(bool)
|
|
||||||
if !isType {
|
|
||||||
return false, incorrectTypeForFlagError(name, "bool", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolT returns an bool from the map otherwise returns true
|
|
||||||
func (fsm *MapInputSource) BoolT(name string) (bool, error) {
|
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := otherGenericValue.(bool)
|
|
||||||
if !isType {
|
|
||||||
return true, incorrectTypeForFlagError(name, "bool", otherGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.(bool)
|
|
||||||
if !isType {
|
|
||||||
return true, incorrectTypeForFlagError(name, "bool", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error {
|
|
||||||
valueType := reflect.TypeOf(value)
|
|
||||||
valueTypeName := ""
|
|
||||||
if valueType != nil {
|
|
||||||
valueTypeName = valueType.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("Mismatched type for flag '%s'. Expected '%s' but actual is '%s'", name, expectedTypeName, valueTypeName)
|
|
||||||
}
|
|
310
vendor/github.com/urfave/cli/altsrc/toml_command_test.go
generated
vendored
310
vendor/github.com/urfave/cli/altsrc/toml_command_test.go
generated
vendored
|
@ -1,310 +0,0 @@
|
||||||
// Disabling building of toml support in cases where golang is 1.0 or 1.1
|
|
||||||
// as the encoding library is not implemented or supported.
|
|
||||||
|
|
||||||
// +build go1.2
|
|
||||||
|
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCommandTomFileTest(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 15)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "10")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 10)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "10")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 10)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml", "--test", "7"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 7)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte(`[top]
|
|
||||||
test = 15`), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml", "--top.test", "7"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 7)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 15)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test", Value: 7}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 15)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "11")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 11)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
|
||||||
defer os.Remove("current.toml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "11")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.toml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 11)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load"))
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
113
vendor/github.com/urfave/cli/altsrc/toml_file_loader.go
generated
vendored
113
vendor/github.com/urfave/cli/altsrc/toml_file_loader.go
generated
vendored
|
@ -1,113 +0,0 @@
|
||||||
// Disabling building of toml support in cases where golang is 1.0 or 1.1
|
|
||||||
// as the encoding library is not implemented or supported.
|
|
||||||
|
|
||||||
// +build go1.2
|
|
||||||
|
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tomlMap struct {
|
|
||||||
Map map[interface{}]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) {
|
|
||||||
ret = make(map[interface{}]interface{})
|
|
||||||
m := i.(map[string]interface{})
|
|
||||||
for key, val := range m {
|
|
||||||
v := reflect.ValueOf(val)
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
ret[key] = val.(bool)
|
|
||||||
case reflect.String:
|
|
||||||
ret[key] = val.(string)
|
|
||||||
case reflect.Int:
|
|
||||||
ret[key] = int(val.(int))
|
|
||||||
case reflect.Int8:
|
|
||||||
ret[key] = int(val.(int8))
|
|
||||||
case reflect.Int16:
|
|
||||||
ret[key] = int(val.(int16))
|
|
||||||
case reflect.Int32:
|
|
||||||
ret[key] = int(val.(int32))
|
|
||||||
case reflect.Int64:
|
|
||||||
ret[key] = int(val.(int64))
|
|
||||||
case reflect.Uint:
|
|
||||||
ret[key] = int(val.(uint))
|
|
||||||
case reflect.Uint8:
|
|
||||||
ret[key] = int(val.(uint8))
|
|
||||||
case reflect.Uint16:
|
|
||||||
ret[key] = int(val.(uint16))
|
|
||||||
case reflect.Uint32:
|
|
||||||
ret[key] = int(val.(uint32))
|
|
||||||
case reflect.Uint64:
|
|
||||||
ret[key] = int(val.(uint64))
|
|
||||||
case reflect.Float32:
|
|
||||||
ret[key] = float64(val.(float32))
|
|
||||||
case reflect.Float64:
|
|
||||||
ret[key] = float64(val.(float64))
|
|
||||||
case reflect.Map:
|
|
||||||
if tmp, err := unmarshalMap(val); err == nil {
|
|
||||||
ret[key] = tmp
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
ret[key] = val.([]interface{})
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *tomlMap) UnmarshalTOML(i interface{}) error {
|
|
||||||
if tmp, err := unmarshalMap(i); err == nil {
|
|
||||||
self.Map = tmp
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type tomlSourceContext struct {
|
|
||||||
FilePath string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath.
|
|
||||||
func NewTomlSourceFromFile(file string) (InputSourceContext, error) {
|
|
||||||
tsc := &tomlSourceContext{FilePath: file}
|
|
||||||
var results tomlMap = tomlMap{}
|
|
||||||
if err := readCommandToml(tsc.FilePath, &results); err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to load TOML file '%s': inner error: \n'%v'", tsc.FilePath, err.Error())
|
|
||||||
}
|
|
||||||
return &MapInputSource{valueMap: results.Map}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context.
|
|
||||||
func NewTomlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) {
|
|
||||||
return func(context *cli.Context) (InputSourceContext, error) {
|
|
||||||
filePath := context.String(flagFileName)
|
|
||||||
return NewTomlSourceFromFile(filePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readCommandToml(filePath string, container interface{}) (err error) {
|
|
||||||
b, err := loadDataFrom(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = toml.Unmarshal(b, container)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = nil
|
|
||||||
return
|
|
||||||
}
|
|
313
vendor/github.com/urfave/cli/altsrc/yaml_command_test.go
generated
vendored
313
vendor/github.com/urfave/cli/altsrc/yaml_command_test.go
generated
vendored
|
@ -1,313 +0,0 @@
|
||||||
// Disabling building of yaml support in cases where golang is 1.0 or 1.1
|
|
||||||
// as the encoding library is not implemented or supported.
|
|
||||||
|
|
||||||
// +build go1.2
|
|
||||||
|
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCommandYamlFileTest(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 15)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "10")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 10)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
|
||||||
test: 15`), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "10")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 10)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml", "--test", "7"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 7)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
|
||||||
test: 15`), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml", "--top.test", "7"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 7)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 15)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test", Value: 7}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
|
||||||
test: 15`), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 15)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "11")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("test")
|
|
||||||
expect(t, val, 11)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *testing.T) {
|
|
||||||
app := cli.NewApp()
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
|
||||||
test: 15`), 0666)
|
|
||||||
defer os.Remove("current.yaml")
|
|
||||||
|
|
||||||
os.Setenv("THE_TEST", "11")
|
|
||||||
defer os.Setenv("THE_TEST", "")
|
|
||||||
|
|
||||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
|
||||||
set.Parse(test)
|
|
||||||
|
|
||||||
c := cli.NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := &cli.Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
val := c.Int("top.test")
|
|
||||||
expect(t, val, 11)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}),
|
|
||||||
cli.StringFlag{Name: "load"}},
|
|
||||||
}
|
|
||||||
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
|
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
|
||||||
}
|
|
92
vendor/github.com/urfave/cli/altsrc/yaml_file_loader.go
generated
vendored
92
vendor/github.com/urfave/cli/altsrc/yaml_file_loader.go
generated
vendored
|
@ -1,92 +0,0 @@
|
||||||
// Disabling building of yaml support in cases where golang is 1.0 or 1.1
|
|
||||||
// as the encoding library is not implemented or supported.
|
|
||||||
|
|
||||||
// +build go1.2
|
|
||||||
|
|
||||||
package altsrc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type yamlSourceContext struct {
|
|
||||||
FilePath string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath.
|
|
||||||
func NewYamlSourceFromFile(file string) (InputSourceContext, error) {
|
|
||||||
ysc := &yamlSourceContext{FilePath: file}
|
|
||||||
var results map[interface{}]interface{}
|
|
||||||
err := readCommandYaml(ysc.FilePath, &results)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to load Yaml file '%s': inner error: \n'%v'", ysc.FilePath, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MapInputSource{valueMap: results}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context.
|
|
||||||
func NewYamlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) {
|
|
||||||
return func(context *cli.Context) (InputSourceContext, error) {
|
|
||||||
filePath := context.String(flagFileName)
|
|
||||||
return NewYamlSourceFromFile(filePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readCommandYaml(filePath string, container interface{}) (err error) {
|
|
||||||
b, err := loadDataFrom(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = yaml.Unmarshal(b, container)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadDataFrom(filePath string) ([]byte, error) {
|
|
||||||
u, err := url.Parse(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Host != "" { // i have a host, now do i support the scheme?
|
|
||||||
switch u.Scheme {
|
|
||||||
case "http", "https":
|
|
||||||
res, err := http.Get(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ioutil.ReadAll(res.Body)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("scheme of %s is unsupported", filePath)
|
|
||||||
}
|
|
||||||
} else if u.Path != "" { // i dont have a host, but I have a path. I am a local file.
|
|
||||||
if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil {
|
|
||||||
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
|
|
||||||
}
|
|
||||||
return ioutil.ReadFile(filePath)
|
|
||||||
} else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") {
|
|
||||||
// on Windows systems u.Path is always empty, so we need to check the string directly.
|
|
||||||
if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil {
|
|
||||||
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
|
|
||||||
}
|
|
||||||
return ioutil.ReadFile(filePath)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
|
|
||||||
}
|
|
||||||
}
|
|
497
vendor/github.com/urfave/cli/app.go
generated
vendored
497
vendor/github.com/urfave/cli/app.go
generated
vendored
|
@ -1,497 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
|
|
||||||
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
|
|
||||||
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
|
|
||||||
|
|
||||||
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
|
|
||||||
|
|
||||||
errInvalidActionType = NewExitError("ERROR invalid Action type. "+
|
|
||||||
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
|
|
||||||
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
// App is the main structure of a cli application. It is recommended that
|
|
||||||
// an app be created with the cli.NewApp() function
|
|
||||||
type App struct {
|
|
||||||
// The name of the program. Defaults to path.Base(os.Args[0])
|
|
||||||
Name string
|
|
||||||
// Full name of command for help, defaults to Name
|
|
||||||
HelpName string
|
|
||||||
// Description of the program.
|
|
||||||
Usage string
|
|
||||||
// Text to override the USAGE section of help
|
|
||||||
UsageText string
|
|
||||||
// Description of the program argument format.
|
|
||||||
ArgsUsage string
|
|
||||||
// Version of the program
|
|
||||||
Version string
|
|
||||||
// Description of the program
|
|
||||||
Description string
|
|
||||||
// List of commands to execute
|
|
||||||
Commands []Command
|
|
||||||
// List of flags to parse
|
|
||||||
Flags []Flag
|
|
||||||
// Boolean to enable bash completion commands
|
|
||||||
EnableBashCompletion bool
|
|
||||||
// Boolean to hide built-in help command
|
|
||||||
HideHelp bool
|
|
||||||
// Boolean to hide built-in version flag and the VERSION section of help
|
|
||||||
HideVersion bool
|
|
||||||
// Populate on app startup, only gettable through method Categories()
|
|
||||||
categories CommandCategories
|
|
||||||
// An action to execute when the bash-completion flag is set
|
|
||||||
BashComplete BashCompleteFunc
|
|
||||||
// An action to execute before any subcommands are run, but after the context is ready
|
|
||||||
// If a non-nil error is returned, no subcommands are run
|
|
||||||
Before BeforeFunc
|
|
||||||
// An action to execute after any subcommands are run, but after the subcommand has finished
|
|
||||||
// It is run even if Action() panics
|
|
||||||
After AfterFunc
|
|
||||||
|
|
||||||
// The action to execute when no subcommands are specified
|
|
||||||
// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
|
|
||||||
// *Note*: support for the deprecated `Action` signature will be removed in a future version
|
|
||||||
Action interface{}
|
|
||||||
|
|
||||||
// Execute this function if the proper command cannot be found
|
|
||||||
CommandNotFound CommandNotFoundFunc
|
|
||||||
// Execute this function if an usage error occurs
|
|
||||||
OnUsageError OnUsageErrorFunc
|
|
||||||
// Compilation date
|
|
||||||
Compiled time.Time
|
|
||||||
// List of all authors who contributed
|
|
||||||
Authors []Author
|
|
||||||
// Copyright of the binary if any
|
|
||||||
Copyright string
|
|
||||||
// Name of Author (Note: Use App.Authors, this is deprecated)
|
|
||||||
Author string
|
|
||||||
// Email of Author (Note: Use App.Authors, this is deprecated)
|
|
||||||
Email string
|
|
||||||
// Writer writer to write output to
|
|
||||||
Writer io.Writer
|
|
||||||
// ErrWriter writes error output
|
|
||||||
ErrWriter io.Writer
|
|
||||||
// Other custom info
|
|
||||||
Metadata map[string]interface{}
|
|
||||||
// Carries a function which returns app specific info.
|
|
||||||
ExtraInfo func() map[string]string
|
|
||||||
// CustomAppHelpTemplate the text template for app help topic.
|
|
||||||
// cli.go uses text/template to render templates. You can
|
|
||||||
// render custom help text by setting this variable.
|
|
||||||
CustomAppHelpTemplate string
|
|
||||||
|
|
||||||
didSetup bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tries to find out when this binary was compiled.
|
|
||||||
// Returns the current time if it fails to find it.
|
|
||||||
func compileTime() time.Time {
|
|
||||||
info, err := os.Stat(os.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
return time.Now()
|
|
||||||
}
|
|
||||||
return info.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewApp creates a new cli Application with some reasonable defaults for Name,
|
|
||||||
// Usage, Version and Action.
|
|
||||||
func NewApp() *App {
|
|
||||||
return &App{
|
|
||||||
Name: filepath.Base(os.Args[0]),
|
|
||||||
HelpName: filepath.Base(os.Args[0]),
|
|
||||||
Usage: "A new cli application",
|
|
||||||
UsageText: "",
|
|
||||||
Version: "0.0.0",
|
|
||||||
BashComplete: DefaultAppComplete,
|
|
||||||
Action: helpCommand.Action,
|
|
||||||
Compiled: compileTime(),
|
|
||||||
Writer: os.Stdout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup runs initialization code to ensure all data structures are ready for
|
|
||||||
// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
|
|
||||||
// will return early if setup has already happened.
|
|
||||||
func (a *App) Setup() {
|
|
||||||
if a.didSetup {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
a.didSetup = true
|
|
||||||
|
|
||||||
if a.Author != "" || a.Email != "" {
|
|
||||||
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
|
|
||||||
}
|
|
||||||
|
|
||||||
newCmds := []Command{}
|
|
||||||
for _, c := range a.Commands {
|
|
||||||
if c.HelpName == "" {
|
|
||||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
|
||||||
}
|
|
||||||
newCmds = append(newCmds, c)
|
|
||||||
}
|
|
||||||
a.Commands = newCmds
|
|
||||||
|
|
||||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
|
||||||
a.Commands = append(a.Commands, helpCommand)
|
|
||||||
if (HelpFlag != BoolFlag{}) {
|
|
||||||
a.appendFlag(HelpFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideVersion {
|
|
||||||
a.appendFlag(VersionFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.categories = CommandCategories{}
|
|
||||||
for _, command := range a.Commands {
|
|
||||||
a.categories = a.categories.AddCommand(command.Category, command)
|
|
||||||
}
|
|
||||||
sort.Sort(a.categories)
|
|
||||||
|
|
||||||
if a.Metadata == nil {
|
|
||||||
a.Metadata = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Writer == nil {
|
|
||||||
a.Writer = os.Stdout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
|
||||||
// to the proper flag/args combination
|
|
||||||
func (a *App) Run(arguments []string) (err error) {
|
|
||||||
a.Setup()
|
|
||||||
|
|
||||||
// handle the completion flag separately from the flagset since
|
|
||||||
// completion could be attempted after a flag, but before its value was put
|
|
||||||
// on the command line. this causes the flagset to interpret the completion
|
|
||||||
// flag name as the value of the flag before it which is undesirable
|
|
||||||
// note that we can only do this because the shell autocomplete function
|
|
||||||
// always appends the completion flag at the end of the command
|
|
||||||
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
|
|
||||||
|
|
||||||
// parse flags
|
|
||||||
set, err := flagSet(a.Name, a.Flags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
set.SetOutput(ioutil.Discard)
|
|
||||||
err = set.Parse(arguments[1:])
|
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
|
||||||
context := NewContext(a, set, nil)
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(a.Writer, nerr)
|
|
||||||
ShowAppHelp(context)
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
context.shellComplete = shellComplete
|
|
||||||
|
|
||||||
if checkCompletions(context) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if a.OnUsageError != nil {
|
|
||||||
err := a.OnUsageError(context, err, false)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
|
||||||
ShowAppHelp(context)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideHelp && checkHelp(context) {
|
|
||||||
ShowAppHelp(context)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideVersion && checkVersion(context) {
|
|
||||||
ShowVersion(context)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.After != nil {
|
|
||||||
defer func() {
|
|
||||||
if afterErr := a.After(context); afterErr != nil {
|
|
||||||
if err != nil {
|
|
||||||
err = NewMultiError(err, afterErr)
|
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Before != nil {
|
|
||||||
beforeErr := a.Before(context)
|
|
||||||
if beforeErr != nil {
|
|
||||||
ShowAppHelp(context)
|
|
||||||
HandleExitCoder(beforeErr)
|
|
||||||
err = beforeErr
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args := context.Args()
|
|
||||||
if args.Present() {
|
|
||||||
name := args.First()
|
|
||||||
c := a.Command(name)
|
|
||||||
if c != nil {
|
|
||||||
return c.Run(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Action == nil {
|
|
||||||
a.Action = helpCommand.Action
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run default Action
|
|
||||||
err = HandleAction(a.Action, context)
|
|
||||||
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
|
|
||||||
//
|
|
||||||
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
|
|
||||||
// to cli.App.Run. This will cause the application to exit with the given eror
|
|
||||||
// code in the cli.ExitCoder
|
|
||||||
func (a *App) RunAndExitOnError() {
|
|
||||||
if err := a.Run(os.Args); err != nil {
|
|
||||||
fmt.Fprintln(a.errWriter(), err)
|
|
||||||
OsExiter(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
|
|
||||||
// generate command-specific flags
|
|
||||||
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|
||||||
// append help to commands
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
|
||||||
a.Commands = append(a.Commands, helpCommand)
|
|
||||||
if (HelpFlag != BoolFlag{}) {
|
|
||||||
a.appendFlag(HelpFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newCmds := []Command{}
|
|
||||||
for _, c := range a.Commands {
|
|
||||||
if c.HelpName == "" {
|
|
||||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
|
||||||
}
|
|
||||||
newCmds = append(newCmds, c)
|
|
||||||
}
|
|
||||||
a.Commands = newCmds
|
|
||||||
|
|
||||||
// parse flags
|
|
||||||
set, err := flagSet(a.Name, a.Flags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
set.SetOutput(ioutil.Discard)
|
|
||||||
err = set.Parse(ctx.Args().Tail())
|
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
|
||||||
context := NewContext(a, set, ctx)
|
|
||||||
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(a.Writer, nerr)
|
|
||||||
fmt.Fprintln(a.Writer)
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
ShowSubcommandHelp(context)
|
|
||||||
} else {
|
|
||||||
ShowCommandHelp(ctx, context.Args().First())
|
|
||||||
}
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCompletions(context) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if a.OnUsageError != nil {
|
|
||||||
err = a.OnUsageError(context, err, true)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
|
||||||
ShowSubcommandHelp(context)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
if checkSubcommandHelp(context) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if checkCommandHelp(ctx, context.Args().First()) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.After != nil {
|
|
||||||
defer func() {
|
|
||||||
afterErr := a.After(context)
|
|
||||||
if afterErr != nil {
|
|
||||||
HandleExitCoder(err)
|
|
||||||
if err != nil {
|
|
||||||
err = NewMultiError(err, afterErr)
|
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Before != nil {
|
|
||||||
beforeErr := a.Before(context)
|
|
||||||
if beforeErr != nil {
|
|
||||||
HandleExitCoder(beforeErr)
|
|
||||||
err = beforeErr
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args := context.Args()
|
|
||||||
if args.Present() {
|
|
||||||
name := args.First()
|
|
||||||
c := a.Command(name)
|
|
||||||
if c != nil {
|
|
||||||
return c.Run(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run default Action
|
|
||||||
err = HandleAction(a.Action, context)
|
|
||||||
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the named command on App. Returns nil if the command does not exist
|
|
||||||
func (a *App) Command(name string) *Command {
|
|
||||||
for _, c := range a.Commands {
|
|
||||||
if c.HasName(name) {
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Categories returns a slice containing all the categories with the commands they contain
|
|
||||||
func (a *App) Categories() CommandCategories {
|
|
||||||
return a.categories
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleCategories returns a slice of categories and commands that are
|
|
||||||
// Hidden=false
|
|
||||||
func (a *App) VisibleCategories() []*CommandCategory {
|
|
||||||
ret := []*CommandCategory{}
|
|
||||||
for _, category := range a.categories {
|
|
||||||
if visible := func() *CommandCategory {
|
|
||||||
for _, command := range category.Commands {
|
|
||||||
if !command.Hidden {
|
|
||||||
return category
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}(); visible != nil {
|
|
||||||
ret = append(ret, visible)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
||||||
func (a *App) VisibleCommands() []Command {
|
|
||||||
ret := []Command{}
|
|
||||||
for _, command := range a.Commands {
|
|
||||||
if !command.Hidden {
|
|
||||||
ret = append(ret, command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleFlags returns a slice of the Flags with Hidden=false
|
|
||||||
func (a *App) VisibleFlags() []Flag {
|
|
||||||
return visibleFlags(a.Flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) hasFlag(flag Flag) bool {
|
|
||||||
for _, f := range a.Flags {
|
|
||||||
if flag == f {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) errWriter() io.Writer {
|
|
||||||
|
|
||||||
// When the app ErrWriter is nil use the package level one.
|
|
||||||
if a.ErrWriter == nil {
|
|
||||||
return ErrWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.ErrWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) appendFlag(flag Flag) {
|
|
||||||
if !a.hasFlag(flag) {
|
|
||||||
a.Flags = append(a.Flags, flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Author represents someone who has contributed to a cli project.
|
|
||||||
type Author struct {
|
|
||||||
Name string // The Authors name
|
|
||||||
Email string // The Authors email
|
|
||||||
}
|
|
||||||
|
|
||||||
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
|
|
||||||
func (a Author) String() string {
|
|
||||||
e := ""
|
|
||||||
if a.Email != "" {
|
|
||||||
e = " <" + a.Email + ">"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v%v", a.Name, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleAction attempts to figure out which Action signature was used. If
|
|
||||||
// it's an ActionFunc or a func with the legacy signature for Action, the func
|
|
||||||
// is run!
|
|
||||||
func HandleAction(action interface{}, context *Context) (err error) {
|
|
||||||
if a, ok := action.(ActionFunc); ok {
|
|
||||||
return a(context)
|
|
||||||
} else if a, ok := action.(func(*Context) error); ok {
|
|
||||||
return a(context)
|
|
||||||
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
|
|
||||||
a(context)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return errInvalidActionType
|
|
||||||
}
|
|
||||||
}
|
|
1742
vendor/github.com/urfave/cli/app_test.go
generated
vendored
1742
vendor/github.com/urfave/cli/app_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
26
vendor/github.com/urfave/cli/appveyor.yml
generated
vendored
26
vendor/github.com/urfave/cli/appveyor.yml
generated
vendored
|
@ -1,26 +0,0 @@
|
||||||
version: "{build}"
|
|
||||||
|
|
||||||
os: Windows Server 2016
|
|
||||||
|
|
||||||
image: Visual Studio 2017
|
|
||||||
|
|
||||||
clone_folder: c:\gopath\src\github.com\urfave\cli
|
|
||||||
|
|
||||||
environment:
|
|
||||||
GOPATH: C:\gopath
|
|
||||||
GOVERSION: 1.8.x
|
|
||||||
PYTHON: C:\Python36-x64
|
|
||||||
PYTHON_VERSION: 3.6.x
|
|
||||||
PYTHON_ARCH: 64
|
|
||||||
|
|
||||||
install:
|
|
||||||
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
|
|
||||||
- go version
|
|
||||||
- go env
|
|
||||||
- go get github.com/urfave/gfmrun/...
|
|
||||||
- go get -v -t ./...
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- python runtests vet
|
|
||||||
- python runtests test
|
|
||||||
- python runtests gfmrun
|
|
16
vendor/github.com/urfave/cli/autocomplete/bash_autocomplete
generated
vendored
16
vendor/github.com/urfave/cli/autocomplete/bash_autocomplete
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
#! /bin/bash
|
|
||||||
|
|
||||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
|
||||||
|
|
||||||
_cli_bash_autocomplete() {
|
|
||||||
local cur opts base
|
|
||||||
COMPREPLY=()
|
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
complete -F _cli_bash_autocomplete $PROG
|
|
||||||
|
|
||||||
unset PROG
|
|
5
vendor/github.com/urfave/cli/autocomplete/zsh_autocomplete
generated
vendored
5
vendor/github.com/urfave/cli/autocomplete/zsh_autocomplete
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
autoload -U compinit && compinit
|
|
||||||
autoload -U bashcompinit && bashcompinit
|
|
||||||
|
|
||||||
script_dir=$(dirname $0)
|
|
||||||
source ${script_dir}/bash_autocomplete
|
|
44
vendor/github.com/urfave/cli/category.go
generated
vendored
44
vendor/github.com/urfave/cli/category.go
generated
vendored
|
@ -1,44 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
// CommandCategories is a slice of *CommandCategory.
|
|
||||||
type CommandCategories []*CommandCategory
|
|
||||||
|
|
||||||
// CommandCategory is a category containing commands.
|
|
||||||
type CommandCategory struct {
|
|
||||||
Name string
|
|
||||||
Commands Commands
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandCategories) Less(i, j int) bool {
|
|
||||||
return c[i].Name < c[j].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandCategories) Len() int {
|
|
||||||
return len(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandCategories) Swap(i, j int) {
|
|
||||||
c[i], c[j] = c[j], c[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddCommand adds a command to a category.
|
|
||||||
func (c CommandCategories) AddCommand(category string, command Command) CommandCategories {
|
|
||||||
for _, commandCategory := range c {
|
|
||||||
if commandCategory.Name == category {
|
|
||||||
commandCategory.Commands = append(commandCategory.Commands, command)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return append(c, &CommandCategory{Name: category, Commands: []Command{command}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
||||||
func (c *CommandCategory) VisibleCommands() []Command {
|
|
||||||
ret := []Command{}
|
|
||||||
for _, command := range c.Commands {
|
|
||||||
if !command.Hidden {
|
|
||||||
ret = append(ret, command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
22
vendor/github.com/urfave/cli/cli.go
generated
vendored
22
vendor/github.com/urfave/cli/cli.go
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
// Package cli provides a minimal framework for creating and organizing command line
|
|
||||||
// Go applications. cli is designed to be easy to understand and write, the most simple
|
|
||||||
// cli application can be written as follows:
|
|
||||||
// func main() {
|
|
||||||
// cli.NewApp().Run(os.Args)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Of course this application does not do much, so let's make this an actual application:
|
|
||||||
// func main() {
|
|
||||||
// app := cli.NewApp()
|
|
||||||
// app.Name = "greet"
|
|
||||||
// app.Usage = "say a greeting"
|
|
||||||
// app.Action = func(c *cli.Context) error {
|
|
||||||
// println("Greetings")
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// app.Run(os.Args)
|
|
||||||
// }
|
|
||||||
package cli
|
|
||||||
|
|
||||||
//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
|
|
304
vendor/github.com/urfave/cli/command.go
generated
vendored
304
vendor/github.com/urfave/cli/command.go
generated
vendored
|
@ -1,304 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Command is a subcommand for a cli.App.
|
|
||||||
type Command struct {
|
|
||||||
// The name of the command
|
|
||||||
Name string
|
|
||||||
// short name of the command. Typically one character (deprecated, use `Aliases`)
|
|
||||||
ShortName string
|
|
||||||
// A list of aliases for the command
|
|
||||||
Aliases []string
|
|
||||||
// A short description of the usage of this command
|
|
||||||
Usage string
|
|
||||||
// Custom text to show on USAGE section of help
|
|
||||||
UsageText string
|
|
||||||
// A longer explanation of how the command works
|
|
||||||
Description string
|
|
||||||
// A short description of the arguments of this command
|
|
||||||
ArgsUsage string
|
|
||||||
// The category the command is part of
|
|
||||||
Category string
|
|
||||||
// The function to call when checking for bash command completions
|
|
||||||
BashComplete BashCompleteFunc
|
|
||||||
// An action to execute before any sub-subcommands are run, but after the context is ready
|
|
||||||
// If a non-nil error is returned, no sub-subcommands are run
|
|
||||||
Before BeforeFunc
|
|
||||||
// An action to execute after any subcommands are run, but after the subcommand has finished
|
|
||||||
// It is run even if Action() panics
|
|
||||||
After AfterFunc
|
|
||||||
// The function to call when this command is invoked
|
|
||||||
Action interface{}
|
|
||||||
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
|
|
||||||
// of deprecation period has passed, maybe?
|
|
||||||
|
|
||||||
// Execute this function if a usage error occurs.
|
|
||||||
OnUsageError OnUsageErrorFunc
|
|
||||||
// List of child commands
|
|
||||||
Subcommands Commands
|
|
||||||
// List of flags to parse
|
|
||||||
Flags []Flag
|
|
||||||
// Treat all flags as normal arguments if true
|
|
||||||
SkipFlagParsing bool
|
|
||||||
// Skip argument reordering which attempts to move flags before arguments,
|
|
||||||
// but only works if all flags appear after all arguments. This behavior was
|
|
||||||
// removed n version 2 since it only works under specific conditions so we
|
|
||||||
// backport here by exposing it as an option for compatibility.
|
|
||||||
SkipArgReorder bool
|
|
||||||
// Boolean to hide built-in help command
|
|
||||||
HideHelp bool
|
|
||||||
// Boolean to hide this command from help or completion
|
|
||||||
Hidden bool
|
|
||||||
|
|
||||||
// Full name of command for help, defaults to full command name, including parent commands.
|
|
||||||
HelpName string
|
|
||||||
commandNamePath []string
|
|
||||||
|
|
||||||
// CustomHelpTemplate the text template for the command help topic.
|
|
||||||
// cli.go uses text/template to render templates. You can
|
|
||||||
// render custom help text by setting this variable.
|
|
||||||
CustomHelpTemplate string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommandsByName []Command
|
|
||||||
|
|
||||||
func (c CommandsByName) Len() int {
|
|
||||||
return len(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandsByName) Less(i, j int) bool {
|
|
||||||
return c[i].Name < c[j].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandsByName) Swap(i, j int) {
|
|
||||||
c[i], c[j] = c[j], c[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// FullName returns the full name of the command.
|
|
||||||
// For subcommands this ensures that parent commands are part of the command path
|
|
||||||
func (c Command) FullName() string {
|
|
||||||
if c.commandNamePath == nil {
|
|
||||||
return c.Name
|
|
||||||
}
|
|
||||||
return strings.Join(c.commandNamePath, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commands is a slice of Command
|
|
||||||
type Commands []Command
|
|
||||||
|
|
||||||
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
|
||||||
func (c Command) Run(ctx *Context) (err error) {
|
|
||||||
if len(c.Subcommands) > 0 {
|
|
||||||
return c.startApp(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.HideHelp && (HelpFlag != BoolFlag{}) {
|
|
||||||
// append help to flags
|
|
||||||
c.Flags = append(
|
|
||||||
c.Flags,
|
|
||||||
HelpFlag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
set, err := flagSet(c.Name, c.Flags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
set.SetOutput(ioutil.Discard)
|
|
||||||
|
|
||||||
if c.SkipFlagParsing {
|
|
||||||
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
|
|
||||||
} else if !c.SkipArgReorder {
|
|
||||||
firstFlagIndex := -1
|
|
||||||
terminatorIndex := -1
|
|
||||||
for index, arg := range ctx.Args() {
|
|
||||||
if arg == "--" {
|
|
||||||
terminatorIndex = index
|
|
||||||
break
|
|
||||||
} else if arg == "-" {
|
|
||||||
// Do nothing. A dash alone is not really a flag.
|
|
||||||
continue
|
|
||||||
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
|
|
||||||
firstFlagIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if firstFlagIndex > -1 {
|
|
||||||
args := ctx.Args()
|
|
||||||
regularArgs := make([]string, len(args[1:firstFlagIndex]))
|
|
||||||
copy(regularArgs, args[1:firstFlagIndex])
|
|
||||||
|
|
||||||
var flagArgs []string
|
|
||||||
if terminatorIndex > -1 {
|
|
||||||
flagArgs = args[firstFlagIndex:terminatorIndex]
|
|
||||||
regularArgs = append(regularArgs, args[terminatorIndex:]...)
|
|
||||||
} else {
|
|
||||||
flagArgs = args[firstFlagIndex:]
|
|
||||||
}
|
|
||||||
|
|
||||||
err = set.Parse(append(flagArgs, regularArgs...))
|
|
||||||
} else {
|
|
||||||
err = set.Parse(ctx.Args().Tail())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = set.Parse(ctx.Args().Tail())
|
|
||||||
}
|
|
||||||
|
|
||||||
nerr := normalizeFlags(c.Flags, set)
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(ctx.App.Writer, nerr)
|
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
|
|
||||||
context := NewContext(ctx.App, set, ctx)
|
|
||||||
context.Command = c
|
|
||||||
if checkCommandCompletions(context, c.Name) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if c.OnUsageError != nil {
|
|
||||||
err := c.OnUsageError(context, err, false)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
|
|
||||||
fmt.Fprintln(context.App.Writer)
|
|
||||||
ShowCommandHelp(context, c.Name)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCommandHelp(context, c.Name) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.After != nil {
|
|
||||||
defer func() {
|
|
||||||
afterErr := c.After(context)
|
|
||||||
if afterErr != nil {
|
|
||||||
HandleExitCoder(err)
|
|
||||||
if err != nil {
|
|
||||||
err = NewMultiError(err, afterErr)
|
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Before != nil {
|
|
||||||
err = c.Before(context)
|
|
||||||
if err != nil {
|
|
||||||
ShowCommandHelp(context, c.Name)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Action == nil {
|
|
||||||
c.Action = helpSubcommand.Action
|
|
||||||
}
|
|
||||||
|
|
||||||
err = HandleAction(c.Action, context)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
HandleExitCoder(err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names returns the names including short names and aliases.
|
|
||||||
func (c Command) Names() []string {
|
|
||||||
names := []string{c.Name}
|
|
||||||
|
|
||||||
if c.ShortName != "" {
|
|
||||||
names = append(names, c.ShortName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(names, c.Aliases...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasName returns true if Command.Name or Command.ShortName matches given name
|
|
||||||
func (c Command) HasName(name string) bool {
|
|
||||||
for _, n := range c.Names() {
|
|
||||||
if n == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Command) startApp(ctx *Context) error {
|
|
||||||
app := NewApp()
|
|
||||||
app.Metadata = ctx.App.Metadata
|
|
||||||
// set the name and usage
|
|
||||||
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
|
|
||||||
if c.HelpName == "" {
|
|
||||||
app.HelpName = c.HelpName
|
|
||||||
} else {
|
|
||||||
app.HelpName = app.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Usage = c.Usage
|
|
||||||
app.Description = c.Description
|
|
||||||
app.ArgsUsage = c.ArgsUsage
|
|
||||||
|
|
||||||
// set CommandNotFound
|
|
||||||
app.CommandNotFound = ctx.App.CommandNotFound
|
|
||||||
app.CustomAppHelpTemplate = c.CustomHelpTemplate
|
|
||||||
|
|
||||||
// set the flags and commands
|
|
||||||
app.Commands = c.Subcommands
|
|
||||||
app.Flags = c.Flags
|
|
||||||
app.HideHelp = c.HideHelp
|
|
||||||
|
|
||||||
app.Version = ctx.App.Version
|
|
||||||
app.HideVersion = ctx.App.HideVersion
|
|
||||||
app.Compiled = ctx.App.Compiled
|
|
||||||
app.Author = ctx.App.Author
|
|
||||||
app.Email = ctx.App.Email
|
|
||||||
app.Writer = ctx.App.Writer
|
|
||||||
app.ErrWriter = ctx.App.ErrWriter
|
|
||||||
|
|
||||||
app.categories = CommandCategories{}
|
|
||||||
for _, command := range c.Subcommands {
|
|
||||||
app.categories = app.categories.AddCommand(command.Category, command)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(app.categories)
|
|
||||||
|
|
||||||
// bash completion
|
|
||||||
app.EnableBashCompletion = ctx.App.EnableBashCompletion
|
|
||||||
if c.BashComplete != nil {
|
|
||||||
app.BashComplete = c.BashComplete
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the actions
|
|
||||||
app.Before = c.Before
|
|
||||||
app.After = c.After
|
|
||||||
if c.Action != nil {
|
|
||||||
app.Action = c.Action
|
|
||||||
} else {
|
|
||||||
app.Action = helpSubcommand.Action
|
|
||||||
}
|
|
||||||
app.OnUsageError = c.OnUsageError
|
|
||||||
|
|
||||||
for index, cc := range app.Commands {
|
|
||||||
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
|
||||||
}
|
|
||||||
|
|
||||||
return app.RunAsSubcommand(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleFlags returns a slice of the Flags with Hidden=false
|
|
||||||
func (c Command) VisibleFlags() []Flag {
|
|
||||||
return visibleFlags(c.Flags)
|
|
||||||
}
|
|
240
vendor/github.com/urfave/cli/command_test.go
generated
vendored
240
vendor/github.com/urfave/cli/command_test.go
generated
vendored
|
@ -1,240 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCommandFlagParsing(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
testArgs []string
|
|
||||||
skipFlagParsing bool
|
|
||||||
skipArgReorder bool
|
|
||||||
expectedErr error
|
|
||||||
}{
|
|
||||||
// Test normal "not ignoring flags" flow
|
|
||||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break")},
|
|
||||||
|
|
||||||
// Test no arg reorder
|
|
||||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil},
|
|
||||||
|
|
||||||
{[]string{"test-cmd", "blah", "blah"}, true, false, nil}, // Test SkipFlagParsing without any args that look like flags
|
|
||||||
{[]string{"test-cmd", "blah", "-break"}, true, false, nil}, // Test SkipFlagParsing with random flag arg
|
|
||||||
{[]string{"test-cmd", "blah", "-help"}, true, false, nil}, // Test SkipFlagParsing with "special" help flag arg
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
app := NewApp()
|
|
||||||
app.Writer = ioutil.Discard
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Parse(c.testArgs)
|
|
||||||
|
|
||||||
context := NewContext(app, set, nil)
|
|
||||||
|
|
||||||
command := Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(_ *Context) error { return nil },
|
|
||||||
SkipFlagParsing: c.skipFlagParsing,
|
|
||||||
SkipArgReorder: c.skipArgReorder,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := command.Run(context)
|
|
||||||
|
|
||||||
expect(t, err, c.expectedErr)
|
|
||||||
expect(t, []string(context.Args()), c.testArgs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
|
|
||||||
app := NewApp()
|
|
||||||
app.Commands = []Command{
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Before: func(c *Context) error {
|
|
||||||
return fmt.Errorf("before error")
|
|
||||||
},
|
|
||||||
After: func(c *Context) error {
|
|
||||||
return fmt.Errorf("after error")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := app.Run([]string{"foo", "bar"})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected to receive error from Run, got none")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(err.Error(), "before error") {
|
|
||||||
t.Errorf("expected text of error from Before method, but got none in \"%v\"", err)
|
|
||||||
}
|
|
||||||
if !strings.Contains(err.Error(), "after error") {
|
|
||||||
t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommand_Run_BeforeSavesMetadata(t *testing.T) {
|
|
||||||
var receivedMsgFromAction string
|
|
||||||
var receivedMsgFromAfter string
|
|
||||||
|
|
||||||
app := NewApp()
|
|
||||||
app.Commands = []Command{
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Before: func(c *Context) error {
|
|
||||||
c.App.Metadata["msg"] = "hello world"
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Action: func(c *Context) error {
|
|
||||||
msg, ok := c.App.Metadata["msg"]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("msg not found")
|
|
||||||
}
|
|
||||||
receivedMsgFromAction = msg.(string)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
After: func(c *Context) error {
|
|
||||||
msg, ok := c.App.Metadata["msg"]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("msg not found")
|
|
||||||
}
|
|
||||||
receivedMsgFromAfter = msg.(string)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := app.Run([]string{"foo", "bar"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("expected no error from Run, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedMsg := "hello world"
|
|
||||||
|
|
||||||
if receivedMsgFromAction != expectedMsg {
|
|
||||||
t.Fatalf("expected msg from Action to match. Given: %q\nExpected: %q",
|
|
||||||
receivedMsgFromAction, expectedMsg)
|
|
||||||
}
|
|
||||||
if receivedMsgFromAfter != expectedMsg {
|
|
||||||
t.Fatalf("expected msg from After to match. Given: %q\nExpected: %q",
|
|
||||||
receivedMsgFromAction, expectedMsg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommand_OnUsageError_hasCommandContext(t *testing.T) {
|
|
||||||
app := NewApp()
|
|
||||||
app.Commands = []Command{
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Flags: []Flag{
|
|
||||||
IntFlag{Name: "flag"},
|
|
||||||
},
|
|
||||||
OnUsageError: func(c *Context, err error, _ bool) error {
|
|
||||||
return fmt.Errorf("intercepted in %s: %s", c.Command.Name, err.Error())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected to receive error from Run, got none")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(err.Error(), "intercepted in bar") {
|
|
||||||
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
|
||||||
app := NewApp()
|
|
||||||
app.Commands = []Command{
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Flags: []Flag{
|
|
||||||
IntFlag{Name: "flag"},
|
|
||||||
},
|
|
||||||
OnUsageError: func(c *Context, err error, _ bool) error {
|
|
||||||
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
|
|
||||||
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
|
|
||||||
}
|
|
||||||
return errors.New("intercepted: " + err.Error())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected to receive error from Run, got none")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
|
|
||||||
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommand_OnUsageError_WithSubcommand(t *testing.T) {
|
|
||||||
app := NewApp()
|
|
||||||
app.Commands = []Command{
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Subcommands: []Command{
|
|
||||||
{
|
|
||||||
Name: "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Flags: []Flag{
|
|
||||||
IntFlag{Name: "flag"},
|
|
||||||
},
|
|
||||||
OnUsageError: func(c *Context, err error, _ bool) error {
|
|
||||||
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
|
|
||||||
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
|
|
||||||
}
|
|
||||||
return errors.New("intercepted: " + err.Error())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected to receive error from Run, got none")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
|
|
||||||
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) {
|
|
||||||
app := NewApp()
|
|
||||||
app.ErrWriter = ioutil.Discard
|
|
||||||
app.Commands = []Command{
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Subcommands: []Command{
|
|
||||||
{
|
|
||||||
Name: "baz",
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Action: func(c *Context) error {
|
|
||||||
if c.App.ErrWriter != ioutil.Discard {
|
|
||||||
return fmt.Errorf("ErrWriter not passed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := app.Run([]string{"foo", "bar", "baz"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
278
vendor/github.com/urfave/cli/context.go
generated
vendored
278
vendor/github.com/urfave/cli/context.go
generated
vendored
|
@ -1,278 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Context is a type that is passed through to
|
|
||||||
// each Handler action in a cli application. Context
|
|
||||||
// can be used to retrieve context-specific Args and
|
|
||||||
// parsed command-line options.
|
|
||||||
type Context struct {
|
|
||||||
App *App
|
|
||||||
Command Command
|
|
||||||
shellComplete bool
|
|
||||||
flagSet *flag.FlagSet
|
|
||||||
setFlags map[string]bool
|
|
||||||
parentContext *Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext creates a new context. For use in when invoking an App or Command action.
|
|
||||||
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
|
|
||||||
c := &Context{App: app, flagSet: set, parentContext: parentCtx}
|
|
||||||
|
|
||||||
if parentCtx != nil {
|
|
||||||
c.shellComplete = parentCtx.shellComplete
|
|
||||||
}
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumFlags returns the number of flags set
|
|
||||||
func (c *Context) NumFlags() int {
|
|
||||||
return c.flagSet.NFlag()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets a context flag to a value.
|
|
||||||
func (c *Context) Set(name, value string) error {
|
|
||||||
c.setFlags = nil
|
|
||||||
return c.flagSet.Set(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalSet sets a context flag to a value on the global flagset
|
|
||||||
func (c *Context) GlobalSet(name, value string) error {
|
|
||||||
globalContext(c).setFlags = nil
|
|
||||||
return globalContext(c).flagSet.Set(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSet determines if the flag was actually set
|
|
||||||
func (c *Context) IsSet(name string) bool {
|
|
||||||
if c.setFlags == nil {
|
|
||||||
c.setFlags = make(map[string]bool)
|
|
||||||
|
|
||||||
c.flagSet.Visit(func(f *flag.Flag) {
|
|
||||||
c.setFlags[f.Name] = true
|
|
||||||
})
|
|
||||||
|
|
||||||
c.flagSet.VisitAll(func(f *flag.Flag) {
|
|
||||||
if _, ok := c.setFlags[f.Name]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.setFlags[f.Name] = false
|
|
||||||
})
|
|
||||||
|
|
||||||
// XXX hack to support IsSet for flags with EnvVar
|
|
||||||
//
|
|
||||||
// There isn't an easy way to do this with the current implementation since
|
|
||||||
// whether a flag was set via an environment variable is very difficult to
|
|
||||||
// determine here. Instead, we intend to introduce a backwards incompatible
|
|
||||||
// change in version 2 to add `IsSet` to the Flag interface to push the
|
|
||||||
// responsibility closer to where the information required to determine
|
|
||||||
// whether a flag is set by non-standard means such as environment
|
|
||||||
// variables is avaliable.
|
|
||||||
//
|
|
||||||
// See https://github.com/urfave/cli/issues/294 for additional discussion
|
|
||||||
flags := c.Command.Flags
|
|
||||||
if c.Command.Name == "" { // cannot == Command{} since it contains slice types
|
|
||||||
if c.App != nil {
|
|
||||||
flags = c.App.Flags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, f := range flags {
|
|
||||||
eachName(f.GetName(), func(name string) {
|
|
||||||
if isSet, ok := c.setFlags[name]; isSet || !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.ValueOf(f)
|
|
||||||
if val.Kind() == reflect.Ptr {
|
|
||||||
val = val.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
envVarValue := val.FieldByName("EnvVar")
|
|
||||||
if !envVarValue.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(envVarValue.String(), func(envVar string) {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if _, ok := syscall.Getenv(envVar); ok {
|
|
||||||
c.setFlags[name] = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.setFlags[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalIsSet determines if the global flag was actually set
|
|
||||||
func (c *Context) GlobalIsSet(name string) bool {
|
|
||||||
ctx := c
|
|
||||||
if ctx.parentContext != nil {
|
|
||||||
ctx = ctx.parentContext
|
|
||||||
}
|
|
||||||
|
|
||||||
for ; ctx != nil; ctx = ctx.parentContext {
|
|
||||||
if ctx.IsSet(name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlagNames returns a slice of flag names used in this context.
|
|
||||||
func (c *Context) FlagNames() (names []string) {
|
|
||||||
for _, flag := range c.Command.Flags {
|
|
||||||
name := strings.Split(flag.GetName(), ",")[0]
|
|
||||||
if name == "help" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalFlagNames returns a slice of global flag names used by the app.
|
|
||||||
func (c *Context) GlobalFlagNames() (names []string) {
|
|
||||||
for _, flag := range c.App.Flags {
|
|
||||||
name := strings.Split(flag.GetName(), ",")[0]
|
|
||||||
if name == "help" || name == "version" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent returns the parent context, if any
|
|
||||||
func (c *Context) Parent() *Context {
|
|
||||||
return c.parentContext
|
|
||||||
}
|
|
||||||
|
|
||||||
// value returns the value of the flag coressponding to `name`
|
|
||||||
func (c *Context) value(name string) interface{} {
|
|
||||||
return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args contains apps console arguments
|
|
||||||
type Args []string
|
|
||||||
|
|
||||||
// Args returns the command line arguments associated with the context.
|
|
||||||
func (c *Context) Args() Args {
|
|
||||||
args := Args(c.flagSet.Args())
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
// NArg returns the number of the command line arguments.
|
|
||||||
func (c *Context) NArg() int {
|
|
||||||
return len(c.Args())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the nth argument, or else a blank string
|
|
||||||
func (a Args) Get(n int) string {
|
|
||||||
if len(a) > n {
|
|
||||||
return a[n]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// First returns the first argument, or else a blank string
|
|
||||||
func (a Args) First() string {
|
|
||||||
return a.Get(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tail returns the rest of the arguments (not the first one)
|
|
||||||
// or else an empty string slice
|
|
||||||
func (a Args) Tail() []string {
|
|
||||||
if len(a) >= 2 {
|
|
||||||
return []string(a)[1:]
|
|
||||||
}
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present checks if there are any arguments present
|
|
||||||
func (a Args) Present() bool {
|
|
||||||
return len(a) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap swaps arguments at the given indexes
|
|
||||||
func (a Args) Swap(from, to int) error {
|
|
||||||
if from >= len(a) || to >= len(a) {
|
|
||||||
return errors.New("index out of range")
|
|
||||||
}
|
|
||||||
a[from], a[to] = a[to], a[from]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func globalContext(ctx *Context) *Context {
|
|
||||||
if ctx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if ctx.parentContext == nil {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
ctx = ctx.parentContext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
|
|
||||||
if ctx.parentContext != nil {
|
|
||||||
ctx = ctx.parentContext
|
|
||||||
}
|
|
||||||
for ; ctx != nil; ctx = ctx.parentContext {
|
|
||||||
if f := ctx.flagSet.Lookup(name); f != nil {
|
|
||||||
return ctx.flagSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
|
||||||
switch ff.Value.(type) {
|
|
||||||
case *StringSlice:
|
|
||||||
default:
|
|
||||||
set.Set(name, ff.Value.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
|
|
||||||
visited := make(map[string]bool)
|
|
||||||
set.Visit(func(f *flag.Flag) {
|
|
||||||
visited[f.Name] = true
|
|
||||||
})
|
|
||||||
for _, f := range flags {
|
|
||||||
parts := strings.Split(f.GetName(), ",")
|
|
||||||
if len(parts) == 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var ff *flag.Flag
|
|
||||||
for _, name := range parts {
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
if visited[name] {
|
|
||||||
if ff != nil {
|
|
||||||
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
|
|
||||||
}
|
|
||||||
ff = set.Lookup(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ff == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, name := range parts {
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
if !visited[name] {
|
|
||||||
copyFlag(name, ff, set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
403
vendor/github.com/urfave/cli/context_test.go
generated
vendored
403
vendor/github.com/urfave/cli/context_test.go
generated
vendored
|
@ -1,403 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewContext(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Int("myflag", 12, "doc")
|
|
||||||
set.Int64("myflagInt64", int64(12), "doc")
|
|
||||||
set.Uint("myflagUint", uint(93), "doc")
|
|
||||||
set.Uint64("myflagUint64", uint64(93), "doc")
|
|
||||||
set.Float64("myflag64", float64(17), "doc")
|
|
||||||
globalSet := flag.NewFlagSet("test", 0)
|
|
||||||
globalSet.Int("myflag", 42, "doc")
|
|
||||||
globalSet.Int64("myflagInt64", int64(42), "doc")
|
|
||||||
globalSet.Uint("myflagUint", uint(33), "doc")
|
|
||||||
globalSet.Uint64("myflagUint64", uint64(33), "doc")
|
|
||||||
globalSet.Float64("myflag64", float64(47), "doc")
|
|
||||||
globalCtx := NewContext(nil, globalSet, nil)
|
|
||||||
command := Command{Name: "mycommand"}
|
|
||||||
c := NewContext(nil, set, globalCtx)
|
|
||||||
c.Command = command
|
|
||||||
expect(t, c.Int("myflag"), 12)
|
|
||||||
expect(t, c.Int64("myflagInt64"), int64(12))
|
|
||||||
expect(t, c.Uint("myflagUint"), uint(93))
|
|
||||||
expect(t, c.Uint64("myflagUint64"), uint64(93))
|
|
||||||
expect(t, c.Float64("myflag64"), float64(17))
|
|
||||||
expect(t, c.GlobalInt("myflag"), 42)
|
|
||||||
expect(t, c.GlobalInt64("myflagInt64"), int64(42))
|
|
||||||
expect(t, c.GlobalUint("myflagUint"), uint(33))
|
|
||||||
expect(t, c.GlobalUint64("myflagUint64"), uint64(33))
|
|
||||||
expect(t, c.GlobalFloat64("myflag64"), float64(47))
|
|
||||||
expect(t, c.Command.Name, "mycommand")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Int(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Int("myflag", 12, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.Int("myflag"), 12)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Int64(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Int64("myflagInt64", 12, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.Int64("myflagInt64"), int64(12))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Uint(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Uint("myflagUint", uint(13), "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.Uint("myflagUint"), uint(13))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Uint64(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Uint64("myflagUint64", uint64(9), "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.Uint64("myflagUint64"), uint64(9))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalInt(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Int("myflag", 12, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.GlobalInt("myflag"), 12)
|
|
||||||
expect(t, c.GlobalInt("nope"), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalInt64(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Int64("myflagInt64", 12, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.GlobalInt64("myflagInt64"), int64(12))
|
|
||||||
expect(t, c.GlobalInt64("nope"), int64(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Float64(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Float64("myflag", float64(17), "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.Float64("myflag"), float64(17))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalFloat64(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Float64("myflag", float64(17), "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.GlobalFloat64("myflag"), float64(17))
|
|
||||||
expect(t, c.GlobalFloat64("nope"), float64(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Duration(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Duration("myflag", time.Duration(12*time.Second), "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.Duration("myflag"), time.Duration(12*time.Second))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_String(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.String("myflag", "hello world", "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.String("myflag"), "hello world")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Bool(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Bool("myflag", false, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.Bool("myflag"), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_BoolT(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Bool("myflag", true, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
expect(t, c.BoolT("myflag"), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalBool(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
|
|
||||||
globalSet := flag.NewFlagSet("test-global", 0)
|
|
||||||
globalSet.Bool("myflag", false, "doc")
|
|
||||||
globalCtx := NewContext(nil, globalSet, nil)
|
|
||||||
|
|
||||||
c := NewContext(nil, set, globalCtx)
|
|
||||||
expect(t, c.GlobalBool("myflag"), false)
|
|
||||||
expect(t, c.GlobalBool("nope"), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalBoolT(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
|
|
||||||
globalSet := flag.NewFlagSet("test-global", 0)
|
|
||||||
globalSet.Bool("myflag", true, "doc")
|
|
||||||
globalCtx := NewContext(nil, globalSet, nil)
|
|
||||||
|
|
||||||
c := NewContext(nil, set, globalCtx)
|
|
||||||
expect(t, c.GlobalBoolT("myflag"), true)
|
|
||||||
expect(t, c.GlobalBoolT("nope"), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Args(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Bool("myflag", false, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
|
||||||
expect(t, len(c.Args()), 2)
|
|
||||||
expect(t, c.Bool("myflag"), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_NArg(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Bool("myflag", false, "doc")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
|
||||||
expect(t, c.NArg(), 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_IsSet(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Bool("myflag", false, "doc")
|
|
||||||
set.String("otherflag", "hello world", "doc")
|
|
||||||
globalSet := flag.NewFlagSet("test", 0)
|
|
||||||
globalSet.Bool("myflagGlobal", true, "doc")
|
|
||||||
globalCtx := NewContext(nil, globalSet, nil)
|
|
||||||
c := NewContext(nil, set, globalCtx)
|
|
||||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
|
||||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
|
||||||
expect(t, c.IsSet("myflag"), true)
|
|
||||||
expect(t, c.IsSet("otherflag"), false)
|
|
||||||
expect(t, c.IsSet("bogusflag"), false)
|
|
||||||
expect(t, c.IsSet("myflagGlobal"), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
|
||||||
// Should be moved to `flag_test` in v2
|
|
||||||
func TestContext_IsSet_fromEnv(t *testing.T) {
|
|
||||||
var (
|
|
||||||
timeoutIsSet, tIsSet bool
|
|
||||||
noEnvVarIsSet, nIsSet bool
|
|
||||||
passwordIsSet, pIsSet bool
|
|
||||||
unparsableIsSet, uIsSet bool
|
|
||||||
)
|
|
||||||
|
|
||||||
clearenv()
|
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
|
||||||
os.Setenv("APP_PASSWORD", "")
|
|
||||||
a := App{
|
|
||||||
Flags: []Flag{
|
|
||||||
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
|
||||||
StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"},
|
|
||||||
Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"},
|
|
||||||
Float64Flag{Name: "no-env-var, n"},
|
|
||||||
},
|
|
||||||
Action: func(ctx *Context) error {
|
|
||||||
timeoutIsSet = ctx.IsSet("timeout")
|
|
||||||
tIsSet = ctx.IsSet("t")
|
|
||||||
passwordIsSet = ctx.IsSet("password")
|
|
||||||
pIsSet = ctx.IsSet("p")
|
|
||||||
unparsableIsSet = ctx.IsSet("unparsable")
|
|
||||||
uIsSet = ctx.IsSet("u")
|
|
||||||
noEnvVarIsSet = ctx.IsSet("no-env-var")
|
|
||||||
nIsSet = ctx.IsSet("n")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.Run([]string{"run"})
|
|
||||||
expect(t, timeoutIsSet, true)
|
|
||||||
expect(t, tIsSet, true)
|
|
||||||
expect(t, passwordIsSet, true)
|
|
||||||
expect(t, pIsSet, true)
|
|
||||||
expect(t, noEnvVarIsSet, false)
|
|
||||||
expect(t, nIsSet, false)
|
|
||||||
|
|
||||||
os.Setenv("APP_UNPARSABLE", "foobar")
|
|
||||||
a.Run([]string{"run"})
|
|
||||||
expect(t, unparsableIsSet, false)
|
|
||||||
expect(t, uIsSet, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalIsSet(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Bool("myflag", false, "doc")
|
|
||||||
set.String("otherflag", "hello world", "doc")
|
|
||||||
globalSet := flag.NewFlagSet("test", 0)
|
|
||||||
globalSet.Bool("myflagGlobal", true, "doc")
|
|
||||||
globalSet.Bool("myflagGlobalUnset", true, "doc")
|
|
||||||
globalCtx := NewContext(nil, globalSet, nil)
|
|
||||||
c := NewContext(nil, set, globalCtx)
|
|
||||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
|
||||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
|
||||||
expect(t, c.GlobalIsSet("myflag"), false)
|
|
||||||
expect(t, c.GlobalIsSet("otherflag"), false)
|
|
||||||
expect(t, c.GlobalIsSet("bogusflag"), false)
|
|
||||||
expect(t, c.GlobalIsSet("myflagGlobal"), true)
|
|
||||||
expect(t, c.GlobalIsSet("myflagGlobalUnset"), false)
|
|
||||||
expect(t, c.GlobalIsSet("bogusGlobal"), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
|
||||||
// Should be moved to `flag_test` in v2
|
|
||||||
func TestContext_GlobalIsSet_fromEnv(t *testing.T) {
|
|
||||||
var (
|
|
||||||
timeoutIsSet, tIsSet bool
|
|
||||||
noEnvVarIsSet, nIsSet bool
|
|
||||||
passwordIsSet, pIsSet bool
|
|
||||||
unparsableIsSet, uIsSet bool
|
|
||||||
)
|
|
||||||
|
|
||||||
clearenv()
|
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
|
||||||
os.Setenv("APP_PASSWORD", "")
|
|
||||||
a := App{
|
|
||||||
Flags: []Flag{
|
|
||||||
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
|
||||||
StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"},
|
|
||||||
Float64Flag{Name: "no-env-var, n"},
|
|
||||||
Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"},
|
|
||||||
},
|
|
||||||
Commands: []Command{
|
|
||||||
{
|
|
||||||
Name: "hello",
|
|
||||||
Action: func(ctx *Context) error {
|
|
||||||
timeoutIsSet = ctx.GlobalIsSet("timeout")
|
|
||||||
tIsSet = ctx.GlobalIsSet("t")
|
|
||||||
passwordIsSet = ctx.GlobalIsSet("password")
|
|
||||||
pIsSet = ctx.GlobalIsSet("p")
|
|
||||||
unparsableIsSet = ctx.GlobalIsSet("unparsable")
|
|
||||||
uIsSet = ctx.GlobalIsSet("u")
|
|
||||||
noEnvVarIsSet = ctx.GlobalIsSet("no-env-var")
|
|
||||||
nIsSet = ctx.GlobalIsSet("n")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := a.Run([]string{"run", "hello"}); err != nil {
|
|
||||||
t.Logf("error running Run(): %+v", err)
|
|
||||||
}
|
|
||||||
expect(t, timeoutIsSet, true)
|
|
||||||
expect(t, tIsSet, true)
|
|
||||||
expect(t, passwordIsSet, true)
|
|
||||||
expect(t, pIsSet, true)
|
|
||||||
expect(t, noEnvVarIsSet, false)
|
|
||||||
expect(t, nIsSet, false)
|
|
||||||
|
|
||||||
os.Setenv("APP_UNPARSABLE", "foobar")
|
|
||||||
if err := a.Run([]string{"run"}); err != nil {
|
|
||||||
t.Logf("error running Run(): %+v", err)
|
|
||||||
}
|
|
||||||
expect(t, unparsableIsSet, false)
|
|
||||||
expect(t, uIsSet, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_NumFlags(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Bool("myflag", false, "doc")
|
|
||||||
set.String("otherflag", "hello world", "doc")
|
|
||||||
globalSet := flag.NewFlagSet("test", 0)
|
|
||||||
globalSet.Bool("myflagGlobal", true, "doc")
|
|
||||||
globalCtx := NewContext(nil, globalSet, nil)
|
|
||||||
c := NewContext(nil, set, globalCtx)
|
|
||||||
set.Parse([]string{"--myflag", "--otherflag=foo"})
|
|
||||||
globalSet.Parse([]string{"--myflagGlobal"})
|
|
||||||
expect(t, c.NumFlags(), 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalFlag(t *testing.T) {
|
|
||||||
var globalFlag string
|
|
||||||
var globalFlagSet bool
|
|
||||||
app := NewApp()
|
|
||||||
app.Flags = []Flag{
|
|
||||||
StringFlag{Name: "global, g", Usage: "global"},
|
|
||||||
}
|
|
||||||
app.Action = func(c *Context) error {
|
|
||||||
globalFlag = c.GlobalString("global")
|
|
||||||
globalFlagSet = c.GlobalIsSet("global")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
app.Run([]string{"command", "-g", "foo"})
|
|
||||||
expect(t, globalFlag, "foo")
|
|
||||||
expect(t, globalFlagSet, true)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalFlagsInSubcommands(t *testing.T) {
|
|
||||||
subcommandRun := false
|
|
||||||
parentFlag := false
|
|
||||||
app := NewApp()
|
|
||||||
|
|
||||||
app.Flags = []Flag{
|
|
||||||
BoolFlag{Name: "debug, d", Usage: "Enable debugging"},
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Commands = []Command{
|
|
||||||
{
|
|
||||||
Name: "foo",
|
|
||||||
Flags: []Flag{
|
|
||||||
BoolFlag{Name: "parent, p", Usage: "Parent flag"},
|
|
||||||
},
|
|
||||||
Subcommands: []Command{
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Action: func(c *Context) error {
|
|
||||||
if c.GlobalBool("debug") {
|
|
||||||
subcommandRun = true
|
|
||||||
}
|
|
||||||
if c.GlobalBool("parent") {
|
|
||||||
parentFlag = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Run([]string{"command", "-d", "foo", "-p", "bar"})
|
|
||||||
|
|
||||||
expect(t, subcommandRun, true)
|
|
||||||
expect(t, parentFlag, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_Set(t *testing.T) {
|
|
||||||
set := flag.NewFlagSet("test", 0)
|
|
||||||
set.Int("int", 5, "an int")
|
|
||||||
c := NewContext(nil, set, nil)
|
|
||||||
|
|
||||||
expect(t, c.IsSet("int"), false)
|
|
||||||
c.Set("int", "1")
|
|
||||||
expect(t, c.Int("int"), 1)
|
|
||||||
expect(t, c.IsSet("int"), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_GlobalSet(t *testing.T) {
|
|
||||||
gSet := flag.NewFlagSet("test", 0)
|
|
||||||
gSet.Int("int", 5, "an int")
|
|
||||||
|
|
||||||
set := flag.NewFlagSet("sub", 0)
|
|
||||||
set.Int("int", 3, "an int")
|
|
||||||
|
|
||||||
pc := NewContext(nil, gSet, nil)
|
|
||||||
c := NewContext(nil, set, pc)
|
|
||||||
|
|
||||||
c.Set("int", "1")
|
|
||||||
expect(t, c.Int("int"), 1)
|
|
||||||
expect(t, c.GlobalInt("int"), 5)
|
|
||||||
|
|
||||||
expect(t, c.GlobalIsSet("int"), false)
|
|
||||||
c.GlobalSet("int", "1")
|
|
||||||
expect(t, c.Int("int"), 1)
|
|
||||||
expect(t, c.GlobalInt("int"), 1)
|
|
||||||
expect(t, c.GlobalIsSet("int"), true)
|
|
||||||
}
|
|
115
vendor/github.com/urfave/cli/errors.go
generated
vendored
115
vendor/github.com/urfave/cli/errors.go
generated
vendored
|
@ -1,115 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
|
|
||||||
var OsExiter = os.Exit
|
|
||||||
|
|
||||||
// ErrWriter is used to write errors to the user. This can be anything
|
|
||||||
// implementing the io.Writer interface and defaults to os.Stderr.
|
|
||||||
var ErrWriter io.Writer = os.Stderr
|
|
||||||
|
|
||||||
// MultiError is an error that wraps multiple errors.
|
|
||||||
type MultiError struct {
|
|
||||||
Errors []error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMultiError creates a new MultiError. Pass in one or more errors.
|
|
||||||
func NewMultiError(err ...error) MultiError {
|
|
||||||
return MultiError{Errors: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error implements the error interface.
|
|
||||||
func (m MultiError) Error() string {
|
|
||||||
errs := make([]string, len(m.Errors))
|
|
||||||
for i, err := range m.Errors {
|
|
||||||
errs[i] = err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(errs, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorFormatter interface {
|
|
||||||
Format(s fmt.State, verb rune)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
|
||||||
// code
|
|
||||||
type ExitCoder interface {
|
|
||||||
error
|
|
||||||
ExitCode() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
|
||||||
type ExitError struct {
|
|
||||||
exitCode int
|
|
||||||
message interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExitError makes a new *ExitError
|
|
||||||
func NewExitError(message interface{}, exitCode int) *ExitError {
|
|
||||||
return &ExitError{
|
|
||||||
exitCode: exitCode,
|
|
||||||
message: message,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the string message, fulfilling the interface required by
|
|
||||||
// `error`
|
|
||||||
func (ee *ExitError) Error() string {
|
|
||||||
return fmt.Sprintf("%v", ee.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitCode returns the exit code, fulfilling the interface required by
|
|
||||||
// `ExitCoder`
|
|
||||||
func (ee *ExitError) ExitCode() int {
|
|
||||||
return ee.exitCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
|
||||||
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
|
||||||
// given exit code. If the given error is a MultiError, then this func is
|
|
||||||
// called on all members of the Errors slice and calls OsExiter with the last exit code.
|
|
||||||
func HandleExitCoder(err error) {
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if exitErr, ok := err.(ExitCoder); ok {
|
|
||||||
if err.Error() != "" {
|
|
||||||
if _, ok := exitErr.(ErrorFormatter); ok {
|
|
||||||
fmt.Fprintf(ErrWriter, "%+v\n", err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(ErrWriter, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OsExiter(exitErr.ExitCode())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if multiErr, ok := err.(MultiError); ok {
|
|
||||||
code := handleMultiError(multiErr)
|
|
||||||
OsExiter(code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMultiError(multiErr MultiError) int {
|
|
||||||
code := 1
|
|
||||||
for _, merr := range multiErr.Errors {
|
|
||||||
if multiErr2, ok := merr.(MultiError); ok {
|
|
||||||
code = handleMultiError(multiErr2)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(ErrWriter, merr)
|
|
||||||
if exitErr, ok := merr.(ExitCoder); ok {
|
|
||||||
code = exitErr.ExitCode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
122
vendor/github.com/urfave/cli/errors_test.go
generated
vendored
122
vendor/github.com/urfave/cli/errors_test.go
generated
vendored
|
@ -1,122 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHandleExitCoder_nil(t *testing.T) {
|
|
||||||
exitCode := 0
|
|
||||||
called := false
|
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
|
||||||
if !called {
|
|
||||||
exitCode = rc
|
|
||||||
called = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
|
||||||
|
|
||||||
HandleExitCoder(nil)
|
|
||||||
|
|
||||||
expect(t, exitCode, 0)
|
|
||||||
expect(t, called, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleExitCoder_ExitCoder(t *testing.T) {
|
|
||||||
exitCode := 0
|
|
||||||
called := false
|
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
|
||||||
if !called {
|
|
||||||
exitCode = rc
|
|
||||||
called = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
|
||||||
|
|
||||||
HandleExitCoder(NewExitError("galactic perimeter breach", 9))
|
|
||||||
|
|
||||||
expect(t, exitCode, 9)
|
|
||||||
expect(t, called, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
|
|
||||||
exitCode := 0
|
|
||||||
called := false
|
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
|
||||||
if !called {
|
|
||||||
exitCode = rc
|
|
||||||
called = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
|
||||||
|
|
||||||
exitErr := NewExitError("galactic perimeter breach", 9)
|
|
||||||
exitErr2 := NewExitError("last ExitCoder", 11)
|
|
||||||
err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
|
|
||||||
expect(t, exitCode, 11)
|
|
||||||
expect(t, called, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a stub to not import pkg/errors
|
|
||||||
type ErrorWithFormat struct {
|
|
||||||
error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewErrorWithFormat(m string) *ErrorWithFormat {
|
|
||||||
return &ErrorWithFormat{error: errors.New(m)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *ErrorWithFormat) Format(s fmt.State, verb rune) {
|
|
||||||
fmt.Fprintf(s, "This the format: %v", f.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
|
|
||||||
called := false
|
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
|
||||||
if !called {
|
|
||||||
called = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ErrWriter = &bytes.Buffer{}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
OsExiter = fakeOsExiter
|
|
||||||
ErrWriter = fakeErrWriter
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := NewExitError(NewErrorWithFormat("I am formatted"), 1)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
|
|
||||||
expect(t, called, true)
|
|
||||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) {
|
|
||||||
called := false
|
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
|
||||||
if !called {
|
|
||||||
called = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ErrWriter = &bytes.Buffer{}
|
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
|
||||||
|
|
||||||
err := NewMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2"))
|
|
||||||
HandleExitCoder(err)
|
|
||||||
|
|
||||||
expect(t, called, true)
|
|
||||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n")
|
|
||||||
}
|
|
93
vendor/github.com/urfave/cli/flag-types.json
generated
vendored
93
vendor/github.com/urfave/cli/flag-types.json
generated
vendored
|
@ -1,93 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Bool",
|
|
||||||
"type": "bool",
|
|
||||||
"value": false,
|
|
||||||
"context_default": "false",
|
|
||||||
"parser": "strconv.ParseBool(f.Value.String())"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "BoolT",
|
|
||||||
"type": "bool",
|
|
||||||
"value": false,
|
|
||||||
"doctail": " that is true by default",
|
|
||||||
"context_default": "false",
|
|
||||||
"parser": "strconv.ParseBool(f.Value.String())"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Duration",
|
|
||||||
"type": "time.Duration",
|
|
||||||
"doctail": " (see https://golang.org/pkg/time/#ParseDuration)",
|
|
||||||
"context_default": "0",
|
|
||||||
"parser": "time.ParseDuration(f.Value.String())"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Float64",
|
|
||||||
"type": "float64",
|
|
||||||
"context_default": "0",
|
|
||||||
"parser": "strconv.ParseFloat(f.Value.String(), 64)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Generic",
|
|
||||||
"type": "Generic",
|
|
||||||
"dest": false,
|
|
||||||
"context_default": "nil",
|
|
||||||
"context_type": "interface{}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Int64",
|
|
||||||
"type": "int64",
|
|
||||||
"context_default": "0",
|
|
||||||
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Int",
|
|
||||||
"type": "int",
|
|
||||||
"context_default": "0",
|
|
||||||
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)",
|
|
||||||
"parser_cast": "int(parsed)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "IntSlice",
|
|
||||||
"type": "*IntSlice",
|
|
||||||
"dest": false,
|
|
||||||
"context_default": "nil",
|
|
||||||
"context_type": "[]int",
|
|
||||||
"parser": "(f.Value.(*IntSlice)).Value(), error(nil)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Int64Slice",
|
|
||||||
"type": "*Int64Slice",
|
|
||||||
"dest": false,
|
|
||||||
"context_default": "nil",
|
|
||||||
"context_type": "[]int64",
|
|
||||||
"parser": "(f.Value.(*Int64Slice)).Value(), error(nil)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "String",
|
|
||||||
"type": "string",
|
|
||||||
"context_default": "\"\"",
|
|
||||||
"parser": "f.Value.String(), error(nil)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "StringSlice",
|
|
||||||
"type": "*StringSlice",
|
|
||||||
"dest": false,
|
|
||||||
"context_default": "nil",
|
|
||||||
"context_type": "[]string",
|
|
||||||
"parser": "(f.Value.(*StringSlice)).Value(), error(nil)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Uint64",
|
|
||||||
"type": "uint64",
|
|
||||||
"context_default": "0",
|
|
||||||
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Uint",
|
|
||||||
"type": "uint",
|
|
||||||
"context_default": "0",
|
|
||||||
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)",
|
|
||||||
"parser_cast": "uint(parsed)"
|
|
||||||
}
|
|
||||||
]
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user