slowing improving install process

This commit is contained in:
Brad Rydzewski 2015-08-06 15:53:39 -07:00
commit 9a70cb5ab4
67 changed files with 2306 additions and 7580 deletions

17
Godeps/Godeps.json generated
View file

@ -1,8 +1,8 @@
{
"ImportPath": "github.com/drone/drone",
"GoVersion": "go1.4.2",
"GoVersion": "go1.3.1",
"Packages": [
"github.com/drone/drone..."
"./..."
],
"Deps": [
{
@ -28,6 +28,11 @@
"Comment": "v2.2.0-16-gc48cfd5",
"Rev": "c48cfd5d9711c75acb6036d2698ef3aef7bb655a"
},
{
"ImportPath": "github.com/docker/docker/pkg/units",
"Comment": "v1.4.1-4375-g9356c76",
"Rev": "9356c76d9f6e285e71f04df33ef7870455a42775"
},
{
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
"Rev": "bea323321994103859d60197d229f1a94699dde3"
@ -76,12 +81,8 @@
"Rev": "542ae647f8601bafd96233961b150cae198e0295"
},
{
"ImportPath": "github.com/naoina/go-stringutil",
"Rev": "360db0db4b01d34e12a2ec042c09e7d37fece761"
},
{
"ImportPath": "github.com/naoina/toml",
"Rev": "7b2dffbeaee47506726f29e36d19cf4ee90d361b"
"ImportPath": "github.com/namsral/flag",
"Rev": "881a43080604bcf99ab1118a814d1cb2c268fc36"
},
{
"ImportPath": "github.com/russross/meddler",

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -1,7 +1,7 @@
package gogitlab
import (
"github.com/stretchr/testify/assert"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/assert"
"testing"
)

View file

@ -0,0 +1,31 @@
package units
import (
"fmt"
"time"
)
// HumanDuration returns a human-readable approximation of a duration
// (eg. "About a minute", "4 hours ago", etc.)
func HumanDuration(d time.Duration) string {
if seconds := int(d.Seconds()); seconds < 1 {
return "Less than a second"
} else if seconds < 60 {
return fmt.Sprintf("%d seconds", seconds)
} else if minutes := int(d.Minutes()); minutes == 1 {
return "About a minute"
} else if minutes < 60 {
return fmt.Sprintf("%d minutes", minutes)
} else if hours := int(d.Hours()); hours == 1 {
return "About an hour"
} else if hours < 48 {
return fmt.Sprintf("%d hours", hours)
} else if hours < 24*7*2 {
return fmt.Sprintf("%d days", hours/24)
} else if hours < 24*30*3 {
return fmt.Sprintf("%d weeks", hours/24/7)
} else if hours < 24*365*2 {
return fmt.Sprintf("%d months", hours/24/30)
}
return fmt.Sprintf("%d years", int(d.Hours())/24/365)
}

View file

@ -0,0 +1,46 @@
package units
import (
"testing"
"time"
)
func TestHumanDuration(t *testing.T) {
// Useful duration abstractions
day := 24 * time.Hour
week := 7 * day
month := 30 * day
year := 365 * day
assertEquals(t, "Less than a second", HumanDuration(450*time.Millisecond))
assertEquals(t, "47 seconds", HumanDuration(47*time.Second))
assertEquals(t, "About a minute", HumanDuration(1*time.Minute))
assertEquals(t, "3 minutes", HumanDuration(3*time.Minute))
assertEquals(t, "35 minutes", HumanDuration(35*time.Minute))
assertEquals(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second))
assertEquals(t, "About an hour", HumanDuration(1*time.Hour))
assertEquals(t, "About an hour", HumanDuration(1*time.Hour+45*time.Minute))
assertEquals(t, "3 hours", HumanDuration(3*time.Hour))
assertEquals(t, "3 hours", HumanDuration(3*time.Hour+59*time.Minute))
assertEquals(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute))
assertEquals(t, "24 hours", HumanDuration(24*time.Hour))
assertEquals(t, "36 hours", HumanDuration(1*day+12*time.Hour))
assertEquals(t, "2 days", HumanDuration(2*day))
assertEquals(t, "7 days", HumanDuration(7*day))
assertEquals(t, "13 days", HumanDuration(13*day+5*time.Hour))
assertEquals(t, "2 weeks", HumanDuration(2*week))
assertEquals(t, "2 weeks", HumanDuration(2*week+4*day))
assertEquals(t, "3 weeks", HumanDuration(3*week))
assertEquals(t, "4 weeks", HumanDuration(4*week))
assertEquals(t, "4 weeks", HumanDuration(4*week+3*day))
assertEquals(t, "4 weeks", HumanDuration(1*month))
assertEquals(t, "6 weeks", HumanDuration(1*month+2*week))
assertEquals(t, "8 weeks", HumanDuration(2*month))
assertEquals(t, "3 months", HumanDuration(3*month+1*week))
assertEquals(t, "5 months", HumanDuration(5*month+2*week))
assertEquals(t, "13 months", HumanDuration(13*month))
assertEquals(t, "23 months", HumanDuration(23*month))
assertEquals(t, "24 months", HumanDuration(24*month))
assertEquals(t, "2 years", HumanDuration(24*month+2*week))
assertEquals(t, "3 years", HumanDuration(3*year+2*month))
}

View file

@ -0,0 +1,93 @@
package units
import (
"fmt"
"regexp"
"strconv"
"strings"
)
// See: http://en.wikipedia.org/wiki/Binary_prefix
const (
// Decimal
KB = 1000
MB = 1000 * KB
GB = 1000 * MB
TB = 1000 * GB
PB = 1000 * TB
// Binary
KiB = 1024
MiB = 1024 * KiB
GiB = 1024 * MiB
TiB = 1024 * GiB
PiB = 1024 * TiB
)
type unitMap map[string]int64
var (
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
sizeRegex = regexp.MustCompile(`^(\d+)([kKmMgGtTpP])?[bB]?$`)
)
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
// CustomSize returns a human-readable approximation of a size
// using custom format
func CustomSize(format string, size float64, base float64, _map []string) string {
i := 0
for size >= base {
size = size / base
i++
}
return fmt.Sprintf(format, size, _map[i])
}
// HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB")
func HumanSize(size float64) string {
return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs)
}
func BytesSize(size float64) string {
return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs)
}
// FromHumanSize returns an integer from a human-readable specification of a
// size using SI standard (eg. "44kB", "17MB")
func FromHumanSize(size string) (int64, error) {
return parseSize(size, decimalMap)
}
// RAMInBytes parses a human-readable string representing an amount of RAM
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
// returns the number of bytes, or -1 if the string is unparseable.
// Units are case-insensitive, and the 'b' suffix is optional.
func RAMInBytes(size string) (int64, error) {
return parseSize(size, binaryMap)
}
// Parses the human-readable size string into the amount it represents
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
matches := sizeRegex.FindStringSubmatch(sizeStr)
if len(matches) != 3 {
return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
}
size, err := strconv.ParseInt(matches[1], 10, 0)
if err != nil {
return -1, err
}
unitPrefix := strings.ToLower(matches[2])
if mul, ok := uMap[unitPrefix]; ok {
size *= mul
}
return size, nil
}

View file

@ -0,0 +1,108 @@
package units
import (
"reflect"
"runtime"
"strings"
"testing"
)
func TestBytesSize(t *testing.T) {
assertEquals(t, "1 KiB", BytesSize(1024))
assertEquals(t, "1 MiB", BytesSize(1024*1024))
assertEquals(t, "1 MiB", BytesSize(1048576))
assertEquals(t, "2 MiB", BytesSize(2*MiB))
assertEquals(t, "3.42 GiB", BytesSize(3.42*GiB))
assertEquals(t, "5.372 TiB", BytesSize(5.372*TiB))
assertEquals(t, "2.22 PiB", BytesSize(2.22*PiB))
}
func TestHumanSize(t *testing.T) {
assertEquals(t, "1 kB", HumanSize(1000))
assertEquals(t, "1.024 kB", HumanSize(1024))
assertEquals(t, "1 MB", HumanSize(1000000))
assertEquals(t, "1.049 MB", HumanSize(1048576))
assertEquals(t, "2 MB", HumanSize(2*MB))
assertEquals(t, "3.42 GB", HumanSize(float64(3.42*GB)))
assertEquals(t, "5.372 TB", HumanSize(float64(5.372*TB)))
assertEquals(t, "2.22 PB", HumanSize(float64(2.22*PB)))
}
func TestFromHumanSize(t *testing.T) {
assertSuccessEquals(t, 32, FromHumanSize, "32")
assertSuccessEquals(t, 32, FromHumanSize, "32b")
assertSuccessEquals(t, 32, FromHumanSize, "32B")
assertSuccessEquals(t, 32*KB, FromHumanSize, "32k")
assertSuccessEquals(t, 32*KB, FromHumanSize, "32K")
assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb")
assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb")
assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb")
assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb")
assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb")
assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb")
assertError(t, FromHumanSize, "")
assertError(t, FromHumanSize, "hello")
assertError(t, FromHumanSize, "-32")
assertError(t, FromHumanSize, "32.3")
assertError(t, FromHumanSize, " 32 ")
assertError(t, FromHumanSize, "32.3Kb")
assertError(t, FromHumanSize, "32 mb")
assertError(t, FromHumanSize, "32m b")
assertError(t, FromHumanSize, "32bm")
}
func TestRAMInBytes(t *testing.T) {
assertSuccessEquals(t, 32, RAMInBytes, "32")
assertSuccessEquals(t, 32, RAMInBytes, "32b")
assertSuccessEquals(t, 32, RAMInBytes, "32B")
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k")
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K")
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb")
assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb")
assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb")
assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb")
assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb")
assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb")
assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB")
assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P")
assertError(t, RAMInBytes, "")
assertError(t, RAMInBytes, "hello")
assertError(t, RAMInBytes, "-32")
assertError(t, RAMInBytes, "32.3")
assertError(t, RAMInBytes, " 32 ")
assertError(t, RAMInBytes, "32.3Kb")
assertError(t, RAMInBytes, "32 mb")
assertError(t, RAMInBytes, "32m b")
assertError(t, RAMInBytes, "32bm")
}
func assertEquals(t *testing.T, expected, actual interface{}) {
if expected != actual {
t.Errorf("Expected '%v' but got '%v'", expected, actual)
}
}
// func that maps to the parse function signatures as testing abstraction
type parseFn func(string) (int64, error)
// Define 'String()' for pretty-print
func (fn parseFn) String() string {
fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
return fnName[strings.LastIndex(fnName, ".")+1:]
}
func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) {
res, err := fn(arg)
if err != nil || res != expected {
t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err)
}
}
func assertError(t *testing.T, fn parseFn, arg string) {
res, err := fn(arg)
if err == nil && res != -1 {
t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res)
}
}

View file

@ -1,7 +0,0 @@
# Guide to run Gin under App Engine LOCAL Development Server
1. Download, install and setup Go in your computer. (That includes setting your `$GOPATH`.)
2. Download SDK for your platform from here: `https://developers.google.com/appengine/downloads?hl=es#Google_App_Engine_SDK_for_Go`
3. Download Gin source code using: `$ go get github.com/gin-gonic/gin`
4. Navigate to examples folder: `$ cd $GOPATH/src/github.com/gin-gonic/gin/examples/`
5. Run it: `$ goapp serve app-engine/`

View file

@ -1,8 +0,0 @@
application: hello
version: 1
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app

View file

@ -1,23 +0,0 @@
package hello
import (
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
"net/http"
)
// This function's name is a must. App Engine uses it to drive the requests properly.
func init() {
// Starts a new Gin instance with no middle-ware
r := gin.New()
// Define your handlers
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello World!")
})
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
// Handle all requests using net/http
http.Handle("/", r)
}

View file

