vendor: bump to Kube 1.9/master
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
parent
7076c73172
commit
7a675ccd92
202 changed files with 8543 additions and 7270 deletions
119
vendor/github.com/buger/goterm/README.md
generated
vendored
119
vendor/github.com/buger/goterm/README.md
generated
vendored
|
@ -1,119 +0,0 @@
|
|||
## Description
|
||||
|
||||
This library provides basic building blocks for building advanced console UIs.
|
||||
|
||||
Initially created for [Gor](http://github.com/buger/gor).
|
||||
|
||||
Full API documentation: http://godoc.org/github.com/buger/goterm
|
||||
|
||||
## Basic usage
|
||||
|
||||
Full screen console app, printing current time:
|
||||
|
||||
```go
|
||||
import (
|
||||
tm "github.com/buger/goterm"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tm.Clear() // Clear current screen
|
||||
|
||||
for {
|
||||
// By moving cursor to top-left position we ensure that console output
|
||||
// will be overwritten each time, instead of adding new.
|
||||
tm.MoveCursor(1,1)
|
||||
|
||||
tm.Println("Current Time:", time.Now().Format(time.RFC1123))
|
||||
|
||||
tm.Flush() // Call it every time at the end of rendering
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This can be seen in [examples/time_example.go](examples/time_example.go). To
|
||||
run it yourself, go into your `$GOPATH/src/github.com/buger/goterm` directory
|
||||
and run `go run ./examples/time_example.go`
|
||||
|
||||
|
||||
Print red bold message on white background:
|
||||
|
||||
```go
|
||||
tm.Println(tm.Background(tm.Color(tm.Bold("Important header"), tm.RED), tm.WHITE))
|
||||
```
|
||||
|
||||
|
||||
Create box and move it to center of the screen:
|
||||
|
||||
```go
|
||||
tm.Clear()
|
||||
|
||||
// Create Box with 30% width of current screen, and height of 20 lines
|
||||
box := tm.NewBox(30|tm.PCT, 20, 0)
|
||||
|
||||
// Add some content to the box
|
||||
// Note that you can add ANY content, even tables
|
||||
fmt.Fprint(box, "Some box content")
|
||||
|
||||
// Move Box to approx center of the screen
|
||||
tm.Print(tm.MoveTo(box.String(), 40|tm.PCT, 40|tm.PCT))
|
||||
|
||||
tm.Flush()
|
||||
```
|
||||
|
||||
This can be found in [examples/box_example.go](examples/box_example.go).
|
||||
|
||||
Draw table:
|
||||
|
||||
```go
|
||||
// Based on http://golang.org/pkg/text/tabwriter
|
||||
totals := tm.NewTable(0, 10, 5, ' ', 0)
|
||||
fmt.Fprintf(totals, "Time\tStarted\tActive\tFinished\n")
|
||||
fmt.Fprintf(totals, "%s\t%d\t%d\t%d\n", "All", started, started-finished, finished)
|
||||
tm.Println(totals)
|
||||
tm.Flush()
|
||||
```
|
||||
|
||||
This can be found in [examples/table_example.go](examples/table_example.go).
|
||||
|
||||
## Line charts
|
||||
|
||||
Chart example:
|
||||
|
||||

|
||||
|
||||
|
||||
```go
|
||||
import (
|
||||
tm "github.com/buger/goterm"
|
||||
)
|
||||
|
||||
chart := tm.NewLineChart(100, 20)
|
||||
|
||||
data := new(tm.DataTable)
|
||||
data.addColumn("Time")
|
||||
data.addColumn("Sin(x)")
|
||||
data.addColumn("Cos(x+1)")
|
||||
|
||||
for i := 0.1; i < 10; i += 0.1 {
|
||||
data.addRow(i, math.Sin(i), math.Cos(i+1))
|
||||
}
|
||||
|
||||
tm.Println(chart.Draw(data))
|
||||
```
|
||||
|
||||
This can be found in [examples/chart_example.go](examples/chart_example.go).
|
||||
|
||||
Drawing 2 separate graphs in different scales. Each graph have its own Y axe.
|
||||
|
||||
```go
|
||||
chart.Flags = tm.DRAW_INDEPENDENT
|
||||
```
|
||||
|
||||
Drawing graph with relative scale (Grapwh draw starting from min value instead of zero)
|
||||
|
||||
```go
|
||||
chart.Flags = tm.DRAW_RELATIVE
|
||||
```
|
122
vendor/github.com/buger/goterm/box.go
generated
vendored
122
vendor/github.com/buger/goterm/box.go
generated
vendored
|
@ -1,122 +0,0 @@
|
|||
package goterm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const DEFAULT_BORDER = "- │ ┌ ┐ └ ┘"
|
||||
|
||||
// Box allows you to create independent parts of screen, with its own buffer and borders.
|
||||
// Can be used for creating modal windows
|
||||
//
|
||||
// Generates boxes likes this:
|
||||
// ┌--------┐
|
||||
// │hello │
|
||||
// │world │
|
||||
// │ │
|
||||
// └--------┘
|
||||
//
|
||||
type Box struct {
|
||||
Buf *bytes.Buffer
|
||||
|
||||
Width int
|
||||
Height int
|
||||
|
||||
// To get even padding: PaddingX ~= PaddingY*4
|
||||
PaddingX int
|
||||
PaddingY int
|
||||
|
||||
// Should contain 6 border pieces separated by spaces
|
||||
//
|
||||
// Example border:
|
||||
// "- │ ┌ ┐ └ ┘"
|
||||
Border string
|
||||
|
||||
Flags int // Not used now
|
||||
}
|
||||
|
||||
// Create new Box.
|
||||
// Width and height can be relative:
|
||||
//
|
||||
// // Create box with 50% with of current screen and 10 lines height
|
||||
// box := tm.NewBox(50|tm.PCT, 10, 0)
|
||||
//
|
||||
func NewBox(width, height int, flags int) *Box {
|
||||
width, height = GetXY(width, height)
|
||||
|
||||
box := new(Box)
|
||||
box.Buf = new(bytes.Buffer)
|
||||
box.Width = width
|
||||
box.Height = height
|
||||
box.Border = DEFAULT_BORDER
|
||||
box.PaddingX = 1
|
||||
box.PaddingY = 0
|
||||
box.Flags = flags
|
||||
|
||||
return box
|
||||
}
|
||||
|
||||
func (b *Box) Write(p []byte) (int, error) {
|
||||
return b.Buf.Write(p)
|
||||
}
|
||||
|
||||
// Render Box
|
||||
func (b *Box) String() (out string) {
|
||||
borders := strings.Split(b.Border, " ")
|
||||
lines := strings.Split(b.Buf.String(), "\n")
|
||||
|
||||
// Border + padding
|
||||
prefix := borders[1] + strings.Repeat(" ", b.PaddingX)
|
||||
suffix := strings.Repeat(" ", b.PaddingX) + borders[1]
|
||||
|
||||
offset := b.PaddingY + 1 // 1 is border width
|
||||
|
||||
// Content width without borders and padding
|
||||
contentWidth := b.Width - (b.PaddingX+1)*2
|
||||
|
||||
for y := 0; y < b.Height; y++ {
|
||||
var line string
|
||||
|
||||
switch {
|
||||
// Draw borders for first line
|
||||
case y == 0:
|
||||
line = borders[2] + strings.Repeat(borders[0], b.Width-2) + borders[3]
|
||||
|
||||
// Draw borders for last line
|
||||
case y == (b.Height - 1):
|
||||
line = borders[4] + strings.Repeat(borders[0], b.Width-2) + borders[5]
|
||||
|
||||
// Draw top and bottom padding
|
||||
case y <= b.PaddingY || y >= (b.Height-b.PaddingY):
|
||||
line = borders[1] + strings.Repeat(" ", b.Width-2) + borders[1]
|
||||
|
||||
// Render content
|
||||
default:
|
||||
if len(lines) > y-offset {
|
||||
line = lines[y-offset]
|
||||
} else {
|
||||
line = ""
|
||||
}
|
||||
|
||||
if len(line) > contentWidth-1 {
|
||||
// If line is too large limit it
|
||||
line = line[0:contentWidth]
|
||||
} else {
|
||||
// If line is too small enlarge it by adding spaces
|
||||
line = line + strings.Repeat(" ", contentWidth-len(line))
|
||||
}
|
||||
|
||||
line = prefix + line + suffix
|
||||
}
|
||||
|
||||
// Don't add newline for last element
|
||||
if y != b.Height-1 {
|
||||
line = line + "\n"
|
||||
}
|
||||
|
||||
out += line
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
328
vendor/github.com/buger/goterm/plot.go
generated
vendored
328
vendor/github.com/buger/goterm/plot.go
generated
vendored
|
@ -1,328 +0,0 @@
|
|||
package goterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
AXIS_LEFT = iota
|
||||
AXIS_RIGHT
|
||||
)
|
||||
|
||||
const (
|
||||
DRAW_INDEPENDENT = 1 << iota
|
||||
DRAW_RELATIVE
|
||||
)
|
||||
|
||||
type DataTable struct {
|
||||
columns []string
|
||||
|
||||
rows [][]float64
|
||||
}
|
||||
|
||||
func (d *DataTable) AddColumn(name string) {
|
||||
d.columns = append(d.columns, name)
|
||||
}
|
||||
|
||||
func (d *DataTable) AddRow(elms ...float64) {
|
||||
d.rows = append(d.rows, elms)
|
||||
}
|
||||
|
||||
type Chart interface {
|
||||
Draw(data DataTable, flags int) string
|
||||
}
|
||||
|
||||
type LineChart struct {
|
||||
Buf []string
|
||||
chartBuf []string
|
||||
|
||||
data *DataTable
|
||||
|
||||
Width int
|
||||
Height int
|
||||
|
||||
chartHeight int
|
||||
chartWidth int
|
||||
|
||||
paddingX int
|
||||
|
||||
paddingY int
|
||||
|
||||
Flags int
|
||||
}
|
||||
|
||||
func genBuf(size int) []string {
|
||||
buf := make([]string, size)
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
buf[i] = " "
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// Format float
|
||||
func ff(num interface{}) string {
|
||||
return fmt.Sprintf("%.1f", num)
|
||||
}
|
||||
|
||||
func NewLineChart(width, height int) *LineChart {
|
||||
chart := new(LineChart)
|
||||
chart.Width = width
|
||||
chart.Height = height
|
||||
chart.Buf = genBuf(width * height)
|
||||
|
||||
// axis lines + axies text
|
||||
chart.paddingY = 2
|
||||
|
||||
return chart
|
||||
}
|
||||
|
||||
func (c *LineChart) DrawAxes(maxX, minX, maxY, minY float64, index int) {
|
||||
side := AXIS_LEFT
|
||||
|
||||
if c.Flags&DRAW_INDEPENDENT != 0 {
|
||||
if index%2 == 0 {
|
||||
side = AXIS_RIGHT
|
||||
}
|
||||
|
||||
c.DrawLine(c.paddingX-1, 1, c.Width-c.paddingX, 1, "-")
|
||||
} else {
|
||||
c.DrawLine(c.paddingX-1, 1, c.Width-1, 1, "-")
|
||||
}
|
||||
|
||||
if side == AXIS_LEFT {
|
||||
c.DrawLine(c.paddingX-1, 1, c.paddingX-1, c.Height-1, "│")
|
||||
} else {
|
||||
c.DrawLine(c.Width-c.paddingX, 1, c.Width-c.paddingX, c.Height-1, "│")
|
||||
}
|
||||
|
||||
left := 0
|
||||
if side == AXIS_RIGHT {
|
||||
left = c.Width - c.paddingX + 1
|
||||
}
|
||||
|
||||
if c.Flags&DRAW_RELATIVE != 0 {
|
||||
c.writeText(ff(minY), left, 1)
|
||||
} else {
|
||||
if minY > 0 {
|
||||
c.writeText("0", left, 1)
|
||||
} else {
|
||||
c.writeText(ff(minY), left, 1)
|
||||
}
|
||||
}
|
||||
|
||||
c.writeText(ff(maxY), left, c.Height-1)
|
||||
|
||||
c.writeText(ff(minX), c.paddingX, 0)
|
||||
|
||||
x_col := c.data.columns[0]
|
||||
c.writeText(c.data.columns[0], c.Width/2-len(x_col)/2, 1)
|
||||
|
||||
if c.Flags&DRAW_INDEPENDENT != 0 || len(c.data.columns) < 3 {
|
||||
col := c.data.columns[index]
|
||||
|
||||
for idx, char := range strings.Split(col, "") {
|
||||
start_from := c.Height/2 + len(col)/2 - idx
|
||||
|
||||
if side == AXIS_LEFT {
|
||||
c.writeText(char, c.paddingX-1, start_from)
|
||||
} else {
|
||||
c.writeText(char, c.Width-c.paddingX, start_from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.Flags&DRAW_INDEPENDENT != 0 {
|
||||
c.writeText(ff(maxX), c.Width-c.paddingX-len(ff(maxX)), 0)
|
||||
} else {
|
||||
c.writeText(ff(maxX), c.Width-len(ff(maxX)), 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LineChart) writeText(text string, x, y int) {
|
||||
coord := y*c.Width + x
|
||||
|
||||
for idx, char := range strings.Split(text, "") {
|
||||
c.Buf[coord+idx] = char
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LineChart) Draw(data *DataTable) (out string) {
|
||||
var scaleY, scaleX float64
|
||||
|
||||
c.data = data
|
||||
|
||||
if c.Flags&DRAW_INDEPENDENT != 0 && len(data.columns) > 3 {
|
||||
fmt.Println("Error: Can't use DRAW_INDEPENDENT for more then 2 graphs")
|
||||
return ""
|
||||
}
|
||||
|
||||
charts := len(data.columns) - 1
|
||||
|
||||
prevPoint := [2]int{-1, -1}
|
||||
|
||||
maxX, minX, maxY, minY := getBoundaryValues(data, -1)
|
||||
|
||||
c.paddingX = int(math.Max(float64(len(ff(minY))), float64(len(ff(maxY))))) + 1
|
||||
|
||||
c.chartHeight = c.Height - c.paddingY
|
||||
|
||||
if c.Flags&DRAW_INDEPENDENT != 0 {
|
||||
c.chartWidth = c.Width - 2*c.paddingX
|
||||
} else {
|
||||
c.chartWidth = c.Width - c.paddingX - 1
|
||||
}
|
||||
|
||||
scaleX = float64(c.chartWidth) / (maxX - minX)
|
||||
|
||||
if c.Flags&DRAW_RELATIVE != 0 || minY < 0 {
|
||||
scaleY = float64(c.chartHeight) / (maxY - minY)
|
||||
} else {
|
||||
scaleY = float64(c.chartHeight) / maxY
|
||||
}
|
||||
|
||||
for i := 1; i < charts+1; i++ {
|
||||
if c.Flags&DRAW_INDEPENDENT != 0 {
|
||||
maxX, minX, maxY, minY = getBoundaryValues(data, i)
|
||||
|
||||
scaleX = float64(c.chartWidth-1) / (maxX - minX)
|
||||
scaleY = float64(c.chartHeight) / maxY
|
||||
|
||||
if c.Flags&DRAW_RELATIVE != 0 || minY < 0 {
|
||||
scaleY = float64(c.chartHeight) / (maxY - minY)
|
||||
}
|
||||
}
|
||||
|
||||
symbol := Color("•", i)
|
||||
|
||||
c_data := getChartData(data, i)
|
||||
|
||||
for _, point := range c_data {
|
||||
x := int((point[0]-minX)*scaleX) + c.paddingX
|
||||
y := int((point[1])*scaleY) + c.paddingY
|
||||
|
||||
if c.Flags&DRAW_RELATIVE != 0 || minY < 0 {
|
||||
y = int((point[1]-minY)*scaleY) + c.paddingY
|
||||
}
|
||||
|
||||
if prevPoint[0] == -1 {
|
||||
prevPoint[0] = x
|
||||
prevPoint[1] = y
|
||||
}
|
||||
|
||||
if prevPoint[0] <= x {
|
||||
c.DrawLine(prevPoint[0], prevPoint[1], x, y, symbol)
|
||||
}
|
||||
|
||||
prevPoint[0] = x
|
||||
prevPoint[1] = y
|
||||
}
|
||||
|
||||
c.DrawAxes(maxX, minX, maxY, minY, i)
|
||||
}
|
||||
|
||||
for row := c.Height - 1; row >= 0; row-- {
|
||||
out += strings.Join(c.Buf[row*c.Width:(row+1)*c.Width], "") + "\n"
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *LineChart) DrawLine(x0, y0, x1, y1 int, symbol string) {
|
||||
drawLine(x0, y0, x1, y1, func(x, y int) {
|
||||
coord := y*c.Width + x
|
||||
|
||||
if coord > 0 && coord < len(c.Buf) {
|
||||
c.Buf[coord] = symbol
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func getBoundaryValues(data *DataTable, index int) (maxX, minX, maxY, minY float64) {
|
||||
maxX = data.rows[0][0]
|
||||
minX = data.rows[0][0]
|
||||
maxY = data.rows[0][1]
|
||||
minY = data.rows[0][1]
|
||||
|
||||
for _, r := range data.rows {
|
||||
maxX = math.Max(maxX, r[0])
|
||||
minX = math.Min(minX, r[0])
|
||||
|
||||
for idx, c := range r {
|
||||
if idx > 0 {
|
||||
if index == -1 || index == idx {
|
||||
maxY = math.Max(maxY, c)
|
||||
minY = math.Min(minY, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if maxY > 0 {
|
||||
maxY = maxY * 1.1
|
||||
} else {
|
||||
maxY = maxY * 0.9
|
||||
}
|
||||
|
||||
if minY > 0 {
|
||||
minY = minY * 0.9
|
||||
} else {
|
||||
minY = minY * 1.1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DataTable can contain data for multiple graphs, we need to extract only 1
|
||||
func getChartData(data *DataTable, index int) (out [][]float64) {
|
||||
for _, r := range data.rows {
|
||||
out = append(out, []float64{r[0], r[index]})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Algorithm for drawing line between two points
|
||||
//
|
||||
// http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
|
||||
func drawLine(x0, y0, x1, y1 int, plot func(int, int)) {
|
||||
dx := x1 - x0
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
}
|
||||
dy := y1 - y0
|
||||
if dy < 0 {
|
||||
dy = -dy
|
||||
}
|
||||
var sx, sy int
|
||||
if x0 < x1 {
|
||||
sx = 1
|
||||
} else {
|
||||
sx = -1
|
||||
}
|
||||
if y0 < y1 {
|
||||
sy = 1
|
||||
} else {
|
||||
sy = -1
|
||||
}
|
||||
err := dx - dy
|
||||
|
||||
for {
|
||||
plot(x0, y0)
|
||||
if x0 == x1 && y0 == y1 {
|
||||
break
|
||||
}
|
||||
e2 := 2 * err
|
||||
if e2 > -dy {
|
||||
err -= dy
|
||||
x0 += sx
|
||||
}
|
||||
if e2 < dx {
|
||||
err += dx
|
||||
y0 += sy
|
||||
}
|
||||
}
|
||||
}
|
34
vendor/github.com/buger/goterm/table.go
generated
vendored
34
vendor/github.com/buger/goterm/table.go
generated
vendored
|
@ -1,34 +0,0 @@
|
|||
package goterm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
// Tabwriter with own buffer:
|
||||
//
|
||||
// totals := tm.NewTable(0, 10, 5, ' ', 0)
|
||||
// fmt.Fprintf(totals, "Time\tStarted\tActive\tFinished\n")
|
||||
// fmt.Fprintf(totals, "%s\t%d\t%d\t%d\n", "All", started, started-finished, finished)
|
||||
// tm.Println(totals)
|
||||
//
|
||||
// Based on http://golang.org/pkg/text/tabwriter
|
||||
type Table struct {
|
||||
tabwriter.Writer
|
||||
|
||||
Buf *bytes.Buffer
|
||||
}
|
||||
|
||||
// Same as here http://golang.org/pkg/text/tabwriter/#Writer.Init
|
||||
func NewTable(minwidth, tabwidth, padding int, padchar byte, flags uint) *Table {
|
||||
tbl := new(Table)
|
||||
tbl.Buf = new(bytes.Buffer)
|
||||
tbl.Init(tbl.Buf, minwidth, tabwidth, padding, padchar, flags)
|
||||
|
||||
return tbl
|
||||
}
|
||||
|
||||
func (t *Table) String() string {
|
||||
t.Flush()
|
||||
return t.Buf.String()
|
||||
}
|
258
vendor/github.com/buger/goterm/terminal.go
generated
vendored
258
vendor/github.com/buger/goterm/terminal.go
generated
vendored
|
@ -1,258 +0,0 @@
|
|||
// Provides basic bulding blocks for advanced console UI
|
||||
//
|
||||
// Coordinate system:
|
||||
//
|
||||
// 1/1---X---->
|
||||
// |
|
||||
// Y
|
||||
// |
|
||||
// v
|
||||
//
|
||||
// Documentation for ANSI codes: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
//
|
||||
// Inspired by: http://www.darkcoding.net/software/pretty-command-line-console-output-on-unix-in-python-and-go-lang/
|
||||
package goterm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Reset all custom styles
|
||||
const RESET = "\033[0m"
|
||||
|
||||
// Reset to default color
|
||||
const RESET_COLOR = "\033[32m"
|
||||
|
||||
// Return curor to start of line and clean it
|
||||
const RESET_LINE = "\r\033[K"
|
||||
|
||||
// List of possible colors
|
||||
const (
|
||||
BLACK = iota
|
||||
RED
|
||||
GREEN
|
||||
YELLOW
|
||||
BLUE
|
||||
MAGENTA
|
||||
CYAN
|
||||
WHITE
|
||||
)
|
||||
|
||||
var Output *bufio.Writer = bufio.NewWriter(os.Stdout)
|
||||
|
||||
func getColor(code int) string {
|
||||
return fmt.Sprintf("\033[3%dm", code)
|
||||
}
|
||||
|
||||
func getBgColor(code int) string {
|
||||
return fmt.Sprintf("\033[4%dm", code)
|
||||
}
|
||||
|
||||
// Set percent flag: num | PCT
|
||||
//
|
||||
// Check percent flag: num & PCT
|
||||
//
|
||||
// Reset percent flag: num & 0xFF
|
||||
const shift = uint(^uint(0)>>63) << 4
|
||||
const PCT = 0x8000 << shift
|
||||
|
||||
type winsize struct {
|
||||
Row uint16
|
||||
Col uint16
|
||||
Xpixel uint16
|
||||
Ypixel uint16
|
||||
}
|
||||
|
||||
// Global screen buffer
|
||||
// Its not recommented write to buffer dirrectly, use package Print,Printf,Println fucntions instead.
|
||||
var Screen *bytes.Buffer = new(bytes.Buffer)
|
||||
|
||||
// Get relative or absolute coorditantes
|
||||
// To get relative, set PCT flag to number:
|
||||
//
|
||||
// // Get 10% of total width to `x` and 20 to y
|
||||
// x, y = tm.GetXY(10|tm.PCT, 20)
|
||||
//
|
||||
func GetXY(x int, y int) (int, int) {
|
||||
if y == -1 {
|
||||
y = CurrentHeight() + 1
|
||||
}
|
||||
|
||||
if x&PCT != 0 {
|
||||
x = int((x & 0xFF) * Width() / 100)
|
||||
}
|
||||
|
||||
if y&PCT != 0 {
|
||||
y = int((y & 0xFF) * Height() / 100)
|
||||
}
|
||||
|
||||
return x, y
|
||||
}
|
||||
|
||||
type sf func(int, string) string
|
||||
|
||||
// Apply given transformation func for each line in string
|
||||
func applyTransform(str string, transform sf) (out string) {
|
||||
out = ""
|
||||
|
||||
for idx, line := range strings.Split(str, "\n") {
|
||||
out += transform(idx, line)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
func Clear() {
|
||||
Output.WriteString("\033[2J")
|
||||
}
|
||||
|
||||
// Move cursor to given position
|
||||
func MoveCursor(x int, y int) {
|
||||
fmt.Fprintf(Screen, "\033[%d;%dH", x, y)
|
||||
}
|
||||
|
||||
// Move cursor up relative the current position
|
||||
func MoveCursorUp(bias int) {
|
||||
fmt.Fprintf(Screen, "\033[%dA", bias)
|
||||
}
|
||||
|
||||
// Move cursor down relative the current position
|
||||
func MoveCursorDown(bias int) {
|
||||
fmt.Fprintf(Screen, "\033[%dB", bias)
|
||||
}
|
||||
|
||||
// Move cursor forward relative the current position
|
||||
func MoveCursorForward(bias int) {
|
||||
fmt.Fprintf(Screen, "\033[%dC", bias)
|
||||
}
|
||||
|
||||
// Move cursor backward relative the current position
|
||||
func MoveCursorBackward(bias int) {
|
||||
fmt.Fprintf(Screen, "\033[%dD", bias)
|
||||
}
|
||||
|
||||
// Move string to possition
|
||||
func MoveTo(str string, x int, y int) (out string) {
|
||||
x, y = GetXY(x, y)
|
||||
|
||||
return applyTransform(str, func(idx int, line string) string {
|
||||
return fmt.Sprintf("\033[%d;%dH%s", y+idx, x, line)
|
||||
})
|
||||
}
|
||||
|
||||
// Return carrier to start of line
|
||||
func ResetLine(str string) (out string) {
|
||||
return applyTransform(str, func(idx int, line string) string {
|
||||
return fmt.Sprintf(RESET_LINE, line)
|
||||
})
|
||||
}
|
||||
|
||||
// Make bold
|
||||
func Bold(str string) string {
|
||||
return applyTransform(str, func(idx int, line string) string {
|
||||
return fmt.Sprintf("\033[1m%s\033[0m", line)
|
||||
})
|
||||
}
|
||||
|
||||
// Apply given color to string:
|
||||
//
|
||||
// tm.Color("RED STRING", tm.RED)
|
||||
//
|
||||
func Color(str string, color int) string {
|
||||
return applyTransform(str, func(idx int, line string) string {
|
||||
return fmt.Sprintf("%s%s%s", getColor(color), line, RESET)
|
||||
})
|
||||
}
|
||||
|
||||
func Highlight(str, substr string, color int) string {
|
||||
hiSubstr := Color(substr, color)
|
||||
return strings.Replace(str, substr, hiSubstr, -1)
|
||||
}
|
||||
|
||||
func HighlightRegion(str string, from, to, color int) string {
|
||||
return str[:from] + Color(str[from:to], color) + str[to:]
|
||||
}
|
||||
|
||||
// Change background color of string:
|
||||
//
|
||||
// tm.Background("string", tm.RED)
|
||||
//
|
||||
func Background(str string, color int) string {
|
||||
return applyTransform(str, func(idx int, line string) string {
|
||||
return fmt.Sprintf("%s%s%s", getBgColor(color), line, RESET)
|
||||
})
|
||||
}
|
||||
|
||||
// Get console width
|
||||
func Width() int {
|
||||
ws, err := getWinsize()
|
||||
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return int(ws.Col)
|
||||
}
|
||||
|
||||
// Get console height
|
||||
func Height() int {
|
||||
ws, err := getWinsize()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(ws.Row)
|
||||
}
|
||||
|
||||
// Get current height. Line count in Screen buffer.
|
||||
func CurrentHeight() int {
|
||||
return strings.Count(Screen.String(), "\n")
|
||||
}
|
||||
|
||||
// Flush buffer and ensure that it will not overflow screen
|
||||
func Flush() {
|
||||
for idx, str := range strings.Split(Screen.String(), "\n") {
|
||||
if idx > Height() {
|
||||
return
|
||||
}
|
||||
|
||||
Output.WriteString(str + "\n")
|
||||
}
|
||||
|
||||
Output.Flush()
|
||||
Screen.Reset()
|
||||
}
|
||||
|
||||
func Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(Screen, a...)
|
||||
}
|
||||
|
||||
func Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(Screen, a...)
|
||||
}
|
||||
|
||||
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(Screen, format, a...)
|
||||
}
|
||||
|
||||
func Context(data string, idx, max int) string {
|
||||
var start, end int
|
||||
|
||||
if len(data[:idx]) < (max / 2) {
|
||||
start = 0
|
||||
} else {
|
||||
start = idx - max/2
|
||||
}
|
||||
|
||||
if len(data)-idx < (max / 2) {
|
||||
end = len(data) - 1
|
||||
} else {
|
||||
end = idx + max/2
|
||||
}
|
||||
|
||||
return data[start:end]
|
||||
}
|
12
vendor/github.com/buger/goterm/terminal_nosysioctl.go
generated
vendored
12
vendor/github.com/buger/goterm/terminal_nosysioctl.go
generated
vendored
|
@ -1,12 +0,0 @@
|
|||
// +build windows plan9 solaris
|
||||
|
||||
package goterm
|
||||
|
||||
func getWinsize() (*winsize, error) {
|
||||
ws := new(winsize)
|
||||
|
||||
ws.Col = 80
|
||||
ws.Row = 24
|
||||
|
||||
return ws, nil
|
||||
}
|
36
vendor/github.com/buger/goterm/terminal_sysioctl.go
generated
vendored
36
vendor/github.com/buger/goterm/terminal_sysioctl.go
generated
vendored
|
@ -1,36 +0,0 @@
|
|||
// +build !windows,!plan9,!solaris
|
||||
|
||||
package goterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getWinsize() (*winsize, error) {
|
||||
ws := new(winsize)
|
||||
|
||||
var _TIOCGWINSZ int64
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
_TIOCGWINSZ = 0x5413
|
||||
case "darwin":
|
||||
_TIOCGWINSZ = 1074295912
|
||||
}
|
||||
|
||||
r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
|
||||
uintptr(syscall.Stdin),
|
||||
uintptr(_TIOCGWINSZ),
|
||||
uintptr(unsafe.Pointer(ws)),
|
||||
)
|
||||
|
||||
if int(r1) == -1 {
|
||||
fmt.Println("Error:", os.NewSyscallError("GetWinsize", errno))
|
||||
return nil, os.NewSyscallError("GetWinsize", errno)
|
||||
}
|
||||
return ws, nil
|
||||
}
|
86
vendor/github.com/containers/image/pkg/sysregistries/system_registries.go
generated
vendored
86
vendor/github.com/containers/image/pkg/sysregistries/system_registries.go
generated
vendored
|
@ -1,86 +0,0 @@
|
|||
package sysregistries
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/containers/image/types"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// systemRegistriesConfPath is the path to the system-wide registry configuration file
|
||||
// and is used to add/subtract potential registries for obtaining images.
|
||||
// You can override this at build time with
|
||||
// -ldflags '-X github.com/containers/image/sysregistries.systemRegistriesConfPath=$your_path'
|
||||
var systemRegistriesConfPath = builtinRegistriesConfPath
|
||||
|
||||
// builtinRegistriesConfPath is the path to registry configuration file
|
||||
// DO NOT change this, instead see systemRegistriesConfPath above.
|
||||
const builtinRegistriesConfPath = "/etc/containers/registries.conf"
|
||||
|
||||
type registries struct {
|
||||
Registries []string `toml:"registries"`
|
||||
}
|
||||
|
||||
type tomlConfig struct {
|
||||
Registries struct {
|
||||
Search registries `toml:"search"`
|
||||
Insecure registries `toml:"insecure"`
|
||||
Block registries `toml:"block"`
|
||||
} `toml:"registries"`
|
||||
}
|
||||
|
||||
// Reads the global registry file from the filesystem. Returns
|
||||
// a byte array
|
||||
func readRegistryConf(ctx *types.SystemContext) ([]byte, error) {
|
||||
dirPath := systemRegistriesConfPath
|
||||
if ctx != nil {
|
||||
if ctx.SystemRegistriesConfPath != "" {
|
||||
dirPath = ctx.SystemRegistriesConfPath
|
||||
} else if ctx.RootForImplicitAbsolutePaths != "" {
|
||||
dirPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath)
|
||||
}
|
||||
}
|
||||
configBytes, err := ioutil.ReadFile(dirPath)
|
||||
return configBytes, err
|
||||
}
|
||||
|
||||
// For mocking in unittests
|
||||
var readConf = readRegistryConf
|
||||
|
||||
// Loads the registry configuration file from the filesystem and
|
||||
// then unmarshals it. Returns the unmarshalled object.
|
||||
func loadRegistryConf(ctx *types.SystemContext) (*tomlConfig, error) {
|
||||
config := &tomlConfig{}
|
||||
|
||||
configBytes, err := readConf(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = toml.Unmarshal(configBytes, &config)
|
||||
return config, err
|
||||
}
|
||||
|
||||
// GetRegistries returns an array of strings that contain the names
|
||||
// of the registries as defined in the system-wide
|
||||
// registries file. it returns an empty array if none are
|
||||
// defined
|
||||
func GetRegistries(ctx *types.SystemContext) ([]string, error) {
|
||||
config, err := loadRegistryConf(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config.Registries.Search.Registries, nil
|
||||
}
|
||||
|
||||
// GetInsecureRegistries returns an array of strings that contain the names
|
||||
// of the insecure registries as defined in the system-wide
|
||||
// registries file. it returns an empty array if none are
|
||||
// defined
|
||||
func GetInsecureRegistries(ctx *types.SystemContext) ([]string, error) {
|
||||
config, err := loadRegistryConf(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config.Registries.Insecure.Registries, nil
|
||||
}
|
22
vendor/github.com/emicklei/go-restful-swagger12/LICENSE
generated
vendored
22
vendor/github.com/emicklei/go-restful-swagger12/LICENSE
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
Copyright (c) 2017 Ernest Micklei
|
||||
|
||||
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.
|
83
vendor/github.com/emicklei/go-restful-swagger12/README.md
generated
vendored
83
vendor/github.com/emicklei/go-restful-swagger12/README.md
generated
vendored
|
@ -1,83 +0,0 @@
|
|||
# go-restful-swagger12
|
||||
|
||||
[](https://travis-ci.org/emicklei/go-restful-swagger12)
|
||||
[](https://godoc.org/github.com/emicklei/go-restful-swagger12)
|
||||
|
||||
How to use Swagger UI with go-restful
|
||||
=
|
||||
|
||||
Get the Swagger UI sources (version 1.2 only)
|
||||
|
||||
git clone https://github.com/wordnik/swagger-ui.git
|
||||
|
||||
The project contains a "dist" folder.
|
||||
Its contents has all the Swagger UI files you need.
|
||||
|
||||
The `index.html` has an `url` set to `http://petstore.swagger.wordnik.com/api/api-docs`.
|
||||
You need to change that to match your WebService JSON endpoint e.g. `http://localhost:8080/apidocs.json`
|
||||
|
||||
Now, you can install the Swagger WebService for serving the Swagger specification in JSON.
|
||||
|
||||
config := swagger.Config{
|
||||
WebServices: restful.RegisteredWebServices(),
|
||||
ApiPath: "/apidocs.json",
|
||||
SwaggerPath: "/apidocs/",
|
||||
SwaggerFilePath: "/Users/emicklei/Projects/swagger-ui/dist"}
|
||||
swagger.InstallSwaggerService(config)
|
||||
|
||||
|
||||
Documenting Structs
|
||||
--
|
||||
|
||||
Currently there are 2 ways to document your structs in the go-restful Swagger.
|
||||
|
||||
###### By using struct tags
|
||||
- Use tag "description" to annotate a struct field with a description to show in the UI
|
||||
- Use tag "modelDescription" to annotate the struct itself with a description to show in the UI. The tag can be added in an field of the struct and in case that there are multiple definition, they will be appended with an empty line.
|
||||
|
||||
###### By using the SwaggerDoc method
|
||||
Here is an example with an `Address` struct and the documentation for each of the fields. The `""` is a special entry for **documenting the struct itself**.
|
||||
|
||||
type Address struct {
|
||||
Country string `json:"country,omitempty"`
|
||||
PostCode int `json:"postcode,omitempty"`
|
||||
}
|
||||
|
||||
func (Address) SwaggerDoc() map[string]string {
|
||||
return map[string]string{
|
||||
"": "Address doc",
|
||||
"country": "Country doc",
|
||||
"postcode": "PostCode doc",
|
||||
}
|
||||
}
|
||||
|
||||
This example will generate a JSON like this
|
||||
|
||||
{
|
||||
"Address": {
|
||||
"id": "Address",
|
||||
"description": "Address doc",
|
||||
"properties": {
|
||||
"country": {
|
||||
"type": "string",
|
||||
"description": "Country doc"
|
||||
},
|
||||
"postcode": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"description": "PostCode doc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
**Very Important Notes:**
|
||||
- `SwaggerDoc()` is using a **NON-Pointer** receiver (e.g. func (Address) and not func (*Address))
|
||||
- The returned map should use as key the name of the field as defined in the JSON parameter (e.g. `"postcode"` and not `"PostCode"`)
|
||||
|
||||
Notes
|
||||
--
|
||||
- The Nickname of an Operation is automatically set by finding the name of the function. You can override it using RouteBuilder.Operation(..)
|
||||
- The WebServices field of swagger.Config can be used to control which service you want to expose and document ; you can have multiple configs and therefore multiple endpoints.
|
||||
|
||||
© 2017, ernestmicklei.com. MIT License. Contributions welcome.
|
64
vendor/github.com/emicklei/go-restful-swagger12/api_declaration_list.go
generated
vendored
64
vendor/github.com/emicklei/go-restful-swagger12/api_declaration_list.go
generated
vendored
|
@ -1,64 +0,0 @@
|
|||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// ApiDeclarationList maintains an ordered list of ApiDeclaration.
|
||||
type ApiDeclarationList struct {
|
||||
List []ApiDeclaration
|
||||
}
|
||||
|
||||
// At returns the ApiDeclaration by its path unless absent, then ok is false
|
||||
func (l *ApiDeclarationList) At(path string) (a ApiDeclaration, ok bool) {
|
||||
for _, each := range l.List {
|
||||
if each.ResourcePath == path {
|
||||
return each, true
|
||||
}
|
||||
}
|
||||
return a, false
|
||||
}
|
||||
|
||||
// Put adds or replaces a ApiDeclaration with this name
|
||||
func (l *ApiDeclarationList) Put(path string, a ApiDeclaration) {
|
||||
// maybe replace existing
|
||||
for i, each := range l.List {
|
||||
if each.ResourcePath == path {
|
||||
// replace
|
||||
l.List[i] = a
|
||||
return
|
||||
}
|
||||
}
|
||||
// add
|
||||
l.List = append(l.List, a)
|
||||
}
|
||||
|
||||
// Do enumerates all the properties, each with its assigned name
|
||||
func (l *ApiDeclarationList) Do(block func(path string, decl ApiDeclaration)) {
|
||||
for _, each := range l.List {
|
||||
block(each.ResourcePath, each)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON writes the ModelPropertyList as if it was a map[string]ModelProperty
|
||||
func (l ApiDeclarationList) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
buf.WriteString("{\n")
|
||||
for i, each := range l.List {
|
||||
buf.WriteString("\"")
|
||||
buf.WriteString(each.ResourcePath)
|
||||
buf.WriteString("\": ")
|
||||
encoder.Encode(each)
|
||||
if i < len(l.List)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.Bytes(), nil
|
||||
}
|
46
vendor/github.com/emicklei/go-restful-swagger12/config.go
generated
vendored
46
vendor/github.com/emicklei/go-restful-swagger12/config.go
generated
vendored
|
@ -1,46 +0,0 @@
|
|||
package swagger
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
)
|
||||
|
||||
// PostBuildDeclarationMapFunc can be used to modify the api declaration map.
|
||||
type PostBuildDeclarationMapFunc func(apiDeclarationMap *ApiDeclarationList)
|
||||
|
||||
// MapSchemaFormatFunc can be used to modify typeName at definition time.
|
||||
type MapSchemaFormatFunc func(typeName string) string
|
||||
|
||||
// MapModelTypeNameFunc can be used to return the desired typeName for a given
|
||||
// type. It will return false if the default name should be used.
|
||||
type MapModelTypeNameFunc func(t reflect.Type) (string, bool)
|
||||
|
||||
type Config struct {
|
||||
// url where the services are available, e.g. http://localhost:8080
|
||||
// if left empty then the basePath of Swagger is taken from the actual request
|
||||
WebServicesUrl string
|
||||
// path where the JSON api is avaiable , e.g. /apidocs
|
||||
ApiPath string
|
||||
// [optional] path where the swagger UI will be served, e.g. /swagger
|
||||
SwaggerPath string
|
||||
// [optional] location of folder containing Swagger HTML5 application index.html
|
||||
SwaggerFilePath string
|
||||
// api listing is constructed from this list of restful WebServices.
|
||||
WebServices []*restful.WebService
|
||||
// will serve all static content (scripts,pages,images)
|
||||
StaticHandler http.Handler
|
||||
// [optional] on default CORS (Cross-Origin-Resource-Sharing) is enabled.
|
||||
DisableCORS bool
|
||||
// Top-level API version. Is reflected in the resource listing.
|
||||
ApiVersion string
|
||||
// If set then call this handler after building the complete ApiDeclaration Map
|
||||
PostBuildHandler PostBuildDeclarationMapFunc
|
||||
// Swagger global info struct
|
||||
Info Info
|
||||
// [optional] If set, model builder should call this handler to get addition typename-to-swagger-format-field conversion.
|
||||
SchemaFormatHandler MapSchemaFormatFunc
|
||||
// [optional] If set, model builder should call this handler to retrieve the name for a given type.
|
||||
ModelTypeNameHandler MapModelTypeNameFunc
|
||||
}
|
467
vendor/github.com/emicklei/go-restful-swagger12/model_builder.go
generated
vendored
467
vendor/github.com/emicklei/go-restful-swagger12/model_builder.go
generated
vendored
|
@ -1,467 +0,0 @@
|
|||
package swagger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ModelBuildable is used for extending Structs that need more control over
|
||||
// how the Model appears in the Swagger api declaration.
|
||||
type ModelBuildable interface {
|
||||
PostBuildModel(m *Model) *Model
|
||||
}
|
||||
|
||||
type modelBuilder struct {
|
||||
Models *ModelList
|
||||
Config *Config
|
||||
}
|
||||
|
||||
type documentable interface {
|
||||
SwaggerDoc() map[string]string
|
||||
}
|
||||
|
||||
// Check if this structure has a method with signature func (<theModel>) SwaggerDoc() map[string]string
|
||||
// If it exists, retrive the documentation and overwrite all struct tag descriptions
|
||||
func getDocFromMethodSwaggerDoc2(model reflect.Type) map[string]string {
|
||||
if docable, ok := reflect.New(model).Elem().Interface().(documentable); ok {
|
||||
return docable.SwaggerDoc()
|
||||
}
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
// addModelFrom creates and adds a Model to the builder and detects and calls
|
||||
// the post build hook for customizations
|
||||
func (b modelBuilder) addModelFrom(sample interface{}) {
|
||||
if modelOrNil := b.addModel(reflect.TypeOf(sample), ""); modelOrNil != nil {
|
||||
// allow customizations
|
||||
if buildable, ok := sample.(ModelBuildable); ok {
|
||||
modelOrNil = buildable.PostBuildModel(modelOrNil)
|
||||
b.Models.Put(modelOrNil.Id, *modelOrNil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b modelBuilder) addModel(st reflect.Type, nameOverride string) *Model {
|
||||
// Turn pointers into simpler types so further checks are
|
||||
// correct.
|
||||
if st.Kind() == reflect.Ptr {
|
||||
st = st.Elem()
|
||||
}
|
||||
|
||||
modelName := b.keyFrom(st)
|
||||
if nameOverride != "" {
|
||||
modelName = nameOverride
|
||||
}
|
||||
// no models needed for primitive types
|
||||
if b.isPrimitiveType(modelName) {
|
||||
return nil
|
||||
}
|
||||
// golang encoding/json packages says array and slice values encode as
|
||||
// JSON arrays, except that []byte encodes as a base64-encoded string.
|
||||
// If we see a []byte here, treat it at as a primitive type (string)
|
||||
// and deal with it in buildArrayTypeProperty.
|
||||
if (st.Kind() == reflect.Slice || st.Kind() == reflect.Array) &&
|
||||
st.Elem().Kind() == reflect.Uint8 {
|
||||
return nil
|
||||
}
|
||||
// see if we already have visited this model
|
||||
if _, ok := b.Models.At(modelName); ok {
|
||||
return nil
|
||||
}
|
||||
sm := Model{
|
||||
Id: modelName,
|
||||
Required: []string{},
|
||||
Properties: ModelPropertyList{}}
|
||||
|
||||
// reference the model before further initializing (enables recursive structs)
|
||||
b.Models.Put(modelName, sm)
|
||||
|
||||
// check for slice or array
|
||||
if st.Kind() == reflect.Slice || st.Kind() == reflect.Array {
|
||||
b.addModel(st.Elem(), "")
|
||||
return &sm
|
||||
}
|
||||
// check for structure or primitive type
|
||||
if st.Kind() != reflect.Struct {
|
||||
return &sm
|
||||
}
|
||||
|
||||
fullDoc := getDocFromMethodSwaggerDoc2(st)
|
||||
modelDescriptions := []string{}
|
||||
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
field := st.Field(i)
|
||||
jsonName, modelDescription, prop := b.buildProperty(field, &sm, modelName)
|
||||
if len(modelDescription) > 0 {
|
||||
modelDescriptions = append(modelDescriptions, modelDescription)
|
||||
}
|
||||
|
||||
// add if not omitted
|
||||
if len(jsonName) != 0 {
|
||||
// update description
|
||||
if fieldDoc, ok := fullDoc[jsonName]; ok {
|
||||
prop.Description = fieldDoc
|
||||
}
|
||||
// update Required
|
||||
if b.isPropertyRequired(field) {
|
||||
sm.Required = append(sm.Required, jsonName)
|
||||
}
|
||||
sm.Properties.Put(jsonName, prop)
|
||||
}
|
||||
}
|
||||
|
||||
// We always overwrite documentation if SwaggerDoc method exists
|
||||
// "" is special for documenting the struct itself
|
||||
if modelDoc, ok := fullDoc[""]; ok {
|
||||
sm.Description = modelDoc
|
||||
} else if len(modelDescriptions) != 0 {
|
||||
sm.Description = strings.Join(modelDescriptions, "\n")
|
||||
}
|
||||
|
||||
// update model builder with completed model
|
||||
b.Models.Put(modelName, sm)
|
||||
|
||||
return &sm
|
||||
}
|
||||
|
||||
func (b modelBuilder) isPropertyRequired(field reflect.StructField) bool {
|
||||
required := true
|
||||
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
|
||||
s := strings.Split(jsonTag, ",")
|
||||
if len(s) > 1 && s[1] == "omitempty" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return required
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, modelName string) (jsonName, modelDescription string, prop ModelProperty) {
|
||||
jsonName = b.jsonNameOfField(field)
|
||||
if len(jsonName) == 0 {
|
||||
// empty name signals skip property
|
||||
return "", "", prop
|
||||
}
|
||||
|
||||
if field.Name == "XMLName" && field.Type.String() == "xml.Name" {
|
||||
// property is metadata for the xml.Name attribute, can be skipped
|
||||
return "", "", prop
|
||||
}
|
||||
|
||||
if tag := field.Tag.Get("modelDescription"); tag != "" {
|
||||
modelDescription = tag
|
||||
}
|
||||
|
||||
prop.setPropertyMetadata(field)
|
||||
if prop.Type != nil {
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
|
||||
// check if type is doing its own marshalling
|
||||
marshalerType := reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||
if fieldType.Implements(marshalerType) {
|
||||
var pType = "string"
|
||||
if prop.Type == nil {
|
||||
prop.Type = &pType
|
||||
}
|
||||
if prop.Format == "" {
|
||||
prop.Format = b.jsonSchemaFormat(b.keyFrom(fieldType))
|
||||
}
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
|
||||
// check if annotation says it is a string
|
||||
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
|
||||
s := strings.Split(jsonTag, ",")
|
||||
if len(s) > 1 && s[1] == "string" {
|
||||
stringt := "string"
|
||||
prop.Type = &stringt
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
}
|
||||
|
||||
fieldKind := fieldType.Kind()
|
||||
switch {
|
||||
case fieldKind == reflect.Struct:
|
||||
jsonName, prop := b.buildStructTypeProperty(field, jsonName, model)
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.Slice || fieldKind == reflect.Array:
|
||||
jsonName, prop := b.buildArrayTypeProperty(field, jsonName, modelName)
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.Ptr:
|
||||
jsonName, prop := b.buildPointerTypeProperty(field, jsonName, modelName)
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.String:
|
||||
stringt := "string"
|
||||
prop.Type = &stringt
|
||||
return jsonName, modelDescription, prop
|
||||
case fieldKind == reflect.Map:
|
||||
// if it's a map, it's unstructured, and swagger 1.2 can't handle it
|
||||
objectType := "object"
|
||||
prop.Type = &objectType
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
|
||||
fieldTypeName := b.keyFrom(fieldType)
|
||||
if b.isPrimitiveType(fieldTypeName) {
|
||||
mapped := b.jsonSchemaType(fieldTypeName)
|
||||
prop.Type = &mapped
|
||||
prop.Format = b.jsonSchemaFormat(fieldTypeName)
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
modelType := b.keyFrom(fieldType)
|
||||
prop.Ref = &modelType
|
||||
|
||||
if fieldType.Name() == "" { // override type of anonymous structs
|
||||
nestedTypeName := modelName + "." + jsonName
|
||||
prop.Ref = &nestedTypeName
|
||||
b.addModel(fieldType, nestedTypeName)
|
||||
}
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
|
||||
func hasNamedJSONTag(field reflect.StructField) bool {
|
||||
parts := strings.Split(field.Tag.Get("json"), ",")
|
||||
if len(parts) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, s := range parts[1:] {
|
||||
if s == "inline" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(parts[0]) > 0
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonName string, model *Model) (nameJson string, prop ModelProperty) {
|
||||
prop.setPropertyMetadata(field)
|
||||
// Check for type override in tag
|
||||
if prop.Type != nil {
|
||||
return jsonName, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
// check for anonymous
|
||||
if len(fieldType.Name()) == 0 {
|
||||
// anonymous
|
||||
anonType := model.Id + "." + jsonName
|
||||
b.addModel(fieldType, anonType)
|
||||
prop.Ref = &anonType
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
if field.Name == fieldType.Name() && field.Anonymous && !hasNamedJSONTag(field) {
|
||||
// embedded struct
|
||||
sub := modelBuilder{new(ModelList), b.Config}
|
||||
sub.addModel(fieldType, "")
|
||||
subKey := sub.keyFrom(fieldType)
|
||||
// merge properties from sub
|
||||
subModel, _ := sub.Models.At(subKey)
|
||||
subModel.Properties.Do(func(k string, v ModelProperty) {
|
||||
model.Properties.Put(k, v)
|
||||
// if subModel says this property is required then include it
|
||||
required := false
|
||||
for _, each := range subModel.Required {
|
||||
if k == each {
|
||||
required = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if required {
|
||||
model.Required = append(model.Required, k)
|
||||
}
|
||||
})
|
||||
// add all new referenced models
|
||||
sub.Models.Do(func(key string, sub Model) {
|
||||
if key != subKey {
|
||||
if _, ok := b.Models.At(key); !ok {
|
||||
b.Models.Put(key, sub)
|
||||
}
|
||||
}
|
||||
})
|
||||
// empty name signals skip property
|
||||
return "", prop
|
||||
}
|
||||
// simple struct
|
||||
b.addModel(fieldType, "")
|
||||
var pType = b.keyFrom(fieldType)
|
||||
prop.Ref = &pType
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop ModelProperty) {
|
||||
// check for type override in tags
|
||||
prop.setPropertyMetadata(field)
|
||||
if prop.Type != nil {
|
||||
return jsonName, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
if fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
stringt := "string"
|
||||
prop.Type = &stringt
|
||||
return jsonName, prop
|
||||
}
|
||||
var pType = "array"
|
||||
prop.Type = &pType
|
||||
isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
|
||||
elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
|
||||
prop.Items = new(Item)
|
||||
if isPrimitive {
|
||||
mapped := b.jsonSchemaType(elemTypeName)
|
||||
prop.Items.Type = &mapped
|
||||
} else {
|
||||
prop.Items.Ref = &elemTypeName
|
||||
}
|
||||
// add|overwrite model for element type
|
||||
if fieldType.Elem().Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
}
|
||||
if !isPrimitive {
|
||||
b.addModel(fieldType.Elem(), elemTypeName)
|
||||
}
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
func (b modelBuilder) buildPointerTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop ModelProperty) {
|
||||
prop.setPropertyMetadata(field)
|
||||
// Check for type override in tags
|
||||
if prop.Type != nil {
|
||||
return jsonName, prop
|
||||
}
|
||||
fieldType := field.Type
|
||||
|
||||
// override type of pointer to list-likes
|
||||
if fieldType.Elem().Kind() == reflect.Slice || fieldType.Elem().Kind() == reflect.Array {
|
||||
var pType = "array"
|
||||
prop.Type = &pType
|
||||
isPrimitive := b.isPrimitiveType(fieldType.Elem().Elem().Name())
|
||||
elemName := b.getElementTypeName(modelName, jsonName, fieldType.Elem().Elem())
|
||||
if isPrimitive {
|
||||
primName := b.jsonSchemaType(elemName)
|
||||
prop.Items = &Item{Ref: &primName}
|
||||
} else {
|
||||
prop.Items = &Item{Ref: &elemName}
|
||||
}
|
||||
if !isPrimitive {
|
||||
// add|overwrite model for element type
|
||||
b.addModel(fieldType.Elem().Elem(), elemName)
|
||||
}
|
||||
} else {
|
||||
// non-array, pointer type
|
||||
fieldTypeName := b.keyFrom(fieldType.Elem())
|
||||
var pType = b.jsonSchemaType(fieldTypeName) // no star, include pkg path
|
||||
if b.isPrimitiveType(fieldTypeName) {
|
||||
prop.Type = &pType
|
||||
prop.Format = b.jsonSchemaFormat(fieldTypeName)
|
||||
return jsonName, prop
|
||||
}
|
||||
prop.Ref = &pType
|
||||
elemName := ""
|
||||
if fieldType.Elem().Name() == "" {
|
||||
elemName = modelName + "." + jsonName
|
||||
prop.Ref = &elemName
|
||||
}
|
||||
b.addModel(fieldType.Elem(), elemName)
|
||||
}
|
||||
return jsonName, prop
|
||||
}
|
||||
|
||||
func (b modelBuilder) getElementTypeName(modelName, jsonName string, t reflect.Type) string {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Name() == "" {
|
||||
return modelName + "." + jsonName
|
||||
}
|
||||
return b.keyFrom(t)
|
||||
}
|
||||
|
||||
func (b modelBuilder) keyFrom(st reflect.Type) string {
|
||||
key := st.String()
|
||||
if b.Config != nil && b.Config.ModelTypeNameHandler != nil {
|
||||
if name, ok := b.Config.ModelTypeNameHandler(st); ok {
|
||||
key = name
|
||||
}
|
||||
}
|
||||
if len(st.Name()) == 0 { // unnamed type
|
||||
// Swagger UI has special meaning for [
|
||||
key = strings.Replace(key, "[]", "||", -1)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// see also https://golang.org/ref/spec#Numeric_types
|
||||
func (b modelBuilder) isPrimitiveType(modelName string) bool {
|
||||
if len(modelName) == 0 {
|
||||
return false
|
||||
}
|
||||
return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time", modelName)
|
||||
}
|
||||
|
||||
// jsonNameOfField returns the name of the field as it should appear in JSON format
|
||||
// An empty string indicates that this field is not part of the JSON representation
|
||||
func (b modelBuilder) jsonNameOfField(field reflect.StructField) string {
|
||||
if jsonTag := field.Tag.Get("json"); jsonTag != "" {
|
||||
s := strings.Split(jsonTag, ",")
|
||||
if s[0] == "-" {
|
||||
// empty name signals skip property
|
||||
return ""
|
||||
} else if s[0] != "" {
|
||||
return s[0]
|
||||
}
|
||||
}
|
||||
return field.Name
|
||||
}
|
||||
|
||||
// see also http://json-schema.org/latest/json-schema-core.html#anchor8
|
||||
func (b modelBuilder) jsonSchemaType(modelName string) string {
|
||||
schemaMap := map[string]string{
|
||||
"uint": "integer",
|
||||
"uint8": "integer",
|
||||
"uint16": "integer",
|
||||
"uint32": "integer",
|
||||
"uint64": "integer",
|
||||
|
||||
"int": "integer",
|
||||
"int8": "integer",
|
||||
"int16": "integer",
|
||||
"int32": "integer",
|
||||
"int64": "integer",
|
||||
|
||||
"byte": "integer",
|
||||
"float64": "number",
|
||||
"float32": "number",
|
||||
"bool": "boolean",
|
||||
"time.Time": "string",
|
||||
}
|
||||
mapped, ok := schemaMap[modelName]
|
||||
if !ok {
|
||||
return modelName // use as is (custom or struct)
|
||||
}
|
||||
return mapped
|
||||
}
|
||||
|
||||
func (b modelBuilder) jsonSchemaFormat(modelName string) string {
|
||||
if b.Config != nil && b.Config.SchemaFormatHandler != nil {
|
||||
if mapped := b.Config.SchemaFormatHandler(modelName); mapped != "" {
|
||||
return mapped
|
||||
}
|
||||
}
|
||||
schemaMap := map[string]string{
|
||||
"int": "int32",
|
||||
"int32": "int32",
|
||||
"int64": "int64",
|
||||
"byte": "byte",
|
||||
"uint": "integer",
|
||||
"uint8": "byte",
|
||||
"float64": "double",
|
||||
"float32": "float",
|
||||
"time.Time": "date-time",
|
||||
"*time.Time": "date-time",
|
||||
}
|
||||
mapped, ok := schemaMap[modelName]
|
||||
if !ok {
|
||||
return "" // no format
|
||||
}
|
||||
return mapped
|
||||
}
|
86
vendor/github.com/emicklei/go-restful-swagger12/model_list.go
generated
vendored
86
vendor/github.com/emicklei/go-restful-swagger12/model_list.go
generated
vendored
|
@ -1,86 +0,0 @@
|
|||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NamedModel associates a name with a Model (not using its Id)
|
||||
type NamedModel struct {
|
||||
Name string
|
||||
Model Model
|
||||
}
|
||||
|
||||
// ModelList encapsulates a list of NamedModel (association)
|
||||
type ModelList struct {
|
||||
List []NamedModel
|
||||
}
|
||||
|
||||
// Put adds or replaces a Model by its name
|
||||
func (l *ModelList) Put(name string, model Model) {
|
||||
for i, each := range l.List {
|
||||
if each.Name == name {
|
||||
// replace
|
||||
l.List[i] = NamedModel{name, model}
|
||||
return
|
||||
}
|
||||
}
|
||||
// add
|
||||
l.List = append(l.List, NamedModel{name, model})
|
||||
}
|
||||
|
||||
// At returns a Model by its name, ok is false if absent
|
||||
func (l *ModelList) At(name string) (m Model, ok bool) {
|
||||
for _, each := range l.List {
|
||||
if each.Name == name {
|
||||
return each.Model, true
|
||||
}
|
||||
}
|
||||
return m, false
|
||||
}
|
||||
|
||||
// Do enumerates all the models, each with its assigned name
|
||||
func (l *ModelList) Do(block func(name string, value Model)) {
|
||||
for _, each := range l.List {
|
||||
block(each.Name, each.Model)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON writes the ModelList as if it was a map[string]Model
|
||||
func (l ModelList) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
buf.WriteString("{\n")
|
||||
for i, each := range l.List {
|
||||
buf.WriteString("\"")
|
||||
buf.WriteString(each.Name)
|
||||
buf.WriteString("\": ")
|
||||
encoder.Encode(each.Model)
|
||||
if i < len(l.List)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON reads back a ModelList. This is an expensive operation.
|
||||
func (l *ModelList) UnmarshalJSON(data []byte) error {
|
||||
raw := map[string]interface{}{}
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&raw)
|
||||
for k, v := range raw {
|
||||
// produces JSON bytes for each value
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var m Model
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&m)
|
||||
l.Put(k, m)
|
||||
}
|
||||
return nil
|
||||
}
|
81
vendor/github.com/emicklei/go-restful-swagger12/model_property_ext.go
generated
vendored
81
vendor/github.com/emicklei/go-restful-swagger12/model_property_ext.go
generated
vendored
|
@ -1,81 +0,0 @@
|
|||
package swagger
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (prop *ModelProperty) setDescription(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("description"); tag != "" {
|
||||
prop.Description = tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setDefaultValue(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("default"); tag != "" {
|
||||
prop.DefaultValue = Special(tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setEnumValues(field reflect.StructField) {
|
||||
// We use | to separate the enum values. This value is chosen
|
||||
// since its unlikely to be useful in actual enumeration values.
|
||||
if tag := field.Tag.Get("enum"); tag != "" {
|
||||
prop.Enum = strings.Split(tag, "|")
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setMaximum(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("maximum"); tag != "" {
|
||||
prop.Maximum = tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setType(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("type"); tag != "" {
|
||||
// Check if the first two characters of the type tag are
|
||||
// intended to emulate slice/array behaviour.
|
||||
//
|
||||
// If type is intended to be a slice/array then add the
|
||||
// overriden type to the array item instead of the main property
|
||||
if len(tag) > 2 && tag[0:2] == "[]" {
|
||||
pType := "array"
|
||||
prop.Type = &pType
|
||||
prop.Items = new(Item)
|
||||
|
||||
iType := tag[2:]
|
||||
prop.Items.Type = &iType
|
||||
return
|
||||
}
|
||||
|
||||
prop.Type = &tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setMinimum(field reflect.StructField) {
|
||||
if tag := field.Tag.Get("minimum"); tag != "" {
|
||||
prop.Minimum = tag
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setUniqueItems(field reflect.StructField) {
|
||||
tag := field.Tag.Get("unique")
|
||||
switch tag {
|
||||
case "true":
|
||||
v := true
|
||||
prop.UniqueItems = &v
|
||||
case "false":
|
||||
v := false
|
||||
prop.UniqueItems = &v
|
||||
}
|
||||
}
|
||||
|
||||
func (prop *ModelProperty) setPropertyMetadata(field reflect.StructField) {
|
||||
prop.setDescription(field)
|
||||
prop.setEnumValues(field)
|
||||
prop.setMinimum(field)
|
||||
prop.setMaximum(field)
|
||||
prop.setUniqueItems(field)
|
||||
prop.setDefaultValue(field)
|
||||
prop.setType(field)
|
||||
}
|
87
vendor/github.com/emicklei/go-restful-swagger12/model_property_list.go
generated
vendored
87
vendor/github.com/emicklei/go-restful-swagger12/model_property_list.go
generated
vendored
|
@ -1,87 +0,0 @@
|
|||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NamedModelProperty associates a name to a ModelProperty
|
||||
type NamedModelProperty struct {
|
||||
Name string
|
||||
Property ModelProperty
|
||||
}
|
||||
|
||||
// ModelPropertyList encapsulates a list of NamedModelProperty (association)
|
||||
type ModelPropertyList struct {
|
||||
List []NamedModelProperty
|
||||
}
|
||||
|
||||
// At returns the ModelPropety by its name unless absent, then ok is false
|
||||
func (l *ModelPropertyList) At(name string) (p ModelProperty, ok bool) {
|
||||
for _, each := range l.List {
|
||||
if each.Name == name {
|
||||
return each.Property, true
|
||||
}
|
||||
}
|
||||
return p, false
|
||||
}
|
||||
|
||||
// Put adds or replaces a ModelProperty with this name
|
||||
func (l *ModelPropertyList) Put(name string, prop ModelProperty) {
|
||||
// maybe replace existing
|
||||
for i, each := range l.List {
|
||||
if each.Name == name {
|
||||
// replace
|
||||
l.List[i] = NamedModelProperty{Name: name, Property: prop}
|
||||
return
|
||||
}
|
||||
}
|
||||
// add
|
||||
l.List = append(l.List, NamedModelProperty{Name: name, Property: prop})
|
||||
}
|
||||
|
||||
// Do enumerates all the properties, each with its assigned name
|
||||
func (l *ModelPropertyList) Do(block func(name string, value ModelProperty)) {
|
||||
for _, each := range l.List {
|
||||
block(each.Name, each.Property)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON writes the ModelPropertyList as if it was a map[string]ModelProperty
|
||||
func (l ModelPropertyList) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
buf.WriteString("{\n")
|
||||
for i, each := range l.List {
|
||||
buf.WriteString("\"")
|
||||
buf.WriteString(each.Name)
|
||||
buf.WriteString("\": ")
|
||||
encoder.Encode(each.Property)
|
||||
if i < len(l.List)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON reads back a ModelPropertyList. This is an expensive operation.
|
||||
func (l *ModelPropertyList) UnmarshalJSON(data []byte) error {
|
||||
raw := map[string]interface{}{}
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&raw)
|
||||
for k, v := range raw {
|
||||
// produces JSON bytes for each value
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var m ModelProperty
|
||||
json.NewDecoder(bytes.NewReader(data)).Decode(&m)
|
||||
l.Put(k, m)
|
||||
}
|
||||
return nil
|
||||
}
|
36
vendor/github.com/emicklei/go-restful-swagger12/ordered_route_map.go
generated
vendored
36
vendor/github.com/emicklei/go-restful-swagger12/ordered_route_map.go
generated
vendored
|
@ -1,36 +0,0 @@
|
|||
package swagger
|
||||
|
||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
||||
// Use of this source code is governed by a license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
import "github.com/emicklei/go-restful"
|
||||
|
||||
type orderedRouteMap struct {
|
||||
elements map[string][]restful.Route
|
||||
keys []string
|
||||
}
|
||||
|
||||
func newOrderedRouteMap() *orderedRouteMap {
|
||||
return &orderedRouteMap{
|
||||
elements: map[string][]restful.Route{},
|
||||
keys: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func (o *orderedRouteMap) Add(key string, route restful.Route) {
|
||||
routes, ok := o.elements[key]
|
||||
if ok {
|
||||
routes = append(routes, route)
|
||||
o.elements[key] = routes
|
||||
return
|
||||
}
|
||||
o.elements[key] = []restful.Route{route}
|
||||
o.keys = append(o.keys, key)
|
||||
}
|
||||
|
||||
func (o *orderedRouteMap) Do(block func(key string, routes []restful.Route)) {
|
||||
for _, k := range o.keys {
|
||||
block(k, o.elements[k])
|
||||
}
|
||||
}
|
185
vendor/github.com/emicklei/go-restful-swagger12/swagger.go
generated
vendored
185
vendor/github.com/emicklei/go-restful-swagger12/swagger.go
generated
vendored
|
@ -1,185 +0,0 @@
|
|||
// Package swagger implements the structures of the Swagger
|
||||
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md
|
||||
package swagger
|
||||
|
||||
const swaggerVersion = "1.2"
|
||||
|
||||
// 4.3.3 Data Type Fields
|
||||
type DataTypeFields struct {
|
||||
Type *string `json:"type,omitempty"` // if Ref not used
|
||||
Ref *string `json:"$ref,omitempty"` // if Type not used
|
||||
Format string `json:"format,omitempty"`
|
||||
DefaultValue Special `json:"defaultValue,omitempty"`
|
||||
Enum []string `json:"enum,omitempty"`
|
||||
Minimum string `json:"minimum,omitempty"`
|
||||
Maximum string `json:"maximum,omitempty"`
|
||||
Items *Item `json:"items,omitempty"`
|
||||
UniqueItems *bool `json:"uniqueItems,omitempty"`
|
||||
}
|
||||
|
||||
type Special string
|
||||
|
||||
// 4.3.4 Items Object
|
||||
type Item struct {
|
||||
Type *string `json:"type,omitempty"`
|
||||
Ref *string `json:"$ref,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
}
|
||||
|
||||
// 5.1 Resource Listing
|
||||
type ResourceListing struct {
|
||||
SwaggerVersion string `json:"swaggerVersion"` // e.g 1.2
|
||||
Apis []Resource `json:"apis"`
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
Info Info `json:"info"`
|
||||
Authorizations []Authorization `json:"authorizations,omitempty"`
|
||||
}
|
||||
|
||||
// 5.1.2 Resource Object
|
||||
type Resource struct {
|
||||
Path string `json:"path"` // relative or absolute, must start with /
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// 5.1.3 Info Object
|
||||
type Info struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
TermsOfServiceUrl string `json:"termsOfServiceUrl,omitempty"`
|
||||
Contact string `json:"contact,omitempty"`
|
||||
License string `json:"license,omitempty"`
|
||||
LicenseUrl string `json:"licenseUrl,omitempty"`
|
||||
}
|
||||
|
||||
// 5.1.5
|
||||
type Authorization struct {
|
||||
Type string `json:"type"`
|
||||
PassAs string `json:"passAs"`
|
||||
Keyname string `json:"keyname"`
|
||||
Scopes []Scope `json:"scopes"`
|
||||
GrantTypes []GrantType `json:"grandTypes"`
|
||||
}
|
||||
|
||||
// 5.1.6, 5.2.11
|
||||
type Scope struct {
|
||||
// Required. The name of the scope.
|
||||
Scope string `json:"scope"`
|
||||
// Recommended. A short description of the scope.
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// 5.1.7
|
||||
type GrantType struct {
|
||||
Implicit Implicit `json:"implicit"`
|
||||
AuthorizationCode AuthorizationCode `json:"authorization_code"`
|
||||
}
|
||||
|
||||
// 5.1.8 Implicit Object
|
||||
type Implicit struct {
|
||||
// Required. The login endpoint definition.
|
||||
loginEndpoint LoginEndpoint `json:"loginEndpoint"`
|
||||
// An optional alternative name to standard "access_token" OAuth2 parameter.
|
||||
TokenName string `json:"tokenName"`
|
||||
}
|
||||
|
||||
// 5.1.9 Authorization Code Object
|
||||
type AuthorizationCode struct {
|
||||
TokenRequestEndpoint TokenRequestEndpoint `json:"tokenRequestEndpoint"`
|
||||
TokenEndpoint TokenEndpoint `json:"tokenEndpoint"`
|
||||
}
|
||||
|
||||
// 5.1.10 Login Endpoint Object
|
||||
type LoginEndpoint struct {
|
||||
// Required. The URL of the authorization endpoint for the implicit grant flow. The value SHOULD be in a URL format.
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
// 5.1.11 Token Request Endpoint Object
|
||||
type TokenRequestEndpoint struct {
|
||||
// Required. The URL of the authorization endpoint for the authentication code grant flow. The value SHOULD be in a URL format.
|
||||
Url string `json:"url"`
|
||||
// An optional alternative name to standard "client_id" OAuth2 parameter.
|
||||
ClientIdName string `json:"clientIdName"`
|
||||
// An optional alternative name to the standard "client_secret" OAuth2 parameter.
|
||||
ClientSecretName string `json:"clientSecretName"`
|
||||
}
|
||||
|
||||
// 5.1.12 Token Endpoint Object
|
||||
type TokenEndpoint struct {
|
||||
// Required. The URL of the token endpoint for the authentication code grant flow. The value SHOULD be in a URL format.
|
||||
Url string `json:"url"`
|
||||
// An optional alternative name to standard "access_token" OAuth2 parameter.
|
||||
TokenName string `json:"tokenName"`
|
||||
}
|
||||
|
||||
// 5.2 API Declaration
|
||||
type ApiDeclaration struct {
|
||||
SwaggerVersion string `json:"swaggerVersion"`
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
BasePath string `json:"basePath"`
|
||||
ResourcePath string `json:"resourcePath"` // must start with /
|
||||
Info Info `json:"info"`
|
||||
Apis []Api `json:"apis,omitempty"`
|
||||
Models ModelList `json:"models,omitempty"`
|
||||
Produces []string `json:"produces,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty"`
|
||||
Authorizations []Authorization `json:"authorizations,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.2 API Object
|
||||
type Api struct {
|
||||
Path string `json:"path"` // relative or absolute, must start with /
|
||||
Description string `json:"description"`
|
||||
Operations []Operation `json:"operations,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.3 Operation Object
|
||||
type Operation struct {
|
||||
DataTypeFields
|
||||
Method string `json:"method"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Notes string `json:"notes,omitempty"`
|
||||
Nickname string `json:"nickname"`
|
||||
Authorizations []Authorization `json:"authorizations,omitempty"`
|
||||
Parameters []Parameter `json:"parameters"`
|
||||
ResponseMessages []ResponseMessage `json:"responseMessages,omitempty"` // optional
|
||||
Produces []string `json:"produces,omitempty"`
|
||||
Consumes []string `json:"consumes,omitempty"`
|
||||
Deprecated string `json:"deprecated,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.4 Parameter Object
|
||||
type Parameter struct {
|
||||
DataTypeFields
|
||||
ParamType string `json:"paramType"` // path,query,body,header,form
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Required bool `json:"required"`
|
||||
AllowMultiple bool `json:"allowMultiple"`
|
||||
}
|
||||
|
||||
// 5.2.5 Response Message Object
|
||||
type ResponseMessage struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
ResponseModel string `json:"responseModel,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.6, 5.2.7 Models Object
|
||||
type Model struct {
|
||||
Id string `json:"id"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Required []string `json:"required,omitempty"`
|
||||
Properties ModelPropertyList `json:"properties"`
|
||||
SubTypes []string `json:"subTypes,omitempty"`
|
||||
Discriminator string `json:"discriminator,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.8 Properties Object
|
||||
type ModelProperty struct {
|
||||
DataTypeFields
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// 5.2.10
|
||||
type Authorizations map[string]Authorization
|
21
vendor/github.com/emicklei/go-restful-swagger12/swagger_builder.go
generated
vendored
21
vendor/github.com/emicklei/go-restful-swagger12/swagger_builder.go
generated
vendored
|
@ -1,21 +0,0 @@
|
|||
package swagger
|
||||
|
||||
type SwaggerBuilder struct {
|
||||
SwaggerService
|
||||
}
|
||||
|
||||
func NewSwaggerBuilder(config Config) *SwaggerBuilder {
|
||||
return &SwaggerBuilder{*newSwaggerService(config)}
|
||||
}
|
||||
|
||||
func (sb SwaggerBuilder) ProduceListing() ResourceListing {
|
||||
return sb.SwaggerService.produceListing()
|
||||
}
|
||||
|
||||
func (sb SwaggerBuilder) ProduceAllDeclarations() map[string]ApiDeclaration {
|
||||
return sb.SwaggerService.produceAllDeclarations()
|
||||
}
|
||||
|
||||
func (sb SwaggerBuilder) ProduceDeclarations(route string) (*ApiDeclaration, bool) {
|
||||
return sb.SwaggerService.produceDeclarations(route)
|
||||
}
|
443
vendor/github.com/emicklei/go-restful-swagger12/swagger_webservice.go
generated
vendored
443
vendor/github.com/emicklei/go-restful-swagger12/swagger_webservice.go
generated
vendored
|
@ -1,443 +0,0 @@
|
|||
package swagger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
// "github.com/emicklei/hopwatch"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful/log"
|
||||
)
|
||||
|
||||
type SwaggerService struct {
|
||||
config Config
|
||||
apiDeclarationMap *ApiDeclarationList
|
||||
}
|
||||
|
||||
func newSwaggerService(config Config) *SwaggerService {
|
||||
sws := &SwaggerService{
|
||||
config: config,
|
||||
apiDeclarationMap: new(ApiDeclarationList)}
|
||||
|
||||
// Build all ApiDeclarations
|
||||
for _, each := range config.WebServices {
|
||||
rootPath := each.RootPath()
|
||||
// skip the api service itself
|
||||
if rootPath != config.ApiPath {
|
||||
if rootPath == "" || rootPath == "/" {
|
||||
// use routes
|
||||
for _, route := range each.Routes() {
|
||||
entry := staticPathFromRoute(route)
|
||||
_, exists := sws.apiDeclarationMap.At(entry)
|
||||
if !exists {
|
||||
sws.apiDeclarationMap.Put(entry, sws.composeDeclaration(each, entry))
|
||||
}
|
||||
}
|
||||
} else { // use root path
|
||||
sws.apiDeclarationMap.Put(each.RootPath(), sws.composeDeclaration(each, each.RootPath()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if specified then call the PostBuilderHandler
|
||||
if config.PostBuildHandler != nil {
|
||||
config.PostBuildHandler(sws.apiDeclarationMap)
|
||||
}
|
||||
return sws
|
||||
}
|
||||
|
||||
// LogInfo is the function that is called when this package needs to log. It defaults to log.Printf
|
||||
var LogInfo = func(format string, v ...interface{}) {
|
||||
// use the restful package-wide logger
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
|
||||
// InstallSwaggerService add the WebService that provides the API documentation of all services
|
||||
// conform the Swagger documentation specifcation. (https://github.com/wordnik/swagger-core/wiki).
|
||||
func InstallSwaggerService(aSwaggerConfig Config) {
|
||||
RegisterSwaggerService(aSwaggerConfig, restful.DefaultContainer)
|
||||
}
|
||||
|
||||
// RegisterSwaggerService add the WebService that provides the API documentation of all services
|
||||
// conform the Swagger documentation specifcation. (https://github.com/wordnik/swagger-core/wiki).
|
||||
func RegisterSwaggerService(config Config, wsContainer *restful.Container) {
|
||||
sws := newSwaggerService(config)
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(config.ApiPath)
|
||||
ws.Produces(restful.MIME_JSON)
|
||||
if config.DisableCORS {
|
||||
ws.Filter(enableCORS)
|
||||
}
|
||||
ws.Route(ws.GET("/").To(sws.getListing))
|
||||
ws.Route(ws.GET("/{a}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}/{f}").To(sws.getDeclarations))
|
||||
ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}/{f}/{g}").To(sws.getDeclarations))
|
||||
LogInfo("[restful/swagger] listing is available at %v%v", config.WebServicesUrl, config.ApiPath)
|
||||
wsContainer.Add(ws)
|
||||
|
||||
// Check paths for UI serving
|
||||
if config.StaticHandler == nil && config.SwaggerFilePath != "" && config.SwaggerPath != "" {
|
||||
swaggerPathSlash := config.SwaggerPath
|
||||
// path must end with slash /
|
||||
if "/" != config.SwaggerPath[len(config.SwaggerPath)-1:] {
|
||||
LogInfo("[restful/swagger] use corrected SwaggerPath ; must end with slash (/)")
|
||||
swaggerPathSlash += "/"
|
||||
}
|
||||
|
||||
LogInfo("[restful/swagger] %v%v is mapped to folder %v", config.WebServicesUrl, swaggerPathSlash, config.SwaggerFilePath)
|
||||
wsContainer.Handle(swaggerPathSlash, http.StripPrefix(swaggerPathSlash, http.FileServer(http.Dir(config.SwaggerFilePath))))
|
||||
|
||||
//if we define a custom static handler use it
|
||||
} else if config.StaticHandler != nil && config.SwaggerPath != "" {
|
||||
swaggerPathSlash := config.SwaggerPath
|
||||
// path must end with slash /
|
||||
if "/" != config.SwaggerPath[len(config.SwaggerPath)-1:] {
|
||||
LogInfo("[restful/swagger] use corrected SwaggerFilePath ; must end with slash (/)")
|
||||
swaggerPathSlash += "/"
|
||||
|
||||
}
|
||||
LogInfo("[restful/swagger] %v%v is mapped to custom Handler %T", config.WebServicesUrl, swaggerPathSlash, config.StaticHandler)
|
||||
wsContainer.Handle(swaggerPathSlash, config.StaticHandler)
|
||||
|
||||
} else {
|
||||
LogInfo("[restful/swagger] Swagger(File)Path is empty ; no UI is served")
|
||||
}
|
||||
}
|
||||
|
||||
func staticPathFromRoute(r restful.Route) string {
|
||||
static := r.Path
|
||||
bracket := strings.Index(static, "{")
|
||||
if bracket <= 1 { // result cannot be empty
|
||||
return static
|
||||
}
|
||||
if bracket != -1 {
|
||||
static = r.Path[:bracket]
|
||||
}
|
||||
if strings.HasSuffix(static, "/") {
|
||||
return static[:len(static)-1]
|
||||
} else {
|
||||
return static
|
||||
}
|
||||
}
|
||||
|
||||
func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
|
||||
if origin := req.HeaderParameter(restful.HEADER_Origin); origin != "" {
|
||||
// prevent duplicate header
|
||||
if len(resp.Header().Get(restful.HEADER_AccessControlAllowOrigin)) == 0 {
|
||||
resp.AddHeader(restful.HEADER_AccessControlAllowOrigin, origin)
|
||||
}
|
||||
}
|
||||
chain.ProcessFilter(req, resp)
|
||||
}
|
||||
|
||||
func (sws SwaggerService) getListing(req *restful.Request, resp *restful.Response) {
|
||||
listing := sws.produceListing()
|
||||
resp.WriteAsJson(listing)
|
||||
}
|
||||
|
||||
func (sws SwaggerService) produceListing() ResourceListing {
|
||||
listing := ResourceListing{SwaggerVersion: swaggerVersion, ApiVersion: sws.config.ApiVersion, Info: sws.config.Info}
|
||||
sws.apiDeclarationMap.Do(func(k string, v ApiDeclaration) {
|
||||
ref := Resource{Path: k}
|
||||
if len(v.Apis) > 0 { // use description of first (could still be empty)
|
||||
ref.Description = v.Apis[0].Description
|
||||
}
|
||||
listing.Apis = append(listing.Apis, ref)
|
||||
})
|
||||
return listing
|
||||
}
|
||||
|
||||
func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Response) {
|
||||
decl, ok := sws.produceDeclarations(composeRootPath(req))
|
||||
if !ok {
|
||||
resp.WriteErrorString(http.StatusNotFound, "ApiDeclaration not found")
|
||||
return
|
||||
}
|
||||
// unless WebServicesUrl is given
|
||||
if len(sws.config.WebServicesUrl) == 0 {
|
||||
// update base path from the actual request
|
||||
// TODO how to detect https? assume http for now
|
||||
var host string
|
||||
// X-Forwarded-Host or Host or Request.Host
|
||||
hostvalues, ok := req.Request.Header["X-Forwarded-Host"] // apache specific?
|
||||
if !ok || len(hostvalues) == 0 {
|
||||
forwarded, ok := req.Request.Header["Host"] // without reverse-proxy
|
||||
if !ok || len(forwarded) == 0 {
|
||||
// fallback to Host field
|
||||
host = req.Request.Host
|
||||
} else {
|
||||
host = forwarded[0]
|
||||
}
|
||||
} else {
|
||||
host = hostvalues[0]
|
||||
}
|
||||
// inspect Referer for the scheme (http vs https)
|
||||
scheme := "http"
|
||||
if referer := req.Request.Header["Referer"]; len(referer) > 0 {
|
||||
if strings.HasPrefix(referer[0], "https") {
|
||||
scheme = "https"
|
||||
}
|
||||
}
|
||||
decl.BasePath = fmt.Sprintf("%s://%s", scheme, host)
|
||||
}
|
||||
resp.WriteAsJson(decl)
|
||||
}
|
||||
|
||||
func (sws SwaggerService) produceAllDeclarations() map[string]ApiDeclaration {
|
||||
decls := map[string]ApiDeclaration{}
|
||||
sws.apiDeclarationMap.Do(func(k string, v ApiDeclaration) {
|
||||
decls[k] = v
|
||||
})
|
||||
return decls
|
||||
}
|
||||
|
||||
func (sws SwaggerService) produceDeclarations(route string) (*ApiDeclaration, bool) {
|
||||
decl, ok := sws.apiDeclarationMap.At(route)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
decl.BasePath = sws.config.WebServicesUrl
|
||||
return &decl, true
|
||||
}
|
||||
|
||||
// composeDeclaration uses all routes and parameters to create a ApiDeclaration
|
||||
func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix string) ApiDeclaration {
|
||||
decl := ApiDeclaration{
|
||||
SwaggerVersion: swaggerVersion,
|
||||
BasePath: sws.config.WebServicesUrl,
|
||||
ResourcePath: pathPrefix,
|
||||
Models: ModelList{},
|
||||
ApiVersion: ws.Version()}
|
||||
|
||||
// collect any path parameters
|
||||
rootParams := []Parameter{}
|
||||
for _, param := range ws.PathParameters() {
|
||||
rootParams = append(rootParams, asSwaggerParameter(param.Data()))
|
||||
}
|
||||
// aggregate by path
|
||||
pathToRoutes := newOrderedRouteMap()
|
||||
for _, other := range ws.Routes() {
|
||||
if strings.HasPrefix(other.Path, pathPrefix) {
|
||||
if len(pathPrefix) > 1 && len(other.Path) > len(pathPrefix) && other.Path[len(pathPrefix)] != '/' {
|
||||
continue
|
||||
}
|
||||
pathToRoutes.Add(other.Path, other)
|
||||
}
|
||||
}
|
||||
pathToRoutes.Do(func(path string, routes []restful.Route) {
|
||||
api := Api{Path: strings.TrimSuffix(withoutWildcard(path), "/"), Description: ws.Documentation()}
|
||||
voidString := "void"
|
||||
for _, route := range routes {
|
||||
operation := Operation{
|
||||
Method: route.Method,
|
||||
Summary: route.Doc,
|
||||
Notes: route.Notes,
|
||||
// Type gets overwritten if there is a write sample
|
||||
DataTypeFields: DataTypeFields{Type: &voidString},
|
||||
Parameters: []Parameter{},
|
||||
Nickname: route.Operation,
|
||||
ResponseMessages: composeResponseMessages(route, &decl, &sws.config)}
|
||||
|
||||
operation.Consumes = route.Consumes
|
||||
operation.Produces = route.Produces
|
||||
|
||||
// share root params if any
|
||||
for _, swparam := range rootParams {
|
||||
operation.Parameters = append(operation.Parameters, swparam)
|
||||
}
|
||||
// route specific params
|
||||
for _, param := range route.ParameterDocs {
|
||||
operation.Parameters = append(operation.Parameters, asSwaggerParameter(param.Data()))
|
||||
}
|
||||
|
||||
sws.addModelsFromRouteTo(&operation, route, &decl)
|
||||
api.Operations = append(api.Operations, operation)
|
||||
}
|
||||
decl.Apis = append(decl.Apis, api)
|
||||
})
|
||||
return decl
|
||||
}
|
||||
|
||||
func withoutWildcard(path string) string {
|
||||
if strings.HasSuffix(path, ":*}") {
|
||||
return path[0:len(path)-3] + "}"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// composeResponseMessages takes the ResponseErrors (if any) and creates ResponseMessages from them.
|
||||
func composeResponseMessages(route restful.Route, decl *ApiDeclaration, config *Config) (messages []ResponseMessage) {
|
||||
if route.ResponseErrors == nil {
|
||||
return messages
|
||||
}
|
||||
// sort by code
|
||||
codes := sort.IntSlice{}
|
||||
for code := range route.ResponseErrors {
|
||||
codes = append(codes, code)
|
||||
}
|
||||
codes.Sort()
|
||||
for _, code := range codes {
|
||||
each := route.ResponseErrors[code]
|
||||
message := ResponseMessage{
|
||||
Code: code,
|
||||
Message: each.Message,
|
||||
}
|
||||
if each.Model != nil {
|
||||
st := reflect.TypeOf(each.Model)
|
||||
isCollection, st := detectCollectionType(st)
|
||||
// collection cannot be in responsemodel
|
||||
if !isCollection {
|
||||
modelName := modelBuilder{}.keyFrom(st)
|
||||
modelBuilder{Models: &decl.Models, Config: config}.addModel(st, "")
|
||||
message.ResponseModel = modelName
|
||||
}
|
||||
}
|
||||
messages = append(messages, message)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// addModelsFromRoute takes any read or write sample from the Route and creates a Swagger model from it.
|
||||
func (sws SwaggerService) addModelsFromRouteTo(operation *Operation, route restful.Route, decl *ApiDeclaration) {
|
||||
if route.ReadSample != nil {
|
||||
sws.addModelFromSampleTo(operation, false, route.ReadSample, &decl.Models)
|
||||
}
|
||||
if route.WriteSample != nil {
|
||||
sws.addModelFromSampleTo(operation, true, route.WriteSample, &decl.Models)
|
||||
}
|
||||
}
|
||||
|
||||
func detectCollectionType(st reflect.Type) (bool, reflect.Type) {
|
||||
isCollection := false
|
||||
if st.Kind() == reflect.Slice || st.Kind() == reflect.Array {
|
||||
st = st.Elem()
|
||||
isCollection = true
|
||||
} else {
|
||||
if st.Kind() == reflect.Ptr {
|
||||
if st.Elem().Kind() == reflect.Slice || st.Elem().Kind() == reflect.Array {
|
||||
st = st.Elem().Elem()
|
||||
isCollection = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return isCollection, st
|
||||
}
|
||||
|
||||
// addModelFromSample creates and adds (or overwrites) a Model from a sample resource
|
||||
func (sws SwaggerService) addModelFromSampleTo(operation *Operation, isResponse bool, sample interface{}, models *ModelList) {
|
||||
mb := modelBuilder{Models: models, Config: &sws.config}
|
||||
if isResponse {
|
||||
sampleType, items := asDataType(sample, &sws.config)
|
||||
operation.Type = sampleType
|
||||
operation.Items = items
|
||||
}
|
||||
mb.addModelFrom(sample)
|
||||
}
|
||||
|
||||
func asSwaggerParameter(param restful.ParameterData) Parameter {
|
||||
return Parameter{
|
||||
DataTypeFields: DataTypeFields{
|
||||
Type: ¶m.DataType,
|
||||
Format: asFormat(param.DataType, param.DataFormat),
|
||||
DefaultValue: Special(param.DefaultValue),
|
||||
},
|
||||
Name: param.Name,
|
||||
Description: param.Description,
|
||||
ParamType: asParamType(param.Kind),
|
||||
|
||||
Required: param.Required}
|
||||
}
|
||||
|
||||
// Between 1..7 path parameters is supported
|
||||
func composeRootPath(req *restful.Request) string {
|
||||
path := "/" + req.PathParameter("a")
|
||||
b := req.PathParameter("b")
|
||||
if b == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + b
|
||||
c := req.PathParameter("c")
|
||||
if c == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + c
|
||||
d := req.PathParameter("d")
|
||||
if d == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + d
|
||||
e := req.PathParameter("e")
|
||||
if e == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + e
|
||||
f := req.PathParameter("f")
|
||||
if f == "" {
|
||||
return path
|
||||
}
|
||||
path = path + "/" + f
|
||||
g := req.PathParameter("g")
|
||||
if g == "" {
|
||||
return path
|
||||
}
|
||||
return path + "/" + g
|
||||
}
|
||||
|
||||
func asFormat(dataType string, dataFormat string) string {
|
||||
if dataFormat != "" {
|
||||
return dataFormat
|
||||
}
|
||||
return "" // TODO
|
||||
}
|
||||
|
||||
func asParamType(kind int) string {
|
||||
switch {
|
||||
case kind == restful.PathParameterKind:
|
||||
return "path"
|
||||
case kind == restful.QueryParameterKind:
|
||||
return "query"
|
||||
case kind == restful.BodyParameterKind:
|
||||
return "body"
|
||||
case kind == restful.HeaderParameterKind:
|
||||
return "header"
|
||||
case kind == restful.FormParameterKind:
|
||||
return "form"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func asDataType(any interface{}, config *Config) (*string, *Item) {
|
||||
// If it's not a collection, return the suggested model name
|
||||
st := reflect.TypeOf(any)
|
||||
isCollection, st := detectCollectionType(st)
|
||||
modelName := modelBuilder{}.keyFrom(st)
|
||||
// if it's not a collection we are done
|
||||
if !isCollection {
|
||||
return &modelName, nil
|
||||
}
|
||||
|
||||
// XXX: This is not very elegant
|
||||
// We create an Item object referring to the given model
|
||||
models := ModelList{}
|
||||
mb := modelBuilder{Models: &models, Config: config}
|
||||
mb.addModelFrom(any)
|
||||
|
||||
elemTypeName := mb.getElementTypeName(modelName, "", st)
|
||||
item := new(Item)
|
||||
if mb.isPrimitiveType(elemTypeName) {
|
||||
mapped := mb.jsonSchemaType(elemTypeName)
|
||||
item.Type = &mapped
|
||||
} else {
|
||||
item.Ref = &elemTypeName
|
||||
}
|
||||
tmp := "array"
|
||||
return &tmp, item
|
||||
}
|
20
vendor/github.com/fatih/camelcase/LICENSE.md
generated
vendored
20
vendor/github.com/fatih/camelcase/LICENSE.md
generated
vendored
|
@ -1,20 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Fatih Arslan
|
||||
|
||||
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.
|
58
vendor/github.com/fatih/camelcase/README.md
generated
vendored
58
vendor/github.com/fatih/camelcase/README.md
generated
vendored
|
@ -1,58 +0,0 @@
|
|||
# CamelCase [](http://godoc.org/github.com/fatih/camelcase) [](https://travis-ci.org/fatih/camelcase)
|
||||
|
||||
CamelCase is a Golang (Go) package to split the words of a camelcase type
|
||||
string into a slice of words. It can be used to convert a camelcase word (lower
|
||||
or upper case) into any type of word.
|
||||
|
||||
## Splitting rules:
|
||||
|
||||
1. If string is not valid UTF-8, return it without splitting as
|
||||
single item array.
|
||||
2. Assign all unicode characters into one of 4 sets: lower case
|
||||
letters, upper case letters, numbers, and all other characters.
|
||||
3. Iterate through characters of string, introducing splits
|
||||
between adjacent characters that belong to different sets.
|
||||
4. Iterate through array of split strings, and if a given string
|
||||
is upper case:
|
||||
* if subsequent string is lower case:
|
||||
* move last character of upper case string to beginning of
|
||||
lower case string
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/camelcase
|
||||
```
|
||||
|
||||
## Usage and examples
|
||||
|
||||
```go
|
||||
splitted := camelcase.Split("GolangPackage")
|
||||
|
||||
fmt.Println(splitted[0], splitted[1]) // prints: "Golang", "Package"
|
||||
```
|
||||
|
||||
Both lower camel case and upper camel case are supported. For more info please
|
||||
check: [http://en.wikipedia.org/wiki/CamelCase](http://en.wikipedia.org/wiki/CamelCase)
|
||||
|
||||
Below are some example cases:
|
||||
|
||||
```
|
||||
"" => []
|
||||
"lowercase" => ["lowercase"]
|
||||
"Class" => ["Class"]
|
||||
"MyClass" => ["My", "Class"]
|
||||
"MyC" => ["My", "C"]
|
||||
"HTML" => ["HTML"]
|
||||
"PDFLoader" => ["PDF", "Loader"]
|
||||
"AString" => ["A", "String"]
|
||||
"SimpleXMLParser" => ["Simple", "XML", "Parser"]
|
||||
"vimRPCPlugin" => ["vim", "RPC", "Plugin"]
|
||||
"GL11Version" => ["GL", "11", "Version"]
|
||||
"99Bottles" => ["99", "Bottles"]
|
||||
"May5" => ["May", "5"]
|
||||
"BFG9000" => ["BFG", "9000"]
|
||||
"BöseÜberraschung" => ["Böse", "Überraschung"]
|
||||
"Two spaces" => ["Two", " ", "spaces"]
|
||||
"BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"]
|
||||
```
|
90
vendor/github.com/fatih/camelcase/camelcase.go
generated
vendored
90
vendor/github.com/fatih/camelcase/camelcase.go
generated
vendored
|
@ -1,90 +0,0 @@
|
|||
// Package camelcase is a micro package to split the words of a camelcase type
|
||||
// string into a slice of words.
|
||||
package camelcase
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Split splits the camelcase word and returns a list of words. It also
|
||||
// supports digits. Both lower camel case and upper camel case are supported.
|
||||
// For more info please check: http://en.wikipedia.org/wiki/CamelCase
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// "" => [""]
|
||||
// "lowercase" => ["lowercase"]
|
||||
// "Class" => ["Class"]
|
||||
// "MyClass" => ["My", "Class"]
|
||||
// "MyC" => ["My", "C"]
|
||||
// "HTML" => ["HTML"]
|
||||
// "PDFLoader" => ["PDF", "Loader"]
|
||||
// "AString" => ["A", "String"]
|
||||
// "SimpleXMLParser" => ["Simple", "XML", "Parser"]
|
||||
// "vimRPCPlugin" => ["vim", "RPC", "Plugin"]
|
||||
// "GL11Version" => ["GL", "11", "Version"]
|
||||
// "99Bottles" => ["99", "Bottles"]
|
||||
// "May5" => ["May", "5"]
|
||||
// "BFG9000" => ["BFG", "9000"]
|
||||
// "BöseÜberraschung" => ["Böse", "Überraschung"]
|
||||
// "Two spaces" => ["Two", " ", "spaces"]
|
||||
// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"]
|
||||
//
|
||||
// Splitting rules
|
||||
//
|
||||
// 1) If string is not valid UTF-8, return it without splitting as
|
||||
// single item array.
|
||||
// 2) Assign all unicode characters into one of 4 sets: lower case
|
||||
// letters, upper case letters, numbers, and all other characters.
|
||||
// 3) Iterate through characters of string, introducing splits
|
||||
// between adjacent characters that belong to different sets.
|
||||
// 4) Iterate through array of split strings, and if a given string
|
||||
// is upper case:
|
||||
// if subsequent string is lower case:
|
||||
// move last character of upper case string to beginning of
|
||||
// lower case string
|
||||
func Split(src string) (entries []string) {
|
||||
// don't split invalid utf8
|
||||
if !utf8.ValidString(src) {
|
||||
return []string{src}
|
||||
}
|
||||
entries = []string{}
|
||||
var runes [][]rune
|
||||
lastClass := 0
|
||||
class := 0
|
||||
// split into fields based on class of unicode character
|
||||
for _, r := range src {
|
||||
switch true {
|
||||
case unicode.IsLower(r):
|
||||
class = 1
|
||||
case unicode.IsUpper(r):
|
||||
class = 2
|
||||
case unicode.IsDigit(r):
|
||||
class = 3
|
||||
default:
|
||||
class = 4
|
||||
}
|
||||
if class == lastClass {
|
||||
runes[len(runes)-1] = append(runes[len(runes)-1], r)
|
||||
} else {
|
||||
runes = append(runes, []rune{r})
|
||||
}
|
||||
lastClass = class
|
||||
}
|
||||
// handle upper case -> lower case sequences, e.g.
|
||||
// "PDFL", "oader" -> "PDF", "Loader"
|
||||
for i := 0; i < len(runes)-1; i++ {
|
||||
if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) {
|
||||
runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...)
|
||||
runes[i] = runes[i][:len(runes[i])-1]
|
||||
}
|
||||
}
|
||||
// construct []string from results
|
||||
for _, s := range runes {
|
||||
if len(s) > 0 {
|
||||
entries = append(entries, string(s))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
22
vendor/github.com/ulule/deepcopier/LICENSE
generated
vendored
22
vendor/github.com/ulule/deepcopier/LICENSE
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Ulule
|
||||
|
||||
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.
|
||||
|
129
vendor/github.com/ulule/deepcopier/README.md
generated
vendored
129
vendor/github.com/ulule/deepcopier/README.md
generated
vendored
|
@ -1,129 +0,0 @@
|
|||
# Deepcopier
|
||||
|
||||
[](http://travis-ci.org/ulule/deepcopier)
|
||||
|
||||
This package is meant to make copying of structs to/from others structs a bit easier.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get -u github.com/ulule/deepcopier
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```golang
|
||||
// Deep copy instance1 into instance2
|
||||
Copy(instance1).To(instance2)
|
||||
|
||||
// Deep copy instance1 into instance2 and passes the following context (which
|
||||
// is basically a map[string]interface{}) as first argument
|
||||
// to methods of instance2 that defined the struct tag "context".
|
||||
Copy(instance1).WithContext(map[string]interface{}{"foo": "bar"}).To(instance2)
|
||||
|
||||
// Deep copy instance2 into instance1
|
||||
Copy(instance1).From(instance2)
|
||||
|
||||
// Deep copy instance2 into instance1 and passes the following context (which
|
||||
// is basically a map[string]interface{}) as first argument
|
||||
// to methods of instance1 that defined the struct tag "context".
|
||||
Copy(instance1).WithContext(map[string]interface{}{"foo": "bar"}).From(instance2)
|
||||
```
|
||||
|
||||
Available options for `deepcopier` struct tag:
|
||||
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------------------------- |
|
||||
| `field` | Field or method name in source instance |
|
||||
| `skip` | Ignores the field |
|
||||
| `context` | Takes a `map[string]interface{}` as first argument (for methods) |
|
||||
| `force` | Set the value of a `sql.Null*` field (instead of copying the struct) |
|
||||
|
||||
**Options example:**
|
||||
|
||||
```golang
|
||||
type Source struct {
|
||||
Name string
|
||||
SkipMe string
|
||||
SQLNullStringToSQLNullString sql.NullString
|
||||
SQLNullStringToString sql.NullString
|
||||
|
||||
}
|
||||
|
||||
func (Source) MethodThatTakesContext(c map[string]interface{}) string {
|
||||
return "whatever"
|
||||
}
|
||||
|
||||
type Destination struct {
|
||||
FieldWithAnotherNameInSource string `deepcopier:"field:Name"`
|
||||
SkipMe string `deepcopier:"skip"`
|
||||
MethodThatTakesContext string `deepcopier:"context"`
|
||||
SQLNullStringToSQLNullString sql.NullString
|
||||
SQLNullStringToString string `deepcopier:"force"`
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ulule/deepcopier"
|
||||
)
|
||||
|
||||
// Model
|
||||
type User struct {
|
||||
// Basic string field
|
||||
Name string
|
||||
// Deepcopier supports https://golang.org/pkg/database/sql/driver/#Valuer
|
||||
Email sql.NullString
|
||||
}
|
||||
|
||||
func (u *User) MethodThatTakesContext(ctx map[string]interface{}) string {
|
||||
// do whatever you want
|
||||
return "hello from this method"
|
||||
}
|
||||
|
||||
// Resource
|
||||
type UserResource struct {
|
||||
DisplayName string `deepcopier:"field:Name"`
|
||||
SkipMe string `deepcopier:"skip"`
|
||||
MethodThatTakesContext string `deepcopier:"context"`
|
||||
Email string `deepcopier:"force"`
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
user := &User{
|
||||
Name: "gilles",
|
||||
Email: sql.NullString{
|
||||
Valid: true,
|
||||
String: "gilles@example.com",
|
||||
},
|
||||
}
|
||||
|
||||
resource := &UserResource{}
|
||||
|
||||
deepcopier.Copy(user).To(resource)
|
||||
|
||||
fmt.Println(resource.DisplayName)
|
||||
fmt.Println(resource.Email)
|
||||
}
|
||||
```
|
||||
|
||||
Looking for more information about the usage?
|
||||
|
||||
We wrote [an introduction article](https://github.com/ulule/deepcopier/blob/master/examples/rest-usage/README.rst).
|
||||
Have a look and feel free to give us your feedback.
|
||||
|
||||
## Contributing
|
||||
|
||||
* Ping us on twitter [@oibafsellig](https://twitter.com/oibafsellig), [@thoas](https://twitter.com/thoas)
|
||||
* Fork the [project](https://github.com/ulule/deepcopier)
|
||||
* Help us improving and fixing [issues](https://github.com/ulule/deepcopier/issues)
|
||||
|
||||
Don't hesitate ;)
|
350
vendor/github.com/ulule/deepcopier/deepcopier.go
generated
vendored
350
vendor/github.com/ulule/deepcopier/deepcopier.go
generated
vendored
|
@ -1,350 +0,0 @@
|
|||
package deepcopier
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// TagName is the deepcopier struct tag name.
|
||||
TagName = "deepcopier"
|
||||
// FieldOptionName is the from field option name for struct tag.
|
||||
FieldOptionName = "field"
|
||||
// ContextOptionName is the context option name for struct tag.
|
||||
ContextOptionName = "context"
|
||||
// SkipOptionName is the skip option name for struct tag.
|
||||
SkipOptionName = "skip"
|
||||
// ForceOptionName is the skip option name for struct tag.
|
||||
ForceOptionName = "force"
|
||||
)
|
||||
|
||||
type (
|
||||
// TagOptions is a map that contains extracted struct tag options.
|
||||
TagOptions map[string]string
|
||||
|
||||
// Options are copier options.
|
||||
Options struct {
|
||||
// Context given to WithContext() method.
|
||||
Context map[string]interface{}
|
||||
// Reversed reverses struct tag checkings.
|
||||
Reversed bool
|
||||
}
|
||||
)
|
||||
|
||||
// DeepCopier deep copies a struct to/from a struct.
|
||||
type DeepCopier struct {
|
||||
dst interface{}
|
||||
src interface{}
|
||||
ctx map[string]interface{}
|
||||
}
|
||||
|
||||
// Copy sets source or destination.
|
||||
func Copy(src interface{}) *DeepCopier {
|
||||
return &DeepCopier{src: src}
|
||||
}
|
||||
|
||||
// WithContext injects the given context into the builder instance.
|
||||
func (dc *DeepCopier) WithContext(ctx map[string]interface{}) *DeepCopier {
|
||||
dc.ctx = ctx
|
||||
return dc
|
||||
}
|
||||
|
||||
// To sets the destination.
|
||||
func (dc *DeepCopier) To(dst interface{}) error {
|
||||
dc.dst = dst
|
||||
return process(dc.dst, dc.src, Options{Context: dc.ctx})
|
||||
}
|
||||
|
||||
// From sets the given the source as destination and destination as source.
|
||||
func (dc *DeepCopier) From(src interface{}) error {
|
||||
dc.dst = dc.src
|
||||
dc.src = src
|
||||
return process(dc.dst, dc.src, Options{Context: dc.ctx, Reversed: true})
|
||||
}
|
||||
|
||||
// process handles copy.
|
||||
func process(dst interface{}, src interface{}, args ...Options) error {
|
||||
var (
|
||||
options = Options{}
|
||||
srcValue = reflect.Indirect(reflect.ValueOf(src))
|
||||
dstValue = reflect.Indirect(reflect.ValueOf(dst))
|
||||
srcFieldNames = getFieldNames(src)
|
||||
srcMethodNames = getMethodNames(src)
|
||||
)
|
||||
|
||||
if len(args) > 0 {
|
||||
options = args[0]
|
||||
}
|
||||
|
||||
if !dstValue.CanAddr() {
|
||||
return fmt.Errorf("destination %+v is unaddressable", dstValue.Interface())
|
||||
}
|
||||
|
||||
for _, f := range srcFieldNames {
|
||||
var (
|
||||
srcFieldValue = srcValue.FieldByName(f)
|
||||
srcFieldType, srcFieldFound = srcValue.Type().FieldByName(f)
|
||||
srcFieldName = srcFieldType.Name
|
||||
dstFieldName = srcFieldName
|
||||
tagOptions TagOptions
|
||||
)
|
||||
|
||||
if !srcFieldFound {
|
||||
continue
|
||||
}
|
||||
|
||||
if options.Reversed {
|
||||
tagOptions = getTagOptions(srcFieldType.Tag.Get(TagName))
|
||||
if v, ok := tagOptions[FieldOptionName]; ok && v != "" {
|
||||
dstFieldName = v
|
||||
}
|
||||
} else {
|
||||
if name, opts := getRelatedField(dst, srcFieldName); name != "" {
|
||||
dstFieldName, tagOptions = name, opts
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := tagOptions[SkipOptionName]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
dstFieldType, dstFieldFound = dstValue.Type().FieldByName(dstFieldName)
|
||||
dstFieldValue = dstValue.FieldByName(dstFieldName)
|
||||
)
|
||||
|
||||
if !dstFieldFound {
|
||||
continue
|
||||
}
|
||||
|
||||
// Force option for empty interfaces and nullable types
|
||||
_, force := tagOptions[ForceOptionName]
|
||||
|
||||
// Valuer -> ptr
|
||||
if isNullableType(srcFieldType.Type) && dstFieldValue.Kind() == reflect.Ptr && force {
|
||||
v, _ := srcFieldValue.Interface().(driver.Valuer).Value()
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
valueType := reflect.TypeOf(v)
|
||||
|
||||
ptr := reflect.New(valueType)
|
||||
ptr.Elem().Set(reflect.ValueOf(v))
|
||||
|
||||
if valueType.AssignableTo(dstFieldType.Type.Elem()) {
|
||||
dstFieldValue.Set(ptr)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Valuer -> value
|
||||
if isNullableType(srcFieldType.Type) {
|
||||
if force {
|
||||
v, _ := srcFieldValue.Interface().(driver.Valuer).Value()
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Type().AssignableTo(dstFieldType.Type) {
|
||||
dstFieldValue.Set(rv)
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if dstFieldValue.Kind() == reflect.Interface {
|
||||
if force {
|
||||
dstFieldValue.Set(srcFieldValue)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Ptr -> Value
|
||||
if srcFieldType.Type.Kind() == reflect.Ptr && !srcFieldValue.IsNil() && dstFieldType.Type.Kind() != reflect.Ptr {
|
||||
indirect := reflect.Indirect(srcFieldValue)
|
||||
|
||||
if indirect.Type().AssignableTo(dstFieldType.Type) {
|
||||
dstFieldValue.Set(indirect)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Other types
|
||||
if srcFieldType.Type.AssignableTo(dstFieldType.Type) {
|
||||
dstFieldValue.Set(srcFieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range srcMethodNames {
|
||||
name, opts := getRelatedField(dst, m)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := opts[SkipOptionName]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
method := reflect.ValueOf(src).MethodByName(m)
|
||||
if !method.IsValid() {
|
||||
return fmt.Errorf("method %s is invalid", m)
|
||||
}
|
||||
|
||||
var (
|
||||
dstFieldType, _ = dstValue.Type().FieldByName(name)
|
||||
dstFieldValue = dstValue.FieldByName(name)
|
||||
_, withContext = opts[ContextOptionName]
|
||||
_, force = opts[ForceOptionName]
|
||||
)
|
||||
|
||||
args := []reflect.Value{}
|
||||
if withContext {
|
||||
args = []reflect.Value{reflect.ValueOf(options.Context)}
|
||||
}
|
||||
|
||||
var (
|
||||
result = method.Call(args)[0]
|
||||
resultInterface = result.Interface()
|
||||
resultValue = reflect.ValueOf(resultInterface)
|
||||
resultType = resultValue.Type()
|
||||
)
|
||||
|
||||
// Value -> Ptr
|
||||
if dstFieldValue.Kind() == reflect.Ptr && force {
|
||||
ptr := reflect.New(resultType)
|
||||
ptr.Elem().Set(resultValue)
|
||||
|
||||
if ptr.Type().AssignableTo(dstFieldType.Type) {
|
||||
dstFieldValue.Set(ptr)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Ptr -> value
|
||||
if resultValue.Kind() == reflect.Ptr && force {
|
||||
if resultValue.Elem().Type().AssignableTo(dstFieldType.Type) {
|
||||
dstFieldValue.Set(resultValue.Elem())
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if resultType.AssignableTo(dstFieldType.Type) && result.IsValid() {
|
||||
dstFieldValue.Set(result)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTagOptions parses deepcopier tag field and returns options.
|
||||
func getTagOptions(value string) TagOptions {
|
||||
options := TagOptions{}
|
||||
|
||||
for _, opt := range strings.Split(value, ";") {
|
||||
o := strings.Split(opt, ":")
|
||||
|
||||
// deepcopier:"keyword; without; value;"
|
||||
if len(o) == 1 {
|
||||
options[o[0]] = ""
|
||||
}
|
||||
|
||||
// deepcopier:"key:value; anotherkey:anothervalue"
|
||||
if len(o) == 2 {
|
||||
options[strings.TrimSpace(o[0])] = strings.TrimSpace(o[1])
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
// getRelatedField returns first matching field.
|
||||
func getRelatedField(instance interface{}, name string) (string, TagOptions) {
|
||||
var (
|
||||
value = reflect.Indirect(reflect.ValueOf(instance))
|
||||
fieldName string
|
||||
tagOptions TagOptions
|
||||
)
|
||||
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
var (
|
||||
vField = value.Field(i)
|
||||
tField = value.Type().Field(i)
|
||||
tagOptions = getTagOptions(tField.Tag.Get(TagName))
|
||||
)
|
||||
|
||||
if tField.Type.Kind() == reflect.Struct && tField.Anonymous {
|
||||
if n, o := getRelatedField(vField.Interface(), name); n != "" {
|
||||
return n, o
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := tagOptions[FieldOptionName]; ok && v == name {
|
||||
return tField.Name, tagOptions
|
||||
}
|
||||
|
||||
if tField.Name == name {
|
||||
return tField.Name, tagOptions
|
||||
}
|
||||
}
|
||||
|
||||
return fieldName, tagOptions
|
||||
}
|
||||
|
||||
// getMethodNames returns instance's method names.
|
||||
func getMethodNames(instance interface{}) []string {
|
||||
var methods []string
|
||||
|
||||
t := reflect.TypeOf(instance)
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
methods = append(methods, t.Method(i).Name)
|
||||
}
|
||||
|
||||
return methods
|
||||
}
|
||||
|
||||
// getFieldNames returns instance's field names.
|
||||
func getFieldNames(instance interface{}) []string {
|
||||
var (
|
||||
fields []string
|
||||
v = reflect.Indirect(reflect.ValueOf(instance))
|
||||
t = v.Type()
|
||||
)
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
var (
|
||||
vField = v.Field(i)
|
||||
tField = v.Type().Field(i)
|
||||
)
|
||||
|
||||
// Is exportable?
|
||||
if tField.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if tField.Type.Kind() == reflect.Struct && tField.Anonymous {
|
||||
fields = append(fields, getFieldNames(vField.Interface())...)
|
||||
continue
|
||||
}
|
||||
|
||||
fields = append(fields, tField.Name)
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// isNullableType returns true if the given type is a nullable one.
|
||||
func isNullableType(t reflect.Type) bool {
|
||||
return t.ConvertibleTo(reflect.TypeOf((*driver.Valuer)(nil)).Elem())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue