├── .DS_Store ├── .gitignore ├── DOCS.md ├── Dockerfile ├── LICENSE ├── README.md ├── logo.svg ├── main.go ├── plugin.go ├── template.go └── vendor ├── github.com ├── aymerick │ └── raymond │ │ ├── BENCHMARKS.md │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── VERSION │ │ ├── ast │ │ ├── node.go │ │ └── print.go │ │ ├── data_frame.go │ │ ├── escape.go │ │ ├── eval.go │ │ ├── helper.go │ │ ├── lexer │ │ ├── lexer.go │ │ └── token.go │ │ ├── parser │ │ ├── parser.go │ │ └── whitespace.go │ │ ├── partial.go │ │ ├── raymond.go │ │ ├── raymond.png │ │ ├── string.go │ │ ├── template.go │ │ └── utils.go └── urfave │ └── cli │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── app.go │ ├── appveyor.yml │ ├── category.go │ ├── cli.go │ ├── command.go │ ├── context.go │ ├── errors.go │ ├── flag-types.json │ ├── flag.go │ ├── flag_generated.go │ ├── funcs.go │ ├── generate-flag-types │ ├── help.go │ └── runtests └── vendor.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clem109/drone-wechat/43762c4521e73098a781c1e390280a72b89a65d5/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /release -------------------------------------------------------------------------------- /DOCS.md: -------------------------------------------------------------------------------- 1 | Use the wechat plugin to notify users during the Drone pipeline. 2 | 3 | Please only use these paramters for now: 4 | 5 | * `corpid` - The corpid for authorization 6 | * `corp_secret` - The corp secret for authorization 7 | * `agent_id` - The agent id to send the message 8 | * `to_party` - The party ids to send the message 9 | * `to_user` - The user ids to send the message to 10 | * `to_tag` - The tag ids to send the message to 11 | * `title` - Title of the card 12 | * `description` - Text description of the card 13 | * `msg_url` - The agent id to send the message 14 | * `btntxt` - The text for the button on the card 15 | 16 | ## Example 17 | 18 | The following is a sample configuration in your .drone.yml file: 19 | 20 | ```yaml 21 | pipeline: 22 | wechat: 23 | image: clem109/drone-wechat 24 | corpid: corpid 25 | corp_secret: secret 26 | agent_id: 1234567 27 | title: ${DRONE_REPO_NAME} 28 | description: "Build Number: ${DRONE_BUILD_NUMBER} failed. ${DRONE_COMMIT_AUTHOR} please fix. Check the results here: ${DRONE_BUILD_LINK} " 29 | msg_url: ${DRONE_BUILD_LINK} 30 | btn_txt: btn 31 | when: 32 | status: [ failure ] 33 | ``` 34 | 35 | If you want to add secrets that you set in the drone UI make sure you use the 36 | correct naming scheme, all parts of the build can be hidden and set within the 37 | Drone UI, please consult the [main.go](main.go) EnvVar: 38 | 39 | ```yaml 40 | pipeline: 41 | wechat: 42 | image: clem109/drone-wechat 43 | secrets: [plugin_corpid, plugin_corp_secret, plugin_agent_id] 44 | title: ${DRONE_REPO_NAME} 45 | description: "Build Number: ${DRONE_BUILD_NUMBER} failed. ${DRONE_COMMIT_AUTHOR} please fix. Check the results here: ${DRONE_BUILD_LINK} " 46 | msg_url: ${DRONE_BUILD_LINK} 47 | btn_txt: btn 48 | when: 49 | status: [ failure ] 50 | ``` 51 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM plugins/base:multiarch 2 | MAINTAINER Clement Venard 3 | 4 | LABEL org.label-schema.version=latest 5 | LABEL org.label-schema.vcs-url="https://github.com/clem109/drone-wechat.git" 6 | LABEL org.label-schema.name="Drone Wechat" 7 | LABEL org.label-schema.schema-version="1.0" 8 | 9 | ADD release/linux/amd64/drone-wechat /bin/ 10 | ENTRYPOINT ["/bin/drone-wechat"] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Clement Venard 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drone-wechat-work 2 | 3 | Drone plugin to send build status notifications via WeChat for Work. For usage 4 | information please look at [the docs](DOCS.md). This plugin creates a HTTP 5 | request in the form as 6 | [shown here (it's in chinese)](https://work.weixin.qq.com/api/doc#10167/文本卡片消息) 7 | 8 | ## Build 9 | 10 | Build the binary with the following commands: 11 | 12 | ``` 13 | go build 14 | ``` 15 | 16 | ## Docker 17 | 18 | Build the Docker image with the following commands: 19 | 20 | ``` 21 | GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a -tags netgo -o release/linux/amd64/drone-wechat 22 | docker build --rm -t clem109/drone-wechat . 23 | ``` 24 | 25 | ### Usage 26 | 27 | ``` 28 | docker run --rm \ 29 | -e PLUGIN_CORPID=corpid \ 30 | -e PLUGIN_CORP_SECRET=corpsecret \ 31 | -e PLUGIN_AGENT_ID=agentid \ 32 | -e PLUGIN_TO_USER=userId \ 33 | -e PLUGIN_TO_TAG=tagId \ 34 | -e PLUGIN_TO_PARTY=toParty \ 35 | -e PLUGIN_SAFE=1 \ 36 | -e PLUGIN_DEBUG=true \ 37 | -e PLUGIN_MSG_URL=url \ 38 | -e PLUGIN_BTN_TXT=true \ 39 | -e PLUGIN_TITLE=title \ 40 | -e PLUGIN_DESCRIPTION=description \ 41 | -e DRONE_REPO_OWNER=octocat \ 42 | -e DRONE_REPO_NAME=hello-world \ 43 | -e DRONE_COMMIT_SHA=7fd1a60b01f91b314f59955a4e4d4e80d8edf11d \ 44 | -e DRONE_COMMIT_BRANCH=master \ 45 | -e DRONE_COMMIT_AUTHOR=octocat \ 46 | -e DRONE_BUILD_NUMBER=1 \ 47 | -e DRONE_BUILD_STATUS=success \ 48 | -e DRONE_BUILD_LINK=http://github.com/octocat/hello-world \ 49 | -e DRONE_TAG=1.0.0 \ 50 | plugins/drone-wechat 51 | ``` 52 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | var build = "0" // build number set at compile-time 12 | 13 | func main() { 14 | app := cli.NewApp() 15 | app.Name = "WeChat work plugin" 16 | app.Usage = "Wechat Work plugin" 17 | app.Action = run 18 | app.Version = fmt.Sprintf("1.0.0+%s", build) 19 | app.Flags = []cli.Flag{ 20 | cli.StringFlag{ 21 | Name: "method", 22 | Usage: "webhook method", 23 | EnvVar: "PLUGIN_METHOD", 24 | Value: "POST", 25 | }, 26 | cli.StringFlag{ 27 | Name: "corpid", 28 | Usage: "The corpid to get the access token", 29 | EnvVar: "PLUGIN_CORPID,WEBHOOK_CORPID", 30 | }, 31 | cli.StringFlag{ 32 | Name: "corp-secret", 33 | Usage: "The corpsecret to get the access token", 34 | EnvVar: "PLUGIN_CORP_SECRET,WEBHOOK_CORP_Secret", 35 | }, 36 | cli.StringFlag{ 37 | Name: "agentid", 38 | Usage: "Agent ID", 39 | EnvVar: "PLUGIN_AGENT_ID,WEBHOOK_AGENT_ID", 40 | }, 41 | cli.StringFlag{ 42 | Name: "msgtype", 43 | Usage: "The type of message, either text, textcard", 44 | EnvVar: "PLUGIN_MSGTYPE,WEBHOOK_MSGTYPE", 45 | Value: "textcard", 46 | }, 47 | cli.StringFlag{ 48 | Name: "content-type", 49 | Usage: "content type", 50 | EnvVar: "PLUGIN_CONTENT_TYPE", 51 | Value: "application/json", 52 | }, 53 | cli.StringFlag{ 54 | Name: "touser", 55 | Usage: "The users to send the message to, @all for all users", 56 | EnvVar: "PLUGIN_TO_USER", 57 | Value: "@all", 58 | }, 59 | cli.StringFlag{ 60 | Name: "toparty", 61 | Usage: "Party ID to send messages to", 62 | EnvVar: "PLUGIN_TO_PARTY", 63 | }, 64 | cli.StringFlag{ 65 | Name: "totag", 66 | Usage: "Tag ID to send messages to", 67 | EnvVar: "PLUGIN_TO_TAG", 68 | }, 69 | cli.StringFlag{ 70 | Name: "title", 71 | Usage: "Message title", 72 | EnvVar: "PLUGIN_TITLE", 73 | }, 74 | cli.StringFlag{ 75 | Name: "description", 76 | Usage: "Description ", 77 | EnvVar: "PLUGIN_DESCRIPTION", 78 | }, 79 | cli.StringFlag{ 80 | Name: "msgurl", 81 | Usage: "message url ", 82 | EnvVar: "PLUGIN_MSG_URL", 83 | }, 84 | cli.StringFlag{ 85 | Name: "btntxt", 86 | Usage: "Button text ", 87 | EnvVar: "PLUGIN_BTN_TXT", 88 | }, 89 | cli.StringFlag{ 90 | Name: "safe", 91 | Usage: "Whether to make this message confidential or not, 0 is false, 1 is true. Defaults to false", 92 | EnvVar: "PLUGIN_SAFE", 93 | Value: "0", 94 | }, 95 | cli.BoolFlag{ 96 | Name: "debug", 97 | Usage: "enable debug information", 98 | EnvVar: "PLUGIN_DEBUG", 99 | }, 100 | cli.BoolFlag{ 101 | Name: "skip-verify", 102 | Usage: "skip ssl verification", 103 | EnvVar: "PLUGIN_SKIP_VERIFY", 104 | }, 105 | cli.StringFlag{ 106 | Name: "repo.owner", 107 | Usage: "repository owner", 108 | EnvVar: "DRONE_REPO_OWNER", 109 | }, 110 | cli.StringFlag{ 111 | Name: "repo.name", 112 | Usage: "repository name", 113 | EnvVar: "DRONE_REPO_NAME", 114 | }, 115 | cli.StringFlag{ 116 | Name: "commit.sha", 117 | Usage: "git commit sha", 118 | EnvVar: "DRONE_COMMIT_SHA", 119 | }, 120 | cli.StringFlag{ 121 | Name: "commit.ref", 122 | Value: "refs/heads/master", 123 | Usage: "git commit ref", 124 | EnvVar: "DRONE_COMMIT_REF", 125 | }, 126 | cli.StringFlag{ 127 | Name: "commit.branch", 128 | Value: "master", 129 | Usage: "git commit branch", 130 | EnvVar: "DRONE_COMMIT_BRANCH", 131 | }, 132 | cli.StringFlag{ 133 | Name: "commit.author", 134 | Usage: "git author name", 135 | EnvVar: "DRONE_COMMIT_AUTHOR", 136 | }, 137 | cli.StringFlag{ 138 | Name: "commit.message", 139 | Usage: "commit message", 140 | EnvVar: "DRONE_COMMIT_MESSAGE", 141 | }, 142 | cli.StringFlag{ 143 | Name: "build.event", 144 | Value: "push", 145 | Usage: "build event", 146 | EnvVar: "DRONE_BUILD_EVENT", 147 | }, 148 | cli.IntFlag{ 149 | Name: "build.number", 150 | Usage: "build number", 151 | EnvVar: "DRONE_BUILD_NUMBER", 152 | }, 153 | cli.StringFlag{ 154 | Name: "build.status", 155 | Usage: "build status", 156 | Value: "success", 157 | EnvVar: "DRONE_BUILD_STATUS", 158 | }, 159 | cli.StringFlag{ 160 | Name: "build.link", 161 | Usage: "build link", 162 | EnvVar: "DRONE_BUILD_LINK", 163 | }, 164 | cli.Int64Flag{ 165 | Name: "build.started", 166 | Usage: "build started", 167 | EnvVar: "DRONE_BUILD_STARTED", 168 | }, 169 | cli.Int64Flag{ 170 | Name: "build.created", 171 | Usage: "build created", 172 | EnvVar: "DRONE_BUILD_CREATED", 173 | }, 174 | cli.StringFlag{ 175 | Name: "build.tag", 176 | Usage: "build tag", 177 | EnvVar: "DRONE_TAG", 178 | }, 179 | cli.Int64Flag{ 180 | Name: "job.started", 181 | Usage: "job started", 182 | EnvVar: "DRONE_JOB_STARTED", 183 | }, 184 | cli.StringFlag{ 185 | Name: "env-file", 186 | Usage: "source env file", 187 | }, 188 | } 189 | if err := app.Run(os.Args); err != nil { 190 | log.Fatal(err) 191 | } 192 | } 193 | 194 | func run(c *cli.Context) error { 195 | plugin := Plugin{ 196 | Repo: Repo{ 197 | Owner: c.String("repo.owner"), 198 | Name: c.String("repo.name"), 199 | }, 200 | Build: Build{ 201 | Tag: c.String("build.tag"), 202 | Number: c.Int("build.number"), 203 | Event: c.String("build.event"), 204 | Status: c.String("build.status"), 205 | Commit: c.String("commit.sha"), 206 | Ref: c.String("commit.ref"), 207 | Branch: c.String("commit.branch"), 208 | Author: c.String("commit.author"), 209 | Message: c.String("commit.message"), 210 | Link: c.String("build.link"), 211 | Started: c.Int64("build.started"), 212 | Created: c.Int64("build.created"), 213 | }, 214 | Job: Job{ 215 | Started: c.Int64("job.started"), 216 | }, 217 | Config: Config{ 218 | Method: c.String("method"), 219 | CorpID: c.String("corpid"), 220 | CorpSecret: c.String("corp-secret"), 221 | Agentid: c.Int("agentid"), 222 | MsgType: c.String("msgtype"), 223 | MsgURL: c.String("msgurl"), 224 | BtnTxt: c.String("btntxt"), 225 | ToUser: c.String("touser"), 226 | ToParty: c.String("toparty"), 227 | ToTag: c.String("totag"), 228 | Title: c.String("title"), 229 | Description: c.String("description"), 230 | Safe: c.Int("safe"), 231 | ContentType: c.String("content-type"), 232 | Debug: c.Bool("debug"), 233 | SkipVerify: c.Bool("skip-verify"), 234 | }, 235 | } 236 | return plugin.Exec() 237 | } 238 | -------------------------------------------------------------------------------- /plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "encoding/json" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | ) 11 | 12 | const ( 13 | respFormat = "Webhook %d\n URL: %s\n RESPONSE STATUS: %s\n RESPONSE BODY: %s\n" 14 | debugRespFormat = "Webhook %d\n URL: %s\n METHOD: %s\n HEADERS: %s\n REQUEST BODY: %s\n RESPONSE STATUS: %s\n RESPONSE BODY: %s\n" 15 | ) 16 | 17 | type ( 18 | Repo struct { 19 | Owner string `json:"owner"` 20 | Name string `json:"name"` 21 | } 22 | 23 | Build struct { 24 | Tag string `json:"tag"` 25 | Event string `json:"event"` 26 | Number int `json:"number"` 27 | Commit string `json:"commit"` 28 | Ref string `json:"ref"` 29 | Branch string `json:"branch"` 30 | Author string `json:"author"` 31 | Message string `json:"message"` 32 | Status string `json:"status"` 33 | Link string `json:"link"` 34 | Started int64 `json:"started"` 35 | Created int64 `json:"created"` 36 | } 37 | 38 | Config struct { 39 | Method string 40 | CorpID string 41 | CorpSecret string 42 | Agentid int `json:"agentid"` 43 | MsgType string `json:"msgtype"` 44 | URL string 45 | MsgURL string 46 | BtnTxt string 47 | ToUser string `json:"touser"` 48 | ToParty string `json:"toparty"` 49 | ToTag string `json:"totag"` 50 | Safe int `json:"safe"` 51 | ContentType string 52 | Debug bool 53 | SkipVerify bool 54 | Title string `json:"title"` 55 | Description string `json:"description"` 56 | } 57 | 58 | Response struct { 59 | Errcode int `json:"errcode"` 60 | Errmsg string `json:"errmsg"` 61 | AccessToken string `json:"access_token"` 62 | ExpiresIn int `json:"expires_in"` 63 | } 64 | 65 | Job struct { 66 | Started int64 `json:"started"` 67 | } 68 | 69 | Plugin struct { 70 | Repo Repo 71 | Build Build 72 | Config Config 73 | Response Response 74 | Job Job 75 | } 76 | ) 77 | 78 | func getAccessToken(body []byte) (*Response, error) { 79 | var s = new(Response) 80 | err := json.Unmarshal(body, &s) 81 | if err != nil { 82 | fmt.Println("whoops:", err) 83 | } 84 | return s, err 85 | } 86 | 87 | func (p Plugin) Exec() error { 88 | 89 | var buf bytes.Buffer 90 | var b []byte 91 | 92 | // construct URL to get access token 93 | accessURL := "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + p.Config.CorpID + "&corpsecret=" + p.Config.CorpSecret 94 | fmt.Println("URL:>", accessURL) 95 | 96 | req, err := http.NewRequest("GET", accessURL, bytes.NewBuffer(b)) 97 | var client = http.DefaultClient 98 | if p.Config.SkipVerify { 99 | client = &http.Client{ 100 | Transport: &http.Transport{ 101 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 102 | }, 103 | } 104 | } 105 | resp, err := client.Do(req) 106 | 107 | if err != nil { 108 | fmt.Printf("Error: Failed to execute the HTTP request. %s\n", err) 109 | return err 110 | } 111 | 112 | defer resp.Body.Close() 113 | 114 | fmt.Println("response Status:", resp.Status) 115 | fmt.Println("response Headers:", resp.Header) 116 | body, _ := ioutil.ReadAll(resp.Body) 117 | fmt.Println("response Body:", string(body)) 118 | 119 | s, err := getAccessToken(body) 120 | 121 | // POST Request to WeChat work 122 | if p.Config.Title == "" { 123 | data := struct { 124 | Repo Repo `json:"repo"` 125 | Build Build `json:"build"` 126 | }{p.Repo, p.Build} 127 | 128 | if err := json.NewEncoder(&buf).Encode(&data); err != nil { 129 | fmt.Printf("Error: Failed to encode JSON payload. %s\n", err) 130 | return err 131 | } 132 | b = buf.Bytes() 133 | } else { 134 | textCard := struct { 135 | Title string `json:"title"` 136 | Description string `json:"description"` 137 | MsgURL string `json:"url"` 138 | BtnTxt string `json:"btntext"` 139 | }{p.Config.Title, p.Config.Description, p.Config.MsgURL, p.Config.BtnTxt} 140 | data := struct { 141 | ToUser string `json:"touser"` 142 | ToParty string `json:"toparty"` 143 | ToTag string `json:"totag"` 144 | MsgType string `json:"msgtype"` 145 | Agentid int `json:"agentid"` 146 | Safe int `json:"safe"` 147 | TextCard struct { 148 | Title string `json:"title"` 149 | Description string `json:"description"` 150 | MsgURL string `json:"url"` 151 | BtnTxt string `json:"btntext"` 152 | } `json:"textcard"` 153 | }{p.Config.ToUser, p.Config.ToParty, p.Config.ToTag, p.Config.MsgType, p.Config.Agentid, p.Config.Safe, textCard} 154 | 155 | b, _ = json.Marshal(data) // []byte(data) 156 | 157 | } 158 | 159 | // POST URL 160 | url := "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + s.AccessToken 161 | 162 | request, err := http.NewRequest("POST", url, bytes.NewBuffer(b)) 163 | request.Header.Set("Content-Type", "application/json") 164 | 165 | client = http.DefaultClient 166 | if p.Config.SkipVerify { 167 | client = &http.Client{ 168 | Transport: &http.Transport{ 169 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 170 | }, 171 | } 172 | } 173 | 174 | response, err := client.Do(request) 175 | 176 | if err != nil { 177 | fmt.Printf("Error: Failed to execute the HTTP request. %s\n", err) 178 | return err 179 | } 180 | defer response.Body.Close() 181 | 182 | fmt.Println("response Status:", response.Status) 183 | fmt.Println("response Headers:", response.Header) 184 | responseBody, _ := ioutil.ReadAll(response.Body) 185 | fmt.Println("response Body:", string(responseBody)) 186 | 187 | if p.Config.Debug || resp.StatusCode >= http.StatusBadRequest { 188 | body, err := ioutil.ReadAll(resp.Body) 189 | 190 | if err != nil { 191 | fmt.Printf("Error: Failed to read the HTTP response body. %s\n", err) 192 | } 193 | 194 | if p.Config.Debug { 195 | fmt.Printf( 196 | debugRespFormat, 197 | req.URL, 198 | req.Method, 199 | req.Header, 200 | string(b), 201 | resp.Status, 202 | string(body), 203 | ) 204 | } else { 205 | fmt.Printf( 206 | respFormat, 207 | req.URL, 208 | resp.Status, 209 | string(body), 210 | ) 211 | } 212 | } 213 | return nil 214 | } 215 | -------------------------------------------------------------------------------- /template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "net/url" 8 | "strings" 9 | "time" 10 | "unicode" 11 | "unicode/utf8" 12 | 13 | "github.com/aymerick/raymond" 14 | ) 15 | 16 | func init() { 17 | raymond.RegisterHelpers(funcs) 18 | } 19 | 20 | // Render parses and executes a template, returning the results in string format. 21 | func Render(template string, payload interface{}) (s string, err error) { 22 | u, err := url.Parse(template) 23 | if err == nil { 24 | switch u.Scheme { 25 | case "http", "https": 26 | res, err := http.Get(template) 27 | if err != nil { 28 | return s, err 29 | } 30 | defer res.Body.Close() 31 | out, err := ioutil.ReadAll(res.Body) 32 | if err != nil { 33 | return s, err 34 | } 35 | template = string(out) 36 | 37 | case "file": 38 | out, err := ioutil.ReadFile(u.Path) 39 | if err != nil { 40 | return s, err 41 | } 42 | template = string(out) 43 | } 44 | } 45 | 46 | return raymond.Render(template, payload) 47 | } 48 | 49 | // RenderTrim parses and executes a template, returning the results in string 50 | // format. The result is trimmed to remove left and right padding and newlines 51 | // that may be added unintentially in the template markup. 52 | func RenderTrim(template string, playload interface{}) (string, error) { 53 | out, err := Render(template, playload) 54 | return strings.Trim(out, " \n"), err 55 | } 56 | 57 | var funcs = map[string]interface{}{ 58 | "uppercasefirst": uppercaseFirst, 59 | "uppercase": strings.ToUpper, 60 | "lowercase": strings.ToLower, 61 | "duration": toDuration, 62 | "datetime": toDatetime, 63 | "success": isSuccess, 64 | "failure": isFailure, 65 | "truncate": truncate, 66 | "urlencode": urlencode, 67 | "since": since, 68 | } 69 | 70 | func truncate(s string, len int) string { 71 | if utf8.RuneCountInString(s) <= len { 72 | return s 73 | } 74 | runes := []rune(s) 75 | return string(runes[:len]) 76 | 77 | } 78 | 79 | func uppercaseFirst(s string) string { 80 | a := []rune(s) 81 | a[0] = unicode.ToUpper(a[0]) 82 | s = string(a) 83 | return s 84 | } 85 | 86 | func toDuration(started, finished float64) string { 87 | return fmt.Sprintln(time.Duration(finished-started) * time.Second) 88 | } 89 | 90 | func toDatetime(timestamp float64, layout, zone string) string { 91 | if len(zone) == 0 { 92 | return time.Unix(int64(timestamp), 0).Format(layout) 93 | } 94 | loc, err := time.LoadLocation(zone) 95 | if err != nil { 96 | return time.Unix(int64(timestamp), 0).Local().Format(layout) 97 | } 98 | return time.Unix(int64(timestamp), 0).In(loc).Format(layout) 99 | } 100 | 101 | func isSuccess(conditional bool, options *raymond.Options) string { 102 | if !conditional { 103 | return options.Inverse() 104 | } 105 | 106 | switch options.ParamStr(0) { 107 | case "success": 108 | return options.Fn() 109 | default: 110 | return options.Inverse() 111 | } 112 | } 113 | 114 | func isFailure(conditional bool, options *raymond.Options) string { 115 | if !conditional { 116 | return options.Inverse() 117 | } 118 | 119 | switch options.ParamStr(0) { 120 | case "failure", "error", "killed": 121 | return options.Fn() 122 | default: 123 | return options.Inverse() 124 | } 125 | } 126 | 127 | func urlencode(options *raymond.Options) string { 128 | return url.QueryEscape(options.Fn()) 129 | } 130 | 131 | func since(start int64) string { 132 | // NOTE: not using `time.Since()` because the fractional second component 133 | // will give us something like "40m12.917523438s" vs "40m12s". We lose 134 | // some precision, but the format is much more readable. 135 | now := time.Unix(time.Now().Unix(), 0) 136 | return fmt.Sprintln(now.Sub(time.Unix(start, 0))) 137 | } 138 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/BENCHMARKS.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | Hardware: MacBookPro11,1 - Intel Core i5 - 2,6 GHz - 8 Go RAM 4 | 5 | With: 6 | 7 | - handlebars.js #8cba84df119c317fcebc49fb285518542ca9c2d0 8 | - raymond #7bbaaf50ed03c96b56687d7fa6c6e04e02375a98 9 | 10 | 11 | ## handlebars.js (ops/ms) 12 | 13 | arguments 198 ±4 (5) 14 | array-each 568 ±23 (5) 15 | array-mustache 522 ±18 (4) 16 | complex 71 ±7 (3) 17 | data 67 ±2 (3) 18 | depth-1 47 ±2 (3) 19 | depth-2 14 ±1 (2) 20 | object-mustache 1099 ±47 (5) 21 | object 907 ±58 (4) 22 | partial-recursion 46 ±3 (4) 23 | partial 68 ±3 (3) 24 | paths 1650 ±50 (3) 25 | string 2552 ±157 (3) 26 | subexpression 141 ±2 (4) 27 | variables 2671 ±83 (4) 28 | 29 | 30 | ## raymond 31 | 32 | BenchmarkArguments 200000 6642 ns/op 151 ops/ms 33 | BenchmarkArrayEach 100000 19584 ns/op 51 ops/ms 34 | BenchmarkArrayMustache 100000 17305 ns/op 58 ops/ms 35 | BenchmarkComplex 30000 50270 ns/op 20 ops/ms 36 | BenchmarkData 50000 25551 ns/op 39 ops/ms 37 | BenchmarkDepth1 100000 20162 ns/op 50 ops/ms 38 | BenchmarkDepth2 30000 47782 ns/op 21 ops/ms 39 | BenchmarkObjectMustache 200000 7668 ns/op 130 ops/ms 40 | BenchmarkObject 200000 8843 ns/op 113 ops/ms 41 | BenchmarkPartialRecursion 50000 23139 ns/op 43 ops/ms 42 | BenchmarkPartial 50000 31015 ns/op 32 ops/ms 43 | BenchmarkPath 200000 8997 ns/op 111 ops/ms 44 | BenchmarkString 1000000 1879 ns/op 532 ops/ms 45 | BenchmarkSubExpression 300000 4935 ns/op 203 ops/ms 46 | BenchmarkVariables 200000 6478 ns/op 154 ops/ms 47 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Raymond Changelog 2 | 3 | ### Raymond 2.0.1 _(June 01, 2016)_ 4 | 5 | - [BUGFIX] Removes data races [#3](https://github.com/aymerick/raymond/issues/3) - Thanks [@markbates](https://github.com/markbates) 6 | 7 | ### Raymond 2.0.0 _(May 01, 2016)_ 8 | 9 | - [BUGFIX] Fixes passing of context in helper options [#2](https://github.com/aymerick/raymond/issues/2) - Thanks [@GhostRussia](https://github.com/GhostRussia) 10 | - [BREAKING] Renames and unexports constants: 11 | 12 | - `handlebars.DUMP_TPL` 13 | - `lexer.ESCAPED_ESCAPED_OPEN_MUSTACHE` 14 | - `lexer.ESCAPED_OPEN_MUSTACHE` 15 | - `lexer.OPEN_MUSTACHE` 16 | - `lexer.CLOSE_MUSTACHE` 17 | - `lexer.CLOSE_STRIP_MUSTACHE` 18 | - `lexer.CLOSE_UNESCAPED_STRIP_MUSTACHE` 19 | - `lexer.DUMP_TOKEN_POS` 20 | - `lexer.DUMP_ALL_TOKENS_VAL` 21 | 22 | 23 | ### Raymond 1.1.0 _(June 15, 2015)_ 24 | 25 | - Permits templates references with lowercase versions of struct fields. 26 | - Adds `ParseFile()` function. 27 | - Adds `RegisterPartialFile()`, `RegisterPartialFiles()` and `Clone()` methods on `Template`. 28 | - Helpers can now be struct methods. 29 | - Ensures safe concurrent access to helpers and partials. 30 | 31 | ### Raymond 1.0.0 _(June 09, 2015)_ 32 | 33 | - 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. 34 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Aymerick JEHANNE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/VERSION: -------------------------------------------------------------------------------- 1 | 2.0.1 2 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/ast/print.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // printVisitor implements the Visitor interface to print a AST. 9 | type printVisitor struct { 10 | buf string 11 | depth int 12 | 13 | original bool 14 | inBlock bool 15 | } 16 | 17 | func newPrintVisitor() *printVisitor { 18 | return &printVisitor{} 19 | } 20 | 21 | // Print returns a string representation of given AST, that can be used for debugging purpose. 22 | func Print(node Node) string { 23 | visitor := newPrintVisitor() 24 | node.Accept(visitor) 25 | return visitor.output() 26 | } 27 | 28 | func (v *printVisitor) output() string { 29 | return v.buf 30 | } 31 | 32 | func (v *printVisitor) indent() { 33 | for i := 0; i < v.depth; { 34 | v.buf += " " 35 | i++ 36 | } 37 | } 38 | 39 | func (v *printVisitor) str(val string) { 40 | v.buf += val 41 | } 42 | 43 | func (v *printVisitor) nl() { 44 | v.str("\n") 45 | } 46 | 47 | func (v *printVisitor) line(val string) { 48 | v.indent() 49 | v.str(val) 50 | v.nl() 51 | } 52 | 53 | // 54 | // Visitor interface 55 | // 56 | 57 | // Statements 58 | 59 | // VisitProgram implements corresponding Visitor interface method 60 | func (v *printVisitor) VisitProgram(node *Program) interface{} { 61 | if len(node.BlockParams) > 0 { 62 | v.line("BLOCK PARAMS: [ " + strings.Join(node.BlockParams, " ") + " ]") 63 | } 64 | 65 | for _, n := range node.Body { 66 | n.Accept(v) 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // VisitMustache implements corresponding Visitor interface method 73 | func (v *printVisitor) VisitMustache(node *MustacheStatement) interface{} { 74 | v.indent() 75 | v.str("{{ ") 76 | 77 | node.Expression.Accept(v) 78 | 79 | v.str(" }}") 80 | v.nl() 81 | 82 | return nil 83 | } 84 | 85 | // VisitBlock implements corresponding Visitor interface method 86 | func (v *printVisitor) VisitBlock(node *BlockStatement) interface{} { 87 | v.inBlock = true 88 | 89 | v.line("BLOCK:") 90 | v.depth++ 91 | 92 | node.Expression.Accept(v) 93 | 94 | if node.Program != nil { 95 | v.line("PROGRAM:") 96 | v.depth++ 97 | node.Program.Accept(v) 98 | v.depth-- 99 | } 100 | 101 | if node.Inverse != nil { 102 | // if node.Program != nil { 103 | // v.depth++ 104 | // } 105 | 106 | v.line("{{^}}") 107 | v.depth++ 108 | node.Inverse.Accept(v) 109 | v.depth-- 110 | 111 | // if node.Program != nil { 112 | // v.depth-- 113 | // } 114 | } 115 | 116 | v.inBlock = false 117 | 118 | return nil 119 | } 120 | 121 | // VisitPartial implements corresponding Visitor interface method 122 | func (v *printVisitor) VisitPartial(node *PartialStatement) interface{} { 123 | v.indent() 124 | v.str("{{> PARTIAL:") 125 | 126 | v.original = true 127 | node.Name.Accept(v) 128 | v.original = false 129 | 130 | if len(node.Params) > 0 { 131 | v.str(" ") 132 | node.Params[0].Accept(v) 133 | } 134 | 135 | // hash 136 | if node.Hash != nil { 137 | v.str(" ") 138 | node.Hash.Accept(v) 139 | } 140 | 141 | v.str(" }}") 142 | v.nl() 143 | 144 | return nil 145 | } 146 | 147 | // VisitContent implements corresponding Visitor interface method 148 | func (v *printVisitor) VisitContent(node *ContentStatement) interface{} { 149 | v.line("CONTENT[ '" + node.Value + "' ]") 150 | 151 | return nil 152 | } 153 | 154 | // VisitComment implements corresponding Visitor interface method 155 | func (v *printVisitor) VisitComment(node *CommentStatement) interface{} { 156 | v.line("{{! '" + node.Value + "' }}") 157 | 158 | return nil 159 | } 160 | 161 | // Expressions 162 | 163 | // VisitExpression implements corresponding Visitor interface method 164 | func (v *printVisitor) VisitExpression(node *Expression) interface{} { 165 | if v.inBlock { 166 | v.indent() 167 | } 168 | 169 | // path 170 | node.Path.Accept(v) 171 | 172 | // params 173 | v.str(" [") 174 | for i, n := range node.Params { 175 | if i > 0 { 176 | v.str(", ") 177 | } 178 | n.Accept(v) 179 | } 180 | v.str("]") 181 | 182 | // hash 183 | if node.Hash != nil { 184 | v.str(" ") 185 | node.Hash.Accept(v) 186 | } 187 | 188 | if v.inBlock { 189 | v.nl() 190 | } 191 | 192 | return nil 193 | } 194 | 195 | // VisitSubExpression implements corresponding Visitor interface method 196 | func (v *printVisitor) VisitSubExpression(node *SubExpression) interface{} { 197 | node.Expression.Accept(v) 198 | 199 | return nil 200 | } 201 | 202 | // VisitPath implements corresponding Visitor interface method 203 | func (v *printVisitor) VisitPath(node *PathExpression) interface{} { 204 | if v.original { 205 | v.str(node.Original) 206 | } else { 207 | path := strings.Join(node.Parts, "/") 208 | 209 | result := "" 210 | if node.Data { 211 | result += "@" 212 | } 213 | 214 | v.str(result + "PATH:" + path) 215 | } 216 | 217 | return nil 218 | } 219 | 220 | // Literals 221 | 222 | // VisitString implements corresponding Visitor interface method 223 | func (v *printVisitor) VisitString(node *StringLiteral) interface{} { 224 | if v.original { 225 | v.str(node.Value) 226 | } else { 227 | v.str("\"" + node.Value + "\"") 228 | } 229 | 230 | return nil 231 | } 232 | 233 | // VisitBoolean implements corresponding Visitor interface method 234 | func (v *printVisitor) VisitBoolean(node *BooleanLiteral) interface{} { 235 | if v.original { 236 | v.str(node.Original) 237 | } else { 238 | v.str(fmt.Sprintf("BOOLEAN{%s}", node.Canonical())) 239 | } 240 | 241 | return nil 242 | } 243 | 244 | // VisitNumber implements corresponding Visitor interface method 245 | func (v *printVisitor) VisitNumber(node *NumberLiteral) interface{} { 246 | if v.original { 247 | v.str(node.Original) 248 | } else { 249 | v.str(fmt.Sprintf("NUMBER{%s}", node.Canonical())) 250 | } 251 | 252 | return nil 253 | } 254 | 255 | // Miscellaneous 256 | 257 | // VisitHash implements corresponding Visitor interface method 258 | func (v *printVisitor) VisitHash(node *Hash) interface{} { 259 | v.str("HASH{") 260 | 261 | for i, p := range node.Pairs { 262 | if i > 0 { 263 | v.str(", ") 264 | } 265 | p.Accept(v) 266 | } 267 | 268 | v.str("}") 269 | 270 | return nil 271 | } 272 | 273 | // VisitHashPair implements corresponding Visitor interface method 274 | func (v *printVisitor) VisitHashPair(node *HashPair) interface{} { 275 | v.str(node.Key + "=") 276 | node.Val.Accept(v) 277 | 278 | return nil 279 | } 280 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/data_frame.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import "reflect" 4 | 5 | // DataFrame represents a private data frame. 6 | // 7 | // Cf. private variables documentation at: http://handlebarsjs.com/block_helpers.html 8 | type DataFrame struct { 9 | parent *DataFrame 10 | data map[string]interface{} 11 | } 12 | 13 | // NewDataFrame instanciates a new private data frame. 14 | func NewDataFrame() *DataFrame { 15 | return &DataFrame{ 16 | data: make(map[string]interface{}), 17 | } 18 | } 19 | 20 | // Copy instanciates a new private data frame with receiver as parent. 21 | func (p *DataFrame) Copy() *DataFrame { 22 | result := NewDataFrame() 23 | 24 | for k, v := range p.data { 25 | result.data[k] = v 26 | } 27 | 28 | result.parent = p 29 | 30 | return result 31 | } 32 | 33 | // newIterDataFrame instanciates a new private data frame with receiver as parent and with iteration data set (@index, @key, @first, @last) 34 | func (p *DataFrame) newIterDataFrame(length int, i int, key interface{}) *DataFrame { 35 | result := p.Copy() 36 | 37 | result.Set("index", i) 38 | result.Set("key", key) 39 | result.Set("first", i == 0) 40 | result.Set("last", i == length-1) 41 | 42 | return result 43 | } 44 | 45 | // Set sets a data value. 46 | func (p *DataFrame) Set(key string, val interface{}) { 47 | p.data[key] = val 48 | } 49 | 50 | // Get gets a data value. 51 | func (p *DataFrame) Get(key string) interface{} { 52 | return p.find([]string{key}) 53 | } 54 | 55 | // find gets a deep data value 56 | // 57 | // @todo This is NOT consistent with the way we resolve data in template (cf. `evalDataPathExpression()`) ! FIX THAT ! 58 | func (p *DataFrame) find(parts []string) interface{} { 59 | data := p.data 60 | 61 | for i, part := range parts { 62 | val := data[part] 63 | if val == nil { 64 | return nil 65 | } 66 | 67 | if i == len(parts)-1 { 68 | // found 69 | return val 70 | } 71 | 72 | valValue := reflect.ValueOf(val) 73 | if valValue.Kind() != reflect.Map { 74 | // not found 75 | return nil 76 | } 77 | 78 | // continue 79 | data = mapStringInterface(valValue) 80 | } 81 | 82 | // not found 83 | return nil 84 | } 85 | 86 | // mapStringInterface converts any `map` to `map[string]interface{}` 87 | func mapStringInterface(value reflect.Value) map[string]interface{} { 88 | result := make(map[string]interface{}) 89 | 90 | for _, key := range value.MapKeys() { 91 | result[strValue(key)] = value.MapIndex(key).Interface() 92 | } 93 | 94 | return result 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/escape.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | ) 7 | 8 | // 9 | // That whole file is borrowed from https://github.com/golang/go/tree/master/src/html/escape.go 10 | // 11 | // With changes: 12 | // ' => ' 13 | // " => " 14 | // 15 | // To stay in sync with JS implementation, and make mustache tests pass. 16 | // 17 | 18 | type writer interface { 19 | WriteString(string) (int, error) 20 | } 21 | 22 | const escapedChars = `&'<>"` 23 | 24 | func escape(w writer, s string) error { 25 | i := strings.IndexAny(s, escapedChars) 26 | for i != -1 { 27 | if _, err := w.WriteString(s[:i]); err != nil { 28 | return err 29 | } 30 | var esc string 31 | switch s[i] { 32 | case '&': 33 | esc = "&" 34 | case '\'': 35 | esc = "'" 36 | case '<': 37 | esc = "<" 38 | case '>': 39 | esc = ">" 40 | case '"': 41 | esc = """ 42 | default: 43 | panic("unrecognized escape character") 44 | } 45 | s = s[i+1:] 46 | if _, err := w.WriteString(esc); err != nil { 47 | return err 48 | } 49 | i = strings.IndexAny(s, escapedChars) 50 | } 51 | _, err := w.WriteString(s) 52 | return err 53 | } 54 | 55 | // Escape escapes special HTML characters. 56 | // 57 | // It can be used by helpers that return a SafeString and that need to escape some content by themselves. 58 | func Escape(s string) string { 59 | if strings.IndexAny(s, escapedChars) == -1 { 60 | return s 61 | } 62 | var buf bytes.Buffer 63 | escape(&buf, s) 64 | return buf.String() 65 | } 66 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/helper.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "reflect" 7 | "sync" 8 | ) 9 | 10 | // Options represents the options argument provided to helpers and context functions. 11 | type Options struct { 12 | // evaluation visitor 13 | eval *evalVisitor 14 | 15 | // params 16 | params []interface{} 17 | hash map[string]interface{} 18 | } 19 | 20 | // helpers stores all globally registered helpers 21 | var helpers = make(map[string]reflect.Value) 22 | 23 | // protects global helpers 24 | var helpersMutex sync.RWMutex 25 | 26 | func init() { 27 | // register builtin helpers 28 | RegisterHelper("if", ifHelper) 29 | RegisterHelper("unless", unlessHelper) 30 | RegisterHelper("with", withHelper) 31 | RegisterHelper("each", eachHelper) 32 | RegisterHelper("log", logHelper) 33 | RegisterHelper("lookup", lookupHelper) 34 | RegisterHelper("equal", equalHelper) 35 | } 36 | 37 | // RegisterHelper registers a global helper. That helper will be available to all templates. 38 | func RegisterHelper(name string, helper interface{}) { 39 | helpersMutex.Lock() 40 | defer helpersMutex.Unlock() 41 | 42 | if helpers[name] != zero { 43 | panic(fmt.Errorf("Helper already registered: %s", name)) 44 | } 45 | 46 | val := reflect.ValueOf(helper) 47 | ensureValidHelper(name, val) 48 | 49 | helpers[name] = val 50 | } 51 | 52 | // RegisterHelpers registers several global helpers. Those helpers will be available to all templates. 53 | func RegisterHelpers(helpers map[string]interface{}) { 54 | for name, helper := range helpers { 55 | RegisterHelper(name, helper) 56 | } 57 | } 58 | 59 | // ensureValidHelper panics if given helper is not valid 60 | func ensureValidHelper(name string, funcValue reflect.Value) { 61 | if funcValue.Kind() != reflect.Func { 62 | panic(fmt.Errorf("Helper must be a function: %s", name)) 63 | } 64 | 65 | funcType := funcValue.Type() 66 | 67 | if funcType.NumOut() != 1 { 68 | panic(fmt.Errorf("Helper function must return a string or a SafeString: %s", name)) 69 | } 70 | 71 | // @todo Check if first returned value is a string, SafeString or interface{} ? 72 | } 73 | 74 | // findHelper finds a globally registered helper 75 | func findHelper(name string) reflect.Value { 76 | helpersMutex.RLock() 77 | defer helpersMutex.RUnlock() 78 | 79 | return helpers[name] 80 | } 81 | 82 | // newOptions instanciates a new Options 83 | func newOptions(eval *evalVisitor, params []interface{}, hash map[string]interface{}) *Options { 84 | return &Options{ 85 | eval: eval, 86 | params: params, 87 | hash: hash, 88 | } 89 | } 90 | 91 | // newEmptyOptions instanciates a new empty Options 92 | func newEmptyOptions(eval *evalVisitor) *Options { 93 | return &Options{ 94 | eval: eval, 95 | hash: make(map[string]interface{}), 96 | } 97 | } 98 | 99 | // 100 | // Context Values 101 | // 102 | 103 | // Value returns field value from current context. 104 | func (options *Options) Value(name string) interface{} { 105 | value := options.eval.evalField(options.eval.curCtx(), name, false) 106 | if !value.IsValid() { 107 | return nil 108 | } 109 | 110 | return value.Interface() 111 | } 112 | 113 | // ValueStr returns string representation of field value from current context. 114 | func (options *Options) ValueStr(name string) string { 115 | return Str(options.Value(name)) 116 | } 117 | 118 | // Ctx returns current evaluation context. 119 | func (options *Options) Ctx() interface{} { 120 | return options.eval.curCtx().Interface() 121 | } 122 | 123 | // 124 | // Hash Arguments 125 | // 126 | 127 | // HashProp returns hash property. 128 | func (options *Options) HashProp(name string) interface{} { 129 | return options.hash[name] 130 | } 131 | 132 | // HashStr returns string representation of hash property. 133 | func (options *Options) HashStr(name string) string { 134 | return Str(options.hash[name]) 135 | } 136 | 137 | // Hash returns entire hash. 138 | func (options *Options) Hash() map[string]interface{} { 139 | return options.hash 140 | } 141 | 142 | // 143 | // Parameters 144 | // 145 | 146 | // Param returns parameter at given position. 147 | func (options *Options) Param(pos int) interface{} { 148 | if len(options.params) > pos { 149 | return options.params[pos] 150 | } 151 | 152 | return nil 153 | } 154 | 155 | // ParamStr returns string representation of parameter at given position. 156 | func (options *Options) ParamStr(pos int) string { 157 | return Str(options.Param(pos)) 158 | } 159 | 160 | // Params returns all parameters. 161 | func (options *Options) Params() []interface{} { 162 | return options.params 163 | } 164 | 165 | // 166 | // Private data 167 | // 168 | 169 | // Data returns private data value. 170 | func (options *Options) Data(name string) interface{} { 171 | return options.eval.dataFrame.Get(name) 172 | } 173 | 174 | // DataStr returns string representation of private data value. 175 | func (options *Options) DataStr(name string) string { 176 | return Str(options.eval.dataFrame.Get(name)) 177 | } 178 | 179 | // DataFrame returns current private data frame. 180 | func (options *Options) DataFrame() *DataFrame { 181 | return options.eval.dataFrame 182 | } 183 | 184 | // NewDataFrame instanciates a new data frame that is a copy of current evaluation data frame. 185 | // 186 | // Parent of returned data frame is set to current evaluation data frame. 187 | func (options *Options) NewDataFrame() *DataFrame { 188 | return options.eval.dataFrame.Copy() 189 | } 190 | 191 | // newIterDataFrame instanciates a new data frame and set iteration specific vars 192 | func (options *Options) newIterDataFrame(length int, i int, key interface{}) *DataFrame { 193 | return options.eval.dataFrame.newIterDataFrame(length, i, key) 194 | } 195 | 196 | // 197 | // Evaluation 198 | // 199 | 200 | // evalBlock evaluates block with given context, private data and iteration key 201 | func (options *Options) evalBlock(ctx interface{}, data *DataFrame, key interface{}) string { 202 | result := "" 203 | 204 | if block := options.eval.curBlock(); (block != nil) && (block.Program != nil) { 205 | result = options.eval.evalProgram(block.Program, ctx, data, key) 206 | } 207 | 208 | return result 209 | } 210 | 211 | // Fn evaluates block with current evaluation context. 212 | func (options *Options) Fn() string { 213 | return options.evalBlock(nil, nil, nil) 214 | } 215 | 216 | // FnCtxData evaluates block with given context and private data frame. 217 | func (options *Options) FnCtxData(ctx interface{}, data *DataFrame) string { 218 | return options.evalBlock(ctx, data, nil) 219 | } 220 | 221 | // FnWith evaluates block with given context. 222 | func (options *Options) FnWith(ctx interface{}) string { 223 | return options.evalBlock(ctx, nil, nil) 224 | } 225 | 226 | // FnData evaluates block with given private data frame. 227 | func (options *Options) FnData(data *DataFrame) string { 228 | return options.evalBlock(nil, data, nil) 229 | } 230 | 231 | // Inverse evaluates "else block". 232 | func (options *Options) Inverse() string { 233 | result := "" 234 | if block := options.eval.curBlock(); (block != nil) && (block.Inverse != nil) { 235 | result, _ = block.Inverse.Accept(options.eval).(string) 236 | } 237 | 238 | return result 239 | } 240 | 241 | // Eval evaluates field for given context. 242 | func (options *Options) Eval(ctx interface{}, field string) interface{} { 243 | if ctx == nil { 244 | return nil 245 | } 246 | 247 | if field == "" { 248 | return nil 249 | } 250 | 251 | val := options.eval.evalField(reflect.ValueOf(ctx), field, false) 252 | if !val.IsValid() { 253 | return nil 254 | } 255 | 256 | return val.Interface() 257 | } 258 | 259 | // 260 | // Misc 261 | // 262 | 263 | // isIncludableZero returns true if 'includeZero' option is set and first param is the number 0 264 | func (options *Options) isIncludableZero() bool { 265 | b, ok := options.HashProp("includeZero").(bool) 266 | if ok && b { 267 | nb, ok := options.Param(0).(int) 268 | if ok && nb == 0 { 269 | return true 270 | } 271 | } 272 | 273 | return false 274 | } 275 | 276 | // 277 | // Builtin helpers 278 | // 279 | 280 | // #if block helper 281 | func ifHelper(conditional interface{}, options *Options) interface{} { 282 | if options.isIncludableZero() || IsTrue(conditional) { 283 | return options.Fn() 284 | } 285 | 286 | return options.Inverse() 287 | } 288 | 289 | // #unless block helper 290 | func unlessHelper(conditional interface{}, options *Options) interface{} { 291 | if options.isIncludableZero() || IsTrue(conditional) { 292 | return options.Inverse() 293 | } 294 | 295 | return options.Fn() 296 | } 297 | 298 | // #with block helper 299 | func withHelper(context interface{}, options *Options) interface{} { 300 | if IsTrue(context) { 301 | return options.FnWith(context) 302 | } 303 | 304 | return options.Inverse() 305 | } 306 | 307 | // #each block helper 308 | func eachHelper(context interface{}, options *Options) interface{} { 309 | if !IsTrue(context) { 310 | return options.Inverse() 311 | } 312 | 313 | result := "" 314 | 315 | val := reflect.ValueOf(context) 316 | switch val.Kind() { 317 | case reflect.Array, reflect.Slice: 318 | for i := 0; i < val.Len(); i++ { 319 | // computes private data 320 | data := options.newIterDataFrame(val.Len(), i, nil) 321 | 322 | // evaluates block 323 | result += options.evalBlock(val.Index(i).Interface(), data, i) 324 | } 325 | case reflect.Map: 326 | // note: a go hash is not ordered, so result may vary, this behaviour differs from the JS implementation 327 | keys := val.MapKeys() 328 | for i := 0; i < len(keys); i++ { 329 | key := keys[i].Interface() 330 | ctx := val.MapIndex(keys[i]).Interface() 331 | 332 | // computes private data 333 | data := options.newIterDataFrame(len(keys), i, key) 334 | 335 | // evaluates block 336 | result += options.evalBlock(ctx, data, key) 337 | } 338 | case reflect.Struct: 339 | var exportedFields []int 340 | 341 | // collect exported fields only 342 | for i := 0; i < val.NumField(); i++ { 343 | if tField := val.Type().Field(i); tField.PkgPath == "" { 344 | exportedFields = append(exportedFields, i) 345 | } 346 | } 347 | 348 | for i, fieldIndex := range exportedFields { 349 | key := val.Type().Field(fieldIndex).Name 350 | ctx := val.Field(fieldIndex).Interface() 351 | 352 | // computes private data 353 | data := options.newIterDataFrame(len(exportedFields), i, key) 354 | 355 | // evaluates block 356 | result += options.evalBlock(ctx, data, key) 357 | } 358 | } 359 | 360 | return result 361 | } 362 | 363 | // #log helper 364 | func logHelper(message string) interface{} { 365 | log.Print(message) 366 | return "" 367 | } 368 | 369 | // #lookup helper 370 | func lookupHelper(obj interface{}, field string, options *Options) interface{} { 371 | return Str(options.Eval(obj, field)) 372 | } 373 | 374 | // #equal helper 375 | // Ref: https://github.com/aymerick/raymond/issues/7 376 | func equalHelper(a interface{}, b interface{}, options *Options) interface{} { 377 | if Str(a) == Str(b) { 378 | return options.Fn() 379 | } 380 | 381 | return "" 382 | } 383 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/lexer/lexer.go: -------------------------------------------------------------------------------- 1 | // Package lexer provides a handlebars tokenizer. 2 | package lexer 3 | 4 | import ( 5 | "fmt" 6 | "regexp" 7 | "strings" 8 | "unicode" 9 | "unicode/utf8" 10 | ) 11 | 12 | // References: 13 | // - https://github.com/wycats/handlebars.js/blob/master/src/handlebars.l 14 | // - https://github.com/golang/go/blob/master/src/text/template/parse/lex.go 15 | 16 | const ( 17 | // Mustaches detection 18 | escapedEscapedOpenMustache = "\\\\{{" 19 | escapedOpenMustache = "\\{{" 20 | openMustache = "{{" 21 | closeMustache = "}}" 22 | closeStripMustache = "~}}" 23 | closeUnescapedStripMustache = "}~}}" 24 | ) 25 | 26 | const eof = -1 27 | 28 | // lexFunc represents a function that returns the next lexer function. 29 | type lexFunc func(*Lexer) lexFunc 30 | 31 | // Lexer is a lexical analyzer. 32 | type Lexer struct { 33 | input string // input to scan 34 | name string // lexer name, used for testing purpose 35 | tokens chan Token // channel of scanned tokens 36 | nextFunc lexFunc // the next function to execute 37 | 38 | pos int // current byte position in input string 39 | line int // current line position in input string 40 | width int // size of last rune scanned from input string 41 | start int // start position of the token we are scanning 42 | 43 | // the shameful contextual properties needed because `nextFunc` is not enough 44 | closeComment *regexp.Regexp // regexp to scan close of current comment 45 | rawBlock bool // are we parsing a raw block content ? 46 | } 47 | 48 | var ( 49 | lookheadChars = `[\s` + regexp.QuoteMeta("=~}/)|") + `]` 50 | literalLookheadChars = `[\s` + regexp.QuoteMeta("~})") + `]` 51 | 52 | // characters not allowed in an identifier 53 | unallowedIDChars = " \n\t!\"#%&'()*+,./;<=>@[\\]^`{|}~" 54 | 55 | // regular expressions 56 | rID = regexp.MustCompile(`^[^` + regexp.QuoteMeta(unallowedIDChars) + `]+`) 57 | rDotID = regexp.MustCompile(`^\.` + lookheadChars) 58 | rTrue = regexp.MustCompile(`^true` + literalLookheadChars) 59 | rFalse = regexp.MustCompile(`^false` + literalLookheadChars) 60 | rOpenRaw = regexp.MustCompile(`^\{\{\{\{`) 61 | rCloseRaw = regexp.MustCompile(`^\}\}\}\}`) 62 | rOpenEndRaw = regexp.MustCompile(`^\{\{\{\{/`) 63 | rOpenEndRawLookAhead = regexp.MustCompile(`\{\{\{\{/`) 64 | rOpenUnescaped = regexp.MustCompile(`^\{\{~?\{`) 65 | rCloseUnescaped = regexp.MustCompile(`^\}~?\}\}`) 66 | rOpenBlock = regexp.MustCompile(`^\{\{~?#`) 67 | rOpenEndBlock = regexp.MustCompile(`^\{\{~?/`) 68 | rOpenPartial = regexp.MustCompile(`^\{\{~?>`) 69 | // {{^}} or {{else}} 70 | rInverse = regexp.MustCompile(`^(\{\{~?\^\s*~?\}\}|\{\{~?\s*else\s*~?\}\})`) 71 | rOpenInverse = regexp.MustCompile(`^\{\{~?\^`) 72 | rOpenInverseChain = regexp.MustCompile(`^\{\{~?\s*else`) 73 | // {{ or {{& 74 | rOpen = regexp.MustCompile(`^\{\{~?&?`) 75 | rClose = regexp.MustCompile(`^~?\}\}`) 76 | rOpenBlockParams = regexp.MustCompile(`^as\s+\|`) 77 | // {{!-- ... --}} 78 | rOpenCommentDash = regexp.MustCompile(`^\{\{~?!--\s*`) 79 | rCloseCommentDash = regexp.MustCompile(`^\s*--~?\}\}`) 80 | // {{! ... }} 81 | rOpenComment = regexp.MustCompile(`^\{\{~?!\s*`) 82 | rCloseComment = regexp.MustCompile(`^\s*~?\}\}`) 83 | ) 84 | 85 | // Scan scans given input. 86 | // 87 | // Tokens can then be fetched sequentially thanks to NextToken() function on returned lexer. 88 | func Scan(input string) *Lexer { 89 | return scanWithName(input, "") 90 | } 91 | 92 | // scanWithName scans given input, with a name used for testing 93 | // 94 | // Tokens can then be fetched sequentially thanks to NextToken() function on returned lexer. 95 | func scanWithName(input string, name string) *Lexer { 96 | result := &Lexer{ 97 | input: input, 98 | name: name, 99 | tokens: make(chan Token), 100 | line: 1, 101 | } 102 | 103 | go result.run() 104 | 105 | return result 106 | } 107 | 108 | // Collect scans and collect all tokens. 109 | // 110 | // This should be used for debugging purpose only. You should use Scan() and lexer.NextToken() functions instead. 111 | func Collect(input string) []Token { 112 | var result []Token 113 | 114 | l := Scan(input) 115 | for { 116 | token := l.NextToken() 117 | result = append(result, token) 118 | 119 | if token.Kind == TokenEOF || token.Kind == TokenError { 120 | break 121 | } 122 | } 123 | 124 | return result 125 | } 126 | 127 | // NextToken returns the next scanned token. 128 | func (l *Lexer) NextToken() Token { 129 | result := <-l.tokens 130 | 131 | return result 132 | } 133 | 134 | // run starts lexical analysis 135 | func (l *Lexer) run() { 136 | for l.nextFunc = lexContent; l.nextFunc != nil; { 137 | l.nextFunc = l.nextFunc(l) 138 | } 139 | } 140 | 141 | // next returns next character from input, or eof of there is nothing left to scan 142 | func (l *Lexer) next() rune { 143 | if l.pos >= len(l.input) { 144 | l.width = 0 145 | return eof 146 | } 147 | 148 | r, w := utf8.DecodeRuneInString(l.input[l.pos:]) 149 | l.width = w 150 | l.pos += l.width 151 | 152 | return r 153 | } 154 | 155 | func (l *Lexer) produce(kind TokenKind, val string) { 156 | l.tokens <- Token{kind, val, l.start, l.line} 157 | 158 | // scanning a new token 159 | l.start = l.pos 160 | 161 | // update line number 162 | l.line += strings.Count(val, "\n") 163 | } 164 | 165 | // emit emits a new scanned token 166 | func (l *Lexer) emit(kind TokenKind) { 167 | l.produce(kind, l.input[l.start:l.pos]) 168 | } 169 | 170 | // emitContent emits scanned content 171 | func (l *Lexer) emitContent() { 172 | if l.pos > l.start { 173 | l.emit(TokenContent) 174 | } 175 | } 176 | 177 | // emitString emits a scanned string 178 | func (l *Lexer) emitString(delimiter rune) { 179 | str := l.input[l.start:l.pos] 180 | 181 | // replace escaped delimiters 182 | str = strings.Replace(str, "\\"+string(delimiter), string(delimiter), -1) 183 | 184 | l.produce(TokenString, str) 185 | } 186 | 187 | // peek returns but does not consume the next character in the input 188 | func (l *Lexer) peek() rune { 189 | r := l.next() 190 | l.backup() 191 | return r 192 | } 193 | 194 | // backup steps back one character 195 | // 196 | // WARNING: Can only be called once per call of next 197 | func (l *Lexer) backup() { 198 | l.pos -= l.width 199 | } 200 | 201 | // ignoreskips all characters that have been scanned up to current position 202 | func (l *Lexer) ignore() { 203 | l.start = l.pos 204 | } 205 | 206 | // accept scans the next character if it is included in given string 207 | func (l *Lexer) accept(valid string) bool { 208 | if strings.IndexRune(valid, l.next()) >= 0 { 209 | return true 210 | } 211 | 212 | l.backup() 213 | 214 | return false 215 | } 216 | 217 | // acceptRun scans all following characters that are part of given string 218 | func (l *Lexer) acceptRun(valid string) { 219 | for strings.IndexRune(valid, l.next()) >= 0 { 220 | } 221 | 222 | l.backup() 223 | } 224 | 225 | // errorf emits an error token 226 | func (l *Lexer) errorf(format string, args ...interface{}) lexFunc { 227 | l.tokens <- Token{TokenError, fmt.Sprintf(format, args...), l.start, l.line} 228 | return nil 229 | } 230 | 231 | // isString returns true if content at current scanning position starts with given string 232 | func (l *Lexer) isString(str string) bool { 233 | return strings.HasPrefix(l.input[l.pos:], str) 234 | } 235 | 236 | // findRegexp returns the first string from current scanning position that matches given regular expression 237 | func (l *Lexer) findRegexp(r *regexp.Regexp) string { 238 | return r.FindString(l.input[l.pos:]) 239 | } 240 | 241 | // indexRegexp returns the index of the first string from current scanning position that matches given regular expression 242 | // 243 | // It returns -1 if not found 244 | func (l *Lexer) indexRegexp(r *regexp.Regexp) int { 245 | loc := r.FindStringIndex(l.input[l.pos:]) 246 | if loc == nil { 247 | return -1 248 | } 249 | return loc[0] 250 | } 251 | 252 | // lexContent scans content (ie: not between mustaches) 253 | func lexContent(l *Lexer) lexFunc { 254 | var next lexFunc 255 | 256 | if l.rawBlock { 257 | if i := l.indexRegexp(rOpenEndRawLookAhead); i != -1 { 258 | // {{{{/ 259 | l.rawBlock = false 260 | l.pos += i 261 | 262 | next = lexOpenMustache 263 | } else { 264 | return l.errorf("Unclosed raw block") 265 | } 266 | } else if l.isString(escapedEscapedOpenMustache) { 267 | // \\{{ 268 | 269 | // emit content with only one escaped escape 270 | l.next() 271 | l.emitContent() 272 | 273 | // ignore second escaped escape 274 | l.next() 275 | l.ignore() 276 | 277 | next = lexContent 278 | } else if l.isString(escapedOpenMustache) { 279 | // \{{ 280 | next = lexEscapedOpenMustache 281 | } else if str := l.findRegexp(rOpenCommentDash); str != "" { 282 | // {{!-- 283 | l.closeComment = rCloseCommentDash 284 | 285 | next = lexComment 286 | } else if str := l.findRegexp(rOpenComment); str != "" { 287 | // {{! 288 | l.closeComment = rCloseComment 289 | 290 | next = lexComment 291 | } else if l.isString(openMustache) { 292 | // {{ 293 | next = lexOpenMustache 294 | } 295 | 296 | if next != nil { 297 | // emit scanned content 298 | l.emitContent() 299 | 300 | // scan next token 301 | return next 302 | } 303 | 304 | // scan next rune 305 | if l.next() == eof { 306 | // emit scanned content 307 | l.emitContent() 308 | 309 | // this is over 310 | l.emit(TokenEOF) 311 | return nil 312 | } 313 | 314 | // continue content scanning 315 | return lexContent 316 | } 317 | 318 | // lexEscapedOpenMustache scans \{{ 319 | func lexEscapedOpenMustache(l *Lexer) lexFunc { 320 | // ignore escape character 321 | l.next() 322 | l.ignore() 323 | 324 | // scan mustaches 325 | for l.peek() == '{' { 326 | l.next() 327 | } 328 | 329 | return lexContent 330 | } 331 | 332 | // lexOpenMustache scans {{ 333 | func lexOpenMustache(l *Lexer) lexFunc { 334 | var str string 335 | var tok TokenKind 336 | 337 | nextFunc := lexExpression 338 | 339 | if str = l.findRegexp(rOpenEndRaw); str != "" { 340 | tok = TokenOpenEndRawBlock 341 | } else if str = l.findRegexp(rOpenRaw); str != "" { 342 | tok = TokenOpenRawBlock 343 | l.rawBlock = true 344 | } else if str = l.findRegexp(rOpenUnescaped); str != "" { 345 | tok = TokenOpenUnescaped 346 | } else if str = l.findRegexp(rOpenBlock); str != "" { 347 | tok = TokenOpenBlock 348 | } else if str = l.findRegexp(rOpenEndBlock); str != "" { 349 | tok = TokenOpenEndBlock 350 | } else if str = l.findRegexp(rOpenPartial); str != "" { 351 | tok = TokenOpenPartial 352 | } else if str = l.findRegexp(rInverse); str != "" { 353 | tok = TokenInverse 354 | nextFunc = lexContent 355 | } else if str = l.findRegexp(rOpenInverse); str != "" { 356 | tok = TokenOpenInverse 357 | } else if str = l.findRegexp(rOpenInverseChain); str != "" { 358 | tok = TokenOpenInverseChain 359 | } else if str = l.findRegexp(rOpen); str != "" { 360 | tok = TokenOpen 361 | } else { 362 | // this is rotten 363 | panic("Current pos MUST be an opening mustache") 364 | } 365 | 366 | l.pos += len(str) 367 | l.emit(tok) 368 | 369 | return nextFunc 370 | } 371 | 372 | // lexCloseMustache scans }} or ~}} 373 | func lexCloseMustache(l *Lexer) lexFunc { 374 | var str string 375 | var tok TokenKind 376 | 377 | if str = l.findRegexp(rCloseRaw); str != "" { 378 | // }}}} 379 | tok = TokenCloseRawBlock 380 | } else if str = l.findRegexp(rCloseUnescaped); str != "" { 381 | // }}} 382 | tok = TokenCloseUnescaped 383 | } else if str = l.findRegexp(rClose); str != "" { 384 | // }} 385 | tok = TokenClose 386 | } else { 387 | // this is rotten 388 | panic("Current pos MUST be a closing mustache") 389 | } 390 | 391 | l.pos += len(str) 392 | l.emit(tok) 393 | 394 | return lexContent 395 | } 396 | 397 | // lexExpression scans inside mustaches 398 | func lexExpression(l *Lexer) lexFunc { 399 | // search close mustache delimiter 400 | if l.isString(closeMustache) || l.isString(closeStripMustache) || l.isString(closeUnescapedStripMustache) { 401 | return lexCloseMustache 402 | } 403 | 404 | // search some patterns before advancing scanning position 405 | 406 | // "as |" 407 | if str := l.findRegexp(rOpenBlockParams); str != "" { 408 | l.pos += len(str) 409 | l.emit(TokenOpenBlockParams) 410 | return lexExpression 411 | } 412 | 413 | // .. 414 | if l.isString("..") { 415 | l.pos += len("..") 416 | l.emit(TokenID) 417 | return lexExpression 418 | } 419 | 420 | // . 421 | if str := l.findRegexp(rDotID); str != "" { 422 | l.pos += len(".") 423 | l.emit(TokenID) 424 | return lexExpression 425 | } 426 | 427 | // true 428 | if str := l.findRegexp(rTrue); str != "" { 429 | l.pos += len("true") 430 | l.emit(TokenBoolean) 431 | return lexExpression 432 | } 433 | 434 | // false 435 | if str := l.findRegexp(rFalse); str != "" { 436 | l.pos += len("false") 437 | l.emit(TokenBoolean) 438 | return lexExpression 439 | } 440 | 441 | // let's scan next character 442 | switch r := l.next(); { 443 | case r == eof: 444 | return l.errorf("Unclosed expression") 445 | case isIgnorable(r): 446 | return lexIgnorable 447 | case r == '(': 448 | l.emit(TokenOpenSexpr) 449 | case r == ')': 450 | l.emit(TokenCloseSexpr) 451 | case r == '=': 452 | l.emit(TokenEquals) 453 | case r == '@': 454 | l.emit(TokenData) 455 | case r == '"' || r == '\'': 456 | l.backup() 457 | return lexString 458 | case r == '/' || r == '.': 459 | l.emit(TokenSep) 460 | case r == '|': 461 | l.emit(TokenCloseBlockParams) 462 | case r == '+' || r == '-' || (r >= '0' && r <= '9'): 463 | l.backup() 464 | return lexNumber 465 | case r == '[': 466 | return lexPathLiteral 467 | case strings.IndexRune(unallowedIDChars, r) < 0: 468 | l.backup() 469 | return lexIdentifier 470 | default: 471 | return l.errorf("Unexpected character in expression: '%c'", r) 472 | } 473 | 474 | return lexExpression 475 | } 476 | 477 | // lexComment scans {{!-- or {{! 478 | func lexComment(l *Lexer) lexFunc { 479 | if str := l.findRegexp(l.closeComment); str != "" { 480 | l.pos += len(str) 481 | l.emit(TokenComment) 482 | 483 | return lexContent 484 | } 485 | 486 | if r := l.next(); r == eof { 487 | return l.errorf("Unclosed comment") 488 | } 489 | 490 | return lexComment 491 | } 492 | 493 | // lexIgnorable scans all following ignorable characters 494 | func lexIgnorable(l *Lexer) lexFunc { 495 | for isIgnorable(l.peek()) { 496 | l.next() 497 | } 498 | l.ignore() 499 | 500 | return lexExpression 501 | } 502 | 503 | // lexString scans a string 504 | func lexString(l *Lexer) lexFunc { 505 | // get string delimiter 506 | delim := l.next() 507 | var prev rune 508 | 509 | // ignore delimiter 510 | l.ignore() 511 | 512 | for { 513 | r := l.next() 514 | if r == eof || r == '\n' { 515 | return l.errorf("Unterminated string") 516 | } 517 | 518 | if (r == delim) && (prev != '\\') { 519 | break 520 | } 521 | 522 | prev = r 523 | } 524 | 525 | // remove end delimiter 526 | l.backup() 527 | 528 | // emit string 529 | l.emitString(delim) 530 | 531 | // skip end delimiter 532 | l.next() 533 | l.ignore() 534 | 535 | return lexExpression 536 | } 537 | 538 | // lexNumber scans a number: decimal, octal, hex, float, or imaginary. This 539 | // isn't a perfect number scanner - for instance it accepts "." and "0x0.2" 540 | // and "089" - but when it's wrong the input is invalid and the parser (via 541 | // strconv) will notice. 542 | // 543 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/parse/lex.go 544 | func lexNumber(l *Lexer) lexFunc { 545 | if !l.scanNumber() { 546 | return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) 547 | } 548 | if sign := l.peek(); sign == '+' || sign == '-' { 549 | // Complex: 1+2i. No spaces, must end in 'i'. 550 | if !l.scanNumber() || l.input[l.pos-1] != 'i' { 551 | return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) 552 | } 553 | l.emit(TokenNumber) 554 | } else { 555 | l.emit(TokenNumber) 556 | } 557 | return lexExpression 558 | } 559 | 560 | // scanNumber scans a number 561 | // 562 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/parse/lex.go 563 | func (l *Lexer) scanNumber() bool { 564 | // Optional leading sign. 565 | l.accept("+-") 566 | 567 | // Is it hex? 568 | digits := "0123456789" 569 | 570 | if l.accept("0") && l.accept("xX") { 571 | digits = "0123456789abcdefABCDEF" 572 | } 573 | 574 | l.acceptRun(digits) 575 | 576 | if l.accept(".") { 577 | l.acceptRun(digits) 578 | } 579 | 580 | if l.accept("eE") { 581 | l.accept("+-") 582 | l.acceptRun("0123456789") 583 | } 584 | 585 | // Is it imaginary? 586 | l.accept("i") 587 | 588 | // Next thing mustn't be alphanumeric. 589 | if isAlphaNumeric(l.peek()) { 590 | l.next() 591 | return false 592 | } 593 | 594 | return true 595 | } 596 | 597 | // lexIdentifier scans an ID 598 | func lexIdentifier(l *Lexer) lexFunc { 599 | str := l.findRegexp(rID) 600 | if len(str) == 0 { 601 | // this is rotten 602 | panic("Identifier expected") 603 | } 604 | 605 | l.pos += len(str) 606 | l.emit(TokenID) 607 | 608 | return lexExpression 609 | } 610 | 611 | // lexPathLiteral scans an [ID] 612 | func lexPathLiteral(l *Lexer) lexFunc { 613 | for { 614 | r := l.next() 615 | if r == eof || r == '\n' { 616 | return l.errorf("Unterminated path literal") 617 | } 618 | 619 | if r == ']' { 620 | break 621 | } 622 | } 623 | 624 | l.emit(TokenID) 625 | 626 | return lexExpression 627 | } 628 | 629 | // isIgnorable returns true if given character is ignorable (ie. whitespace of line feed) 630 | func isIgnorable(r rune) bool { 631 | return r == ' ' || r == '\t' || r == '\n' 632 | } 633 | 634 | // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. 635 | // 636 | // NOTE borrowed from https://github.com/golang/go/tree/master/src/text/template/parse/lex.go 637 | func isAlphaNumeric(r rune) bool { 638 | return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) 639 | } 640 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/lexer/token.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import "fmt" 4 | 5 | const ( 6 | // TokenError represents an error 7 | TokenError TokenKind = iota 8 | 9 | // TokenEOF represents an End Of File 10 | TokenEOF 11 | 12 | // 13 | // Mustache delimiters 14 | // 15 | 16 | // TokenOpen is the OPEN token 17 | TokenOpen 18 | 19 | // TokenClose is the CLOSE token 20 | TokenClose 21 | 22 | // TokenOpenRawBlock is the OPEN_RAW_BLOCK token 23 | TokenOpenRawBlock 24 | 25 | // TokenCloseRawBlock is the CLOSE_RAW_BLOCK token 26 | TokenCloseRawBlock 27 | 28 | // TokenOpenEndRawBlock is the END_RAW_BLOCK token 29 | TokenOpenEndRawBlock 30 | 31 | // TokenOpenUnescaped is the OPEN_UNESCAPED token 32 | TokenOpenUnescaped 33 | 34 | // TokenCloseUnescaped is the CLOSE_UNESCAPED token 35 | TokenCloseUnescaped 36 | 37 | // TokenOpenBlock is the OPEN_BLOCK token 38 | TokenOpenBlock 39 | 40 | // TokenOpenEndBlock is the OPEN_ENDBLOCK token 41 | TokenOpenEndBlock 42 | 43 | // TokenInverse is the INVERSE token 44 | TokenInverse 45 | 46 | // TokenOpenInverse is the OPEN_INVERSE token 47 | TokenOpenInverse 48 | 49 | // TokenOpenInverseChain is the OPEN_INVERSE_CHAIN token 50 | TokenOpenInverseChain 51 | 52 | // TokenOpenPartial is the OPEN_PARTIAL token 53 | TokenOpenPartial 54 | 55 | // TokenComment is the COMMENT token 56 | TokenComment 57 | 58 | // 59 | // Inside mustaches 60 | // 61 | 62 | // TokenOpenSexpr is the OPEN_SEXPR token 63 | TokenOpenSexpr 64 | 65 | // TokenCloseSexpr is the CLOSE_SEXPR token 66 | TokenCloseSexpr 67 | 68 | // TokenEquals is the EQUALS token 69 | TokenEquals 70 | 71 | // TokenData is the DATA token 72 | TokenData 73 | 74 | // TokenSep is the SEP token 75 | TokenSep 76 | 77 | // TokenOpenBlockParams is the OPEN_BLOCK_PARAMS token 78 | TokenOpenBlockParams 79 | 80 | // TokenCloseBlockParams is the CLOSE_BLOCK_PARAMS token 81 | TokenCloseBlockParams 82 | 83 | // 84 | // Tokens with content 85 | // 86 | 87 | // TokenContent is the CONTENT token 88 | TokenContent 89 | 90 | // TokenID is the ID token 91 | TokenID 92 | 93 | // TokenString is the STRING token 94 | TokenString 95 | 96 | // TokenNumber is the NUMBER token 97 | TokenNumber 98 | 99 | // TokenBoolean is the BOOLEAN token 100 | TokenBoolean 101 | ) 102 | 103 | const ( 104 | // Option to generate token position in its string representation 105 | dumpTokenPos = false 106 | 107 | // Option to generate values for all token kinds for their string representations 108 | dumpAllTokensVal = true 109 | ) 110 | 111 | // TokenKind represents a Token type. 112 | type TokenKind int 113 | 114 | // Token represents a scanned token. 115 | type Token struct { 116 | Kind TokenKind // Token kind 117 | Val string // Token value 118 | 119 | Pos int // Byte position in input string 120 | Line int // Line number in input string 121 | } 122 | 123 | // tokenName permits to display token name given token type 124 | var tokenName = map[TokenKind]string{ 125 | TokenError: "Error", 126 | TokenEOF: "EOF", 127 | TokenContent: "Content", 128 | TokenComment: "Comment", 129 | TokenOpen: "Open", 130 | TokenClose: "Close", 131 | TokenOpenUnescaped: "OpenUnescaped", 132 | TokenCloseUnescaped: "CloseUnescaped", 133 | TokenOpenBlock: "OpenBlock", 134 | TokenOpenEndBlock: "OpenEndBlock", 135 | TokenOpenRawBlock: "OpenRawBlock", 136 | TokenCloseRawBlock: "CloseRawBlock", 137 | TokenOpenEndRawBlock: "OpenEndRawBlock", 138 | TokenOpenBlockParams: "OpenBlockParams", 139 | TokenCloseBlockParams: "CloseBlockParams", 140 | TokenInverse: "Inverse", 141 | TokenOpenInverse: "OpenInverse", 142 | TokenOpenInverseChain: "OpenInverseChain", 143 | TokenOpenPartial: "OpenPartial", 144 | TokenOpenSexpr: "OpenSexpr", 145 | TokenCloseSexpr: "CloseSexpr", 146 | TokenID: "ID", 147 | TokenEquals: "Equals", 148 | TokenString: "String", 149 | TokenNumber: "Number", 150 | TokenBoolean: "Boolean", 151 | TokenData: "Data", 152 | TokenSep: "Sep", 153 | } 154 | 155 | // String returns the token kind string representation for debugging. 156 | func (k TokenKind) String() string { 157 | s := tokenName[k] 158 | if s == "" { 159 | return fmt.Sprintf("Token-%d", int(k)) 160 | } 161 | return s 162 | } 163 | 164 | // String returns the token string representation for debugging. 165 | func (t Token) String() string { 166 | result := "" 167 | 168 | if dumpTokenPos { 169 | result += fmt.Sprintf("%d:", t.Pos) 170 | } 171 | 172 | result += fmt.Sprintf("%s", t.Kind) 173 | 174 | if (dumpAllTokensVal || (t.Kind >= TokenContent)) && len(t.Val) > 0 { 175 | if len(t.Val) > 100 { 176 | result += fmt.Sprintf("{%.20q...}", t.Val) 177 | } else { 178 | result += fmt.Sprintf("{%q}", t.Val) 179 | } 180 | } 181 | 182 | return result 183 | } 184 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/parser/whitespace.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "regexp" 5 | 6 | "github.com/aymerick/raymond/ast" 7 | ) 8 | 9 | // whitespaceVisitor walks through the AST to perform whitespace control 10 | // 11 | // The logic was shamelessly borrowed from: 12 | // https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/compiler/whitespace-control.js 13 | type whitespaceVisitor struct { 14 | isRootSeen bool 15 | } 16 | 17 | var ( 18 | rTrimLeft = regexp.MustCompile(`^[ \t]*\r?\n?`) 19 | rTrimLeftMultiple = regexp.MustCompile(`^\s+`) 20 | 21 | rTrimRight = regexp.MustCompile(`[ \t]+$`) 22 | rTrimRightMultiple = regexp.MustCompile(`\s+$`) 23 | 24 | rPrevWhitespace = regexp.MustCompile(`\r?\n\s*?$`) 25 | rPrevWhitespaceStart = regexp.MustCompile(`(^|\r?\n)\s*?$`) 26 | 27 | rNextWhitespace = regexp.MustCompile(`^\s*?\r?\n`) 28 | rNextWhitespaceEnd = regexp.MustCompile(`^\s*?(\r?\n|$)`) 29 | 30 | rPartialIndent = regexp.MustCompile(`([ \t]+$)`) 31 | ) 32 | 33 | // newWhitespaceVisitor instanciates a new whitespaceVisitor 34 | func newWhitespaceVisitor() *whitespaceVisitor { 35 | return &whitespaceVisitor{} 36 | } 37 | 38 | // processWhitespaces performs whitespace control on given AST 39 | // 40 | // WARNING: It must be called only once on AST. 41 | func processWhitespaces(node ast.Node) { 42 | node.Accept(newWhitespaceVisitor()) 43 | } 44 | 45 | func omitRightFirst(body []ast.Node, multiple bool) { 46 | omitRight(body, -1, multiple) 47 | } 48 | 49 | func omitRight(body []ast.Node, i int, multiple bool) { 50 | if i+1 >= len(body) { 51 | return 52 | } 53 | 54 | current := body[i+1] 55 | 56 | node, ok := current.(*ast.ContentStatement) 57 | if !ok { 58 | return 59 | } 60 | 61 | if !multiple && node.RightStripped { 62 | return 63 | } 64 | 65 | original := node.Value 66 | 67 | r := rTrimLeft 68 | if multiple { 69 | r = rTrimLeftMultiple 70 | } 71 | 72 | node.Value = r.ReplaceAllString(node.Value, "") 73 | 74 | node.RightStripped = (original != node.Value) 75 | } 76 | 77 | func omitLeftLast(body []ast.Node, multiple bool) { 78 | omitLeft(body, len(body), multiple) 79 | } 80 | 81 | func omitLeft(body []ast.Node, i int, multiple bool) bool { 82 | if i-1 < 0 { 83 | return false 84 | } 85 | 86 | current := body[i-1] 87 | 88 | node, ok := current.(*ast.ContentStatement) 89 | if !ok { 90 | return false 91 | } 92 | 93 | if !multiple && node.LeftStripped { 94 | return false 95 | } 96 | 97 | original := node.Value 98 | 99 | r := rTrimRight 100 | if multiple { 101 | r = rTrimRightMultiple 102 | } 103 | 104 | node.Value = r.ReplaceAllString(node.Value, "") 105 | 106 | node.LeftStripped = (original != node.Value) 107 | 108 | return node.LeftStripped 109 | } 110 | 111 | func isPrevWhitespace(body []ast.Node) bool { 112 | return isPrevWhitespaceProgram(body, len(body), false) 113 | } 114 | 115 | func isPrevWhitespaceProgram(body []ast.Node, i int, isRoot bool) bool { 116 | if i < 1 { 117 | return isRoot 118 | } 119 | 120 | prev := body[i-1] 121 | 122 | if node, ok := prev.(*ast.ContentStatement); ok { 123 | if (node.Value == "") && node.RightStripped { 124 | // already stripped, so it may be an empty string not catched by regexp 125 | return true 126 | } 127 | 128 | r := rPrevWhitespaceStart 129 | if (i > 1) || !isRoot { 130 | r = rPrevWhitespace 131 | } 132 | 133 | return r.MatchString(node.Value) 134 | } 135 | 136 | return false 137 | } 138 | 139 | func isNextWhitespace(body []ast.Node) bool { 140 | return isNextWhitespaceProgram(body, -1, false) 141 | } 142 | 143 | func isNextWhitespaceProgram(body []ast.Node, i int, isRoot bool) bool { 144 | if i+1 >= len(body) { 145 | return isRoot 146 | } 147 | 148 | next := body[i+1] 149 | 150 | if node, ok := next.(*ast.ContentStatement); ok { 151 | if (node.Value == "") && node.LeftStripped { 152 | // already stripped, so it may be an empty string not catched by regexp 153 | return true 154 | } 155 | 156 | r := rNextWhitespaceEnd 157 | if (i+2 > len(body)) || !isRoot { 158 | r = rNextWhitespace 159 | } 160 | 161 | return r.MatchString(node.Value) 162 | } 163 | 164 | return false 165 | } 166 | 167 | // 168 | // Visitor interface 169 | // 170 | 171 | func (v *whitespaceVisitor) VisitProgram(program *ast.Program) interface{} { 172 | isRoot := !v.isRootSeen 173 | v.isRootSeen = true 174 | 175 | body := program.Body 176 | for i, current := range body { 177 | strip, _ := current.Accept(v).(*ast.Strip) 178 | if strip == nil { 179 | continue 180 | } 181 | 182 | _isPrevWhitespace := isPrevWhitespaceProgram(body, i, isRoot) 183 | _isNextWhitespace := isNextWhitespaceProgram(body, i, isRoot) 184 | 185 | openStandalone := strip.OpenStandalone && _isPrevWhitespace 186 | closeStandalone := strip.CloseStandalone && _isNextWhitespace 187 | inlineStandalone := strip.InlineStandalone && _isPrevWhitespace && _isNextWhitespace 188 | 189 | if strip.Close { 190 | omitRight(body, i, true) 191 | } 192 | 193 | if strip.Open && (i > 0) { 194 | omitLeft(body, i, true) 195 | } 196 | 197 | if inlineStandalone { 198 | omitRight(body, i, false) 199 | 200 | if omitLeft(body, i, false) { 201 | // If we are on a standalone node, save the indent info for partials 202 | if partial, ok := current.(*ast.PartialStatement); ok { 203 | // Pull out the whitespace from the final line 204 | if i > 0 { 205 | if prevContent, ok := body[i-1].(*ast.ContentStatement); ok { 206 | partial.Indent = rPartialIndent.FindString(prevContent.Original) 207 | } 208 | } 209 | } 210 | } 211 | } 212 | 213 | if b, ok := current.(*ast.BlockStatement); ok { 214 | if openStandalone { 215 | prog := b.Program 216 | if prog == nil { 217 | prog = b.Inverse 218 | } 219 | 220 | omitRightFirst(prog.Body, false) 221 | 222 | // Strip out the previous content node if it's whitespace only 223 | omitLeft(body, i, false) 224 | } 225 | 226 | if closeStandalone { 227 | prog := b.Inverse 228 | if prog == nil { 229 | prog = b.Program 230 | } 231 | 232 | // Always strip the next node 233 | omitRight(body, i, false) 234 | 235 | omitLeftLast(prog.Body, false) 236 | } 237 | 238 | } 239 | } 240 | 241 | return nil 242 | } 243 | 244 | func (v *whitespaceVisitor) VisitBlock(block *ast.BlockStatement) interface{} { 245 | if block.Program != nil { 246 | block.Program.Accept(v) 247 | } 248 | 249 | if block.Inverse != nil { 250 | block.Inverse.Accept(v) 251 | } 252 | 253 | program := block.Program 254 | inverse := block.Inverse 255 | 256 | if program == nil { 257 | program = inverse 258 | inverse = nil 259 | } 260 | 261 | firstInverse := inverse 262 | lastInverse := inverse 263 | 264 | if (inverse != nil) && inverse.Chained { 265 | b, _ := inverse.Body[0].(*ast.BlockStatement) 266 | firstInverse = b.Program 267 | 268 | for lastInverse.Chained { 269 | b, _ := lastInverse.Body[len(lastInverse.Body)-1].(*ast.BlockStatement) 270 | lastInverse = b.Program 271 | } 272 | } 273 | 274 | closeProg := firstInverse 275 | if closeProg == nil { 276 | closeProg = program 277 | } 278 | 279 | strip := &ast.Strip{ 280 | Open: (block.OpenStrip != nil) && block.OpenStrip.Open, 281 | Close: (block.CloseStrip != nil) && block.CloseStrip.Close, 282 | 283 | OpenStandalone: isNextWhitespace(program.Body), 284 | CloseStandalone: isPrevWhitespace(closeProg.Body), 285 | } 286 | 287 | if (block.OpenStrip != nil) && block.OpenStrip.Close { 288 | omitRightFirst(program.Body, true) 289 | } 290 | 291 | if inverse != nil { 292 | if block.InverseStrip != nil { 293 | inverseStrip := block.InverseStrip 294 | 295 | if inverseStrip.Open { 296 | omitLeftLast(program.Body, true) 297 | } 298 | 299 | if inverseStrip.Close { 300 | omitRightFirst(firstInverse.Body, true) 301 | } 302 | } 303 | 304 | if (block.CloseStrip != nil) && block.CloseStrip.Open { 305 | omitLeftLast(lastInverse.Body, true) 306 | } 307 | 308 | // Find standalone else statements 309 | if isPrevWhitespace(program.Body) && isNextWhitespace(firstInverse.Body) { 310 | omitLeftLast(program.Body, false) 311 | 312 | omitRightFirst(firstInverse.Body, false) 313 | } 314 | } else if (block.CloseStrip != nil) && block.CloseStrip.Open { 315 | omitLeftLast(program.Body, true) 316 | } 317 | 318 | return strip 319 | } 320 | 321 | func (v *whitespaceVisitor) VisitMustache(mustache *ast.MustacheStatement) interface{} { 322 | return mustache.Strip 323 | } 324 | 325 | func _inlineStandalone(strip *ast.Strip) interface{} { 326 | return &ast.Strip{ 327 | Open: strip.Open, 328 | Close: strip.Close, 329 | InlineStandalone: true, 330 | } 331 | } 332 | 333 | func (v *whitespaceVisitor) VisitPartial(node *ast.PartialStatement) interface{} { 334 | strip := node.Strip 335 | if strip == nil { 336 | strip = &ast.Strip{} 337 | } 338 | 339 | return _inlineStandalone(strip) 340 | } 341 | 342 | func (v *whitespaceVisitor) VisitComment(node *ast.CommentStatement) interface{} { 343 | strip := node.Strip 344 | if strip == nil { 345 | strip = &ast.Strip{} 346 | } 347 | 348 | return _inlineStandalone(strip) 349 | } 350 | 351 | // NOOP 352 | func (v *whitespaceVisitor) VisitContent(node *ast.ContentStatement) interface{} { return nil } 353 | func (v *whitespaceVisitor) VisitExpression(node *ast.Expression) interface{} { return nil } 354 | func (v *whitespaceVisitor) VisitSubExpression(node *ast.SubExpression) interface{} { return nil } 355 | func (v *whitespaceVisitor) VisitPath(node *ast.PathExpression) interface{} { return nil } 356 | func (v *whitespaceVisitor) VisitString(node *ast.StringLiteral) interface{} { return nil } 357 | func (v *whitespaceVisitor) VisitBoolean(node *ast.BooleanLiteral) interface{} { return nil } 358 | func (v *whitespaceVisitor) VisitNumber(node *ast.NumberLiteral) interface{} { return nil } 359 | func (v *whitespaceVisitor) VisitHash(node *ast.Hash) interface{} { return nil } 360 | func (v *whitespaceVisitor) VisitHashPair(node *ast.HashPair) interface{} { return nil } 361 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/partial.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // partial represents a partial template 9 | type partial struct { 10 | name string 11 | source string 12 | tpl *Template 13 | } 14 | 15 | // partials stores all global partials 16 | var partials map[string]*partial 17 | 18 | // protects global partials 19 | var partialsMutex sync.RWMutex 20 | 21 | func init() { 22 | partials = make(map[string]*partial) 23 | } 24 | 25 | // newPartial instanciates a new partial 26 | func newPartial(name string, source string, tpl *Template) *partial { 27 | return &partial{ 28 | name: name, 29 | source: source, 30 | tpl: tpl, 31 | } 32 | } 33 | 34 | // RegisterPartial registers a global partial. That partial will be available to all templates. 35 | func RegisterPartial(name string, source string) { 36 | partialsMutex.Lock() 37 | defer partialsMutex.Unlock() 38 | 39 | if partials[name] != nil { 40 | panic(fmt.Errorf("Partial already registered: %s", name)) 41 | } 42 | 43 | partials[name] = newPartial(name, source, nil) 44 | } 45 | 46 | // RegisterPartials registers several global partials. Those partials will be available to all templates. 47 | func RegisterPartials(partials map[string]string) { 48 | for name, p := range partials { 49 | RegisterPartial(name, p) 50 | } 51 | } 52 | 53 | // RegisterPartialTemplate registers a global partial with given parsed template. That partial will be available to all templates. 54 | func RegisterPartialTemplate(name string, tpl *Template) { 55 | partialsMutex.Lock() 56 | defer partialsMutex.Unlock() 57 | 58 | if partials[name] != nil { 59 | panic(fmt.Errorf("Partial already registered: %s", name)) 60 | } 61 | 62 | partials[name] = newPartial(name, "", tpl) 63 | } 64 | 65 | // findPartial finds a registered global partial 66 | func findPartial(name string) *partial { 67 | partialsMutex.RLock() 68 | defer partialsMutex.RUnlock() 69 | 70 | return partials[name] 71 | } 72 | 73 | // template returns parsed partial template 74 | func (p *partial) template() (*Template, error) { 75 | if p.tpl == nil { 76 | var err error 77 | 78 | p.tpl, err = Parse(p.source) 79 | if err != nil { 80 | return nil, err 81 | } 82 | } 83 | 84 | return p.tpl, nil 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/raymond.go: -------------------------------------------------------------------------------- 1 | // Package raymond provides handlebars evaluation 2 | package raymond 3 | 4 | // Render parses a template and evaluates it with given context 5 | // 6 | // Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead. 7 | func Render(source string, ctx interface{}) (string, error) { 8 | // parse template 9 | tpl, err := Parse(source) 10 | if err != nil { 11 | return "", err 12 | } 13 | 14 | // renders template 15 | str, err := tpl.Exec(ctx) 16 | if err != nil { 17 | return "", err 18 | } 19 | 20 | return str, nil 21 | } 22 | 23 | // MustRender parses a template and evaluates it with given context. It panics on error. 24 | // 25 | // Note that this function call is not optimal as your template is parsed everytime you call it. You should use Parse() function instead. 26 | func MustRender(source string, ctx interface{}) string { 27 | return MustParse(source).MustExec(ctx) 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/raymond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clem109/drone-wechat/43762c4521e73098a781c1e390280a72b89a65d5/vendor/github.com/aymerick/raymond/raymond.png -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/string.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strconv" 7 | ) 8 | 9 | // SafeString represents a string that must not be escaped. 10 | // 11 | // A SafeString can be returned by helpers to disable escaping. 12 | type SafeString string 13 | 14 | // isSafeString returns true if argument is a SafeString 15 | func isSafeString(value interface{}) bool { 16 | if _, ok := value.(SafeString); ok { 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | // Str returns string representation of any basic type value. 23 | func Str(value interface{}) string { 24 | return strValue(reflect.ValueOf(value)) 25 | } 26 | 27 | // strValue returns string representation of a reflect.Value 28 | func strValue(value reflect.Value) string { 29 | result := "" 30 | 31 | ival, ok := printableValue(value) 32 | if !ok { 33 | panic(fmt.Errorf("Can't print value: %q", value)) 34 | } 35 | 36 | val := reflect.ValueOf(ival) 37 | 38 | switch val.Kind() { 39 | case reflect.Array, reflect.Slice: 40 | for i := 0; i < val.Len(); i++ { 41 | result += strValue(val.Index(i)) 42 | } 43 | case reflect.Bool: 44 | result = "false" 45 | if val.Bool() { 46 | result = "true" 47 | } 48 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 49 | result = fmt.Sprintf("%d", ival) 50 | case reflect.Float32, reflect.Float64: 51 | result = strconv.FormatFloat(val.Float(), 'f', -1, 64) 52 | case reflect.Invalid: 53 | result = "" 54 | default: 55 | result = fmt.Sprintf("%s", ival) 56 | } 57 | 58 | return result 59 | } 60 | 61 | // printableValue returns the, possibly indirected, interface value inside v that 62 | // is best for a call to formatted printer. 63 | // 64 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 65 | func printableValue(v reflect.Value) (interface{}, bool) { 66 | if v.Kind() == reflect.Ptr { 67 | v, _ = indirect(v) // fmt.Fprint handles nil. 68 | } 69 | if !v.IsValid() { 70 | return "", true 71 | } 72 | 73 | if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) { 74 | if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) { 75 | v = v.Addr() 76 | } else { 77 | switch v.Kind() { 78 | case reflect.Chan, reflect.Func: 79 | return nil, false 80 | } 81 | } 82 | } 83 | return v.Interface(), true 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/template.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "reflect" 7 | "runtime" 8 | "sync" 9 | 10 | "github.com/aymerick/raymond/ast" 11 | "github.com/aymerick/raymond/parser" 12 | ) 13 | 14 | // Template represents a handlebars template. 15 | type Template struct { 16 | source string 17 | program *ast.Program 18 | helpers map[string]reflect.Value 19 | partials map[string]*partial 20 | mutex sync.RWMutex // protects helpers and partials 21 | } 22 | 23 | // newTemplate instanciate a new template without parsing it 24 | func newTemplate(source string) *Template { 25 | return &Template{ 26 | source: source, 27 | helpers: make(map[string]reflect.Value), 28 | partials: make(map[string]*partial), 29 | } 30 | } 31 | 32 | // Parse instanciates a template by parsing given source. 33 | func Parse(source string) (*Template, error) { 34 | tpl := newTemplate(source) 35 | 36 | // parse template 37 | if err := tpl.parse(); err != nil { 38 | return nil, err 39 | } 40 | 41 | return tpl, nil 42 | } 43 | 44 | // MustParse instanciates a template by parsing given source. It panics on error. 45 | func MustParse(source string) *Template { 46 | result, err := Parse(source) 47 | if err != nil { 48 | panic(err) 49 | } 50 | return result 51 | } 52 | 53 | // ParseFile reads given file and returns parsed template. 54 | func ParseFile(filePath string) (*Template, error) { 55 | b, err := ioutil.ReadFile(filePath) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | return Parse(string(b)) 61 | } 62 | 63 | // parse parses the template 64 | // 65 | // It can be called several times, the parsing will be done only once. 66 | func (tpl *Template) parse() error { 67 | if tpl.program == nil { 68 | var err error 69 | 70 | tpl.program, err = parser.Parse(tpl.source) 71 | if err != nil { 72 | return err 73 | } 74 | } 75 | 76 | return nil 77 | } 78 | 79 | // Clone returns a copy of that template. 80 | func (tpl *Template) Clone() *Template { 81 | result := newTemplate(tpl.source) 82 | 83 | result.program = tpl.program 84 | 85 | tpl.mutex.RLock() 86 | defer tpl.mutex.RUnlock() 87 | 88 | for name, helper := range tpl.helpers { 89 | result.RegisterHelper(name, helper.Interface()) 90 | } 91 | 92 | for name, partial := range tpl.partials { 93 | result.addPartial(name, partial.source, partial.tpl) 94 | } 95 | 96 | return result 97 | } 98 | 99 | func (tpl *Template) findHelper(name string) reflect.Value { 100 | tpl.mutex.RLock() 101 | defer tpl.mutex.RUnlock() 102 | 103 | return tpl.helpers[name] 104 | } 105 | 106 | // RegisterHelper registers a helper for that template. 107 | func (tpl *Template) RegisterHelper(name string, helper interface{}) { 108 | tpl.mutex.Lock() 109 | defer tpl.mutex.Unlock() 110 | 111 | if tpl.helpers[name] != zero { 112 | panic(fmt.Sprintf("Helper %s already registered", name)) 113 | } 114 | 115 | val := reflect.ValueOf(helper) 116 | ensureValidHelper(name, val) 117 | 118 | tpl.helpers[name] = val 119 | } 120 | 121 | // RegisterHelpers registers several helpers for that template. 122 | func (tpl *Template) RegisterHelpers(helpers map[string]interface{}) { 123 | for name, helper := range helpers { 124 | tpl.RegisterHelper(name, helper) 125 | } 126 | } 127 | 128 | func (tpl *Template) addPartial(name string, source string, template *Template) { 129 | tpl.mutex.Lock() 130 | defer tpl.mutex.Unlock() 131 | 132 | if tpl.partials[name] != nil { 133 | panic(fmt.Sprintf("Partial %s already registered", name)) 134 | } 135 | 136 | tpl.partials[name] = newPartial(name, source, template) 137 | } 138 | 139 | func (tpl *Template) findPartial(name string) *partial { 140 | tpl.mutex.RLock() 141 | defer tpl.mutex.RUnlock() 142 | 143 | return tpl.partials[name] 144 | } 145 | 146 | // RegisterPartial registers a partial for that template. 147 | func (tpl *Template) RegisterPartial(name string, source string) { 148 | tpl.addPartial(name, source, nil) 149 | } 150 | 151 | // RegisterPartials registers several partials for that template. 152 | func (tpl *Template) RegisterPartials(partials map[string]string) { 153 | for name, partial := range partials { 154 | tpl.RegisterPartial(name, partial) 155 | } 156 | } 157 | 158 | // RegisterPartialFile reads given file and registers its content as a partial with given name. 159 | func (tpl *Template) RegisterPartialFile(filePath string, name string) error { 160 | b, err := ioutil.ReadFile(filePath) 161 | if err != nil { 162 | return err 163 | } 164 | 165 | tpl.RegisterPartial(name, string(b)) 166 | 167 | return nil 168 | } 169 | 170 | // RegisterPartialFiles reads several files and registers them as partials, the filename base is used as the partial name. 171 | func (tpl *Template) RegisterPartialFiles(filePaths ...string) error { 172 | if len(filePaths) == 0 { 173 | return nil 174 | } 175 | 176 | for _, filePath := range filePaths { 177 | name := fileBase(filePath) 178 | 179 | if err := tpl.RegisterPartialFile(filePath, name); err != nil { 180 | return err 181 | } 182 | } 183 | 184 | return nil 185 | } 186 | 187 | // RegisterPartialTemplate registers an already parsed partial for that template. 188 | func (tpl *Template) RegisterPartialTemplate(name string, template *Template) { 189 | tpl.addPartial(name, "", template) 190 | } 191 | 192 | // Exec evaluates template with given context. 193 | func (tpl *Template) Exec(ctx interface{}) (result string, err error) { 194 | return tpl.ExecWith(ctx, nil) 195 | } 196 | 197 | // MustExec evaluates template with given context. It panics on error. 198 | func (tpl *Template) MustExec(ctx interface{}) string { 199 | result, err := tpl.Exec(ctx) 200 | if err != nil { 201 | panic(err) 202 | } 203 | return result 204 | } 205 | 206 | // ExecWith evaluates template with given context and private data frame. 207 | func (tpl *Template) ExecWith(ctx interface{}, privData *DataFrame) (result string, err error) { 208 | defer errRecover(&err) 209 | 210 | // parses template if necessary 211 | err = tpl.parse() 212 | if err != nil { 213 | return 214 | } 215 | 216 | // setup visitor 217 | v := newEvalVisitor(tpl, ctx, privData) 218 | 219 | // visit AST 220 | result, _ = tpl.program.Accept(v).(string) 221 | 222 | // named return values 223 | return 224 | } 225 | 226 | // errRecover recovers evaluation panic 227 | func errRecover(errp *error) { 228 | e := recover() 229 | if e != nil { 230 | switch err := e.(type) { 231 | case runtime.Error: 232 | panic(e) 233 | case error: 234 | *errp = err 235 | default: 236 | panic(e) 237 | } 238 | } 239 | } 240 | 241 | // PrintAST returns string representation of parsed template. 242 | func (tpl *Template) PrintAST() string { 243 | if err := tpl.parse(); err != nil { 244 | return fmt.Sprintf("PARSER ERROR: %s", err) 245 | } 246 | 247 | return ast.Print(tpl.program) 248 | } 249 | -------------------------------------------------------------------------------- /vendor/github.com/aymerick/raymond/utils.go: -------------------------------------------------------------------------------- 1 | package raymond 2 | 3 | import ( 4 | "path" 5 | "reflect" 6 | ) 7 | 8 | // indirect returns the item at the end of indirection, and a bool to indicate if it's nil. 9 | // We indirect through pointers and empty interfaces (only) because 10 | // non-empty interfaces have methods we might need. 11 | // 12 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 13 | func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { 14 | for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { 15 | if v.IsNil() { 16 | return v, true 17 | } 18 | if v.Kind() == reflect.Interface && v.NumMethod() > 0 { 19 | break 20 | } 21 | } 22 | return v, false 23 | } 24 | 25 | // IsTrue returns true if obj is a truthy value. 26 | func IsTrue(obj interface{}) bool { 27 | thruth, ok := isTrueValue(reflect.ValueOf(obj)) 28 | if !ok { 29 | return false 30 | } 31 | return thruth 32 | } 33 | 34 | // isTrueValue reports whether the value is 'true', in the sense of not the zero of its type, 35 | // and whether the value has a meaningful truth value 36 | // 37 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 38 | func isTrueValue(val reflect.Value) (truth, ok bool) { 39 | if !val.IsValid() { 40 | // Something like var x interface{}, never set. It's a form of nil. 41 | return false, true 42 | } 43 | switch val.Kind() { 44 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 45 | truth = val.Len() > 0 46 | case reflect.Bool: 47 | truth = val.Bool() 48 | case reflect.Complex64, reflect.Complex128: 49 | truth = val.Complex() != 0 50 | case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface: 51 | truth = !val.IsNil() 52 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 53 | truth = val.Int() != 0 54 | case reflect.Float32, reflect.Float64: 55 | truth = val.Float() != 0 56 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 57 | truth = val.Uint() != 0 58 | case reflect.Struct: 59 | truth = true // Struct values are always true. 60 | default: 61 | return 62 | } 63 | return truth, true 64 | } 65 | 66 | // canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. 67 | // 68 | // NOTE: borrowed from https://github.com/golang/go/tree/master/src/text/template/exec.go 69 | func canBeNil(typ reflect.Type) bool { 70 | switch typ.Kind() { 71 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 72 | return true 73 | } 74 | return false 75 | } 76 | 77 | // fileBase returns base file name 78 | // 79 | // example: /foo/bar/baz.png => baz 80 | func fileBase(filePath string) string { 81 | fileName := path.Base(filePath) 82 | fileExt := path.Ext(filePath) 83 | 84 | return fileName[:len(fileName)-len(fileExt)] 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | **ATTN**: This project uses [semantic versioning](http://semver.org/). 4 | 5 | ## [Unreleased] 6 | 7 | ## [1.19.1] - 2016-11-21 8 | 9 | ### Fixed 10 | 11 | - Fixes regression introduced in 1.19.0 where using an `ActionFunc` as 12 | the `Action` for a command would cause it to error rather than calling the 13 | function. Should not have a affected declarative cases using `func(c 14 | *cli.Context) err)`. 15 | - Shell completion now handles the case where the user specifies 16 | `--generate-bash-completion` immediately after a flag that takes an argument. 17 | Previously it call the application with `--generate-bash-completion` as the 18 | flag value. 19 | 20 | ## [1.19.0] - 2016-11-19 21 | ### Added 22 | - `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`) 23 | - A `Description` field was added to `App` for a more detailed description of 24 | the application (similar to the existing `Description` field on `Command`) 25 | - Flag type code generation via `go generate` 26 | - Write to stderr and exit 1 if action returns non-nil error 27 | - Added support for TOML to the `altsrc` loader 28 | - `SkipArgReorder` was added to allow users to skip the argument reordering. 29 | This is useful if you want to consider all "flags" after an argument as 30 | arguments rather than flags (the default behavior of the stdlib `flag` 31 | library). This is backported functionality from the [removal of the flag 32 | reordering](https://github.com/urfave/cli/pull/398) in the unreleased version 33 | 2 34 | - For formatted errors (those implementing `ErrorFormatter`), the errors will 35 | be formatted during output. Compatible with `pkg/errors`. 36 | 37 | ### Changed 38 | - Raise minimum tested/supported Go version to 1.2+ 39 | 40 | ### Fixed 41 | - Consider empty environment variables as set (previously environment variables 42 | with the equivalent of `""` would be skipped rather than their value used). 43 | - Return an error if the value in a given environment variable cannot be parsed 44 | as the flag type. Previously these errors were silently swallowed. 45 | - Print full error when an invalid flag is specified (which includes the invalid flag) 46 | - `App.Writer` defaults to `stdout` when `nil` 47 | - If no action is specified on a command or app, the help is now printed instead of `panic`ing 48 | - `App.Metadata` is initialized automatically now (previously was `nil` unless initialized) 49 | - Correctly show help message if `-h` is provided to a subcommand 50 | - `context.(Global)IsSet` now respects environment variables. Previously it 51 | would return `false` if a flag was specified in the environment rather than 52 | as an argument 53 | - Removed deprecation warnings to STDERR to avoid them leaking to the end-user 54 | - `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This 55 | fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well 56 | as `altsrc` where Go would complain that the types didn't match 57 | 58 | ## [1.18.1] - 2016-08-28 59 | ### Fixed 60 | - Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported) 61 | 62 | ## [1.18.0] - 2016-06-27 63 | ### Added 64 | - `./runtests` test runner with coverage tracking by default 65 | - testing on OS X 66 | - testing on Windows 67 | - `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code 68 | 69 | ### Changed 70 | - Use spaces for alignment in help/usage output instead of tabs, making the 71 | output alignment consistent regardless of tab width 72 | 73 | ### Fixed 74 | - Printing of command aliases in help text 75 | - Printing of visible flags for both struct and struct pointer flags 76 | - Display the `help` subcommand when using `CommandCategories` 77 | - No longer swallows `panic`s that occur within the `Action`s themselves when 78 | detecting the signature of the `Action` field 79 | 80 | ## [1.17.1] - 2016-08-28 81 | ### Fixed 82 | - Removed deprecation warnings to STDERR to avoid them leaking to the end-user 83 | 84 | ## [1.17.0] - 2016-05-09 85 | ### Added 86 | - Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` 87 | - `context.GlobalBoolT` was added as an analogue to `context.GlobalBool` 88 | - Support for hiding commands by setting `Hidden: true` -- this will hide the 89 | commands in help output 90 | 91 | ### Changed 92 | - `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer 93 | quoted in help text output. 94 | - All flag types now include `(default: {value})` strings following usage when a 95 | default value can be (reasonably) detected. 96 | - `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent 97 | with non-slice flag types 98 | - Apps now exit with a code of 3 if an unknown subcommand is specified 99 | (previously they printed "No help topic for...", but still exited 0. This 100 | makes it easier to script around apps built using `cli` since they can trust 101 | that a 0 exit code indicated a successful execution. 102 | - cleanups based on [Go Report Card 103 | feedback](https://goreportcard.com/report/github.com/urfave/cli) 104 | 105 | ## [1.16.1] - 2016-08-28 106 | ### Fixed 107 | - Removed deprecation warnings to STDERR to avoid them leaking to the end-user 108 | 109 | ## [1.16.0] - 2016-05-02 110 | ### Added 111 | - `Hidden` field on all flag struct types to omit from generated help text 112 | 113 | ### Changed 114 | - `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from 115 | generated help text via the `Hidden` field 116 | 117 | ### Fixed 118 | - handling of error values in `HandleAction` and `HandleExitCoder` 119 | 120 | ## [1.15.0] - 2016-04-30 121 | ### Added 122 | - This file! 123 | - Support for placeholders in flag usage strings 124 | - `App.Metadata` map for arbitrary data/state management 125 | - `Set` and `GlobalSet` methods on `*cli.Context` for altering values after 126 | parsing. 127 | - Support for nested lookup of dot-delimited keys in structures loaded from 128 | YAML. 129 | 130 | ### Changed 131 | - The `App.Action` and `Command.Action` now prefer a return signature of 132 | `func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil 133 | `error` is returned, there may be two outcomes: 134 | - If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called 135 | automatically 136 | - Else the error is bubbled up and returned from `App.Run` 137 | - Specifying an `Action` with the legacy return signature of 138 | `func(*cli.Context)` will produce a deprecation message to stderr 139 | - Specifying an `Action` that is not a `func` type will produce a non-zero exit 140 | from `App.Run` 141 | - Specifying an `Action` func that has an invalid (input) signature will 142 | produce a non-zero exit from `App.Run` 143 | 144 | ### Deprecated 145 | - 146 | `cli.App.RunAndExitOnError`, which should now be done by returning an error 147 | that fulfills `cli.ExitCoder` to `cli.App.Run`. 148 | - the legacy signature for 149 | `cli.App.Action` of `func(*cli.Context)`, which should now have a return 150 | signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. 151 | 152 | ### Fixed 153 | - Added missing `*cli.Context.GlobalFloat64` method 154 | 155 | ## [1.14.0] - 2016-04-03 (backfilled 2016-04-25) 156 | ### Added 157 | - Codebeat badge 158 | - Support for categorization via `CategorizedHelp` and `Categories` on app. 159 | 160 | ### Changed 161 | - Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`. 162 | 163 | ### Fixed 164 | - Ensure version is not shown in help text when `HideVersion` set. 165 | 166 | ## [1.13.0] - 2016-03-06 (backfilled 2016-04-25) 167 | ### Added 168 | - YAML file input support. 169 | - `NArg` method on context. 170 | 171 | ## [1.12.0] - 2016-02-17 (backfilled 2016-04-25) 172 | ### Added 173 | - Custom usage error handling. 174 | - Custom text support in `USAGE` section of help output. 175 | - Improved help messages for empty strings. 176 | - AppVeyor CI configuration. 177 | 178 | ### Changed 179 | - Removed `panic` from default help printer func. 180 | - De-duping and optimizations. 181 | 182 | ### Fixed 183 | - Correctly handle `Before`/`After` at command level when no subcommands. 184 | - Case of literal `-` argument causing flag reordering. 185 | - Environment variable hints on Windows. 186 | - Docs updates. 187 | 188 | ## [1.11.1] - 2015-12-21 (backfilled 2016-04-25) 189 | ### Changed 190 | - Use `path.Base` in `Name` and `HelpName` 191 | - Export `GetName` on flag types. 192 | 193 | ### Fixed 194 | - Flag parsing when skipping is enabled. 195 | - Test output cleanup. 196 | - Move completion check to account for empty input case. 197 | 198 | ## [1.11.0] - 2015-11-15 (backfilled 2016-04-25) 199 | ### Added 200 | - Destination scan support for flags. 201 | - Testing against `tip` in Travis CI config. 202 | 203 | ### Changed 204 | - Go version in Travis CI config. 205 | 206 | ### Fixed 207 | - Removed redundant tests. 208 | - Use correct example naming in tests. 209 | 210 | ## [1.10.2] - 2015-10-29 (backfilled 2016-04-25) 211 | ### Fixed 212 | - Remove unused var in bash completion. 213 | 214 | ## [1.10.1] - 2015-10-21 (backfilled 2016-04-25) 215 | ### Added 216 | - Coverage and reference logos in README. 217 | 218 | ### Fixed 219 | - Use specified values in help and version parsing. 220 | - Only display app version and help message once. 221 | 222 | ## [1.10.0] - 2015-10-06 (backfilled 2016-04-25) 223 | ### Added 224 | - More tests for existing functionality. 225 | - `ArgsUsage` at app and command level for help text flexibility. 226 | 227 | ### Fixed 228 | - Honor `HideHelp` and `HideVersion` in `App.Run`. 229 | - Remove juvenile word from README. 230 | 231 | ## [1.9.0] - 2015-09-08 (backfilled 2016-04-25) 232 | ### Added 233 | - `FullName` on command with accompanying help output update. 234 | - Set default `$PROG` in bash completion. 235 | 236 | ### Changed 237 | - Docs formatting. 238 | 239 | ### Fixed 240 | - Removed self-referential imports in tests. 241 | 242 | ## [1.8.0] - 2015-06-30 (backfilled 2016-04-25) 243 | ### Added 244 | - Support for `Copyright` at app level. 245 | - `Parent` func at context level to walk up context lineage. 246 | 247 | ### Fixed 248 | - Global flag processing at top level. 249 | 250 | ## [1.7.1] - 2015-06-11 (backfilled 2016-04-25) 251 | ### Added 252 | - Aggregate errors from `Before`/`After` funcs. 253 | - Doc comments on flag structs. 254 | - Include non-global flags when checking version and help. 255 | - Travis CI config updates. 256 | 257 | ### Fixed 258 | - Ensure slice type flags have non-nil values. 259 | - Collect global flags from the full command hierarchy. 260 | - Docs prose. 261 | 262 | ## [1.7.0] - 2015-05-03 (backfilled 2016-04-25) 263 | ### Changed 264 | - `HelpPrinter` signature includes output writer. 265 | 266 | ### Fixed 267 | - Specify go 1.1+ in docs. 268 | - Set `Writer` when running command as app. 269 | 270 | ## [1.6.0] - 2015-03-23 (backfilled 2016-04-25) 271 | ### Added 272 | - Multiple author support. 273 | - `NumFlags` at context level. 274 | - `Aliases` at command level. 275 | 276 | ### Deprecated 277 | - `ShortName` at command level. 278 | 279 | ### Fixed 280 | - Subcommand help output. 281 | - Backward compatible support for deprecated `Author` and `Email` fields. 282 | - Docs regarding `Names`/`Aliases`. 283 | 284 | ## [1.5.0] - 2015-02-20 (backfilled 2016-04-25) 285 | ### Added 286 | - `After` hook func support at app and command level. 287 | 288 | ### Fixed 289 | - Use parsed context when running command as subcommand. 290 | - Docs prose. 291 | 292 | ## [1.4.1] - 2015-01-09 (backfilled 2016-04-25) 293 | ### Added 294 | - Support for hiding `-h / --help` flags, but not `help` subcommand. 295 | - Stop flag parsing after `--`. 296 | 297 | ### Fixed 298 | - Help text for generic flags to specify single value. 299 | - Use double quotes in output for defaults. 300 | - Use `ParseInt` instead of `ParseUint` for int environment var values. 301 | - Use `0` as base when parsing int environment var values. 302 | 303 | ## [1.4.0] - 2014-12-12 (backfilled 2016-04-25) 304 | ### Added 305 | - Support for environment variable lookup "cascade". 306 | - Support for `Stdout` on app for output redirection. 307 | 308 | ### Fixed 309 | - Print command help instead of app help in `ShowCommandHelp`. 310 | 311 | ## [1.3.1] - 2014-11-13 (backfilled 2016-04-25) 312 | ### Added 313 | - Docs and example code updates. 314 | 315 | ### Changed 316 | - Default `-v / --version` flag made optional. 317 | 318 | ## [1.3.0] - 2014-08-10 (backfilled 2016-04-25) 319 | ### Added 320 | - `FlagNames` at context level. 321 | - Exposed `VersionPrinter` var for more control over version output. 322 | - Zsh completion hook. 323 | - `AUTHOR` section in default app help template. 324 | - Contribution guidelines. 325 | - `DurationFlag` type. 326 | 327 | ## [1.2.0] - 2014-08-02 328 | ### Added 329 | - Support for environment variable defaults on flags plus tests. 330 | 331 | ## [1.1.0] - 2014-07-15 332 | ### Added 333 | - Bash completion. 334 | - Optional hiding of built-in help command. 335 | - Optional skipping of flag parsing at command level. 336 | - `Author`, `Email`, and `Compiled` metadata on app. 337 | - `Before` hook func support at app and command level. 338 | - `CommandNotFound` func support at app level. 339 | - Command reference available on context. 340 | - `GenericFlag` type. 341 | - `Float64Flag` type. 342 | - `BoolTFlag` type. 343 | - `IsSet` flag helper on context. 344 | - More flag lookup funcs at context level. 345 | - More tests & docs. 346 | 347 | ### Changed 348 | - Help template updates to account for presence/absence of flags. 349 | - Separated subcommand help template. 350 | - Exposed `HelpPrinter` var for more control over help output. 351 | 352 | ## [1.0.0] - 2013-11-01 353 | ### Added 354 | - `help` flag in default app flag set and each command flag set. 355 | - Custom handling of argument parsing errors. 356 | - Command lookup by name at app level. 357 | - `StringSliceFlag` type and supporting `StringSlice` type. 358 | - `IntSliceFlag` type and supporting `IntSlice` type. 359 | - Slice type flag lookups by name at context level. 360 | - Export of app and command help functions. 361 | - More tests & docs. 362 | 363 | ## 0.1.0 - 2013-07-22 364 | ### Added 365 | - Initial implementation. 366 | 367 | [Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD 368 | [1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 369 | [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 370 | [1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 371 | [1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0 372 | [1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0 373 | [1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0 374 | [1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0 375 | [1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1 376 | [1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0 377 | [1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2 378 | [1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1 379 | [1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0 380 | [1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0 381 | [1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0 382 | [1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1 383 | [1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0 384 | [1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0 385 | [1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0 386 | [1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1 387 | [1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0 388 | [1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1 389 | [1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0 390 | [1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0 391 | [1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0 392 | [1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0 393 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jeremy Saenz & Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/app.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "sort" 10 | "time" 11 | ) 12 | 13 | var ( 14 | changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" 15 | appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) 16 | runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) 17 | 18 | contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." 19 | 20 | errInvalidActionType = NewExitError("ERROR invalid Action type. "+ 21 | fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ 22 | fmt.Sprintf("See %s", appActionDeprecationURL), 2) 23 | ) 24 | 25 | // App is the main structure of a cli application. It is recommended that 26 | // an app be created with the cli.NewApp() function 27 | type App struct { 28 | // The name of the program. Defaults to path.Base(os.Args[0]) 29 | Name string 30 | // Full name of command for help, defaults to Name 31 | HelpName string 32 | // Description of the program. 33 | Usage string 34 | // Text to override the USAGE section of help 35 | UsageText string 36 | // Description of the program argument format. 37 | ArgsUsage string 38 | // Version of the program 39 | Version string 40 | // Description of the program 41 | Description string 42 | // List of commands to execute 43 | Commands []Command 44 | // List of flags to parse 45 | Flags []Flag 46 | // Boolean to enable bash completion commands 47 | EnableBashCompletion bool 48 | // Boolean to hide built-in help command 49 | HideHelp bool 50 | // Boolean to hide built-in version flag and the VERSION section of help 51 | HideVersion bool 52 | // Populate on app startup, only gettable through method Categories() 53 | categories CommandCategories 54 | // An action to execute when the bash-completion flag is set 55 | BashComplete BashCompleteFunc 56 | // An action to execute before any subcommands are run, but after the context is ready 57 | // If a non-nil error is returned, no subcommands are run 58 | Before BeforeFunc 59 | // An action to execute after any subcommands are run, but after the subcommand has finished 60 | // It is run even if Action() panics 61 | After AfterFunc 62 | 63 | // The action to execute when no subcommands are specified 64 | // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}` 65 | // *Note*: support for the deprecated `Action` signature will be removed in a future version 66 | Action interface{} 67 | 68 | // Execute this function if the proper command cannot be found 69 | CommandNotFound CommandNotFoundFunc 70 | // Execute this function if an usage error occurs 71 | OnUsageError OnUsageErrorFunc 72 | // Compilation date 73 | Compiled time.Time 74 | // List of all authors who contributed 75 | Authors []Author 76 | // Copyright of the binary if any 77 | Copyright string 78 | // Name of Author (Note: Use App.Authors, this is deprecated) 79 | Author string 80 | // Email of Author (Note: Use App.Authors, this is deprecated) 81 | Email string 82 | // Writer writer to write output to 83 | Writer io.Writer 84 | // ErrWriter writes error output 85 | ErrWriter io.Writer 86 | // Other custom info 87 | Metadata map[string]interface{} 88 | // Carries a function which returns app specific info. 89 | ExtraInfo func() map[string]string 90 | // CustomAppHelpTemplate the text template for app help topic. 91 | // cli.go uses text/template to render templates. You can 92 | // render custom help text by setting this variable. 93 | CustomAppHelpTemplate string 94 | 95 | didSetup bool 96 | } 97 | 98 | // Tries to find out when this binary was compiled. 99 | // Returns the current time if it fails to find it. 100 | func compileTime() time.Time { 101 | info, err := os.Stat(os.Args[0]) 102 | if err != nil { 103 | return time.Now() 104 | } 105 | return info.ModTime() 106 | } 107 | 108 | // NewApp creates a new cli Application with some reasonable defaults for Name, 109 | // Usage, Version and Action. 110 | func NewApp() *App { 111 | return &App{ 112 | Name: filepath.Base(os.Args[0]), 113 | HelpName: filepath.Base(os.Args[0]), 114 | Usage: "A new cli application", 115 | UsageText: "", 116 | Version: "0.0.0", 117 | BashComplete: DefaultAppComplete, 118 | Action: helpCommand.Action, 119 | Compiled: compileTime(), 120 | Writer: os.Stdout, 121 | } 122 | } 123 | 124 | // Setup runs initialization code to ensure all data structures are ready for 125 | // `Run` or inspection prior to `Run`. It is internally called by `Run`, but 126 | // will return early if setup has already happened. 127 | func (a *App) Setup() { 128 | if a.didSetup { 129 | return 130 | } 131 | 132 | a.didSetup = true 133 | 134 | if a.Author != "" || a.Email != "" { 135 | a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) 136 | } 137 | 138 | newCmds := []Command{} 139 | for _, c := range a.Commands { 140 | if c.HelpName == "" { 141 | c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) 142 | } 143 | newCmds = append(newCmds, c) 144 | } 145 | a.Commands = newCmds 146 | 147 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 148 | a.Commands = append(a.Commands, helpCommand) 149 | if (HelpFlag != BoolFlag{}) { 150 | a.appendFlag(HelpFlag) 151 | } 152 | } 153 | 154 | if !a.HideVersion { 155 | a.appendFlag(VersionFlag) 156 | } 157 | 158 | a.categories = CommandCategories{} 159 | for _, command := range a.Commands { 160 | a.categories = a.categories.AddCommand(command.Category, command) 161 | } 162 | sort.Sort(a.categories) 163 | 164 | if a.Metadata == nil { 165 | a.Metadata = make(map[string]interface{}) 166 | } 167 | 168 | if a.Writer == nil { 169 | a.Writer = os.Stdout 170 | } 171 | } 172 | 173 | // Run is the entry point to the cli app. Parses the arguments slice and routes 174 | // to the proper flag/args combination 175 | func (a *App) Run(arguments []string) (err error) { 176 | a.Setup() 177 | 178 | // handle the completion flag separately from the flagset since 179 | // completion could be attempted after a flag, but before its value was put 180 | // on the command line. this causes the flagset to interpret the completion 181 | // flag name as the value of the flag before it which is undesirable 182 | // note that we can only do this because the shell autocomplete function 183 | // always appends the completion flag at the end of the command 184 | shellComplete, arguments := checkShellCompleteFlag(a, arguments) 185 | 186 | // parse flags 187 | set, err := flagSet(a.Name, a.Flags) 188 | if err != nil { 189 | return err 190 | } 191 | 192 | set.SetOutput(ioutil.Discard) 193 | err = set.Parse(arguments[1:]) 194 | nerr := normalizeFlags(a.Flags, set) 195 | context := NewContext(a, set, nil) 196 | if nerr != nil { 197 | fmt.Fprintln(a.Writer, nerr) 198 | ShowAppHelp(context) 199 | return nerr 200 | } 201 | context.shellComplete = shellComplete 202 | 203 | if checkCompletions(context) { 204 | return nil 205 | } 206 | 207 | if err != nil { 208 | if a.OnUsageError != nil { 209 | err := a.OnUsageError(context, err, false) 210 | HandleExitCoder(err) 211 | return err 212 | } 213 | fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) 214 | ShowAppHelp(context) 215 | return err 216 | } 217 | 218 | if !a.HideHelp && checkHelp(context) { 219 | ShowAppHelp(context) 220 | return nil 221 | } 222 | 223 | if !a.HideVersion && checkVersion(context) { 224 | ShowVersion(context) 225 | return nil 226 | } 227 | 228 | if a.After != nil { 229 | defer func() { 230 | if afterErr := a.After(context); afterErr != nil { 231 | if err != nil { 232 | err = NewMultiError(err, afterErr) 233 | } else { 234 | err = afterErr 235 | } 236 | } 237 | }() 238 | } 239 | 240 | if a.Before != nil { 241 | beforeErr := a.Before(context) 242 | if beforeErr != nil { 243 | ShowAppHelp(context) 244 | HandleExitCoder(beforeErr) 245 | err = beforeErr 246 | return err 247 | } 248 | } 249 | 250 | args := context.Args() 251 | if args.Present() { 252 | name := args.First() 253 | c := a.Command(name) 254 | if c != nil { 255 | return c.Run(context) 256 | } 257 | } 258 | 259 | if a.Action == nil { 260 | a.Action = helpCommand.Action 261 | } 262 | 263 | // Run default Action 264 | err = HandleAction(a.Action, context) 265 | 266 | HandleExitCoder(err) 267 | return err 268 | } 269 | 270 | // RunAndExitOnError calls .Run() and exits non-zero if an error was returned 271 | // 272 | // Deprecated: instead you should return an error that fulfills cli.ExitCoder 273 | // to cli.App.Run. This will cause the application to exit with the given eror 274 | // code in the cli.ExitCoder 275 | func (a *App) RunAndExitOnError() { 276 | if err := a.Run(os.Args); err != nil { 277 | fmt.Fprintln(a.errWriter(), err) 278 | OsExiter(1) 279 | } 280 | } 281 | 282 | // RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to 283 | // generate command-specific flags 284 | func (a *App) RunAsSubcommand(ctx *Context) (err error) { 285 | // append help to commands 286 | if len(a.Commands) > 0 { 287 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 288 | a.Commands = append(a.Commands, helpCommand) 289 | if (HelpFlag != BoolFlag{}) { 290 | a.appendFlag(HelpFlag) 291 | } 292 | } 293 | } 294 | 295 | newCmds := []Command{} 296 | for _, c := range a.Commands { 297 | if c.HelpName == "" { 298 | c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) 299 | } 300 | newCmds = append(newCmds, c) 301 | } 302 | a.Commands = newCmds 303 | 304 | // parse flags 305 | set, err := flagSet(a.Name, a.Flags) 306 | if err != nil { 307 | return err 308 | } 309 | 310 | set.SetOutput(ioutil.Discard) 311 | err = set.Parse(ctx.Args().Tail()) 312 | nerr := normalizeFlags(a.Flags, set) 313 | context := NewContext(a, set, ctx) 314 | 315 | if nerr != nil { 316 | fmt.Fprintln(a.Writer, nerr) 317 | fmt.Fprintln(a.Writer) 318 | if len(a.Commands) > 0 { 319 | ShowSubcommandHelp(context) 320 | } else { 321 | ShowCommandHelp(ctx, context.Args().First()) 322 | } 323 | return nerr 324 | } 325 | 326 | if checkCompletions(context) { 327 | return nil 328 | } 329 | 330 | if err != nil { 331 | if a.OnUsageError != nil { 332 | err = a.OnUsageError(context, err, true) 333 | HandleExitCoder(err) 334 | return err 335 | } 336 | fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) 337 | ShowSubcommandHelp(context) 338 | return err 339 | } 340 | 341 | if len(a.Commands) > 0 { 342 | if checkSubcommandHelp(context) { 343 | return nil 344 | } 345 | } else { 346 | if checkCommandHelp(ctx, context.Args().First()) { 347 | return nil 348 | } 349 | } 350 | 351 | if a.After != nil { 352 | defer func() { 353 | afterErr := a.After(context) 354 | if afterErr != nil { 355 | HandleExitCoder(err) 356 | if err != nil { 357 | err = NewMultiError(err, afterErr) 358 | } else { 359 | err = afterErr 360 | } 361 | } 362 | }() 363 | } 364 | 365 | if a.Before != nil { 366 | beforeErr := a.Before(context) 367 | if beforeErr != nil { 368 | HandleExitCoder(beforeErr) 369 | err = beforeErr 370 | return err 371 | } 372 | } 373 | 374 | args := context.Args() 375 | if args.Present() { 376 | name := args.First() 377 | c := a.Command(name) 378 | if c != nil { 379 | return c.Run(context) 380 | } 381 | } 382 | 383 | // Run default Action 384 | err = HandleAction(a.Action, context) 385 | 386 | HandleExitCoder(err) 387 | return err 388 | } 389 | 390 | // Command returns the named command on App. Returns nil if the command does not exist 391 | func (a *App) Command(name string) *Command { 392 | for _, c := range a.Commands { 393 | if c.HasName(name) { 394 | return &c 395 | } 396 | } 397 | 398 | return nil 399 | } 400 | 401 | // Categories returns a slice containing all the categories with the commands they contain 402 | func (a *App) Categories() CommandCategories { 403 | return a.categories 404 | } 405 | 406 | // VisibleCategories returns a slice of categories and commands that are 407 | // Hidden=false 408 | func (a *App) VisibleCategories() []*CommandCategory { 409 | ret := []*CommandCategory{} 410 | for _, category := range a.categories { 411 | if visible := func() *CommandCategory { 412 | for _, command := range category.Commands { 413 | if !command.Hidden { 414 | return category 415 | } 416 | } 417 | return nil 418 | }(); visible != nil { 419 | ret = append(ret, visible) 420 | } 421 | } 422 | return ret 423 | } 424 | 425 | // VisibleCommands returns a slice of the Commands with Hidden=false 426 | func (a *App) VisibleCommands() []Command { 427 | ret := []Command{} 428 | for _, command := range a.Commands { 429 | if !command.Hidden { 430 | ret = append(ret, command) 431 | } 432 | } 433 | return ret 434 | } 435 | 436 | // VisibleFlags returns a slice of the Flags with Hidden=false 437 | func (a *App) VisibleFlags() []Flag { 438 | return visibleFlags(a.Flags) 439 | } 440 | 441 | func (a *App) hasFlag(flag Flag) bool { 442 | for _, f := range a.Flags { 443 | if flag == f { 444 | return true 445 | } 446 | } 447 | 448 | return false 449 | } 450 | 451 | func (a *App) errWriter() io.Writer { 452 | 453 | // When the app ErrWriter is nil use the package level one. 454 | if a.ErrWriter == nil { 455 | return ErrWriter 456 | } 457 | 458 | return a.ErrWriter 459 | } 460 | 461 | func (a *App) appendFlag(flag Flag) { 462 | if !a.hasFlag(flag) { 463 | a.Flags = append(a.Flags, flag) 464 | } 465 | } 466 | 467 | // Author represents someone who has contributed to a cli project. 468 | type Author struct { 469 | Name string // The Authors name 470 | Email string // The Authors email 471 | } 472 | 473 | // String makes Author comply to the Stringer interface, to allow an easy print in the templating process 474 | func (a Author) String() string { 475 | e := "" 476 | if a.Email != "" { 477 | e = " <" + a.Email + ">" 478 | } 479 | 480 | return fmt.Sprintf("%v%v", a.Name, e) 481 | } 482 | 483 | // HandleAction attempts to figure out which Action signature was used. If 484 | // it's an ActionFunc or a func with the legacy signature for Action, the func 485 | // is run! 486 | func HandleAction(action interface{}, context *Context) (err error) { 487 | if a, ok := action.(ActionFunc); ok { 488 | return a(context) 489 | } else if a, ok := action.(func(*Context) error); ok { 490 | return a(context) 491 | } else if a, ok := action.(func(*Context)); ok { // deprecated function signature 492 | a(context) 493 | return nil 494 | } else { 495 | return errInvalidActionType 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | os: Windows Server 2012 R2 4 | 5 | clone_folder: c:\gopath\src\github.com\urfave\cli 6 | 7 | environment: 8 | GOPATH: C:\gopath 9 | GOVERSION: 1.6 10 | PYTHON: C:\Python27-x64 11 | PYTHON_VERSION: 2.7.x 12 | PYTHON_ARCH: 64 13 | 14 | install: 15 | - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% 16 | - go version 17 | - go env 18 | - go get github.com/urfave/gfmrun/... 19 | - go get -v -t ./... 20 | 21 | build_script: 22 | - python runtests vet 23 | - python runtests test 24 | - python runtests gfmrun 25 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/category.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // CommandCategories is a slice of *CommandCategory. 4 | type CommandCategories []*CommandCategory 5 | 6 | // CommandCategory is a category containing commands. 7 | type CommandCategory struct { 8 | Name string 9 | Commands Commands 10 | } 11 | 12 | func (c CommandCategories) Less(i, j int) bool { 13 | return c[i].Name < c[j].Name 14 | } 15 | 16 | func (c CommandCategories) Len() int { 17 | return len(c) 18 | } 19 | 20 | func (c CommandCategories) Swap(i, j int) { 21 | c[i], c[j] = c[j], c[i] 22 | } 23 | 24 | // AddCommand adds a command to a category. 25 | func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { 26 | for _, commandCategory := range c { 27 | if commandCategory.Name == category { 28 | commandCategory.Commands = append(commandCategory.Commands, command) 29 | return c 30 | } 31 | } 32 | return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) 33 | } 34 | 35 | // VisibleCommands returns a slice of the Commands with Hidden=false 36 | func (c *CommandCategory) VisibleCommands() []Command { 37 | ret := []Command{} 38 | for _, command := range c.Commands { 39 | if !command.Hidden { 40 | ret = append(ret, command) 41 | } 42 | } 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) error { 14 | // println("Greetings") 15 | // return nil 16 | // } 17 | // 18 | // app.Run(os.Args) 19 | // } 20 | package cli 21 | 22 | //go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go 23 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/command.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | // Command is a subcommand for a cli.App. 11 | type Command struct { 12 | // The name of the command 13 | Name string 14 | // short name of the command. Typically one character (deprecated, use `Aliases`) 15 | ShortName string 16 | // A list of aliases for the command 17 | Aliases []string 18 | // A short description of the usage of this command 19 | Usage string 20 | // Custom text to show on USAGE section of help 21 | UsageText string 22 | // A longer explanation of how the command works 23 | Description string 24 | // A short description of the arguments of this command 25 | ArgsUsage string 26 | // The category the command is part of 27 | Category string 28 | // The function to call when checking for bash command completions 29 | BashComplete BashCompleteFunc 30 | // An action to execute before any sub-subcommands are run, but after the context is ready 31 | // If a non-nil error is returned, no sub-subcommands are run 32 | Before BeforeFunc 33 | // An action to execute after any subcommands are run, but after the subcommand has finished 34 | // It is run even if Action() panics 35 | After AfterFunc 36 | // The function to call when this command is invoked 37 | Action interface{} 38 | // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind 39 | // of deprecation period has passed, maybe? 40 | 41 | // Execute this function if a usage error occurs. 42 | OnUsageError OnUsageErrorFunc 43 | // List of child commands 44 | Subcommands Commands 45 | // List of flags to parse 46 | Flags []Flag 47 | // Treat all flags as normal arguments if true 48 | SkipFlagParsing bool 49 | // Skip argument reordering which attempts to move flags before arguments, 50 | // but only works if all flags appear after all arguments. This behavior was 51 | // removed n version 2 since it only works under specific conditions so we 52 | // backport here by exposing it as an option for compatibility. 53 | SkipArgReorder bool 54 | // Boolean to hide built-in help command 55 | HideHelp bool 56 | // Boolean to hide this command from help or completion 57 | Hidden bool 58 | 59 | // Full name of command for help, defaults to full command name, including parent commands. 60 | HelpName string 61 | commandNamePath []string 62 | 63 | // CustomHelpTemplate the text template for the command help topic. 64 | // cli.go uses text/template to render templates. You can 65 | // render custom help text by setting this variable. 66 | CustomHelpTemplate string 67 | } 68 | 69 | type CommandsByName []Command 70 | 71 | func (c CommandsByName) Len() int { 72 | return len(c) 73 | } 74 | 75 | func (c CommandsByName) Less(i, j int) bool { 76 | return c[i].Name < c[j].Name 77 | } 78 | 79 | func (c CommandsByName) Swap(i, j int) { 80 | c[i], c[j] = c[j], c[i] 81 | } 82 | 83 | // FullName returns the full name of the command. 84 | // For subcommands this ensures that parent commands are part of the command path 85 | func (c Command) FullName() string { 86 | if c.commandNamePath == nil { 87 | return c.Name 88 | } 89 | return strings.Join(c.commandNamePath, " ") 90 | } 91 | 92 | // Commands is a slice of Command 93 | type Commands []Command 94 | 95 | // Run invokes the command given the context, parses ctx.Args() to generate command-specific flags 96 | func (c Command) Run(ctx *Context) (err error) { 97 | if len(c.Subcommands) > 0 { 98 | return c.startApp(ctx) 99 | } 100 | 101 | if !c.HideHelp && (HelpFlag != BoolFlag{}) { 102 | // append help to flags 103 | c.Flags = append( 104 | c.Flags, 105 | HelpFlag, 106 | ) 107 | } 108 | 109 | set, err := flagSet(c.Name, c.Flags) 110 | if err != nil { 111 | return err 112 | } 113 | set.SetOutput(ioutil.Discard) 114 | 115 | if c.SkipFlagParsing { 116 | err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) 117 | } else if !c.SkipArgReorder { 118 | firstFlagIndex := -1 119 | terminatorIndex := -1 120 | for index, arg := range ctx.Args() { 121 | if arg == "--" { 122 | terminatorIndex = index 123 | break 124 | } else if arg == "-" { 125 | // Do nothing. A dash alone is not really a flag. 126 | continue 127 | } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { 128 | firstFlagIndex = index 129 | } 130 | } 131 | 132 | if firstFlagIndex > -1 { 133 | args := ctx.Args() 134 | regularArgs := make([]string, len(args[1:firstFlagIndex])) 135 | copy(regularArgs, args[1:firstFlagIndex]) 136 | 137 | var flagArgs []string 138 | if terminatorIndex > -1 { 139 | flagArgs = args[firstFlagIndex:terminatorIndex] 140 | regularArgs = append(regularArgs, args[terminatorIndex:]...) 141 | } else { 142 | flagArgs = args[firstFlagIndex:] 143 | } 144 | 145 | err = set.Parse(append(flagArgs, regularArgs...)) 146 | } else { 147 | err = set.Parse(ctx.Args().Tail()) 148 | } 149 | } else { 150 | err = set.Parse(ctx.Args().Tail()) 151 | } 152 | 153 | nerr := normalizeFlags(c.Flags, set) 154 | if nerr != nil { 155 | fmt.Fprintln(ctx.App.Writer, nerr) 156 | fmt.Fprintln(ctx.App.Writer) 157 | ShowCommandHelp(ctx, c.Name) 158 | return nerr 159 | } 160 | 161 | context := NewContext(ctx.App, set, ctx) 162 | context.Command = c 163 | if checkCommandCompletions(context, c.Name) { 164 | return nil 165 | } 166 | 167 | if err != nil { 168 | if c.OnUsageError != nil { 169 | err := c.OnUsageError(context, err, false) 170 | HandleExitCoder(err) 171 | return err 172 | } 173 | fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) 174 | fmt.Fprintln(context.App.Writer) 175 | ShowCommandHelp(context, c.Name) 176 | return err 177 | } 178 | 179 | if checkCommandHelp(context, c.Name) { 180 | return nil 181 | } 182 | 183 | if c.After != nil { 184 | defer func() { 185 | afterErr := c.After(context) 186 | if afterErr != nil { 187 | HandleExitCoder(err) 188 | if err != nil { 189 | err = NewMultiError(err, afterErr) 190 | } else { 191 | err = afterErr 192 | } 193 | } 194 | }() 195 | } 196 | 197 | if c.Before != nil { 198 | err = c.Before(context) 199 | if err != nil { 200 | ShowCommandHelp(context, c.Name) 201 | HandleExitCoder(err) 202 | return err 203 | } 204 | } 205 | 206 | if c.Action == nil { 207 | c.Action = helpSubcommand.Action 208 | } 209 | 210 | err = HandleAction(c.Action, context) 211 | 212 | if err != nil { 213 | HandleExitCoder(err) 214 | } 215 | return err 216 | } 217 | 218 | // Names returns the names including short names and aliases. 219 | func (c Command) Names() []string { 220 | names := []string{c.Name} 221 | 222 | if c.ShortName != "" { 223 | names = append(names, c.ShortName) 224 | } 225 | 226 | return append(names, c.Aliases...) 227 | } 228 | 229 | // HasName returns true if Command.Name or Command.ShortName matches given name 230 | func (c Command) HasName(name string) bool { 231 | for _, n := range c.Names() { 232 | if n == name { 233 | return true 234 | } 235 | } 236 | return false 237 | } 238 | 239 | func (c Command) startApp(ctx *Context) error { 240 | app := NewApp() 241 | app.Metadata = ctx.App.Metadata 242 | // set the name and usage 243 | app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) 244 | if c.HelpName == "" { 245 | app.HelpName = c.HelpName 246 | } else { 247 | app.HelpName = app.Name 248 | } 249 | 250 | app.Usage = c.Usage 251 | app.Description = c.Description 252 | app.ArgsUsage = c.ArgsUsage 253 | 254 | // set CommandNotFound 255 | app.CommandNotFound = ctx.App.CommandNotFound 256 | app.CustomAppHelpTemplate = c.CustomHelpTemplate 257 | 258 | // set the flags and commands 259 | app.Commands = c.Subcommands 260 | app.Flags = c.Flags 261 | app.HideHelp = c.HideHelp 262 | 263 | app.Version = ctx.App.Version 264 | app.HideVersion = ctx.App.HideVersion 265 | app.Compiled = ctx.App.Compiled 266 | app.Author = ctx.App.Author 267 | app.Email = ctx.App.Email 268 | app.Writer = ctx.App.Writer 269 | app.ErrWriter = ctx.App.ErrWriter 270 | 271 | app.categories = CommandCategories{} 272 | for _, command := range c.Subcommands { 273 | app.categories = app.categories.AddCommand(command.Category, command) 274 | } 275 | 276 | sort.Sort(app.categories) 277 | 278 | // bash completion 279 | app.EnableBashCompletion = ctx.App.EnableBashCompletion 280 | if c.BashComplete != nil { 281 | app.BashComplete = c.BashComplete 282 | } 283 | 284 | // set the actions 285 | app.Before = c.Before 286 | app.After = c.After 287 | if c.Action != nil { 288 | app.Action = c.Action 289 | } else { 290 | app.Action = helpSubcommand.Action 291 | } 292 | app.OnUsageError = c.OnUsageError 293 | 294 | for index, cc := range app.Commands { 295 | app.Commands[index].commandNamePath = []string{c.Name, cc.Name} 296 | } 297 | 298 | return app.RunAsSubcommand(ctx) 299 | } 300 | 301 | // VisibleFlags returns a slice of the Flags with Hidden=false 302 | func (c Command) VisibleFlags() []Flag { 303 | return visibleFlags(c.Flags) 304 | } 305 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "reflect" 7 | "strings" 8 | "syscall" 9 | ) 10 | 11 | // Context is a type that is passed through to 12 | // each Handler action in a cli application. Context 13 | // can be used to retrieve context-specific Args and 14 | // parsed command-line options. 15 | type Context struct { 16 | App *App 17 | Command Command 18 | shellComplete bool 19 | flagSet *flag.FlagSet 20 | setFlags map[string]bool 21 | parentContext *Context 22 | } 23 | 24 | // NewContext creates a new context. For use in when invoking an App or Command action. 25 | func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { 26 | c := &Context{App: app, flagSet: set, parentContext: parentCtx} 27 | 28 | if parentCtx != nil { 29 | c.shellComplete = parentCtx.shellComplete 30 | } 31 | 32 | return c 33 | } 34 | 35 | // NumFlags returns the number of flags set 36 | func (c *Context) NumFlags() int { 37 | return c.flagSet.NFlag() 38 | } 39 | 40 | // Set sets a context flag to a value. 41 | func (c *Context) Set(name, value string) error { 42 | c.setFlags = nil 43 | return c.flagSet.Set(name, value) 44 | } 45 | 46 | // GlobalSet sets a context flag to a value on the global flagset 47 | func (c *Context) GlobalSet(name, value string) error { 48 | globalContext(c).setFlags = nil 49 | return globalContext(c).flagSet.Set(name, value) 50 | } 51 | 52 | // IsSet determines if the flag was actually set 53 | func (c *Context) IsSet(name string) bool { 54 | if c.setFlags == nil { 55 | c.setFlags = make(map[string]bool) 56 | 57 | c.flagSet.Visit(func(f *flag.Flag) { 58 | c.setFlags[f.Name] = true 59 | }) 60 | 61 | c.flagSet.VisitAll(func(f *flag.Flag) { 62 | if _, ok := c.setFlags[f.Name]; ok { 63 | return 64 | } 65 | c.setFlags[f.Name] = false 66 | }) 67 | 68 | // XXX hack to support IsSet for flags with EnvVar 69 | // 70 | // There isn't an easy way to do this with the current implementation since 71 | // whether a flag was set via an environment variable is very difficult to 72 | // determine here. Instead, we intend to introduce a backwards incompatible 73 | // change in version 2 to add `IsSet` to the Flag interface to push the 74 | // responsibility closer to where the information required to determine 75 | // whether a flag is set by non-standard means such as environment 76 | // variables is avaliable. 77 | // 78 | // See https://github.com/urfave/cli/issues/294 for additional discussion 79 | flags := c.Command.Flags 80 | if c.Command.Name == "" { // cannot == Command{} since it contains slice types 81 | if c.App != nil { 82 | flags = c.App.Flags 83 | } 84 | } 85 | for _, f := range flags { 86 | eachName(f.GetName(), func(name string) { 87 | if isSet, ok := c.setFlags[name]; isSet || !ok { 88 | return 89 | } 90 | 91 | val := reflect.ValueOf(f) 92 | if val.Kind() == reflect.Ptr { 93 | val = val.Elem() 94 | } 95 | 96 | envVarValue := val.FieldByName("EnvVar") 97 | if !envVarValue.IsValid() { 98 | return 99 | } 100 | 101 | eachName(envVarValue.String(), func(envVar string) { 102 | envVar = strings.TrimSpace(envVar) 103 | if _, ok := syscall.Getenv(envVar); ok { 104 | c.setFlags[name] = true 105 | return 106 | } 107 | }) 108 | }) 109 | } 110 | } 111 | 112 | return c.setFlags[name] 113 | } 114 | 115 | // GlobalIsSet determines if the global flag was actually set 116 | func (c *Context) GlobalIsSet(name string) bool { 117 | ctx := c 118 | if ctx.parentContext != nil { 119 | ctx = ctx.parentContext 120 | } 121 | 122 | for ; ctx != nil; ctx = ctx.parentContext { 123 | if ctx.IsSet(name) { 124 | return true 125 | } 126 | } 127 | return false 128 | } 129 | 130 | // FlagNames returns a slice of flag names used in this context. 131 | func (c *Context) FlagNames() (names []string) { 132 | for _, flag := range c.Command.Flags { 133 | name := strings.Split(flag.GetName(), ",")[0] 134 | if name == "help" { 135 | continue 136 | } 137 | names = append(names, name) 138 | } 139 | return 140 | } 141 | 142 | // GlobalFlagNames returns a slice of global flag names used by the app. 143 | func (c *Context) GlobalFlagNames() (names []string) { 144 | for _, flag := range c.App.Flags { 145 | name := strings.Split(flag.GetName(), ",")[0] 146 | if name == "help" || name == "version" { 147 | continue 148 | } 149 | names = append(names, name) 150 | } 151 | return 152 | } 153 | 154 | // Parent returns the parent context, if any 155 | func (c *Context) Parent() *Context { 156 | return c.parentContext 157 | } 158 | 159 | // value returns the value of the flag coressponding to `name` 160 | func (c *Context) value(name string) interface{} { 161 | return c.flagSet.Lookup(name).Value.(flag.Getter).Get() 162 | } 163 | 164 | // Args contains apps console arguments 165 | type Args []string 166 | 167 | // Args returns the command line arguments associated with the context. 168 | func (c *Context) Args() Args { 169 | args := Args(c.flagSet.Args()) 170 | return args 171 | } 172 | 173 | // NArg returns the number of the command line arguments. 174 | func (c *Context) NArg() int { 175 | return len(c.Args()) 176 | } 177 | 178 | // Get returns the nth argument, or else a blank string 179 | func (a Args) Get(n int) string { 180 | if len(a) > n { 181 | return a[n] 182 | } 183 | return "" 184 | } 185 | 186 | // First returns the first argument, or else a blank string 187 | func (a Args) First() string { 188 | return a.Get(0) 189 | } 190 | 191 | // Tail returns the rest of the arguments (not the first one) 192 | // or else an empty string slice 193 | func (a Args) Tail() []string { 194 | if len(a) >= 2 { 195 | return []string(a)[1:] 196 | } 197 | return []string{} 198 | } 199 | 200 | // Present checks if there are any arguments present 201 | func (a Args) Present() bool { 202 | return len(a) != 0 203 | } 204 | 205 | // Swap swaps arguments at the given indexes 206 | func (a Args) Swap(from, to int) error { 207 | if from >= len(a) || to >= len(a) { 208 | return errors.New("index out of range") 209 | } 210 | a[from], a[to] = a[to], a[from] 211 | return nil 212 | } 213 | 214 | func globalContext(ctx *Context) *Context { 215 | if ctx == nil { 216 | return nil 217 | } 218 | 219 | for { 220 | if ctx.parentContext == nil { 221 | return ctx 222 | } 223 | ctx = ctx.parentContext 224 | } 225 | } 226 | 227 | func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { 228 | if ctx.parentContext != nil { 229 | ctx = ctx.parentContext 230 | } 231 | for ; ctx != nil; ctx = ctx.parentContext { 232 | if f := ctx.flagSet.Lookup(name); f != nil { 233 | return ctx.flagSet 234 | } 235 | } 236 | return nil 237 | } 238 | 239 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 240 | switch ff.Value.(type) { 241 | case *StringSlice: 242 | default: 243 | set.Set(name, ff.Value.String()) 244 | } 245 | } 246 | 247 | func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 248 | visited := make(map[string]bool) 249 | set.Visit(func(f *flag.Flag) { 250 | visited[f.Name] = true 251 | }) 252 | for _, f := range flags { 253 | parts := strings.Split(f.GetName(), ",") 254 | if len(parts) == 1 { 255 | continue 256 | } 257 | var ff *flag.Flag 258 | for _, name := range parts { 259 | name = strings.Trim(name, " ") 260 | if visited[name] { 261 | if ff != nil { 262 | return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 263 | } 264 | ff = set.Lookup(name) 265 | } 266 | } 267 | if ff == nil { 268 | continue 269 | } 270 | for _, name := range parts { 271 | name = strings.Trim(name, " ") 272 | if !visited[name] { 273 | copyFlag(name, ff, set) 274 | } 275 | } 276 | } 277 | return nil 278 | } 279 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/errors.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // OsExiter is the function used when the app exits. If not set defaults to os.Exit. 11 | var OsExiter = os.Exit 12 | 13 | // ErrWriter is used to write errors to the user. This can be anything 14 | // implementing the io.Writer interface and defaults to os.Stderr. 15 | var ErrWriter io.Writer = os.Stderr 16 | 17 | // MultiError is an error that wraps multiple errors. 18 | type MultiError struct { 19 | Errors []error 20 | } 21 | 22 | // NewMultiError creates a new MultiError. Pass in one or more errors. 23 | func NewMultiError(err ...error) MultiError { 24 | return MultiError{Errors: err} 25 | } 26 | 27 | // Error implements the error interface. 28 | func (m MultiError) Error() string { 29 | errs := make([]string, len(m.Errors)) 30 | for i, err := range m.Errors { 31 | errs[i] = err.Error() 32 | } 33 | 34 | return strings.Join(errs, "\n") 35 | } 36 | 37 | type ErrorFormatter interface { 38 | Format(s fmt.State, verb rune) 39 | } 40 | 41 | // ExitCoder is the interface checked by `App` and `Command` for a custom exit 42 | // code 43 | type ExitCoder interface { 44 | error 45 | ExitCode() int 46 | } 47 | 48 | // ExitError fulfills both the builtin `error` interface and `ExitCoder` 49 | type ExitError struct { 50 | exitCode int 51 | message interface{} 52 | } 53 | 54 | // NewExitError makes a new *ExitError 55 | func NewExitError(message interface{}, exitCode int) *ExitError { 56 | return &ExitError{ 57 | exitCode: exitCode, 58 | message: message, 59 | } 60 | } 61 | 62 | // Error returns the string message, fulfilling the interface required by 63 | // `error` 64 | func (ee *ExitError) Error() string { 65 | return fmt.Sprintf("%v", ee.message) 66 | } 67 | 68 | // ExitCode returns the exit code, fulfilling the interface required by 69 | // `ExitCoder` 70 | func (ee *ExitError) ExitCode() int { 71 | return ee.exitCode 72 | } 73 | 74 | // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if 75 | // so prints the error to stderr (if it is non-empty) and calls OsExiter with the 76 | // given exit code. If the given error is a MultiError, then this func is 77 | // called on all members of the Errors slice and calls OsExiter with the last exit code. 78 | func HandleExitCoder(err error) { 79 | if err == nil { 80 | return 81 | } 82 | 83 | if exitErr, ok := err.(ExitCoder); ok { 84 | if err.Error() != "" { 85 | if _, ok := exitErr.(ErrorFormatter); ok { 86 | fmt.Fprintf(ErrWriter, "%+v\n", err) 87 | } else { 88 | fmt.Fprintln(ErrWriter, err) 89 | } 90 | } 91 | OsExiter(exitErr.ExitCode()) 92 | return 93 | } 94 | 95 | if multiErr, ok := err.(MultiError); ok { 96 | code := handleMultiError(multiErr) 97 | OsExiter(code) 98 | return 99 | } 100 | } 101 | 102 | func handleMultiError(multiErr MultiError) int { 103 | code := 1 104 | for _, merr := range multiErr.Errors { 105 | if multiErr2, ok := merr.(MultiError); ok { 106 | code = handleMultiError(multiErr2) 107 | } else { 108 | fmt.Fprintln(ErrWriter, merr) 109 | if exitErr, ok := merr.(ExitCoder); ok { 110 | code = exitErr.ExitCode() 111 | } 112 | } 113 | } 114 | return code 115 | } 116 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Bool", 4 | "type": "bool", 5 | "value": false, 6 | "context_default": "false", 7 | "parser": "strconv.ParseBool(f.Value.String())" 8 | }, 9 | { 10 | "name": "BoolT", 11 | "type": "bool", 12 | "value": false, 13 | "doctail": " that is true by default", 14 | "context_default": "false", 15 | "parser": "strconv.ParseBool(f.Value.String())" 16 | }, 17 | { 18 | "name": "Duration", 19 | "type": "time.Duration", 20 | "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", 21 | "context_default": "0", 22 | "parser": "time.ParseDuration(f.Value.String())" 23 | }, 24 | { 25 | "name": "Float64", 26 | "type": "float64", 27 | "context_default": "0", 28 | "parser": "strconv.ParseFloat(f.Value.String(), 64)" 29 | }, 30 | { 31 | "name": "Generic", 32 | "type": "Generic", 33 | "dest": false, 34 | "context_default": "nil", 35 | "context_type": "interface{}" 36 | }, 37 | { 38 | "name": "Int64", 39 | "type": "int64", 40 | "context_default": "0", 41 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" 42 | }, 43 | { 44 | "name": "Int", 45 | "type": "int", 46 | "context_default": "0", 47 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", 48 | "parser_cast": "int(parsed)" 49 | }, 50 | { 51 | "name": "IntSlice", 52 | "type": "*IntSlice", 53 | "dest": false, 54 | "context_default": "nil", 55 | "context_type": "[]int", 56 | "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" 57 | }, 58 | { 59 | "name": "Int64Slice", 60 | "type": "*Int64Slice", 61 | "dest": false, 62 | "context_default": "nil", 63 | "context_type": "[]int64", 64 | "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" 65 | }, 66 | { 67 | "name": "String", 68 | "type": "string", 69 | "context_default": "\"\"", 70 | "parser": "f.Value.String(), error(nil)" 71 | }, 72 | { 73 | "name": "StringSlice", 74 | "type": "*StringSlice", 75 | "dest": false, 76 | "context_default": "nil", 77 | "context_type": "[]string", 78 | "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" 79 | }, 80 | { 81 | "name": "Uint64", 82 | "type": "uint64", 83 | "context_default": "0", 84 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" 85 | }, 86 | { 87 | "name": "Uint", 88 | "type": "uint", 89 | "context_default": "0", 90 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", 91 | "parser_cast": "uint(parsed)" 92 | } 93 | ] 94 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag_generated.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | // WARNING: This file is generated! 10 | 11 | // BoolFlag is a flag with type bool 12 | type BoolFlag struct { 13 | Name string 14 | Usage string 15 | EnvVar string 16 | Hidden bool 17 | Destination *bool 18 | } 19 | 20 | // String returns a readable representation of this value 21 | // (for usage defaults) 22 | func (f BoolFlag) String() string { 23 | return FlagStringer(f) 24 | } 25 | 26 | // GetName returns the name of the flag 27 | func (f BoolFlag) GetName() string { 28 | return f.Name 29 | } 30 | 31 | // Bool looks up the value of a local BoolFlag, returns 32 | // false if not found 33 | func (c *Context) Bool(name string) bool { 34 | return lookupBool(name, c.flagSet) 35 | } 36 | 37 | // GlobalBool looks up the value of a global BoolFlag, returns 38 | // false if not found 39 | func (c *Context) GlobalBool(name string) bool { 40 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 41 | return lookupBool(name, fs) 42 | } 43 | return false 44 | } 45 | 46 | func lookupBool(name string, set *flag.FlagSet) bool { 47 | f := set.Lookup(name) 48 | if f != nil { 49 | parsed, err := strconv.ParseBool(f.Value.String()) 50 | if err != nil { 51 | return false 52 | } 53 | return parsed 54 | } 55 | return false 56 | } 57 | 58 | // BoolTFlag is a flag with type bool that is true by default 59 | type BoolTFlag struct { 60 | Name string 61 | Usage string 62 | EnvVar string 63 | Hidden bool 64 | Destination *bool 65 | } 66 | 67 | // String returns a readable representation of this value 68 | // (for usage defaults) 69 | func (f BoolTFlag) String() string { 70 | return FlagStringer(f) 71 | } 72 | 73 | // GetName returns the name of the flag 74 | func (f BoolTFlag) GetName() string { 75 | return f.Name 76 | } 77 | 78 | // BoolT looks up the value of a local BoolTFlag, returns 79 | // false if not found 80 | func (c *Context) BoolT(name string) bool { 81 | return lookupBoolT(name, c.flagSet) 82 | } 83 | 84 | // GlobalBoolT looks up the value of a global BoolTFlag, returns 85 | // false if not found 86 | func (c *Context) GlobalBoolT(name string) bool { 87 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 88 | return lookupBoolT(name, fs) 89 | } 90 | return false 91 | } 92 | 93 | func lookupBoolT(name string, set *flag.FlagSet) bool { 94 | f := set.Lookup(name) 95 | if f != nil { 96 | parsed, err := strconv.ParseBool(f.Value.String()) 97 | if err != nil { 98 | return false 99 | } 100 | return parsed 101 | } 102 | return false 103 | } 104 | 105 | // DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) 106 | type DurationFlag struct { 107 | Name string 108 | Usage string 109 | EnvVar string 110 | Hidden bool 111 | Value time.Duration 112 | Destination *time.Duration 113 | } 114 | 115 | // String returns a readable representation of this value 116 | // (for usage defaults) 117 | func (f DurationFlag) String() string { 118 | return FlagStringer(f) 119 | } 120 | 121 | // GetName returns the name of the flag 122 | func (f DurationFlag) GetName() string { 123 | return f.Name 124 | } 125 | 126 | // Duration looks up the value of a local DurationFlag, returns 127 | // 0 if not found 128 | func (c *Context) Duration(name string) time.Duration { 129 | return lookupDuration(name, c.flagSet) 130 | } 131 | 132 | // GlobalDuration looks up the value of a global DurationFlag, returns 133 | // 0 if not found 134 | func (c *Context) GlobalDuration(name string) time.Duration { 135 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 136 | return lookupDuration(name, fs) 137 | } 138 | return 0 139 | } 140 | 141 | func lookupDuration(name string, set *flag.FlagSet) time.Duration { 142 | f := set.Lookup(name) 143 | if f != nil { 144 | parsed, err := time.ParseDuration(f.Value.String()) 145 | if err != nil { 146 | return 0 147 | } 148 | return parsed 149 | } 150 | return 0 151 | } 152 | 153 | // Float64Flag is a flag with type float64 154 | type Float64Flag struct { 155 | Name string 156 | Usage string 157 | EnvVar string 158 | Hidden bool 159 | Value float64 160 | Destination *float64 161 | } 162 | 163 | // String returns a readable representation of this value 164 | // (for usage defaults) 165 | func (f Float64Flag) String() string { 166 | return FlagStringer(f) 167 | } 168 | 169 | // GetName returns the name of the flag 170 | func (f Float64Flag) GetName() string { 171 | return f.Name 172 | } 173 | 174 | // Float64 looks up the value of a local Float64Flag, returns 175 | // 0 if not found 176 | func (c *Context) Float64(name string) float64 { 177 | return lookupFloat64(name, c.flagSet) 178 | } 179 | 180 | // GlobalFloat64 looks up the value of a global Float64Flag, returns 181 | // 0 if not found 182 | func (c *Context) GlobalFloat64(name string) float64 { 183 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 184 | return lookupFloat64(name, fs) 185 | } 186 | return 0 187 | } 188 | 189 | func lookupFloat64(name string, set *flag.FlagSet) float64 { 190 | f := set.Lookup(name) 191 | if f != nil { 192 | parsed, err := strconv.ParseFloat(f.Value.String(), 64) 193 | if err != nil { 194 | return 0 195 | } 196 | return parsed 197 | } 198 | return 0 199 | } 200 | 201 | // GenericFlag is a flag with type Generic 202 | type GenericFlag struct { 203 | Name string 204 | Usage string 205 | EnvVar string 206 | Hidden bool 207 | Value Generic 208 | } 209 | 210 | // String returns a readable representation of this value 211 | // (for usage defaults) 212 | func (f GenericFlag) String() string { 213 | return FlagStringer(f) 214 | } 215 | 216 | // GetName returns the name of the flag 217 | func (f GenericFlag) GetName() string { 218 | return f.Name 219 | } 220 | 221 | // Generic looks up the value of a local GenericFlag, returns 222 | // nil if not found 223 | func (c *Context) Generic(name string) interface{} { 224 | return lookupGeneric(name, c.flagSet) 225 | } 226 | 227 | // GlobalGeneric looks up the value of a global GenericFlag, returns 228 | // nil if not found 229 | func (c *Context) GlobalGeneric(name string) interface{} { 230 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 231 | return lookupGeneric(name, fs) 232 | } 233 | return nil 234 | } 235 | 236 | func lookupGeneric(name string, set *flag.FlagSet) interface{} { 237 | f := set.Lookup(name) 238 | if f != nil { 239 | parsed, err := f.Value, error(nil) 240 | if err != nil { 241 | return nil 242 | } 243 | return parsed 244 | } 245 | return nil 246 | } 247 | 248 | // Int64Flag is a flag with type int64 249 | type Int64Flag struct { 250 | Name string 251 | Usage string 252 | EnvVar string 253 | Hidden bool 254 | Value int64 255 | Destination *int64 256 | } 257 | 258 | // String returns a readable representation of this value 259 | // (for usage defaults) 260 | func (f Int64Flag) String() string { 261 | return FlagStringer(f) 262 | } 263 | 264 | // GetName returns the name of the flag 265 | func (f Int64Flag) GetName() string { 266 | return f.Name 267 | } 268 | 269 | // Int64 looks up the value of a local Int64Flag, returns 270 | // 0 if not found 271 | func (c *Context) Int64(name string) int64 { 272 | return lookupInt64(name, c.flagSet) 273 | } 274 | 275 | // GlobalInt64 looks up the value of a global Int64Flag, returns 276 | // 0 if not found 277 | func (c *Context) GlobalInt64(name string) int64 { 278 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 279 | return lookupInt64(name, fs) 280 | } 281 | return 0 282 | } 283 | 284 | func lookupInt64(name string, set *flag.FlagSet) int64 { 285 | f := set.Lookup(name) 286 | if f != nil { 287 | parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) 288 | if err != nil { 289 | return 0 290 | } 291 | return parsed 292 | } 293 | return 0 294 | } 295 | 296 | // IntFlag is a flag with type int 297 | type IntFlag struct { 298 | Name string 299 | Usage string 300 | EnvVar string 301 | Hidden bool 302 | Value int 303 | Destination *int 304 | } 305 | 306 | // String returns a readable representation of this value 307 | // (for usage defaults) 308 | func (f IntFlag) String() string { 309 | return FlagStringer(f) 310 | } 311 | 312 | // GetName returns the name of the flag 313 | func (f IntFlag) GetName() string { 314 | return f.Name 315 | } 316 | 317 | // Int looks up the value of a local IntFlag, returns 318 | // 0 if not found 319 | func (c *Context) Int(name string) int { 320 | return lookupInt(name, c.flagSet) 321 | } 322 | 323 | // GlobalInt looks up the value of a global IntFlag, returns 324 | // 0 if not found 325 | func (c *Context) GlobalInt(name string) int { 326 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 327 | return lookupInt(name, fs) 328 | } 329 | return 0 330 | } 331 | 332 | func lookupInt(name string, set *flag.FlagSet) int { 333 | f := set.Lookup(name) 334 | if f != nil { 335 | parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) 336 | if err != nil { 337 | return 0 338 | } 339 | return int(parsed) 340 | } 341 | return 0 342 | } 343 | 344 | // IntSliceFlag is a flag with type *IntSlice 345 | type IntSliceFlag struct { 346 | Name string 347 | Usage string 348 | EnvVar string 349 | Hidden bool 350 | Value *IntSlice 351 | } 352 | 353 | // String returns a readable representation of this value 354 | // (for usage defaults) 355 | func (f IntSliceFlag) String() string { 356 | return FlagStringer(f) 357 | } 358 | 359 | // GetName returns the name of the flag 360 | func (f IntSliceFlag) GetName() string { 361 | return f.Name 362 | } 363 | 364 | // IntSlice looks up the value of a local IntSliceFlag, returns 365 | // nil if not found 366 | func (c *Context) IntSlice(name string) []int { 367 | return lookupIntSlice(name, c.flagSet) 368 | } 369 | 370 | // GlobalIntSlice looks up the value of a global IntSliceFlag, returns 371 | // nil if not found 372 | func (c *Context) GlobalIntSlice(name string) []int { 373 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 374 | return lookupIntSlice(name, fs) 375 | } 376 | return nil 377 | } 378 | 379 | func lookupIntSlice(name string, set *flag.FlagSet) []int { 380 | f := set.Lookup(name) 381 | if f != nil { 382 | parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) 383 | if err != nil { 384 | return nil 385 | } 386 | return parsed 387 | } 388 | return nil 389 | } 390 | 391 | // Int64SliceFlag is a flag with type *Int64Slice 392 | type Int64SliceFlag struct { 393 | Name string 394 | Usage string 395 | EnvVar string 396 | Hidden bool 397 | Value *Int64Slice 398 | } 399 | 400 | // String returns a readable representation of this value 401 | // (for usage defaults) 402 | func (f Int64SliceFlag) String() string { 403 | return FlagStringer(f) 404 | } 405 | 406 | // GetName returns the name of the flag 407 | func (f Int64SliceFlag) GetName() string { 408 | return f.Name 409 | } 410 | 411 | // Int64Slice looks up the value of a local Int64SliceFlag, returns 412 | // nil if not found 413 | func (c *Context) Int64Slice(name string) []int64 { 414 | return lookupInt64Slice(name, c.flagSet) 415 | } 416 | 417 | // GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns 418 | // nil if not found 419 | func (c *Context) GlobalInt64Slice(name string) []int64 { 420 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 421 | return lookupInt64Slice(name, fs) 422 | } 423 | return nil 424 | } 425 | 426 | func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { 427 | f := set.Lookup(name) 428 | if f != nil { 429 | parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) 430 | if err != nil { 431 | return nil 432 | } 433 | return parsed 434 | } 435 | return nil 436 | } 437 | 438 | // StringFlag is a flag with type string 439 | type StringFlag struct { 440 | Name string 441 | Usage string 442 | EnvVar string 443 | Hidden bool 444 | Value string 445 | Destination *string 446 | } 447 | 448 | // String returns a readable representation of this value 449 | // (for usage defaults) 450 | func (f StringFlag) String() string { 451 | return FlagStringer(f) 452 | } 453 | 454 | // GetName returns the name of the flag 455 | func (f StringFlag) GetName() string { 456 | return f.Name 457 | } 458 | 459 | // String looks up the value of a local StringFlag, returns 460 | // "" if not found 461 | func (c *Context) String(name string) string { 462 | return lookupString(name, c.flagSet) 463 | } 464 | 465 | // GlobalString looks up the value of a global StringFlag, returns 466 | // "" if not found 467 | func (c *Context) GlobalString(name string) string { 468 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 469 | return lookupString(name, fs) 470 | } 471 | return "" 472 | } 473 | 474 | func lookupString(name string, set *flag.FlagSet) string { 475 | f := set.Lookup(name) 476 | if f != nil { 477 | parsed, err := f.Value.String(), error(nil) 478 | if err != nil { 479 | return "" 480 | } 481 | return parsed 482 | } 483 | return "" 484 | } 485 | 486 | // StringSliceFlag is a flag with type *StringSlice 487 | type StringSliceFlag struct { 488 | Name string 489 | Usage string 490 | EnvVar string 491 | Hidden bool 492 | Value *StringSlice 493 | } 494 | 495 | // String returns a readable representation of this value 496 | // (for usage defaults) 497 | func (f StringSliceFlag) String() string { 498 | return FlagStringer(f) 499 | } 500 | 501 | // GetName returns the name of the flag 502 | func (f StringSliceFlag) GetName() string { 503 | return f.Name 504 | } 505 | 506 | // StringSlice looks up the value of a local StringSliceFlag, returns 507 | // nil if not found 508 | func (c *Context) StringSlice(name string) []string { 509 | return lookupStringSlice(name, c.flagSet) 510 | } 511 | 512 | // GlobalStringSlice looks up the value of a global StringSliceFlag, returns 513 | // nil if not found 514 | func (c *Context) GlobalStringSlice(name string) []string { 515 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 516 | return lookupStringSlice(name, fs) 517 | } 518 | return nil 519 | } 520 | 521 | func lookupStringSlice(name string, set *flag.FlagSet) []string { 522 | f := set.Lookup(name) 523 | if f != nil { 524 | parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) 525 | if err != nil { 526 | return nil 527 | } 528 | return parsed 529 | } 530 | return nil 531 | } 532 | 533 | // Uint64Flag is a flag with type uint64 534 | type Uint64Flag struct { 535 | Name string 536 | Usage string 537 | EnvVar string 538 | Hidden bool 539 | Value uint64 540 | Destination *uint64 541 | } 542 | 543 | // String returns a readable representation of this value 544 | // (for usage defaults) 545 | func (f Uint64Flag) String() string { 546 | return FlagStringer(f) 547 | } 548 | 549 | // GetName returns the name of the flag 550 | func (f Uint64Flag) GetName() string { 551 | return f.Name 552 | } 553 | 554 | // Uint64 looks up the value of a local Uint64Flag, returns 555 | // 0 if not found 556 | func (c *Context) Uint64(name string) uint64 { 557 | return lookupUint64(name, c.flagSet) 558 | } 559 | 560 | // GlobalUint64 looks up the value of a global Uint64Flag, returns 561 | // 0 if not found 562 | func (c *Context) GlobalUint64(name string) uint64 { 563 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 564 | return lookupUint64(name, fs) 565 | } 566 | return 0 567 | } 568 | 569 | func lookupUint64(name string, set *flag.FlagSet) uint64 { 570 | f := set.Lookup(name) 571 | if f != nil { 572 | parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) 573 | if err != nil { 574 | return 0 575 | } 576 | return parsed 577 | } 578 | return 0 579 | } 580 | 581 | // UintFlag is a flag with type uint 582 | type UintFlag struct { 583 | Name string 584 | Usage string 585 | EnvVar string 586 | Hidden bool 587 | Value uint 588 | Destination *uint 589 | } 590 | 591 | // String returns a readable representation of this value 592 | // (for usage defaults) 593 | func (f UintFlag) String() string { 594 | return FlagStringer(f) 595 | } 596 | 597 | // GetName returns the name of the flag 598 | func (f UintFlag) GetName() string { 599 | return f.Name 600 | } 601 | 602 | // Uint looks up the value of a local UintFlag, returns 603 | // 0 if not found 604 | func (c *Context) Uint(name string) uint { 605 | return lookupUint(name, c.flagSet) 606 | } 607 | 608 | // GlobalUint looks up the value of a global UintFlag, returns 609 | // 0 if not found 610 | func (c *Context) GlobalUint(name string) uint { 611 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 612 | return lookupUint(name, fs) 613 | } 614 | return 0 615 | } 616 | 617 | func lookupUint(name string, set *flag.FlagSet) uint { 618 | f := set.Lookup(name) 619 | if f != nil { 620 | parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) 621 | if err != nil { 622 | return 0 623 | } 624 | return uint(parsed) 625 | } 626 | return 0 627 | } 628 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/funcs.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // BashCompleteFunc is an action to execute when the bash-completion flag is set 4 | type BashCompleteFunc func(*Context) 5 | 6 | // BeforeFunc is an action to execute before any subcommands are run, but after 7 | // the context is ready if a non-nil error is returned, no subcommands are run 8 | type BeforeFunc func(*Context) error 9 | 10 | // AfterFunc is an action to execute after any subcommands are run, but after the 11 | // subcommand has finished it is run even if Action() panics 12 | type AfterFunc func(*Context) error 13 | 14 | // ActionFunc is the action to execute when no subcommands are specified 15 | type ActionFunc func(*Context) error 16 | 17 | // CommandNotFoundFunc is executed if the proper command cannot be found 18 | type CommandNotFoundFunc func(*Context, string) 19 | 20 | // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying 21 | // customized usage error messages. This function is able to replace the 22 | // original error messages. If this function is not set, the "Incorrect usage" 23 | // is displayed and the execution is interrupted. 24 | type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error 25 | 26 | // FlagStringFunc is used by the help generation to display a flag, which is 27 | // expected to be a single line. 28 | type FlagStringFunc func(Flag) string 29 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/generate-flag-types: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | The flag types that ship with the cli library have many things in common, and 4 | so we can take advantage of the `go generate` command to create much of the 5 | source code from a list of definitions. These definitions attempt to cover 6 | the parts that vary between flag types, and should evolve as needed. 7 | 8 | An example of the minimum definition needed is: 9 | 10 | { 11 | "name": "SomeType", 12 | "type": "sometype", 13 | "context_default": "nil" 14 | } 15 | 16 | In this example, the code generated for the `cli` package will include a type 17 | named `SomeTypeFlag` that is expected to wrap a value of type `sometype`. 18 | Fetching values by name via `*cli.Context` will default to a value of `nil`. 19 | 20 | A more complete, albeit somewhat redundant, example showing all available 21 | definition keys is: 22 | 23 | { 24 | "name": "VeryMuchType", 25 | "type": "*VeryMuchType", 26 | "value": true, 27 | "dest": false, 28 | "doctail": " which really only wraps a []float64, oh well!", 29 | "context_type": "[]float64", 30 | "context_default": "nil", 31 | "parser": "parseVeryMuchType(f.Value.String())", 32 | "parser_cast": "[]float64(parsed)" 33 | } 34 | 35 | The meaning of each field is as follows: 36 | 37 | name (string) - The type "name", which will be suffixed with 38 | `Flag` when generating the type definition 39 | for `cli` and the wrapper type for `altsrc` 40 | type (string) - The type that the generated `Flag` type for `cli` 41 | is expected to "contain" as its `.Value` member 42 | value (bool) - Should the generated `cli` type have a `Value` 43 | member? 44 | dest (bool) - Should the generated `cli` type support a 45 | destination pointer? 46 | doctail (string) - Additional docs for the `cli` flag type comment 47 | context_type (string) - The literal type used in the `*cli.Context` 48 | reader func signature 49 | context_default (string) - The literal value used as the default by the 50 | `*cli.Context` reader funcs when no value is 51 | present 52 | parser (string) - Literal code used to parse the flag `f`, 53 | expected to have a return signature of 54 | (value, error) 55 | parser_cast (string) - Literal code used to cast the `parsed` value 56 | returned from the `parser` code 57 | """ 58 | 59 | from __future__ import print_function, unicode_literals 60 | 61 | import argparse 62 | import json 63 | import os 64 | import subprocess 65 | import sys 66 | import tempfile 67 | import textwrap 68 | 69 | 70 | class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter, 71 | argparse.RawDescriptionHelpFormatter): 72 | pass 73 | 74 | 75 | def main(sysargs=sys.argv[:]): 76 | parser = argparse.ArgumentParser( 77 | description='Generate flag type code!', 78 | formatter_class=_FancyFormatter) 79 | parser.add_argument( 80 | 'package', 81 | type=str, default='cli', choices=_WRITEFUNCS.keys(), 82 | help='Package for which flag types will be generated' 83 | ) 84 | parser.add_argument( 85 | '-i', '--in-json', 86 | type=argparse.FileType('r'), 87 | default=sys.stdin, 88 | help='Input JSON file which defines each type to be generated' 89 | ) 90 | parser.add_argument( 91 | '-o', '--out-go', 92 | type=argparse.FileType('w'), 93 | default=sys.stdout, 94 | help='Output file/stream to which generated source will be written' 95 | ) 96 | parser.epilog = __doc__ 97 | 98 | args = parser.parse_args(sysargs[1:]) 99 | _generate_flag_types(_WRITEFUNCS[args.package], args.out_go, args.in_json) 100 | return 0 101 | 102 | 103 | def _generate_flag_types(writefunc, output_go, input_json): 104 | types = json.load(input_json) 105 | 106 | tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False) 107 | writefunc(tmp, types) 108 | tmp.close() 109 | 110 | new_content = subprocess.check_output( 111 | ['goimports', tmp.name] 112 | ).decode('utf-8') 113 | 114 | print(new_content, file=output_go, end='') 115 | output_go.flush() 116 | os.remove(tmp.name) 117 | 118 | 119 | def _set_typedef_defaults(typedef): 120 | typedef.setdefault('doctail', '') 121 | typedef.setdefault('context_type', typedef['type']) 122 | typedef.setdefault('dest', True) 123 | typedef.setdefault('value', True) 124 | typedef.setdefault('parser', 'f.Value, error(nil)') 125 | typedef.setdefault('parser_cast', 'parsed') 126 | 127 | 128 | def _write_cli_flag_types(outfile, types): 129 | _fwrite(outfile, """\ 130 | package cli 131 | 132 | // WARNING: This file is generated! 133 | 134 | """) 135 | 136 | for typedef in types: 137 | _set_typedef_defaults(typedef) 138 | 139 | _fwrite(outfile, """\ 140 | // {name}Flag is a flag with type {type}{doctail} 141 | type {name}Flag struct {{ 142 | Name string 143 | Usage string 144 | EnvVar string 145 | Hidden bool 146 | """.format(**typedef)) 147 | 148 | if typedef['value']: 149 | _fwrite(outfile, """\ 150 | Value {type} 151 | """.format(**typedef)) 152 | 153 | if typedef['dest']: 154 | _fwrite(outfile, """\ 155 | Destination *{type} 156 | """.format(**typedef)) 157 | 158 | _fwrite(outfile, "\n}\n\n") 159 | 160 | _fwrite(outfile, """\ 161 | // String returns a readable representation of this value 162 | // (for usage defaults) 163 | func (f {name}Flag) String() string {{ 164 | return FlagStringer(f) 165 | }} 166 | 167 | // GetName returns the name of the flag 168 | func (f {name}Flag) GetName() string {{ 169 | return f.Name 170 | }} 171 | 172 | // {name} looks up the value of a local {name}Flag, returns 173 | // {context_default} if not found 174 | func (c *Context) {name}(name string) {context_type} {{ 175 | return lookup{name}(name, c.flagSet) 176 | }} 177 | 178 | // Global{name} looks up the value of a global {name}Flag, returns 179 | // {context_default} if not found 180 | func (c *Context) Global{name}(name string) {context_type} {{ 181 | if fs := lookupGlobalFlagSet(name, c); fs != nil {{ 182 | return lookup{name}(name, fs) 183 | }} 184 | return {context_default} 185 | }} 186 | 187 | func lookup{name}(name string, set *flag.FlagSet) {context_type} {{ 188 | f := set.Lookup(name) 189 | if f != nil {{ 190 | parsed, err := {parser} 191 | if err != nil {{ 192 | return {context_default} 193 | }} 194 | return {parser_cast} 195 | }} 196 | return {context_default} 197 | }} 198 | """.format(**typedef)) 199 | 200 | 201 | def _write_altsrc_flag_types(outfile, types): 202 | _fwrite(outfile, """\ 203 | package altsrc 204 | 205 | import ( 206 | "gopkg.in/urfave/cli.v1" 207 | ) 208 | 209 | // WARNING: This file is generated! 210 | 211 | """) 212 | 213 | for typedef in types: 214 | _set_typedef_defaults(typedef) 215 | 216 | _fwrite(outfile, """\ 217 | // {name}Flag is the flag type that wraps cli.{name}Flag to allow 218 | // for other values to be specified 219 | type {name}Flag struct {{ 220 | cli.{name}Flag 221 | set *flag.FlagSet 222 | }} 223 | 224 | // New{name}Flag creates a new {name}Flag 225 | func New{name}Flag(fl cli.{name}Flag) *{name}Flag {{ 226 | return &{name}Flag{{{name}Flag: fl, set: nil}} 227 | }} 228 | 229 | // Apply saves the flagSet for later usage calls, then calls the 230 | // wrapped {name}Flag.Apply 231 | func (f *{name}Flag) Apply(set *flag.FlagSet) {{ 232 | f.set = set 233 | f.{name}Flag.Apply(set) 234 | }} 235 | 236 | // ApplyWithError saves the flagSet for later usage calls, then calls the 237 | // wrapped {name}Flag.ApplyWithError 238 | func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{ 239 | f.set = set 240 | return f.{name}Flag.ApplyWithError(set) 241 | }} 242 | """.format(**typedef)) 243 | 244 | 245 | def _fwrite(outfile, text): 246 | print(textwrap.dedent(text), end='', file=outfile) 247 | 248 | 249 | _WRITEFUNCS = { 250 | 'cli': _write_cli_flag_types, 251 | 'altsrc': _write_altsrc_flag_types 252 | } 253 | 254 | if __name__ == '__main__': 255 | sys.exit(main()) 256 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/help.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | "text/tabwriter" 9 | "text/template" 10 | ) 11 | 12 | // AppHelpTemplate is the text template for the Default help topic. 13 | // cli.go uses text/template to render templates. You can 14 | // render custom help text by setting this variable. 15 | var AppHelpTemplate = `NAME: 16 | {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} 17 | 18 | USAGE: 19 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} 20 | 21 | VERSION: 22 | {{.Version}}{{end}}{{end}}{{if .Description}} 23 | 24 | DESCRIPTION: 25 | {{.Description}}{{end}}{{if len .Authors}} 26 | 27 | AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: 28 | {{range $index, $author := .Authors}}{{if $index}} 29 | {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} 30 | 31 | COMMANDS:{{range .VisibleCategories}}{{if .Name}} 32 | {{.Name}}:{{end}}{{range .VisibleCommands}} 33 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} 34 | 35 | GLOBAL OPTIONS: 36 | {{range $index, $option := .VisibleFlags}}{{if $index}} 37 | {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} 38 | 39 | COPYRIGHT: 40 | {{.Copyright}}{{end}} 41 | ` 42 | 43 | // CommandHelpTemplate is the text template for the command help topic. 44 | // cli.go uses text/template to render templates. You can 45 | // render custom help text by setting this variable. 46 | var CommandHelpTemplate = `NAME: 47 | {{.HelpName}} - {{.Usage}} 48 | 49 | USAGE: 50 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} 51 | 52 | CATEGORY: 53 | {{.Category}}{{end}}{{if .Description}} 54 | 55 | DESCRIPTION: 56 | {{.Description}}{{end}}{{if .VisibleFlags}} 57 | 58 | OPTIONS: 59 | {{range .VisibleFlags}}{{.}} 60 | {{end}}{{end}} 61 | ` 62 | 63 | // SubcommandHelpTemplate is the text template for the subcommand help topic. 64 | // cli.go uses text/template to render templates. You can 65 | // render custom help text by setting this variable. 66 | var SubcommandHelpTemplate = `NAME: 67 | {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} 68 | 69 | USAGE: 70 | {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} 71 | 72 | COMMANDS:{{range .VisibleCategories}}{{if .Name}} 73 | {{.Name}}:{{end}}{{range .VisibleCommands}} 74 | {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} 75 | {{end}}{{if .VisibleFlags}} 76 | OPTIONS: 77 | {{range .VisibleFlags}}{{.}} 78 | {{end}}{{end}} 79 | ` 80 | 81 | var helpCommand = Command{ 82 | Name: "help", 83 | Aliases: []string{"h"}, 84 | Usage: "Shows a list of commands or help for one command", 85 | ArgsUsage: "[command]", 86 | Action: func(c *Context) error { 87 | args := c.Args() 88 | if args.Present() { 89 | return ShowCommandHelp(c, args.First()) 90 | } 91 | 92 | ShowAppHelp(c) 93 | return nil 94 | }, 95 | } 96 | 97 | var helpSubcommand = Command{ 98 | Name: "help", 99 | Aliases: []string{"h"}, 100 | Usage: "Shows a list of commands or help for one command", 101 | ArgsUsage: "[command]", 102 | Action: func(c *Context) error { 103 | args := c.Args() 104 | if args.Present() { 105 | return ShowCommandHelp(c, args.First()) 106 | } 107 | 108 | return ShowSubcommandHelp(c) 109 | }, 110 | } 111 | 112 | // Prints help for the App or Command 113 | type helpPrinter func(w io.Writer, templ string, data interface{}) 114 | 115 | // Prints help for the App or Command with custom template function. 116 | type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) 117 | 118 | // HelpPrinter is a function that writes the help output. If not set a default 119 | // is used. The function signature is: 120 | // func(w io.Writer, templ string, data interface{}) 121 | var HelpPrinter helpPrinter = printHelp 122 | 123 | // HelpPrinterCustom is same as HelpPrinter but 124 | // takes a custom function for template function map. 125 | var HelpPrinterCustom helpPrinterCustom = printHelpCustom 126 | 127 | // VersionPrinter prints the version for the App 128 | var VersionPrinter = printVersion 129 | 130 | // ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. 131 | func ShowAppHelpAndExit(c *Context, exitCode int) { 132 | ShowAppHelp(c) 133 | os.Exit(exitCode) 134 | } 135 | 136 | // ShowAppHelp is an action that displays the help. 137 | func ShowAppHelp(c *Context) (err error) { 138 | if c.App.CustomAppHelpTemplate == "" { 139 | HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) 140 | return 141 | } 142 | customAppData := func() map[string]interface{} { 143 | if c.App.ExtraInfo == nil { 144 | return nil 145 | } 146 | return map[string]interface{}{ 147 | "ExtraInfo": c.App.ExtraInfo, 148 | } 149 | } 150 | HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) 151 | return nil 152 | } 153 | 154 | // DefaultAppComplete prints the list of subcommands as the default app completion method 155 | func DefaultAppComplete(c *Context) { 156 | for _, command := range c.App.Commands { 157 | if command.Hidden { 158 | continue 159 | } 160 | for _, name := range command.Names() { 161 | fmt.Fprintln(c.App.Writer, name) 162 | } 163 | } 164 | } 165 | 166 | // ShowCommandHelpAndExit - exits with code after showing help 167 | func ShowCommandHelpAndExit(c *Context, command string, code int) { 168 | ShowCommandHelp(c, command) 169 | os.Exit(code) 170 | } 171 | 172 | // ShowCommandHelp prints help for the given command 173 | func ShowCommandHelp(ctx *Context, command string) error { 174 | // show the subcommand help for a command with subcommands 175 | if command == "" { 176 | HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) 177 | return nil 178 | } 179 | 180 | for _, c := range ctx.App.Commands { 181 | if c.HasName(command) { 182 | if c.CustomHelpTemplate != "" { 183 | HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) 184 | } else { 185 | HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) 186 | } 187 | return nil 188 | } 189 | } 190 | 191 | if ctx.App.CommandNotFound == nil { 192 | return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) 193 | } 194 | 195 | ctx.App.CommandNotFound(ctx, command) 196 | return nil 197 | } 198 | 199 | // ShowSubcommandHelp prints help for the given subcommand 200 | func ShowSubcommandHelp(c *Context) error { 201 | return ShowCommandHelp(c, c.Command.Name) 202 | } 203 | 204 | // ShowVersion prints the version number of the App 205 | func ShowVersion(c *Context) { 206 | VersionPrinter(c) 207 | } 208 | 209 | func printVersion(c *Context) { 210 | fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) 211 | } 212 | 213 | // ShowCompletions prints the lists of commands within a given context 214 | func ShowCompletions(c *Context) { 215 | a := c.App 216 | if a != nil && a.BashComplete != nil { 217 | a.BashComplete(c) 218 | } 219 | } 220 | 221 | // ShowCommandCompletions prints the custom completions for a given command 222 | func ShowCommandCompletions(ctx *Context, command string) { 223 | c := ctx.App.Command(command) 224 | if c != nil && c.BashComplete != nil { 225 | c.BashComplete(ctx) 226 | } 227 | } 228 | 229 | func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { 230 | funcMap := template.FuncMap{ 231 | "join": strings.Join, 232 | } 233 | if customFunc != nil { 234 | for key, value := range customFunc { 235 | funcMap[key] = value 236 | } 237 | } 238 | 239 | w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) 240 | t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) 241 | err := t.Execute(w, data) 242 | if err != nil { 243 | // If the writer is closed, t.Execute will fail, and there's nothing 244 | // we can do to recover. 245 | if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { 246 | fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) 247 | } 248 | return 249 | } 250 | w.Flush() 251 | } 252 | 253 | func printHelp(out io.Writer, templ string, data interface{}) { 254 | printHelpCustom(out, templ, data, nil) 255 | } 256 | 257 | func checkVersion(c *Context) bool { 258 | found := false 259 | if VersionFlag.GetName() != "" { 260 | eachName(VersionFlag.GetName(), func(name string) { 261 | if c.GlobalBool(name) || c.Bool(name) { 262 | found = true 263 | } 264 | }) 265 | } 266 | return found 267 | } 268 | 269 | func checkHelp(c *Context) bool { 270 | found := false 271 | if HelpFlag.GetName() != "" { 272 | eachName(HelpFlag.GetName(), func(name string) { 273 | if c.GlobalBool(name) || c.Bool(name) { 274 | found = true 275 | } 276 | }) 277 | } 278 | return found 279 | } 280 | 281 | func checkCommandHelp(c *Context, name string) bool { 282 | if c.Bool("h") || c.Bool("help") { 283 | ShowCommandHelp(c, name) 284 | return true 285 | } 286 | 287 | return false 288 | } 289 | 290 | func checkSubcommandHelp(c *Context) bool { 291 | if c.Bool("h") || c.Bool("help") { 292 | ShowSubcommandHelp(c) 293 | return true 294 | } 295 | 296 | return false 297 | } 298 | 299 | func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { 300 | if !a.EnableBashCompletion { 301 | return false, arguments 302 | } 303 | 304 | pos := len(arguments) - 1 305 | lastArg := arguments[pos] 306 | 307 | if lastArg != "--"+BashCompletionFlag.GetName() { 308 | return false, arguments 309 | } 310 | 311 | return true, arguments[:pos] 312 | } 313 | 314 | func checkCompletions(c *Context) bool { 315 | if !c.shellComplete { 316 | return false 317 | } 318 | 319 | if args := c.Args(); args.Present() { 320 | name := args.First() 321 | if cmd := c.App.Command(name); cmd != nil { 322 | // let the command handle the completion 323 | return false 324 | } 325 | } 326 | 327 | ShowCompletions(c) 328 | return true 329 | } 330 | 331 | func checkCommandCompletions(c *Context, name string) bool { 332 | if !c.shellComplete { 333 | return false 334 | } 335 | 336 | ShowCommandCompletions(c, name) 337 | return true 338 | } 339 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import argparse 5 | import os 6 | import sys 7 | import tempfile 8 | 9 | from subprocess import check_call, check_output 10 | 11 | 12 | PACKAGE_NAME = os.environ.get( 13 | 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' 14 | ) 15 | 16 | 17 | def main(sysargs=sys.argv[:]): 18 | targets = { 19 | 'vet': _vet, 20 | 'test': _test, 21 | 'gfmrun': _gfmrun, 22 | 'toc': _toc, 23 | 'gen': _gen, 24 | } 25 | 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument( 28 | 'target', nargs='?', choices=tuple(targets.keys()), default='test' 29 | ) 30 | args = parser.parse_args(sysargs[1:]) 31 | 32 | targets[args.target]() 33 | return 0 34 | 35 | 36 | def _test(): 37 | if check_output('go version'.split()).split()[2] < 'go1.2': 38 | _run('go test -v .') 39 | return 40 | 41 | coverprofiles = [] 42 | for subpackage in ['', 'altsrc']: 43 | coverprofile = 'cli.coverprofile' 44 | if subpackage != '': 45 | coverprofile = '{}.coverprofile'.format(subpackage) 46 | 47 | coverprofiles.append(coverprofile) 48 | 49 | _run('go test -v'.split() + [ 50 | '-coverprofile={}'.format(coverprofile), 51 | ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') 52 | ]) 53 | 54 | combined_name = _combine_coverprofiles(coverprofiles) 55 | _run('go tool cover -func={}'.format(combined_name)) 56 | os.remove(combined_name) 57 | 58 | 59 | def _gfmrun(): 60 | go_version = check_output('go version'.split()).split()[2] 61 | if go_version < 'go1.3': 62 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 63 | return 64 | _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) 65 | 66 | 67 | def _vet(): 68 | _run('go vet ./...') 69 | 70 | 71 | def _toc(): 72 | _run('node_modules/.bin/markdown-toc -i README.md') 73 | _run('git diff --exit-code') 74 | 75 | 76 | def _gen(): 77 | go_version = check_output('go version'.split()).split()[2] 78 | if go_version < 'go1.5': 79 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 80 | return 81 | 82 | _run('go generate ./...') 83 | _run('git diff --exit-code') 84 | 85 | 86 | def _run(command): 87 | if hasattr(command, 'split'): 88 | command = command.split() 89 | print('runtests: {}'.format(' '.join(command)), file=sys.stderr) 90 | check_call(command) 91 | 92 | 93 | def _gfmrun_count(): 94 | with open('README.md') as infile: 95 | lines = infile.read().splitlines() 96 | return len(filter(_is_go_runnable, lines)) 97 | 98 | 99 | def _is_go_runnable(line): 100 | return line.startswith('package main') 101 | 102 | 103 | def _combine_coverprofiles(coverprofiles): 104 | combined = tempfile.NamedTemporaryFile( 105 | suffix='.coverprofile', delete=False 106 | ) 107 | combined.write('mode: set\n') 108 | 109 | for coverprofile in coverprofiles: 110 | with open(coverprofile, 'r') as infile: 111 | for line in infile.readlines(): 112 | if not line.startswith('mode: '): 113 | combined.write(line) 114 | 115 | combined.flush() 116 | name = combined.name 117 | combined.close() 118 | return name 119 | 120 | 121 | if __name__ == '__main__': 122 | sys.exit(main()) 123 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "PZ4KJai7DnuJ2YNJ2v2l2BseB1g=", 7 | "path": "github.com/aymerick/raymond", 8 | "revision": "72acac2207479d21dd45898c2a4264246c818148", 9 | "revisionTime": "2016-12-09T22:07:24Z" 10 | }, 11 | { 12 | "checksumSHA1": "Rvn+RH9pwFno1w6W+mhWsj/PxlA=", 13 | "path": "github.com/aymerick/raymond/ast", 14 | "revision": "72acac2207479d21dd45898c2a4264246c818148", 15 | "revisionTime": "2016-12-09T22:07:24Z" 16 | }, 17 | { 18 | "checksumSHA1": "5SJwPK0MYtJt5YiE1BNc9Wl3+S0=", 19 | "path": "github.com/aymerick/raymond/lexer", 20 | "revision": "72acac2207479d21dd45898c2a4264246c818148", 21 | "revisionTime": "2016-12-09T22:07:24Z" 22 | }, 23 | { 24 | "checksumSHA1": "TCu/8QBP8TApLjSt13a7Qjnyxrs=", 25 | "path": "github.com/aymerick/raymond/parser", 26 | "revision": "72acac2207479d21dd45898c2a4264246c818148", 27 | "revisionTime": "2016-12-09T22:07:24Z" 28 | }, 29 | { 30 | "checksumSHA1": "9LeR7BH4PSu8LRDZ8bY7QY1HXJE=", 31 | "path": "github.com/urfave/cli", 32 | "revision": "4b90d79a682b4bf685762c7452db20f2a676ecb2", 33 | "revisionTime": "2017-07-06T19:46:25Z" 34 | } 35 | ], 36 | "rootPath": "github.com/clem109/drone-wechat" 37 | } 38 | --------------------------------------------------------------------------------