@ -1,56 +0,0 @@
package main
import (
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
)
var DB = make(map[string]string)
func main() {
r := gin.Default()
// Ping test
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
// Get user value
r.GET("/user/:name", func(c *gin.Context) {
user := c.Params.ByName("name")
value, ok := DB[user]
if ok {
c.JSON(200, gin.H{"user": user, "value": value})
} else {
c.JSON(200, gin.H{"user": user, "status": "no value"})
}
})
// Authorized group (uses gin.BasicAuth() middleware)
// Same than:
// authorized := r.Group("/")
// authorized.Use(gin.BasicAuth(gin.Credentials{
// "foo": "bar",
// "manu": "123",
//}))
authorized := r.Group("/", gin.BasicAuth(gin.Accounts{
"foo": "bar", // user:foo password:bar
"manu": "123", // user:manu password:123
}))
authorized.POST("admin", func(c *gin.Context) {
user := c.MustGet(gin.AuthUserKey).(string)
// Parse JSON
var json struct {
Value string `json:"value" binding:"required"`
}
if c.Bind(&json) {
DB[user] = json.Value
c.JSON(200, gin.H{"status": "ok"})
}
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}

View file

@ -1,39 +0,0 @@
package main
import (
"fmt"
"runtime"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
)
func main() {
ConfigRuntime()
StartWorkers()
StartGin()
}
func ConfigRuntime() {
nuCPU := runtime.NumCPU()
runtime.GOMAXPROCS(nuCPU)
fmt.Printf("Running with %d CPUs\n", nuCPU)
}
func StartWorkers() {
go statsWorker()
}
func StartGin() {
gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.Use(rateLimit, gin.Recovery())
router.LoadHTMLGlob("resources/*.templ.html")
router.Static("/static", "resources/static")
router.GET("/", index)
router.GET("/room/:roomid", roomGET)
router.POST("/room-post/:roomid", roomPOST)
router.GET("/stream/:roomid", streamRoom)
router.Run(":80")
}

View file

@ -1,208 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Server-Sent Events. Room "{{.roomid}}"</title>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="http://malsup.github.com/jquery.form.js"></script>
<!-- EPOCH -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="/static/epoch.min.js"></script>
<link rel="stylesheet" href="/static/epoch.min.css">
<script src="/static/realtime.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<!-- Primjs -->
<link href="/static/prismjs.min.css" rel="stylesheet" />
<script type="text/javascript">
$(document).ready(function() {
StartRealtime({{.roomid}}, {{.timestamp}});
});
</script>
<style>
body { padding-top: 50px; }
</style>
</head>
<body>
<nav class="navbar navbar-fixed-top navbar-inverse">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Server-Sent Events</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Demo</a></li>
<li><a href="http://www.w3.org/TR/2009/WD-eventsource-20091029/">W3 Standard</a></li>
<li><a href="http://caniuse.com/#feat=eventsource">Browser Support</a></li>
<li><a href="http://gin-gonic.github.io/gin/">Gin Framework</a></li>
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">Github</a></li>
</ul>
</div><!-- /.nav-collapse -->
</div><!-- /.container -->
</nav><!-- /.navbar -->
<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
<div class="container">
<h1>Server-Sent Events in Go</h1>
<p>Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. It is not websockets. <a href="http://www.html5rocks.com/en/tutorials/eventsource/basics/">Learn more.</a></p>
<p>The chat and the charts data is provided in realtime using the SSE implemention of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p>
<div class="row">
<div class="col-md-8">
<div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:290px">
<table id="table-style" class="table" data-show-header="false">
<thead>
<tr>
<th data-field="nick" class="col-md-2">Nick</th>
<th data-field="message" class="col-md-8">Message</th>
</tr>
</thead>
<tbody id="chat"></tbody>
</table>
</div>
{{if .nick}}
<form autocomplete="off" class="form-inline" id="chat-form" action="/room-post/{{.roomid}}?nick={{.nick}}" method="post">
<div class="form-group">
<label class="sr-only" for="chat-message">Message</label>
<div class="input-group">
<div class="input-group-addon">{{.nick}}</div>
<input type="text" name="message" id="chat-message" class="form-control" placeholder="a message" value="" />
</div>
</div>
<input type="submit" class="btn btn-primary" value="Send" />
</form>
{{else}}
<form action="" method="get" class="form-inline">
<legend>Join the SSE real-time chat</legend>
<div class="form-group">
<input value='' name="nick" id="nick" placeholder="Your Name" type="text" class="form-control" />
</div>
<div class="form-group text-center">
<input type="submit" class="btn btn-success btn-login-submit" value="Join" />
</div>
</form>
{{end}}
</div>
<div class="col-md-4">
<div id="messagesChart" class="epoch category10"></div>
<p>
<span style="font-size:20px; color:#1f77b4">◼︎</span> Users<br>
<span style="font-size:20px; color:#ff7f0e">◼︎</span> Inbound messages / sec<br>
<span style="font-size:20px; color:#2ca02c">◼︎</span> Outbound messages / sec<br>
</p>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<h2>Realtime server Go stats</h2>
<div class="col-md-6">
<h3>Memory usage</h3>
<p>
<div id="heapChart" class="epoch category20c"></div>
</p>
<p>
<span style="font-size:20px; color:#1f77b4">◼︎</span> Heap bytes<br>
<span style="font-size:20px; color:#aec7e8">◼︎</span> Stack bytes<br>
</p>
</div>
<div class="col-md-6">
<h3>Allocations per second</h3>
<p>
<div id="mallocsChart" class="epoch category20b"></div>
</p>
<p>
<span style="font-size:20px; color:#393b79">◼︎</span> Mallocs / sec<br>
<span style="font-size:20px; color:#5254a3">◼︎</span> Frees / sec<br>
</p>
</div>
</div>
<div class="row">
<h2>MIT Open Sourced</h2>
<ul>
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">This demo website (JS and Go)</a></li>
<li><a href="https://github.com/manucorporat/sse">The SSE implementation in Go</a></li>
<li><a href="https://github.com/gin-gonic/gin">The Web Framework (Gin)</a></li>
</ul>
<div class="col-md-6">
<script src="/static/prismjs.min.js"></script>
<h3>Server-side (Go)</h3>
<pre><code class="language-go">func streamRoom(c *gin.Context) {
roomid := c.ParamValue(&quot;roomid&quot;)
listener := openListener(roomid)
statsTicker := time.NewTicker(1 * time.Second)
defer closeListener(roomid, listener)
defer statsTicker.Stop()
c.Stream(func(w io.Writer) bool {
select {
case msg := &lt;-listener:
c.SSEvent(&quot;message&quot;, msg)
case &lt;-statsTicker.C:
c.SSEvent(&quot;stats&quot;, Stats())
}
return true
})
}</code></pre>
</div>
<div class="col-md-6">
<h3>Client-side (JS)</h3>
<pre><code class="language-javascript">function StartSSE(roomid) {
var source = new EventSource('/stream/'+roomid);
source.addEventListener('message', newChatMessage, false);
source.addEventListener('stats', stats, false);
}</code></pre>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>SSE package</h3>
<pre><code class="language-go">import &quot;github.com/manucorporat/sse&quot;
func httpHandler(w http.ResponseWriter, req *http.Request) {
// data can be a primitive like a string, an integer or a float
sse.Encode(w, sse.Event{
Event: &quot;message&quot;,
Data: &quot;some data\nmore data&quot;,
})
// also a complex type, like a map, a struct or a slice
sse.Encode(w, sse.Event{
Id: &quot;124&quot;,
Event: &quot;message&quot;,
Data: map[string]interface{}{
&quot;user&quot;: &quot;manu&quot;,
&quot;date&quot;: time.Now().Unix(),
&quot;content&quot;: &quot;hi!&quot;,
},
})
}</code></pre>
<pre>event: message
data: some data\\nmore data
id: 124
event: message
data: {&quot;content&quot;:&quot;hi!&quot;,&quot;date&quot;:1431540810,&quot;user&quot;:&quot;manu&quot;}</pre>
</div>
</div>
<hr>
<footer>
<p>Created with <span class="glyphicon glyphicon-heart"></span> by <a href="https://github.com/manucorporat">Manu Martinez-Almeida</a></p>
</footer>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -1,137 +0,0 @@
/* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+go */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View file

@ -1,144 +0,0 @@
function StartRealtime(roomid, timestamp) {
StartEpoch(timestamp);
StartSSE(roomid);
StartForm();
}
function StartForm() {
$('#chat-message').focus();
$('#chat-form').ajaxForm(function() {
$('#chat-message').val('');
$('#chat-message').focus();
});
}
function StartEpoch(timestamp) {
var windowSize = 60;
var height = 200;
var defaultData = histogram(windowSize, timestamp);
window.heapChart = $('#heapChart').epoch({
type: 'time.area',
axes: ['bottom', 'left'],
height: height,
historySize: 10,
data: [
{values: defaultData},
{values: defaultData}
]
});
window.mallocsChart = $('#mallocsChart').epoch({
type: 'time.area',
axes: ['bottom', 'left'],
height: height,
historySize: 10,
data: [
{values: defaultData},
{values: defaultData}
]
});
window.messagesChart = $('#messagesChart').epoch({
type: 'time.line',
axes: ['bottom', 'left'],
height: 240,
historySize: 10,
data: [
{values: defaultData},
{values: defaultData},
{values: defaultData}
]
});
}
function StartSSE(roomid) {
if (!window.EventSource) {
alert("EventSource is not enabled in this browser");
return;
}
var source = new EventSource('/stream/'+roomid);
source.addEventListener('message', newChatMessage, false);
source.addEventListener('stats', stats, false);
}
function stats(e) {
var data = parseJSONStats(e.data);
heapChart.push(data.heap);
mallocsChart.push(data.mallocs);
messagesChart.push(data.messages);
}
function parseJSONStats(e) {
var data = jQuery.parseJSON(e);
var timestamp = data.timestamp;
var heap = [
{time: timestamp, y: data.HeapInuse},
{time: timestamp, y: data.StackInuse}
];
var mallocs = [
{time: timestamp, y: data.Mallocs},
{time: timestamp, y: data.Frees}
];
var messages = [
{time: timestamp, y: data.Connected},
{time: timestamp, y: data.Inbound},
{time: timestamp, y: data.Outbound}
];
return {
heap: heap,
mallocs: mallocs,
messages: messages
}
}
function newChatMessage(e) {
var data = jQuery.parseJSON(e.data);
var nick = data.nick;
var message = data.message;
var style = rowStyle(nick);
var html = "<tr class=\""+style+"\"><td>"+nick+"</td><td>"+message+"</td></tr>";
$('#chat').append(html);
$("#chat-scroll").scrollTop($("#chat-scroll")[0].scrollHeight);
}
function histogram(windowSize, timestamp) {
var entries = new Array(windowSize);
for(var i = 0; i < windowSize; i++) {
entries[i] = {time: (timestamp-windowSize+i-1), y:0};
}
return entries;
}
var entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};
function rowStyle(nick) {
var classes = ['active', 'success', 'info', 'warning', 'danger'];
var index = hashCode(nick)%5;
return classes[index];
}
function hashCode(s){
return Math.abs(s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0));
}
function escapeHtml(string) {
return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}
window.StartRealtime = StartRealtime

View file

@ -1,25 +0,0 @@
package main
import "github.com/dustin/go-broadcast"
var roomChannels = make(map[string]broadcast.Broadcaster)
func openListener(roomid string) chan interface{} {
listener := make(chan interface{})
room(roomid).Register(listener)
return listener
}
func closeListener(roomid string, listener chan interface{}) {
room(roomid).Unregister(listener)
close(listener)
}
func room(roomid string) broadcast.Broadcaster {
b, ok := roomChannels[roomid]
if !ok {
b = broadcast.NewBroadcaster(10)
roomChannels[roomid] = b
}
return b
}

View file

@ -1,96 +0,0 @@
package main
import (
"fmt"
"html"
"io"
"strings"
"time"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
)
func rateLimit(c *gin.Context) {
ip := c.ClientIP()
value := int(ips.Add(ip, 1))
if value%50 == 0 {
fmt.Printf("ip: %s, count: %d\n", ip, value)
}
if value >= 200 {
if value%200 == 0 {
fmt.Println("ip blocked")
}
c.Abort()
c.String(503, "you were automatically banned :)")
}
}
func index(c *gin.Context) {
c.Redirect(301, "/room/hn")
}
func roomGET(c *gin.Context) {
roomid := c.ParamValue("roomid")
nick := c.FormValue("nick")
if len(nick) < 2 {
nick = ""
}
if len(nick) > 13 {
nick = nick[0:12] + "..."
}
c.HTML(200, "room_login.templ.html", gin.H{
"roomid": roomid,
"nick": nick,
"timestamp": time.Now().Unix(),
})
}
func roomPOST(c *gin.Context) {
roomid := c.ParamValue("roomid")
nick := c.FormValue("nick")
message := c.PostFormValue("message")
message = strings.TrimSpace(message)
validMessage := len(message) > 1 && len(message) < 200
validNick := len(nick) > 1 && len(nick) < 14
if !validMessage || !validNick {
c.JSON(400, gin.H{
"status": "failed",
"error": "the message or nickname is too long",
})
return
}
post := gin.H{
"nick": html.EscapeString(nick),
"message": html.EscapeString(message),
}
messages.Add("inbound", 1)
room(roomid).Submit(post)
c.JSON(200, post)
}
func streamRoom(c *gin.Context) {
roomid := c.ParamValue("roomid")
listener := openListener(roomid)
ticker := time.NewTicker(1 * time.Second)
users.Add("connected", 1)
defer func() {
closeListener(roomid, listener)
ticker.Stop()
users.Add("disconnected", 1)
}()
c.Stream(func(w io.Writer) bool {
select {
case msg := <-listener:
messages.Add("outbound", 1)
c.SSEvent("message", msg)
case <-ticker.C:
c.SSEvent("stats", Stats())
}
return true
})
}

View file

@ -1,56 +0,0 @@
package main
import (
"runtime"
"sync"
"time"
"github.com/manucorporat/stats"
)
var ips = stats.New()
var messages = stats.New()
var users = stats.New()
var mutexStats sync.RWMutex
var savedStats map[string]uint64
func statsWorker() {
c := time.Tick(1 * time.Second)
var lastMallocs uint64 = 0
var lastFrees uint64 = 0
for range c {
var stats runtime.MemStats
runtime.ReadMemStats(&stats)
mutexStats.Lock()
savedStats = map[string]uint64{
"timestamp": uint64(time.Now().Unix()),
"HeapInuse": stats.HeapInuse,
"StackInuse": stats.StackInuse,
"Mallocs": (stats.Mallocs - lastMallocs),
"Frees": (stats.Frees - lastFrees),
"Inbound": uint64(messages.Get("inbound")),
"Outbound": uint64(messages.Get("outbound")),
"Connected": connectedUsers(),
}
lastMallocs = stats.Mallocs
lastFrees = stats.Frees
messages.Reset()
mutexStats.Unlock()
}
}
func connectedUsers() uint64 {
connected := users.Get("connected") - users.Get("disconnected")
if connected < 0 {
return 0
}
return uint64(connected)
}
func Stats() map[string]uint64 {
mutexStats.RLock()
defer mutexStats.RUnlock()
return savedStats
}

View file

@ -1,58 +0,0 @@
package main
import (
"fmt"
"io"
"math/rand"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.SetHTMLTemplate(html)
router.GET("/room/:roomid", roomGET)
router.POST("/room/:roomid", roomPOST)
router.DELETE("/room/:roomid", roomDELETE)
router.GET("/stream/:roomid", stream)
router.Run(":8080")
}
func stream(c *gin.Context) {
roomid := c.ParamValue("roomid")
listener := openListener(roomid)
defer closeListener(roomid, listener)
c.Stream(func(w io.Writer) bool {
c.SSEvent("message", <-listener)
return true
})
}
func roomGET(c *gin.Context) {
roomid := c.ParamValue("roomid")
userid := fmt.Sprint(rand.Int31())
c.HTML(200, "chat_room", gin.H{
"roomid": roomid,
"userid": userid,
})
}
func roomPOST(c *gin.Context) {
roomid := c.ParamValue("roomid")
userid := c.PostFormValue("user")
message := c.PostFormValue("message")
room(roomid).Submit(userid + ": " + message)
c.JSON(200, gin.H{
"status": "success",
"message": message,
})
}
func roomDELETE(c *gin.Context) {
roomid := c.ParamValue("roomid")
deleteBroadcast(roomid)
}

View file

@ -1,33 +0,0 @@
package main
import "github.com/dustin/go-broadcast"
var roomChannels = make(map[string]broadcast.Broadcaster)
func openListener(roomid string) chan interface{} {
listener := make(chan interface{})
room(roomid).Register(listener)
return listener
}
func closeListener(roomid string, listener chan interface{}) {
room(roomid).Unregister(listener)
close(listener)
}
func deleteBroadcast(roomid string) {
b, ok := roomChannels[roomid]
if ok {
b.Close()
delete(roomChannels, roomid)
}
}
func room(roomid string) broadcast.Broadcaster {
b, ok := roomChannels[roomid]
if !ok {
b = broadcast.NewBroadcaster(10)
roomChannels[roomid] = b
}
return b
}

View file

@ -1,44 +0,0 @@
package main
import "html/template"
var html = template.Must(template.New("chat_room").Parse(`
<html>
<head>
<title>{{.roomid}}</title>
<link rel="stylesheet" type="text/css" href="http://meyerweb.com/eric/tools/css/reset/reset.css"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
<script src="http://malsup.github.com/jquery.form.js"></script>
<script>
$('#message_form').focus();
$(document).ready(function() {
// bind 'myForm' and provide a simple callback function
$('#myForm').ajaxForm(function() {
$('#message_form').val('');
$('#message_form').focus();
});
if (!!window.EventSource) {
var source = new EventSource('/stream/{{.roomid}}');
source.addEventListener('message', function(e) {
$('#messages').append(e.data + "</br>");
$('html, body').animate({scrollTop:$(document).height()}, 'slow');
}, false);
} else {
alert("NOT SUPPORTED");
}
});
</script>
</head>
<body>
<h1>Welcome to {{.roomid}} room</h1>
<div id="messages"></div>
<form id="myForm" action="/room/{{.roomid}}" method="post">
User: <input id="user_form" name="user" value="{{.userid}}"></input>
Message: <input id="message_form" name="message"></input>
<input type="submit" value="Submit" />
</form>
</body>
</html>
`))

27
Godeps/_workspace/src/github.com/namsral/flag/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

184
Godeps/_workspace/src/github.com/namsral/flag/README.md generated vendored Normal file
View file

@ -0,0 +1,184 @@
Flag
===
Flag is a drop in replacement for Go's flag package with the addition to parse files and environment variables. If you support the [twelve-factor app methodology][], Flag complies with the third factor; "Store config in the environment".
[twelve-factor app methodology]: http://12factor.net
An example using a gopher:
```go
$ cat > gopher.go
package main
import (
"fmt"
"github.com/namsral/flag"
)
var age int
flag.IntVar(&age, "age", 0, "age of gopher")
flag.Parse()
fmt.Print("age:", age)
$ go run gopher.go -age 1
age: 1
```
Same code but using an environment variable:
```go
$ export AGE=2
$ go run gopher.go
age: 2
```
Same code but using a configuration file:
```go
$ cat > gopher.conf
age 3
$ go run gopher.go -config gopher.conf
age: 3
```
The following table shows how flags are translated to environment variables and configuration files:
| Type | Flag | Environment | File |
| ------ | :------------ |:------------ |:------------ |
| int | -age 2 | AGE=2 | age 2 |
| bool | -female | FEMALE=true | female true |
| float | -length 175.5 | LENGTH=175.5 | length 175.5 |
| string | -name Gloria | NAME=Gloria | name Gloria |
This package is a port of Go's [flag][] package from the standard library with the addition of two functions `ParseEnv` and `ParseFile`.
[flag]: http://golang.org/src/pkg/flagconfiguration
Why?
---
Why not use one of the many INI, JSON or YAML parsers?
I find it best practice to have simple configuration options to control the behaviour of an applications when it starts up. Use basic types like ints, floats and strings for configuration options and store more complex data structures in the "datastore" layer.
Usage
---
It's intended for projects which require a simple configuration made available through command-line flags, configuration files and shell environments. It's similar to the original `flag` package.
Example:
```go
import "github/namsral/flag"
flag.String("config", "", "help message for config")
flag.Int("age", 24, "help message for age")
flag.Parse()
```
Order of precedence:
1. Command line options
2. Environment variables
3. Configuration file
4. Default values
#### Parsing Configuration Files
Create a configuration file:
```go
$ cat > ./gopher.conf
# empty newlines and lines beginning with a "#" character are ignored.
name bob
# keys and values can also be separated by the "=" character
age=20
# booleans can be empty, set with 0, 1, true, false, etc
hacker
```
Add a "config" flag:
```go
flag.String("config", "", "help message for config")
```
Run the command:
```go
$ go run ./gopher.go -config ./gopher.conf
```
#### Parsing Environment Variables
Environment variables are parsed 1-on-1 with defined flags:
```go
$ export AGE=44
$ go run ./gopher.go
age=44
```
You can also parse prefixed environment variables by setting a prefix name when creating a new empty flag set:
```go
fs := flag.NewFlagSetWithEnvPrefix(os.Args[0], "GO", 0)
fs.Int("age", 24, "help message for age")
fs.Parse(os.Args[1:])
...
$ go export GO_AGE=33
$ go run ./gopher.go
age=33
```
For more examples see the [examples][] directory in the project repository.
[examples]: https://github.com/namsral/flag/tree/master/examples
That's it.
License
---
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,82 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// These examples demonstrate more intricate uses of the flag package.
package flag
import (
"errors"
"fmt"
"strings"
"time"
)
// Example 1: A single string flag called "species" with default value "gopher".
var species = String("species", "gopher", "the species we are studying")
// Example 2: Two flags sharing a variable, so we can have a shorthand.
// The order of initialization is undefined, so make sure both use the
// same default value. They must be set up with an init function.
var gopherType string
func init() {
const (
defaultGopher = "pocket"
usage = "the variety of gopher"
)
StringVar(&gopherType, "gopher_type", defaultGopher, usage)
StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
}
// Example 3: A user-defined flag type, a slice of durations.
type interval []time.Duration
// String is the method to format the flag's value, part of the Value interface.
// The String method's output will be used in diagnostics.
func (i *interval) String() string {
return fmt.Sprint(*i)
}
// Set is the method to set the flag value, part of the Value interface.
// Set's argument is a string to be parsed to set the
// It's a comma-separated list, so we split it.
func (i *interval) Set(value string) error {
// If we wanted to allow the flag to be set multiple times,
// accumulating values, we would delete this if statement.
// That would permit usages such as
// -deltaT 10s -deltaT 15s
// and other combinations.
if len(*i) > 0 {
return errors.New("interval flag already set")
}
for _, dt := range strings.Split(value, ",") {
duration, err := time.ParseDuration(dt)
if err != nil {
return err
}
*i = append(*i, duration)
}
return nil
}
// Define a flag to accumulate durations. Because it has a special type,
// we need to use the Var function and therefore create the flag during
// init.
var intervalFlag interval
func init() {
// Tie the command-line flag to the intervalFlag variable and
// set a usage message.
Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
}
func Example() {
// All the interesting pieces are with the variables declared above, but
// to enable the flag package to see the flags defined there, one must
// execute, typically at the start of main (not init!):
// flag.Parse()
// We don't run it here because this is not a main function and
// the testing suite has already parsed the flags.
}

View file

@ -0,0 +1,5 @@
# this is a comment followed by an empty line
length 175.5
age 2
name Gloria

View file

@ -0,0 +1,29 @@
package main
import (
"github.com/drone/drone/Godeps/_workspace/src/github.com/namsral/flag"
"fmt"
)
func main() {
var (
config string
length float64
age int
name string
female bool
)
flag.StringVar(&config, "config", "", "help message")
flag.StringVar(&name, "name", "", "help message")
flag.IntVar(&age, "age", 0, "help message")
flag.Float64Var(&length, "length", 0, "help message")
flag.BoolVar(&female, "female", false, "help message")
flag.Parse()
fmt.Println("length:", length)
fmt.Println("age:", age)
fmt.Println("name:", name)
fmt.Println("female:", female)
}

View file

@ -0,0 +1,17 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import "os"
// Additional routines compiled into the package only during testing.
// ResetForTesting clears all flag state and sets the usage function as directed.
// After calling ResetForTesting, parse errors in flag handling will not
// exit the program.
func ResetForTesting(usage func()) {
CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
Usage = usage
}

1040
Godeps/_workspace/src/github.com/namsral/flag/flag.go generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,475 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flag
import (
"bytes"
"fmt"
"os"
"sort"
"strings"
"syscall"
"testing"
"time"
)
func boolString(s string) string {
if s == "0" {
return "false"
}
return "true"
}
func TestEverything(t *testing.T) {
ResetForTesting(nil)
Bool("test_bool", false, "bool value")
Int("test_int", 0, "int value")
Int64("test_int64", 0, "int64 value")
Uint("test_uint", 0, "uint value")
Uint64("test_uint64", 0, "uint64 value")
String("test_string", "0", "string value")
Float64("test_float64", 0, "float64 value")
Duration("test_duration", 0, "time.Duration value")
m := make(map[string]*Flag)
desired := "0"
visitor := func(f *Flag) {
if len(f.Name) > 5 && f.Name[0:5] == "test_" {
m[f.Name] = f
ok := false
switch {
case f.Value.String() == desired:
ok = true
case f.Name == "test_bool" && f.Value.String() == boolString(desired):
ok = true
case f.Name == "test_duration" && f.Value.String() == desired+"s":
ok = true
}
if !ok {
t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
}
}
}
VisitAll(visitor)
if len(m) != 8 {
t.Error("VisitAll misses some flags")
for k, v := range m {
t.Log(k, *v)
}
}
m = make(map[string]*Flag)
Visit(visitor)
if len(m) != 0 {
t.Errorf("Visit sees unset flags")
for k, v := range m {
t.Log(k, *v)
}
}
// Now set all flags
Set("test_bool", "true")
Set("test_int", "1")
Set("test_int64", "1")
Set("test_uint", "1")
Set("test_uint64", "1")
Set("test_string", "1")
Set("test_float64", "1")
Set("test_duration", "1s")
desired = "1"
Visit(visitor)
if len(m) != 8 {
t.Error("Visit fails after set")
for k, v := range m {
t.Log(k, *v)
}
}
// Now test they're visited in sort order.
var flagNames []string
Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
if !sort.StringsAreSorted(flagNames) {
t.Errorf("flag names not sorted: %v", flagNames)
}
}
func TestGet(t *testing.T) {
ResetForTesting(nil)
Bool("test_bool", true, "bool value")
Int("test_int", 1, "int value")
Int64("test_int64", 2, "int64 value")
Uint("test_uint", 3, "uint value")
Uint64("test_uint64", 4, "uint64 value")
String("test_string", "5", "string value")
Float64("test_float64", 6, "float64 value")
Duration("test_duration", 7, "time.Duration value")
visitor := func(f *Flag) {
if len(f.Name) > 5 && f.Name[0:5] == "test_" {
g, ok := f.Value.(Getter)
if !ok {
t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
return
}
switch f.Name {
case "test_bool":
ok = g.Get() == true
case "test_int":
ok = g.Get() == int(1)
case "test_int64":
ok = g.Get() == int64(2)
case "test_uint":
ok = g.Get() == uint(3)
case "test_uint64":
ok = g.Get() == uint64(4)
case "test_string":
ok = g.Get() == "5"
case "test_float64":
ok = g.Get() == float64(6)
case "test_duration":
ok = g.Get() == time.Duration(7)
}
if !ok {
t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name)
}
}
}
VisitAll(visitor)
}
func TestUsage(t *testing.T) {
called := false
ResetForTesting(func() { called = true })
if CommandLine.Parse([]string{"-x"}) == nil {
t.Error("parse did not fail for unknown flag")
}
if !called {
t.Error("did not call Usage for unknown flag")
}
}
func testParse(f *FlagSet, t *testing.T) {
if f.Parsed() {
t.Error("f.Parse() = true before Parse")
}
boolFlag := f.Bool("bool", false, "bool value")
bool2Flag := f.Bool("bool2", false, "bool2 value")
intFlag := f.Int("int", 0, "int value")
int64Flag := f.Int64("int64", 0, "int64 value")
uintFlag := f.Uint("uint", 0, "uint value")
uint64Flag := f.Uint64("uint64", 0, "uint64 value")
stringFlag := f.String("string", "0", "string value")
float64Flag := f.Float64("float64", 0, "float64 value")
durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
extra := "one-extra-argument"
args := []string{
"-bool",
"-bool2=true",
"--int", "22",
"--int64", "0x23",
"-uint", "24",
"--uint64", "25",
"-string", "hello",
"-float64", "2718e28",
"-duration", "2m",
extra,
}
if err := f.Parse(args); err != nil {
t.Fatal(err)
}
if !f.Parsed() {
t.Error("f.Parse() = false after Parse")
}
if *boolFlag != true {
t.Error("bool flag should be true, is ", *boolFlag)
}
if *bool2Flag != true {
t.Error("bool2 flag should be true, is ", *bool2Flag)
}
if *intFlag != 22 {
t.Error("int flag should be 22, is ", *intFlag)
}
if *int64Flag != 0x23 {
t.Error("int64 flag should be 0x23, is ", *int64Flag)
}
if *uintFlag != 24 {
t.Error("uint flag should be 24, is ", *uintFlag)
}
if *uint64Flag != 25 {
t.Error("uint64 flag should be 25, is ", *uint64Flag)
}
if *stringFlag != "hello" {
t.Error("string flag should be `hello`, is ", *stringFlag)
}
if *float64Flag != 2718e28 {
t.Error("float64 flag should be 2718e28, is ", *float64Flag)
}
if *durationFlag != 2*time.Minute {
t.Error("duration flag should be 2m, is ", *durationFlag)
}
if len(f.Args()) != 1 {
t.Error("expected one argument, got", len(f.Args()))
} else if f.Args()[0] != extra {
t.Errorf("expected argument %q got %q", extra, f.Args()[0])
}
}
func TestParse(t *testing.T) {
ResetForTesting(func() { t.Error("bad parse") })
testParse(CommandLine, t)
}
func TestFlagSetParse(t *testing.T) {
testParse(NewFlagSet("test", ContinueOnError), t)
}
// Declare a user-defined flag type.
type flagVar []string
func (f *flagVar) String() string {
return fmt.Sprint([]string(*f))
}
func (f *flagVar) Set(value string) error {
*f = append(*f, value)
return nil
}
func TestUserDefined(t *testing.T) {
var flags FlagSet
flags.Init("test", ContinueOnError)
var v flagVar
flags.Var(&v, "v", "usage")
if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
t.Error(err)
}
if len(v) != 3 {
t.Fatal("expected 3 args; got ", len(v))
}
expect := "[1 2 3]"
if v.String() != expect {
t.Errorf("expected value %q got %q", expect, v.String())
}
}
// Declare a user-defined boolean flag type.
type boolFlagVar struct {
count int
}
func (b *boolFlagVar) String() string {
return fmt.Sprintf("%d", b.count)
}
func (b *boolFlagVar) Set(value string) error {
if value == "true" {
b.count++
}
return nil
}
func (b *boolFlagVar) IsBoolFlag() bool {
return b.count < 4
}
func TestUserDefinedBool(t *testing.T) {
var flags FlagSet
flags.Init("test", ContinueOnError)
var b boolFlagVar
var err error
flags.Var(&b, "b", "usage")
if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
if b.count < 4 {
t.Error(err)
}
}
if b.count != 4 {
t.Errorf("want: %d; got: %d", 4, b.count)
}
if err == nil {
t.Error("expected error; got none")
}
}
func TestSetOutput(t *testing.T) {
var flags FlagSet
var buf bytes.Buffer
flags.SetOutput(&buf)
flags.Init("test", ContinueOnError)
flags.Parse([]string{"-unknown"})
if out := buf.String(); !strings.Contains(out, "-unknown") {
t.Logf("expected output mentioning unknown; got %q", out)
}
}
// This tests that one can reset the flags. This still works but not well, and is
// superseded by FlagSet.
func TestChangingArgs(t *testing.T) {
ResetForTesting(func() { t.Fatal("bad parse") })
oldArgs := os.Args
defer func() { os.Args = oldArgs }()
os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
before := Bool("before", false, "")
if err := CommandLine.Parse(os.Args[1:]); err != nil {
t.Fatal(err)
}
cmd := Arg(0)
os.Args = Args()
after := Bool("after", false, "")
Parse()
args := Args()
if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
}
}
// Test that -help invokes the usage message and returns ErrHelp.
func TestHelp(t *testing.T) {
var helpCalled = false
fs := NewFlagSet("help test", ContinueOnError)
fs.Usage = func() { helpCalled = true }
var flag bool
fs.BoolVar(&flag, "flag", false, "regular flag")
// Regular flag invocation should work
err := fs.Parse([]string{"-flag=true"})
if err != nil {
t.Fatal("expected no error; got ", err)
}
if !flag {
t.Error("flag was not set by -flag")
}
if helpCalled {
t.Error("help called for regular flag")
helpCalled = false // reset for next test
}
// Help flag should work as expected.
err = fs.Parse([]string{"-help"})
if err == nil {
t.Fatal("error expected")
}
if err != ErrHelp {
t.Fatal("expected ErrHelp; got ", err)
}
if !helpCalled {
t.Fatal("help was not called")
}
// If we define a help flag, that should override.
var help bool
fs.BoolVar(&help, "help", false, "help flag")
helpCalled = false
err = fs.Parse([]string{"-help"})
if err != nil {
t.Fatal("expected no error for defined -help; got ", err)
}
if helpCalled {
t.Fatal("help was called; should not have been for defined help flag")
}
}
// Test parsing a environment variables
func TestParseEnv(t *testing.T) {
syscall.Setenv("BOOL", "")
syscall.Setenv("BOOL2", "true")
syscall.Setenv("INT", "22")
syscall.Setenv("INT64", "0x23")
syscall.Setenv("UINT", "24")
syscall.Setenv("UINT64", "25")
syscall.Setenv("STRING", "hello")
syscall.Setenv("FLOAT64", "2718e28")
syscall.Setenv("DURATION", "2m")
f := NewFlagSet(os.Args[0], ContinueOnError)
boolFlag := f.Bool("bool", false, "bool value")
bool2Flag := f.Bool("bool2", false, "bool2 value")
intFlag := f.Int("int", 0, "int value")
int64Flag := f.Int64("int64", 0, "int64 value")
uintFlag := f.Uint("uint", 0, "uint value")
uint64Flag := f.Uint64("uint64", 0, "uint64 value")
stringFlag := f.String("string", "0", "string value")
float64Flag := f.Float64("float64", 0, "float64 value")
durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
err := f.ParseEnv(os.Environ())
if err != nil {
t.Fatal("expected no error; got ", err)
}
if *boolFlag != true {
t.Error("bool flag should be true, is ", *boolFlag)
}
if *bool2Flag != true {
t.Error("bool2 flag should be true, is ", *bool2Flag)
}
if *intFlag != 22 {
t.Error("int flag should be 22, is ", *intFlag)
}
if *int64Flag != 0x23 {
t.Error("int64 flag should be 0x23, is ", *int64Flag)
}
if *uintFlag != 24 {
t.Error("uint flag should be 24, is ", *uintFlag)
}
if *uint64Flag != 25 {
t.Error("uint64 flag should be 25, is ", *uint64Flag)
}
if *stringFlag != "hello" {
t.Error("string flag should be `hello`, is ", *stringFlag)
}
if *float64Flag != 2718e28 {
t.Error("float64 flag should be 2718e28, is ", *float64Flag)
}
if *durationFlag != 2*time.Minute {
t.Error("duration flag should be 2m, is ", *durationFlag)
}
}
// Test parsing a configuration file
func TestParseFile(t *testing.T) {
f := NewFlagSet(os.Args[0], ContinueOnError)
boolFlag := f.Bool("bool", false, "bool value")
bool2Flag := f.Bool("bool2", false, "bool2 value")
intFlag := f.Int("int", 0, "int value")
int64Flag := f.Int64("int64", 0, "int64 value")
uintFlag := f.Uint("uint", 0, "uint value")
uint64Flag := f.Uint64("uint64", 0, "uint64 value")
stringFlag := f.String("string", "0", "string value")
float64Flag := f.Float64("float64", 0, "float64 value")
durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
err := f.ParseFile("./testdata/test.conf")
if err != nil {
t.Fatal("expected no error; got ", err)
}
if *boolFlag != true {
t.Error("bool flag should be true, is ", *boolFlag)
}
if *bool2Flag != true {
t.Error("bool2 flag should be true, is ", *bool2Flag)
}
if *intFlag != 22 {
t.Error("int flag should be 22, is ", *intFlag)
}
if *int64Flag != 0x23 {
t.Error("int64 flag should be 0x23, is ", *int64Flag)
}
if *uintFlag != 24 {
t.Error("uint flag should be 24, is ", *uintFlag)
}
if *uint64Flag != 25 {
t.Error("uint64 flag should be 25, is ", *uint64Flag)
}
if *stringFlag != "hello" {
t.Error("string flag should be `hello`, is ", *stringFlag)
}
if *float64Flag != 2718e28 {
t.Error("float64 flag should be 2718e28, is ", *float64Flag)
}
if *durationFlag != 2*time.Minute {
t.Error("duration flag should be 2m, is ", *durationFlag)
}
}

View file

@ -0,0 +1,11 @@
# this is a comment followed by an empty line
bool
bool2=true
int 22
int64 0x23
uint 24
uint64 25
string hello
float64 2718e28
duration 2m

View file

@ -1,9 +0,0 @@
language: go
go:
- 1.3
- 1.4
- tip
install:
- go get -v github.com/naoina/go-stringutil
script:
- go test -v ./... -bench . -benchmem

View file

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

View file

@ -1,13 +0,0 @@
# stringutil [![Build Status](https://travis-ci.org/naoina/go-stringutil.png?branch=master)](https://travis-ci.org/naoina/go-stringutil)
## Installation
go get -u github.com/naoina/go-stringutil
## Documentation
See https://godoc.org/github.com/naoina/go-stringutil
## License
MIT

View file

@ -1,120 +0,0 @@
package stringutil
import (
"bytes"
"unicode"
)
// ToUpperCamelCase returns a copy of the string s with all Unicode letters mapped to their camel case.
// It will convert to upper case previous letter of '_' and first letter, and remove letter of '_'.
func ToUpperCamelCase(s string) string {
if s == "" {
return ""
}
upper := true
var result bytes.Buffer
for _, c := range s {
if c == '_' {
upper = true
continue
}
if upper {
result.WriteRune(unicode.ToUpper(c))
upper = false
continue
}
result.WriteRune(c)
}
return result.String()
}
// ToUpperCamelCaseASCII is similar to ToUpperCamelCase, but optimized for
// only the ASCII characters.
// ToUpperCamelCaseASCII is faster than ToUpperCamelCase, but doesn't work if
// contains non-ASCII characters.
func ToUpperCamelCaseASCII(s string) string {
if s == "" {
return ""
}
upper := true
result := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if c == '_' {
upper = true
continue
}
if upper {
result = append(result, toUpperASCII(c))
upper = false
continue
}
result = append(result, c)
}
return string(result)
}
// ToSnakeCase returns a copy of the string s with all Unicode letters mapped to their snake case.
// It will insert letter of '_' at position of previous letter of uppercase and all
// letters convert to lower case.
func ToSnakeCase(s string) string {
if s == "" {
return ""
}
var result bytes.Buffer
for _, c := range s {
if unicode.IsUpper(c) {
result.WriteByte('_')
}
result.WriteRune(unicode.ToLower(c))
}
s = result.String()
if s[0] == '_' {
return s[1:]
}
return s
}
// ToSnakeCaseASCII is similar to ToSnakeCase, but optimized for only the ASCII
// characters.
// ToSnakeCaseASCII is faster than ToSnakeCase, but doesn't work correctly if
// contains non-ASCII characters.
func ToSnakeCaseASCII(s string) string {
if s == "" {
return ""
}
result := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if isUpperASCII(c) {
result = append(result, '_')
}
result = append(result, toLowerASCII(c))
}
if result[0] == '_' {
return string(result[1:])
}
return string(result)
}
func isUpperASCII(c byte) bool {
return 'A' <= c && c <= 'Z'
}
func isLowerASCII(c byte) bool {
return 'a' <= c && c <= 'z'
}
func toUpperASCII(c byte) byte {
if isLowerASCII(c) {
return c - ('a' - 'A')
}
return c
}
func toLowerASCII(c byte) byte {
if isUpperASCII(c) {
return c + 'a' - 'A'
}
return c
}

View file

@ -1,35 +0,0 @@
package stringutil_test
import (
"testing"
"github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/go-stringutil"
)
var benchcaseForCamelCase = "the_quick_brown_fox_jumps_over_the_lazy_dog"
func BenchmarkToUpperCamelCase(b *testing.B) {
for i := 0; i < b.N; i++ {
stringutil.ToUpperCamelCase(benchcaseForCamelCase)
}
}
func BenchmarkToUpperCamelCaseASCII(b *testing.B) {
for i := 0; i < b.N; i++ {
stringutil.ToUpperCamelCaseASCII(benchcaseForCamelCase)
}
}
var benchcaseForSnakeCase = "TheQuickBrownFoxJumpsOverTheLazyDog"
func BenchmarkToSnakeCase(b *testing.B) {
for i := 0; i < b.N; i++ {
stringutil.ToSnakeCase(benchcaseForSnakeCase)
}
}
func BenchmarkToSnakeCaseASCII(b *testing.B) {
for i := 0; i < b.N; i++ {
stringutil.ToSnakeCaseASCII(benchcaseForSnakeCase)
}
}

View file

@ -1,88 +0,0 @@
package stringutil_test
import (
"reflect"
"testing"
"github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/go-stringutil"
)
func TestToUpperCamelCase(t *testing.T) {
for _, v := range []struct {
input, expect string
}{
{"", ""},
{"thequickbrownfoxoverthelazydog", "Thequickbrownfoxoverthelazydog"},
{"thequickbrownfoxoverthelazydoG", "ThequickbrownfoxoverthelazydoG"},
{"thequickbrownfoxoverthelazydo_g", "ThequickbrownfoxoverthelazydoG"},
{"TheQuickBrownFoxJumpsOverTheLazyDog", "TheQuickBrownFoxJumpsOverTheLazyDog"},
{"the_quick_brown_fox_jumps_over_the_lazy_dog", "TheQuickBrownFoxJumpsOverTheLazyDog"},
{"the_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog", "TheQuickBrownFoxJumpsOverTheLazyDog"},
{"_______", ""},
} {
actual := stringutil.ToUpperCamelCase(v.input)
expect := v.expect
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`stringutil.ToUpperCamelCase(%#v) => %#v; want %#v`, v.input, actual, expect)
}
}
}
func TestToUpperCamelCaseASCII(t *testing.T) {
for _, v := range []struct {
input, expect string
}{
{"", ""},
{"thequickbrownfoxoverthelazydog", "Thequickbrownfoxoverthelazydog"},
{"thequickbrownfoxoverthelazydoG", "ThequickbrownfoxoverthelazydoG"},
{"thequickbrownfoxoverthelazydo_g", "ThequickbrownfoxoverthelazydoG"},
{"TheQuickBrownFoxJumpsOverTheLazyDog", "TheQuickBrownFoxJumpsOverTheLazyDog"},
{"the_quick_brown_fox_jumps_over_the_lazy_dog", "TheQuickBrownFoxJumpsOverTheLazyDog"},
{"the_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog", "TheQuickBrownFoxJumpsOverTheLazyDog"},
} {
actual := stringutil.ToUpperCamelCaseASCII(v.input)
expect := v.expect
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`stringutil.ToUpperCamelCaseASCII(%#v) => %#v; want %#v`, v.input, actual, expect)
}
}
}
func TestToSnakeCase(t *testing.T) {
for _, v := range []struct {
input, expect string
}{
{"", ""},
{"thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsoverthelazydog"},
{"Thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsoverthelazydog"},
{"ThequickbrownfoxjumpsoverthelazydoG", "thequickbrownfoxjumpsoverthelazydo_g"},
{"TheQuickBrownFoxJumpsOverTheLazyDog", "the_quick_brown_fox_jumps_over_the_lazy_dog"},
{"the_quick_brown_fox_jumps_over_the_lazy_dog", "the_quick_brown_fox_jumps_over_the_lazy_dog"},
{"", "_______"},
} {
actual := stringutil.ToSnakeCase(v.input)
expect := v.expect
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`stringutil.ToSnakeCase(%#v) => %#v; want %#v`, v.input, actual, expect)
}
}
}
func TestToSnakeCaseASCII(t *testing.T) {
for _, v := range []struct {
input, expect string
}{
{"", ""},
{"thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsoverthelazydog"},
{"Thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsoverthelazydog"},
{"ThequickbrownfoxjumpsoverthelazydoG", "thequickbrownfoxjumpsoverthelazydo_g"},
{"TheQuickBrownFoxJumpsOverTheLazyDog", "the_quick_brown_fox_jumps_over_the_lazy_dog"},
{"the_quick_brown_fox_jumps_over_the_lazy_dog", "the_quick_brown_fox_jumps_over_the_lazy_dog"},
} {
actual := stringutil.ToSnakeCaseASCII(v.input)
expect := v.expect
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`stringutil.ToSnakeCaseASCII(%#v) => %#v; want %#v`, v.input, actual, expect)
}
}
}

View file

@ -1,11 +0,0 @@
language: go
go:
- 1.3
- tip
install:
- go get -v ./...
script:
- go test ./...

View file

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

View file

@ -1,16 +0,0 @@
GO = go
PEG = peg
.SUFFIXES: .peg .peg.go
.PHONY: all test clean
all: parse.peg.go
.peg.peg.go:
$(PEG) -switch -inline $<
test: all
$(GO) test ./...
clean:
$(RM) *.peg.go

View file

@ -1,364 +0,0 @@
# TOML parser and encoder library for Golang [![Build Status](https://travis-ci.org/naoina/toml.png?branch=master)](https://travis-ci.org/naoina/toml)
[TOML](https://github.com/toml-lang/toml) parser and encoder library for [Golang](http://golang.org/).
This library is compatible with TOML version [v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md).
## Installation
go get -u github.com/naoina/toml
## Usage
The following TOML save as `example.toml`.
```toml
# This is a TOML document. Boom.
title = "TOML Example"
[owner]
name = "Lance Uppercut"
dob = 1979-05-27T07:32:00-08:00 # First class dates? Why not?
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ]
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]
```
Then above TOML will mapping to `tomlConfig` struct using `toml.Unmarshal`.
```go
package main
import (
"io/ioutil"
"os"
"time"
"github.com/naoina/toml"
)
type tomlConfig struct {
Title string
Owner struct {
Name string
Dob time.Time
}
Database struct {
Server string
Ports []int
ConnectionMax uint
Enabled bool
}
Servers map[string]Server
Clients struct {
Data [][]interface{}
Hosts []string
}
}
type Server struct {
IP string
DC string
}
func main() {
f, err := os.Open("example.toml")
if err != nil {
panic(err)
}
defer f.Close()
buf, err := ioutil.ReadAll(f)
if err != nil {
panic(err)
}
var config tomlConfig
if err := toml.Unmarshal(buf, &config); err != nil {
panic(err)
}
// then to use the unmarshaled config...
}
```
## Mappings
A key and value of TOML will map to the corresponding field.
The fields of struct for mapping must be exported.
The rules of the mapping of key are following:
#### Exact matching
```toml
timeout_seconds = 256
```
```go
type Config struct {
Timeout_seconds int
}
```
#### Camelcase matching
```toml
server_name = "srv1"
```
```go
type Config struct {
ServerName string
}
```
#### Uppercase matching
```toml
ip = "10.0.0.1"
```
```go
type Config struct {
IP string
}
```
See the following examples for the value mappings.
### String
```toml
val = "string"
```
```go
type Config struct {
Val string
}
```
### Integer
```toml
val = 100
```
```go
type Config struct {
Val int
}
```
All types that can be used are following:
* int8 (from `-128` to `127`)
* int16 (from `-32768` to `32767`)
* int32 (from `-2147483648` to `2147483647`)
* int64 (from `-9223372036854775808` to `9223372036854775807`)
* int (same as `int32` on 32bit environment, or `int64` on 64bit environment)
* uint8 (from `0` to `255`)
* uint16 (from `0` to `65535`)
* uint32 (from `0` to `4294967295`)
* uint64 (from `0` to `18446744073709551615`)
* uint (same as `uint` on 32bit environment, or `uint64` on 64bit environment)
### Float
```toml
val = 3.1415
```
```go
type Config struct {
Val float32
}
```
All types that can be used are following:
* float32
* float64
### Boolean
```toml
val = true
```
```go
type Config struct {
Val bool
}
```
### Datetime
```toml
val = 2014-09-28T21:27:39Z
```
```go
type Config struct {
Val time.Time
}
```
### Array
```toml
val = ["a", "b", "c"]
```
```go
type Config struct {
Val []string
}
```
Also following examples all can be mapped:
```toml
val1 = [1, 2, 3]
val2 = [["a", "b"], ["c", "d"]]
val3 = [[1, 2, 3], ["a", "b", "c"]]
val4 = [[1, 2, 3], [["a", "b"], [true, false]]]
```
```go
type Config struct {
Val1 []int
Val2 [][]string
Val3 [][]interface{}
Val4 [][]interface{}
}
```
### Table
```toml
[server]
type = "app"
[server.development]
ip = "10.0.0.1"
[server.production]
ip = "10.0.0.2"
```
```go
type Config struct {
Server map[string]Server
}
type Server struct {
IP string
}
```
You can also use the following struct instead of map of struct.
```go
type Config struct {
Server struct {
Development Server
Production Server
}
}
type Server struct {
IP string
}
```
### Array of Tables
```toml
[[fruit]]
name = "apple"
[fruit.physical]
color = "red"
shape = "round"
[[fruit.variety]]
name = "red delicious"
[[fruit.variety]]
name = "granny smith"
[[fruit]]
name = "banana"
[[fruit.variety]]
name = "plantain"
```
```go
type Config struct {
Fruit []struct {
Name string
Physical struct {
Color string
Shape string
}
Variety []struct {
Name string
}
}
}
```
### Using `toml.UnmarshalTOML` interface
```toml
duration = "10s"
```
```go
import time
type Config struct {
Duration Duration
}
type Duration struct {
time.Duration
}
func (d *Duration) UnmarshalTOML(data []byte) error {
d.Duration, err := time.ParseDuration(string(data))
return err
}
```
## API documentation
See [Godoc](http://godoc.org/github.com/naoina/toml).
## License
MIT

View file

@ -1,184 +0,0 @@
package ast
import (
"strconv"
"time"
)
type Position struct {
Begin int
End int
}
type Value interface {
Pos() int
End() int
Source() string
}
type String struct {
Position Position
Value string
Data []rune
}
func (s *String) Pos() int {
return s.Position.Begin
}
func (s *String) End() int {
return s.Position.End
}
func (s *String) Source() string {
return string(s.Data)
}
type Integer struct {
Position Position
Value string
Data []rune
}
func (i *Integer) Pos() int {
return i.Position.Begin
}
func (i *Integer) End() int {
return i.Position.End
}
func (i *Integer) Source() string {
return string(i.Data)
}
func (i *Integer) Int() (int64, error) {
return strconv.ParseInt(i.Value, 10, 64)
}
type Float struct {
Position Position
Value string
Data []rune
}
func (f *Float) Pos() int {
return f.Position.Begin
}
func (f *Float) End() int {
return f.Position.End
}
func (f *Float) Source() string {
return string(f.Data)
}
func (f *Float) Float() (float64, error) {
return strconv.ParseFloat(f.Value, 64)
}
type Boolean struct {
Position Position
Value string
Data []rune
}
func (b *Boolean) Pos() int {
return b.Position.Begin
}
func (b *Boolean) End() int {
return b.Position.End
}
func (b *Boolean) Source() string {
return string(b.Data)
}
func (b *Boolean) Boolean() (bool, error) {
return strconv.ParseBool(b.Value)
}
type Datetime struct {
Position Position
Value string
Data []rune
}
func (d *Datetime) Pos() int {
return d.Position.Begin
}
func (d *Datetime) End() int {
return d.Position.End
}
func (d *Datetime) Source() string {
return string(d.Data)
}
func (d *Datetime) Time() (time.Time, error) {
return time.Parse(time.RFC3339Nano, d.Value)
}
type Array struct {
Position Position
Value []Value
Data []rune
}
func (a *Array) Pos() int {
return a.Position.Begin
}
func (a *Array) End() int {
return a.Position.End
}
func (a *Array) Source() string {
return string(a.Data)
}
type TableType uint8
const (
TableTypeNormal TableType = iota
TableTypeArray
)
var tableTypes = [...]string{
"normal",
"array",
}
func (t TableType) String() string {
return tableTypes[t]
}
type Table struct {
Position Position
Line int
Name string
Fields map[string]interface{}
Type TableType
Data []rune
}
func (t *Table) Pos() int {
return t.Position.Begin
}
func (t *Table) End() int {
return t.Position.End
}
func (t *Table) Source() string {
return string(t.Data)
}
type KeyValue struct {
Key string
Value Value
Line int
}

View file

@ -1,649 +0,0 @@
package toml
import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/toml/ast"
)
const (
tableSeparator = '.'
)
var (
escapeReplacer = strings.NewReplacer(
"\b", "\\n",
"\f", "\\f",
"\n", "\\n",
"\r", "\\r",
"\t", "\\t",
)
underscoreReplacer = strings.NewReplacer(
"_", "",
)
)
// Unmarshal parses the TOML data and stores the result in the value pointed to by v.
//
// Unmarshal will mapped to v that according to following rules:
//
// TOML strings to string
// TOML integers to any int type
// TOML floats to float32 or float64
// TOML booleans to bool
// TOML datetimes to time.Time
// TOML arrays to any type of slice or []interface{}
// TOML tables to struct
// TOML array of tables to slice of struct
func Unmarshal(data []byte, v interface{}) error {
table, err := Parse(data)
if err != nil {
return err
}
if err := UnmarshalTable(table, v); err != nil {
return fmt.Errorf("toml: unmarshal: %v", err)
}
return nil
}
// Unmarshaler is the interface implemented by objects that can unmarshal a
// TOML description of themselves.
// The input can be assumed to be a valid encoding of a TOML value.
// UnmarshalJSON must copy the TOML data if it wishes to retain the data after
// returning.
type Unmarshaler interface {
UnmarshalTOML([]byte) error
}
// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
//
// UnmarshalTable will mapped to v that according to following rules:
//
// TOML strings to string
// TOML integers to any int type
// TOML floats to float32 or float64
// TOML booleans to bool
// TOML datetimes to time.Time
// TOML arrays to any type of slice or []interface{}
// TOML tables to struct
// TOML array of tables to slice of struct
func UnmarshalTable(t *ast.Table, v interface{}) (err error) {
if v == nil {
return fmt.Errorf("v must not be nil")
}
rv := reflect.ValueOf(v)
if kind := rv.Kind(); kind != reflect.Ptr && kind != reflect.Map {
return fmt.Errorf("v must be a pointer or map")
}
for rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
for key, val := range t.Fields {
switch av := val.(type) {
case *ast.KeyValue:
fv, fieldName, found := findField(rv, key)
if !found {
return fmt.Errorf("line %d: field corresponding to `%s' is not defined in `%T'", av.Line, key, v)
}
switch fv.Kind() {
case reflect.Map:
mv := reflect.New(fv.Type().Elem()).Elem()
if err := UnmarshalTable(t, mv.Addr().Interface()); err != nil {
return err
}
fv.SetMapIndex(reflect.ValueOf(fieldName), mv)
default:
if err := setValue(fv, av.Value); err != nil {
return fmt.Errorf("line %d: %v.%s: %v", av.Line, rv.Type(), fieldName, err)
}
if rv.Kind() == reflect.Map {
rv.SetMapIndex(reflect.ValueOf(fieldName), fv)
}
}
case *ast.Table:
fv, fieldName, found := findField(rv, key)
if !found {
return fmt.Errorf("line %d: field corresponding to `%s' is not defined in `%T'", av.Line, key, v)
}
if err, ok := setUnmarshaler(fv, string(av.Data)); ok {
if err != nil {
return err
}
continue
}
for fv.Kind() == reflect.Ptr {
fv.Set(reflect.New(fv.Type().Elem()))
fv = fv.Elem()
}
switch fv.Kind() {
case reflect.Struct:
vv := reflect.New(fv.Type()).Elem()
if err := UnmarshalTable(av, vv.Addr().Interface()); err != nil {
return err
}
fv.Set(vv)
if rv.Kind() == reflect.Map {
rv.SetMapIndex(reflect.ValueOf(fieldName), fv)
}
case reflect.Map:
mv := reflect.MakeMap(fv.Type())
if err := UnmarshalTable(av, mv.Interface()); err != nil {
return err
}
fv.Set(mv)
default:
return fmt.Errorf("line %d: `%v.%s' must be struct or map, but %v given", av.Line, rv.Type(), fieldName, fv.Kind())
}
case []*ast.Table:
fv, fieldName, found := findField(rv, key)
if !found {
return fmt.Errorf("line %d: field corresponding to `%s' is not defined in `%T'", av[0].Line, key, v)
}
data := make([]string, 0, len(av))
for _, tbl := range av {
data = append(data, string(tbl.Data))
}
if err, ok := setUnmarshaler(fv, strings.Join(data, "\n")); ok {
if err != nil {
return err
}
continue
}
t := fv.Type().Elem()
pc := 0
for ; t.Kind() == reflect.Ptr; pc++ {
t = t.Elem()
}
if fv.Kind() != reflect.Slice {
return fmt.Errorf("line %d: `%v.%s' must be slice type, but %v given", av[0].Line, rv.Type(), fieldName, fv.Kind())
}
for _, tbl := range av {
var vv reflect.Value
switch t.Kind() {
case reflect.Map:
vv = reflect.MakeMap(t)
if err := UnmarshalTable(tbl, vv.Interface()); err != nil {
return err
}
default:
vv = reflect.New(t).Elem()
if err := UnmarshalTable(tbl, vv.Addr().Interface()); err != nil {
return err
}
}
for i := 0; i < pc; i++ {
vv = vv.Addr()
pv := reflect.New(vv.Type()).Elem()
pv.Set(vv)
vv = pv
}
fv.Set(reflect.Append(fv, vv))
}
if rv.Kind() == reflect.Map {
rv.SetMapIndex(reflect.ValueOf(fieldName), fv)
}
default:
return fmt.Errorf("BUG: unknown type `%T'", t)
}
}
return nil
}
func setUnmarshaler(lhs reflect.Value, data string) (error, bool) {
for lhs.Kind() == reflect.Ptr {
lhs.Set(reflect.New(lhs.Type().Elem()))
lhs = lhs.Elem()
}
if lhs.CanAddr() {
if u, ok := lhs.Addr().Interface().(Unmarshaler); ok {
return u.UnmarshalTOML([]byte(data)), true
}
}
return nil, false
}
func setValue(lhs reflect.Value, val ast.Value) error {
for lhs.Kind() == reflect.Ptr {
lhs.Set(reflect.New(lhs.Type().Elem()))
lhs = lhs.Elem()
}
if err, ok := setUnmarshaler(lhs, val.Source()); ok {
return err
}
switch v := val.(type) {
case *ast.Integer:
if err := setInt(lhs, v); err != nil {
return err
}
case *ast.Float:
if err := setFloat(lhs, v); err != nil {
return err
}
case *ast.String:
if err := setString(lhs, v); err != nil {
return err
}
case *ast.Boolean:
if err := setBoolean(lhs, v); err != nil {
return err
}
case *ast.Datetime:
if err := setDatetime(lhs, v); err != nil {
return err
}
case *ast.Array:
if err := setArray(lhs, v); err != nil {
return err
}
}
return nil
}
func setInt(fv reflect.Value, v *ast.Integer) error {
i, err := v.Int()
if err != nil {
return err
}
switch fv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if fv.OverflowInt(i) {
return &errorOutOfRange{fv.Kind(), i}
}
fv.SetInt(i)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fv.SetUint(uint64(i))
case reflect.Interface:
fv.Set(reflect.ValueOf(i))
default:
return fmt.Errorf("`%v' is not any types of int", fv.Type())
}
return nil
}
func setFloat(fv reflect.Value, v *ast.Float) error {
f, err := v.Float()
if err != nil {
return err
}
switch fv.Kind() {
case reflect.Float32, reflect.Float64:
if fv.OverflowFloat(f) {
return &errorOutOfRange{fv.Kind(), f}
}
fv.SetFloat(f)
case reflect.Interface:
fv.Set(reflect.ValueOf(f))
default:
return fmt.Errorf("`%v' is not float32 or float64", fv.Type())
}
return nil
}
func setString(fv reflect.Value, v *ast.String) error {
return set(fv, v.Value)
}
func setBoolean(fv reflect.Value, v *ast.Boolean) error {
b, err := v.Boolean()
if err != nil {
return err
}
return set(fv, b)
}
func setDatetime(fv reflect.Value, v *ast.Datetime) error {
tm, err := v.Time()
if err != nil {
return err
}
return set(fv, tm)
}
func setArray(fv reflect.Value, v *ast.Array) error {
if len(v.Value) == 0 {
return nil
}
typ := reflect.TypeOf(v.Value[0])
for _, vv := range v.Value[1:] {
if typ != reflect.TypeOf(vv) {
return fmt.Errorf("array cannot contain multiple types")
}
}
sliceType := fv.Type()
if fv.Kind() == reflect.Interface {
sliceType = reflect.SliceOf(sliceType)
}
slice := reflect.MakeSlice(sliceType, 0, len(v.Value))
t := sliceType.Elem()
for _, vv := range v.Value {
tmp := reflect.New(t).Elem()
if err := setValue(tmp, vv); err != nil {
return err
}
slice = reflect.Append(slice, tmp)
}
fv.Set(slice)
return nil
}
func set(fv reflect.Value, v interface{}) error {
rhs := reflect.ValueOf(v)
if !rhs.Type().AssignableTo(fv.Type()) {
return fmt.Errorf("`%v' type is not assignable to `%v' type", rhs.Type(), fv.Type())
}
fv.Set(rhs)
return nil
}
type stack struct {
key string
table *ast.Table
}
type toml struct {
table *ast.Table
line int
currentTable *ast.Table
s string
key string
val ast.Value
arr *array
tableMap map[string]*ast.Table
stack []*stack
skip bool
}
func (p *toml) init() {
p.line = 1
p.table = &ast.Table{
Line: p.line,
Type: ast.TableTypeNormal,
}
p.tableMap = map[string]*ast.Table{
"": p.table,
}
p.currentTable = p.table
}
func (p *toml) Error(err error) {
panic(convertError{fmt.Errorf("toml: line %d: %v", p.line, err)})
}
func (p *tomlParser) SetTime(begin, end int) {
p.val = &ast.Datetime{
Position: ast.Position{Begin: begin, End: end},
Data: p.buffer[begin:end],
Value: string(p.buffer[begin:end]),
}
}
func (p *tomlParser) SetFloat64(begin, end int) {
p.val = &ast.Float{
Position: ast.Position{Begin: begin, End: end},
Data: p.buffer[begin:end],
Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
}
}
func (p *tomlParser) SetInt64(begin, end int) {
p.val = &ast.Integer{
Position: ast.Position{Begin: begin, End: end},
Data: p.buffer[begin:end],
Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
}
}
func (p *tomlParser) SetString(begin, end int) {
p.val = &ast.String{
Position: ast.Position{Begin: begin, End: end},
Data: p.buffer[begin:end],
Value: p.s,
}
p.s = ""
}
func (p *tomlParser) SetBool(begin, end int) {
p.val = &ast.Boolean{
Position: ast.Position{Begin: begin, End: end},
Data: p.buffer[begin:end],
Value: string(p.buffer[begin:end]),
}
}
func (p *tomlParser) StartArray() {
if p.arr == nil {
p.arr = &array{line: p.line, current: &ast.Array{}}
return
}
p.arr.child = &array{parent: p.arr, line: p.line, current: &ast.Array{}}
p.arr = p.arr.child
}
func (p *tomlParser) AddArrayVal() {
if p.arr.current == nil {
p.arr.current = &ast.Array{}
}
p.arr.current.Value = append(p.arr.current.Value, p.val)
}
func (p *tomlParser) SetArray(begin, end int) {
p.arr.current.Position = ast.Position{Begin: begin, End: end}
p.arr.current.Data = p.buffer[begin:end]
p.val = p.arr.current
p.arr = p.arr.parent
}
func (p *toml) SetTable(buf []rune, begin, end int) {
p.setTable(p.table, buf, begin, end)
}
func (p *toml) setTable(t *ast.Table, buf []rune, begin, end int) {
name := string(buf[begin:end])
names := splitTableKey(name)
if t, exists := p.tableMap[name]; exists {
if lt := p.tableMap[names[len(names)-1]]; t.Type == ast.TableTypeArray || lt != nil && lt.Type == ast.TableTypeNormal {
p.Error(fmt.Errorf("table `%s' is in conflict with %v table in line %d", name, t.Type, t.Line))
}
}
t, err := p.lookupTable(t, names)
if err != nil {
p.Error(err)
}
p.currentTable = t
p.tableMap[name] = p.currentTable
}
func (p *tomlParser) SetTableString(begin, end int) {
p.currentTable.Data = p.buffer[begin:end]
p.currentTable.Position.Begin = begin
p.currentTable.Position.End = end
}
func (p *toml) SetArrayTable(buf []rune, begin, end int) {
p.setArrayTable(p.table, buf, begin, end)
}
func (p *toml) setArrayTable(t *ast.Table, buf []rune, begin, end int) {
name := string(buf[begin:end])
if t, exists := p.tableMap[name]; exists && t.Type == ast.TableTypeNormal {
p.Error(fmt.Errorf("table `%s' is in conflict with %v table in line %d", name, t.Type, t.Line))
}
names := splitTableKey(name)
t, err := p.lookupTable(t, names[:len(names)-1])
if err != nil {
p.Error(err)
}
last := names[len(names)-1]
tbl := &ast.Table{
Position: ast.Position{begin, end},
Line: p.line,
Name: last,
Type: ast.TableTypeArray,
}
switch v := t.Fields[last].(type) {
case nil:
if t.Fields == nil {
t.Fields = make(map[string]interface{})
}
t.Fields[last] = []*ast.Table{tbl}
case []*ast.Table:
t.Fields[last] = append(v, tbl)
case *ast.KeyValue:
p.Error(fmt.Errorf("key `%s' is in conflict with line %d", last, v.Line))
default:
p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", last, v))
}
p.currentTable = tbl
p.tableMap[name] = p.currentTable
}
func (p *toml) StartInlineTable() {
p.skip = false
p.stack = append(p.stack, &stack{p.key, p.currentTable})
buf := []rune(p.key)
if p.arr == nil {
p.setTable(p.currentTable, buf, 0, len(buf))
} else {
p.setArrayTable(p.currentTable, buf, 0, len(buf))
}
}
func (p *toml) EndInlineTable() {
st := p.stack[len(p.stack)-1]
p.key, p.currentTable = st.key, st.table
p.stack[len(p.stack)-1] = nil
p.stack = p.stack[:len(p.stack)-1]
p.skip = true
}
func (p *toml) AddLineCount(i int) {
p.line += i
}
func (p *toml) SetKey(buf []rune, begin, end int) {
p.key = string(buf[begin:end])
}
func (p *toml) AddKeyValue() {
if p.skip {
p.skip = false
return
}
if val, exists := p.currentTable.Fields[p.key]; exists {
switch v := val.(type) {
case *ast.Table:
p.Error(fmt.Errorf("key `%s' is in conflict with %v table in line %d", p.key, v.Type, v.Line))
case *ast.KeyValue:
p.Error(fmt.Errorf("key `%s' is in conflict with line %d", p.key, v.Line))
default:
p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", p.key, v))
}
}
if p.currentTable.Fields == nil {
p.currentTable.Fields = make(map[string]interface{})
}
p.currentTable.Fields[p.key] = &ast.KeyValue{
Key: p.key,
Value: p.val,
Line: p.line,
}
}
func (p *toml) SetBasicString(buf []rune, begin, end int) {
p.s = p.unquote(string(buf[begin:end]))
}
func (p *toml) SetMultilineString() {
p.s = p.unquote(`"` + escapeReplacer.Replace(strings.TrimLeft(p.s, "\r\n")) + `"`)
}
func (p *toml) AddMultilineBasicBody(buf []rune, begin, end int) {
p.s += string(buf[begin:end])
}
func (p *toml) SetLiteralString(buf []rune, begin, end int) {
p.s = string(buf[begin:end])
}
func (p *toml) SetMultilineLiteralString(buf []rune, begin, end int) {
p.s = strings.TrimLeft(string(buf[begin:end]), "\r\n")
}
func (p *toml) unquote(s string) string {
s, err := strconv.Unquote(s)
if err != nil {
p.Error(err)
}
return s
}
func (p *toml) lookupTable(t *ast.Table, keys []string) (*ast.Table, error) {
for _, s := range keys {
val, exists := t.Fields[s]
if !exists {
tbl := &ast.Table{
Line: p.line,
Name: s,
Type: ast.TableTypeNormal,
}
if t.Fields == nil {
t.Fields = make(map[string]interface{})
}
t.Fields[s] = tbl
t = tbl
continue
}
switch v := val.(type) {
case *ast.Table:
t = v
case []*ast.Table:
t = v[len(v)-1]
case *ast.KeyValue:
return nil, fmt.Errorf("key `%s' is in conflict with line %d", s, v.Line)
default:
return nil, fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", s, v)
}
}
return t, nil
}
func splitTableKey(tk string) []string {
key := make([]byte, 0, 1)
keys := make([]string, 0, 1)
inQuote := false
for i := 0; i < len(tk); i++ {
k := tk[i]
switch {
case k == tableSeparator && !inQuote:
keys = append(keys, string(key))
key = key[:0] // reuse buffer.
case k == '"':
inQuote = !inQuote
case (k == ' ' || k == '\t') && !inQuote:
// skip.
default:
key = append(key, k)
}
}
keys = append(keys, string(key))
return keys
}
type convertError struct {
err error
}
func (e convertError) Error() string {
return e.err.Error()
}
type array struct {
parent *array
child *array
current *ast.Array
line int
}

View file

@ -1,49 +0,0 @@
package toml_test
import (
"testing"
"time"
"github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/toml"
)
func BenchmarkUnmarshal(b *testing.B) {
var v struct {
Title string
Owner struct {
Name string
Organization string
Bio string
Dob time.Time
}
Database struct {
Server string
Ports []int
ConnectionMax int
Enabled bool
}
Servers struct {
Alpha struct {
IP string
DC string
}
Beta struct {
IP string
DC string
}
}
Clients struct {
Data []interface{}
Hosts []string
}
}
data, err := loadTestData()
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
if err := toml.Unmarshal(data, &v); err != nil {
b.Fatal(err)
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,211 +0,0 @@
package toml
import (
"fmt"
"reflect"
"strconv"
"time"
"go/ast"
"github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/go-stringutil"
)
const (
tagOmitempty = "omitempty"
tagSkip = "-"
)
// Marshal returns the TOML encoding of v.
//
// Struct values encode as TOML. Each exported struct field becomes a field of
// the TOML structure unless
// - the field's tag is "-", or
// - the field is empty and its tag specifies the "omitempty" option.
// The "toml" key in the struct field's tag value is the key name, followed by
// an optional comma and options. Examples:
//
// // Field is ignored by this package.
// Field int `toml:"-"`
//
// // Field appears in TOML as key "myName".
// Field int `toml:"myName"`
//
// // Field appears in TOML as key "myName" and the field is omitted from the
// // result of encoding if its value is empty.
// Field int `toml:"myName,omitempty"`
//
// // Field appears in TOML as key "field", but the field is skipped if
// // empty.
// // Note the leading comma.
// Field int `toml:",omitempty"`
func Marshal(v interface{}) ([]byte, error) {
return marshal(nil, "", reflect.ValueOf(v), false, false)
}
// Marshaler is the interface implemented by objects that can marshal themshelves into valid TOML.
type Marshaler interface {
MarshalTOML() ([]byte, error)
}
func marshal(buf []byte, prefix string, rv reflect.Value, inArray, arrayTable bool) ([]byte, error) {
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
ft := rt.Field(i)
if !ast.IsExported(ft.Name) {
continue
}
colName, rest := extractTag(rt.Field(i).Tag.Get(fieldTagName))
if colName == tagSkip {
continue
}
if colName == "" {
colName = stringutil.ToSnakeCase(ft.Name)
}
fv := rv.Field(i)
switch rest {
case tagOmitempty:
if fv.Interface() == reflect.Zero(ft.Type).Interface() {
continue
}
}
var err error
if buf, err = encodeValue(buf, prefix, colName, fv, inArray, arrayTable); err != nil {
return nil, err
}
}
return buf, nil
}
func encodeValue(buf []byte, prefix, name string, fv reflect.Value, inArray, arrayTable bool) ([]byte, error) {
switch t := fv.Interface().(type) {
case Marshaler:
b, err := t.MarshalTOML()
if err != nil {
return nil, err
}
return appendNewline(append(appendKey(buf, name, inArray, arrayTable), b...), inArray, arrayTable), nil
case time.Time:
return appendNewline(encodeTime(appendKey(buf, name, inArray, arrayTable), t), inArray, arrayTable), nil
}
switch fv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return appendNewline(encodeInt(appendKey(buf, name, inArray, arrayTable), fv.Int()), inArray, arrayTable), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return appendNewline(encodeUint(appendKey(buf, name, inArray, arrayTable), fv.Uint()), inArray, arrayTable), nil
case reflect.Float32, reflect.Float64:
return appendNewline(encodeFloat(appendKey(buf, name, inArray, arrayTable), fv.Float()), inArray, arrayTable), nil
case reflect.Bool:
return appendNewline(encodeBool(appendKey(buf, name, inArray, arrayTable), fv.Bool()), inArray, arrayTable), nil
case reflect.String:
return appendNewline(encodeString(appendKey(buf, name, inArray, arrayTable), fv.String()), inArray, arrayTable), nil
case reflect.Slice, reflect.Array:
ft := fv.Type().Elem()
for ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
if ft.Kind() == reflect.Struct {
name := tableName(prefix, name)
var err error
for i := 0; i < fv.Len(); i++ {
if buf, err = marshal(append(append(append(buf, '[', '['), name...), ']', ']', '\n'), name, fv.Index(i), false, true); err != nil {
return nil, err
}
}
return buf, nil
}
buf = append(appendKey(buf, name, inArray, arrayTable), '[')
var err error
for i := 0; i < fv.Len(); i++ {
if i != 0 {
buf = append(buf, ',')
}
if buf, err = encodeValue(buf, prefix, name, fv.Index(i), true, false); err != nil {
return nil, err
}
}
return appendNewline(append(buf, ']'), inArray, arrayTable), nil
case reflect.Struct:
name := tableName(prefix, name)
return marshal(append(append(append(buf, '['), name...), ']', '\n'), name, fv, inArray, arrayTable)
case reflect.Interface:
var err error
if buf, err = encodeInterface(appendKey(buf, name, inArray, arrayTable), fv.Interface()); err != nil {
return nil, err
}
return appendNewline(buf, inArray, arrayTable), nil
}
return nil, fmt.Errorf("toml: marshal: unsupported type %v", fv.Kind())
}
func appendKey(buf []byte, key string, inArray, arrayTable bool) []byte {
if !inArray {
return append(append(buf, key...), '=')
}
return buf
}
func appendNewline(buf []byte, inArray, arrayTable bool) []byte {
if !inArray {
return append(buf, '\n')
}
return buf
}
func encodeInterface(buf []byte, v interface{}) ([]byte, error) {
switch v := v.(type) {
case int:
return encodeInt(buf, int64(v)), nil
case int8:
return encodeInt(buf, int64(v)), nil
case int16:
return encodeInt(buf, int64(v)), nil
case int32:
return encodeInt(buf, int64(v)), nil
case int64:
return encodeInt(buf, v), nil
case uint:
return encodeUint(buf, uint64(v)), nil
case uint8:
return encodeUint(buf, uint64(v)), nil
case uint16:
return encodeUint(buf, uint64(v)), nil
case uint32:
return encodeUint(buf, uint64(v)), nil
case uint64:
return encodeUint(buf, v), nil
case float32:
return encodeFloat(buf, float64(v)), nil
case float64:
return encodeFloat(buf, v), nil
case bool:
return encodeBool(buf, v), nil
case string:
return encodeString(buf, v), nil
}
return nil, fmt.Errorf("toml: marshal: unable to detect a type of value `%v'", v)
}
func encodeInt(buf []byte, i int64) []byte {
return strconv.AppendInt(buf, i, 10)
}
func encodeUint(buf []byte, u uint64) []byte {
return strconv.AppendUint(buf, u, 10)
}
func encodeFloat(buf []byte, f float64) []byte {
return strconv.AppendFloat(buf, f, 'e', -1, 64)
}
func encodeBool(buf []byte, b bool) []byte {
return strconv.AppendBool(buf, b)
}
func encodeString(buf []byte, s string) []byte {
return strconv.AppendQuote(buf, s)
}
func encodeTime(buf []byte, t time.Time) []byte {
return append(buf, t.Format(time.RFC3339Nano)...)
}

View file

@ -1,298 +0,0 @@
package toml_test
import (
"reflect"
"testing"
"time"
"github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/toml"
)
func TestMarshal(t *testing.T) {
for _, v := range []struct {
v interface{}
expect string
}{
{struct{ Name string }{"alice"}, "name=\"alice\"\n"},
{struct{ Age int }{7}, "age=7\n"},
{struct {
Name string
Age int
}{"alice", 7}, "name=\"alice\"\nage=7\n"},
{struct {
Name string `toml:"-"`
Age int
}{"alice", 7}, "age=7\n"},
{struct {
Name string `toml:"my_name"`
}{"bob"}, "my_name=\"bob\"\n"},
{struct {
Name string `toml:"my_name,omitempty"`
}{"bob"}, "my_name=\"bob\"\n"},
{struct {
Name string `toml:",omitempty"`
}{"bob"}, "name=\"bob\"\n"},
{struct {
Name string `toml:",omitempty"`
}{""}, ""},
} {
b, err := toml.Marshal(v.v)
var actual interface{} = err
var expect interface{} = nil
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`Marshal(%#v) => %#v; want %#v`, v.v, actual, expect)
}
actual = string(b)
expect = v.expect
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`Marshal(%#v); v => %#v; want %#v`, v, actual, expect)
}
}
}
func TestMarshalWhole(t *testing.T) {
for _, v := range []struct {
v interface{}
expect string
}{
{
testStruct{
Table: Table{
Key: "value",
Subtable: Subtable{
Key: "another value",
},
Inline: Inline{
Name: Name{
First: "Tom",
Last: "Preston-Werner",
},
Point: Point{
X: 1,
Y: 2,
},
},
},
X: X{},
String: String{
Basic: Basic{
Basic: "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.",
},
Multiline: Multiline{
Key1: "One\nTwo",
Continued: Continued{
Key1: "The quick brown fox jumps over the lazy dog.",
},
},
Literal: Literal{
Winpath: `C:\Users\nodejs\templates`,
Winpath2: `\\ServerX\admin$\system32\`,
Quoted: `Tom "Dubs" Preston-Werner`,
Regex: `<\i\c*\s*>`,
Multiline: LiteralMultiline{
Regex2: `I [dw]on't need \d{2} apples`,
Lines: "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n",
},
},
},
Integer: Integer{
Key1: 99,
Key2: 42,
Key3: 0,
Key4: -17,
Underscores: IntegerUnderscores{
Key1: 1000,
Key2: 5349221,
Key3: 12345,
},
},
Float: Float{
Fractional: Fractional{
Key1: 1.0,
Key2: 3.1415,
Key3: -0.01,
},
Exponent: Exponent{
Key1: 5e22,
Key2: 1e6,
Key3: -2e-2,
},
Both: Both{
Key: 6.626e-34,
},
Underscores: FloatUnderscores{
Key1: 9224617.445991228313,
Key2: 1e100,
},
},
Boolean: Boolean{
True: true,
False: false,
},
Datetime: Datetime{
Key1: mustTime(time.Parse(time.RFC3339Nano, "1979-05-27T07:32:00Z")),
Key2: mustTime(time.Parse(time.RFC3339Nano, "1979-05-27T00:32:00-07:00")),
Key3: mustTime(time.Parse(time.RFC3339Nano, "1979-05-27T00:32:00.999999-07:00")),
},
Array: Array{
Key1: []int{1, 2, 3},
Key2: []string{"red", "yellow", "green"},
Key3: [][]int{{1, 2}, {3, 4, 5}},
Key4: [][]interface{}{{int64(1), int64(2)}, {"a", "b", "c"}},
Key5: []int{1, 2, 3},
Key6: []int{1, 2},
},
Products: []Product{
{Name: "Hammer", Sku: 738594937},
{},
{Name: "Nail", Sku: 284758393, Color: "gray"},
},
Fruit: []Fruit{
{
Name: "apple",
Physical: Physical{
Color: "red",
Shape: "round",
},
Variety: []Variety{
{Name: "red delicious"},
{Name: "granny smith"},
},
},
{
Name: "banana",
Variety: []Variety{
{Name: "plantain"},
},
},
},
},
`[table]
key="value"
[table.subtable]
key="another value"
[table.inline]
[table.inline.name]
first="Tom"
last="Preston-Werner"
[table.inline.point]
x=1
y=2
[x]
[x.y]
[x.y.z]
[x.y.z.w]
[string]
[string.basic]
basic="I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."
[string.multiline]
key1="One\nTwo"
key2=""
key3=""
[string.multiline.continued]
key1="The quick brown fox jumps over the lazy dog."
key2=""
key3=""
[string.literal]
winpath="C:\\Users\\nodejs\\templates"
winpath2="\\\\ServerX\\admin$\\system32\\"
quoted="Tom \"Dubs\" Preston-Werner"
regex="<\\i\\c*\\s*>"
[string.literal.multiline]
regex2="I [dw]on't need \\d{2} apples"
lines="The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n"
[integer]
key1=99
key2=42
key3=0
key4=-17
[integer.underscores]
key1=1000
key2=5349221
key3=12345
[float]
[float.fractional]
key1=1e+00
key2=3.1415e+00
key3=-1e-02
[float.exponent]
key1=5e+22
key2=1e+06
key3=-2e-02
[float.both]
key=6.626e-34
[float.underscores]
key1=9.224617445991227e+06
key2=1e+100
[boolean]
true=true
false=false
[datetime]
key1=1979-05-27T07:32:00Z
key2=1979-05-27T00:32:00-07:00
key3=1979-05-27T00:32:00.999999-07:00
[array]
key1=[1,2,3]
key2=["red","yellow","green"]
key3=[[1,2],[3,4,5]]
key4=[[1,2],["a","b","c"]]
key5=[1,2,3]
key6=[1,2]
[[products]]
name="Hammer"
sku=738594937
color=""
[[products]]
name=""
sku=0
color=""
[[products]]
name="Nail"
sku=284758393
color="gray"
[[fruit]]
name="apple"
[fruit.physical]
color="red"
shape="round"
[[fruit.variety]]
name="red delicious"
[[fruit.variety]]
name="granny smith"
[[fruit]]
name="banana"
[fruit.physical]
color=""
shape=""
[[fruit.variety]]
name="plantain"
`,
},
} {
b, err := toml.Marshal(v.v)
var actual interface{} = err
var expect interface{} = nil
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`Marshal(%#v) => %#v; want %#v`, v.v, actual, expect)
}
actual = string(b)
expect = v.expect
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`Marshal(%#v); v => %#v; want %#v`, v.v, actual, expect)
}
// test for reversible.
dest := testStruct{}
actual = toml.Unmarshal(b, &dest)
expect = nil
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`Unmarshal after Marshal => %#v; want %#v`, actual, expect)
}
actual = dest
expect = v.v
if !reflect.DeepEqual(actual, expect) {
t.Errorf(`Unmarshal after Marshal => %#v; want %#v`, v, actual, expect)
}
}
}

View file

@ -1,31 +0,0 @@
package toml
import (
"fmt"
"reflect"
)
func (e *parseError) Line() int {
tokens := e.p.tokenTree.Error()
positions := make([]int, len(tokens)*2)
p := 0
for _, token := range tokens {
positions[p], p = int(token.begin), p+1
positions[p], p = int(token.end), p+1
}
for _, t := range translatePositions(e.p.Buffer, positions) {
if e.p.line < t.line {
e.p.line = t.line
}
}
return e.p.line
}
type errorOutOfRange struct {
kind reflect.Kind
v interface{}
}
func (err *errorOutOfRange) Error() string {
return fmt.Sprintf("value %d is out of range for `%v` type", err.v, err.kind)
}

View file

@ -1,54 +0,0 @@
package toml
import (
"fmt"
"github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/toml/ast"
)
// Parse returns an AST representation of TOML.
// The toplevel is represented by a table.
func Parse(data []byte) (*ast.Table, error) {
d := &parseState{p: &tomlParser{Buffer: string(data)}}
d.init()
if err := d.parse(); err != nil {
return nil, err
}
return d.p.toml.table, nil
}
type parseState struct {
p *tomlParser
}
func (d *parseState) init() {
d.p.Init()
d.p.toml.init()
}
func (d *parseState) parse() error {
if err := d.p.Parse(); err != nil {
if err, ok := err.(*parseError); ok {
return fmt.Errorf("toml: line %d: parse error", err.Line())
}
return err
}
return d.execute()
}
func (d *parseState) execute() (err error) {
defer func() {
e := recover()
if e != nil {
cerr, ok := e.(convertError)
if !ok {
panic(e)
}
err = cerr.err
}
}()
d.p.Execute()
return nil
}

View file

