Update to golang 1.18, update all vendored libraries

This commit is contained in:
Louis Abel 2022-10-14 18:22:42 -07:00
parent 6ede82b418
commit ddcdad2c0f
Signed by: label
GPG Key ID: 6735C0E1BD65D048
76 changed files with 9296 additions and 1474 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
rpaste

View File

@ -1,3 +1,17 @@
# rpaste # rpaste
rpaste utility written in go. rpaste utility written in go.
This repository is hosted at the [RESF Git Service](https://git.resf.org) with a mirror to [Github](https://github.com/rocky-linux/rpaste).
## Installation
If you are running Rocky Linux, it is available in the extras repository.
```
# dnf install rpaste
```
## Building
If you are wanting to build this utility yourself, minimum golang 1.18 is required. Rocky Linux 8.7 and 9.1 should have this.

9
go.mod
View File

@ -1,8 +1,11 @@
module github.com/rocky-linux/rpaste module github.com/rocky-linux/rpaste
go 1.16 go 1.18
require github.com/urfave/cli/v2 v2.19.2
require ( require (
github.com/urfave/cli/v2 v2.3.0 github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
) )

22
go.sum
View File

@ -1,14 +1,8 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/urfave/cli/v2 v2.19.2 h1:eXu5089gqqiDQKSnFW+H/FhjrxRGztwSxlTsVK7IuqQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/urfave/cli/v2 v2.19.2/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -17,7 +17,7 @@ import (
// Sane defaults and basic info // Sane defaults and basic info
var ( var (
AppName = "rpaste" AppName = "rpaste"
Version = "0.2.0" Version = "0.3.0"
DefaultConf = "/etc/rpaste/rpaste.conf" DefaultConf = "/etc/rpaste/rpaste.conf"
DefaultLexer = "text" DefaultLexer = "text"
DefaultLifeTime = "1hour" DefaultLifeTime = "1hour"

View File

@ -15,7 +15,7 @@ type roffRenderer struct {
extensions blackfriday.Extensions extensions blackfriday.Extensions
listCounters []int listCounters []int
firstHeader bool firstHeader bool
defineTerm bool firstDD bool
listDepth int listDepth int
} }
@ -42,7 +42,8 @@ const (
quoteCloseTag = "\n.RE\n" quoteCloseTag = "\n.RE\n"
listTag = "\n.RS\n" listTag = "\n.RS\n"
listCloseTag = "\n.RE\n" listCloseTag = "\n.RE\n"
arglistTag = "\n.TP\n" dtTag = "\n.TP\n"
dd2Tag = "\n"
tableStart = "\n.TS\nallbox;\n" tableStart = "\n.TS\nallbox;\n"
tableEnd = ".TE\n" tableEnd = ".TE\n"
tableCellStart = "T{\n" tableCellStart = "T{\n"
@ -90,7 +91,7 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering
switch node.Type { switch node.Type {
case blackfriday.Text: case blackfriday.Text:
r.handleText(w, node, entering) escapeSpecialChars(w, node.Literal)
case blackfriday.Softbreak: case blackfriday.Softbreak:
out(w, crTag) out(w, crTag)
case blackfriday.Hardbreak: case blackfriday.Hardbreak:
@ -150,40 +151,21 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering
out(w, codeCloseTag) out(w, codeCloseTag)
case blackfriday.Table: case blackfriday.Table:
r.handleTable(w, node, entering) r.handleTable(w, node, entering)
case blackfriday.TableCell:
r.handleTableCell(w, node, entering)
case blackfriday.TableHead: case blackfriday.TableHead:
case blackfriday.TableBody: case blackfriday.TableBody:
case blackfriday.TableRow: case blackfriday.TableRow:
// no action as cell entries do all the nroff formatting // no action as cell entries do all the nroff formatting
return blackfriday.GoToNext return blackfriday.GoToNext
case blackfriday.TableCell:
r.handleTableCell(w, node, entering)
case blackfriday.HTMLSpan:
// ignore other HTML tags
default: default:
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String()) fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
} }
return walkAction return walkAction
} }
func (r *roffRenderer) handleText(w io.Writer, node *blackfriday.Node, entering bool) {
var (
start, end string
)
// handle special roff table cell text encapsulation
if node.Parent.Type == blackfriday.TableCell {
if len(node.Literal) > 30 {
start = tableCellStart
end = tableCellEnd
} else {
// end rows that aren't terminated by "tableCellEnd" with a cr if end of row
if node.Parent.Next == nil && !node.Parent.IsHeader {
end = crTag
}
}
}
out(w, start)
escapeSpecialChars(w, node.Literal)
out(w, end)
}
func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) { func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
if entering { if entering {
switch node.Level { switch node.Level {
@ -230,15 +212,20 @@ func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering
if node.ListFlags&blackfriday.ListTypeOrdered != 0 { if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1])) out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
r.listCounters[len(r.listCounters)-1]++ r.listCounters[len(r.listCounters)-1]++
} else if node.ListFlags&blackfriday.ListTypeTerm != 0 {
// DT (definition term): line just before DD (see below).
out(w, dtTag)
r.firstDD = true
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 { } else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// state machine for handling terms and following definitions // DD (definition description): line that starts with ": ".
// since blackfriday does not distinguish them properly, nor //
// does it seperate them into separate lists as it should // We have to distinguish between the first DD and the
if !r.defineTerm { // subsequent ones, as there should be no vertical
out(w, arglistTag) // whitespace between the DT and the first DD.
r.defineTerm = true if r.firstDD {
r.firstDD = false
} else { } else {
r.defineTerm = false out(w, dd2Tag)
} }
} else { } else {
out(w, ".IP \\(bu 2\n") out(w, ".IP \\(bu 2\n")
@ -251,7 +238,7 @@ func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering
func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) { func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
if entering { if entering {
out(w, tableStart) out(w, tableStart)
//call walker to count cells (and rows?) so format section can be produced // call walker to count cells (and rows?) so format section can be produced
columns := countColumns(node) columns := countColumns(node)
out(w, strings.Repeat("l ", columns)+"\n") out(w, strings.Repeat("l ", columns)+"\n")
out(w, strings.Repeat("l ", columns)+".\n") out(w, strings.Repeat("l ", columns)+".\n")
@ -261,28 +248,41 @@ func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering
} }
func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) { func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
var (
start, end string
)
if node.IsHeader {
start = codespanTag
end = codespanCloseTag
}
if entering { if entering {
var start string
if node.Prev != nil && node.Prev.Type == blackfriday.TableCell { if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
out(w, "\t"+start) start = "\t"
} else {
out(w, start)
} }
if node.IsHeader {
start += codespanTag
} else if nodeLiteralSize(node) > 30 {
start += tableCellStart
}
out(w, start)
} else { } else {
// need to carriage return if we are at the end of the header row var end string
if node.IsHeader && node.Next == nil { if node.IsHeader {
end = end + crTag end = codespanCloseTag
} else if nodeLiteralSize(node) > 30 {
end = tableCellEnd
}
if node.Next == nil && end != tableCellEnd {
// Last cell: need to carriage return if we are at the end of the
// header row and content isn't wrapped in a "tablecell"
end += crTag
} }
out(w, end) out(w, end)
} }
} }
func nodeLiteralSize(node *blackfriday.Node) int {
total := 0
for n := node.FirstChild; n != nil; n = n.FirstChild {
total += len(n.Literal)
}
return total
}
// because roff format requires knowing the column count before outputting any table // because roff format requires knowing the column count before outputting any table
// data we need to walk a table tree and count the columns // data we need to walk a table tree and count the columns
func countColumns(node *blackfriday.Node) int { func countColumns(node *blackfriday.Node) int {
@ -309,15 +309,6 @@ func out(w io.Writer, output string) {
io.WriteString(w, output) // nolint: errcheck io.WriteString(w, output) // nolint: errcheck
} }
func needsBackslash(c byte) bool {
for _, r := range []byte("-_&\\~") {
if c == r {
return true
}
}
return false
}
func escapeSpecialChars(w io.Writer, text []byte) { func escapeSpecialChars(w io.Writer, text []byte) {
for i := 0; i < len(text); i++ { for i := 0; i < len(text); i++ {
// escape initial apostrophe or period // escape initial apostrophe or period
@ -328,7 +319,7 @@ func escapeSpecialChars(w io.Writer, text []byte) {
// directly copy normal characters // directly copy normal characters
org := i org := i
for i < len(text) && !needsBackslash(text[i]) { for i < len(text) && text[i] != '\\' {
i++ i++
} }
if i > org { if i > org {

View File

@ -1,4 +1,6 @@
Blackfriday [![Build Status](https://travis-ci.org/russross/blackfriday.svg?branch=master)](https://travis-ci.org/russross/blackfriday) Blackfriday
[![Build Status][BuildV2SVG]][BuildV2URL]
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
=========== ===========
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
@ -16,19 +18,21 @@ It started as a translation from C of [Sundown][3].
Installation Installation
------------ ------------
Blackfriday is compatible with any modern Go release. With Go 1.7 and git Blackfriday is compatible with modern Go releases in module mode.
installed: With Go installed:
go get gopkg.in/russross/blackfriday.v2 go get github.com/russross/blackfriday/v2
will download, compile, and install the package into your `$GOPATH` will resolve and add the package to the current development module,
directory hierarchy. Alternatively, you can achieve the same if you then build and install it. Alternatively, you can achieve the same
import it into a project: if you import it in a package:
import "gopkg.in/russross/blackfriday.v2" import "github.com/russross/blackfriday/v2"
and `go get` without parameters. and `go get` without parameters.
Legacy GOPATH mode is unsupported.
Versions Versions
-------- --------
@ -36,13 +40,9 @@ Versions
Currently maintained and recommended version of Blackfriday is `v2`. It's being Currently maintained and recommended version of Blackfriday is `v2`. It's being
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
documentation is available at documentation is available at
https://godoc.org/gopkg.in/russross/blackfriday.v2. https://pkg.go.dev/github.com/russross/blackfriday/v2.
It is `go get`-able via via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`, It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.
but we highly recommend using package management tool like [dep][7] or
[Glide][8] and make use of semantic versioning. With package management you
should import `github.com/russross/blackfriday` and specify that you're using
version 2.0.0.
Version 2 offers a number of improvements over v1: Version 2 offers a number of improvements over v1:
@ -62,6 +62,11 @@ Potential drawbacks:
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
tracking. tracking.
If you are still interested in the legacy `v1`, you can import it from
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
here: https://pkg.go.dev/github.com/russross/blackfriday.
Usage Usage
----- -----
@ -91,7 +96,7 @@ Here's an example of simple usage of Blackfriday together with Bluemonday:
```go ```go
import ( import (
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday" "github.com/russross/blackfriday/v2"
) )
// ... // ...
@ -104,6 +109,8 @@ html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
If you want to customize the set of options, use `blackfriday.WithExtensions`, If you want to customize the set of options, use `blackfriday.WithExtensions`,
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`. `blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
### `blackfriday-tool`
You can also check out `blackfriday-tool` for a more complete example You can also check out `blackfriday-tool` for a more complete example
of how to use it. Download and install it using: of how to use it. Download and install it using:
@ -114,7 +121,7 @@ markdown file using a standalone program. You can also browse the
source directly on github if you are just looking for some example source directly on github if you are just looking for some example
code: code:
* <http://github.com/russross/blackfriday-tool> * <https://github.com/russross/blackfriday-tool>
Note that if you have not already done so, installing Note that if you have not already done so, installing
`blackfriday-tool` will be sufficient to download and install `blackfriday-tool` will be sufficient to download and install
@ -123,6 +130,22 @@ installed in `$GOPATH/bin`. This is a statically-linked binary that
can be copied to wherever you need it without worrying about can be copied to wherever you need it without worrying about
dependencies and library versions. dependencies and library versions.
### Sanitized anchor names
Blackfriday includes an algorithm for creating sanitized anchor names
corresponding to a given input text. This algorithm is used to create
anchors for headings when `AutoHeadingIDs` extension is enabled. The
algorithm has a specification, so that other packages can create
compatible anchor names and links to those anchors.
The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names.
[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to
create compatible links to the anchor names generated by blackfriday.
This algorithm is also implemented in a small standalone package at
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
that want a small package and don't need full functionality of blackfriday.
Features Features
-------- --------
@ -199,6 +222,15 @@ implements the following extensions:
You can use 3 or more backticks to mark the beginning of the You can use 3 or more backticks to mark the beginning of the
block, and the same number to mark the end of the block. block, and the same number to mark the end of the block.
To preserve classes of fenced code blocks while using the bluemonday
HTML sanitizer, use the following policy:
```go
p := bluemonday.UGCPolicy()
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
html := p.SanitizeBytes(unsafe)
```
* **Definition lists**. A simple definition list is made of a single-line * **Definition lists**. A simple definition list is made of a single-line
term followed by a colon and the definition for that term. term followed by a colon and the definition for that term.
@ -250,7 +282,7 @@ Other renderers
Blackfriday is structured to allow alternative rendering engines. Here Blackfriday is structured to allow alternative rendering engines. Here
are a few of note: are a few of note:
* [github_flavored_markdown](https://godoc.org/github.com/shurcooL/github_flavored_markdown): * [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
provides a GitHub Flavored Markdown renderer with fenced code block provides a GitHub Flavored Markdown renderer with fenced code block
highlighting, clickable heading anchor links. highlighting, clickable heading anchor links.
@ -261,20 +293,28 @@ are a few of note:
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt, * [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
but for markdown. but for markdown.
* [LaTeX output](https://github.com/Ambrevar/Blackfriday-LaTeX): * [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
renders output as LaTeX. renders output as LaTeX.
* [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
integration with the [Chroma](https://github.com/alecthomas/chroma) code
highlighting library. bfchroma is only compatible with v2 of Blackfriday and
provides a drop-in renderer ready to use with Blackfriday, as well as
options and means for further customization.
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer. * [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
* [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style
Todo
TODO
---- ----
* More unit testing * More unit testing
* Improve unicode support. It does not understand all unicode * Improve Unicode support. It does not understand all Unicode
rules (about what constitutes a letter, a punctuation symbol, rules (about what constitutes a letter, a punctuation symbol,
etc.), so it may fail to detect word boundaries correctly in etc.), so it may fail to detect word boundaries correctly in
some instances. It is safe on all utf-8 input. some instances. It is safe on all UTF-8 input.
License License
@ -286,6 +326,10 @@ License
[1]: https://daringfireball.net/projects/markdown/ "Markdown" [1]: https://daringfireball.net/projects/markdown/ "Markdown"
[2]: https://golang.org/ "Go Language" [2]: https://golang.org/ "Go Language"
[3]: https://github.com/vmg/sundown "Sundown" [3]: https://github.com/vmg/sundown "Sundown"
[4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func" [4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday" [5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
[6]: https://labix.org/gopkg.in "gopkg.in"
[BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
[BuildV2URL]: https://travis-ci.org/russross/blackfriday
[PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
[PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2

View File

@ -18,8 +18,7 @@ import (
"html" "html"
"regexp" "regexp"
"strings" "strings"
"unicode"
"github.com/shurcooL/sanitized_anchor_name"
) )
const ( const (
@ -259,7 +258,7 @@ func (p *Markdown) prefixHeading(data []byte) int {
} }
if end > i { if end > i {
if id == "" && p.extensions&AutoHeadingIDs != 0 { if id == "" && p.extensions&AutoHeadingIDs != 0 {
id = sanitized_anchor_name.Create(string(data[i:end])) id = SanitizedAnchorName(string(data[i:end]))
} }
block := p.addBlock(Heading, data[i:end]) block := p.addBlock(Heading, data[i:end])
block.HeadingID = id block.HeadingID = id
@ -673,6 +672,7 @@ func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
if beg == 0 || beg >= len(data) { if beg == 0 || beg >= len(data) {
return 0 return 0
} }
fenceLength := beg - 1
var work bytes.Buffer var work bytes.Buffer
work.Write([]byte(info)) work.Write([]byte(info))
@ -706,6 +706,7 @@ func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
if doRender { if doRender {
block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer
block.IsFenced = true block.IsFenced = true
block.FenceLength = fenceLength
finalizeCodeBlock(block) finalizeCodeBlock(block)
} }
@ -1503,7 +1504,7 @@ func (p *Markdown) paragraph(data []byte) int {
id := "" id := ""
if p.extensions&AutoHeadingIDs != 0 { if p.extensions&AutoHeadingIDs != 0 {
id = sanitized_anchor_name.Create(string(data[prev:eol])) id = SanitizedAnchorName(string(data[prev:eol]))
} }
block := p.addBlock(Heading, data[prev:eol]) block := p.addBlock(Heading, data[prev:eol])
@ -1588,3 +1589,24 @@ func skipUntilChar(text []byte, start int, char byte) int {
} }
return i return i
} }
// SanitizedAnchorName returns a sanitized anchor name for the given text.
//
// It implements the algorithm specified in the package comment.
func SanitizedAnchorName(text string) string {
var anchorName []rune
futureDash := false
for _, r := range text {
switch {
case unicode.IsLetter(r) || unicode.IsNumber(r):
if futureDash && len(anchorName) > 0 {
anchorName = append(anchorName, '-')
}
futureDash = false
anchorName = append(anchorName, unicode.ToLower(r))
default:
futureDash = true
}
}
return string(anchorName)
}

View File

@ -15,4 +15,32 @@
// //
// If you're interested in calling Blackfriday from command line, see // If you're interested in calling Blackfriday from command line, see
// https://github.com/russross/blackfriday-tool. // https://github.com/russross/blackfriday-tool.
//
// Sanitized Anchor Names
//
// Blackfriday includes an algorithm for creating sanitized anchor names
// corresponding to a given input text. This algorithm is used to create
// anchors for headings when AutoHeadingIDs extension is enabled. The
// algorithm is specified below, so that other packages can create
// compatible anchor names and links to those anchors.
//
// The algorithm iterates over the input text, interpreted as UTF-8,
// one Unicode code point (rune) at a time. All runes that are letters (category L)
// or numbers (category N) are considered valid characters. They are mapped to
// lower case, and included in the output. All other runes are considered
// invalid characters. Invalid characters that precede the first valid character,
// as well as invalid character that follow the last valid character
// are dropped completely. All other sequences of invalid characters
// between two valid characters are replaced with a single dash character '-'.
//
// SanitizedAnchorName exposes this functionality, and can be used to
// create compatible links to the anchor names generated by blackfriday.
// This algorithm is also implemented in a small standalone package at
// github.com/shurcooL/sanitized_anchor_name. It can be useful for clients
// that want a small package and don't need full functionality of blackfriday.
package blackfriday package blackfriday
// NOTE: Keep Sanitized Anchor Name algorithm in sync with package
// github.com/shurcooL/sanitized_anchor_name.
// Otherwise, users of sanitized_anchor_name will get anchor names
// that are incompatible with those generated by blackfriday.

2236
vendor/github.com/russross/blackfriday/v2/entities.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,14 +13,28 @@ var htmlEscaper = [256][]byte{
} }
func escapeHTML(w io.Writer, s []byte) { func escapeHTML(w io.Writer, s []byte) {
escapeEntities(w, s, false)
}
func escapeAllHTML(w io.Writer, s []byte) {
escapeEntities(w, s, true)
}
func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) {
var start, end int var start, end int
for end < len(s) { for end < len(s) {
escSeq := htmlEscaper[s[end]] escSeq := htmlEscaper[s[end]]
if escSeq != nil { if escSeq != nil {
isEntity, entityEnd := nodeIsEntity(s, end)
if isEntity && !escapeValidEntities {
w.Write(s[start : entityEnd+1])
start = entityEnd + 1
} else {
w.Write(s[start:end]) w.Write(s[start:end])
w.Write(escSeq) w.Write(escSeq)
start = end + 1 start = end + 1
} }
}
end++ end++
} }
if start < len(s) && end <= len(s) { if start < len(s) && end <= len(s) {
@ -28,6 +42,28 @@ func escapeHTML(w io.Writer, s []byte) {
} }
} }
func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) {
isEntity = false
endEntityPos = end + 1
if s[end] == '&' {
for endEntityPos < len(s) {
if s[endEntityPos] == ';' {
if entities[string(s[end:endEntityPos+1])] {
isEntity = true
break
}
}
if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' {
break
}
endEntityPos++
}
}
return isEntity, endEntityPos
}
func escLink(w io.Writer, text []byte) { func escLink(w io.Writer, text []byte) {
unesc := html.UnescapeString(string(text)) unesc := html.UnescapeString(string(text))
escapeHTML(w, []byte(unesc)) escapeHTML(w, []byte(unesc))

View File

@ -1 +0,0 @@
module github.com/russross/blackfriday/v2

View File

@ -132,7 +132,10 @@ func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
} }
if params.FootnoteReturnLinkContents == "" { if params.FootnoteReturnLinkContents == "" {
params.FootnoteReturnLinkContents = `<sup>[return]</sup>` // U+FE0E is VARIATION SELECTOR-15.
// It suppresses automatic emoji presentation of the preceding
// U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS.
params.FootnoteReturnLinkContents = "<span aria-label='Return'>↩\ufe0e</span>"
} }
return &HTMLRenderer{ return &HTMLRenderer{
@ -616,7 +619,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
} }
case Code: case Code:
r.out(w, codeTag) r.out(w, codeTag)
escapeHTML(w, node.Literal) escapeAllHTML(w, node.Literal)
r.out(w, codeCloseTag) r.out(w, codeCloseTag)
case Document: case Document:
break break
@ -762,7 +765,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
r.cr(w) r.cr(w)
r.out(w, preTag) r.out(w, preTag)
r.tag(w, codeTag[:len(codeTag)-1], attrs) r.tag(w, codeTag[:len(codeTag)-1], attrs)
escapeHTML(w, node.Literal) escapeAllHTML(w, node.Literal)
r.out(w, codeCloseTag) r.out(w, codeCloseTag)
r.out(w, preCloseTag) r.out(w, preCloseTag)
if node.Parent.Type != Item { if node.Parent.Type != Item {

View File

@ -278,7 +278,7 @@ func link(p *Markdown, data []byte, offset int) (int, *Node) {
case data[i] == '\n': case data[i] == '\n':
textHasNl = true textHasNl = true
case data[i-1] == '\\': case isBackslashEscaped(data, i):
continue continue
case data[i] == '[': case data[i] == '[':

View File

@ -199,7 +199,8 @@ func (n *Node) InsertBefore(sibling *Node) {
} }
} }
func (n *Node) isContainer() bool { // IsContainer returns true if 'n' can contain children.
func (n *Node) IsContainer() bool {
switch n.Type { switch n.Type {
case Document: case Document:
fallthrough fallthrough
@ -238,6 +239,11 @@ func (n *Node) isContainer() bool {
} }
} }
// IsLeaf returns true if 'n' is a leaf node.
func (n *Node) IsLeaf() bool {
return !n.IsContainer()
}
func (n *Node) canContain(t NodeType) bool { func (n *Node) canContain(t NodeType) bool {
if n.Type == List { if n.Type == List {
return t == Item return t == Item
@ -309,11 +315,11 @@ func newNodeWalker(root *Node) *nodeWalker {
} }
func (nw *nodeWalker) next() { func (nw *nodeWalker) next() {
if (!nw.current.isContainer() || !nw.entering) && nw.current == nw.root { if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root {
nw.current = nil nw.current = nil
return return
} }
if nw.entering && nw.current.isContainer() { if nw.entering && nw.current.IsContainer() {
if nw.current.FirstChild != nil { if nw.current.FirstChild != nil {
nw.current = nw.current.FirstChild nw.current = nw.current.FirstChild
nw.entering = true nw.entering = true

View File

@ -1,16 +0,0 @@
sudo: false
language: go
go:
- 1.x
- master
matrix:
allow_failures:
- go: master
fast_finish: true
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d -s .)
- go tool vet .
- go test -v -race ./...

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2015 Dmitri Shuralyov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,36 +0,0 @@
sanitized_anchor_name
=====================
[![Build Status](https://travis-ci.org/shurcooL/sanitized_anchor_name.svg?branch=master)](https://travis-ci.org/shurcooL/sanitized_anchor_name) [![GoDoc](https://godoc.org/github.com/shurcooL/sanitized_anchor_name?status.svg)](https://godoc.org/github.com/shurcooL/sanitized_anchor_name)
Package sanitized_anchor_name provides a func to create sanitized anchor names.
Its logic can be reused by multiple packages to create interoperable anchor names
and links to those anchors.
At this time, it does not try to ensure that generated anchor names
are unique, that responsibility falls on the caller.
Installation
------------
```bash
go get -u github.com/shurcooL/sanitized_anchor_name
```
Example
-------
```Go
anchorName := sanitized_anchor_name.Create("This is a header")
fmt.Println(anchorName)
// Output:
// this-is-a-header
```
License
-------
- [MIT License](LICENSE)

View File

@ -1 +0,0 @@
module github.com/shurcooL/sanitized_anchor_name

View File

@ -1,29 +0,0 @@
// Package sanitized_anchor_name provides a func to create sanitized anchor names.
//
// Its logic can be reused by multiple packages to create interoperable anchor names
// and links to those anchors.
//
// At this time, it does not try to ensure that generated anchor names
// are unique, that responsibility falls on the caller.
package sanitized_anchor_name // import "github.com/shurcooL/sanitized_anchor_name"
import "unicode"
// Create returns a sanitized anchor name for the given text.
func Create(text string) string {
var anchorName []rune
var futureDash = false
for _, r := range text {
switch {
case unicode.IsLetter(r) || unicode.IsNumber(r):
if futureDash && len(anchorName) > 0 {
anchorName = append(anchorName, '-')
}
futureDash = false
anchorName = append(anchorName, unicode.ToLower(r))
default:
futureDash = true
}
}
return string(anchorName)
}

View File

@ -1,7 +1,13 @@
*.coverprofile *.coverprofile
*.exe
*.orig *.orig
node_modules/ .*envrc
vendor .envrc
.idea .idea
internal/*/built-example /.local/
/site/
coverage.txt coverage.txt
internal/*/built-example
vendor
/cmd/urfave-cli-genflags/urfave-cli-genflags
*.exe

View File

@ -55,11 +55,12 @@ further defined and clarified by project maintainers.
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting Dan Buch at dan@meatballhat.com. All complaints will be reported by contacting urfave-governance@googlegroups.com, a members-only group
reviewed and investigated and will result in a response that is deemed necessary that is world-postable. All complaints will be reviewed and investigated and
and appropriate to the circumstances. The project team is obligated to maintain will result in a response that is deemed necessary and appropriate to the
confidentiality with regard to the reporter of an incident. Further details of circumstances. The project team is obligated to maintain confidentiality with
specific enforcement policies may be posted separately. regard to the reporter of an incident. Further details of specific enforcement
policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other faith may face temporary or permanent repercussions as determined by other

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2016 Jeremy Saenz & Contributors Copyright (c) 2022 urfave/cli maintainers
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

26
vendor/github.com/urfave/cli/v2/Makefile generated vendored Normal file
View File

@ -0,0 +1,26 @@
# NOTE: this Makefile is meant to provide a simplified entry point for humans to
# run all of the critical steps to verify one's changes are harmonious in
# nature. Keeping target bodies to one line each and abstaining from make magic
# are very important so that maintainers and contributors can focus their
# attention on files that are primarily Go.
GO_RUN_BUILD := go run internal/build/build.go
.PHONY: all
all: generate vet test check-binary-size gfmrun yamlfmt v2diff
# NOTE: this is a special catch-all rule to run any of the commands
# defined in internal/build/build.go with optional arguments passed
# via GFLAGS (global flags) and FLAGS (command-specific flags), e.g.:
#
# $ make test GFLAGS='--packages cli'
%:
$(GO_RUN_BUILD) $(GFLAGS) $* $(FLAGS)
.PHONY: docs
docs:
mkdocs build
.PHONY: serve-docs
serve-docs:
mkdocs serve

View File

@ -1,70 +1,19 @@
cli # cli
===
[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://pkg.go.dev/github.com/urfave/cli/v2)
[![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) [![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli)
[![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli)
[![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli) [![codecov](https://codecov.io/gh/urfave/cli/branch/main/graph/badge.svg)](https://codecov.io/gh/urfave/cli)
cli is a simple, fast, and fun package for building command line apps in Go. The cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line goal is to enable developers to write fast and distributable command line
applications in an expressive way. applications in an expressive way.
## Usage Documentation ## Documentation
Usage documentation exists for each major version. Don't know what version you're on? You're probably using the version from the `master` branch, which is currently `v2`. More documentation is available in [`./docs`](./docs) or the hosted
documentation site at <https://cli.urfave.org>.
- `v2` - [./docs/v2/manual.md](./docs/v2/manual.md) ## License
- `v1` - [./docs/v1/manual.md](./docs/v1/manual.md)
Guides for migrating to newer versions: See [`LICENSE`](./LICENSE)
- `v1-to-v2` - [./docs/migrate-v1-to-v2.md](./docs/migrate-v1-to-v2.md)
## Installation
Using this package requires a working Go environment. [See the install instructions for Go](http://golang.org/doc/install.html).
Go Modules are required when using this package. [See the go blog guide on using Go Modules](https://blog.golang.org/using-go-modules).
### Using `v2` releases
```
$ GO111MODULE=on go get github.com/urfave/cli/v2
```
```go
...
import (
"github.com/urfave/cli/v2" // imports as package "cli"
)
...
```
### Using `v1` releases
```
$ GO111MODULE=on go get github.com/urfave/cli
```
```go
...
import (
"github.com/urfave/cli"
)
...
```
### GOPATH
Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can
be easily used:
```
export PATH=$PATH:$GOPATH/bin
```
### Supported platforms
cli is tested against multiple versions of Go on Linux, and against the latest
released version of Go on OS X and Windows. This project uses Github Actions for
builds. To see our currently supported go versions and platforms, look at the [./.github/workflows/cli.yml](https://github.com/urfave/cli/blob/master/.github/workflows/cli.yml).

View File

@ -7,17 +7,24 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"sort" "sort"
"time" "time"
) )
const suggestDidYouMeanTemplate = "Did you mean %q?"
var ( var (
changeLogURL = "https://github.com/urfave/cli/blob/master/docs/CHANGELOG.md" changeLogURL = "https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md"
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
errInvalidActionType = NewExitError("ERROR invalid Action type. "+ errInvalidActionType = NewExitError("ERROR invalid Action type. "+
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
fmt.Sprintf("See %s", appActionDeprecationURL), 2) fmt.Sprintf("See %s", appActionDeprecationURL), 2)
SuggestFlag SuggestFlagFunc = suggestFlag
SuggestCommand SuggestCommandFunc = suggestCommand
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
) )
// App is the main structure of a cli application. It is recommended that // App is the main structure of a cli application. It is recommended that
@ -37,6 +44,9 @@ type App struct {
Version string Version string
// Description of the program // Description of the program
Description string Description string
// DefaultCommand is the (optional) name of a command
// to run if no command names are passed as CLI arguments.
DefaultCommand string
// List of commands to execute // List of commands to execute
Commands []*Command Commands []*Command
// List of flags to parse // List of flags to parse
@ -52,6 +62,8 @@ type App struct {
HideVersion bool HideVersion bool
// categories contains the categorized commands and is populated on app startup // categories contains the categorized commands and is populated on app startup
categories CommandCategories categories CommandCategories
// flagCategories contains the categorized flags and is populated on app startup
flagCategories FlagCategories
// An action to execute when the shell completion flag is set // An action to execute when the shell completion flag is set
BashComplete BashCompleteFunc BashComplete BashCompleteFunc
// An action to execute before any subcommands are run, but after the context is ready // An action to execute before any subcommands are run, but after the context is ready
@ -66,6 +78,8 @@ type App struct {
CommandNotFound CommandNotFoundFunc CommandNotFound CommandNotFoundFunc
// Execute this function if a usage error occurs // Execute this function if a usage error occurs
OnUsageError OnUsageErrorFunc OnUsageError OnUsageErrorFunc
// Execute this function when an invalid flag is accessed from the context
InvalidFlagAccessHandler InvalidFlagAccessFunc
// Compilation date // Compilation date
Compiled time.Time Compiled time.Time
// List of all authors who contributed // List of all authors who contributed
@ -94,10 +108,16 @@ type App struct {
// single-character bool arguments into one // single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov // i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool UseShortOptionHandling bool
// Enable suggestions for commands and flags
Suggest bool
didSetup bool didSetup bool
} }
type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
type SuggestCommandFunc func(commands []*Command, provided string) string
// Tries to find out when this binary was compiled. // Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it. // Returns the current time if it fails to find it.
func compileTime() time.Time { func compileTime() time.Time {
@ -113,7 +133,6 @@ func compileTime() time.Time {
func NewApp() *App { func NewApp() *App {
return &App{ return &App{
Name: filepath.Base(os.Args[0]), Name: filepath.Base(os.Args[0]),
HelpName: filepath.Base(os.Args[0]),
Usage: "A new cli application", Usage: "A new cli application",
UsageText: "", UsageText: "",
BashComplete: DefaultAppComplete, BashComplete: DefaultAppComplete,
@ -140,7 +159,7 @@ func (a *App) Setup() {
} }
if a.HelpName == "" { if a.HelpName == "" {
a.HelpName = filepath.Base(os.Args[0]) a.HelpName = a.Name
} }
if a.Usage == "" { if a.Usage == "" {
@ -181,6 +200,8 @@ func (a *App) Setup() {
if c.HelpName == "" { if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
} }
c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
newCommands = append(newCommands, c) newCommands = append(newCommands, c)
} }
a.Commands = newCommands a.Commands = newCommands
@ -205,6 +226,15 @@ func (a *App) Setup() {
} }
sort.Sort(a.categories.(*commandCategories)) sort.Sort(a.categories.(*commandCategories))
a.flagCategories = newFlagCategories()
for _, fl := range a.Flags {
if cf, ok := fl.(CategorizableFlag); ok {
if cf.GetCategory() != "" {
a.flagCategories.AddFlag(cf.GetCategory(), cf)
}
}
}
if a.Metadata == nil { if a.Metadata == nil {
a.Metadata = make(map[string]interface{}) a.Metadata = make(map[string]interface{})
} }
@ -245,48 +275,41 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
err = parseIter(set, a, arguments[1:], shellComplete) err = parseIter(set, a, arguments[1:], shellComplete)
nerr := normalizeFlags(a.Flags, set) nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, &Context{Context: ctx}) cCtx := NewContext(a, set, &Context{Context: ctx})
if nerr != nil { if nerr != nil {
_, _ = fmt.Fprintln(a.Writer, nerr) _, _ = fmt.Fprintln(a.Writer, nerr)
_ = ShowAppHelp(context) if !a.HideHelp {
_ = ShowAppHelp(cCtx)
}
return nerr return nerr
} }
context.shellComplete = shellComplete cCtx.shellComplete = shellComplete
if checkCompletions(context) { if checkCompletions(cCtx) {
return nil return nil
} }
if err != nil { if err != nil {
if a.OnUsageError != nil { if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false) err := a.OnUsageError(cCtx, err, false)
a.handleExitCoder(context, err) a.handleExitCoder(cCtx, err)
return err return err
} }
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
_ = ShowAppHelp(context) if a.Suggest {
if suggestion, err := a.suggestFlagFromError(err, ""); err == nil {
fmt.Fprintf(a.Writer, suggestion)
}
}
if !a.HideHelp {
_ = ShowAppHelp(cCtx)
}
return err return err
} }
if !a.HideHelp && checkHelp(context) { if a.After != nil && !cCtx.shellComplete {
_ = ShowAppHelp(context)
return nil
}
if !a.HideVersion && checkVersion(context) {
ShowVersion(context)
return nil
}
cerr := checkRequiredFlags(a.Flags, context)
if cerr != nil {
_ = ShowAppHelp(context)
return cerr
}
if a.After != nil {
defer func() { defer func() {
if afterErr := a.After(context); afterErr != nil { if afterErr := a.After(cCtx); afterErr != nil {
if err != nil { if err != nil {
err = newMultiError(err, afterErr) err = newMultiError(err, afterErr)
} else { } else {
@ -296,39 +319,114 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
}() }()
} }
if a.Before != nil { if !a.HideHelp && checkHelp(cCtx) {
beforeErr := a.Before(context) _ = ShowAppHelp(cCtx)
return nil
}
if !a.HideVersion && checkVersion(cCtx) {
ShowVersion(cCtx)
return nil
}
cerr := cCtx.checkRequiredFlags(a.Flags)
if cerr != nil {
_ = ShowAppHelp(cCtx)
return cerr
}
if a.Before != nil && !cCtx.shellComplete {
beforeErr := a.Before(cCtx)
if beforeErr != nil { if beforeErr != nil {
a.handleExitCoder(context, beforeErr) a.handleExitCoder(cCtx, beforeErr)
err = beforeErr err = beforeErr
return err return err
} }
} }
args := context.Args() if err = runFlagActions(cCtx, a.Flags); err != nil {
return err
}
var c *Command
args := cCtx.Args()
if args.Present() { if args.Present() {
name := args.First() name := args.First()
c := a.Command(name) if a.validCommandName(name) {
if c != nil { c = a.Command(name)
return c.Run(context) } else {
hasDefault := a.DefaultCommand != ""
isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames())
var (
isDefaultSubcommand = false
defaultHasSubcommands = false
)
if hasDefault {
dc := a.Command(a.DefaultCommand)
defaultHasSubcommands = len(dc.Subcommands) > 0
for _, dcSub := range dc.Subcommands {
if checkStringSliceIncludes(name, dcSub.Names()) {
isDefaultSubcommand = true
break
} }
} }
}
if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) {
argsWithDefault := a.argsWithDefaultCommand(args)
if !reflect.DeepEqual(args, argsWithDefault) {
c = a.Command(argsWithDefault.First())
}
}
}
} else if a.DefaultCommand != "" {
c = a.Command(a.DefaultCommand)
}
if c != nil {
return c.Run(cCtx)
}
if a.Action == nil { if a.Action == nil {
a.Action = helpCommand.Action a.Action = helpCommand.Action
} }
// Run default Action // Run default Action
err = a.Action(context) err = a.Action(cCtx)
a.handleExitCoder(context, err) a.handleExitCoder(cCtx, err)
return err return err
} }
func (a *App) suggestFlagFromError(err error, command string) (string, error) {
flag, parseErr := flagFromError(err)
if parseErr != nil {
return "", err
}
flags := a.Flags
if command != "" {
cmd := a.Command(command)
if cmd == nil {
return "", err
}
flags = cmd.Flags
}
suggestion := SuggestFlag(flags, flag, a.HideHelp)
if len(suggestion) == 0 {
return "", err
}
return fmt.Sprintf(SuggestDidYouMeanTemplate+"\n\n", suggestion), nil
}
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned // RunAndExitOnError calls .Run() and exits non-zero if an error was returned
// //
// Deprecated: instead you should return an error that fulfills cli.ExitCoder // Deprecated: instead you should return an error that fulfills cli.ExitCoder
// to cli.App.Run. This will cause the application to exit with the given eror // to cli.App.Run. This will cause the application to exit with the given error
// code in the cli.ExitCoder // code in the cli.ExitCoder
func (a *App) RunAndExitOnError() { func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil { if err := a.Run(os.Args); err != nil {
@ -359,55 +457,60 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete) err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
nerr := normalizeFlags(a.Flags, set) nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, ctx) cCtx := NewContext(a, set, ctx)
if nerr != nil { if nerr != nil {
_, _ = fmt.Fprintln(a.Writer, nerr) _, _ = fmt.Fprintln(a.Writer, nerr)
_, _ = fmt.Fprintln(a.Writer) _, _ = fmt.Fprintln(a.Writer)
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
_ = ShowSubcommandHelp(context) _ = ShowSubcommandHelp(cCtx)
} else { } else {
_ = ShowCommandHelp(ctx, context.Args().First()) _ = ShowCommandHelp(ctx, cCtx.Args().First())
} }
return nerr return nerr
} }
if checkCompletions(context) { if checkCompletions(cCtx) {
return nil return nil
} }
if err != nil { if err != nil {
if a.OnUsageError != nil { if a.OnUsageError != nil {
err = a.OnUsageError(context, err, true) err = a.OnUsageError(cCtx, err, true)
a.handleExitCoder(context, err) a.handleExitCoder(cCtx, err)
return err return err
} }
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
_ = ShowSubcommandHelp(context) if a.Suggest {
if suggestion, err := a.suggestFlagFromError(err, cCtx.Command.Name); err == nil {
fmt.Fprintf(a.Writer, suggestion)
}
}
_ = ShowSubcommandHelp(cCtx)
return err return err
} }
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
if checkSubcommandHelp(context) { if checkSubcommandHelp(cCtx) {
return nil return nil
} }
} else { } else {
if checkCommandHelp(ctx, context.Args().First()) { if checkCommandHelp(ctx, cCtx.Args().First()) {
return nil return nil
} }
} }
cerr := checkRequiredFlags(a.Flags, context) cerr := cCtx.checkRequiredFlags(a.Flags)
if cerr != nil { if cerr != nil {
_ = ShowSubcommandHelp(context) _ = ShowSubcommandHelp(cCtx)
return cerr return cerr
} }
if a.After != nil { if a.After != nil && !cCtx.shellComplete {
defer func() { defer func() {
afterErr := a.After(context) afterErr := a.After(cCtx)
if afterErr != nil { if afterErr != nil {
a.handleExitCoder(context, err) a.handleExitCoder(cCtx, err)
if err != nil { if err != nil {
err = newMultiError(err, afterErr) err = newMultiError(err, afterErr)
} else { } else {
@ -417,28 +520,32 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
}() }()
} }
if a.Before != nil { if a.Before != nil && !cCtx.shellComplete {
beforeErr := a.Before(context) beforeErr := a.Before(cCtx)
if beforeErr != nil { if beforeErr != nil {
a.handleExitCoder(context, beforeErr) a.handleExitCoder(cCtx, beforeErr)
err = beforeErr err = beforeErr
return err return err
} }
} }
args := context.Args() if err = runFlagActions(cCtx, a.Flags); err != nil {
return err
}
args := cCtx.Args()
if args.Present() { if args.Present() {
name := args.First() name := args.First()
c := a.Command(name) c := a.Command(name)
if c != nil { if c != nil {
return c.Run(context) return c.Run(cCtx)
} }
} }
// Run default Action // Run default Action
err = a.Action(context) err = a.Action(cCtx)
a.handleExitCoder(context, err) a.handleExitCoder(cCtx, err)
return err return err
} }
@ -481,6 +588,14 @@ func (a *App) VisibleCommands() []*Command {
return ret return ret
} }
// VisibleFlagCategories returns a slice containing all the categories with the flags they contain
func (a *App) VisibleFlagCategories() []VisibleFlagCategory {
if a.flagCategories == nil {
return []VisibleFlagCategory{}
}
return a.flagCategories.VisibleCategories()
}
// VisibleFlags returns a slice of the Flags with Hidden=false // VisibleFlags returns a slice of the Flags with Hidden=false
func (a *App) VisibleFlags() []Flag { func (a *App) VisibleFlags() []Flag {
return visibleFlags(a.Flags) return visibleFlags(a.Flags)
@ -498,14 +613,69 @@ func (a *App) appendCommand(c *Command) {
} }
} }
func (a *App) handleExitCoder(context *Context, err error) { func (a *App) handleExitCoder(cCtx *Context, err error) {
if a.ExitErrHandler != nil { if a.ExitErrHandler != nil {
a.ExitErrHandler(context, err) a.ExitErrHandler(cCtx, err)
} else { } else {
HandleExitCoder(err) HandleExitCoder(err)
} }
} }
func (a *App) commandNames() []string {
var cmdNames []string
for _, cmd := range a.Commands {
cmdNames = append(cmdNames, cmd.Names()...)
}
return cmdNames
}
func (a *App) validCommandName(checkCmdName string) bool {
valid := false
allCommandNames := a.commandNames()
for _, cmdName := range allCommandNames {
if checkCmdName == cmdName {
valid = true
break
}
}
return valid
}
func (a *App) argsWithDefaultCommand(oldArgs Args) Args {
if a.DefaultCommand != "" {
rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...)
newArgs := args(rawArgs)
return &newArgs
}
return oldArgs
}
func runFlagActions(c *Context, fs []Flag) error {
for _, f := range fs {
isSet := false
for _, name := range f.Names() {
if c.IsSet(name) {
isSet = true
break
}
}
if isSet {
if af, ok := f.(ActionableFlag); ok {
if err := af.RunAction(c); err != nil {
return err
}
}
}
}
return nil
}
// Author represents someone who has contributed to a cli project. // Author represents someone who has contributed to a cli project.
type Author struct { type Author struct {
Name string // The Authors name Name string // The Authors name
@ -525,16 +695,28 @@ func (a *Author) String() string {
// HandleAction attempts to figure out which Action signature was used. If // HandleAction attempts to figure out which Action signature was used. If
// it's an ActionFunc or a func with the legacy signature for Action, the func // it's an ActionFunc or a func with the legacy signature for Action, the func
// is run! // is run!
func HandleAction(action interface{}, context *Context) (err error) { func HandleAction(action interface{}, cCtx *Context) (err error) {
switch a := action.(type) { switch a := action.(type) {
case ActionFunc: case ActionFunc:
return a(context) return a(cCtx)
case func(*Context) error: case func(*Context) error:
return a(context) return a(cCtx)
case func(*Context): // deprecated function signature case func(*Context): // deprecated function signature
a(context) a(cCtx)
return nil return nil
} }
return errInvalidActionType return errInvalidActionType
} }
func checkStringSliceIncludes(want string, sSlice []string) bool {
found := false
for _, s := range sSlice {
if want == s {
found = true
break
}
}
return found
}

View File

@ -1,10 +1,12 @@
package cli package cli
import "sort"
// CommandCategories interface allows for category manipulation // CommandCategories interface allows for category manipulation
type CommandCategories interface { type CommandCategories interface {
// AddCommand adds a command to a category, creating a new category if necessary. // AddCommand adds a command to a category, creating a new category if necessary.
AddCommand(category string, command *Command) AddCommand(category string, command *Command)
// categories returns a copy of the category slice // Categories returns a slice of categories sorted by name
Categories() []CommandCategory Categories() []CommandCategory
} }
@ -77,3 +79,95 @@ func (c *commandCategory) VisibleCommands() []*Command {
} }
return ret return ret
} }
// FlagCategories interface allows for category manipulation
type FlagCategories interface {
// AddFlags adds a flag to a category, creating a new category if necessary.
AddFlag(category string, fl Flag)
// VisibleCategories returns a slice of visible flag categories sorted by name
VisibleCategories() []VisibleFlagCategory
}
type defaultFlagCategories struct {
m map[string]*defaultVisibleFlagCategory
}
func newFlagCategories() FlagCategories {
return &defaultFlagCategories{
m: map[string]*defaultVisibleFlagCategory{},
}
}
func newFlagCategoriesFromFlags(fs []Flag) FlagCategories {
fc := newFlagCategories()
for _, fl := range fs {
if cf, ok := fl.(CategorizableFlag); ok {
if cf.GetCategory() != "" {
fc.AddFlag(cf.GetCategory(), cf)
}
}
}
return fc
}
func (f *defaultFlagCategories) AddFlag(category string, fl Flag) {
if _, ok := f.m[category]; !ok {
f.m[category] = &defaultVisibleFlagCategory{name: category, m: map[string]Flag{}}
}
f.m[category].m[fl.String()] = fl
}
func (f *defaultFlagCategories) VisibleCategories() []VisibleFlagCategory {
catNames := []string{}
for name := range f.m {
catNames = append(catNames, name)
}
sort.Strings(catNames)
ret := make([]VisibleFlagCategory, len(catNames))
for i, name := range catNames {
ret[i] = f.m[name]
}
return ret
}
// VisibleFlagCategory is a category containing flags.
type VisibleFlagCategory interface {
// Name returns the category name string
Name() string
// Flags returns a slice of VisibleFlag sorted by name
Flags() []VisibleFlag
}
type defaultVisibleFlagCategory struct {
name string
m map[string]Flag
}
func (fc *defaultVisibleFlagCategory) Name() string {
return fc.name
}
func (fc *defaultVisibleFlagCategory) Flags() []VisibleFlag {
vfNames := []string{}
for flName, fl := range fc.m {
if vf, ok := fl.(VisibleFlag); ok {
if vf.IsVisible() {
vfNames = append(vfNames, flName)
}
}
}
sort.Strings(vfNames)
ret := make([]VisibleFlag, len(vfNames))
for i, flName := range vfNames {
ret[i] = fc.m[flName].(VisibleFlag)
}
return ret
}

View File

@ -1,11 +1,13 @@
// Package cli provides a minimal framework for creating and organizing command line // Package cli provides a minimal framework for creating and organizing command line
// Go applications. cli is designed to be easy to understand and write, the most simple // Go applications. cli is designed to be easy to understand and write, the most simple
// cli application can be written as follows: // cli application can be written as follows:
//
// func main() { // func main() {
// (&cli.App{}).Run(os.Args) // (&cli.App{}).Run(os.Args)
// } // }
// //
// Of course this application does not do much, so let's make this an actual application: // Of course this application does not do much, so let's make this an actual application:
//
// func main() { // func main() {
// app := &cli.App{ // app := &cli.App{
// Name: "greet", // Name: "greet",
@ -20,4 +22,4 @@
// } // }
package cli package cli
//go:generate go run flag-gen/main.go flag-gen/assets_vfsdata.go //go:generate go run cmd/urfave-cli-genflags/main.go

View File

@ -39,6 +39,7 @@ type Command struct {
Subcommands []*Command Subcommands []*Command
// List of flags to parse // List of flags to parse
Flags []Flag Flags []Flag
flagCategories FlagCategories
// Treat all flags as normal arguments if true // Treat all flags as normal arguments if true
SkipFlagParsing bool SkipFlagParsing bool
// Boolean to hide built-in help command and help flag // Boolean to hide built-in help command and help flag
@ -105,39 +106,48 @@ func (c *Command) Run(ctx *Context) (err error) {
set, err := c.parseFlags(ctx.Args(), ctx.shellComplete) set, err := c.parseFlags(ctx.Args(), ctx.shellComplete)
context := NewContext(ctx.App, set, ctx) cCtx := NewContext(ctx.App, set, ctx)
context.Command = c cCtx.Command = c
if checkCommandCompletions(context, c.Name) { if checkCommandCompletions(cCtx, c.Name) {
return nil return nil
} }
if err != nil { if err != nil {
if c.OnUsageError != nil { if c.OnUsageError != nil {
err = c.OnUsageError(context, err, false) err = c.OnUsageError(cCtx, err, false)
context.App.handleExitCoder(context, err) cCtx.App.handleExitCoder(cCtx, err)
return err return err
} }
_, _ = fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) _, _ = fmt.Fprintln(cCtx.App.Writer, "Incorrect Usage:", err.Error())
_, _ = fmt.Fprintln(context.App.Writer) _, _ = fmt.Fprintln(cCtx.App.Writer)
_ = ShowCommandHelp(context, c.Name) if ctx.App.Suggest {
if suggestion, err := ctx.App.suggestFlagFromError(err, c.Name); err == nil {
fmt.Fprintf(cCtx.App.Writer, suggestion)
}
}
if !c.HideHelp {
_ = ShowCommandHelp(cCtx, c.Name)
}
return err return err
} }
if checkCommandHelp(context, c.Name) { if checkCommandHelp(cCtx, c.Name) {
return nil return nil
} }
cerr := checkRequiredFlags(c.Flags, context) cerr := cCtx.checkRequiredFlags(c.Flags)
if cerr != nil { if cerr != nil {
_ = ShowCommandHelp(context, c.Name) if !c.HideHelp {
_ = ShowCommandHelp(cCtx, c.Name)
}
return cerr return cerr
} }
if c.After != nil { if c.After != nil {
defer func() { defer func() {
afterErr := c.After(context) afterErr := c.After(cCtx)
if afterErr != nil { if afterErr != nil {
context.App.handleExitCoder(context, err) cCtx.App.handleExitCoder(cCtx, err)
if err != nil { if err != nil {
err = newMultiError(err, afterErr) err = newMultiError(err, afterErr)
} else { } else {
@ -148,22 +158,26 @@ func (c *Command) Run(ctx *Context) (err error) {
} }
if c.Before != nil { if c.Before != nil {
err = c.Before(context) err = c.Before(cCtx)
if err != nil { if err != nil {
context.App.handleExitCoder(context, err) cCtx.App.handleExitCoder(cCtx, err)
return err return err
} }
} }
if c.Action == nil { if err = runFlagActions(cCtx, c.Flags); err != nil {
c.Action = helpSubcommand.Action return err
} }
context.Command = c if c.Action == nil {
err = c.Action(context) c.Action = helpCommand.Action
}
cCtx.Command = c
err = c.Action(cCtx)
if err != nil { if err != nil {
context.App.handleExitCoder(context, err) cCtx.App.handleExitCoder(cCtx, err)
} }
return err return err
} }
@ -227,6 +241,7 @@ func (c *Command) startApp(ctx *Context) error {
} }
app.Usage = c.Usage app.Usage = c.Usage
app.UsageText = c.UsageText
app.Description = c.Description app.Description = c.Description
app.ArgsUsage = c.ArgsUsage app.ArgsUsage = c.ArgsUsage
@ -243,10 +258,12 @@ func (c *Command) startApp(ctx *Context) error {
app.Version = ctx.App.Version app.Version = ctx.App.Version
app.HideVersion = true app.HideVersion = true
app.Compiled = ctx.App.Compiled app.Compiled = ctx.App.Compiled
app.Reader = ctx.App.Reader
app.Writer = ctx.App.Writer app.Writer = ctx.App.Writer
app.ErrWriter = ctx.App.ErrWriter app.ErrWriter = ctx.App.ErrWriter
app.ExitErrHandler = ctx.App.ExitErrHandler app.ExitErrHandler = ctx.App.ExitErrHandler
app.UseShortOptionHandling = ctx.App.UseShortOptionHandling app.UseShortOptionHandling = ctx.App.UseShortOptionHandling
app.Suggest = ctx.App.Suggest
app.categories = newCommandCategories() app.categories = newCommandCategories()
for _, command := range c.Subcommands { for _, command := range c.Subcommands {
@ -267,7 +284,7 @@ func (c *Command) startApp(ctx *Context) error {
if c.Action != nil { if c.Action != nil {
app.Action = c.Action app.Action = c.Action
} else { } else {
app.Action = helpSubcommand.Action app.Action = helpCommand.Action
} }
app.OnUsageError = c.OnUsageError app.OnUsageError = c.OnUsageError
@ -278,6 +295,25 @@ func (c *Command) startApp(ctx *Context) error {
return app.RunAsSubcommand(ctx) return app.RunAsSubcommand(ctx)
} }
// VisibleCommands returns a slice of the Commands with Hidden=false
func (c *Command) VisibleCommands() []*Command {
var ret []*Command
for _, command := range c.Subcommands {
if !command.Hidden {
ret = append(ret, command)
}
}
return ret
}
// VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain
func (c *Command) VisibleFlagCategories() []VisibleFlagCategory {
if c.flagCategories == nil {
c.flagCategories = newFlagCategoriesFromFlags(c.Flags)
}
return c.flagCategories.VisibleCategories()
}
// VisibleFlags returns a slice of the Flags with Hidden=false // VisibleFlags returns a slice of the Flags with Hidden=false
func (c *Command) VisibleFlags() []Flag { func (c *Command) VisibleFlags() []Flag {
return visibleFlags(c.Flags) return visibleFlags(c.Flags)

View File

@ -2,7 +2,6 @@ package cli
import ( import (
"context" "context"
"errors"
"flag" "flag"
"fmt" "fmt"
"strings" "strings"
@ -42,19 +41,22 @@ func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
} }
// NumFlags returns the number of flags set // NumFlags returns the number of flags set
func (c *Context) NumFlags() int { func (cCtx *Context) NumFlags() int {
return c.flagSet.NFlag() return cCtx.flagSet.NFlag()
} }
// Set sets a context flag to a value. // Set sets a context flag to a value.
func (c *Context) Set(name, value string) error { func (cCtx *Context) Set(name, value string) error {
return c.flagSet.Set(name, value) if fs := cCtx.lookupFlagSet(name); fs != nil {
return fs.Set(name, value)
}
return fmt.Errorf("no such flag -%s", name)
} }
// IsSet determines if the flag was actually set // IsSet determines if the flag was actually set
func (c *Context) IsSet(name string) bool { func (cCtx *Context) IsSet(name string) bool {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
if fs := lookupFlagSet(name, c); fs != nil {
isSet := false isSet := false
fs.Visit(func(f *flag.Flag) { fs.Visit(func(f *flag.Flag) {
if f.Name == name { if f.Name == name {
@ -64,9 +66,8 @@ func (c *Context) IsSet(name string) bool {
if isSet { if isSet {
return true return true
} }
}
f := lookupFlag(name, c) f := cCtx.lookupFlag(name)
if f == nil { if f == nil {
return false return false
} }
@ -78,52 +79,65 @@ func (c *Context) IsSet(name string) bool {
} }
// LocalFlagNames returns a slice of flag names used in this context. // LocalFlagNames returns a slice of flag names used in this context.
func (c *Context) LocalFlagNames() []string { func (cCtx *Context) LocalFlagNames() []string {
var names []string var names []string
c.flagSet.Visit(makeFlagNameVisitor(&names)) cCtx.flagSet.Visit(makeFlagNameVisitor(&names))
return names return names
} }
// FlagNames returns a slice of flag names used by the this context and all of // FlagNames returns a slice of flag names used by the this context and all of
// its parent contexts. // its parent contexts.
func (c *Context) FlagNames() []string { func (cCtx *Context) FlagNames() []string {
var names []string var names []string
for _, ctx := range c.Lineage() { for _, pCtx := range cCtx.Lineage() {
ctx.flagSet.Visit(makeFlagNameVisitor(&names)) pCtx.flagSet.Visit(makeFlagNameVisitor(&names))
} }
return names return names
} }
// Lineage returns *this* context and all of its ancestor contexts in order from // Lineage returns *this* context and all of its ancestor contexts in order from
// child to parent // child to parent
func (c *Context) Lineage() []*Context { func (cCtx *Context) Lineage() []*Context {
var lineage []*Context var lineage []*Context
for cur := c; cur != nil; cur = cur.parentContext { for cur := cCtx; cur != nil; cur = cur.parentContext {
lineage = append(lineage, cur) lineage = append(lineage, cur)
} }
return lineage return lineage
} }
// Count returns the num of occurences of this flag
func (cCtx *Context) Count(name string) int {
if fs := cCtx.lookupFlagSet(name); fs != nil {
if cf, ok := fs.Lookup(name).Value.(Countable); ok {
return cf.Count()
}
}
return 0
}
// Value returns the value of the flag corresponding to `name` // Value returns the value of the flag corresponding to `name`
func (c *Context) Value(name string) interface{} { func (cCtx *Context) Value(name string) interface{} {
return c.flagSet.Lookup(name).Value.(flag.Getter).Get() if fs := cCtx.lookupFlagSet(name); fs != nil {
return fs.Lookup(name).Value.(flag.Getter).Get()
}
return nil
} }
// Args returns the command line arguments associated with the context. // Args returns the command line arguments associated with the context.
func (c *Context) Args() Args { func (cCtx *Context) Args() Args {
ret := args(c.flagSet.Args()) ret := args(cCtx.flagSet.Args())
return &ret return &ret
} }
// NArg returns the number of the command line arguments. // NArg returns the number of the command line arguments.
func (c *Context) NArg() int { func (cCtx *Context) NArg() int {
return c.Args().Len() return cCtx.Args().Len()
} }
func lookupFlag(name string, ctx *Context) Flag { func (cCtx *Context) lookupFlag(name string) Flag {
for _, c := range ctx.Lineage() { for _, c := range cCtx.Lineage() {
if c.Command == nil { if c.Command == nil {
continue continue
} }
@ -137,8 +151,8 @@ func lookupFlag(name string, ctx *Context) Flag {
} }
} }
if ctx.App != nil { if cCtx.App != nil {
for _, f := range ctx.App.Flags { for _, f := range cCtx.App.Flags {
for _, n := range f.Names() { for _, n := range f.Names() {
if n == name { if n == name {
return f return f
@ -150,58 +164,57 @@ func lookupFlag(name string, ctx *Context) Flag {
return nil return nil
} }
func lookupFlagSet(name string, ctx *Context) *flag.FlagSet { func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet {
for _, c := range ctx.Lineage() { for _, c := range cCtx.Lineage() {
if c.flagSet == nil {
continue
}
if f := c.flagSet.Lookup(name); f != nil { if f := c.flagSet.Lookup(name); f != nil {
return c.flagSet return c.flagSet
} }
} }
cCtx.onInvalidFlag(name)
return nil return nil
} }
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
switch ff.Value.(type) { var missingFlags []string
case Serializer:
_ = set.Set(name, ff.Value.(Serializer).Serialize())
default:
_ = set.Set(name, ff.Value.String())
}
}
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
visited := make(map[string]bool)
set.Visit(func(f *flag.Flag) {
visited[f.Name] = true
})
for _, f := range flags { for _, f := range flags {
parts := f.Names() if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
if len(parts) == 1 { var flagPresent bool
continue var flagName string
}
var ff *flag.Flag for _, key := range f.Names() {
for _, name := range parts { flagName = key
name = strings.Trim(name, " ")
if visited[name] { if cCtx.IsSet(strings.TrimSpace(key)) {
if ff != nil { flagPresent = true
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
}
ff = set.Lookup(name)
} }
} }
if ff == nil {
continue if !flagPresent && flagName != "" {
} missingFlags = append(missingFlags, flagName)
for _, name := range parts {
name = strings.Trim(name, " ")
if !visited[name] {
copyFlag(name, ff, set)
} }
} }
} }
if len(missingFlags) != 0 {
return &errRequiredFlags{missingFlags: missingFlags}
}
return nil return nil
} }
func (cCtx *Context) onInvalidFlag(name string) {
for cCtx != nil {
if cCtx.App != nil && cCtx.App.InvalidFlagAccessHandler != nil {
cCtx.App.InvalidFlagAccessHandler(cCtx, name)
break
}
cCtx = cCtx.parentContext
}
}
func makeFlagNameVisitor(names *[]string) func(*flag.Flag) { func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
return func(f *flag.Flag) { return func(f *flag.Flag) {
nameParts := strings.Split(f.Name, ",") nameParts := strings.Split(f.Name, ",")
@ -219,55 +232,3 @@ func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
} }
} }
} }
type requiredFlagsErr interface {
error
getMissingFlags() []string
}
type errRequiredFlags struct {
missingFlags []string
}
func (e *errRequiredFlags) Error() string {
numberOfMissingFlags := len(e.missingFlags)
if numberOfMissingFlags == 1 {
return fmt.Sprintf("Required flag %q not set", e.missingFlags[0])
}
joinedMissingFlags := strings.Join(e.missingFlags, ", ")
return fmt.Sprintf("Required flags %q not set", joinedMissingFlags)
}
func (e *errRequiredFlags) getMissingFlags() []string {
return e.missingFlags
}
func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr {
var missingFlags []string
for _, f := range flags {
if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
var flagPresent bool
var flagName string
for _, key := range f.Names() {
if len(key) > 1 {
flagName = key
}
if context.IsSet(strings.TrimSpace(key)) {
flagPresent = true
}
}
if !flagPresent && flagName != "" {
missingFlags = append(missingFlags, flagName)
}
}
}
if len(missingFlags) != 0 {
return &errRequiredFlags{missingFlags: missingFlags}
}
return nil
}

View File

@ -1,3 +1,6 @@
//go:build !urfave_cli_no_docs
// +build !urfave_cli_no_docs
package cli package cli
import ( import (
@ -15,31 +18,39 @@ import (
// The function errors if either parsing or writing of the string fails. // The function errors if either parsing or writing of the string fails.
func (a *App) ToMarkdown() (string, error) { func (a *App) ToMarkdown() (string, error) {
var w bytes.Buffer var w bytes.Buffer
if err := a.writeDocTemplate(&w); err != nil { if err := a.writeDocTemplate(&w, 0); err != nil {
return "", err return "", err
} }
return w.String(), nil return w.String(), nil
} }
// ToMan creates a man page string for the `*App` // ToMan creates a man page string with section number for the `*App`
// The function errors if either parsing or writing of the string fails. // The function errors if either parsing or writing of the string fails.
func (a *App) ToMan() (string, error) { func (a *App) ToManWithSection(sectionNumber int) (string, error) {
var w bytes.Buffer var w bytes.Buffer
if err := a.writeDocTemplate(&w); err != nil { if err := a.writeDocTemplate(&w, sectionNumber); err != nil {
return "", err return "", err
} }
man := md2man.Render(w.Bytes()) man := md2man.Render(w.Bytes())
return string(man), nil return string(man), nil
} }
// ToMan creates a man page string for the `*App`
// The function errors if either parsing or writing of the string fails.
func (a *App) ToMan() (string, error) {
man, err := a.ToManWithSection(8)
return man, err
}
type cliTemplate struct { type cliTemplate struct {
App *App App *App
SectionNum int
Commands []string Commands []string
GlobalArgs []string GlobalArgs []string
SynopsisArgs []string SynopsisArgs []string
} }
func (a *App) writeDocTemplate(w io.Writer) error { func (a *App) writeDocTemplate(w io.Writer, sectionNum int) error {
const name = "cli" const name = "cli"
t, err := template.New(name).Parse(MarkdownDocTemplate) t, err := template.New(name).Parse(MarkdownDocTemplate)
if err != nil { if err != nil {
@ -47,6 +58,7 @@ func (a *App) writeDocTemplate(w io.Writer) error {
} }
return t.ExecuteTemplate(w, name, &cliTemplate{ return t.ExecuteTemplate(w, name, &cliTemplate{
App: a, App: a,
SectionNum: sectionNum,
Commands: prepareCommands(a.Commands, 0), Commands: prepareCommands(a.Commands, 0),
GlobalArgs: prepareArgsWithValues(a.VisibleFlags()), GlobalArgs: prepareArgsWithValues(a.VisibleFlags()),
SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()), SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()),
@ -59,25 +71,26 @@ func prepareCommands(commands []*Command, level int) []string {
if command.Hidden { if command.Hidden {
continue continue
} }
usage := ""
if command.Usage != "" {
usage = command.Usage
}
prepared := fmt.Sprintf("%s %s\n\n%s\n", usageText := prepareUsageText(command)
usage := prepareUsage(command, usageText)
prepared := fmt.Sprintf("%s %s\n\n%s%s",
strings.Repeat("#", level+2), strings.Repeat("#", level+2),
strings.Join(command.Names(), ", "), strings.Join(command.Names(), ", "),
usage, usage,
usageText,
) )
flags := prepareArgsWithValues(command.Flags) flags := prepareArgsWithValues(command.VisibleFlags())
if len(flags) > 0 { if len(flags) > 0 {
prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n")) prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n"))
} }
coms = append(coms, prepared) coms = append(coms, prepared)
// recursevly iterate subcommands // recursively iterate subcommands
if len(command.Subcommands) > 0 { if len(command.Subcommands) > 0 {
coms = append( coms = append(
coms, coms,
@ -146,3 +159,40 @@ func flagDetails(flag DocGenerationFlag) string {
} }
return ": " + description return ": " + description
} }
func prepareUsageText(command *Command) string {
if command.UsageText == "" {
return ""
}
// Remove leading and trailing newlines
preparedUsageText := strings.Trim(command.UsageText, "\n")
var usageText string
if strings.Contains(preparedUsageText, "\n") {
// Format multi-line string as a code block using the 4 space schema to allow for embedded markdown such
// that it will not break the continuous code block.
for _, ln := range strings.Split(preparedUsageText, "\n") {
usageText += fmt.Sprintf(" %s\n", ln)
}
} else {
// Style a single line as a note
usageText = fmt.Sprintf(">%s\n", preparedUsageText)
}
return usageText
}
func prepareUsage(command *Command, usageText string) string {
if command.Usage == "" {
return ""
}
usage := command.Usage + "\n"
// Add a newline to the Usage IFF there is a UsageText
if usageText != "" {
usage += "\n"
}
return usage
}

View File

@ -47,6 +47,28 @@ func (m *multiError) Errors() []error {
return errs return errs
} }
type requiredFlagsErr interface {
error
getMissingFlags() []string
}
type errRequiredFlags struct {
missingFlags []string
}
func (e *errRequiredFlags) Error() string {
numberOfMissingFlags := len(e.missingFlags)
if numberOfMissingFlags == 1 {
return fmt.Sprintf("Required flag %q not set", e.missingFlags[0])
}
joinedMissingFlags := strings.Join(e.missingFlags, ", ")
return fmt.Sprintf("Required flags %q not set", joinedMissingFlags)
}
func (e *errRequiredFlags) getMissingFlags() []string {
return e.missingFlags
}
// ErrorFormatter is the interface that will suitably format the error output // ErrorFormatter is the interface that will suitably format the error output
type ErrorFormatter interface { type ErrorFormatter interface {
Format(s fmt.State, verb rune) Format(s fmt.State, verb rune)

View File

@ -95,7 +95,7 @@ func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, pr
completions = append(completions, completion.String()) completions = append(completions, completion.String())
completions = append( completions = append(
completions, completions,
a.prepareFishFlags(command.Flags, command.Names())..., a.prepareFishFlags(command.VisibleFlags(), command.Names())...,
) )
// recursevly iterate subcommands // recursevly iterate subcommands

115
vendor/github.com/urfave/cli/v2/flag-spec.yaml generated vendored Normal file
View File

@ -0,0 +1,115 @@
# NOTE: this file is used by the tool defined in
# ./cmd/urfave-cli-genflags/main.go which uses the
# `Spec` type that maps to this file structure.
flag_types:
bool:
struct_fields:
- name: Count
type: int
pointer: true
- name: Action
type: "func(*Context, bool) error"
float64:
struct_fields:
- name: Action
type: "func(*Context, float64) error"
Float64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: Action
type: "func(*Context, []float64) error"
int:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, int) error"
IntSlice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: Action
type: "func(*Context, []int) error"
int64:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, int64) error"
Int64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: Action
type: "func(*Context, []int64) error"
uint:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, uint) error"
UintSlice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: Action
type: "func(*Context, []uint) error"
uint64:
struct_fields:
- name: Base
type: int
- name: Action
type: "func(*Context, uint64) error"
Uint64Slice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: Action
type: "func(*Context, []uint64) error"
string:
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, string) error"
StringSlice:
value_pointer: true
skip_interfaces:
- fmt.Stringer
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, []string) error"
time.Duration:
struct_fields:
- name: Action
type: "func(*Context, time.Duration) error"
Timestamp:
value_pointer: true
struct_fields:
- name: Layout
type: string
- name: Timezone
type: "*time.Location"
- name: Action
type: "func(*Context, *time.Time) error"
Generic:
no_destination_pointer: true
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, interface{}) error"
Path:
struct_fields:
- name: TakesFile
type: bool
- name: Action
type: "func(*Context, Path) error"

View File

@ -1,13 +1,12 @@
package cli package cli
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"reflect"
"regexp" "regexp"
"runtime" "runtime"
"strconv"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -84,6 +83,12 @@ func (f FlagsByName) Swap(i, j int) {
f[i], f[j] = f[j], f[i] f[i], f[j] = f[j], f[i]
} }
// ActionableFlag is an interface that wraps Flag interface and RunAction operation.
type ActionableFlag interface {
Flag
RunAction(*Context) error
}
// Flag is a common interface related to parsing flags in cli. // Flag is a common interface related to parsing flags in cli.
// For more advanced flag parsing techniques, it is recommended that // For more advanced flag parsing techniques, it is recommended that
// this interface be implemented. // this interface be implemented.
@ -116,6 +121,42 @@ type DocGenerationFlag interface {
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
GetValue() string GetValue() string
// GetDefaultText returns the default text for this flag
GetDefaultText() string
// GetEnvVars returns the env vars for this flag
GetEnvVars() []string
}
// DocGenerationSliceFlag extends DocGenerationFlag for slice-based flags.
type DocGenerationSliceFlag interface {
DocGenerationFlag
// IsSliceFlag returns true for flags that can be given multiple times.
IsSliceFlag() bool
}
// VisibleFlag is an interface that allows to check if a flag is visible
type VisibleFlag interface {
Flag
// IsVisible returns true if the flag is not hidden, otherwise false
IsVisible() bool
}
// CategorizableFlag is an interface that allows us to potentially
// use a flag in a categorized representation.
type CategorizableFlag interface {
VisibleFlag
GetCategory() string
}
// Countable is an interface to enable detection of flag values which support
// repetitive flags
type Countable interface {
Count() int
} }
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
@ -130,11 +171,52 @@ func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
return set, nil return set, nil
} }
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
switch ff.Value.(type) {
case Serializer:
_ = set.Set(name, ff.Value.(Serializer).Serialize())
default:
_ = set.Set(name, ff.Value.String())
}
}
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
visited := make(map[string]bool)
set.Visit(func(f *flag.Flag) {
visited[f.Name] = true
})
for _, f := range flags {
parts := f.Names()
if len(parts) == 1 {
continue
}
var ff *flag.Flag
for _, name := range parts {
name = strings.Trim(name, " ")
if visited[name] {
if ff != nil {
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
}
ff = set.Lookup(name)
}
}
if ff == nil {
continue
}
for _, name := range parts {
name = strings.Trim(name, " ")
if !visited[name] {
copyFlag(name, ff, set)
}
}
}
return nil
}
func visibleFlags(fl []Flag) []Flag { func visibleFlags(fl []Flag) []Flag {
var visible []Flag var visible []Flag
for _, f := range fl { for _, f := range fl {
field := flagValue(f).FieldByName("Hidden") if vf, ok := f.(VisibleFlag); ok && vf.IsVisible() {
if !field.IsValid() || !field.Bool() {
visible = append(visible, f) visible = append(visible, f)
} }
} }
@ -188,7 +270,7 @@ func prefixedNames(names []string, placeholder string) string {
func withEnvHint(envVars []string, str string) string { func withEnvHint(envVars []string, str string) string {
envText := "" envText := ""
if envVars != nil && len(envVars) > 0 { if len(envVars) > 0 {
prefix := "$" prefix := "$"
suffix := "" suffix := ""
sep := ", $" sep := ", $"
@ -203,7 +285,7 @@ func withEnvHint(envVars []string, str string) string {
return str + envText return str + envText
} }
func flagNames(name string, aliases []string) []string { func FlagNames(name string, aliases []string) []string {
var ret []string var ret []string
for _, part := range append([]string{name}, aliases...) { for _, part := range append([]string{name}, aliases...) {
@ -217,17 +299,6 @@ func flagNames(name string, aliases []string) []string {
return ret return ret
} }
func flagStringSliceField(f Flag, name string) []string {
fv := flagValue(f)
field := fv.FieldByName(name)
if field.IsValid() {
return field.Interface().([]string)
}
return []string{}
}
func withFileHint(filePath, str string) string { func withFileHint(filePath, str string) string {
fileText := "" fileText := ""
if filePath != "" { if filePath != "" {
@ -236,130 +307,39 @@ func withFileHint(filePath, str string) string {
return str + fileText return str + fileText
} }
func flagValue(f Flag) reflect.Value {
fv := reflect.ValueOf(f)
for fv.Kind() == reflect.Ptr {
fv = reflect.Indirect(fv)
}
return fv
}
func formatDefault(format string) string { func formatDefault(format string) string {
return " (default: " + format + ")" return " (default: " + format + ")"
} }
func stringifyFlag(f Flag) string { func stringifyFlag(f Flag) string {
fv := flagValue(f) // enforce DocGeneration interface on flags to avoid reflection
df, ok := f.(DocGenerationFlag)
switch f := f.(type) { if !ok {
case *IntSliceFlag: return ""
return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifyIntSliceFlag(f))
case *Int64SliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifyInt64SliceFlag(f))
case *Float64SliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifyFloat64SliceFlag(f))
case *StringSliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"),
stringifyStringSliceFlag(f))
} }
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) placeholder, usage := unquoteUsage(df.GetUsage())
needsPlaceholder := df.TakesValue()
needsPlaceholder := false
defaultValueString := ""
val := fv.FieldByName("Value")
if val.IsValid() {
needsPlaceholder = val.Kind() != reflect.Bool
defaultValueString = fmt.Sprintf(formatDefault("%v"), val.Interface())
if val.Kind() == reflect.String && val.String() != "" {
defaultValueString = fmt.Sprintf(formatDefault("%q"), val.String())
}
}
helpText := fv.FieldByName("DefaultText")
if helpText.IsValid() && helpText.String() != "" {
needsPlaceholder = val.Kind() != reflect.Bool
defaultValueString = fmt.Sprintf(formatDefault("%s"), helpText.String())
}
if defaultValueString == formatDefault("") {
defaultValueString = ""
}
if needsPlaceholder && placeholder == "" { if needsPlaceholder && placeholder == "" {
placeholder = defaultPlaceholder placeholder = defaultPlaceholder
} }
defaultValueString := ""
if s := df.GetDefaultText(); s != "" {
defaultValueString = fmt.Sprintf(formatDefault("%s"), s)
}
usageWithDefault := strings.TrimSpace(usage + defaultValueString) usageWithDefault := strings.TrimSpace(usage + defaultValueString)
return withEnvHint(flagStringSliceField(f, "EnvVars"), pn := prefixedNames(df.Names(), placeholder)
fmt.Sprintf("%s\t%s", prefixedNames(f.Names(), placeholder), usageWithDefault)) sliceFlag, ok := f.(DocGenerationSliceFlag)
} if ok && sliceFlag.IsSliceFlag() {
pn = pn + " [ " + pn + " ]"
func stringifyIntSliceFlag(f *IntSliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.Itoa(i))
}
} }
return stringifySliceFlag(f.Usage, f.Names(), defaultVals) return withEnvHint(df.GetEnvVars(), fmt.Sprintf("%s\t%s", pn, usageWithDefault))
}
func stringifyInt64SliceFlag(f *Int64SliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
}
}
return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
}
func stringifyFloat64SliceFlag(f *Float64SliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), "."))
}
}
return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
}
func stringifyStringSliceFlag(f *StringSliceFlag) string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, s := range f.Value.Value() {
if len(s) > 0 {
defaultVals = append(defaultVals, strconv.Quote(s))
}
}
}
return stringifySliceFlag(f.Usage, f.Names(), defaultVals)
}
func stringifySliceFlag(usage string, names, defaultVals []string) string {
placeholder, usage := unquoteUsage(usage)
if placeholder == "" {
placeholder = defaultPlaceholder
}
defaultVal := ""
if len(defaultVals) > 0 {
defaultVal = fmt.Sprintf(formatDefault("%s"), strings.Join(defaultVals, ", "))
}
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
return fmt.Sprintf("%s\t%s", prefixedNames(names, placeholder), usageWithDefault)
} }
func hasFlag(flags []Flag, fl Flag) bool { func hasFlag(flags []Flag, fl Flag) bool {
@ -372,17 +352,26 @@ func hasFlag(flags []Flag, fl Flag) bool {
return false return false
} }
func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool) { // Return the first value from a list of environment variables and files
// (which may or may not exist), a description of where the value was found,
// and a boolean which is true if a value was found.
func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhere string, found bool) {
for _, envVar := range envVars { for _, envVar := range envVars {
envVar = strings.TrimSpace(envVar) envVar = strings.TrimSpace(envVar)
if val, ok := syscall.Getenv(envVar); ok { if value, found := syscall.Getenv(envVar); found {
return val, true return value, fmt.Sprintf("environment variable %q", envVar), true
} }
} }
for _, fileVar := range strings.Split(filePath, ",") { for _, fileVar := range strings.Split(filePath, ",") {
if fileVar != "" {
if data, err := ioutil.ReadFile(fileVar); err == nil { if data, err := ioutil.ReadFile(fileVar); err == nil {
return string(data), true return string(data), fmt.Sprintf("file %q", filePath), true
} }
} }
return "", false }
return "", "", false
}
func flagSplitMultiValues(val string) []string {
return strings.Split(val, ",")
} }

View File

@ -1,45 +1,61 @@
package cli package cli
import ( import (
"errors"
"flag" "flag"
"fmt" "fmt"
"strconv" "strconv"
) )
// BoolFlag is a flag with type bool // boolValue needs to implement the boolFlag internal interface in flag
type BoolFlag struct { // to be able to capture bool fields and values
Name string //
Aliases []string // type boolFlag interface {
Usage string // Value
EnvVars []string // IsBoolFlag() bool
FilePath string // }
Required bool type boolValue struct {
Hidden bool destination *bool
Value bool count *int
DefaultText string
Destination *bool
HasBeenSet bool
} }
// IsSet returns whether or not the flag has been set through env or file func newBoolValue(val bool, p *bool, count *int) *boolValue {
func (f *BoolFlag) IsSet() bool { *p = val
return f.HasBeenSet return &boolValue{
destination: p,
count: count,
}
} }
// String returns a readable representation of this value func (b *boolValue) Set(s string) error {
// (for usage defaults) v, err := strconv.ParseBool(s)
func (f *BoolFlag) String() string { if err != nil {
return FlagStringer(f) err = errors.New("parse error")
return err
}
*b.destination = v
if b.count != nil {
*b.count = *b.count + 1
}
return err
} }
// Names returns the names of the flag func (b *boolValue) Get() interface{} { return *b.destination }
func (f *BoolFlag) Names() []string {
return flagNames(f.Name, f.Aliases) func (b *boolValue) String() string {
if b.destination != nil {
return strconv.FormatBool(*b.destination)
}
return strconv.FormatBool(false)
} }
// IsRequired returns whether or not the flag is required func (b *boolValue) IsBoolFlag() bool { return true }
func (f *BoolFlag) IsRequired() bool {
return f.Required func (b *boolValue) Count() int {
if b.count != nil {
return *b.count
}
return 0
} }
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
@ -52,42 +68,86 @@ func (f *BoolFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *BoolFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *BoolFlag) GetValue() string { func (f *BoolFlag) GetValue() string {
return "" return ""
} }
// Apply populates the flag given the flag set and environment // GetDefaultText returns the default text for this flag
func (f *BoolFlag) Apply(set *flag.FlagSet) error { func (f *BoolFlag) GetDefaultText() string {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if f.DefaultText != "" {
if val != "" { return f.DefaultText
valBool, err := strconv.ParseBool(val) }
return fmt.Sprintf("%v", f.Value)
}
if err != nil { // GetEnvVars returns the env vars for this flag
return fmt.Errorf("could not parse %q as bool value for flag %s: %s", val, f.Name, err) func (f *BoolFlag) GetEnvVars() []string {
} return f.EnvVars
}
f.Value = valBool // RunAction executes flag action if set
f.HasBeenSet = true func (f *BoolFlag) RunAction(c *Context) error {
} if f.Action != nil {
} return f.Action(c, c.Bool(f.Name))
for _, name := range f.Names() {
if f.Destination != nil {
set.BoolVar(f.Destination, name, f.Value, f.Usage)
continue
}
set.Bool(name, f.Value, f.Usage)
} }
return nil return nil
} }
// Apply populates the flag given the flag set and environment
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valBool, err := strconv.ParseBool(val)
if err != nil {
return fmt.Errorf("could not parse %q as bool value from %s for flag %s: %s", val, source, f.Name, err)
}
f.Value = valBool
} else {
// empty value implies that the env is defined but set to empty string, we have to assume that this is
// what the user wants. If user doesnt want this then the env needs to be deleted or the flag removed from
// file
f.Value = false
}
f.HasBeenSet = true
}
count := f.Count
dest := f.Destination
if count == nil {
count = new(int)
}
if dest == nil {
dest = new(bool)
}
for _, name := range f.Names() {
value := newBoolValue(f.Value, dest, count)
set.Var(value, name, f.Usage)
}
return nil
}
// Get returns the flags value in the given Context.
func (f *BoolFlag) Get(ctx *Context) bool {
return ctx.Bool(f.Name)
}
// Bool looks up the value of a local BoolFlag, returns // Bool looks up the value of a local BoolFlag, returns
// false if not found // false if not found
func (c *Context) Bool(name string) bool { func (cCtx *Context) Bool(name string) bool {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupBool(name, fs) return lookupBool(name, fs)
} }
return false return false

View File

@ -6,42 +6,6 @@ import (
"time" "time"
) )
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
type DurationFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value time.Duration
DefaultText string
Destination *time.Duration
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *DurationFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *DurationFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *DurationFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *DurationFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *DurationFlag) TakesValue() bool { func (f *DurationFlag) TakesValue() bool {
return true return true
@ -52,20 +16,38 @@ func (f *DurationFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *DurationFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *DurationFlag) GetValue() string { func (f *DurationFlag) GetValue() string {
return f.Value.String() return f.Value.String()
} }
// GetDefaultText returns the default text for this flag
func (f *DurationFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *DurationFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *DurationFlag) Apply(set *flag.FlagSet) error { func (f *DurationFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valDuration, err := time.ParseDuration(val) valDuration, err := time.ParseDuration(val)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as duration value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q as duration value from %s for flag %s: %s", val, source, f.Name, err)
} }
f.Value = valDuration f.Value = valDuration
@ -83,10 +65,24 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *DurationFlag) Get(ctx *Context) time.Duration {
return ctx.Duration(f.Name)
}
// RunAction executes flag action if set
func (f *DurationFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Duration(f.Name))
}
return nil
}
// Duration looks up the value of a local DurationFlag, returns // Duration looks up the value of a local DurationFlag, returns
// 0 if not found // 0 if not found
func (c *Context) Duration(name string) time.Duration { func (cCtx *Context) Duration(name string) time.Duration {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupDuration(name, fs) return lookupDuration(name, fs)
} }
return 0 return 0

View File

@ -6,42 +6,6 @@ import (
"strconv" "strconv"
) )
// Float64Flag is a flag with type float64
type Float64Flag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value float64
DefaultText string
Destination *float64
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Float64Flag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *Float64Flag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *Float64Flag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Float64Flag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *Float64Flag) TakesValue() bool { func (f *Float64Flag) TakesValue() bool {
return true return true
@ -52,20 +16,37 @@ func (f *Float64Flag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *Float64Flag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Float64Flag) GetValue() string { func (f *Float64Flag) GetValue() string {
return fmt.Sprintf("%f", f.Value) return fmt.Sprintf("%v", f.Value)
}
// GetDefaultText returns the default text for this flag
func (f *Float64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Float64Flag) GetEnvVars() []string {
return f.EnvVars
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Float64Flag) Apply(set *flag.FlagSet) error { func (f *Float64Flag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valFloat, err := strconv.ParseFloat(val, 10) valFloat, err := strconv.ParseFloat(val, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as float64 value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q as float64 value from %s for flag %s: %s", val, source, f.Name, err)
} }
f.Value = valFloat f.Value = valFloat
@ -84,10 +65,24 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *Float64Flag) Get(ctx *Context) float64 {
return ctx.Float64(f.Name)
}
// RunAction executes flag action if set
func (f *Float64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Float64(f.Name))
}
return nil
}
// Float64 looks up the value of a local Float64Flag, returns // Float64 looks up the value of a local Float64Flag, returns
// 0 if not found // 0 if not found
func (c *Context) Float64(name string) float64 { func (cCtx *Context) Float64(name string) float64 {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupFloat64(name, fs) return lookupFloat64(name, fs)
} }
return 0 return 0

View File

@ -19,6 +19,16 @@ func NewFloat64Slice(defaults ...float64) *Float64Slice {
return &Float64Slice{slice: append([]float64{}, defaults...)} return &Float64Slice{slice: append([]float64{}, defaults...)}
} }
// clone allocate a copy of self object
func (f *Float64Slice) clone() *Float64Slice {
n := &Float64Slice{
slice: make([]float64, len(f.slice)),
hasBeenSet: f.hasBeenSet,
}
copy(n.slice, f.slice)
return n
}
// Set parses the value into a float64 and appends it to the list of values // Set parses the value into a float64 and appends it to the list of values
func (f *Float64Slice) Set(value string) error { func (f *Float64Slice) Set(value string) error {
if !f.hasBeenSet { if !f.hasBeenSet {
@ -33,18 +43,25 @@ func (f *Float64Slice) Set(value string) error {
return nil return nil
} }
tmp, err := strconv.ParseFloat(value, 64) for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
if err != nil { if err != nil {
return err return err
} }
f.slice = append(f.slice, tmp) f.slice = append(f.slice, tmp)
}
return nil return nil
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
func (f *Float64Slice) String() string { func (f *Float64Slice) String() string {
return fmt.Sprintf("%#v", f.slice) v := f.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]float64, 0)
}
return fmt.Sprintf("%#v", v)
} }
// Serialize allows Float64Slice to fulfill Serializer // Serialize allows Float64Slice to fulfill Serializer
@ -63,41 +80,12 @@ func (f *Float64Slice) Get() interface{} {
return *f return *f
} }
// Float64SliceFlag is a flag with type *Float64Slice
type Float64SliceFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value *Float64Slice
DefaultText string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Float64SliceFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *Float64SliceFlag) String() string { func (f *Float64SliceFlag) String() string {
return FlagStringer(f) return FlagStringer(f)
} }
// Names returns the names of the flag
func (f *Float64SliceFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Float64SliceFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true if the flag takes a value, otherwise false // TakesValue returns true if the flag takes a value, otherwise false
func (f *Float64SliceFlag) TakesValue() bool { func (f *Float64SliceFlag) TakesValue() bool {
return true return true
@ -108,36 +96,91 @@ func (f *Float64SliceFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *Float64SliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Float64SliceFlag) GetValue() string { func (f *Float64SliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), "."))
} }
return "" }
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *Float64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Float64SliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Float64SliceFlag) IsSliceFlag() bool {
return true
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { // apply any default
if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]float64, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
// resolve setValue (what we will assign to the set)
var setValue *Float64Slice
switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(Float64Slice)
}
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
f.Value = &Float64Slice{} for _, s := range flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
for _, s := range strings.Split(val, ",") { return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", val, source, f.Name, err)
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as float64 slice value for flag %s: %s", f.Value, f.Name, err)
} }
} }
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true f.HasBeenSet = true
} }
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Value == nil { set.Var(setValue, name, f.Usage)
f.Value = &Float64Slice{}
} }
set.Var(f.Value, name, f.Usage)
return nil
}
// Get returns the flags value in the given Context.
func (f *Float64SliceFlag) Get(ctx *Context) []float64 {
return ctx.Float64Slice(f.Name)
}
// RunAction executes flag action if set
func (f *Float64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Float64Slice(f.Name))
} }
return nil return nil
@ -145,8 +188,8 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
// Float64Slice looks up the value of a local Float64SliceFlag, returns // Float64Slice looks up the value of a local Float64SliceFlag, returns
// nil if not found // nil if not found
func (c *Context) Float64Slice(name string) []float64 { func (cCtx *Context) Float64Slice(name string) []float64 {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupFloat64Slice(name, fs) return lookupFloat64Slice(name, fs)
} }
return nil return nil
@ -155,7 +198,7 @@ func (c *Context) Float64Slice(name string) []float64 {
func lookupFloat64Slice(name string, set *flag.FlagSet) []float64 { func lookupFloat64Slice(name string, set *flag.FlagSet) []float64 {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {
if slice, ok := f.Value.(*Float64Slice); ok { if slice, ok := unwrapFlagValue(f.Value).(*Float64Slice); ok {
return slice.Value() return slice.Value()
} }
} }

View File

@ -11,42 +11,6 @@ type Generic interface {
String() string String() string
} }
// GenericFlag is a flag with type Generic
type GenericFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
TakesFile bool
Value Generic
DefaultText string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *GenericFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *GenericFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *GenericFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *GenericFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *GenericFlag) TakesValue() bool { func (f *GenericFlag) TakesValue() bool {
return true return true
@ -57,6 +21,11 @@ func (f *GenericFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *GenericFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *GenericFlag) GetValue() string { func (f *GenericFlag) GetValue() string {
@ -66,13 +35,26 @@ func (f *GenericFlag) GetValue() string {
return "" return ""
} }
// GetDefaultText returns the default text for this flag
func (f *GenericFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *GenericFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply takes the flagset and calls Set on the generic flag with the value // Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag // provided by the user for parsing by the flag
func (f GenericFlag) Apply(set *flag.FlagSet) error { func (f *GenericFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
if err := f.Value.Set(val); err != nil { if err := f.Value.Set(val); err != nil {
return fmt.Errorf("could not parse %q as value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q from %s as value for flag %s: %s", val, source, f.Name, err)
} }
f.HasBeenSet = true f.HasBeenSet = true
@ -80,16 +62,34 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error {
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Destination != nil {
set.Var(f.Destination, name, f.Usage)
continue
}
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
} }
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *GenericFlag) Get(ctx *Context) interface{} {
return ctx.Generic(f.Name)
}
// RunAction executes flag action if set
func (f *GenericFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Generic(f.Name))
}
return nil
}
// Generic looks up the value of a local GenericFlag, returns // Generic looks up the value of a local GenericFlag, returns
// nil if not found // nil if not found
func (c *Context) Generic(name string) interface{} { func (cCtx *Context) Generic(name string) interface{} {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupGeneric(name, fs) return lookupGeneric(name, fs)
} }
return nil return nil

View File

@ -6,42 +6,6 @@ import (
"strconv" "strconv"
) )
// IntFlag is a flag with type int
type IntFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value int
DefaultText string
Destination *int
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *IntFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *IntFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *IntFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *IntFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *IntFlag) TakesValue() bool { func (f *IntFlag) TakesValue() bool {
return true return true
@ -52,20 +16,38 @@ func (f *IntFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *IntFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *IntFlag) GetValue() string { func (f *IntFlag) GetValue() string {
return fmt.Sprintf("%d", f.Value) return fmt.Sprintf("%d", f.Value)
} }
// GetDefaultText returns the default text for this flag
func (f *IntFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *IntFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *IntFlag) Apply(set *flag.FlagSet) error { func (f *IntFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseInt(val, 0, 64) valInt, err := strconv.ParseInt(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as int value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
} }
f.Value = int(valInt) f.Value = int(valInt)
@ -84,10 +66,24 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *IntFlag) Get(ctx *Context) int {
return ctx.Int(f.Name)
}
// RunAction executes flag action if set
func (f *IntFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int(f.Name))
}
return nil
}
// Int looks up the value of a local IntFlag, returns // Int looks up the value of a local IntFlag, returns
// 0 if not found // 0 if not found
func (c *Context) Int(name string) int { func (cCtx *Context) Int(name string) int {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupInt(name, fs) return lookupInt(name, fs)
} }
return 0 return 0

View File

@ -6,42 +6,6 @@ import (
"strconv" "strconv"
) )
// Int64Flag is a flag with type int64
type Int64Flag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value int64
DefaultText string
Destination *int64
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Int64Flag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *Int64Flag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *Int64Flag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Int64Flag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *Int64Flag) TakesValue() bool { func (f *Int64Flag) TakesValue() bool {
return true return true
@ -52,20 +16,38 @@ func (f *Int64Flag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *Int64Flag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Int64Flag) GetValue() string { func (f *Int64Flag) GetValue() string {
return fmt.Sprintf("%d", f.Value) return fmt.Sprintf("%d", f.Value)
} }
// GetDefaultText returns the default text for this flag
func (f *Int64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Int64Flag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Int64Flag) Apply(set *flag.FlagSet) error { func (f *Int64Flag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseInt(val, 0, 64) valInt, err := strconv.ParseInt(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as int value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
} }
f.Value = valInt f.Value = valInt
@ -83,10 +65,24 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *Int64Flag) Get(ctx *Context) int64 {
return ctx.Int64(f.Name)
}
// RunAction executes flag action if set
func (f *Int64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int64(f.Name))
}
return nil
}
// Int64 looks up the value of a local Int64Flag, returns // Int64 looks up the value of a local Int64Flag, returns
// 0 if not found // 0 if not found
func (c *Context) Int64(name string) int64 { func (cCtx *Context) Int64(name string) int64 {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupInt64(name, fs) return lookupInt64(name, fs)
} }
return 0 return 0

View File

@ -19,6 +19,16 @@ func NewInt64Slice(defaults ...int64) *Int64Slice {
return &Int64Slice{slice: append([]int64{}, defaults...)} return &Int64Slice{slice: append([]int64{}, defaults...)}
} }
// clone allocate a copy of self object
func (i *Int64Slice) clone() *Int64Slice {
n := &Int64Slice{
slice: make([]int64, len(i.slice)),
hasBeenSet: i.hasBeenSet,
}
copy(n.slice, i.slice)
return n
}
// Set parses the value into an integer and appends it to the list of values // Set parses the value into an integer and appends it to the list of values
func (i *Int64Slice) Set(value string) error { func (i *Int64Slice) Set(value string) error {
if !i.hasBeenSet { if !i.hasBeenSet {
@ -33,19 +43,26 @@ func (i *Int64Slice) Set(value string) error {
return nil return nil
} }
tmp, err := strconv.ParseInt(value, 0, 64) for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64)
if err != nil { if err != nil {
return err return err
} }
i.slice = append(i.slice, tmp) i.slice = append(i.slice, tmp)
}
return nil return nil
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
func (i *Int64Slice) String() string { func (i *Int64Slice) String() string {
return fmt.Sprintf("%#v", i.slice) v := i.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]int64, 0)
}
return fmt.Sprintf("%#v", v)
} }
// Serialize allows Int64Slice to fulfill Serializer // Serialize allows Int64Slice to fulfill Serializer
@ -64,79 +81,105 @@ func (i *Int64Slice) Get() interface{} {
return *i return *i
} }
// Int64SliceFlag is a flag with type *Int64Slice
type Int64SliceFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value *Int64Slice
DefaultText string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Int64SliceFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *Int64SliceFlag) String() string { func (f *Int64SliceFlag) String() string {
return FlagStringer(f) return FlagStringer(f)
} }
// Names returns the names of the flag
func (f *Int64SliceFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Int64SliceFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *Int64SliceFlag) TakesValue() bool { func (f *Int64SliceFlag) TakesValue() bool {
return true return true
} }
// GetUsage returns the usage string for the flag // GetUsage returns the usage string for the flag
func (f Int64SliceFlag) GetUsage() string { func (f *Int64SliceFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *Int64SliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Int64SliceFlag) GetValue() string { func (f *Int64SliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
} }
return "" }
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *Int64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Int64SliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Int64SliceFlag) IsSliceFlag() bool {
return true
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { // apply any default
f.Value = &Int64Slice{} if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]int64, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
for _, s := range strings.Split(val, ",") { // resolve setValue (what we will assign to the set)
if err := f.Value.Set(strings.TrimSpace(s)); err != nil { var setValue *Int64Slice
return fmt.Errorf("could not parse %q as int64 slice value for flag %s: %s", val, f.Name, err) switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(Int64Slice)
}
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err)
} }
} }
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true f.HasBeenSet = true
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Value == nil { set.Var(setValue, name, f.Usage)
f.Value = &Int64Slice{}
} }
set.Var(f.Value, name, f.Usage)
return nil
}
// Get returns the flags value in the given Context.
func (f *Int64SliceFlag) Get(ctx *Context) []int64 {
return ctx.Int64Slice(f.Name)
}
// RunAction executes flag action if set
func (f *Int64SliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Int64Slice(f.Name))
} }
return nil return nil
@ -144,14 +187,17 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
// Int64Slice looks up the value of a local Int64SliceFlag, returns // Int64Slice looks up the value of a local Int64SliceFlag, returns
// nil if not found // nil if not found
func (c *Context) Int64Slice(name string) []int64 { func (cCtx *Context) Int64Slice(name string) []int64 {
return lookupInt64Slice(name, c.flagSet) if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupInt64Slice(name, fs)
}
return nil
} }
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {
if slice, ok := f.Value.(*Int64Slice); ok { if slice, ok := unwrapFlagValue(f.Value).(*Int64Slice); ok {
return slice.Value() return slice.Value()
} }
} }

View File

@ -19,6 +19,16 @@ func NewIntSlice(defaults ...int) *IntSlice {
return &IntSlice{slice: append([]int{}, defaults...)} return &IntSlice{slice: append([]int{}, defaults...)}
} }
// clone allocate a copy of self object
func (i *IntSlice) clone() *IntSlice {
n := &IntSlice{
slice: make([]int, len(i.slice)),
hasBeenSet: i.hasBeenSet,
}
copy(n.slice, i.slice)
return n
}
// TODO: Consistently have specific Set function for Int64 and Float64 ? // TODO: Consistently have specific Set function for Int64 and Float64 ?
// SetInt directly adds an integer to the list of values // SetInt directly adds an integer to the list of values
func (i *IntSlice) SetInt(value int) { func (i *IntSlice) SetInt(value int) {
@ -44,19 +54,26 @@ func (i *IntSlice) Set(value string) error {
return nil return nil
} }
tmp, err := strconv.ParseInt(value, 0, 64) for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64)
if err != nil { if err != nil {
return err return err
} }
i.slice = append(i.slice, int(tmp)) i.slice = append(i.slice, int(tmp))
}
return nil return nil
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
func (i *IntSlice) String() string { func (i *IntSlice) String() string {
return fmt.Sprintf("%#v", i.slice) v := i.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]int, 0)
}
return fmt.Sprintf("%#v", v)
} }
// Serialize allows IntSlice to fulfill Serializer // Serialize allows IntSlice to fulfill Serializer
@ -75,79 +92,105 @@ func (i *IntSlice) Get() interface{} {
return *i return *i
} }
// IntSliceFlag is a flag with type *IntSlice
type IntSliceFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value *IntSlice
DefaultText string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *IntSliceFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *IntSliceFlag) String() string { func (f *IntSliceFlag) String() string {
return FlagStringer(f) return FlagStringer(f)
} }
// Names returns the names of the flag
func (f *IntSliceFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *IntSliceFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *IntSliceFlag) TakesValue() bool { func (f *IntSliceFlag) TakesValue() bool {
return true return true
} }
// GetUsage returns the usage string for the flag // GetUsage returns the usage string for the flag
func (f IntSliceFlag) GetUsage() string { func (f *IntSliceFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *IntSliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *IntSliceFlag) GetValue() string { func (f *IntSliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.Itoa(i))
} }
return "" }
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *IntSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *IntSliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *IntSliceFlag) IsSliceFlag() bool {
return true
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { // apply any default
f.Value = &IntSlice{} if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]int, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
for _, s := range strings.Split(val, ",") { // resolve setValue (what we will assign to the set)
if err := f.Value.Set(strings.TrimSpace(s)); err != nil { var setValue *IntSlice
return fmt.Errorf("could not parse %q as int slice value for flag %s: %s", val, f.Name, err) switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(IntSlice)
}
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int slice value from %s for flag %s: %s", val, source, f.Name, err)
} }
} }
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true f.HasBeenSet = true
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Value == nil { set.Var(setValue, name, f.Usage)
f.Value = &IntSlice{}
} }
set.Var(f.Value, name, f.Usage)
return nil
}
// Get returns the flags value in the given Context.
func (f *IntSliceFlag) Get(ctx *Context) []int {
return ctx.IntSlice(f.Name)
}
// RunAction executes flag action if set
func (f *IntSliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.IntSlice(f.Name))
} }
return nil return nil
@ -155,9 +198,9 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
// IntSlice looks up the value of a local IntSliceFlag, returns // IntSlice looks up the value of a local IntSliceFlag, returns
// nil if not found // nil if not found
func (c *Context) IntSlice(name string) []int { func (cCtx *Context) IntSlice(name string) []int {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupIntSlice(name, c.flagSet) return lookupIntSlice(name, fs)
} }
return nil return nil
} }
@ -165,7 +208,7 @@ func (c *Context) IntSlice(name string) []int {
func lookupIntSlice(name string, set *flag.FlagSet) []int { func lookupIntSlice(name string, set *flag.FlagSet) []int {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {
if slice, ok := f.Value.(*IntSlice); ok { if slice, ok := unwrapFlagValue(f.Value).(*IntSlice); ok {
return slice.Value() return slice.Value()
} }
} }

View File

@ -1,42 +1,11 @@
package cli package cli
import "flag" import (
"flag"
"fmt"
)
type PathFlag struct { type Path = string
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
TakesFile bool
Value string
DefaultText string
Destination *string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *PathFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *PathFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *PathFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *PathFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *PathFlag) TakesValue() bool { func (f *PathFlag) TakesValue() bool {
@ -48,15 +17,36 @@ func (f *PathFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *PathFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *PathFlag) GetValue() string { func (f *PathFlag) GetValue() string {
return f.Value return f.Value
} }
// GetDefaultText returns the default text for this flag
func (f *PathFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
if f.Value == "" {
return f.Value
}
return fmt.Sprintf("%q", f.Value)
}
// GetEnvVars returns the env vars for this flag
func (f *PathFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *PathFlag) Apply(set *flag.FlagSet) error { func (f *PathFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val f.Value = val
f.HasBeenSet = true f.HasBeenSet = true
} }
@ -72,10 +62,24 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *PathFlag) Get(ctx *Context) string {
return ctx.Path(f.Name)
}
// RunAction executes flag action if set
func (f *PathFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Path(f.Name))
}
return nil
}
// Path looks up the value of a local PathFlag, returns // Path looks up the value of a local PathFlag, returns
// "" if not found // "" if not found
func (c *Context) Path(name string) string { func (cCtx *Context) Path(name string) string {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupPath(name, fs) return lookupPath(name, fs)
} }

View File

@ -1,43 +1,9 @@
package cli package cli
import "flag" import (
"flag"
// StringFlag is a flag with type string "fmt"
type StringFlag struct { )
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
TakesFile bool
Value string
DefaultText string
Destination *string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *StringFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *StringFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *StringFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *StringFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *StringFlag) TakesValue() bool { func (f *StringFlag) TakesValue() bool {
@ -49,15 +15,36 @@ func (f *StringFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *StringFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *StringFlag) GetValue() string { func (f *StringFlag) GetValue() string {
return f.Value return f.Value
} }
// GetDefaultText returns the default text for this flag
func (f *StringFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
if f.Value == "" {
return f.Value
}
return fmt.Sprintf("%q", f.Value)
}
// GetEnvVars returns the env vars for this flag
func (f *StringFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *StringFlag) Apply(set *flag.FlagSet) error { func (f *StringFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val f.Value = val
f.HasBeenSet = true f.HasBeenSet = true
} }
@ -73,10 +60,24 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *StringFlag) Get(ctx *Context) string {
return ctx.String(f.Name)
}
// RunAction executes flag action if set
func (f *StringFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.String(f.Name))
}
return nil
}
// String looks up the value of a local StringFlag, returns // String looks up the value of a local StringFlag, returns
// "" if not found // "" if not found
func (c *Context) String(name string) string { func (cCtx *Context) String(name string) string {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupString(name, fs) return lookupString(name, fs)
} }
return "" return ""
@ -85,10 +86,7 @@ func (c *Context) String(name string) string {
func lookupString(name string, set *flag.FlagSet) string { func lookupString(name string, set *flag.FlagSet) string {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {
parsed, err := f.Value.String(), error(nil) parsed := f.Value.String()
if err != nil {
return ""
}
return parsed return parsed
} }
return "" return ""

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"strconv"
"strings" "strings"
) )
@ -18,6 +19,16 @@ func NewStringSlice(defaults ...string) *StringSlice {
return &StringSlice{slice: append([]string{}, defaults...)} return &StringSlice{slice: append([]string{}, defaults...)}
} }
// clone allocate a copy of self object
func (s *StringSlice) clone() *StringSlice {
n := &StringSlice{
slice: make([]string, len(s.slice)),
hasBeenSet: s.hasBeenSet,
}
copy(n.slice, s.slice)
return n
}
// Set appends the string value to the list of values // Set appends the string value to the list of values
func (s *StringSlice) Set(value string) error { func (s *StringSlice) Set(value string) error {
if !s.hasBeenSet { if !s.hasBeenSet {
@ -32,7 +43,9 @@ func (s *StringSlice) Set(value string) error {
return nil return nil
} }
s.slice = append(s.slice, value) for _, t := range flagSplitMultiValues(value) {
s.slice = append(s.slice, strings.TrimSpace(t))
}
return nil return nil
} }
@ -58,43 +71,12 @@ func (s *StringSlice) Get() interface{} {
return *s return *s
} }
// StringSliceFlag is a flag with type *StringSlice
type StringSliceFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
TakesFile bool
Value *StringSlice
DefaultText string
HasBeenSet bool
Destination *StringSlice
}
// IsSet returns whether or not the flag has been set through env or file
func (f *StringSliceFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value // String returns a readable representation of this value
// (for usage defaults) // (for usage defaults)
func (f *StringSliceFlag) String() string { func (f *StringSliceFlag) String() string {
return FlagStringer(f) return FlagStringer(f)
} }
// Names returns the names of the flag
func (f *StringSliceFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *StringSliceFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *StringSliceFlag) TakesValue() bool { func (f *StringSliceFlag) TakesValue() bool {
return true return true
@ -105,56 +87,91 @@ func (f *StringSliceFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *StringSliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *StringSliceFlag) GetValue() string { func (f *StringSliceFlag) GetValue() string {
if f.Value != nil { var defaultVals []string
return f.Value.String() if f.Value != nil && len(f.Value.Value()) > 0 {
for _, s := range f.Value.Value() {
if len(s) > 0 {
defaultVals = append(defaultVals, strconv.Quote(s))
} }
return "" }
}
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *StringSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *StringSliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *StringSliceFlag) IsSliceFlag() bool {
return true
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
// apply any default
if f.Destination != nil && f.Value != nil { if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]string, len(f.Value.slice)) f.Destination.slice = make([]string, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice) copy(f.Destination.slice, f.Value.slice)
} }
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { // resolve setValue (what we will assign to the set)
if f.Value == nil { var setValue *StringSlice
f.Value = &StringSlice{} switch {
} case f.Destination != nil:
destination := f.Value setValue = f.Destination
if f.Destination != nil { case f.Value != nil:
destination = f.Destination setValue = f.Value.clone()
default:
setValue = new(StringSlice)
} }
for _, s := range strings.Split(val, ",") { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if err := destination.Set(strings.TrimSpace(s)); err != nil { for _, s := range flagSplitMultiValues(val) {
return fmt.Errorf("could not parse %q as string value for flag %s: %s", val, f.Name, err) if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err)
} }
} }
// Set this to false so that we reset the slice if we then set values from // Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment. // flags that have already been set by the environment.
destination.hasBeenSet = false setValue.hasBeenSet = false
f.HasBeenSet = true f.HasBeenSet = true
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Value == nil { set.Var(setValue, name, f.Usage)
f.Value = &StringSlice{}
} }
if f.Destination != nil { return nil
set.Var(f.Destination, name, f.Usage) }
continue
}
set.Var(f.Value, name, f.Usage) // Get returns the flags value in the given Context.
func (f *StringSliceFlag) Get(ctx *Context) []string {
return ctx.StringSlice(f.Name)
}
// RunAction executes flag action if set
func (f *StringSliceFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.StringSlice(f.Name))
} }
return nil return nil
@ -162,8 +179,8 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
// StringSlice looks up the value of a local StringSliceFlag, returns // StringSlice looks up the value of a local StringSliceFlag, returns
// nil if not found // nil if not found
func (c *Context) StringSlice(name string) []string { func (cCtx *Context) StringSlice(name string) []string {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupStringSlice(name, fs) return lookupStringSlice(name, fs)
} }
return nil return nil
@ -172,7 +189,7 @@ func (c *Context) StringSlice(name string) []string {
func lookupStringSlice(name string, set *flag.FlagSet) []string { func lookupStringSlice(name string, set *flag.FlagSet) []string {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {
if slice, ok := f.Value.(*StringSlice); ok { if slice, ok := unwrapFlagValue(f.Value).(*StringSlice); ok {
return slice.Value() return slice.Value()
} }
} }

View File

@ -11,6 +11,7 @@ type Timestamp struct {
timestamp *time.Time timestamp *time.Time
hasBeenSet bool hasBeenSet bool
layout string layout string
location *time.Location
} }
// Timestamp constructor // Timestamp constructor
@ -31,9 +32,22 @@ func (t *Timestamp) SetLayout(layout string) {
t.layout = layout t.layout = layout
} }
// Set perceived timezone of the to-be parsed time string
func (t *Timestamp) SetLocation(loc *time.Location) {
t.location = loc
}
// Parses the string value to timestamp // Parses the string value to timestamp
func (t *Timestamp) Set(value string) error { func (t *Timestamp) Set(value string) error {
timestamp, err := time.Parse(t.layout, value) var timestamp time.Time
var err error
if t.location != nil {
timestamp, err = time.ParseInLocation(t.layout, value, t.location)
} else {
timestamp, err = time.Parse(t.layout, value)
}
if err != nil { if err != nil {
return err return err
} }
@ -58,42 +72,6 @@ func (t *Timestamp) Get() interface{} {
return *t return *t
} }
// TimestampFlag is a flag with type time
type TimestampFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Layout string
Value *Timestamp
DefaultText string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *TimestampFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *TimestampFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *TimestampFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *TimestampFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *TimestampFlag) TakesValue() bool { func (f *TimestampFlag) TakesValue() bool {
return true return true
@ -104,15 +82,33 @@ func (f *TimestampFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *TimestampFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *TimestampFlag) GetValue() string { func (f *TimestampFlag) GetValue() string {
if f.Value != nil { if f.Value != nil && f.Value.timestamp != nil {
return f.Value.timestamp.String() return f.Value.timestamp.String()
} }
return "" return ""
} }
// GetDefaultText returns the default text for this flag
func (f *TimestampFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *TimestampFlag) GetEnvVars() []string {
return f.EnvVars
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *TimestampFlag) Apply(set *flag.FlagSet) error { func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
if f.Layout == "" { if f.Layout == "" {
@ -122,23 +118,48 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
f.Value = &Timestamp{} f.Value = &Timestamp{}
} }
f.Value.SetLayout(f.Layout) f.Value.SetLayout(f.Layout)
f.Value.SetLocation(f.Timezone)
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if f.Destination != nil {
f.Destination.SetLayout(f.Layout)
f.Destination.SetLocation(f.Timezone)
}
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if err := f.Value.Set(val); err != nil { if err := f.Value.Set(val); err != nil {
return fmt.Errorf("could not parse %q as timestamp value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q as timestamp value from %s for flag %s: %s", val, source, f.Name, err)
} }
f.HasBeenSet = true f.HasBeenSet = true
} }
for _, name := range f.Names() { for _, name := range f.Names() {
if f.Destination != nil {
set.Var(f.Destination, name, f.Usage)
continue
}
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
} }
return nil return nil
} }
// Get returns the flags value in the given Context.
func (f *TimestampFlag) Get(ctx *Context) *time.Time {
return ctx.Timestamp(f.Name)
}
// RunAction executes flag action if set
func (f *TimestampFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Timestamp(f.Name))
}
return nil
}
// Timestamp gets the timestamp from a flag name // Timestamp gets the timestamp from a flag name
func (c *Context) Timestamp(name string) *time.Time { func (cCtx *Context) Timestamp(name string) *time.Time {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupTimestamp(name, fs) return lookupTimestamp(name, fs)
} }
return nil return nil

View File

@ -6,42 +6,6 @@ import (
"strconv" "strconv"
) )
// UintFlag is a flag with type uint
type UintFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value uint
DefaultText string
Destination *uint
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *UintFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *UintFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *UintFlag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *UintFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *UintFlag) TakesValue() bool { func (f *UintFlag) TakesValue() bool {
return true return true
@ -52,13 +16,18 @@ func (f *UintFlag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *UintFlag) GetCategory() string {
return f.Category
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *UintFlag) Apply(set *flag.FlagSet) error { func (f *UintFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseUint(val, 0, 64) valInt, err := strconv.ParseUint(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as uint value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q as uint value from %s for flag %s: %s", val, source, f.Name, err)
} }
f.Value = uint(valInt) f.Value = uint(valInt)
@ -77,16 +46,43 @@ func (f *UintFlag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// RunAction executes flag action if set
func (f *UintFlag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Uint(f.Name))
}
return nil
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *UintFlag) GetValue() string { func (f *UintFlag) GetValue() string {
return fmt.Sprintf("%d", f.Value) return fmt.Sprintf("%d", f.Value)
} }
// GetDefaultText returns the default text for this flag
func (f *UintFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *UintFlag) GetEnvVars() []string {
return f.EnvVars
}
// Get returns the flags value in the given Context.
func (f *UintFlag) Get(ctx *Context) uint {
return ctx.Uint(f.Name)
}
// Uint looks up the value of a local UintFlag, returns // Uint looks up the value of a local UintFlag, returns
// 0 if not found // 0 if not found
func (c *Context) Uint(name string) uint { func (cCtx *Context) Uint(name string) uint {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupUint(name, fs) return lookupUint(name, fs)
} }
return 0 return 0

View File

@ -6,42 +6,6 @@ import (
"strconv" "strconv"
) )
// Uint64Flag is a flag with type uint64
type Uint64Flag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Value uint64
DefaultText string
Destination *uint64
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Uint64Flag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *Uint64Flag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *Uint64Flag) Names() []string {
return flagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Uint64Flag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false // TakesValue returns true of the flag takes a value, otherwise false
func (f *Uint64Flag) TakesValue() bool { func (f *Uint64Flag) TakesValue() bool {
return true return true
@ -52,13 +16,18 @@ func (f *Uint64Flag) GetUsage() string {
return f.Usage return f.Usage
} }
// GetCategory returns the category for the flag
func (f *Uint64Flag) GetCategory() string {
return f.Category
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
func (f *Uint64Flag) Apply(set *flag.FlagSet) error { func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" { if val != "" {
valInt, err := strconv.ParseUint(val, 0, 64) valInt, err := strconv.ParseUint(val, f.Base, 64)
if err != nil { if err != nil {
return fmt.Errorf("could not parse %q as uint64 value for flag %s: %s", val, f.Name, err) return fmt.Errorf("could not parse %q as uint64 value from %s for flag %s: %s", val, source, f.Name, err)
} }
f.Value = valInt f.Value = valInt
@ -77,16 +46,43 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
return nil return nil
} }
// RunAction executes flag action if set
func (f *Uint64Flag) RunAction(c *Context) error {
if f.Action != nil {
return f.Action(c, c.Uint64(f.Name))
}
return nil
}
// GetValue returns the flags value as string representation and an empty // GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all. // string if the flag takes no value at all.
func (f *Uint64Flag) GetValue() string { func (f *Uint64Flag) GetValue() string {
return fmt.Sprintf("%d", f.Value) return fmt.Sprintf("%d", f.Value)
} }
// GetDefaultText returns the default text for this flag
func (f *Uint64Flag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Uint64Flag) GetEnvVars() []string {
return f.EnvVars
}
// Get returns the flags value in the given Context.
func (f *Uint64Flag) Get(ctx *Context) uint64 {
return ctx.Uint64(f.Name)
}
// Uint64 looks up the value of a local Uint64Flag, returns // Uint64 looks up the value of a local Uint64Flag, returns
// 0 if not found // 0 if not found
func (c *Context) Uint64(name string) uint64 { func (cCtx *Context) Uint64(name string) uint64 {
if fs := lookupFlagSet(name, c); fs != nil { if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupUint64(name, fs) return lookupUint64(name, fs)
} }
return 0 return 0

200
vendor/github.com/urfave/cli/v2/flag_uint64_slice.go generated vendored Normal file
View File

@ -0,0 +1,200 @@
package cli
import (
"encoding/json"
"flag"
"fmt"
"strconv"
"strings"
)
// Uint64Slice wraps []int64 to satisfy flag.Value
type Uint64Slice struct {
slice []uint64
hasBeenSet bool
}
// NewUint64Slice makes an *Uint64Slice with default values
func NewUint64Slice(defaults ...uint64) *Uint64Slice {
return &Uint64Slice{slice: append([]uint64{}, defaults...)}
}
// clone allocate a copy of self object
func (i *Uint64Slice) clone() *Uint64Slice {
n := &Uint64Slice{
slice: make([]uint64, len(i.slice)),
hasBeenSet: i.hasBeenSet,
}
copy(n.slice, i.slice)
return n
}
// Set parses the value into an integer and appends it to the list of values
func (i *Uint64Slice) Set(value string) error {
if !i.hasBeenSet {
i.slice = []uint64{}
i.hasBeenSet = true
}
if strings.HasPrefix(value, slPfx) {
// Deserializing assumes overwrite
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
i.hasBeenSet = true
return nil
}
for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 64)
if err != nil {
return err
}
i.slice = append(i.slice, tmp)
}
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (i *Uint64Slice) String() string {
v := i.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]uint64, 0)
}
str := fmt.Sprintf("%d", v)
str = strings.Replace(str, " ", ", ", -1)
str = strings.Replace(str, "[", "{", -1)
str = strings.Replace(str, "]", "}", -1)
return fmt.Sprintf("[]uint64%s", str)
}
// Serialize allows Uint64Slice to fulfill Serializer
func (i *Uint64Slice) Serialize() string {
jsonBytes, _ := json.Marshal(i.slice)
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
}
// Value returns the slice of ints set by this flag
func (i *Uint64Slice) Value() []uint64 {
return i.slice
}
// Get returns the slice of ints set by this flag
func (i *Uint64Slice) Get() interface{} {
return *i
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *Uint64SliceFlag) String() string {
return FlagStringer(f)
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *Uint64SliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *Uint64SliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *Uint64SliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *Uint64SliceFlag) GetValue() string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatUint(i, 10))
}
}
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *Uint64SliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *Uint64SliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *Uint64SliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error {
// apply any default
if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]uint64, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
// resolve setValue (what we will assign to the set)
var setValue *Uint64Slice
switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(Uint64Slice)
}
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as uint64 slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true
}
for _, name := range f.Names() {
set.Var(setValue, name, f.Usage)
}
return nil
}
// Get returns the flags value in the given Context.
func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 {
return ctx.Uint64Slice(f.Name)
}
// Uint64Slice looks up the value of a local Uint64SliceFlag, returns
// nil if not found
func (cCtx *Context) Uint64Slice(name string) []uint64 {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupUint64Slice(name, fs)
}
return nil
}
func lookupUint64Slice(name string, set *flag.FlagSet) []uint64 {
f := set.Lookup(name)
if f != nil {
if slice, ok := unwrapFlagValue(f.Value).(*Uint64Slice); ok {
return slice.Value()
}
}
return nil
}

211
vendor/github.com/urfave/cli/v2/flag_uint_slice.go generated vendored Normal file
View File

@ -0,0 +1,211 @@
package cli
import (
"encoding/json"
"flag"
"fmt"
"strconv"
"strings"
)
// UintSlice wraps []int to satisfy flag.Value
type UintSlice struct {
slice []uint
hasBeenSet bool
}
// NewUintSlice makes an *UintSlice with default values
func NewUintSlice(defaults ...uint) *UintSlice {
return &UintSlice{slice: append([]uint{}, defaults...)}
}
// clone allocate a copy of self object
func (i *UintSlice) clone() *UintSlice {
n := &UintSlice{
slice: make([]uint, len(i.slice)),
hasBeenSet: i.hasBeenSet,
}
copy(n.slice, i.slice)
return n
}
// TODO: Consistently have specific Set function for Int64 and Float64 ?
// SetInt directly adds an integer to the list of values
func (i *UintSlice) SetUint(value uint) {
if !i.hasBeenSet {
i.slice = []uint{}
i.hasBeenSet = true
}
i.slice = append(i.slice, value)
}
// Set parses the value into an integer and appends it to the list of values
func (i *UintSlice) Set(value string) error {
if !i.hasBeenSet {
i.slice = []uint{}
i.hasBeenSet = true
}
if strings.HasPrefix(value, slPfx) {
// Deserializing assumes overwrite
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
i.hasBeenSet = true
return nil
}
for _, s := range flagSplitMultiValues(value) {
tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 32)
if err != nil {
return err
}
i.slice = append(i.slice, uint(tmp))
}
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (i *UintSlice) String() string {
v := i.slice
if v == nil {
// treat nil the same as zero length non-nil
v = make([]uint, 0)
}
str := fmt.Sprintf("%d", v)
str = strings.Replace(str, " ", ", ", -1)
str = strings.Replace(str, "[", "{", -1)
str = strings.Replace(str, "]", "}", -1)
return fmt.Sprintf("[]uint%s", str)
}
// Serialize allows UintSlice to fulfill Serializer
func (i *UintSlice) Serialize() string {
jsonBytes, _ := json.Marshal(i.slice)
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
}
// Value returns the slice of ints set by this flag
func (i *UintSlice) Value() []uint {
return i.slice
}
// Get returns the slice of ints set by this flag
func (i *UintSlice) Get() interface{} {
return *i
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *UintSliceFlag) String() string {
return FlagStringer(f)
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *UintSliceFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *UintSliceFlag) GetUsage() string {
return f.Usage
}
// GetCategory returns the category for the flag
func (f *UintSliceFlag) GetCategory() string {
return f.Category
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *UintSliceFlag) GetValue() string {
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatUint(uint64(i), 10))
}
}
return strings.Join(defaultVals, ", ")
}
// GetDefaultText returns the default text for this flag
func (f *UintSliceFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
return f.GetValue()
}
// GetEnvVars returns the env vars for this flag
func (f *UintSliceFlag) GetEnvVars() []string {
return f.EnvVars
}
// IsSliceFlag implements DocGenerationSliceFlag.
func (f *UintSliceFlag) IsSliceFlag() bool {
return true
}
// Apply populates the flag given the flag set and environment
func (f *UintSliceFlag) Apply(set *flag.FlagSet) error {
// apply any default
if f.Destination != nil && f.Value != nil {
f.Destination.slice = make([]uint, len(f.Value.slice))
copy(f.Destination.slice, f.Value.slice)
}
// resolve setValue (what we will assign to the set)
var setValue *UintSlice
switch {
case f.Destination != nil:
setValue = f.Destination
case f.Value != nil:
setValue = f.Value.clone()
default:
setValue = new(UintSlice)
}
if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" {
for _, s := range flagSplitMultiValues(val) {
if err := setValue.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as uint slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}
// Set this to false so that we reset the slice if we then set values from
// flags that have already been set by the environment.
setValue.hasBeenSet = false
f.HasBeenSet = true
}
for _, name := range f.Names() {
set.Var(setValue, name, f.Usage)
}
return nil
}
// Get returns the flags value in the given Context.
func (f *UintSliceFlag) Get(ctx *Context) []uint {
return ctx.UintSlice(f.Name)
}
// UintSlice looks up the value of a local UintSliceFlag, returns
// nil if not found
func (cCtx *Context) UintSlice(name string) []uint {
if fs := cCtx.lookupFlagSet(name); fs != nil {
return lookupUintSlice(name, fs)
}
return nil
}
func lookupUintSlice(name string, set *flag.FlagSet) []uint {
f := set.Lookup(name)
if f != nil {
if slice, ok := unwrapFlagValue(f.Value).(*UintSlice); ok {
return slice.Value()
}
}
return nil
}

View File

@ -17,15 +17,18 @@ type ActionFunc func(*Context) error
// CommandNotFoundFunc is executed if the proper command cannot be found // CommandNotFoundFunc is executed if the proper command cannot be found
type CommandNotFoundFunc func(*Context, string) type CommandNotFoundFunc func(*Context, string)
// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying // OnUsageErrorFunc is executed if a usage error occurs. This is useful for displaying
// customized usage error messages. This function is able to replace the // customized usage error messages. This function is able to replace the
// original error messages. If this function is not set, the "Incorrect usage" // original error messages. If this function is not set, the "Incorrect usage"
// is displayed and the execution is interrupted. // is displayed and the execution is interrupted.
type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error
// InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context.
type InvalidFlagAccessFunc func(*Context, string)
// ExitErrHandlerFunc is executed if provided in order to handle exitError values // ExitErrHandlerFunc is executed if provided in order to handle exitError values
// returned by Actions and Before/After functions. // returned by Actions and Before/After functions.
type ExitErrHandlerFunc func(context *Context, err error) type ExitErrHandlerFunc func(cCtx *Context, err error)
// FlagStringFunc is used by the help generation to display a flag, which is // FlagStringFunc is used by the help generation to display a flag, which is
// expected to be a single line. // expected to be a single line.

View File

@ -1,9 +0,0 @@
module github.com/urfave/cli/v2
go 1.11
require (
github.com/BurntSushi/toml v0.3.1
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d
gopkg.in/yaml.v2 v2.2.3
)

View File

@ -1,14 +0,0 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

2619
vendor/github.com/urfave/cli/v2/godoc-current.txt generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,34 +10,65 @@ import (
"unicode/utf8" "unicode/utf8"
) )
var helpCommand = &Command{ const (
Name: "help", helpName = "help"
Aliases: []string{"h"}, helpAlias = "h"
)
// this instance is to avoid recursion in the ShowCommandHelp which can
// add a help command again
var helpCommandDontUse = &Command{
Name: helpName,
Aliases: []string{helpAlias},
Usage: "Shows a list of commands or help for one command", Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]", ArgsUsage: "[command]",
Action: func(c *Context) error {
args := c.Args()
if args.Present() {
return ShowCommandHelp(c, args.First())
}
_ = ShowAppHelp(c)
return nil
},
} }
var helpSubcommand = &Command{ var helpCommand = &Command{
Name: "help", Name: helpName,
Aliases: []string{"h"}, Aliases: []string{helpAlias},
Usage: "Shows a list of commands or help for one command", Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]", ArgsUsage: "[command]",
Action: func(c *Context) error { Action: func(cCtx *Context) error {
args := c.Args() args := cCtx.Args()
if args.Present() { argsPresent := args.First() != ""
return ShowCommandHelp(c, args.First()) firstArg := args.First()
// This action can be triggered by a "default" action of a command
// or via cmd.Run when cmd == helpCmd. So we have following possibilities
//
// 1 $ app
// 2 $ app help
// 3 $ app foo
// 4 $ app help foo
// 5 $ app foo help
// Case 4. when executing a help command set the context to parent
// to allow resolution of subsequent args. This will transform
// $ app help foo
// to
// $ app foo
// which will then be handled as case 3
if cCtx.Command.Name == helpName || cCtx.Command.Name == helpAlias {
cCtx = cCtx.parentContext
} }
return ShowSubcommandHelp(c) // Case 4. $ app hello foo
// foo is the command for which help needs to be shown
if argsPresent {
return ShowCommandHelp(cCtx, firstArg)
}
// Case 1 & 2
// Special case when running help on main app itself as opposed to indivdual
// commands/subcommands
if cCtx.parentContext.App == nil {
_ = ShowAppHelp(cCtx)
return nil
}
// Case 3, 5
return ShowSubcommandHelp(cCtx)
}, },
} }
@ -59,6 +90,11 @@ var HelpPrinter helpPrinter = printHelp
// HelpPrinterCustom is a function that writes the help output. It is used as // HelpPrinterCustom is a function that writes the help output. It is used as
// the default implementation of HelpPrinter, and may be called directly if // the default implementation of HelpPrinter, and may be called directly if
// the ExtraInfo field is set on an App. // the ExtraInfo field is set on an App.
//
// In the default implementation, if the customFuncs argument contains a
// "wrapAt" key, which is a function which takes no arguments and returns
// an int, this int value will be used to produce a "wrap" function used
// by the default template to wrap long lines.
var HelpPrinterCustom helpPrinterCustom = printHelpCustom var HelpPrinterCustom helpPrinterCustom = printHelpCustom
// VersionPrinter prints the version for the App // VersionPrinter prints the version for the App
@ -71,30 +107,30 @@ func ShowAppHelpAndExit(c *Context, exitCode int) {
} }
// ShowAppHelp is an action that displays the help. // ShowAppHelp is an action that displays the help.
func ShowAppHelp(c *Context) error { func ShowAppHelp(cCtx *Context) error {
tpl := c.App.CustomAppHelpTemplate tpl := cCtx.App.CustomAppHelpTemplate
if tpl == "" { if tpl == "" {
tpl = AppHelpTemplate tpl = AppHelpTemplate
} }
if c.App.ExtraInfo == nil { if cCtx.App.ExtraInfo == nil {
HelpPrinter(c.App.Writer, tpl, c.App) HelpPrinter(cCtx.App.Writer, tpl, cCtx.App)
return nil return nil
} }
customAppData := func() map[string]interface{} { customAppData := func() map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
"ExtraInfo": c.App.ExtraInfo, "ExtraInfo": cCtx.App.ExtraInfo,
} }
} }
HelpPrinterCustom(c.App.Writer, tpl, c.App, customAppData()) HelpPrinterCustom(cCtx.App.Writer, tpl, cCtx.App, customAppData())
return nil return nil
} }
// DefaultAppComplete prints the list of subcommands as the default app completion method // DefaultAppComplete prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) { func DefaultAppComplete(cCtx *Context) {
DefaultCompleteWithFlags(nil)(c) DefaultCompleteWithFlags(nil)(cCtx)
} }
func printCommandSuggestions(commands []*Command, writer io.Writer) { func printCommandSuggestions(commands []*Command, writer io.Writer) {
@ -102,7 +138,7 @@ func printCommandSuggestions(commands []*Command, writer io.Writer) {
if command.Hidden { if command.Hidden {
continue continue
} }
if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { if strings.HasSuffix(os.Getenv("SHELL"), "zsh") {
for _, name := range command.Names() { for _, name := range command.Names() {
_, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage) _, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage)
} }
@ -159,23 +195,30 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) {
} }
} }
func DefaultCompleteWithFlags(cmd *Command) func(c *Context) { func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) {
return func(c *Context) { return func(cCtx *Context) {
if len(os.Args) > 2 { if len(os.Args) > 2 {
lastArg := os.Args[len(os.Args)-2] lastArg := os.Args[len(os.Args)-2]
if strings.HasPrefix(lastArg, "-") { if strings.HasPrefix(lastArg, "-") {
printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer)
if cmd != nil { if cmd != nil {
printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer) printFlagSuggestions(lastArg, cmd.Flags, cCtx.App.Writer)
return
} }
printFlagSuggestions(lastArg, cCtx.App.Flags, cCtx.App.Writer)
return return
} }
} }
if cmd != nil { if cmd != nil {
printCommandSuggestions(cmd.Subcommands, c.App.Writer) printCommandSuggestions(cmd.Subcommands, cCtx.App.Writer)
} else { return
printCommandSuggestions(c.App.Commands, c.App.Writer)
} }
printCommandSuggestions(cCtx.App.Commands, cCtx.App.Writer)
} }
} }
@ -195,9 +238,19 @@ func ShowCommandHelp(ctx *Context, command string) error {
for _, c := range ctx.App.Commands { for _, c := range ctx.App.Commands {
if c.HasName(command) { if c.HasName(command) {
if !ctx.App.HideHelpCommand && !c.HasName(helpName) && len(c.Subcommands) != 0 {
c.Subcommands = append(c.Subcommands, helpCommandDontUse)
}
if !ctx.App.HideHelp && HelpFlag != nil {
c.appendFlag(HelpFlag)
}
templ := c.CustomHelpTemplate templ := c.CustomHelpTemplate
if templ == "" { if templ == "" {
if len(c.Subcommands) == 0 {
templ = CommandHelpTemplate templ = CommandHelpTemplate
} else {
templ = SubcommandHelpTemplate
}
} }
HelpPrinter(ctx.App.Writer, templ, c) HelpPrinter(ctx.App.Writer, templ, c)
@ -207,7 +260,13 @@ func ShowCommandHelp(ctx *Context, command string) error {
} }
if ctx.App.CommandNotFound == nil { if ctx.App.CommandNotFound == nil {
return Exit(fmt.Sprintf("No help topic for '%v'", command), 3) errMsg := fmt.Sprintf("No help topic for '%v'", command)
if ctx.App.Suggest {
if suggestion := SuggestCommand(ctx.App.Commands, command); suggestion != "" {
errMsg += ". " + suggestion
}
}
return Exit(errMsg, 3)
} }
ctx.App.CommandNotFound(ctx, command) ctx.App.CommandNotFound(ctx, command)
@ -221,32 +280,32 @@ func ShowSubcommandHelpAndExit(c *Context, exitCode int) {
} }
// ShowSubcommandHelp prints help for the given subcommand // ShowSubcommandHelp prints help for the given subcommand
func ShowSubcommandHelp(c *Context) error { func ShowSubcommandHelp(cCtx *Context) error {
if c == nil { if cCtx == nil {
return nil return nil
} }
if c.Command != nil { if cCtx.Command != nil {
return ShowCommandHelp(c, c.Command.Name) return ShowCommandHelp(cCtx, cCtx.Command.Name)
} }
return ShowCommandHelp(c, "") return ShowCommandHelp(cCtx, "")
} }
// ShowVersion prints the version number of the App // ShowVersion prints the version number of the App
func ShowVersion(c *Context) { func ShowVersion(cCtx *Context) {
VersionPrinter(c) VersionPrinter(cCtx)
} }
func printVersion(c *Context) { func printVersion(cCtx *Context) {
_, _ = fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) _, _ = fmt.Fprintf(cCtx.App.Writer, "%v version %v\n", cCtx.App.Name, cCtx.App.Version)
} }
// ShowCompletions prints the lists of commands within a given context // ShowCompletions prints the lists of commands within a given context
func ShowCompletions(c *Context) { func ShowCompletions(cCtx *Context) {
a := c.App a := cCtx.App
if a != nil && a.BashComplete != nil { if a != nil && a.BashComplete != nil {
a.BashComplete(c) a.BashComplete(cCtx)
} }
} }
@ -268,18 +327,48 @@ func ShowCommandCompletions(ctx *Context, command string) {
// The customFuncs map will be combined with a default template.FuncMap to // The customFuncs map will be combined with a default template.FuncMap to
// allow using arbitrary functions in template rendering. // allow using arbitrary functions in template rendering.
func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) { func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) {
const maxLineLength = 10000
funcMap := template.FuncMap{ funcMap := template.FuncMap{
"join": strings.Join, "join": strings.Join,
"subtract": subtract,
"indent": indent, "indent": indent,
"nindent": nindent, "nindent": nindent,
"trim": strings.TrimSpace, "trim": strings.TrimSpace,
"wrap": func(input string, offset int) string { return wrap(input, offset, maxLineLength) },
"offset": offset,
"offsetCommands": offsetCommands,
} }
if customFuncs["wrapAt"] != nil {
if wa, ok := customFuncs["wrapAt"]; ok {
if waf, ok := wa.(func() int); ok {
wrapAt := waf()
customFuncs["wrap"] = func(input string, offset int) string {
return wrap(input, offset, wrapAt)
}
}
}
}
for key, value := range customFuncs { for key, value := range customFuncs {
funcMap[key] = value funcMap[key] = value
} }
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
t.New("helpNameTemplate").Parse(helpNameTemplate)
t.New("usageTemplate").Parse(usageTemplate)
t.New("descriptionTemplate").Parse(descriptionTemplate)
t.New("visibleCommandTemplate").Parse(visibleCommandTemplate)
t.New("copyrightTemplate").Parse(copyrightTemplate)
t.New("versionTemplate").Parse(versionTemplate)
t.New("visibleFlagCategoryTemplate").Parse(visibleFlagCategoryTemplate)
t.New("visibleFlagTemplate").Parse(visibleFlagTemplate)
t.New("visibleGlobalFlagCategoryTemplate").Parse(strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1))
t.New("authorsTemplate").Parse(authorsTemplate)
t.New("visibleCommandCategoryTemplate").Parse(visibleCommandCategoryTemplate)
err := t.Execute(w, data) err := t.Execute(w, data)
if err != nil { if err != nil {
@ -297,20 +386,20 @@ func printHelp(out io.Writer, templ string, data interface{}) {
HelpPrinterCustom(out, templ, data, nil) HelpPrinterCustom(out, templ, data, nil)
} }
func checkVersion(c *Context) bool { func checkVersion(cCtx *Context) bool {
found := false found := false
for _, name := range VersionFlag.Names() { for _, name := range VersionFlag.Names() {
if c.Bool(name) { if cCtx.Bool(name) {
found = true found = true
} }
} }
return found return found
} }
func checkHelp(c *Context) bool { func checkHelp(cCtx *Context) bool {
found := false found := false
for _, name := range HelpFlag.Names() { for _, name := range HelpFlag.Names() {
if c.Bool(name) { if cCtx.Bool(name) {
found = true found = true
} }
} }
@ -326,9 +415,9 @@ func checkCommandHelp(c *Context, name string) bool {
return false return false
} }
func checkSubcommandHelp(c *Context) bool { func checkSubcommandHelp(cCtx *Context) bool {
if c.Bool("h") || c.Bool("help") { if cCtx.Bool("h") || cCtx.Bool("help") {
_ = ShowSubcommandHelp(c) _ = ShowSubcommandHelp(cCtx)
return true return true
} }
@ -350,20 +439,20 @@ func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
return true, arguments[:pos] return true, arguments[:pos]
} }
func checkCompletions(c *Context) bool { func checkCompletions(cCtx *Context) bool {
if !c.shellComplete { if !cCtx.shellComplete {
return false return false
} }
if args := c.Args(); args.Present() { if args := cCtx.Args(); args.Present() {
name := args.First() name := args.First()
if cmd := c.App.Command(name); cmd != nil { if cmd := cCtx.App.Command(name); cmd != nil {
// let the command handle the completion // let the command handle the completion
return false return false
} }
} }
ShowCompletions(c) ShowCompletions(cCtx)
return true return true
} }
@ -376,6 +465,10 @@ func checkCommandCompletions(c *Context, name string) bool {
return true return true
} }
func subtract(a, b int) int {
return a - b
}
func indent(spaces int, v string) string { func indent(spaces int, v string) string {
pad := strings.Repeat(" ", spaces) pad := strings.Repeat(" ", spaces)
return pad + strings.Replace(v, "\n", "\n"+pad, -1) return pad + strings.Replace(v, "\n", "\n"+pad, -1)
@ -384,3 +477,83 @@ func indent(spaces int, v string) string {
func nindent(spaces int, v string) string { func nindent(spaces int, v string) string {
return "\n" + indent(spaces, v) return "\n" + indent(spaces, v)
} }
func wrap(input string, offset int, wrapAt int) string {
var ss []string
lines := strings.Split(input, "\n")
padding := strings.Repeat(" ", offset)
for i, line := range lines {
if line == "" {
ss = append(ss, line)
} else {
wrapped := wrapLine(line, offset, wrapAt, padding)
if i == 0 {
ss = append(ss, wrapped)
} else {
ss = append(ss, padding+wrapped)
}
}
}
return strings.Join(ss, "\n")
}
func wrapLine(input string, offset int, wrapAt int, padding string) string {
if wrapAt <= offset || len(input) <= wrapAt-offset {
return input
}
lineWidth := wrapAt - offset
words := strings.Fields(input)
if len(words) == 0 {
return input
}
wrapped := words[0]
spaceLeft := lineWidth - len(wrapped)
for _, word := range words[1:] {
if len(word)+1 > spaceLeft {
wrapped += "\n" + padding + word
spaceLeft = lineWidth - len(word)
} else {
wrapped += " " + word
spaceLeft -= 1 + len(word)
}
}
return wrapped
}
func offset(input string, fixed int) int {
return len(input) + fixed
}
// this function tries to find the max width of the names column
// so say we have the following rows for help
//
// foo1, foo2, foo3 some string here
// bar1, b2 some other string here
//
// We want to offset the 2nd row usage by some amount so that everything
// is aligned
//
// foo1, foo2, foo3 some string here
// bar1, b2 some other string here
//
// to find that offset we find the length of all the rows and use the max
// to calculate the offset
func offsetCommands(cmds []*Command, fixed int) int {
var max int = 0
for _, cmd := range cmds {
s := strings.Join(cmd.Names(), ", ")
if len(s) > max {
max = len(s)
}
}
return max + fixed
}

View File

@ -0,0 +1,5 @@
mkdocs-git-revision-date-localized-plugin~=1.0
mkdocs-material-extensions~=1.0
mkdocs-material~=8.2
mkdocs~=1.3
pygments~=2.12

107
vendor/github.com/urfave/cli/v2/mkdocs.yml generated vendored Normal file
View File

@ -0,0 +1,107 @@
# NOTE: the mkdocs dependencies will need to be installed out of
# band until this whole thing gets more automated:
#
# pip install -r mkdocs-requirements.txt
#
site_name: urfave/cli
site_url: https://cli.urfave.org/
repo_url: https://github.com/urfave/cli
edit_uri: edit/main/docs/
nav:
- Home:
- Welcome: index.md
- Contributing: CONTRIBUTING.md
- Code of Conduct: CODE_OF_CONDUCT.md
- Releasing: RELEASING.md
- Security: SECURITY.md
- Migrate v1 to v2: migrate-v1-to-v2.md
- v2 Manual:
- Getting Started: v2/getting-started.md
- Migrating From Older Releases: v2/migrating-from-older-releases.md
- Examples:
- Greet: v2/examples/greet.md
- Arguments: v2/examples/arguments.md
- Flags: v2/examples/flags.md
- Subcommands: v2/examples/subcommands.md
- Subcommands Categories: v2/examples/subcommands-categories.md
- Exit Codes: v2/examples/exit-codes.md
- Combining Short Options: v2/examples/combining-short-options.md
- Bash Completions: v2/examples/bash-completions.md
- Generated Help Text: v2/examples/generated-help-text.md
- Version Flag: v2/examples/version-flag.md
- Timestamp Flag: v2/examples/timestamp-flag.md
- Suggestions: v2/examples/suggestions.md
- Full API Example: v2/examples/full-api-example.md
- v1 Manual:
- Getting Started: v1/getting-started.md
- Migrating to v2: v1/migrating-to-v2.md
- Examples:
- Greet: v1/examples/greet.md
- Arguments: v1/examples/arguments.md
- Flags: v1/examples/flags.md
- Subcommands: v1/examples/subcommands.md
- Subcommands (Categories): v1/examples/subcommands-categories.md
- Exit Codes: v1/examples/exit-codes.md
- Combining Short Options: v1/examples/combining-short-options.md
- Bash Completions: v1/examples/bash-completions.md
- Generated Help Text: v1/examples/generated-help-text.md
- Version Flag: v1/examples/version-flag.md
theme:
name: material
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
toggle:
icon: material/brightness-4
name: dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
toggle:
icon: material/brightness-7
name: light mode
features:
- content.code.annotate
- navigation.top
- navigation.instant
- navigation.expand
- navigation.sections
- navigation.tabs
- navigation.tabs.sticky
plugins:
- git-revision-date-localized
- search
- tags
# NOTE: this is the recommended configuration from
# https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration
markdown_extensions:
- abbr
- admonition
- attr_list
- def_list
- footnotes
- meta
- md_in_html
- toc:
permalink: true
- pymdownx.arithmatex:
generic: true
- pymdownx.betterem:
smart_enable: all
- pymdownx.caret
- pymdownx.details
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
- pymdownx.highlight
- pymdownx.inlinehilite
- pymdownx.keys
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.tilde

View File

@ -26,9 +26,8 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple
return err return err
} }
errStr := err.Error() trimmed, trimErr := flagFromError(err)
trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -") if trimErr != nil {
if errStr == trimmed {
return err return err
} }
@ -47,7 +46,10 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple
} }
// swap current argument with the split version // swap current argument with the split version
args = append(args[:i], append(shortOpts, args[i+1:]...)...) // do not include args that parsed correctly so far as it would
// trigger Value.Set() on those args and would result in
// duplicates for slice type flags
args = append(shortOpts, args[i+1:]...)
argsWereSplit = true argsWereSplit = true
break break
} }
@ -57,14 +59,20 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple
if !argsWereSplit { if !argsWereSplit {
return err return err
} }
}
}
// Since custom parsing failed, replace the flag set before retrying const providedButNotDefinedErrMsg = "flag provided but not defined: -"
newSet, err := ip.newFlagSet()
if err != nil { // flagFromError tries to parse a provided flag from an error message. If the
return err // parsing fials, it returns the input error and an empty string
} func flagFromError(err error) (string, error) {
*set = *newSet errStr := err.Error()
trimmed := strings.TrimPrefix(errStr, providedButNotDefinedErrMsg)
if errStr == trimmed {
return "", err
} }
return trimmed, nil
} }
func splitShortOptions(set *flag.FlagSet, arg string) []string { func splitShortOptions(set *flag.FlagSet, arg string) []string {

293
vendor/github.com/urfave/cli/v2/sliceflag.go generated vendored Normal file
View File

@ -0,0 +1,293 @@
//go:build go1.18
// +build go1.18
package cli
import (
"flag"
"reflect"
)
type (
// SliceFlag extends implementations like StringSliceFlag and IntSliceFlag with support for using slices directly,
// as Value and/or Destination.
// See also SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag, MultiIntFlag.
SliceFlag[T SliceFlagTarget[E], S ~[]E, E any] struct {
Target T
Value S
Destination *S
}
// SliceFlagTarget models a target implementation for use with SliceFlag.
// The three methods, SetValue, SetDestination, and GetDestination, are necessary to propagate Value and
// Destination, where Value is propagated inwards (initially), and Destination is propagated outwards (on every
// update).
SliceFlagTarget[E any] interface {
Flag
RequiredFlag
DocGenerationFlag
VisibleFlag
CategorizableFlag
// SetValue should propagate the given slice to the target, ideally as a new value.
// Note that a nil slice should nil/clear any existing value (modelled as ~[]E).
SetValue(slice []E)
// SetDestination should propagate the given slice to the target, ideally as a new value.
// Note that a nil slice should nil/clear any existing value (modelled as ~*[]E).
SetDestination(slice []E)
// GetDestination should return the current value referenced by any destination, or nil if nil/unset.
GetDestination() []E
}
// MultiStringFlag extends StringSliceFlag with support for using slices directly, as Value and/or Destination.
// See also SliceFlag.
MultiStringFlag = SliceFlag[*StringSliceFlag, []string, string]
// MultiFloat64Flag extends Float64SliceFlag with support for using slices directly, as Value and/or Destination.
// See also SliceFlag.
MultiFloat64Flag = SliceFlag[*Float64SliceFlag, []float64, float64]
// MultiInt64Flag extends Int64SliceFlag with support for using slices directly, as Value and/or Destination.
// See also SliceFlag.
MultiInt64Flag = SliceFlag[*Int64SliceFlag, []int64, int64]
// MultiIntFlag extends IntSliceFlag with support for using slices directly, as Value and/or Destination.
// See also SliceFlag.
MultiIntFlag = SliceFlag[*IntSliceFlag, []int, int]
flagValueHook struct {
value Generic
hook func()
}
)
var (
// compile time assertions
_ SliceFlagTarget[string] = (*StringSliceFlag)(nil)
_ SliceFlagTarget[string] = (*SliceFlag[*StringSliceFlag, []string, string])(nil)
_ SliceFlagTarget[string] = (*MultiStringFlag)(nil)
_ SliceFlagTarget[float64] = (*MultiFloat64Flag)(nil)
_ SliceFlagTarget[int64] = (*MultiInt64Flag)(nil)
_ SliceFlagTarget[int] = (*MultiIntFlag)(nil)
_ Generic = (*flagValueHook)(nil)
_ Serializer = (*flagValueHook)(nil)
)
func (x *SliceFlag[T, S, E]) Apply(set *flag.FlagSet) error {
x.Target.SetValue(x.convertSlice(x.Value))
destination := x.Destination
if destination == nil {
x.Target.SetDestination(nil)
return x.Target.Apply(set)
}
x.Target.SetDestination(x.convertSlice(*destination))
return applyFlagValueHook(set, x.Target.Apply, func() {
*destination = x.Target.GetDestination()
})
}
func (x *SliceFlag[T, S, E]) convertSlice(slice S) []E {
result := make([]E, len(slice))
copy(result, slice)
return result
}
func (x *SliceFlag[T, S, E]) SetValue(slice S) {
x.Value = slice
}
func (x *SliceFlag[T, S, E]) SetDestination(slice S) {
if slice != nil {
x.Destination = &slice
} else {
x.Destination = nil
}
}
func (x *SliceFlag[T, S, E]) GetDestination() S {
if destination := x.Destination; destination != nil {
return *destination
}
return nil
}
func (x *SliceFlag[T, S, E]) String() string { return x.Target.String() }
func (x *SliceFlag[T, S, E]) Names() []string { return x.Target.Names() }
func (x *SliceFlag[T, S, E]) IsSet() bool { return x.Target.IsSet() }
func (x *SliceFlag[T, S, E]) IsRequired() bool { return x.Target.IsRequired() }
func (x *SliceFlag[T, S, E]) TakesValue() bool { return x.Target.TakesValue() }
func (x *SliceFlag[T, S, E]) GetUsage() string { return x.Target.GetUsage() }
func (x *SliceFlag[T, S, E]) GetValue() string { return x.Target.GetValue() }
func (x *SliceFlag[T, S, E]) GetDefaultText() string { return x.Target.GetDefaultText() }
func (x *SliceFlag[T, S, E]) GetEnvVars() []string { return x.Target.GetEnvVars() }
func (x *SliceFlag[T, S, E]) IsVisible() bool { return x.Target.IsVisible() }
func (x *SliceFlag[T, S, E]) GetCategory() string { return x.Target.GetCategory() }
func (x *flagValueHook) Set(value string) error {
if err := x.value.Set(value); err != nil {
return err
}
x.hook()
return nil
}
func (x *flagValueHook) String() string {
// note: this is necessary due to the way Go's flag package handles defaults
isZeroValue := func(f flag.Value, v string) bool {
/*
https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/flag/flag.go;drc=2580d0e08d5e9f979b943758d3c49877fb2324cb;l=453
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Build a zero value of the flag's Value type, and see if the
// result of calling its String method equals the value passed in.
// This works unless the Value type is itself an interface type.
typ := reflect.TypeOf(f)
var z reflect.Value
if typ.Kind() == reflect.Pointer {
z = reflect.New(typ.Elem())
} else {
z = reflect.Zero(typ)
}
return v == z.Interface().(flag.Value).String()
}
if x.value != nil {
// only return non-empty if not the same string as returned by the zero value
if s := x.value.String(); !isZeroValue(x.value, s) {
return s
}
}
return ``
}
func (x *flagValueHook) Serialize() string {
if value, ok := x.value.(Serializer); ok {
return value.Serialize()
}
return x.String()
}
// applyFlagValueHook wraps calls apply then wraps flags to call a hook function on update and after initial apply.
func applyFlagValueHook(set *flag.FlagSet, apply func(set *flag.FlagSet) error, hook func()) error {
if apply == nil || set == nil || hook == nil {
panic(`invalid input`)
}
var tmp flag.FlagSet
if err := apply(&tmp); err != nil {
return err
}
tmp.VisitAll(func(f *flag.Flag) { set.Var(&flagValueHook{value: f.Value, hook: hook}, f.Name, f.Usage) })
hook()
return nil
}
// newSliceFlagValue is for implementing SliceFlagTarget.SetValue and SliceFlagTarget.SetDestination.
// It's e.g. as part of StringSliceFlag.SetValue, using the factory NewStringSlice.
func newSliceFlagValue[R any, S ~[]E, E any](factory func(defaults ...E) *R, defaults S) *R {
if defaults == nil {
return nil
}
return factory(defaults...)
}
// unwrapFlagValue strips any/all *flagValueHook wrappers.
func unwrapFlagValue(v flag.Value) flag.Value {
for {
h, ok := v.(*flagValueHook)
if !ok {
return v
}
v = h.value
}
}
// NOTE: the methods below are in this file to make use of the build constraint
func (f *Float64SliceFlag) SetValue(slice []float64) {
f.Value = newSliceFlagValue(NewFloat64Slice, slice)
}
func (f *Float64SliceFlag) SetDestination(slice []float64) {
f.Destination = newSliceFlagValue(NewFloat64Slice, slice)
}
func (f *Float64SliceFlag) GetDestination() []float64 {
if destination := f.Destination; destination != nil {
return destination.Value()
}
return nil
}
func (f *Int64SliceFlag) SetValue(slice []int64) {
f.Value = newSliceFlagValue(NewInt64Slice, slice)
}
func (f *Int64SliceFlag) SetDestination(slice []int64) {
f.Destination = newSliceFlagValue(NewInt64Slice, slice)
}
func (f *Int64SliceFlag) GetDestination() []int64 {
if destination := f.Destination; destination != nil {
return destination.Value()
}
return nil
}
func (f *IntSliceFlag) SetValue(slice []int) {
f.Value = newSliceFlagValue(NewIntSlice, slice)
}
func (f *IntSliceFlag) SetDestination(slice []int) {
f.Destination = newSliceFlagValue(NewIntSlice, slice)
}
func (f *IntSliceFlag) GetDestination() []int {
if destination := f.Destination; destination != nil {
return destination.Value()
}
return nil
}
func (f *StringSliceFlag) SetValue(slice []string) {
f.Value = newSliceFlagValue(NewStringSlice, slice)
}
func (f *StringSliceFlag) SetDestination(slice []string) {
f.Destination = newSliceFlagValue(NewStringSlice, slice)
}
func (f *StringSliceFlag) GetDestination() []string {
if destination := f.Destination; destination != nil {
return destination.Value()
}
return nil
}

10
vendor/github.com/urfave/cli/v2/sliceflag_pre18.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
//go:build !go1.18
// +build !go1.18
package cli
import (
"flag"
)
func unwrapFlagValue(v flag.Value) flag.Value { return v }

60
vendor/github.com/urfave/cli/v2/suggestions.go generated vendored Normal file
View File

@ -0,0 +1,60 @@
package cli
import (
"fmt"
"github.com/xrash/smetrics"
)
func jaroWinkler(a, b string) float64 {
// magic values are from https://github.com/xrash/smetrics/blob/039620a656736e6ad994090895784a7af15e0b80/jaro-winkler.go#L8
const (
boostThreshold = 0.7
prefixSize = 4
)
return smetrics.JaroWinkler(a, b, boostThreshold, prefixSize)
}
func suggestFlag(flags []Flag, provided string, hideHelp bool) string {
distance := 0.0
suggestion := ""
for _, flag := range flags {
flagNames := flag.Names()
if !hideHelp {
flagNames = append(flagNames, HelpFlag.Names()...)
}
for _, name := range flagNames {
newDistance := jaroWinkler(name, provided)
if newDistance > distance {
distance = newDistance
suggestion = name
}
}
}
if len(suggestion) == 1 {
suggestion = "-" + suggestion
} else if len(suggestion) > 1 {
suggestion = "--" + suggestion
}
return suggestion
}
// suggestCommand takes a list of commands and a provided string to suggest a
// command name
func suggestCommand(commands []*Command, provided string) (suggestion string) {
distance := 0.0
for _, command := range commands {
for _, name := range append(command.Names(), helpName, helpAlias) {
newDistance := jaroWinkler(name, provided)
if newDistance > distance {
distance = newDistance
suggestion = name
}
}
}
return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion)
}

View File

@ -1,82 +1,103 @@
package cli package cli
var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}`
var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}`
var descriptionTemplate = `{{wrap .Description 3}}`
var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}`
var visibleCommandTemplate = `{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}}
{{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}`
var visibleCommandCategoryTemplate = `{{range .VisibleCategories}}{{if .Name}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{template "visibleCommandTemplate" .}}{{end}}{{end}}`
var visibleFlagCategoryTemplate = `{{range .VisibleFlagCategories}}
{{if .Name}}{{.Name}}
{{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}}
{{else}}{{$e}}
{{end}}{{end}}{{end}}`
var visibleFlagTemplate = `{{range $i, $e := .VisibleFlags}}
{{wrap $e.String 6}}{{end}}`
var versionTemplate = `{{if .Version}}{{if not .HideVersion}}
VERSION:
{{.Version}}{{end}}{{end}}`
var copyrightTemplate = `{{wrap .Copyright 3}}`
// AppHelpTemplate is the text template for the Default help topic. // AppHelpTemplate is the text template for the Default help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var AppHelpTemplate = `NAME: var AppHelpTemplate = `NAME:
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{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}} {{if .UsageText}}{{wrap .UsageText 3}}{{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}}
VERSION: VERSION:
{{.Version}}{{end}}{{end}}{{if .Description}} {{.Version}}{{end}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{.Description | nindent 3 | trim}}{{end}}{{if len .Authors}} {{template "descriptionTemplate" .}}{{end}}
{{- if len .Authors}}
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: AUTHOR{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}}
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
COMMANDS:{{range .VisibleCategories}}{{if .Name}} COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS: GLOBAL OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
{{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}}
COPYRIGHT: COPYRIGHT:
{{.Copyright}}{{end}} {{template "copyrightTemplate" .}}{{end}}
` `
// CommandHelpTemplate is the text template for the command help topic. // CommandHelpTemplate is the text template for the command help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var CommandHelpTemplate = `NAME: var CommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} {{template "usageTemplate" .}}{{if .Category}}
CATEGORY: CATEGORY:
{{.Category}}{{end}}{{if .Description}} {{.Category}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlags}} {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
OPTIONS: OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
{{range .VisibleFlags}}{{.}}
{{end}}{{end}} OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}
` `
// SubcommandHelpTemplate is the text template for the subcommand help topic. // SubcommandHelpTemplate is the text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME: var SubcommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}} {{template "helpNameTemplate" .}}
USAGE: USAGE:
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
DESCRIPTION: DESCRIPTION:
{{.Description | nindent 3 | trim}}{{end}} {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
COMMANDS:{{range .VisibleCategories}}{{if .Name}} COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
OPTIONS: OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
{{range .VisibleFlags}}{{.}}
{{end}}{{end}} OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}
` `
var MarkdownDocTemplate = `% {{ .App.Name }} 8 var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }}
# NAME {{end}}# NAME
{{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} {{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }}
@ -86,16 +107,18 @@ var MarkdownDocTemplate = `% {{ .App.Name }} 8
{{ if .SynopsisArgs }} {{ if .SynopsisArgs }}
` + "```" + ` ` + "```" + `
{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` {{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + `
{{ end }}{{ if .App.UsageText }} {{ end }}{{ if .App.Description }}
# DESCRIPTION # DESCRIPTION
{{ .App.UsageText }} {{ .App.Description }}
{{ end }} {{ end }}
**Usage**: **Usage**:
` + "```" + ` ` + "```" + `{{ if .App.UsageText }}
{{ .App.UsageText }}
{{ else }}
{{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] {{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
` + "```" + ` {{ end }}` + "```" + `
{{ if .GlobalArgs }} {{ if .GlobalArgs }}
# GLOBAL OPTIONS # GLOBAL OPTIONS
{{ range $v := .GlobalArgs }} {{ range $v := .GlobalArgs }}

798
vendor/github.com/urfave/cli/v2/zz_generated.flags.go generated vendored Normal file
View File

@ -0,0 +1,798 @@
// WARNING: this file is generated. DO NOT EDIT
package cli
import "time"
// Float64SliceFlag is a flag with type *Float64Slice
type Float64SliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *Float64Slice
Destination *Float64Slice
Aliases []string
EnvVars []string
Action func(*Context, []float64) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Float64SliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *Float64SliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Float64SliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *Float64SliceFlag) IsVisible() bool {
return !f.Hidden
}
// GenericFlag is a flag with type Generic
type GenericFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value Generic
Destination Generic
Aliases []string
EnvVars []string
TakesFile bool
Action func(*Context, interface{}) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *GenericFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *GenericFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *GenericFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *GenericFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *GenericFlag) IsVisible() bool {
return !f.Hidden
}
// Int64SliceFlag is a flag with type *Int64Slice
type Int64SliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *Int64Slice
Destination *Int64Slice
Aliases []string
EnvVars []string
Action func(*Context, []int64) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Int64SliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *Int64SliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Int64SliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *Int64SliceFlag) IsVisible() bool {
return !f.Hidden
}
// IntSliceFlag is a flag with type *IntSlice
type IntSliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *IntSlice
Destination *IntSlice
Aliases []string
EnvVars []string
Action func(*Context, []int) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *IntSliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *IntSliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *IntSliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *IntSliceFlag) IsVisible() bool {
return !f.Hidden
}
// PathFlag is a flag with type Path
type PathFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value Path
Destination *Path
Aliases []string
EnvVars []string
TakesFile bool
Action func(*Context, Path) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *PathFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *PathFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *PathFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *PathFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *PathFlag) IsVisible() bool {
return !f.Hidden
}
// StringSliceFlag is a flag with type *StringSlice
type StringSliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *StringSlice
Destination *StringSlice
Aliases []string
EnvVars []string
TakesFile bool
Action func(*Context, []string) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *StringSliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *StringSliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *StringSliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *StringSliceFlag) IsVisible() bool {
return !f.Hidden
}
// TimestampFlag is a flag with type *Timestamp
type TimestampFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *Timestamp
Destination *Timestamp
Aliases []string
EnvVars []string
Layout string
Timezone *time.Location
Action func(*Context, *time.Time) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *TimestampFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *TimestampFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *TimestampFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *TimestampFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *TimestampFlag) IsVisible() bool {
return !f.Hidden
}
// Uint64SliceFlag is a flag with type *Uint64Slice
type Uint64SliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *Uint64Slice
Destination *Uint64Slice
Aliases []string
EnvVars []string
Action func(*Context, []uint64) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Uint64SliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *Uint64SliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Uint64SliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *Uint64SliceFlag) IsVisible() bool {
return !f.Hidden
}
// UintSliceFlag is a flag with type *UintSlice
type UintSliceFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value *UintSlice
Destination *UintSlice
Aliases []string
EnvVars []string
Action func(*Context, []uint) error
}
// IsSet returns whether or not the flag has been set through env or file
func (f *UintSliceFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *UintSliceFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *UintSliceFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *UintSliceFlag) IsVisible() bool {
return !f.Hidden
}
// BoolFlag is a flag with type bool
type BoolFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value bool
Destination *bool
Aliases []string
EnvVars []string
Count *int
Action func(*Context, bool) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *BoolFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *BoolFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *BoolFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *BoolFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *BoolFlag) IsVisible() bool {
return !f.Hidden
}
// Float64Flag is a flag with type float64
type Float64Flag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value float64
Destination *float64
Aliases []string
EnvVars []string
Action func(*Context, float64) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *Float64Flag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Float64Flag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *Float64Flag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Float64Flag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *Float64Flag) IsVisible() bool {
return !f.Hidden
}
// IntFlag is a flag with type int
type IntFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value int
Destination *int
Aliases []string
EnvVars []string
Base int
Action func(*Context, int) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *IntFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *IntFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *IntFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *IntFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *IntFlag) IsVisible() bool {
return !f.Hidden
}
// Int64Flag is a flag with type int64
type Int64Flag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value int64
Destination *int64
Aliases []string
EnvVars []string
Base int
Action func(*Context, int64) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *Int64Flag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Int64Flag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *Int64Flag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Int64Flag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *Int64Flag) IsVisible() bool {
return !f.Hidden
}
// StringFlag is a flag with type string
type StringFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value string
Destination *string
Aliases []string
EnvVars []string
TakesFile bool
Action func(*Context, string) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *StringFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *StringFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *StringFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *StringFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *StringFlag) IsVisible() bool {
return !f.Hidden
}
// DurationFlag is a flag with type time.Duration
type DurationFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value time.Duration
Destination *time.Duration
Aliases []string
EnvVars []string
Action func(*Context, time.Duration) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *DurationFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *DurationFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *DurationFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *DurationFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *DurationFlag) IsVisible() bool {
return !f.Hidden
}
// UintFlag is a flag with type uint
type UintFlag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value uint
Destination *uint
Aliases []string
EnvVars []string
Base int
Action func(*Context, uint) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *UintFlag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *UintFlag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *UintFlag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *UintFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *UintFlag) IsVisible() bool {
return !f.Hidden
}
// Uint64Flag is a flag with type uint64
type Uint64Flag struct {
Name string
Category string
DefaultText string
FilePath string
Usage string
Required bool
Hidden bool
HasBeenSet bool
Value uint64
Destination *uint64
Aliases []string
EnvVars []string
Base int
Action func(*Context, uint64) error
}
// String returns a readable representation of this value (for usage defaults)
func (f *Uint64Flag) String() string {
return FlagStringer(f)
}
// IsSet returns whether or not the flag has been set through env or file
func (f *Uint64Flag) IsSet() bool {
return f.HasBeenSet
}
// Names returns the names of the flag
func (f *Uint64Flag) Names() []string {
return FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether or not the flag is required
func (f *Uint64Flag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false
func (f *Uint64Flag) IsVisible() bool {
return !f.Hidden
}
// vim:ro

9
vendor/github.com/xrash/smetrics/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,9 @@
language: go
go:
- 1.11
- 1.12
- 1.13
- 1.14.x
- master
script:
- cd tests && make

21
vendor/github.com/xrash/smetrics/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (C) 2016 Felipe da Cunha Gonçalves
All Rights Reserved.
MIT LICENSE
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

49
vendor/github.com/xrash/smetrics/README.md generated vendored Normal file
View File

@ -0,0 +1,49 @@
[![Build Status](https://travis-ci.org/xrash/smetrics.svg?branch=master)](http://travis-ci.org/xrash/smetrics)
# smetrics
`smetrics` is "string metrics".
Package smetrics provides a bunch of algorithms for calculating the distance between strings.
There are implementations for calculating the popular Levenshtein distance (aka Edit Distance or Wagner-Fischer), as well as the Jaro distance, the Jaro-Winkler distance, and more.
# How to import
```go
import "github.com/xrash/smetrics"
```
# Documentation
Go to [https://pkg.go.dev/github.com/xrash/smetrics](https://pkg.go.dev/github.com/xrash/smetrics) for complete documentation.
# Example
```go
package main
import (
"github.com/xrash/smetrics"
)
func main() {
smetrics.WagnerFischer("POTATO", "POTATTO", 1, 1, 2)
smetrics.WagnerFischer("MOUSE", "HOUSE", 2, 2, 4)
smetrics.Ukkonen("POTATO", "POTATTO", 1, 1, 2)
smetrics.Ukkonen("MOUSE", "HOUSE", 2, 2, 4)
smetrics.Jaro("AL", "AL")
smetrics.Jaro("MARTHA", "MARHTA")
smetrics.JaroWinkler("AL", "AL", 0.7, 4)
smetrics.JaroWinkler("MARTHA", "MARHTA", 0.7, 4)
smetrics.Soundex("Euler")
smetrics.Soundex("Ellery")
smetrics.Hamming("aaa", "aaa")
smetrics.Hamming("aaa", "aab")
}
```

19
vendor/github.com/xrash/smetrics/doc.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
/*
Package smetrics provides a bunch of algorithms for calculating
the distance between strings.
There are implementations for calculating the popular Levenshtein
distance (aka Edit Distance or Wagner-Fischer), as well as the Jaro
distance, the Jaro-Winkler distance, and more.
For the Levenshtein distance, you can use the functions WagnerFischer()
and Ukkonen(). Read the documentation on these functions.
For the Jaro and Jaro-Winkler algorithms, check the functions
Jaro() and JaroWinkler(). Read the documentation on these functions.
For the Soundex algorithm, check the function Soundex().
For the Hamming distance algorithm, check the function Hamming().
*/
package smetrics

25
vendor/github.com/xrash/smetrics/hamming.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
package smetrics
import (
"fmt"
)
// The Hamming distance is the minimum number of substitutions required to change string A into string B. Both strings must have the same size. If the strings have different sizes, the function returns an error.
func Hamming(a, b string) (int, error) {
al := len(a)
bl := len(b)
if al != bl {
return -1, fmt.Errorf("strings are not equal (len(a)=%d, len(b)=%d)", al, bl)
}
var difference = 0
for i := range a {
if a[i] != b[i] {
difference = difference + 1
}
}
return difference, nil
}

28
vendor/github.com/xrash/smetrics/jaro-winkler.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
package smetrics
import (
"math"
)
// The Jaro-Winkler distance. The result is 1 for equal strings, and 0 for completely different strings. It is commonly used on Record Linkage stuff, thus it tries to be accurate for common typos when writing real names such as person names and street names.
// Jaro-Winkler is a modification of the Jaro algorithm. It works by first running Jaro, then boosting the score of exact matches at the beginning of the strings. Because of that, it introduces two more parameters: the boostThreshold and the prefixSize. These are commonly set to 0.7 and 4, respectively.
func JaroWinkler(a, b string, boostThreshold float64, prefixSize int) float64 {
j := Jaro(a, b)
if j <= boostThreshold {
return j
}
prefixSize = int(math.Min(float64(len(a)), math.Min(float64(prefixSize), float64(len(b)))))
var prefixMatch float64
for i := 0; i < prefixSize; i++ {
if a[i] == b[i] {
prefixMatch++
} else {
break
}
}
return j + 0.1*prefixMatch*(1.0-j)
}

86
vendor/github.com/xrash/smetrics/jaro.go generated vendored Normal file
View File

@ -0,0 +1,86 @@
package smetrics
import (
"math"
)
// The Jaro distance. The result is 1 for equal strings, and 0 for completely different strings.
func Jaro(a, b string) float64 {
// If both strings are zero-length, they are completely equal,
// therefore return 1.
if len(a) == 0 && len(b) == 0 {
return 1
}
// If one string is zero-length, strings are completely different,
// therefore return 0.
if len(a) == 0 || len(b) == 0 {
return 0
}
// Define the necessary variables for the algorithm.
la := float64(len(a))
lb := float64(len(b))
matchRange := int(math.Max(0, math.Floor(math.Max(la, lb)/2.0)-1))
matchesA := make([]bool, len(a))
matchesB := make([]bool, len(b))
var matches float64 = 0
// Step 1: Matches
// Loop through each character of the first string,
// looking for a matching character in the second string.
for i := 0; i < len(a); i++ {
start := int(math.Max(0, float64(i-matchRange)))
end := int(math.Min(lb-1, float64(i+matchRange)))
for j := start; j <= end; j++ {
if matchesB[j] {
continue
}
if a[i] == b[j] {
matchesA[i] = true
matchesB[j] = true
matches++
break
}
}
}
// If there are no matches, strings are completely different,
// therefore return 0.
if matches == 0 {
return 0
}
// Step 2: Transpositions
// Loop through the matches' arrays, looking for
// unaligned matches. Count the number of unaligned matches.
unaligned := 0
j := 0
for i := 0; i < len(a); i++ {
if !matchesA[i] {
continue
}
for !matchesB[j] {
j++
}
if a[i] != b[j] {
unaligned++
}
j++
}
// The number of unaligned matches divided by two, is the number of _transpositions_.
transpositions := math.Floor(float64(unaligned / 2))
// Jaro distance is the average between these three numbers:
// 1. matches / length of string A
// 2. matches / length of string B
// 3. (matches - transpositions/matches)
// So, all that divided by three is the final result.
return ((matches / la) + (matches / lb) + ((matches - transpositions) / matches)) / 3.0
}

41
vendor/github.com/xrash/smetrics/soundex.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
package smetrics
import (
"strings"
)
// The Soundex encoding. It is a phonetic algorithm that considers how the words sound in English. Soundex maps a string to a 4-byte code consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same code.
func Soundex(s string) string {
m := map[byte]string{
'B': "1", 'P': "1", 'F': "1", 'V': "1",
'C': "2", 'S': "2", 'K': "2", 'G': "2", 'J': "2", 'Q': "2", 'X': "2", 'Z': "2",
'D': "3", 'T': "3",
'L': "4",
'M': "5", 'N': "5",
'R': "6",
}
s = strings.ToUpper(s)
r := string(s[0])
p := s[0]
for i := 1; i < len(s) && len(r) < 4; i++ {
c := s[i]
if (c < 'A' || c > 'Z') || (c == p) {
continue
}
p = c
if n, ok := m[c]; ok {
r += n
}
}
for i := len(r); i < 4; i++ {
r += "0"
}
return r
}

94
vendor/github.com/xrash/smetrics/ukkonen.go generated vendored Normal file
View File

@ -0,0 +1,94 @@
package smetrics
import (
"math"
)
// The Ukkonen algorithm for calculating the Levenshtein distance. The algorithm is described in http://www.cs.helsinki.fi/u/ukkonen/InfCont85.PDF, or in docs/InfCont85.PDF. It runs on O(t . min(m, n)) where t is the actual distance between strings a and b. It needs O(min(t, m, n)) space. This function might be preferred over WagnerFischer() for *very* similar strings. But test it out yourself.
// The first two parameters are the two strings to be compared. The last three parameters are the insertion cost, the deletion cost and the substitution cost. These are normally defined as 1, 1 and 2 respectively.
func Ukkonen(a, b string, icost, dcost, scost int) int {
var lowerCost int
if icost < dcost && icost < scost {
lowerCost = icost
} else if dcost < scost {
lowerCost = dcost
} else {
lowerCost = scost
}
infinite := math.MaxInt32 / 2
var r []int
var k, kprime, p, t int
var ins, del, sub int
if len(a) > len(b) {
t = (len(a) - len(b) + 1) * lowerCost
} else {
t = (len(b) - len(a) + 1) * lowerCost
}
for {
if (t / lowerCost) < (len(b) - len(a)) {
continue
}
// This is the right damn thing since the original Ukkonen
// paper minimizes the expression result only, but the uncommented version
// doesn't need to deal with floats so it's faster.
// p = int(math.Floor(0.5*((float64(t)/float64(lowerCost)) - float64(len(b) - len(a)))))
p = ((t / lowerCost) - (len(b) - len(a))) / 2
k = -p
kprime = k
rowlength := (len(b) - len(a)) + (2 * p)
r = make([]int, rowlength+2)
for i := 0; i < rowlength+2; i++ {
r[i] = infinite
}
for i := 0; i <= len(a); i++ {
for j := 0; j <= rowlength; j++ {
if i == j+k && i == 0 {
r[j] = 0
} else {
if j-1 < 0 {
ins = infinite
} else {
ins = r[j-1] + icost
}
del = r[j+1] + dcost
sub = r[j] + scost
if i-1 < 0 || i-1 >= len(a) || j+k-1 >= len(b) || j+k-1 < 0 {
sub = infinite
} else if a[i-1] == b[j+k-1] {
sub = r[j]
}
if ins < del && ins < sub {
r[j] = ins
} else if del < sub {
r[j] = del
} else {
r[j] = sub
}
}
}
k++
}
if r[(len(b)-len(a))+(2*p)+kprime] <= t {
break
} else {
t *= 2
}
}
return r[(len(b)-len(a))+(2*p)+kprime]
}

48
vendor/github.com/xrash/smetrics/wagner-fischer.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package smetrics
// The Wagner-Fischer algorithm for calculating the Levenshtein distance.
// The first two parameters are the two strings to be compared. The last three parameters are the insertion cost, the deletion cost and the substitution cost. These are normally defined as 1, 1 and 2 respectively.
func WagnerFischer(a, b string, icost, dcost, scost int) int {
// Allocate both rows.
row1 := make([]int, len(b)+1)
row2 := make([]int, len(b)+1)
var tmp []int
// Initialize the first row.
for i := 1; i <= len(b); i++ {
row1[i] = i * icost
}
// For each row...
for i := 1; i <= len(a); i++ {
row2[0] = i * dcost
// For each column...
for j := 1; j <= len(b); j++ {
if a[i-1] == b[j-1] {
row2[j] = row1[j-1]
} else {
ins := row2[j-1] + icost
del := row1[j] + dcost
sub := row1[j-1] + scost
if ins < del && ins < sub {
row2[j] = ins
} else if del < sub {
row2[j] = del
} else {
row2[j] = sub
}
}
}
// Swap the rows at the end of each row.
tmp = row1
row1 = row2
row2 = tmp
}
// Because we swapped the rows, the final result is in row1 instead of row2.
return row1[len(row1)-1]
}

15
vendor/modules.txt vendored
View File

@ -1,11 +1,12 @@
# github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d # github.com/cpuguy83/go-md2man/v2 v2.0.2
## explicit; go 1.11
github.com/cpuguy83/go-md2man/v2/md2man github.com/cpuguy83/go-md2man/v2/md2man
# github.com/russross/blackfriday/v2 v2.0.1 # github.com/russross/blackfriday/v2 v2.1.0
## explicit
github.com/russross/blackfriday/v2 github.com/russross/blackfriday/v2
# github.com/shurcooL/sanitized_anchor_name v1.0.0 # github.com/urfave/cli/v2 v2.19.2
github.com/shurcooL/sanitized_anchor_name ## explicit; go 1.18
# github.com/urfave/cli/v2 v2.3.0
## explicit
github.com/urfave/cli/v2 github.com/urfave/cli/v2
# gopkg.in/ini.v1 v1.66.2 # github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673
## explicit ## explicit
github.com/xrash/smetrics