add unlink enpoint
This commit is contained in:
@@ -89,7 +89,8 @@ func setAdminRoutes(mux *http.ServeMux) {
|
|||||||
mux.HandleFunc("/admin/home", withUserWare(cont.ServeHome))
|
mux.HandleFunc("/admin/home", withUserWare(cont.ServeHome))
|
||||||
mux.HandleFunc("/admin/change_own_password", withUserWare(cont.ServeChangeOwnPassword))
|
mux.HandleFunc("/admin/change_own_password", withUserWare(cont.ServeChangeOwnPassword))
|
||||||
mux.HandleFunc("/admin/change_own_password_do", withUserWare(cont.ServeChangeOwnPasswordDo))
|
mux.HandleFunc("/admin/change_own_password_do", withUserWare(cont.ServeChangeOwnPasswordDo))
|
||||||
mux.HandleFunc("/admin/link_lastfm_callback", withUserWare(cont.ServeLinkLastFMCallback))
|
mux.HandleFunc("/admin/link_lastfm_do", withUserWare(cont.ServeLinkLastFMDo))
|
||||||
|
mux.HandleFunc("/admin/unlink_lastfm_do", withUserWare(cont.ServeUnlinkLastFMDo))
|
||||||
mux.HandleFunc("/admin/change_password", withAdminWare(cont.ServeChangePassword))
|
mux.HandleFunc("/admin/change_password", withAdminWare(cont.ServeChangePassword))
|
||||||
mux.HandleFunc("/admin/change_password_do", withAdminWare(cont.ServeChangePasswordDo))
|
mux.HandleFunc("/admin/change_password_do", withAdminWare(cont.ServeChangePasswordDo))
|
||||||
mux.HandleFunc("/admin/create_user", withAdminWare(cont.ServeCreateUser))
|
mux.HandleFunc("/admin/create_user", withAdminWare(cont.ServeCreateUser))
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -2,7 +2,6 @@ module github.com/sentriz/gonic
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.37.1 // indirect
|
cloud.google.com/go v0.37.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1
|
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect
|
github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect
|
||||||
github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca
|
github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca
|
||||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
|
||||||
@@ -17,7 +16,7 @@ require (
|
|||||||
github.com/mattn/go-sqlite3 v1.10.0 // indirect
|
github.com/mattn/go-sqlite3 v1.10.0 // indirect
|
||||||
github.com/mozillazg/go-unidecode v0.1.1
|
github.com/mozillazg/go-unidecode v0.1.1
|
||||||
github.com/myesui/uuid v1.0.0 // indirect
|
github.com/myesui/uuid v1.0.0 // indirect
|
||||||
github.com/twinj/uuid v1.0.0
|
github.com/twinj/uuid v1.0.0 // indirect
|
||||||
github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755
|
github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755
|
||||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a // indirect
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a // indirect
|
||||||
google.golang.org/appengine v1.5.0 // indirect
|
google.golang.org/appengine v1.5.0 // indirect
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) {
|
|||||||
if username == "" || password == "" {
|
if username == "" || password == "" {
|
||||||
session.AddFlash("please provide both a username and password")
|
session.AddFlash("please provide both a username and password")
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user := c.GetUserFromName(username)
|
user := c.GetUserFromName(username)
|
||||||
if !(username == user.Name && password == user.Password) {
|
if !(username == user.Name && password == user.Password) {
|
||||||
session.AddFlash("invalid username / password")
|
session.AddFlash("invalid username / password")
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// put the user name into the session. future endpoints after this one
|
// put the user name into the session. future endpoints after this one
|
||||||
@@ -37,14 +37,14 @@ func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) {
|
|||||||
// session and put the row into the request context.
|
// session and put the row into the request context.
|
||||||
session.Values["user"] = user.Name
|
session.Values["user"] = user.Name
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, "/admin/home", 303)
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ServeLogout(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) ServeLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
session := r.Context().Value("session").(*sessions.Session)
|
session := r.Context().Value("session").(*sessions.Session)
|
||||||
delete(session.Values, "user")
|
delete(session.Values, "user")
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, "/admin/login", 303)
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -53,9 +53,19 @@ func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) {
|
|||||||
c.DB.Table("albums").Count(&data.AlbumCount)
|
c.DB.Table("albums").Count(&data.AlbumCount)
|
||||||
c.DB.Table("tracks").Count(&data.TrackCount)
|
c.DB.Table("tracks").Count(&data.TrackCount)
|
||||||
c.DB.Find(&data.AllUsers)
|
c.DB.Find(&data.AllUsers)
|
||||||
var apiKey db.Setting
|
data.CurrentLastFMAPIKey = c.GetSetting("lastfm_api_key")
|
||||||
c.DB.Where("key = ?", "lastfm_api_key").First(&apiKey)
|
scheme := utilities.FirstExisting(
|
||||||
data.CurrentLastFMAPIKey = apiKey.Value
|
"http", // fallback
|
||||||
|
r.Header.Get("X-Forwarded-Proto"),
|
||||||
|
r.Header.Get("X-Forwarded-Scheme"),
|
||||||
|
r.URL.Scheme,
|
||||||
|
)
|
||||||
|
host := utilities.FirstExisting(
|
||||||
|
"localhost:7373", // fallback
|
||||||
|
r.Header.Get("X-Forwarded-Host"),
|
||||||
|
r.Host,
|
||||||
|
)
|
||||||
|
data.RequestRoot = fmt.Sprintf("%s://%s", scheme, host)
|
||||||
renderTemplate(w, r, "home", &data)
|
renderTemplate(w, r, "home", &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,41 +81,44 @@ func (c *Controller) ServeChangeOwnPasswordDo(w http.ResponseWriter, r *http.Req
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
session.AddFlash(err.Error())
|
session.AddFlash(err.Error())
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user := r.Context().Value("user").(*db.User)
|
user := r.Context().Value("user").(*db.User)
|
||||||
user.Password = passwordOne
|
user.Password = passwordOne
|
||||||
c.DB.Save(user)
|
c.DB.Save(user)
|
||||||
http.Redirect(w, r, "/admin/home", 303)
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ServeLinkLastFMCallback(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) ServeLinkLastFMDo(w http.ResponseWriter, r *http.Request) {
|
||||||
token := r.URL.Query().Get("token")
|
token := r.URL.Query().Get("token")
|
||||||
if token == "" {
|
if token == "" {
|
||||||
http.Error(w, "please provide a token", 400)
|
http.Error(w, "please provide a token", 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var apiKey db.Setting
|
|
||||||
c.DB.Where("key = ?", "lastfm_api_key").First(&apiKey)
|
|
||||||
var secret db.Setting
|
|
||||||
c.DB.Where("key = ?", "lastfm_secret").First(&secret)
|
|
||||||
sessionKey, err := lastfm.GetSession(
|
sessionKey, err := lastfm.GetSession(
|
||||||
apiKey.Value,
|
c.GetSetting("lastfm_api_key"),
|
||||||
secret.Value,
|
c.GetSetting("lastfm_secret"),
|
||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
session := r.Context().Value("session").(*sessions.Session)
|
session := r.Context().Value("session").(*sessions.Session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.AddFlash(err.Error())
|
session.AddFlash(err.Error())
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, "/admin/home", 302)
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user := r.Context().Value("user").(*db.User)
|
user := r.Context().Value("user").(*db.User)
|
||||||
user.LastFMSession = sessionKey
|
user.LastFMSession = sessionKey
|
||||||
c.DB.Save(&user)
|
c.DB.Save(&user)
|
||||||
http.Redirect(w, r, "/admin/home", 302)
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ServeUnlinkLastFMDo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user := r.Context().Value("user").(*db.User)
|
||||||
|
user.LastFMSession = ""
|
||||||
|
c.DB.Save(&user)
|
||||||
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ServeChangePassword(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) ServeChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -136,12 +149,12 @@ func (c *Controller) ServeChangePasswordDo(w http.ResponseWriter, r *http.Reques
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
session.AddFlash(err.Error())
|
session.AddFlash(err.Error())
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.Password = passwordOne
|
user.Password = passwordOne
|
||||||
c.DB.Save(&user)
|
c.DB.Save(&user)
|
||||||
http.Redirect(w, r, "/admin/home", 303)
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ServeCreateUser(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) ServeCreateUser(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -155,7 +168,7 @@ func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
session.AddFlash(err.Error())
|
session.AddFlash(err.Error())
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
passwordOne := r.FormValue("password_one")
|
passwordOne := r.FormValue("password_one")
|
||||||
@@ -164,7 +177,7 @@ func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
session.AddFlash(err.Error())
|
session.AddFlash(err.Error())
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user := db.User{
|
user := db.User{
|
||||||
@@ -177,10 +190,10 @@ func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) {
|
|||||||
"could not create user `%s`: %v", username, err,
|
"could not create user `%s`: %v", username, err,
|
||||||
))
|
))
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/admin/home", 303)
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ServeUpdateLastFMAPIKey(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) ServeUpdateLastFMAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -198,10 +211,10 @@ func (c *Controller) ServeUpdateLastFMAPIKeyDo(w http.ResponseWriter, r *http.Re
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
session.AddFlash(err.Error())
|
session.AddFlash(err.Error())
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, r.Header.Get("Referer"), 302)
|
http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.SetSetting("lastfm_api_key", apiKey)
|
c.SetSetting("lastfm_api_key", apiKey)
|
||||||
c.SetSetting("lastfm_secret", secret)
|
c.SetSetting("lastfm_secret", secret)
|
||||||
http.Redirect(w, r, "/admin/home", 303)
|
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/sentriz/gonic/db"
|
"github.com/sentriz/gonic/db"
|
||||||
"github.com/sentriz/gonic/handler/utilities"
|
|
||||||
"github.com/sentriz/gonic/subsonic"
|
"github.com/sentriz/gonic/subsonic"
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
@@ -182,18 +181,6 @@ func renderTemplate(w http.ResponseWriter, r *http.Request,
|
|||||||
if ok {
|
if ok {
|
||||||
data.User = user
|
data.User = user
|
||||||
}
|
}
|
||||||
scheme := utilities.FirstExisting(
|
|
||||||
"http", // fallback
|
|
||||||
r.Header.Get("X-Forwarded-Proto"),
|
|
||||||
r.Header.Get("X-Forwarded-Scheme"),
|
|
||||||
r.URL.Scheme,
|
|
||||||
)
|
|
||||||
host := utilities.FirstExisting(
|
|
||||||
"localhost:7373", // fallback
|
|
||||||
r.Header.Get("X-Forwarded-Host"),
|
|
||||||
r.Host,
|
|
||||||
)
|
|
||||||
data.RequestRoot = fmt.Sprintf("%s://%s", scheme, host)
|
|
||||||
err := templates[name].ExecuteTemplate(w, "layout", data)
|
err := templates[name].ExecuteTemplate(w, "layout", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("500 when executing: %v", err), 500)
|
http.Error(w, fmt.Sprintf("500 when executing: %v", err), 500)
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func (c *Controller) WithUserSession(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
if !ok {
|
if !ok {
|
||||||
session.AddFlash("you are not authenticated")
|
session.AddFlash("you are not authenticated")
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, "/admin/login", 303)
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// take username from sesion and add the user row
|
// take username from sesion and add the user row
|
||||||
@@ -135,7 +135,7 @@ func (c *Controller) WithAdminSession(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
if !user.IsAdmin {
|
if !user.IsAdmin {
|
||||||
session.AddFlash("you are not an admin")
|
session.AddFlash("you are not an admin")
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, "/admin/login", 303)
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var (
|
|||||||
func getParamSignature(params url.Values, secret string) string {
|
func getParamSignature(params url.Values, secret string) string {
|
||||||
// the parameters must be in order before hashing
|
// the parameters must be in order before hashing
|
||||||
paramKeys := make([]string, 0)
|
paramKeys := make([]string, 0)
|
||||||
for k, _ := range params {
|
for k := range params {
|
||||||
paramKeys = append(paramKeys, k)
|
paramKeys = append(paramKeys, k)
|
||||||
}
|
}
|
||||||
sort.Strings(paramKeys)
|
sort.Strings(paramKeys)
|
||||||
|
|||||||
@@ -88,3 +88,10 @@ form input[type=submit] {
|
|||||||
background-color: #fd1b1b1c;
|
background-color: #fd1b1b1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.angry {
|
||||||
|
background-color: #f4433669;
|
||||||
|
}
|
||||||
|
|
||||||
|
.happy {
|
||||||
|
background-color: #4caf5069;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package tags
|
|
||||||
|
|
||||||
type probeData struct {
|
|
||||||
Format *probeFormat `json:"format"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type probeFormat struct {
|
|
||||||
Filename string `json:"filename"`
|
|
||||||
NBStreams int `json:"nb_streams"`
|
|
||||||
NBPrograms int `json:"nb_programs"`
|
|
||||||
FormatName string `json:"format_name"`
|
|
||||||
FormatLongName string `json:"format_long_name"`
|
|
||||||
StartTime float64 `json:"start_time,string"`
|
|
||||||
Duration float64 `json:"duration,string"`
|
|
||||||
Size string `json:"size"`
|
|
||||||
Bitrate int `json:"bit_rate,string"`
|
|
||||||
ProbeScore int `json:"probe_score"`
|
|
||||||
Tags map[string]string `json:"tags"`
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
203
tags/tags.go
203
tags/tags.go
@@ -1,203 +0,0 @@
|
|||||||
package tags
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
titleFields = []string{
|
|
||||||
"TITLE",
|
|
||||||
"title",
|
|
||||||
}
|
|
||||||
albumFields = []string{
|
|
||||||
"ALBUM",
|
|
||||||
"album",
|
|
||||||
}
|
|
||||||
artistFields = []string{
|
|
||||||
"ARTIST",
|
|
||||||
"artist",
|
|
||||||
}
|
|
||||||
albumArtistFields = []string{
|
|
||||||
"ALBUM ARTIST",
|
|
||||||
"ALBUMARTIST",
|
|
||||||
"ALBUM_ARTIST",
|
|
||||||
"album artist",
|
|
||||||
"album_artist",
|
|
||||||
"albumartist",
|
|
||||||
}
|
|
||||||
composerFields = []string{
|
|
||||||
"COMPOSER",
|
|
||||||
"composer",
|
|
||||||
}
|
|
||||||
genreFields = []string{
|
|
||||||
"GENRE",
|
|
||||||
"genre",
|
|
||||||
}
|
|
||||||
yearFields = []string{
|
|
||||||
"YEAR",
|
|
||||||
"year",
|
|
||||||
}
|
|
||||||
trackFields = []string{
|
|
||||||
"TRACK",
|
|
||||||
"track",
|
|
||||||
}
|
|
||||||
totaltrackFields = []string{
|
|
||||||
"TOTALTRACKS",
|
|
||||||
"TRACKC",
|
|
||||||
"TRACKTOTAL",
|
|
||||||
"totaltracks",
|
|
||||||
"trackc",
|
|
||||||
"tracktotal",
|
|
||||||
}
|
|
||||||
discFields = []string{
|
|
||||||
"DISC",
|
|
||||||
"disc",
|
|
||||||
}
|
|
||||||
totaldiscFields = []string{
|
|
||||||
"DISCC",
|
|
||||||
"DISCTOTAL",
|
|
||||||
"TOTALDISCS",
|
|
||||||
"discc",
|
|
||||||
"disctotal",
|
|
||||||
"totaldiscs",
|
|
||||||
}
|
|
||||||
lyricsFields = []string{
|
|
||||||
"LYRICS",
|
|
||||||
"lyrics",
|
|
||||||
}
|
|
||||||
commentFields = []string{
|
|
||||||
"COMMENT",
|
|
||||||
"COMMENTS",
|
|
||||||
"comment",
|
|
||||||
"comments",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func firstExisting(keys *[]string, map_ *map[string]string) string {
|
|
||||||
for _, field := range *keys {
|
|
||||||
v, ok := (*map_)[field]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type Metadata interface {
|
|
||||||
Title() string
|
|
||||||
Album() string
|
|
||||||
Artist() string
|
|
||||||
AlbumArtist() string
|
|
||||||
Composer() string
|
|
||||||
Genre() string
|
|
||||||
Year() int
|
|
||||||
Track() int
|
|
||||||
TotalTracks() int
|
|
||||||
Disc() int
|
|
||||||
TotalDiscs() int
|
|
||||||
Lyrics() string
|
|
||||||
Comment() string
|
|
||||||
Length() float64
|
|
||||||
Format() string
|
|
||||||
Bitrate() int
|
|
||||||
}
|
|
||||||
|
|
||||||
type Track struct {
|
|
||||||
format *probeFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Title() string {
|
|
||||||
return firstExisting(&titleFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Album() string {
|
|
||||||
return firstExisting(&albumFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Artist() string {
|
|
||||||
return firstExisting(&artistFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) AlbumArtist() string {
|
|
||||||
return firstExisting(&albumArtistFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Composer() string {
|
|
||||||
return firstExisting(&composerFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Genre() string {
|
|
||||||
return firstExisting(&genreFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Year() int {
|
|
||||||
i, _ := strconv.Atoi(firstExisting(&yearFields, &t.format.Tags))
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Track() int {
|
|
||||||
i, _ := strconv.Atoi(firstExisting(&trackFields, &t.format.Tags))
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) TotalTracks() int {
|
|
||||||
i, _ := strconv.Atoi(firstExisting(&totaltrackFields, &t.format.Tags))
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Disc() int {
|
|
||||||
i, _ := strconv.Atoi(firstExisting(&discFields, &t.format.Tags))
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) TotalDiscs() int {
|
|
||||||
i, _ := strconv.Atoi(firstExisting(&totaldiscFields, &t.format.Tags))
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Lyrics() string {
|
|
||||||
return firstExisting(&lyricsFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Comment() string {
|
|
||||||
return firstExisting(&commentFields, &t.format.Tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Format() string {
|
|
||||||
return t.format.FormatName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Length() float64 {
|
|
||||||
return t.format.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Track) Bitrate() int {
|
|
||||||
return t.format.Bitrate
|
|
||||||
}
|
|
||||||
|
|
||||||
func Read(filename string) (Metadata, error) {
|
|
||||||
command := exec.Command(
|
|
||||||
"ffprobe",
|
|
||||||
"-print_format", "json",
|
|
||||||
"-show_format",
|
|
||||||
filename,
|
|
||||||
)
|
|
||||||
probe, err := command.Output()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("when running ffprobe with `%s`: %v",
|
|
||||||
filename, err)
|
|
||||||
}
|
|
||||||
var data probeData
|
|
||||||
err = json.Unmarshal(probe, &data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("when unmarshalling: %v", err)
|
|
||||||
}
|
|
||||||
track := Track{
|
|
||||||
format: data.Format,
|
|
||||||
}
|
|
||||||
return &track, nil
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package tags
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRead(t *testing.T) {
|
|
||||||
start := time.Now()
|
|
||||||
cwd, _ := os.Getwd()
|
|
||||||
data, err := Read(
|
|
||||||
fmt.Sprintf("%s/static/test_flac", cwd),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("when reading tags: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
length := data.Length()
|
|
||||||
if length != 160.4 {
|
|
||||||
t.Errorf("expected length to be `160.4`, got %f", length)
|
|
||||||
}
|
|
||||||
bitrate := data.Bitrate()
|
|
||||||
if bitrate != 815694 {
|
|
||||||
t.Errorf("expected bitrate to be `815694`, got %d", bitrate)
|
|
||||||
}
|
|
||||||
format := data.Format()
|
|
||||||
if format != "flac" {
|
|
||||||
t.Errorf("expected format to be `flac`, got %s", format)
|
|
||||||
}
|
|
||||||
fmt.Println(data.Title())
|
|
||||||
fmt.Println(data.Album())
|
|
||||||
fmt.Println(data.AlbumArtist())
|
|
||||||
fmt.Println(data.Year())
|
|
||||||
fmt.Printf("it's been %s\n", time.Since(start))
|
|
||||||
}
|
|
||||||
@@ -21,9 +21,9 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
{{ if .CurrentLastFMAPIKey }}
|
{{ if .CurrentLastFMAPIKey }}
|
||||||
{{ if .User.LastFMSession }}
|
{{ if .User.LastFMSession }}
|
||||||
<span class="light">current status</span> linked
|
<span class="light">current status</span> <span class="happy">linked</span> <a href="/admin/unlink_lastfm_do">unlink</a><br/>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<span class="light">current status</span> unlinked <a href="https://www.last.fm/api/auth/?api_key={{ .CurrentLastFMAPIKey }}&cb={{ .RequestRoot }}/admin/link_lastfm_callback">link</a><br/>
|
<span class="light">current status</span> <span class="angry">unlinked</span> <a href="https://www.last.fm/api/auth/?api_key={{ .CurrentLastFMAPIKey }}&cb={{ .RequestRoot }}/admin/link_lastfm_do">link</a><br/>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ else if not .User.IsAdmin }}
|
{{ else if not .User.IsAdmin }}
|
||||||
<span class="light">api key not set. please ask your admin to set it</span>
|
<span class="light">api key not set. please ask your admin to set it</span>
|
||||||
|
|||||||
6
vendor/github.com/twinj/uuid/.gitignore
generated
vendored
6
vendor/github.com/twinj/uuid/.gitignore
generated
vendored
@@ -1,6 +0,0 @@
|
|||||||
*.iml
|
|
||||||
/savers/*generator-*
|
|
||||||
/savers/generator-*
|
|
||||||
/vendor/
|
|
||||||
*.out
|
|
||||||
cover*.html
|
|
||||||
34
vendor/github.com/twinj/uuid/.travis.yml
generated
vendored
34
vendor/github.com/twinj/uuid/.travis.yml
generated
vendored
@@ -1,34 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
go_import_path: github.com/myesui/uuid
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.8
|
|
||||||
- 1.7
|
|
||||||
- 1.6
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- PATH=$HOME/gopath/bin:$PATH
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- gofmt -s -l -e .
|
|
||||||
- go vet ./...
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -t -v ./...
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v -race github.com/myesui/uuid
|
|
||||||
- go test -v -covermode=count -coverprofile=cover.out
|
|
||||||
- goveralls -coverprofile=cover.out -service=travis-ci
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email: true
|
|
||||||
20
vendor/github.com/twinj/uuid/LICENSE
generated
vendored
20
vendor/github.com/twinj/uuid/LICENSE
generated
vendored
@@ -1,20 +0,0 @@
|
|||||||
Copyright (C) 2011 by Krzysztof Kowalik <chris@nu7hat.ch>
|
|
||||||
Copyright (C) 2016 by Daniel Kemp <twinj@github.com> Derivative work
|
|
||||||
|
|
||||||
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.
|
|
||||||
311
vendor/github.com/twinj/uuid/README.md
generated
vendored
311
vendor/github.com/twinj/uuid/README.md
generated
vendored
@@ -1,311 +0,0 @@
|
|||||||
Go UUID implementation
|
|
||||||
========================
|
|
||||||
|
|
||||||
[](https://raw.githubusercontent.com/myesui/uuid/master/LICENSE)
|
|
||||||
[](http://godoc.org/github.com/myesui/uuid)
|
|
||||||
[](https://ci.appveyor.com/project/myesui/uuid)
|
|
||||||
[](https://travis-ci.org/myesui/uuid)
|
|
||||||
[](https://coveralls.io/github/myesui/uuid?branch=master)
|
|
||||||
[](https://goreportcard.com/report/github.com/myesui/uuid)
|
|
||||||
|
|
||||||
This package provides RFC 4122 and DCE 1.1 compliant UUIDs.
|
|
||||||
It will generate the following:
|
|
||||||
|
|
||||||
* Version 1: based on a Timestamp and MAC address as Node id
|
|
||||||
* Version 2: based on DCE Security - Experimental
|
|
||||||
* Version 3: based on MD5 hash
|
|
||||||
* Version 4: based on cryptographically secure random numbers
|
|
||||||
* Version 5: based on SHA-1 hash
|
|
||||||
|
|
||||||
Functions NewV1, NewV2, NewV3, NewV4, NewV5, New, NewHex and Parse() for
|
|
||||||
generating version 1, 2, 3, 4 and 5 Uuid's
|
|
||||||
|
|
||||||
# Requirements
|
|
||||||
|
|
||||||
Will generally support last 3 versions of Go.
|
|
||||||
|
|
||||||
- 1.8
|
|
||||||
- 1.7
|
|
||||||
- 1.6
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Use the `go` tool:
|
|
||||||
|
|
||||||
$ go get gopkg.in/myesui/uuid.v1
|
|
||||||
|
|
||||||
|
|
||||||
See [gopkg.in](http://labix.org/gopkg.in)
|
|
||||||
|
|
||||||
# Typical Usage
|
|
||||||
|
|
||||||
See [documentation and examples](http://godoc.org/github.com/myesui/uuid)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
## All UUIDs
|
|
||||||
|
|
||||||
import "gopkg.in/myesui/uuid.v1"
|
|
||||||
|
|
||||||
id, _ := uuid.Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
|
||||||
|
|
||||||
if uuid.Equal(id, uuid.NameSpaceDNS) {
|
|
||||||
fmt.Println("Alas these are equal")
|
|
||||||
}
|
|
||||||
|
|
||||||
if uuid.Compare(id, uuid.NameSpaceDNS) == 0 {
|
|
||||||
fmt.Println("They are also equal")
|
|
||||||
}
|
|
||||||
|
|
||||||
if uuid.Compare(id, uuid.NameSpaceX500) == -1 {
|
|
||||||
fmt.Println("id < uuid.NameSpaceX500")
|
|
||||||
}
|
|
||||||
|
|
||||||
if uuid.Compare(uuid.NameSpaceX50, id) == 1 {
|
|
||||||
fmt.Println("uuid.NameSpaceX500 > id")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default Format is FormatCanonical
|
|
||||||
fmt.Println(uuid.Formatter(id, uuid.FormatCanonicalCurly))
|
|
||||||
|
|
||||||
uuid.SwitchFormat(uuid.FormatCanonicalBracket)
|
|
||||||
|
|
||||||
## Formatting UUIDs
|
|
||||||
|
|
||||||
The default format is uuid.FormatCanonical xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx
|
|
||||||
|
|
||||||
Any call to uuid.String() will produce this output.
|
|
||||||
|
|
||||||
The format is twice as fast as the others at producing a string from the bytes.
|
|
||||||
|
|
||||||
To change to another format permanently use:
|
|
||||||
|
|
||||||
uuid.SwitchFormat(uuid.Format*)
|
|
||||||
uuid.SwitchFormatToUpper(uuid.Format*)
|
|
||||||
|
|
||||||
Once this has been called in an init function all UUID.String() calls will use the new format.
|
|
||||||
|
|
||||||
Available formats:
|
|
||||||
|
|
||||||
FormatHex = xxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
||||||
FormatHexCurly = {xxxxxxxxxxxxxxxxxxxxxxxxxx}
|
|
||||||
FormatHexBracket = (xxxxxxxxxxxxxxxxxxxxxxxxxx)
|
|
||||||
|
|
||||||
// This is the default format.
|
|
||||||
FormatCanonical Format = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx
|
|
||||||
|
|
||||||
FormatCanonicalCurly = {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx}
|
|
||||||
FormatCanonicalBracket = (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx)
|
|
||||||
FormatUrn = urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx
|
|
||||||
|
|
||||||
The uuid.Formatter function also exists and is only ever meant to be used for one time prints where a different format from the default or switched format is required. You can supply your own format as long as it fits the pattern.
|
|
||||||
which contains %x for the five groups in an UUID. Eg: FormatCanonical = %x-%x-%x-%x-%x
|
|
||||||
|
|
||||||
You can also stwict to a custom format
|
|
||||||
|
|
||||||
Note: AT this time cutsom formats are not supported for TextMarshalling. If a custom format is deteced it will use the canonical format. Use a call to String() and save as a string instead.
|
|
||||||
|
|
||||||
## Version 1 and 2 UUIDs
|
|
||||||
|
|
||||||
import "gopkg.in/myesui/uuid.v1"
|
|
||||||
|
|
||||||
id := uuid.NewV1()
|
|
||||||
fmt.Println(id)
|
|
||||||
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
|
|
||||||
|
|
||||||
id = uuid.NewV2(uuid.DomainUser)
|
|
||||||
fmt.Println(id)
|
|
||||||
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
|
|
||||||
|
|
||||||
ids := uuid.BulkV1(500)
|
|
||||||
for _, v := range ids {
|
|
||||||
fmt.Println(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
ids = make([]UUID, 100)
|
|
||||||
ReadV1(ids)
|
|
||||||
for _, v := range ids {
|
|
||||||
fmt.Println(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you wish to register a saving mechanism to keep track of your UUIDs over restarts
|
|
||||||
// It is recommeneded to add a Saver so as to reduce risk in UUID collisions
|
|
||||||
saver := savers.FileSystemSaver.Init()
|
|
||||||
|
|
||||||
// Must be called before any V1 or V2 UUIDs. Do not call other uuid.Register* if
|
|
||||||
// registering a Saver
|
|
||||||
uuid.RegisterSaver(saver)
|
|
||||||
|
|
||||||
## Version 3 and 5 UUIDs
|
|
||||||
|
|
||||||
import "gopkg.in/myesui/uuid.v1"
|
|
||||||
|
|
||||||
id := uuid.NewV3(uuid.NameSpaceURL, uuid.Name("www.example.com"))
|
|
||||||
fmt.Println(id)
|
|
||||||
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
|
|
||||||
|
|
||||||
id := uuid.NewV5(uuid.NameSpaceURL, uuid.Name("www.example.com"))
|
|
||||||
fmt.Println(id)
|
|
||||||
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
|
|
||||||
|
|
||||||
id = uuid.NewV5(uuid.NameSpaceURL, id)
|
|
||||||
fmt.Println(id)
|
|
||||||
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
|
|
||||||
|
|
||||||
## Version 4 UUIDs
|
|
||||||
|
|
||||||
import "gopkg.in/myesui/uuid.v1"
|
|
||||||
|
|
||||||
// A V4 UUID will panic by default if the systems CPRNG fails - this can
|
|
||||||
// be changed by registering your own generator
|
|
||||||
u4 := uuid.NewV4()
|
|
||||||
fmt.Println(id)
|
|
||||||
fmt.Printf("version %d variant %x: %s\n", u4.Version(), u4.Variant(), u4)
|
|
||||||
|
|
||||||
ids := uuid.BulkV4(500)
|
|
||||||
for _, v := range ids {
|
|
||||||
fmt.Println(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
ids := make([]UUID, 100)
|
|
||||||
ReadV4(ids)
|
|
||||||
for _, v := range ids {
|
|
||||||
fmt.Println(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
## Custom Generators
|
|
||||||
|
|
||||||
import "gopkg.in/myesui/uuid.v1"
|
|
||||||
|
|
||||||
// Improve resolution for V1 and 2 UUIDs
|
|
||||||
// The resolution correlates to how many ids can be created before waiting
|
|
||||||
// for the next unique timestamp. The default is a low 1024, this equates
|
|
||||||
// to Ids that can be created in 100 nanoseconds. It is low to encourage
|
|
||||||
// you to set it.
|
|
||||||
uuid.RegisterGenerator(&GeneratorConfig{Resolution: 18465})
|
|
||||||
|
|
||||||
// Provide your own node Id or MAC address
|
|
||||||
uuid.RegisterGenerator(&GeneratorConfig{
|
|
||||||
Id: func() uuid.Node{
|
|
||||||
// My Node Id
|
|
||||||
// If this returns nil a random one will be generated
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Replace the default Timestamp spinner with your own.
|
|
||||||
uuid.RegisterGenerator(&GeneratorConfig{
|
|
||||||
Next: func()(uuid.Timestamp){
|
|
||||||
// My own Timestamp function...
|
|
||||||
// Resolution will become reduendant if you set this.
|
|
||||||
// The package will increment the clock sequence if you produce equal Timestamps
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Replace the default crypto/rand.Read CPRNG with your own.
|
|
||||||
uuid.RegisterGenerator(&GeneratorConfig{
|
|
||||||
Random: func([]byte)(int, error){
|
|
||||||
// My CPRNG function...
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// type HandleRandomError func([]byte, int, error) error
|
|
||||||
|
|
||||||
// Replace the default random number error handler for V4 UUIDs. This function is called
|
|
||||||
// when there is an error in the crypto/rand CPRNG. The default error handler function reads
|
|
||||||
// from math.Rand as a fallback.
|
|
||||||
//
|
|
||||||
// You can change that behaviour and handle the error by providing your own function.
|
|
||||||
//
|
|
||||||
// Errors could be due to a lack of system entropy or some other serious issue. These issues are rare,
|
|
||||||
// however, having the tools to handle such issues is important.
|
|
||||||
// This approach was taken as each user of this package will want to handle this differently.
|
|
||||||
//
|
|
||||||
// For example one user of the package might want to just panic instead.
|
|
||||||
// Returning an error will cause a panic.
|
|
||||||
|
|
||||||
uuid.RegisterGenerator(&GeneratorConfig{
|
|
||||||
HandleError: func(id []byte, n int, err error)bool{
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// You can also just generate your own completely.
|
|
||||||
myGenerator := NewGenerator(nil)
|
|
||||||
|
|
||||||
id := myGenerator.NewV4()
|
|
||||||
|
|
||||||
// You can replace the logger
|
|
||||||
uuid.RegisterGenerator(&GeneratorConfig{
|
|
||||||
Logger: log.New(someWriter, "my-prefix", myFlags),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
## Coverage
|
|
||||||
|
|
||||||
* go test -coverprofile cover.out github.com/myesui/uuid
|
|
||||||
* go test -coverprofile cover.out github.com/myesui/uuid/savers
|
|
||||||
|
|
||||||
* go tool cover -html=cover.out -o cover.html
|
|
||||||
|
|
||||||
## Contribution
|
|
||||||
|
|
||||||
1. fork from the *master* branch to create your own fork
|
|
||||||
2. clone from *master* into $GOPATH/src/github.com/myesui/uuid
|
|
||||||
3. git remote add `username` https://github.com/username/uuid.git
|
|
||||||
4. push changes on your fork and track your remote
|
|
||||||
5. Remember to create a branch
|
|
||||||
|
|
||||||
To ensure you get the correct packages and subpackages install in a gopath which matches *go/src/github.com/myesui/uuid*
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [RFC 4122](http://www.ietf.org/rfc/rfc4122.txt)
|
|
||||||
* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9629399/apdxa.htm)
|
|
||||||
|
|
||||||
# Design considerations
|
|
||||||
|
|
||||||
* UUID is an interface which correlates to
|
|
||||||
|
|
||||||
* V1 UUIDs are sequential. This can cause the Generator to work
|
|
||||||
more slowly compared to other implementations. It can however be manually tuned
|
|
||||||
to have performance that is on par. This is achieved by setting the Timestamp
|
|
||||||
Resolution. Benchmark tests have been provided to help determine the best
|
|
||||||
setting for your server
|
|
||||||
|
|
||||||
Proper test coverage has determined thant the UUID timestamp spinner works
|
|
||||||
correctly, across multiple clock resolutions. The generator produces
|
|
||||||
timestamps that roll out sequentially and will only modify the clock
|
|
||||||
sequence on very rare circumstances.
|
|
||||||
|
|
||||||
It is highly recommended that you register a uuid.Saver if you use V1 or V2
|
|
||||||
UUIDs as it will ensure a higher probability of uniqueness.
|
|
||||||
|
|
||||||
Example V1 output:
|
|
||||||
5fb1a280-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a281-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a282-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a283-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a284-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a285-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a286-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a287-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a288-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a289-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a28a-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a28b-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a28c-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a28d-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a28e-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a28f-30f0-11e6-9614-005056c00001
|
|
||||||
5fb1a290-30f0-11e6-9614-005056c00001
|
|
||||||
|
|
||||||
* The V1 UUID generator should be file system and server agnostic
|
|
||||||
To achieve this there are:
|
|
||||||
** No Os locking threads or file system dependant storage
|
|
||||||
** Provided the uuid.Saver interface so a package can implement its own solution if required
|
|
||||||
* The V4 UUID should allow a package to handle any error that can occur in the CPRNG. The default is to read from math.Rand``````.
|
|
||||||
* The package should be able to handle multiple instances of Generators so a package can produce UUIDs from multiple sources.
|
|
||||||
|
|
||||||
## Copyright
|
|
||||||
|
|
||||||
Copyright (C) 2017 myesui@github.com
|
|
||||||
See [LICENSE](https://github.com/myesui/uuid/tree/master/LICENSE) file for details.
|
|
||||||
30
vendor/github.com/twinj/uuid/appveyor.yml
generated
vendored
30
vendor/github.com/twinj/uuid/appveyor.yml
generated
vendored
@@ -1,30 +0,0 @@
|
|||||||
# version format
|
|
||||||
version: "{build}"
|
|
||||||
|
|
||||||
# Operating system (build VM template)
|
|
||||||
os: Windows Server 2012 R2
|
|
||||||
|
|
||||||
clone_folder: c:\gopath\src\github.com\myesui\uuid
|
|
||||||
|
|
||||||
# environment variables
|
|
||||||
environment:
|
|
||||||
GOPATH: c:\gopath
|
|
||||||
|
|
||||||
# scripts that run after cloning repository
|
|
||||||
install:
|
|
||||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
|
||||||
- go version
|
|
||||||
- go env
|
|
||||||
- go get -t -v ./...
|
|
||||||
|
|
||||||
# to run your custom scripts instead of automatic MSBuild
|
|
||||||
build_script:
|
|
||||||
- go vet -x ./...
|
|
||||||
- gofmt -s -l -e .
|
|
||||||
- go test ./... -v -short -race
|
|
||||||
|
|
||||||
# to disable automatic tests
|
|
||||||
test: off
|
|
||||||
|
|
||||||
# to disable deployment
|
|
||||||
deploy: off
|
|
||||||
161
vendor/github.com/twinj/uuid/format.go
generated
vendored
161
vendor/github.com/twinj/uuid/format.go
generated
vendored
@@ -1,161 +0,0 @@
|
|||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Format represents different styles a UUID can be printed in constants
|
|
||||||
// represent a pattern used by the package with which to print a UUID.
|
|
||||||
type Format string
|
|
||||||
|
|
||||||
// The following are the default Formats supplied by the uuid package.
|
|
||||||
const (
|
|
||||||
FormatHex Format = "%x%x%x%x%x"
|
|
||||||
FormatHexCurly Format = "{%x%x%x%x%x}"
|
|
||||||
FormatHexBracket Format = "(%x%x%x%x%x)"
|
|
||||||
|
|
||||||
// FormatCanonical is the default format.
|
|
||||||
FormatCanonical Format = "%x-%x-%x-%x-%x"
|
|
||||||
|
|
||||||
FormatCanonicalCurly Format = "{%x-%x-%x-%x-%x}"
|
|
||||||
FormatCanonicalBracket Format = "(%x-%x-%x-%x-%x)"
|
|
||||||
FormatUrn Format = "urn:uuid:" + FormatCanonical
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
printFormat = FormatCanonical
|
|
||||||
defaultFormats map[Format]bool = make(map[Format]bool)
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
defaultFormats[FormatHex] = true
|
|
||||||
defaultFormats[FormatHexCurly] = true
|
|
||||||
defaultFormats[FormatHexBracket] = true
|
|
||||||
defaultFormats[FormatCanonical] = true
|
|
||||||
defaultFormats[FormatCanonicalCurly] = true
|
|
||||||
defaultFormats[FormatCanonicalBracket] = true
|
|
||||||
defaultFormats[FormatUrn] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwitchFormat switches the default printing format for ALL UUIDs.
|
|
||||||
//
|
|
||||||
// The default is canonical uuid.Format.FormatCanonical which has been
|
|
||||||
// optimised for use with this package. It is twice as fast compared to other
|
|
||||||
// formats. However, non package formats are still very quick.
|
|
||||||
//
|
|
||||||
// A valid format will have 5 groups of [%x|%X] or follow the pattern,
|
|
||||||
// *%[xX]*%[xX]*%[xX]*%[xX]*%[xX]*. If the supplied format does not meet this
|
|
||||||
// standard the function will panic. Note any extra uses of [%] outside of the
|
|
||||||
// [%x|%X] will also cause a panic.
|
|
||||||
// Constant uuid.Formats have been provided for most likely formats.
|
|
||||||
func SwitchFormat(form Format) {
|
|
||||||
checkFormat(form)
|
|
||||||
printFormat = form
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwitchFormatToUpper is a convenience function to set the uuid.Format to uppercase
|
|
||||||
// versions.
|
|
||||||
func SwitchFormatToUpper(form Format) {
|
|
||||||
SwitchFormat(Format(strings.ToUpper(string(form))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatter will return a string representation of the given UUID.
|
|
||||||
//
|
|
||||||
// Use this for one time formatting when setting the Format using
|
|
||||||
// uuid.SwitchFormat would be overkill.
|
|
||||||
//
|
|
||||||
// A valid format will have 5 groups of [%x|%X] or follow the pattern,
|
|
||||||
// *%[xX]*%[xX]*%[xX]*%[xX]*%[xX]*. If the supplied format does not meet this
|
|
||||||
// standard the function will panic. Note any extra uses of [%] outside of the
|
|
||||||
// [%x|%X] will also cause a panic.
|
|
||||||
func Formatter(id Implementation, form Format) string {
|
|
||||||
checkFormat(form)
|
|
||||||
return formatUuid(id.Bytes(), form)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkFormat(form Format) {
|
|
||||||
if defaultFormats[form] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := strings.ToLower(string(form))
|
|
||||||
if strings.Count(s, "%x") != 5 {
|
|
||||||
panic("uuid: invalid format")
|
|
||||||
}
|
|
||||||
s = strings.Replace(s, "%x", "", -1)
|
|
||||||
if strings.Count(s, "%") > 0 {
|
|
||||||
panic("uuid: invalid format")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
hexTable = "0123456789abcdef"
|
|
||||||
hexUpperTable = "0123456789ABCDEF"
|
|
||||||
|
|
||||||
canonicalLength = length*2 + 4
|
|
||||||
formatArgCount = 10
|
|
||||||
uuidStringBufferSize = length*2 - formatArgCount
|
|
||||||
)
|
|
||||||
|
|
||||||
var groups = [...]int{4, 2, 2, 2, 6}
|
|
||||||
|
|
||||||
func formatUuid(src []byte, form Format) string {
|
|
||||||
if form == FormatCanonical {
|
|
||||||
return string(formatCanonical(src))
|
|
||||||
}
|
|
||||||
return string(format(src, string(form)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func format(src []byte, form string) []byte {
|
|
||||||
end := len(form)
|
|
||||||
buf := make([]byte, end+uuidStringBufferSize)
|
|
||||||
|
|
||||||
var s, ls, b, e, p int
|
|
||||||
var u bool
|
|
||||||
for _, v := range groups {
|
|
||||||
ls = s
|
|
||||||
for ; s < end && form[s] != '%'; s++ {
|
|
||||||
}
|
|
||||||
copy(buf[p:], form[ls:s])
|
|
||||||
p += s - ls
|
|
||||||
s++
|
|
||||||
u = form[s] == 'X'
|
|
||||||
s++
|
|
||||||
e = b + v
|
|
||||||
for i, t := range src[b:e] {
|
|
||||||
j := p + i + i
|
|
||||||
table := hexTable
|
|
||||||
if u {
|
|
||||||
table = hexUpperTable
|
|
||||||
}
|
|
||||||
buf[j] = table[t>>4]
|
|
||||||
buf[j+1] = table[t&0x0f]
|
|
||||||
}
|
|
||||||
b = e
|
|
||||||
p += v + v
|
|
||||||
}
|
|
||||||
ls = s
|
|
||||||
for ; s < end && form[s] != '%'; s++ {
|
|
||||||
}
|
|
||||||
copy(buf[p:], form[ls:s])
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatCanonical(src []byte) []byte {
|
|
||||||
buf := make([]byte, canonicalLength)
|
|
||||||
var b, p, e int
|
|
||||||
for h, v := range groups {
|
|
||||||
e = b + v
|
|
||||||
for i, t := range src[b:e] {
|
|
||||||
j := p + i + i
|
|
||||||
buf[j] = hexTable[t>>4]
|
|
||||||
buf[j+1] = hexTable[t&0x0f]
|
|
||||||
}
|
|
||||||
b = e
|
|
||||||
p += v + v
|
|
||||||
if h < 4 {
|
|
||||||
buf[p] = '-'
|
|
||||||
p += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
527
vendor/github.com/twinj/uuid/generator.go
generated
vendored
527
vendor/github.com/twinj/uuid/generator.go
generated
vendored
@@ -1,527 +0,0 @@
|
|||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"log"
|
|
||||||
mrand "math/rand"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
once = new(sync.Once)
|
|
||||||
generator = newGenerator(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
seed := time.Now().UTC().UnixNano()
|
|
||||||
b := [8]byte{}
|
|
||||||
_, err := rand.Read(b[:])
|
|
||||||
if err == nil {
|
|
||||||
seed += int64(binary.BigEndian.Uint64(b[:]))
|
|
||||||
}
|
|
||||||
mrand.Seed(seed)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Random provides a random number generator which reads into the given []byte, the package
|
|
||||||
// uses crypto/rand.Read by default. You can supply your own implementation or downgrade it
|
|
||||||
// to the match/rand package.
|
|
||||||
//
|
|
||||||
// The function is used by V4 UUIDs and for setting up V1 and V2 UUIDs in the
|
|
||||||
// Generator Init or Register* functions.
|
|
||||||
type Random func([]byte) (int, error)
|
|
||||||
|
|
||||||
// Next provides the next Timestamp value to be used by the next V1 or V2 UUID.
|
|
||||||
// The default uses the uuid.spinner which spins at a resolution of
|
|
||||||
// 100ns ticks and provides a spin resolution redundancy of 1024
|
|
||||||
// cycles. This ensures that the system is not too quick when
|
|
||||||
// generating V1 or V2 UUIDs. Each system requires a tuned Resolution to
|
|
||||||
// enhance performance.
|
|
||||||
type Next func() Timestamp
|
|
||||||
|
|
||||||
// Identifier provides the Node to be used during the life of a
|
|
||||||
// uuid.Generator. If it cannot be determined nil should be returned, the
|
|
||||||
// package will then provide a node identifier provided by the Random function.
|
|
||||||
// The default generator gets a MAC address from the first interface that is 'up' checking
|
|
||||||
// net.FlagUp.
|
|
||||||
type Identifier func() Node
|
|
||||||
|
|
||||||
// HandleRandomError provides the ability to manage a serious error that may be
|
|
||||||
// caused by accessing the standard crypto/rand library or the supplied uuid/Random
|
|
||||||
// function. Due to the rarity of this occurrence the error is swallowed by the
|
|
||||||
// uuid/NewV4 function which relies heavily on random numbers, the package will
|
|
||||||
// panic instead if an error occurs.
|
|
||||||
//
|
|
||||||
// You can change this behaviour by passing in your own uuid/HandleError
|
|
||||||
// function to a custom Generator. This function can attempt to fix the random
|
|
||||||
// number generator. If your uuid/HandleError returns true the generator will
|
|
||||||
// attempt to generate another V4 uuid. If another error occurs the function
|
|
||||||
// will return a fallback v4 uuid generated from the less random math/rand standard library.
|
|
||||||
//
|
|
||||||
// Waiting for system entropy may be all that is required in the initial error.
|
|
||||||
// If something more serious has occurred, handle appropriately using this function.
|
|
||||||
type HandleRandomError func([]byte, int, error) error
|
|
||||||
|
|
||||||
// Generator is used to create and monitor the running of V1 and V2, and V4
|
|
||||||
// UUIDs. It can be setup to take custom implementations for Timestamp, Node
|
|
||||||
// and Random number retrieval by providing those functions as required.
|
|
||||||
// You can also supply a uuid/Saver implementation for saving the state of the generator
|
|
||||||
// and you can also provide an error policy for V4 UUIDs and possible errors in the random
|
|
||||||
// number generator.
|
|
||||||
type Generator struct {
|
|
||||||
// Access to the store needs to be maintained
|
|
||||||
sync.Mutex
|
|
||||||
|
|
||||||
// Once ensures that the generator is only setup and initialised once.
|
|
||||||
// This will occur either when you explicitly call the
|
|
||||||
// uuid.Generator.Init function or when a V1 or V2 id is generated.
|
|
||||||
sync.Once
|
|
||||||
|
|
||||||
// Store contains the current values being used by the Generator.
|
|
||||||
*Store
|
|
||||||
|
|
||||||
// Identifier as per the type Identifier func() Node
|
|
||||||
Identifier
|
|
||||||
|
|
||||||
// HandleRandomError as per the type HandleError func(error) bool
|
|
||||||
HandleRandomError
|
|
||||||
|
|
||||||
// Next as per the type Next func() Timestamp
|
|
||||||
Next
|
|
||||||
|
|
||||||
// Random as per the type Random func([]byte) (int, error)
|
|
||||||
Random
|
|
||||||
|
|
||||||
// Saver provides a non-volatile store to save the state of the
|
|
||||||
// generator, the default is nil which will cause the timestamp
|
|
||||||
// clock sequence to populate with random data. You can register your
|
|
||||||
// own saver by using the uuid.RegisterSaver function or by creating
|
|
||||||
// your own uuid.Generator instance.
|
|
||||||
// UUIDs.
|
|
||||||
Saver
|
|
||||||
|
|
||||||
*log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// GeneratorConfig allows you to setup a new uuid.Generator using uuid.NewGenerator or RegisterGenerator. You can supply your own
|
|
||||||
// implementations for the random number generator Random, Identifier and Timestamp retrieval. You can also
|
|
||||||
// adjust the resolution of the default Timestamp spinner and supply your own
|
|
||||||
// error handler for crypto/rand failures.
|
|
||||||
type GeneratorConfig struct {
|
|
||||||
Saver
|
|
||||||
Next
|
|
||||||
Resolution uint
|
|
||||||
Identifier
|
|
||||||
Random
|
|
||||||
HandleRandomError
|
|
||||||
*log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGenerator will create a new uuid.Generator with the given functions.
|
|
||||||
func NewGenerator(config *GeneratorConfig) (*Generator, error) {
|
|
||||||
return onceDo(newGenerator(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
func onceDo(gen *Generator) (*Generator, error) {
|
|
||||||
var err error
|
|
||||||
gen.Do(func() {
|
|
||||||
err = gen.init()
|
|
||||||
if err != nil {
|
|
||||||
gen = nil
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return gen, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func newGenerator(config *GeneratorConfig) (gen *Generator) {
|
|
||||||
if config == nil {
|
|
||||||
config = new(GeneratorConfig)
|
|
||||||
}
|
|
||||||
gen = new(Generator)
|
|
||||||
if config.Next == nil {
|
|
||||||
if config.Resolution == 0 {
|
|
||||||
config.Resolution = defaultSpinResolution
|
|
||||||
}
|
|
||||||
gen.Next = (&spinner{
|
|
||||||
Resolution: config.Resolution,
|
|
||||||
Count: 0,
|
|
||||||
Timestamp: Now(),
|
|
||||||
now: Now,
|
|
||||||
}).next
|
|
||||||
} else {
|
|
||||||
gen.Next = config.Next
|
|
||||||
}
|
|
||||||
if config.Identifier == nil {
|
|
||||||
gen.Identifier = findFirstHardwareAddress
|
|
||||||
} else {
|
|
||||||
gen.Identifier = config.Identifier
|
|
||||||
}
|
|
||||||
if config.Random == nil {
|
|
||||||
gen.Random = rand.Read
|
|
||||||
} else {
|
|
||||||
gen.Random = config.Random
|
|
||||||
}
|
|
||||||
if config.HandleRandomError == nil {
|
|
||||||
gen.HandleRandomError = gen.runHandleError
|
|
||||||
} else {
|
|
||||||
gen.HandleRandomError = config.HandleRandomError
|
|
||||||
}
|
|
||||||
if config.Logger == nil {
|
|
||||||
gen.Logger = log.New(os.Stderr, "uuid: ", log.LstdFlags)
|
|
||||||
} else {
|
|
||||||
gen.Logger = config.Logger
|
|
||||||
}
|
|
||||||
gen.Saver = config.Saver
|
|
||||||
gen.Store = new(Store)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterGenerator will set the package generator with the given configuration
|
|
||||||
// Like uuid.Init this can only be called once. Any subsequent calls will have no
|
|
||||||
// effect. If you call this you do not need to call uuid.Init.
|
|
||||||
func RegisterGenerator(config *GeneratorConfig) (err error) {
|
|
||||||
notOnce := true
|
|
||||||
once.Do(func() {
|
|
||||||
generator, err = NewGenerator(config)
|
|
||||||
notOnce = false
|
|
||||||
return
|
|
||||||
})
|
|
||||||
if notOnce {
|
|
||||||
panic("uuid: Register* methods cannot be called more than once.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Generator) read() {
|
|
||||||
|
|
||||||
// Save the state (current timestamp, clock sequence, and node ID)
|
|
||||||
// back to the stable store
|
|
||||||
if o.Saver != nil {
|
|
||||||
defer o.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain a lock
|
|
||||||
o.Lock()
|
|
||||||
defer o.Unlock()
|
|
||||||
|
|
||||||
// Get the current time as a 60-bit count of 100-nanosecond intervals
|
|
||||||
// since 00:00:00.00, 15 October 1582.
|
|
||||||
now := o.Next()
|
|
||||||
|
|
||||||
// If the last timestamp is later than
|
|
||||||
// the current timestamp, increment the clock sequence value.
|
|
||||||
if now <= o.Timestamp {
|
|
||||||
o.Sequence++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the timestamp
|
|
||||||
o.Timestamp = now
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Generator) init() error {
|
|
||||||
// From a system-wide shared stable store (e.g., a file), read the
|
|
||||||
// UUID generator state: the values of the timestamp, clock sequence,
|
|
||||||
// and node ID used to generate the last UUID.
|
|
||||||
var (
|
|
||||||
storage Store
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
o.Lock()
|
|
||||||
defer o.Unlock()
|
|
||||||
|
|
||||||
if o.Saver != nil {
|
|
||||||
storage, err = o.Read()
|
|
||||||
if err != nil {
|
|
||||||
o.Saver = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current time as a 60-bit count of 100-nanosecond intervals
|
|
||||||
// since 00:00:00.00, 15 October 1582.
|
|
||||||
now := o.Next()
|
|
||||||
|
|
||||||
// Get the current node id
|
|
||||||
node := o.Identifier()
|
|
||||||
|
|
||||||
if node == nil {
|
|
||||||
o.Println("address error generating random node id")
|
|
||||||
|
|
||||||
node = make([]byte, 6)
|
|
||||||
n, err := o.Random(node)
|
|
||||||
if err != nil {
|
|
||||||
o.Printf("could not read random bytes into node - read [%d] %s", n, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Mark as randomly generated
|
|
||||||
node[0] |= 0x01
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the state was unavailable (e.g., non-existent or corrupted), or
|
|
||||||
// the saved node ID is different than the current node ID, generate
|
|
||||||
// a random clock sequence value.
|
|
||||||
if o.Saver == nil || !bytes.Equal(storage.Node, node) {
|
|
||||||
|
|
||||||
// 4.1.5. Clock Sequence https://www.ietf.org/rfc/rfc4122.txt
|
|
||||||
//
|
|
||||||
// For UUID version 1, the clock sequence is used to help avoid
|
|
||||||
// duplicates that could arise when the clock is set backwards in time
|
|
||||||
// or if the node ID changes.
|
|
||||||
//
|
|
||||||
// If the clock is set backwards, or might have been set backwards
|
|
||||||
// (e.g., while the system was powered off), and the UUID generator can
|
|
||||||
// not be sure that no UUIDs were generated with timestamps larger than
|
|
||||||
// the value to which the clock was set, then the clock sequence has to
|
|
||||||
// be changed. If the previous value of the clock sequence is known, it
|
|
||||||
// can just be incremented; otherwise it should be set to a random or
|
|
||||||
// high-quality pseudo-random value.
|
|
||||||
|
|
||||||
// The clock sequence MUST be originally (i.e., once in the lifetime of
|
|
||||||
// a system) initialized to a random number to minimize the correlation
|
|
||||||
// across systems. This provides maximum protection against node
|
|
||||||
// identifiers that may move or switch from system to system rapidly.
|
|
||||||
// The initial value MUST NOT be correlated to the node identifier.
|
|
||||||
b := make([]byte, 2)
|
|
||||||
n, err := o.Random(b)
|
|
||||||
if err == nil {
|
|
||||||
storage.Sequence = Sequence(binary.BigEndian.Uint16(b))
|
|
||||||
o.Printf("initialised random sequence [%d]", storage.Sequence)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
o.Printf("could not read random bytes into sequence - read [%d] %s", n, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if now < storage.Timestamp {
|
|
||||||
// If the state was available, but the saved timestamp is later than
|
|
||||||
// the current timestamp, increment the clock sequence value.
|
|
||||||
storage.Sequence++
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.Timestamp = now
|
|
||||||
storage.Node = node
|
|
||||||
|
|
||||||
o.Store = &storage
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Generator) save() {
|
|
||||||
func(state *Generator) {
|
|
||||||
if state.Saver != nil {
|
|
||||||
state.Lock()
|
|
||||||
defer state.Unlock()
|
|
||||||
state.Save(*state.Store)
|
|
||||||
}
|
|
||||||
}(o)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV1 generates a new RFC4122 version 1 UUID based on a 60 bit timestamp and
|
|
||||||
// node id.
|
|
||||||
func (o *Generator) NewV1() UUID {
|
|
||||||
o.read()
|
|
||||||
id := UUID{}
|
|
||||||
|
|
||||||
makeUuid(&id,
|
|
||||||
uint32(o.Timestamp),
|
|
||||||
uint16(o.Timestamp>>32),
|
|
||||||
uint16(o.Timestamp>>48),
|
|
||||||
uint16(o.Sequence),
|
|
||||||
o.Node)
|
|
||||||
|
|
||||||
id.setRFC4122Version(VersionOne)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadV1 will read a slice of UUIDs. Be careful with the set amount.
|
|
||||||
func (o *Generator) ReadV1(ids []UUID) {
|
|
||||||
for i := range ids {
|
|
||||||
ids[i] = o.NewV1()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BulkV1 will return a slice of V1 UUIDs. Be careful with the set amount.
|
|
||||||
func (o *Generator) BulkV1(amount int) []UUID {
|
|
||||||
ids := make([]UUID, amount)
|
|
||||||
o.ReadV1(ids)
|
|
||||||
return ids
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV2 generates a new DCE version 2 UUID based on a 60 bit timestamp, node id
|
|
||||||
// and the id of the given Id type.
|
|
||||||
func (o *Generator) NewV2(idType SystemId) UUID {
|
|
||||||
o.read()
|
|
||||||
|
|
||||||
id := UUID{}
|
|
||||||
|
|
||||||
var osId uint32
|
|
||||||
|
|
||||||
switch idType {
|
|
||||||
case SystemIdUser:
|
|
||||||
osId = uint32(os.Getuid())
|
|
||||||
case SystemIdGroup:
|
|
||||||
osId = uint32(os.Getgid())
|
|
||||||
case SystemIdEffectiveUser:
|
|
||||||
osId = uint32(os.Geteuid())
|
|
||||||
case SystemIdEffectiveGroup:
|
|
||||||
osId = uint32(os.Getegid())
|
|
||||||
case SystemIdCallerProcess:
|
|
||||||
osId = uint32(os.Getpid())
|
|
||||||
case SystemIdCallerProcessParent:
|
|
||||||
osId = uint32(os.Getppid())
|
|
||||||
}
|
|
||||||
|
|
||||||
makeUuid(&id,
|
|
||||||
osId,
|
|
||||||
uint16(o.Timestamp>>32),
|
|
||||||
uint16(o.Timestamp>>48),
|
|
||||||
uint16(o.Sequence),
|
|
||||||
o.Node)
|
|
||||||
|
|
||||||
id[9] = byte(idType)
|
|
||||||
id.setRFC4122Version(VersionTwo)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV3 generates a new RFC4122 version 3 UUID based on the MD5 hash of a
|
|
||||||
// namespace UUID namespace Implementation UUID and one or more unique names.
|
|
||||||
func (o *Generator) NewV3(namespace Implementation, names ...interface{}) UUID {
|
|
||||||
id := UUID{}
|
|
||||||
id.unmarshal(digest(md5.New(), namespace.Bytes(), names...))
|
|
||||||
id.setRFC4122Version(VersionThree)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
func digest(hash hash.Hash, name []byte, names ...interface{}) []byte {
|
|
||||||
for _, v := range names {
|
|
||||||
switch t := v.(type) {
|
|
||||||
case string:
|
|
||||||
name = append(name, t...)
|
|
||||||
continue
|
|
||||||
case []byte:
|
|
||||||
name = append(name, t...)
|
|
||||||
continue
|
|
||||||
case *string:
|
|
||||||
name = append(name, (*t)...)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if s, ok := v.(fmt.Stringer); ok {
|
|
||||||
name = append(name, s.String()...)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("uuid: does not support type [%T] as a name for hashed UUIDs.", v))
|
|
||||||
}
|
|
||||||
hash.Write(name)
|
|
||||||
return hash.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV4 generates a cryptographically secure random RFC4122 version 4 UUID. If there is an error with the random
|
|
||||||
// number generator this will
|
|
||||||
func (o *Generator) NewV4() (id UUID) {
|
|
||||||
o.v4(&id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadV4 will read into a slice of UUIDs. Be careful with the set amount.
|
|
||||||
// Note: V4 UUIDs require sufficient entropy from the generator.
|
|
||||||
// If n == len(ids) err will be nil.
|
|
||||||
func (o *Generator) ReadV4(ids []UUID) {
|
|
||||||
for i := range ids {
|
|
||||||
id := UUID{}
|
|
||||||
o.v4(&id)
|
|
||||||
ids[i] = id
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BulkV4 will return a slice of V4 UUIDs. Be careful with the set amount.
|
|
||||||
// Note: V4 UUIDs require sufficient entropy from the generator.
|
|
||||||
// If n == len(ids) err will be nil.
|
|
||||||
func (o *Generator) BulkV4(amount int) []UUID {
|
|
||||||
ids := make([]UUID, amount)
|
|
||||||
o.ReadV4(ids)
|
|
||||||
return ids
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Generator) v4(id *UUID) {
|
|
||||||
n, err := o.Random(id[:])
|
|
||||||
if err != nil {
|
|
||||||
o.Printf("there was an error getting random bytes [%s]", err)
|
|
||||||
if err = o.HandleRandomError(id[:], n, err); err != nil {
|
|
||||||
panic(fmt.Sprintf("random number error - %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id.setRFC4122Version(VersionFour)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// NewV5 generates an RFC4122 version 5 UUID based on the SHA-1 hash of a
|
|
||||||
// namespace Implementation UUID and one or more unique names.
|
|
||||||
func (o *Generator) NewV5(namespace Implementation, names ...interface{}) UUID {
|
|
||||||
id := UUID{}
|
|
||||||
id.unmarshal(digest(sha1.New(), namespace.Bytes(), names...))
|
|
||||||
id.setRFC4122Version(VersionFive)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHash generate a UUID based on the given hash implementation. The hash will
|
|
||||||
// be of the given names. The version will be set to 0 for Unknown and the
|
|
||||||
// variant will be set to VariantFuture.
|
|
||||||
func (o *Generator) NewHash(hash hash.Hash, names ...interface{}) UUID {
|
|
||||||
id := UUID{}
|
|
||||||
id.unmarshal(digest(hash, []byte{}, names...))
|
|
||||||
id[versionIndex] &= 0x0f
|
|
||||||
id[versionIndex] |= uint8(0 << 4)
|
|
||||||
id[variantIndex] &= variantSet
|
|
||||||
id[variantIndex] |= VariantFuture
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeUuid(id *UUID, low uint32, mid, hiAndV, seq uint16, node Node) {
|
|
||||||
|
|
||||||
id[0] = byte(low >> 24)
|
|
||||||
id[1] = byte(low >> 16)
|
|
||||||
id[2] = byte(low >> 8)
|
|
||||||
id[3] = byte(low)
|
|
||||||
|
|
||||||
id[4] = byte(mid >> 8)
|
|
||||||
id[5] = byte(mid)
|
|
||||||
|
|
||||||
id[6] = byte(hiAndV >> 8)
|
|
||||||
id[7] = byte(hiAndV)
|
|
||||||
|
|
||||||
id[8] = byte(seq >> 8)
|
|
||||||
id[9] = byte(seq)
|
|
||||||
|
|
||||||
copy(id[10:], node)
|
|
||||||
}
|
|
||||||
|
|
||||||
func findFirstHardwareAddress() (node Node) {
|
|
||||||
interfaces, err := net.Interfaces()
|
|
||||||
if err == nil {
|
|
||||||
for _, i := range interfaces {
|
|
||||||
if i.Flags&net.FlagUp != 0 && bytes.Compare(i.HardwareAddr, nil) != 0 {
|
|
||||||
// Don't use random as we have a real address
|
|
||||||
node = Node(i.HardwareAddr)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Generator) runHandleError(id []byte, n int, err error) error {
|
|
||||||
o.Lock()
|
|
||||||
mrand.Read(id)
|
|
||||||
o.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
65
vendor/github.com/twinj/uuid/saver.go
generated
vendored
65
vendor/github.com/twinj/uuid/saver.go
generated
vendored
@@ -1,65 +0,0 @@
|
|||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sequence represents an iterated value to help ensure unique UUID generations
|
|
||||||
// values across the same domain, server restarts and clock issues.
|
|
||||||
type Sequence uint16
|
|
||||||
|
|
||||||
// Node represents the last node id setup used by the generator.
|
|
||||||
type Node []byte
|
|
||||||
|
|
||||||
// Store is used for storage of UUID generation history to ensure continuous
|
|
||||||
// running of the UUID generator between restarts and to monitor synchronicity
|
|
||||||
// while generating new V1 or V2 UUIDs.
|
|
||||||
type Store struct {
|
|
||||||
Timestamp
|
|
||||||
Sequence
|
|
||||||
Node
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a string representation of the Store.
|
|
||||||
func (o Store) String() string {
|
|
||||||
return fmt.Sprintf("Timestamp[%s]-Sequence[%d]-Node[%x]", o.Timestamp, o.Sequence, o.Node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Saver is an interface to setup a non volatile store within your system
|
|
||||||
// if you wish to use V1 and V2 UUIDs based on your node id and a constant time
|
|
||||||
// it is highly recommended to implement this.
|
|
||||||
// A default implementation has been provided. FileSystemStorage, the default
|
|
||||||
// behaviour of the package is to generate random sequences where a Saver is not
|
|
||||||
// specified.
|
|
||||||
type Saver interface {
|
|
||||||
// Read is run once, use this to setup your UUID state machine
|
|
||||||
// Read should also return the UUID state from the non volatile store
|
|
||||||
Read() (Store, error)
|
|
||||||
|
|
||||||
// Save saves the state to the non volatile store and is called only if
|
|
||||||
Save(Store)
|
|
||||||
|
|
||||||
// Init allows default setup of a new Saver
|
|
||||||
Init() Saver
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterSaver register's a uuid.Saver implementation to the default package
|
|
||||||
// uuid.Generator. If you wish to save the generator state, this function must
|
|
||||||
// be run before any calls to V1 or V2 UUIDs. uuid.RegisterSaver cannot be run
|
|
||||||
// in conjunction with uuid.Init. You may implement the uuid.Saver interface
|
|
||||||
// or use the provided uuid.Saver's from the uuid/savers package.
|
|
||||||
func RegisterSaver(saver Saver) (err error) {
|
|
||||||
notOnce := true
|
|
||||||
once.Do(func() {
|
|
||||||
generator.Lock()
|
|
||||||
generator.Saver = saver
|
|
||||||
generator.Unlock()
|
|
||||||
err = generator.init()
|
|
||||||
notOnce = false
|
|
||||||
return
|
|
||||||
})
|
|
||||||
if notOnce {
|
|
||||||
panic("uuid: Register* methods cannot be called more than once.")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
120
vendor/github.com/twinj/uuid/timestamp.go
generated
vendored
120
vendor/github.com/twinj/uuid/timestamp.go
generated
vendored
@@ -1,120 +0,0 @@
|
|||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
gregorianToUNIXOffset = 122192928e9
|
|
||||||
|
|
||||||
// set the following to the number of 100ns ticks of the actual
|
|
||||||
// resolution of your system's clock
|
|
||||||
defaultSpinResolution = 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
// Timestamp as per 4.1.4. Timestamp https://www.ietf.org/rfc/rfc4122.txt
|
|
||||||
//
|
|
||||||
// The timestamp is a 60-bit value. For UUID version 1, this is
|
|
||||||
//
|
|
||||||
// represented by Coordinated Universal Time (UTC) as a count of 100-
|
|
||||||
// nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of
|
|
||||||
// Gregorian reform to the Christian calendar).
|
|
||||||
//
|
|
||||||
// For systems that do not have UTC available, but do have the local
|
|
||||||
// time, they may use that instead of UTC, as long as they do so
|
|
||||||
// consistently throughout the system. However, this is not recommended
|
|
||||||
// since generating the UTC from local time only needs a time zone
|
|
||||||
// offset.
|
|
||||||
//
|
|
||||||
// For UUID version 3 or 5, the timestamp is a 60-bit value constructed
|
|
||||||
// from a name as described in Section 4.3.
|
|
||||||
//
|
|
||||||
// For UUID version 4, the timestamp is a randomly or pseudo-randomly
|
|
||||||
// generated 60-bit value, as described in Section 4.4.
|
|
||||||
type Timestamp uint64
|
|
||||||
|
|
||||||
// Now converts Unix formatted time to RFC4122 UUID formatted times
|
|
||||||
// UUID UTC base time is October 15, 1582.
|
|
||||||
// Unix base time is January 1, 1970.
|
|
||||||
// Converts time to 100 nanosecond ticks since epoch. Uses time.Now
|
|
||||||
func Now() Timestamp {
|
|
||||||
return Timestamp(time.Now().UnixNano()/100 + gregorianToUNIXOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time converts UUID Timestamp to UTC time.Time
|
|
||||||
// Note some higher clock resolutions will lose accuracy if above 100 ns ticks
|
|
||||||
func (o Timestamp) Time() time.Time {
|
|
||||||
return time.Unix(0, int64((o-gregorianToUNIXOffset)*100)).UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add returns the timestamp as modified by the duration
|
|
||||||
func (o Timestamp) Add(duration time.Duration) Timestamp {
|
|
||||||
return o + Timestamp(duration/100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sub returns the timestamp as modified by the duration
|
|
||||||
func (o Timestamp) Sub(duration time.Duration) Timestamp {
|
|
||||||
return o - Timestamp(duration/100)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String Converts UUID Timestamp to time.Time and then calls the Stringer
|
|
||||||
func (o Timestamp) String() string {
|
|
||||||
return o.Time().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4.2.1.2. System Clock Resolution https://www.ietf.org/rfc/rfc4122.txt
|
|
||||||
//
|
|
||||||
// The timestamp is generated from the system time, whose resolution may
|
|
||||||
// be less than the resolution of the UUID timestamp.
|
|
||||||
//
|
|
||||||
// If UUIDs do not need to be frequently generated, the timestamp can
|
|
||||||
// simply be the system time multiplied by the number of 100-nanosecond
|
|
||||||
// intervals per system time interval.
|
|
||||||
//
|
|
||||||
// If a system overruns the generator by requesting too many UUIDs
|
|
||||||
// within a single system time interval, the UUID service MUST either
|
|
||||||
// return an error, or stall the UUID generator until the system clock
|
|
||||||
// catches up.
|
|
||||||
//
|
|
||||||
// A high resolution timestamp can be simulated by keeping a count of
|
|
||||||
// the number of UUIDs that have been generated with the same value of
|
|
||||||
// the system time, and using it to construct the low order bits of the
|
|
||||||
// timestamp. The count will range between zero and the number of
|
|
||||||
// 100-nanosecond intervals per system time interval.
|
|
||||||
//
|
|
||||||
// Note: If the processors overrun the UUID generation frequently,
|
|
||||||
// additional node identifiers can be allocated to the system, which
|
|
||||||
// will permit higher speed allocation by making multiple UUIDs
|
|
||||||
// potentially available for each time stamp value.
|
|
||||||
|
|
||||||
type spinner struct {
|
|
||||||
// the amount of ids based on the Timestamp
|
|
||||||
Count, Resolution uint
|
|
||||||
|
|
||||||
// the tracked spin stamp
|
|
||||||
Timestamp
|
|
||||||
|
|
||||||
now func() Timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *spinner) next() Timestamp {
|
|
||||||
for {
|
|
||||||
now := o.now()
|
|
||||||
// if clock reading changed since last UUID generated
|
|
||||||
if o.Timestamp == now {
|
|
||||||
o.Count++
|
|
||||||
if o.Count == o.Resolution {
|
|
||||||
for o.now() < o.Timestamp+Timestamp(o.Resolution) {
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset count of UUIDs with this timestamp
|
|
||||||
o.Count = 0
|
|
||||||
o.Timestamp = now
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return o.Timestamp + Timestamp(o.Count)
|
|
||||||
}
|
|
||||||
175
vendor/github.com/twinj/uuid/types.go
generated
vendored
175
vendor/github.com/twinj/uuid/types.go
generated
vendored
@@ -1,175 +0,0 @@
|
|||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"fmt"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
length = 16
|
|
||||||
variantIndex = 8
|
|
||||||
versionIndex = 6
|
|
||||||
)
|
|
||||||
|
|
||||||
// **************************************************** Create UUIDs
|
|
||||||
|
|
||||||
func (o *UUID) unmarshal(data []byte) {
|
|
||||||
copy(o[:], data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the three most significant bits (bits 0, 1 and 2) of the
|
|
||||||
// sequenceHiAndVariant equivalent in the array to ReservedRFC4122.
|
|
||||||
func (o *UUID) setRFC4122Version(version Version) {
|
|
||||||
o[versionIndex] &= 0x0f
|
|
||||||
o[versionIndex] |= uint8(version << 4)
|
|
||||||
o[variantIndex] &= variantSet
|
|
||||||
o[variantIndex] |= VariantRFC4122
|
|
||||||
}
|
|
||||||
|
|
||||||
// **************************************************** Default implementation
|
|
||||||
|
|
||||||
var _ Implementation = &UUID{}
|
|
||||||
|
|
||||||
// UUID is the default RFC implementation. All uuid functions will return this
|
|
||||||
// type.
|
|
||||||
type UUID [length]byte
|
|
||||||
|
|
||||||
// Size returns the octet length of the Uuid
|
|
||||||
func (o UUID) Size() int {
|
|
||||||
return length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns the uuid.Version of the Uuid
|
|
||||||
func (o UUID) Version() Version {
|
|
||||||
return resolveVersion(o[versionIndex] >> 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variant returns the implementation variant of the Uuid
|
|
||||||
func (o UUID) Variant() uint8 {
|
|
||||||
return variant(o[variantIndex])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes return the underlying data representation of the Uuid.
|
|
||||||
func (o UUID) Bytes() []byte {
|
|
||||||
return o[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the canonical string representation of the UUID or the
|
|
||||||
// uuid.Format the package is set to via uuid.SwitchFormat
|
|
||||||
func (o UUID) String() string {
|
|
||||||
return formatUuid(o[:], printFormat)
|
|
||||||
}
|
|
||||||
|
|
||||||
// **************************************************** Implementations
|
|
||||||
|
|
||||||
// MarshalBinary implements the encoding.BinaryMarshaler interface
|
|
||||||
func (o UUID) MarshalBinary() ([]byte, error) {
|
|
||||||
return o.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
|
|
||||||
func (o *UUID) UnmarshalBinary(bytes []byte) error {
|
|
||||||
if len(bytes) != o.Size() {
|
|
||||||
return errors.New("uuid: invalid length")
|
|
||||||
}
|
|
||||||
o.unmarshal(bytes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText implements the encoding.TextMarshaler interface. It will marshal
|
|
||||||
// text into one of the known formats, if you have changed to a custom Format
|
|
||||||
// the text be output in canonical format.
|
|
||||||
func (o UUID) MarshalText() ([]byte, error) {
|
|
||||||
f := FormatCanonical
|
|
||||||
if defaultFormats[printFormat] {
|
|
||||||
f = printFormat
|
|
||||||
}
|
|
||||||
return []byte(formatUuid(o.Bytes(), f)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText implements the encoding.TextUnmarshaler interface. It will
|
|
||||||
// support any text that MarshalText can produce.
|
|
||||||
func (o *UUID) UnmarshalText(uuid []byte) error {
|
|
||||||
id, err := parse(string(uuid))
|
|
||||||
if err == nil {
|
|
||||||
o.UnmarshalBinary(id)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver.Valuer interface
|
|
||||||
func (o UUID) Value() (value driver.Value, err error) {
|
|
||||||
if IsNil(o) {
|
|
||||||
value, err = nil, nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value, err = o.MarshalText()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan implements the sql.Scanner interface
|
|
||||||
func (o *UUID) Scan(src interface{}) error {
|
|
||||||
if src == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if src == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch src := src.(type) {
|
|
||||||
|
|
||||||
case string:
|
|
||||||
return o.UnmarshalText([]byte(src))
|
|
||||||
|
|
||||||
case []byte:
|
|
||||||
if len(src) == length {
|
|
||||||
return o.UnmarshalBinary(src)
|
|
||||||
} else {
|
|
||||||
return o.UnmarshalText(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("uuid: cannot scan type [%T] into UUID", src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// **************************************************** Immutable UUID
|
|
||||||
|
|
||||||
var _ Implementation = new(Immutable)
|
|
||||||
|
|
||||||
// Immutable is an easy to use UUID which can be used as a key or for constants
|
|
||||||
type Immutable string
|
|
||||||
|
|
||||||
// Size returns the octet length of the Uuid
|
|
||||||
func (o Immutable) Size() int {
|
|
||||||
return length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns the uuid.Version of the Uuid
|
|
||||||
func (o Immutable) Version() Version {
|
|
||||||
return resolveVersion(o[versionIndex] >> 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variant returns the implementation variant of the Uuid
|
|
||||||
func (o Immutable) Variant() uint8 {
|
|
||||||
return variant(o[variantIndex])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes return the underlying data representation of the Uuid in network byte
|
|
||||||
// order
|
|
||||||
func (o Immutable) Bytes() []byte {
|
|
||||||
return []byte(o)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the canonical string representation of the UUID or the
|
|
||||||
// uuid.Format the package is set to via uuid.SwitchFormat
|
|
||||||
func (o Immutable) String() string {
|
|
||||||
return formatUuid([]byte(o), printFormat)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UUID converts this implementation to the default type uuid.UUID
|
|
||||||
func (o Immutable) UUID() UUID {
|
|
||||||
id := UUID{}
|
|
||||||
id.unmarshal(o.Bytes())
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
267
vendor/github.com/twinj/uuid/uuid.go
generated
vendored
267
vendor/github.com/twinj/uuid/uuid.go
generated
vendored
@@ -1,267 +0,0 @@
|
|||||||
// Package uuid provides RFC4122 and DCE 1.1 UUIDs.
|
|
||||||
//
|
|
||||||
// Use NewV1, NewV2, NewV3, NewV4, NewV5, for generating new UUIDs.
|
|
||||||
//
|
|
||||||
// Use New([]byte), NewHex(string), and Parse(string) for
|
|
||||||
// creating UUIDs from existing data.
|
|
||||||
//
|
|
||||||
// The original version was from Krzysztof Kowalik <chris@nu7hat.ch>
|
|
||||||
// Unfortunately, that version was non compliant with RFC4122.
|
|
||||||
//
|
|
||||||
// The package has since been redesigned.
|
|
||||||
//
|
|
||||||
// The example code in the specification was also used as reference for design.
|
|
||||||
//
|
|
||||||
// Copyright (C) 2016 myesui@github.com 2016 MIT licence
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"hash"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Nil represents an empty UUID.
|
|
||||||
const Nil Immutable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
||||||
|
|
||||||
// The following Immutable UUIDs are for use with V3 or V5 UUIDs.
|
|
||||||
|
|
||||||
const (
|
|
||||||
NameSpaceDNS Immutable = "k\xa7\xb8\x10\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
|
|
||||||
NameSpaceURL Immutable = "k\xa7\xb8\x11\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
|
|
||||||
NameSpaceOID Immutable = "k\xa7\xb8\x12\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
|
|
||||||
NameSpaceX500 Immutable = "k\xa7\xb8\x14\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SystemId denotes the type of id to retrieve from the operating system.
|
|
||||||
// That id is then used to create an identifier UUID.
|
|
||||||
type SystemId uint8
|
|
||||||
|
|
||||||
// The following SystemId's are for use with V2 UUIDs.
|
|
||||||
const (
|
|
||||||
SystemIdUser SystemId = iota + 1
|
|
||||||
SystemIdEffectiveUser
|
|
||||||
SystemIdGroup
|
|
||||||
SystemIdEffectiveGroup
|
|
||||||
SystemIdCallerProcess
|
|
||||||
SystemIdCallerProcessParent
|
|
||||||
)
|
|
||||||
|
|
||||||
// Implementation is the common interface implemented by all UUIDs.
|
|
||||||
type Implementation interface {
|
|
||||||
|
|
||||||
// Bytes retrieves the bytes from the underlying UUID
|
|
||||||
Bytes() []byte
|
|
||||||
|
|
||||||
// Size is the length of the underlying UUID implementation
|
|
||||||
Size() int
|
|
||||||
|
|
||||||
// String should return the canonical UUID representation, or the a
|
|
||||||
// given uuid.Format
|
|
||||||
String() string
|
|
||||||
|
|
||||||
// Variant returns the UUID implementation variant
|
|
||||||
Variant() uint8
|
|
||||||
|
|
||||||
// Version returns the version number of the algorithm used to generate
|
|
||||||
// the UUID.
|
|
||||||
Version() Version
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a UUID from a slice of bytes.
|
|
||||||
func New(data []byte) UUID {
|
|
||||||
o := UUID{}
|
|
||||||
o.unmarshal(data)
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHex creates a UUID from a hex string.
|
|
||||||
// Will panic if hex string is invalid use Parse otherwise.
|
|
||||||
func NewHex(uuid string) UUID {
|
|
||||||
o := UUID{}
|
|
||||||
o.unmarshal(fromHex(uuid))
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Pattern used to parse string representation of the UUID.
|
|
||||||
// Current one allows to parse string where only one opening
|
|
||||||
// or closing bracket or any of the hyphens are optional.
|
|
||||||
// It is only used to extract the main bytes to create a UUID,
|
|
||||||
// so these imperfections are of no consequence.
|
|
||||||
hexPattern = `^(urn\:uuid\:)?[\{\(\[]?([[:xdigit:]]{8})-?([[:xdigit:]]{4})-?([1-5][[:xdigit:]]{3})-?([[:xdigit:]]{4})-?([[:xdigit:]]{12})[\]\}\)]?$`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
parseUUIDRegex = regexp.MustCompile(hexPattern)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Parse creates a UUID from a valid string representation.
|
|
||||||
// Accepts UUID string in following formats:
|
|
||||||
// 6ba7b8149dad11d180b400c04fd430c8
|
|
||||||
// 6ba7b814-9dad-11d1-80b4-00c04fd430c8
|
|
||||||
// {6ba7b814-9dad-11d1-80b4-00c04fd430c8}
|
|
||||||
// urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8
|
|
||||||
// [6ba7b814-9dad-11d1-80b4-00c04fd430c8]
|
|
||||||
// (6ba7b814-9dad-11d1-80b4-00c04fd430c8)
|
|
||||||
//
|
|
||||||
func Parse(uuid string) (*UUID, error) {
|
|
||||||
id, err := parse(uuid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
a := UUID{}
|
|
||||||
a.unmarshal(id)
|
|
||||||
return &a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(uuid string) ([]byte, error) {
|
|
||||||
md := parseUUIDRegex.FindStringSubmatch(uuid)
|
|
||||||
if md == nil {
|
|
||||||
return nil, errors.New("uuid: invalid string format this is probably not a UUID")
|
|
||||||
}
|
|
||||||
return fromHex(md[2] + md[3] + md[4] + md[5] + md[6]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromHex(uuid string) []byte {
|
|
||||||
bytes, err := hex.DecodeString(uuid)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV1 generates a new RFC4122 version 1 UUID based on a 60 bit timestamp and
|
|
||||||
// node ID.
|
|
||||||
func NewV1() UUID {
|
|
||||||
return generator.NewV1()
|
|
||||||
}
|
|
||||||
|
|
||||||
// BulkV1 will return a slice of V1 UUIDs. Be careful with the set amount.
|
|
||||||
func BulkV1(amount int) []UUID {
|
|
||||||
return generator.BulkV1(amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadV1 will read a slice of UUIDs. Be careful with the set amount.
|
|
||||||
func ReadV1(ids []UUID) {
|
|
||||||
generator.ReadV1(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV2 generates a new DCE Security version UUID based on a 60 bit timestamp,
|
|
||||||
// node id and POSIX UID.
|
|
||||||
func NewV2(pDomain SystemId) UUID {
|
|
||||||
return generator.NewV2(pDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV3 generates a new RFC4122 version 3 UUID based on the MD5 hash of a
|
|
||||||
// namespace UUID namespace Implementation UUID and one or more unique names.
|
|
||||||
func NewV3(namespace Implementation, names ...interface{}) UUID {
|
|
||||||
return generator.NewV3(namespace, names...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV4 generates a new RFC4122 version 4 UUID a cryptographically secure
|
|
||||||
// random UUID.
|
|
||||||
func NewV4() UUID {
|
|
||||||
return generator.NewV4()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadV4 will read into a slice of UUIDs. Be careful with the set amount.
|
|
||||||
// Note: V4 UUIDs require sufficient entropy from the generator.
|
|
||||||
// If n == len(ids) err will be nil.
|
|
||||||
func ReadV4(ids []UUID) {
|
|
||||||
generator.ReadV4(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BulkV4 will return a slice of V4 UUIDs. Be careful with the set amount.
|
|
||||||
// Note: V4 UUIDs require sufficient entropy from the generator.
|
|
||||||
// If n == len(ids) err will be nil.
|
|
||||||
func BulkV4(amount int) []UUID {
|
|
||||||
return generator.BulkV4(amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewV5 generates an RFC4122 version 5 UUID based on the SHA-1 hash of a
|
|
||||||
// namespace Implementation UUID and one or more unique names.
|
|
||||||
func NewV5(namespace Implementation, names ...interface{}) UUID {
|
|
||||||
return generator.NewV5(namespace, names...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHash generate a UUID based on the given hash implementation. The hash will
|
|
||||||
// be of the given names. The version will be set to 0 for Unknown and the
|
|
||||||
// variant will be set to VariantFuture.
|
|
||||||
func NewHash(hash hash.Hash, names ...interface{}) UUID {
|
|
||||||
return generator.NewHash(hash, names...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare returns an integer comparing two Implementation UUIDs
|
|
||||||
// lexicographically.
|
|
||||||
// The result will be 0 if pId==pId2, -1 if pId < pId2, and +1 if pId > pId2.
|
|
||||||
// A nil argument is equivalent to the Nil Immutable UUID.
|
|
||||||
func Compare(pId, pId2 Implementation) int {
|
|
||||||
|
|
||||||
var b1, b2 = []byte(Nil), []byte(Nil)
|
|
||||||
|
|
||||||
if pId != nil {
|
|
||||||
b1 = pId.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
if pId2 != nil {
|
|
||||||
b2 = pId2.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the time low bytes
|
|
||||||
tl1 := binary.BigEndian.Uint32(b1[:4])
|
|
||||||
tl2 := binary.BigEndian.Uint32(b2[:4])
|
|
||||||
|
|
||||||
if tl1 != tl2 {
|
|
||||||
if tl1 < tl2 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the time hi and ver bytes
|
|
||||||
m1 := binary.BigEndian.Uint16(b1[4:6])
|
|
||||||
m2 := binary.BigEndian.Uint16(b2[4:6])
|
|
||||||
|
|
||||||
if m1 != m2 {
|
|
||||||
if m1 < m2 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the sequence and version
|
|
||||||
m1 = binary.BigEndian.Uint16(b1[6:8])
|
|
||||||
m2 = binary.BigEndian.Uint16(b2[6:8])
|
|
||||||
|
|
||||||
if m1 != m2 {
|
|
||||||
if m1 < m2 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the node id
|
|
||||||
return bytes.Compare(b1[8:], b2[8:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal compares whether each Implementation UUID is the same
|
|
||||||
func Equal(p1, p2 Implementation) bool {
|
|
||||||
return bytes.Equal(p1.Bytes(), p2.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNil returns true if Implementation UUID is all zeros?
|
|
||||||
func IsNil(uuid Implementation) bool {
|
|
||||||
if uuid == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, v := range uuid.Bytes() {
|
|
||||||
if v != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
71
vendor/github.com/twinj/uuid/version.go
generated
vendored
71
vendor/github.com/twinj/uuid/version.go
generated
vendored
@@ -1,71 +0,0 @@
|
|||||||
package uuid
|
|
||||||
|
|
||||||
// Version represents the type of UUID.
|
|
||||||
type Version int
|
|
||||||
|
|
||||||
// The following are the supported Versions.
|
|
||||||
const (
|
|
||||||
VersionUnknown Version = iota // Unknown
|
|
||||||
VersionOne // Time based
|
|
||||||
VersionTwo // DCE security via POSIX UIDs
|
|
||||||
VersionThree // Namespace hash uses MD5
|
|
||||||
VersionFour // Crypto random
|
|
||||||
VersionFive // Namespace hash uses SHA-1
|
|
||||||
)
|
|
||||||
|
|
||||||
// The following are the supported Variants.
|
|
||||||
const (
|
|
||||||
VariantNCS uint8 = 0x00
|
|
||||||
VariantRFC4122 uint8 = 0x80 // or and A0 if masked with 1F
|
|
||||||
VariantMicrosoft uint8 = 0xC0
|
|
||||||
VariantFuture uint8 = 0xE0
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// 3f used by RFC4122 although 1f works for all
|
|
||||||
variantSet = 0x3f
|
|
||||||
|
|
||||||
// rather than using 0xc0 we use 0xe0 to retrieve the variant
|
|
||||||
// The result is the same for all other variants
|
|
||||||
// 0x80 and 0xa0 are used to identify RFC4122 compliance
|
|
||||||
variantGet = 0xe0
|
|
||||||
)
|
|
||||||
|
|
||||||
// String returns English description of version.
|
|
||||||
func (o Version) String() string {
|
|
||||||
switch o {
|
|
||||||
case VersionOne:
|
|
||||||
return "Version 1: Based on a 60 Bit Timestamp."
|
|
||||||
case VersionTwo:
|
|
||||||
return "Version 2: Based on DCE security domain and 60 bit timestamp."
|
|
||||||
case VersionThree:
|
|
||||||
return "Version 3: Namespace UUID and unique names hashed by MD5."
|
|
||||||
case VersionFour:
|
|
||||||
return "Version 4: Crypto-random generated."
|
|
||||||
case VersionFive:
|
|
||||||
return "Version 5: Namespace UUID and unique names hashed by SHA-1."
|
|
||||||
default:
|
|
||||||
return "Unknown: Not supported"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveVersion(version uint8) Version {
|
|
||||||
switch Version(version) {
|
|
||||||
case VersionOne, VersionTwo, VersionThree, VersionFour, VersionFive:
|
|
||||||
return Version(version)
|
|
||||||
default:
|
|
||||||
return VersionUnknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func variant(variant uint8) uint8 {
|
|
||||||
switch variant & variantGet {
|
|
||||||
case VariantRFC4122, 0xA0:
|
|
||||||
return VariantRFC4122
|
|
||||||
case VariantMicrosoft:
|
|
||||||
return VariantMicrosoft
|
|
||||||
case VariantFuture:
|
|
||||||
return VariantFuture
|
|
||||||
}
|
|
||||||
return VariantNCS
|
|
||||||
}
|
|
||||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -18,7 +18,5 @@ github.com/mattn/go-sqlite3
|
|||||||
# github.com/mozillazg/go-unidecode v0.1.1
|
# github.com/mozillazg/go-unidecode v0.1.1
|
||||||
github.com/mozillazg/go-unidecode
|
github.com/mozillazg/go-unidecode
|
||||||
github.com/mozillazg/go-unidecode/table
|
github.com/mozillazg/go-unidecode/table
|
||||||
# github.com/twinj/uuid v1.0.0
|
|
||||||
github.com/twinj/uuid
|
|
||||||
# github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755
|
# github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755
|
||||||
github.com/wader/gormstore
|
github.com/wader/gormstore
|
||||||
|
|||||||
Reference in New Issue
Block a user