@ -1,138 +0,0 @@
package toml
type tomlParser Peg {
toml
}
TOML <- Expression (newline Expression)* newline? !. { _ = buffer }
Expression <- (
<ws table ws comment? (wsnl keyval ws comment?)*> { p.SetTableString(begin, end) }
/ ws keyval ws comment?
/ ws comment?
/ ws
)
newline <- <[\r\n]+> { p.AddLineCount(end - begin) }
ws <- [ \t]*
wsnl <- (
[ \t]
/ <[\r\n]> { p.AddLineCount(end - begin) }
)*
comment <- '#' <[\t -\0x10FFFF]*>
keyval <- key ws '=' ws val { p.AddKeyValue() }
key <- bareKey / quotedKey
bareKey <- <[0-9A-Za-z\-_]+> { p.SetKey(p.buffer, begin, end) }
quotedKey <- '"' <basicChar+> '"' { p.SetKey(p.buffer, begin, end) }
val <- (
<datetime> { p.SetTime(begin, end) }
/ <float> { p.SetFloat64(begin, end) }
/ <integer> { p.SetInt64(begin, end) }
/ <string> { p.SetString(begin, end) }
/ <boolean> { p.SetBool(begin, end) }
/ <array> { p.SetArray(begin, end) }
/ inlineTable
)
table <- stdTable / arrayTable
stdTable <- '[' ws <tableKey> ws ']' { p.SetTable(p.buffer, begin, end) }
arrayTable <- '[[' ws <tableKey> ws ']]' { p.SetArrayTable(p.buffer, begin, end) }
inlineTable <- (
'{' { p.StartInlineTable() }
ws inlineTableKeyValues ws
'}' { p.EndInlineTable() }
)
inlineTableKeyValues <- (keyval inlineTableValSep?)*
tableKey <- key (tableKeySep key)*
tableKeySep <- ws '.' ws
inlineTableValSep <- ws ',' ws
integer <- [\-+]? int
int <- [1-9] (digit / '_' digit)+ / digit
float <- integer (frac exp? / frac? exp)
frac <- '.' digit (digit / '_' digit)*
exp <- [eE] [\-+]? digit (digit / '_' digit)*
string <- (
mlLiteralString
/ literalString
/ mlBasicString
/ basicString
)
basicString <- <'"' basicChar* '"'> { p.SetBasicString(p.buffer, begin, end) }
basicChar <- basicUnescaped / escaped
escaped <- escape ([btnfr"/\\] / 'u' hexQuad / 'U' hexQuad hexQuad)
basicUnescaped <- [ -!#-\[\]-\0x10FFFF]
escape <- '\\'
mlBasicString <- '"""' mlBasicBody '"""' { p.SetMultilineString() }
mlBasicBody <- (
<basicChar / newline> { p.AddMultilineBasicBody(p.buffer, begin, end) }
/ escape newline wsnl
)*
literalString <- "'" <literalChar*> "'" { p.SetLiteralString(p.buffer, begin, end) }
literalChar <- [\t -&(-\0x10FFFF]
mlLiteralString <- "'''" <mlLiteralBody> "'''" { p.SetMultilineLiteralString(p.buffer, begin, end) }
mlLiteralBody <- (!"'''" (mlLiteralChar / newline))*
mlLiteralChar <- [\t -\0x10FFFF]
hexdigit <- [0-9A-Fa-f]
hexQuad <- hexdigit hexdigit hexdigit hexdigit
boolean <- 'true' / 'false'
dateFullYear <- digitQuad
dateMonth <- digitDual
dateMDay <- digitDual
timeHour <- digitDual
timeMinute <- digitDual
timeSecond <- digitDual
timeSecfrac <- '.' digit+
timeNumoffset <- [\-+] timeHour ':' timeMinute
timeOffset <- 'Z' / timeNumoffset
partialTime <- timeHour ':' timeMinute ':' timeSecond timeSecfrac?
fullDate <- dateFullYear '-' dateMonth '-' dateMDay
fullTime <- partialTime timeOffset
datetime <- fullDate 'T' fullTime
digit <- [0-9]
digitDual <- digit digit
digitQuad <- digitDual digitDual
array <- (
'[' { p.StartArray() }
wsnl arrayValues wsnl
']'
)
arrayValues <- (
val { p.AddArrayVal() }
arraySep? (comment? newline)?
)*
arraySep <- ws ',' wsnl

File diff suppressed because it is too large Load diff

View file

@ -1,79 +0,0 @@
package toml
import (
"go/ast"
"reflect"
"strings"
"unicode"
)
// toCamelCase returns a copy of the string s with all Unicode letters mapped to their camel case.
// It will convert to upper case previous letter of '_' and first letter, and remove letter of '_'.
func toCamelCase(s string) string {
if s == "" {
return ""
}
result := make([]rune, 0, len(s))
upper := false
for _, r := range s {
if r == '_' {
upper = true
continue
}
if upper {
result = append(result, unicode.ToUpper(r))
upper = false
continue
}
result = append(result, r)
}
result[0] = unicode.ToUpper(result[0])
return string(result)
}
const (
fieldTagName = "toml"
)
func findField(rv reflect.Value, name string) (field reflect.Value, fieldName string, found bool) {
switch rv.Kind() {
case reflect.Struct:
rt := rv.Type()
for i := 0; i < rt.NumField(); i++ {
ft := rt.Field(i)
if !ast.IsExported(ft.Name) {
continue
}
if col, _ := extractTag(ft.Tag.Get(fieldTagName)); col == name {
return rv.Field(i), ft.Name, true
}
}
for _, name := range []string{
strings.Title(name),
toCamelCase(name),
strings.ToUpper(name),
} {
if field := rv.FieldByName(name); field.IsValid() {
return field, name, true
}
}
case reflect.Map:
return reflect.New(rv.Type().Elem()).Elem(), name, true
}
return field, "", false
}
func extractTag(tag string) (col, rest string) {
tags := strings.SplitN(tag, ",", 2)
if len(tags) == 2 {
return strings.TrimSpace(tags[0]), strings.TrimSpace(tags[1])
}
return strings.TrimSpace(tags[0]), ""
}
func tableName(prefix, name string) string {
if prefix != "" {
return prefix + string(tableSeparator) + name
}
return name
}

View file

@ -1,10 +1,11 @@
package main
import (
"flag"
"html/template"
"net/http"
"github.com/drone/drone/Godeps/_workspace/src/github.com/namsral/flag"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
"github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs"
@ -33,11 +34,61 @@ var (
revision string
)
var (
debug = flag.Bool("debug", false, "")
)
var conf = struct {
debug bool
server struct {
addr string
cert string
key string
}
session struct {
expiry string
secret string
}
docker struct {
host string
cert string
key string
ca string
}
remote struct {
driver string
config string
}
database struct {
driver string
config string
}
plugin struct {
filter string
}
}{}
func main() {
flag.StringVar(&conf.docker.host, "docker-host", "unix:///var/run/docker/docker.sock", "")
flag.StringVar(&conf.docker.cert, "docker-cert", "", "")
flag.StringVar(&conf.docker.key, "docker-key", "", "")
flag.StringVar(&conf.docker.ca, "docker-ca", "", "")
flag.StringVar(&conf.server.addr, "server-addr", ":8080", "")
flag.StringVar(&conf.server.cert, "server-cert", "", "")
flag.StringVar(&conf.server.key, "server-key", "", "")
flag.StringVar(&conf.session.expiry, "session-expiry", "", "")
flag.StringVar(&conf.session.secret, "session-secret", "", "")
flag.StringVar(&conf.remote.driver, "remote-driver", "github", "")
flag.StringVar(&conf.remote.config, "remote-config", "https://github.com", "")
flag.StringVar(&conf.database.driver, "database-driver", "sqlite3", "")
flag.StringVar(&conf.database.config, "database-config", "drone.sqlite", "")
flag.StringVar(&conf.plugin.filter, "plugin-filter", "plugins/*", "")
flag.BoolVar(&conf.debug, "debug", false, "")
flag.String("config", "", "")
flag.Parse()
settings, err := config.Load()
@ -45,12 +96,12 @@ func main() {
panic(err)
}
store, err := store.New(settings.Database.Driver + "://" + settings.Database.Datasource)
store, err := store.New(conf.database.driver, conf.database.config)
if err != nil {
panic(err)
}
remote, err := remote.New(settings.Remote.Driver, settings)
remote, err := remote.New(conf.remote.driver, conf.remote.config)
if err != nil {
panic(err)
}
@ -205,7 +256,7 @@ func static() http.Handler {
AssetDir: AssetDir,
Prefix: "cmd/drone-server/static",
})
if *debug {
if conf.debug {
handler = http.FileServer(
http.Dir("cmd/drone-server/static"),
)

View file

@ -12,7 +12,7 @@
border-top: none;
}
.list .column-avatar {
.list .column-avatar {
width: 60px;
min-width: 60px;
}
@ -48,7 +48,7 @@
.list .column-avatar img {
width:32px;
height:32px;
border-radius:50%;
border-radius:4px;
margin-left:5px;
}
.list h2 {
@ -68,4 +68,4 @@
.list em,
.list em {
color:#65737e;
}
}

View file

@ -6,11 +6,11 @@ import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru"
"github.com/drone/drone/pkg/config"
"github.com/drone/drone/pkg/oauth2"
"github.com/drone/drone/pkg/remote"
common "github.com/drone/drone/pkg/types"
@ -21,6 +21,7 @@ import (
const (
DefaultURL = "https://github.com"
DefaultAPI = "https://api.github.com"
DefaultScope = "repo,repo:status,user:email"
)
@ -41,32 +42,35 @@ func init() {
remote.Register("github", NewDriver)
}
func NewDriver(conf *config.Config) (remote.Remote, error) {
var github = GitHub{
API: conf.Github.API,
URL: conf.Github.Host,
Client: conf.Github.Client,
Secret: conf.Github.Secret,
AllowedOrgs: conf.Github.Orgs,
Open: conf.Github.Open,
PrivateMode: conf.Github.PrivateMode,
SkipVerify: conf.Github.SkipVerify,
}
var err error
github.cache, err = lru.New(1028)
func NewDriver(config string) (remote.Remote, error) {
url_, err := url.Parse(config)
if err != nil {
return nil, err
}
params := url_.Query()
url_.Path = ""
url_.RawQuery = ""
// the API must have a trailing slash
if !strings.HasSuffix(github.API, "/") {
github.API += "/"
github := GitHub{}
github.URL = url_.String()
github.Client = params.Get("client")
github.Secret = params.Get("secret")
github.AllowedOrgs = params["orgs"]
github.PrivateMode, _ = strconv.ParseBool(params.Get("private_mode"))
github.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
github.Open, _ = strconv.ParseBool(params.Get("open"))
if github.URL == DefaultURL {
github.API = DefaultAPI
} else {
github.API = github.URL + "/v3/api/"
}
// the URL must NOT have a trailing slash
if strings.HasSuffix(github.URL, "/") {
github.URL = github.URL[:len(github.URL)-1]
}
return &github, nil
// here we cache permissions to avoid too many api
// calls. this should really be moved outise the
// remote plugin into the app
github.cache, err = lru.New(1028)
return &github, err
}
func (g *GitHub) Login(token, secret string) (*common.User, error) {

View file

@ -11,7 +11,6 @@ import (
"github.com/drone/drone/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client"
"github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru"
"github.com/drone/drone/pkg/config"
"github.com/drone/drone/pkg/oauth2"
"github.com/drone/drone/pkg/remote"
common "github.com/drone/drone/pkg/types"
@ -39,27 +38,31 @@ func init() {
remote.Register("gitlab", NewDriver)
}
func NewDriver(conf *config.Config) (remote.Remote, error) {
var gitlab = Gitlab{
URL: conf.Gitlab.Host,
Client: conf.Gitlab.Client,
Secret: conf.Gitlab.Secret,
AllowedOrgs: conf.Gitlab.Orgs,
Open: conf.Gitlab.Open,
SkipVerify: conf.Gitlab.SkipVerify,
Search: conf.Gitlab.Search,
}
var err error
gitlab.cache, err = lru.New(1028)
func NewDriver(config string) (remote.Remote, error) {
url_, err := url.Parse(config)
if err != nil {
return nil, err
}
params := url_.Query()
url_.Path = ""
url_.RawQuery = ""
// the URL must NOT have a trailing slash
if strings.HasSuffix(gitlab.URL, "/") {
gitlab.URL = gitlab.URL[:len(gitlab.URL)-1]
}
return &gitlab, nil
gitlab := Gitlab{}
gitlab.URL = url_.String()
gitlab.Client = params.Get("client")
gitlab.Secret = params.Get("secret")
gitlab.AllowedOrgs = params["orgs"]
gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
gitlab.Open, _ = strconv.ParseBool(params.Get("open"))
// this is a temp workaround
gitlab.Search, _ = strconv.ParseBool(params.Get("search"))
// here we cache permissions to avoid too many api
// calls. this should really be moved outise the
// remote plugin into the app
gitlab.cache, err = lru.New(1028)
return &gitlab, err
}
func (r *Gitlab) Login(token, secret string) (*common.User, error) {

View file

@ -1,12 +1,12 @@
package remote
import (
"fmt"
"net/http"
"github.com/drone/drone/pkg/config"
"github.com/drone/drone/pkg/oauth2"
common "github.com/drone/drone/pkg/types"
"github.com/drone/drone/pkg/types"
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
)
var drivers = make(map[string]DriverFunc)
@ -26,55 +26,57 @@ func Register(name string, driver DriverFunc) {
// DriverFunc returns a new connection to the remote.
// Config is a struct, with base remote configuration.
type DriverFunc func(conf *config.Config) (Remote, error)
type DriverFunc func(config string) (Remote, error)
// New creates a new remote connection.
func New(driver string, conf *config.Config) (Remote, error) {
func New(driver, config string) (Remote, error) {
fn, ok := drivers[driver]
if !ok {
return nil, fmt.Errorf("remote: unknown driver %q", driver)
log.Fatalf("remote: unknown driver %q", driver)
}
return fn(conf)
log.Infof("remote: loading driver %s", driver)
log.Infof("remote: loading config %s", config)
return fn(config)
}
type Remote interface {
// Login authenticates the session and returns the
// remote user details.
Login(token, secret string) (*common.User, error)
Login(token, secret string) (*types.User, error)
// Orgs fetches the organizations for the given user.
Orgs(u *common.User) ([]string, error)
Orgs(u *types.User) ([]string, error)
// Repo fetches the named repository from the remote system.
Repo(u *common.User, owner, repo string) (*common.Repo, error)
Repo(u *types.User, owner, repo string) (*types.Repo, error)
// Perm fetches the named repository permissions from
// the remote system for the specified user.
Perm(u *common.User, owner, repo string) (*common.Perm, error)
Perm(u *types.User, owner, repo string) (*types.Perm, error)
// Script fetches the build script (.drone.yml) from the remote
// repository and returns in string format.
Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error)
Script(u *types.User, r *types.Repo, b *types.Build) ([]byte, error)
// Status sends the commit status to the remote system.
// An example would be the GitHub pull request status.
Status(u *common.User, r *common.Repo, b *common.Build) error
Status(u *types.User, r *types.Repo, b *types.Build) error
// Netrc returns a .netrc file that can be used to clone
// private repositories from a remote system.
Netrc(u *common.User) (*common.Netrc, error)
Netrc(u *types.User) (*types.Netrc, error)
// Activate activates a repository by creating the post-commit hook and
// adding the SSH deploy key, if applicable.
Activate(u *common.User, r *common.Repo, k *common.Keypair, link string) error
Activate(u *types.User, r *types.Repo, k *types.Keypair, link string) error
// Deactivate removes a repository by removing all the post-commit hooks
// which are equal to link and removing the SSH deploy key.
Deactivate(u *common.User, r *common.Repo, link string) error
Deactivate(u *types.User, r *types.Repo, link string) error
// Hook parses the post-commit hook from the Request body
// and returns the required data in a standard format.
Hook(r *http.Request) (*common.Hook, error)
Hook(r *http.Request) (*types.Hook, error)
// Oauth2Transport
Oauth2Transport(r *http.Request) *oauth2.Transport

View file

@ -27,8 +27,8 @@ var (
DockerHost = os.Getenv("DOCKER_HOST")
// Docker TLS variables
DockerHostCa = os.Getenv("DOCKER_CA")
DockerHostKey = os.Getenv("DOCKER_KEY")
DockerHostCa = os.Getenv("DOCKER_CA")
DockerHostKey = os.Getenv("DOCKER_KEY")
DockerHostCert = os.Getenv("DOCKER_CERT")
)
@ -111,8 +111,8 @@ func (r *Runner) Run(w *queue.Work) error {
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlc = &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}
}

View file

@ -2,7 +2,6 @@ package builtin
import (
"database/sql"
"net/url"
"os"
"github.com/drone/drone/pkg/store"
@ -25,16 +24,7 @@ func init() {
store.Register("postgres", NewDriver)
}
func NewDriver(dsn string) (store.Store, error) {
uri, err := url.Parse(dsn)
if err != nil {
return nil, err
}
driver := uri.Scheme
if uri.Scheme == "sqlite3" {
uri.Scheme = ""
}
datasource := uri.String()
func NewDriver(driver, datasource string) (store.Store, error) {
conn, err := Connect(driver, datasource)
if err != nil {
return nil, err

View file

@ -1,11 +1,11 @@
package store
import (
"fmt"
"io"
"net/url"
"github.com/drone/drone/pkg/types"
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
)
var drivers = make(map[string]DriverFunc)
@ -25,22 +25,19 @@ func Register(name string, driver DriverFunc) {
// DriverFunc returns a new connection to the datastore.
// The name is a string in a driver-specific format.
type DriverFunc func(name string) (Store, error)
type DriverFunc func(driver, datasource string) (Store, error)
// New creates a new database connection specified by its database driver
// name and a driver-specific data source name, usually consisting of at
// least a database name and connection information.
func New(dsn string) (Store, error) {
uri, err := url.Parse(dsn)
if err != nil {
return nil, err
}
driver := uri.Scheme
func New(driver, datasource string) (Store, error) {
fn, ok := drivers[driver]
if !ok {
return nil, fmt.Errorf("datastore: unknown driver %q", driver)
log.Fatalf("datastore: unknown driver %q", driver)
}
return fn(dsn)
log.Infof("datastore: loading driver %s", driver)
log.Infof("datastore: loading config %s", datasource)
return fn(driver, datasource)
}
type Store interface {