Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
352dfcf2b6 | |||
df5c189743 | |||
923672a418 | |||
3e545deb44 | |||
d7191461eb | |||
f9e8f4b9c1 |
@ -1,101 +0,0 @@
|
||||
package calendar
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Calendar struct {
|
||||
Location *time.Location
|
||||
}
|
||||
|
||||
func (cal *Calendar) GetEasterDay(year int) time.Time {
|
||||
g := float64(year % 19.0)
|
||||
c := math.Floor(float64(year) / 100.0)
|
||||
c4 := math.Floor(c / 4.0)
|
||||
h := float64(int(19.0*g+c-c4-math.Floor((8.0*c+13)/25)+15) % 30.0)
|
||||
k := math.Floor(h / 28.0)
|
||||
i := (k*math.Floor(29./(h+1.))*math.Floor((21.-g)/11.)-1.)*k + h
|
||||
|
||||
// jour de Pâques (0=dimanche, 1=lundi....)
|
||||
dayWeek := int(math.Floor(float64(year)/4.)+float64(year)+i+2+c4-c) % 7
|
||||
|
||||
// Jour de Pâques en jours enpartant de 1 = 1er mars
|
||||
presJour := int(28 + int(i) - dayWeek)
|
||||
|
||||
// mois (0 = janvier, ... 2 = mars, 3 = avril)
|
||||
month := 2
|
||||
if presJour > 31 {
|
||||
month = 3
|
||||
}
|
||||
|
||||
// Mois dans l'année
|
||||
month += 1
|
||||
|
||||
// jour du mois
|
||||
day := presJour - 31
|
||||
if month == 2 {
|
||||
day = presJour
|
||||
}
|
||||
|
||||
return time.Date(year, 3, 31, 0, 0, 0, 0, cal.Location).AddDate(0, 0, day)
|
||||
}
|
||||
|
||||
func (cal *Calendar) GetHolidays(year int) *[]time.Time {
|
||||
|
||||
// Calcul du jour de pâques
|
||||
paques := cal.GetEasterDay(year)
|
||||
|
||||
joursFeries := []time.Time{
|
||||
// Jour de l'an
|
||||
time.Date(year, time.January, 1, 0, 0, 0, 0, cal.Location),
|
||||
// Easter
|
||||
paques.AddDate(0, 0, 1),
|
||||
// 1 mai
|
||||
time.Date(year, time.May, 1, 0, 0, 0, 0, cal.Location),
|
||||
// 8 mai
|
||||
time.Date(year, time.May, 8, 0, 0, 0, 0, cal.Location),
|
||||
// Ascension
|
||||
paques.AddDate(0, 0, 39),
|
||||
// 14 juillet
|
||||
time.Date(year, time.July, 14, 0, 0, 0, 0, cal.Location),
|
||||
// 15 aout
|
||||
time.Date(year, time.August, 15, 0, 0, 0, 0, cal.Location),
|
||||
// Toussaint
|
||||
time.Date(year, time.November, 1, 0, 0, 0, 0, cal.Location),
|
||||
// 11 novembre
|
||||
time.Date(year, time.November, 11, 0, 0, 0, 0, cal.Location),
|
||||
// noël
|
||||
time.Date(year, time.December, 25, 0, 0, 0, 0, cal.Location),
|
||||
}
|
||||
|
||||
return &joursFeries
|
||||
}
|
||||
|
||||
func (cal *Calendar) GetHolidaysSet(year int) *map[time.Time]bool {
|
||||
holidays := cal.GetHolidays(year)
|
||||
result := make(map[time.Time]bool, len(*holidays))
|
||||
for _, h := range *holidays {
|
||||
result[h] = true
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func(cal *Calendar) IsHoliday(date time.Time) bool{
|
||||
h := cal.GetHolidaysSet(date.Year())
|
||||
d := date.In(cal.Location)
|
||||
day := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, cal.Location)
|
||||
return (*h)[day]
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsWorkingDay(date time.Time) bool {
|
||||
return !cal.IsHoliday(date) && date.Weekday() >= time.Monday && date.Weekday() <= time.Friday
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsWorkingDayToday() bool {
|
||||
return cal.IsWorkingDay(time.Now())
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsWeekDay(day time.Time) bool{
|
||||
return day.Weekday() >= time.Monday && day.Weekday() <= time.Friday
|
||||
}
|
@ -2,22 +2,27 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"domogeek/calendar"
|
||||
"domogeek/pkg/calendar"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/hellofresh/health-go/v4"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.uber.org/zap"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
cal calendar.Calendar
|
||||
cal *calendar.Calendar
|
||||
location *time.Location
|
||||
calCounter *prometheus.CounterVec
|
||||
calSummary *prometheus.SummaryVec
|
||||
calHistogram *prometheus.HistogramVec
|
||||
@ -26,9 +31,9 @@ var (
|
||||
func init() {
|
||||
loc, err := time.LoadLocation("Europe/Paris")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to load time location: %v", err)
|
||||
zap.S().Fatalf("unable to load time location: %v", err)
|
||||
}
|
||||
cal = calendar.Calendar{Location: loc}
|
||||
location = loc
|
||||
|
||||
calCounter = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "domogeek",
|
||||
@ -67,25 +72,31 @@ type CalendarDay struct {
|
||||
|
||||
type CalendarHandler struct{}
|
||||
|
||||
func (c *CalendarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (c *CalendarHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
|
||||
now := time.Now()
|
||||
calDavHolidays, err := cal.IsHolidaysFromCaldav(now)
|
||||
if err != nil {
|
||||
zap.S().Warnf("unable to read holiday status from caldav: %v", err)
|
||||
calDavHolidays = false
|
||||
}
|
||||
|
||||
cd := CalendarDay{
|
||||
Day: now,
|
||||
WorkingDay: cal.IsWorkingDay(now),
|
||||
Ferie: cal.IsHoliday(now),
|
||||
Holiday: cal.IsHoliday(now),
|
||||
Holiday: calDavHolidays,
|
||||
Weekday: cal.IsWeekDay(now),
|
||||
}
|
||||
|
||||
content, err := json.Marshal(cd)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
log.Printf("unable to marshall response %v, %v", content, err)
|
||||
zap.S().Errorf("unable to marshall response %v, %v", content, err)
|
||||
} else {
|
||||
_, err = w.Write(content)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
log.Printf("unable to marshall response %v, :%v", content, err)
|
||||
zap.S().Errorf("unable to marshall response %v, :%v", content, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,13 +104,55 @@ func (c *CalendarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func main() {
|
||||
var port int
|
||||
var host string
|
||||
var user, pwd string
|
||||
var caldavUrl, caldavPath, caldavSummaryPattern string
|
||||
|
||||
flag.StringVar(&host, "host", "", "host to listen, default all addresses")
|
||||
flag.IntVar(&port, "port", 8080, "port to listen")
|
||||
flag.StringVar(&caldavUrl, "caldav-url", "", "caldav url to use to read holidays events")
|
||||
flag.StringVar(&caldavPath, "caldav-path", "", "caldav path to use to read holidays events")
|
||||
flag.StringVar(&caldavSummaryPattern, "caldav-summary-pattern", "Holidays", "Summary pattern that matches holidays event")
|
||||
flag.StringVar(&user, "caldav-username", "", "Username credential")
|
||||
flag.StringVar(&pwd, "caldav-password", "", "Password credential")
|
||||
flag.Parse()
|
||||
|
||||
logLevel := zap.LevelFlag("log", zap.InfoLevel, "log level")
|
||||
flag.Parse()
|
||||
|
||||
if len(os.Args) <= 1 {
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config := zap.NewDevelopmentConfig()
|
||||
config.Level = zap.NewAtomicLevelAt(*logLevel)
|
||||
lgr, err := config.Build()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to init logger: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = lgr.Sync()
|
||||
}()
|
||||
zap.ReplaceGlobals(lgr)
|
||||
|
||||
urlCaldav, err := url.Parse(caldavUrl)
|
||||
if err != nil {
|
||||
zap.S().Panicf("invalid caldav url '%v': %v", caldavUrl, err)
|
||||
}
|
||||
urlCaldav.User = url.UserPassword(user, pwd)
|
||||
|
||||
cdav, err := calendar.NewCaldav(urlCaldav.String(), caldavPath)
|
||||
if err != nil {
|
||||
zap.S().Fatal("unable to init caldav instance")
|
||||
}
|
||||
cal = calendar.New(location,
|
||||
calendar.WithCaldav(cdav),
|
||||
calendar.WithCaldavPath(caldavPath),
|
||||
calendar.WithCaldavSummaryPattern(caldavSummaryPattern),
|
||||
)
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", host, port)
|
||||
log.Printf("start server on %s", addr)
|
||||
zap.S().Infof("start server on %s", addr)
|
||||
|
||||
h := promhttp.InstrumentHandlerDuration(
|
||||
calHistogram,
|
||||
@ -117,9 +170,28 @@ func main() {
|
||||
Check: func(ctx context.Context) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
))
|
||||
}),
|
||||
health.WithChecks(health.Config{
|
||||
Name: "caldav",
|
||||
Timeout: 5 * time.Second,
|
||||
SkipOnErr: false,
|
||||
Check: func(ctx context.Context) error {
|
||||
_, err := cal.IsHolidaysFromCaldav(time.Now())
|
||||
if err != nil {
|
||||
zap.S().Warnf("unable to check caldav connection: %v", err)
|
||||
}
|
||||
return err
|
||||
},
|
||||
}),
|
||||
)
|
||||
http.Handle("/status", healthz.Handler())
|
||||
|
||||
log.Fatal(http.ListenAndServe(addr, nil))
|
||||
signChan := make(chan os.Signal, 1)
|
||||
go func() {
|
||||
zap.S().Fatal(http.ListenAndServe(addr, nil))
|
||||
}()
|
||||
|
||||
signal.Notify(signChan, syscall.SIGTERM)
|
||||
<-signChan
|
||||
zap.S().Info("exit on sigterm")
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -3,8 +3,10 @@ module domogeek
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/dolanor/caldav-go v0.2.1
|
||||
github.com/hellofresh/health-go/v4 v4.5.0
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
go.uber.org/zap v1.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -17,6 +19,8 @@ require (
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
go.opentelemetry.io/otel v1.0.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
)
|
||||
|
27
go.sum
27
go.sum
@ -39,6 +39,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@ -62,6 +64,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dolanor/caldav-go v0.2.1 h1:wbARF+WKIryMOApYdX6CnFKY25Kz3ChPerfFc/R3Txk=
|
||||
github.com/dolanor/caldav-go v0.2.1/go.mod h1:0A9uEq2TN7U1eNh11hHAZ0FA7jyuFSh5hXeaoFPC0fc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@ -226,9 +230,11 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@ -263,6 +269,7 @@ github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+t
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@ -325,6 +332,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.mongodb.org/mongo-driver v1.7.2/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@ -340,13 +348,21 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
@ -391,6 +407,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -424,6 +441,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -443,6 +461,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -489,7 +508,9 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
@ -558,6 +579,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -645,6 +667,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
@ -656,9 +679,11 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
187
pkg/calendar/calendar.go
Normal file
187
pkg/calendar/calendar.go
Normal file
@ -0,0 +1,187 @@
|
||||
package calendar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/caldav"
|
||||
"github.com/dolanor/caldav-go/caldav/entities"
|
||||
"github.com/dolanor/caldav-go/icalendar/components"
|
||||
"go.uber.org/zap"
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Caldav interface {
|
||||
QueryEvents(path string, query *entities.CalendarQuery) (events []*components.Event, oerr error)
|
||||
}
|
||||
|
||||
type Calendar struct {
|
||||
Location *time.Location
|
||||
cdav Caldav
|
||||
caldavPath string
|
||||
caldavSummaryPattern string
|
||||
}
|
||||
|
||||
func NewCaldav(caldavUrl, caldavPath string) (Caldav, error) {
|
||||
// create a reference to your CalDAV-compliant server
|
||||
server, _ := caldav.NewServer(caldavUrl)
|
||||
// create a CalDAV client to speak to the server
|
||||
var client = caldav.NewClient(server, http.DefaultClient)
|
||||
// start executing requests!
|
||||
err := client.ValidateServer(caldavPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad caldav configuration, unable to validate connexion: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
type Option func(calendar *Calendar)
|
||||
|
||||
func WithCaldav(cdav Caldav) Option {
|
||||
return func(calendar *Calendar) {
|
||||
calendar.cdav = cdav
|
||||
}
|
||||
}
|
||||
|
||||
func WithCaldavSummaryPattern(caldavSummaryPattern string) Option {
|
||||
return func(calendar *Calendar) {
|
||||
calendar.caldavSummaryPattern = caldavSummaryPattern
|
||||
}
|
||||
}
|
||||
|
||||
func WithCaldavPath(caldavPath string) Option {
|
||||
return func(calendar *Calendar) {
|
||||
calendar.caldavPath = caldavPath
|
||||
}
|
||||
}
|
||||
|
||||
func New(location *time.Location, opts ...Option) *Calendar {
|
||||
c := &Calendar{
|
||||
location,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (cal *Calendar) GetEasterDay(year int) time.Time {
|
||||
g := float64(year % 19.0)
|
||||
c := math.Floor(float64(year) / 100.0)
|
||||
c4 := math.Floor(c / 4.0)
|
||||
h := float64(int(19.0*g+c-c4-math.Floor((8.0*c+13)/25)+15) % 30.0)
|
||||
k := math.Floor(h / 28.0)
|
||||
i := (k*math.Floor(29./(h+1.))*math.Floor((21.-g)/11.)-1.)*k + h
|
||||
|
||||
// jour de Pâques (0=dimanche, 1=lundi....)
|
||||
dayWeek := int(math.Floor(float64(year)/4.)+float64(year)+i+2+c4-c) % 7
|
||||
|
||||
// Jour de Pâques en jours enpartant de 1 = 1er mars
|
||||
presJour := int(28 + int(i) - dayWeek)
|
||||
|
||||
// mois (0 = janvier, ... 2 = mars, 3 = avril)
|
||||
month := 2
|
||||
if presJour > 31 {
|
||||
month = 3
|
||||
}
|
||||
|
||||
// Mois dans l'année
|
||||
month += 1
|
||||
|
||||
// jour du mois
|
||||
day := presJour - 31
|
||||
if month == 2 {
|
||||
day = presJour
|
||||
}
|
||||
|
||||
return time.Date(year, 3, 31, 0, 0, 0, 0, cal.Location).AddDate(0, 0, day)
|
||||
}
|
||||
|
||||
func (cal *Calendar) GetHolidays(year int) *[]time.Time {
|
||||
|
||||
// Calcul du jour de pâques
|
||||
paques := cal.GetEasterDay(year)
|
||||
|
||||
joursFeries := []time.Time{
|
||||
// Jour de l'an
|
||||
time.Date(year, time.January, 1, 0, 0, 0, 0, cal.Location),
|
||||
// Easter
|
||||
paques.AddDate(0, 0, 1),
|
||||
// 1 mai
|
||||
time.Date(year, time.May, 1, 0, 0, 0, 0, cal.Location),
|
||||
// 8 mai
|
||||
time.Date(year, time.May, 8, 0, 0, 0, 0, cal.Location),
|
||||
// Ascension
|
||||
paques.AddDate(0, 0, 39),
|
||||
// 14 juillet
|
||||
time.Date(year, time.July, 14, 0, 0, 0, 0, cal.Location),
|
||||
// 15 aout
|
||||
time.Date(year, time.August, 15, 0, 0, 0, 0, cal.Location),
|
||||
// Toussaint
|
||||
time.Date(year, time.November, 1, 0, 0, 0, 0, cal.Location),
|
||||
// 11 novembre
|
||||
time.Date(year, time.November, 11, 0, 0, 0, 0, cal.Location),
|
||||
// noël
|
||||
time.Date(year, time.December, 25, 0, 0, 0, 0, cal.Location),
|
||||
}
|
||||
|
||||
return &joursFeries
|
||||
}
|
||||
|
||||
func (cal *Calendar) GetHolidaysSet(year int) map[time.Time]bool {
|
||||
holidays := cal.GetHolidays(year)
|
||||
result := make(map[time.Time]bool, len(*holidays))
|
||||
for _, h := range *holidays {
|
||||
result[h] = true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsHoliday(date time.Time) bool {
|
||||
h := cal.GetHolidaysSet(date.Year())
|
||||
d := date.In(cal.Location)
|
||||
day := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, cal.Location)
|
||||
caldavHolidays, err := cal.IsHolidaysFromCaldav(day)
|
||||
if err != nil {
|
||||
zap.S().Errorf("unable to check holidays from caldav: %v", err)
|
||||
}
|
||||
return h[day] || caldavHolidays
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsWorkingDay(date time.Time) bool {
|
||||
return !cal.IsHoliday(date) && date.Weekday() >= time.Monday && date.Weekday() <= time.Friday
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsWorkingDayToday() bool {
|
||||
return cal.IsWorkingDay(time.Now())
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsWeekDay(day time.Time) bool {
|
||||
return day.Weekday() >= time.Monday && day.Weekday() <= time.Friday
|
||||
}
|
||||
|
||||
func (cal *Calendar) IsHolidaysFromCaldav(day time.Time) (bool, error) {
|
||||
if cal.cdav == nil {
|
||||
return false, nil
|
||||
}
|
||||
query, err := entities.NewEventRangeQuery(day.UTC(), day.UTC().Add(23*time.Hour+59*time.Minute))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to build events range query: %v", err)
|
||||
}
|
||||
events, err := cal.cdav.QueryEvents(cal.caldavPath, query)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable list events from caldav: %v", err)
|
||||
}
|
||||
|
||||
for _, evt := range events {
|
||||
if strings.Contains(evt.Summary, cal.caldavSummaryPattern) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package calendar
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/caldav/entities"
|
||||
"github.com/dolanor/caldav-go/icalendar/components"
|
||||
"github.com/dolanor/caldav-go/icalendar/values"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -48,7 +51,7 @@ func TestCalendar_GetHolidays(t *testing.T) {
|
||||
time.Date(2020, time.December, 25, 0, 0, 0, 0, loc): true,
|
||||
}
|
||||
|
||||
c := Calendar{loc}
|
||||
c := New(loc)
|
||||
holidays := c.GetHolidays(2020)
|
||||
if len(*holidays) != len(expectedHolidays) {
|
||||
t.Errorf("bad number of holidays, %d but %d are expected", len(*holidays), len(expectedHolidays))
|
||||
@ -69,7 +72,7 @@ func TestCalendar_GetHolidaysSet(t *testing.T) {
|
||||
|
||||
expectedHolidays := []time.Time{
|
||||
time.Date(2020, time.January, 1, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.April, 12, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.April, 13, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.May, 1, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.May, 8, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.May, 21, 0, 0, 0, 0, loc),
|
||||
@ -80,13 +83,13 @@ func TestCalendar_GetHolidaysSet(t *testing.T) {
|
||||
time.Date(2020, time.December, 25, 0, 0, 0, 0, loc),
|
||||
}
|
||||
|
||||
c := Calendar{loc}
|
||||
c := New(loc)
|
||||
holidays := c.GetHolidaysSet(2020)
|
||||
if len(*holidays) != len(expectedHolidays) {
|
||||
t.Errorf("bad number of holidays, %d but %d are expected", len(*holidays), len(expectedHolidays))
|
||||
if len(holidays) != len(expectedHolidays) {
|
||||
t.Errorf("bad number of holidays, %d but %d are expected", len(holidays), len(expectedHolidays))
|
||||
}
|
||||
for _, h := range expectedHolidays {
|
||||
if !(*holidays)[h] {
|
||||
if !(holidays)[h] {
|
||||
t.Errorf("%v is not a holiday", h)
|
||||
}
|
||||
}
|
||||
@ -101,7 +104,7 @@ func TestCalendar_IsHolidays(t *testing.T) {
|
||||
|
||||
expectedHolidays := []time.Time{
|
||||
time.Date(2020, time.January, 1, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.April, 12, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.April, 13, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.May, 1, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.May, 8, 0, 0, 0, 0, loc),
|
||||
time.Date(2020, time.May, 21, 0, 0, 0, 0, loc),
|
||||
@ -112,10 +115,10 @@ func TestCalendar_IsHolidays(t *testing.T) {
|
||||
time.Date(2020, time.December, 25, 0, 0, 0, 0, loc),
|
||||
}
|
||||
|
||||
c := Calendar{loc}
|
||||
c := New(loc)
|
||||
holidays := c.GetHolidaysSet(2020)
|
||||
if len(*holidays) != len(expectedHolidays) {
|
||||
t.Errorf("bad number of holidays, %d but %d are expected", len(*holidays), len(expectedHolidays))
|
||||
if len(holidays) != len(expectedHolidays) {
|
||||
t.Errorf("bad number of holidays, %d but %d are expected", len(holidays), len(expectedHolidays))
|
||||
}
|
||||
for _, h := range expectedHolidays {
|
||||
if !c.IsHoliday(h) {
|
||||
@ -133,7 +136,7 @@ func TestCalendar_IsWorkingDay(t *testing.T) {
|
||||
t.Errorf("unable to load time location: %v", err)
|
||||
t.Fail()
|
||||
}
|
||||
c := Calendar{loc}
|
||||
c := New(loc)
|
||||
|
||||
if c.IsWorkingDay(time.Date(2019, time.January, 01, 0, 0, 0, 0, loc)) {
|
||||
t.Error("1st january is not a working day")
|
||||
@ -164,3 +167,114 @@ func TestCalendar_IsWorkingDay(t *testing.T) {
|
||||
t.Error("Sunday should not be a working day")
|
||||
}
|
||||
}
|
||||
|
||||
type MockCaldav struct {
|
||||
events []*components.Event
|
||||
}
|
||||
|
||||
func (m *MockCaldav) QueryEvents(_ string, _ *entities.CalendarQuery) ([]*components.Event, error) {
|
||||
return m.events, nil
|
||||
}
|
||||
|
||||
func TestCalendar_IsHolidaysFromCaldav(t *testing.T) {
|
||||
loc, err := time.LoadLocation("Europe/Paris")
|
||||
if err != nil {
|
||||
t.Errorf("unable to load time location: %v", err)
|
||||
t.Fail()
|
||||
}
|
||||
type fields struct {
|
||||
Location *time.Location
|
||||
cdav *MockCaldav
|
||||
caldavPath string
|
||||
caldavSummaryPattern string
|
||||
}
|
||||
type args struct {
|
||||
day time.Time
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Holidays in events",
|
||||
fields: fields{
|
||||
Location: loc,
|
||||
cdav: &MockCaldav{
|
||||
events: []*components.Event{
|
||||
{
|
||||
UID: "1",
|
||||
DateStart: values.NewDateTime(time.Date(2022, time.April, 16, 0, 0, 0, 0, loc)),
|
||||
DateEnd: values.NewDateTime(time.Date(2022, time.April, 17, 0, 0, 0, 0, loc)),
|
||||
Summary: "Holidays",
|
||||
},
|
||||
},
|
||||
},
|
||||
caldavPath: "my_calendar/",
|
||||
caldavSummaryPattern: "Holidays",
|
||||
},
|
||||
args: args{
|
||||
day: time.Date(2022, time.April, 16, 0, 0, 0, 0, loc),
|
||||
},
|
||||
want: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Not Holidays in events",
|
||||
fields: fields{
|
||||
Location: loc,
|
||||
cdav: &MockCaldav{
|
||||
events: []*components.Event{
|
||||
{
|
||||
UID: "1",
|
||||
DateStart: values.NewDateTime(time.Date(2022, time.April, 16, 0, 0, 0, 0, loc)),
|
||||
DateEnd: values.NewDateTime(time.Date(2022, time.April, 17, 0, 0, 0, 0, loc)),
|
||||
Summary: "Another event",
|
||||
},
|
||||
},
|
||||
},
|
||||
caldavPath: "my_calendar/",
|
||||
caldavSummaryPattern: "Holidays",
|
||||
},
|
||||
args: args{
|
||||
day: time.Date(2022, time.April, 16, 0, 0, 0, 0, loc),
|
||||
},
|
||||
want: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "No events",
|
||||
fields: fields{
|
||||
Location: loc,
|
||||
cdav: &MockCaldav{},
|
||||
caldavPath: "my_calendar/",
|
||||
caldavSummaryPattern: "Holidays",
|
||||
},
|
||||
args: args{
|
||||
day: time.Date(2022, time.April, 15, 0, 0, 0, 0, loc),
|
||||
},
|
||||
want: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cal := New(
|
||||
loc,
|
||||
WithCaldav(tt.fields.cdav),
|
||||
WithCaldavPath(tt.fields.caldavPath),
|
||||
WithCaldavSummaryPattern(tt.fields.caldavSummaryPattern),
|
||||
)
|
||||
got, err := cal.IsHolidaysFromCaldav(tt.args.day)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("IsHolidaysFromCaldav() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("IsHolidaysFromCaldav() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
22
vendor/github.com/dolanor/caldav-go/LICENSE
generated
vendored
Normal file
22
vendor/github.com/dolanor/caldav-go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015
|
||||
|
||||
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.
|
||||
|
191
vendor/github.com/dolanor/caldav-go/caldav/client.go
generated
vendored
Normal file
191
vendor/github.com/dolanor/caldav-go/caldav/client.go
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
package caldav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
cent "github.com/dolanor/caldav-go/caldav/entities"
|
||||
"github.com/dolanor/caldav-go/icalendar/components"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"github.com/dolanor/caldav-go/webdav"
|
||||
"github.com/dolanor/caldav-go/webdav/entities"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// a client for making WebDAV requests
|
||||
type Client webdav.Client
|
||||
|
||||
// downcasts the client to the WebDAV interface
|
||||
func (c *Client) WebDAV() *webdav.Client {
|
||||
return (*webdav.Client)(c)
|
||||
}
|
||||
|
||||
// returns the embedded CalDAV server reference
|
||||
func (c *Client) Server() *Server {
|
||||
return (*Server)(c.WebDAV().Server())
|
||||
}
|
||||
|
||||
// fetches a list of CalDAV features supported by the server
|
||||
// returns an error if the server does not support DAV
|
||||
func (c *Client) Features(path string) ([]string, error) {
|
||||
var cfeatures []string
|
||||
if features, err := c.WebDAV().Features(path); err != nil {
|
||||
return cfeatures, utils.NewError(c.Features, "unable to detect features", c, err)
|
||||
} else {
|
||||
for _, feature := range features {
|
||||
if strings.HasPrefix(feature, "calendar-") {
|
||||
cfeatures = append(cfeatures, feature)
|
||||
}
|
||||
}
|
||||
return cfeatures, nil
|
||||
}
|
||||
}
|
||||
|
||||
// fetches a list of CalDAV features and checks if a certain one is supported by the server
|
||||
// returns an error if the server does not support DAV
|
||||
func (c *Client) SupportsFeature(name string, path string) (bool, error) {
|
||||
if features, err := c.Features(path); err != nil {
|
||||
return false, utils.NewError(c.SupportsFeature, "feature detection failed", c, err)
|
||||
} else {
|
||||
var test = fmt.Sprintf("calendar-%s", name)
|
||||
for _, feature := range features {
|
||||
if feature == test {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// fetches a list of CalDAV features and checks if a certain one is supported by the server
|
||||
// returns an error if the server does not support DAV
|
||||
func (c *Client) ValidateServer(path string) error {
|
||||
if found, err := c.SupportsFeature("access", path); err != nil {
|
||||
return utils.NewError(c.SupportsFeature, "feature detection failed", c, err)
|
||||
} else if !found {
|
||||
return utils.NewError(c.SupportsFeature, "calendar access feature missing", c, nil)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new calendar collection on a given path
|
||||
func (c *Client) MakeCalendar(path string) error {
|
||||
if req, err := c.Server().NewRequest("MKCALENDAR", path); err != nil {
|
||||
return utils.NewError(c.MakeCalendar, "unable to create request", c, err)
|
||||
} else if resp, err := c.Do(req); err != nil {
|
||||
return utils.NewError(c.MakeCalendar, "unable to execute request", c, err)
|
||||
} else if resp.StatusCode != http.StatusCreated {
|
||||
err := new(entities.Error)
|
||||
resp.Decode(err)
|
||||
msg := fmt.Sprintf("unexpected server response %s", resp.Status)
|
||||
return utils.NewError(c.MakeCalendar, msg, c, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates or updates one or more events on the remote CalDAV server
|
||||
func (c *Client) PutEvents(path string, events ...*components.Event) error {
|
||||
if len(events) <= 0 {
|
||||
return utils.NewError(c.PutEvents, "no calendar events provided", c, nil)
|
||||
} else if cal := components.NewCalendar(events...); events[0] == nil {
|
||||
return utils.NewError(c.PutEvents, "icalendar event must not be nil", c, nil)
|
||||
} else if err := c.PutCalendars(path, cal); err != nil {
|
||||
return utils.NewError(c.PutEvents, "unable to put calendar", c, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// creates or updates one or more calendars on the remote CalDAV server
|
||||
func (c *Client) PutCalendars(path string, calendars ...*components.Calendar) error {
|
||||
if req, err := c.Server().NewRequest("PUT", path, calendars); err != nil {
|
||||
return utils.NewError(c.PutCalendars, "unable to encode request", c, err)
|
||||
} else if resp, err := c.Do(req); err != nil {
|
||||
return utils.NewError(c.PutCalendars, "unable to execute request", c, err)
|
||||
} else if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent {
|
||||
err := new(entities.Error)
|
||||
resp.WebDAV().Decode(err)
|
||||
msg := fmt.Sprintf("unexpected server response %s", resp.Status)
|
||||
return utils.NewError(c.PutCalendars, msg, c, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// attempts to fetch an event on the remote CalDAV server
|
||||
func (c *Client) GetEvents(path string) ([]*components.Event, error) {
|
||||
cal := new(components.Calendar)
|
||||
if req, err := c.Server().NewRequest("GET", path); err != nil {
|
||||
return nil, utils.NewError(c.GetEvents, "unable to create request", c, err)
|
||||
} else if resp, err := c.Do(req); err != nil {
|
||||
return nil, utils.NewError(c.GetEvents, "unable to execute request", c, err)
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
err := new(entities.Error)
|
||||
resp.WebDAV().Decode(err)
|
||||
msg := fmt.Sprintf("unexpected server response %s", resp.Status)
|
||||
return nil, utils.NewError(c.GetEvents, msg, c, err)
|
||||
} else if err := resp.Decode(cal); err != nil {
|
||||
return nil, utils.NewError(c.GetEvents, "unable to decode response", c, err)
|
||||
} else {
|
||||
return cal.Events, nil
|
||||
}
|
||||
}
|
||||
|
||||
// attempts to fetch an event on the remote CalDAV server
|
||||
func (c *Client) QueryEvents(path string, query *cent.CalendarQuery) (events []*components.Event, oerr error) {
|
||||
ms := new(cent.Multistatus)
|
||||
if req, err := c.Server().WebDAV().NewRequest("REPORT", path, query); err != nil {
|
||||
oerr = utils.NewError(c.QueryEvents, "unable to create request", c, err)
|
||||
} else if req.Http().Native().Header.Set("Depth", string(webdav.Depth1)); false {
|
||||
} else if resp, err := c.WebDAV().Do(req); err != nil {
|
||||
oerr = utils.NewError(c.QueryEvents, "unable to execute request", c, err)
|
||||
} else if resp.StatusCode == http.StatusNotFound {
|
||||
return // no events if not found
|
||||
} else if resp.StatusCode != webdav.StatusMulti {
|
||||
err := new(entities.Error)
|
||||
msg := fmt.Sprintf("unexpected server response %s", resp.Status)
|
||||
resp.Decode(err)
|
||||
oerr = utils.NewError(c.QueryEvents, msg, c, err)
|
||||
} else if err := resp.Decode(ms); err != nil {
|
||||
msg := "unable to decode response"
|
||||
oerr = utils.NewError(c.QueryEvents, msg, c, err)
|
||||
} else {
|
||||
for i, r := range ms.Responses {
|
||||
for j, p := range r.PropStats {
|
||||
if p.Prop == nil || p.Prop.CalendarData == nil {
|
||||
continue
|
||||
} else if cal, err := p.Prop.CalendarData.CalendarComponent(); err != nil {
|
||||
msg := fmt.Sprintf("unable to decode property %d of response %d", j, i)
|
||||
oerr = utils.NewError(c.QueryEvents, msg, c, err)
|
||||
return
|
||||
} else {
|
||||
events = append(events, cal.Events...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// executes a CalDAV request
|
||||
func (c *Client) Do(req *Request) (*Response, error) {
|
||||
if resp, err := c.WebDAV().Do((*webdav.Request)(req)); err != nil {
|
||||
return nil, utils.NewError(c.Do, "unable to execute CalDAV request", c, err)
|
||||
} else {
|
||||
return NewResponse(resp), nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new client for communicating with an WebDAV server
|
||||
func NewClient(server *Server, native *http.Client) *Client {
|
||||
return (*Client)(webdav.NewClient((*webdav.Server)(server), native))
|
||||
}
|
||||
|
||||
// creates a new client for communicating with a WebDAV server
|
||||
// uses the default HTTP client from net/http
|
||||
func NewDefaultClient(server *Server) *Client {
|
||||
return NewClient(server, http.DefaultClient)
|
||||
}
|
51
vendor/github.com/dolanor/caldav-go/caldav/entities/calendar_data.go
generated
vendored
Normal file
51
vendor/github.com/dolanor/caldav-go/caldav/entities/calendar_data.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/dolanor/caldav-go/caldav/values"
|
||||
"github.com/dolanor/caldav-go/icalendar"
|
||||
"github.com/dolanor/caldav-go/icalendar/components"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// a CalDAV calendar data object
|
||||
type CalendarData struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav calendar-data"`
|
||||
Component *Component `xml:",omitempty"`
|
||||
RecurrenceSetLimit *RecurrenceSetLimit `xml:",omitempty"`
|
||||
ExpandRecurrenceSet *ExpandRecurrenceSet `xml:",omitempty"`
|
||||
Content string `xml:",chardata"`
|
||||
}
|
||||
|
||||
func (c *CalendarData) CalendarComponent() (*components.Calendar, error) {
|
||||
cal := new(components.Calendar)
|
||||
if content := strings.TrimSpace(c.Content); content == "" {
|
||||
return nil, utils.NewError(c.CalendarComponent, "no calendar data to decode", c, nil)
|
||||
} else if err := icalendar.Unmarshal(content, cal); err != nil {
|
||||
return nil, utils.NewError(c.CalendarComponent, "decoding calendar data failed", c, err)
|
||||
} else {
|
||||
return cal, nil
|
||||
}
|
||||
}
|
||||
|
||||
// an iCalendar specifier for returned calendar data
|
||||
type Component struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav comp"`
|
||||
Properties []*PropertyName `xml:",omitempty"`
|
||||
Components []*Component `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// used to restrict recurring event data to a particular time range
|
||||
type RecurrenceSetLimit struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav limit-recurrence-set"`
|
||||
StartTime *values.DateTime `xml:"start,attr"`
|
||||
EndTime *values.DateTime `xml:"end,attr"`
|
||||
}
|
||||
|
||||
// used to expand recurring events into individual calendar event data
|
||||
type ExpandRecurrenceSet struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav expand"`
|
||||
StartTime *values.DateTime `xml:"start,attr"`
|
||||
EndTime *values.DateTime `xml:"end,attr"`
|
||||
}
|
59
vendor/github.com/dolanor/caldav-go/caldav/entities/calendar_query.go
generated
vendored
Normal file
59
vendor/github.com/dolanor/caldav-go/caldav/entities/calendar_query.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/dolanor/caldav-go/caldav/values"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"github.com/dolanor/caldav-go/webdav/entities"
|
||||
"time"
|
||||
)
|
||||
|
||||
// a CalDAV calendar query object
|
||||
type CalendarQuery struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav calendar-query"`
|
||||
Prop *Prop `xml:",omitempty"`
|
||||
AllProp *entities.AllProp `xml:",omitempty"`
|
||||
Filter *Filter `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// creates a new CalDAV query for iCalendar events from a particular time range
|
||||
func NewEventRangeQuery(start, end time.Time) (*CalendarQuery, error) {
|
||||
|
||||
var err error
|
||||
var dtstart, dtend *values.DateTime
|
||||
if dtstart, err = values.NewDateTime("start", start); err != nil {
|
||||
return nil, utils.NewError(NewEventRangeQuery, "unable to encode start time", start, err)
|
||||
} else if dtend, err = values.NewDateTime("end", end); err != nil {
|
||||
return nil, utils.NewError(NewEventRangeQuery, "unable to encode end time", end, err)
|
||||
}
|
||||
|
||||
// construct the query object
|
||||
query := new(CalendarQuery)
|
||||
|
||||
// request all calendar data
|
||||
query.Prop = new(Prop)
|
||||
query.Prop.CalendarData = new(CalendarData)
|
||||
|
||||
// expand recurring events
|
||||
query.Prop.CalendarData.ExpandRecurrenceSet = new(ExpandRecurrenceSet)
|
||||
query.Prop.CalendarData.ExpandRecurrenceSet.StartTime = dtstart
|
||||
query.Prop.CalendarData.ExpandRecurrenceSet.EndTime = dtend
|
||||
|
||||
// filter down calendar data to only iCalendar data
|
||||
query.Filter = new(Filter)
|
||||
query.Filter.ComponentFilter = new(ComponentFilter)
|
||||
query.Filter.ComponentFilter.Name = values.CalendarComponentName
|
||||
|
||||
// filter down iCalendar data to only events
|
||||
query.Filter.ComponentFilter.ComponentFilter = new(ComponentFilter)
|
||||
query.Filter.ComponentFilter.ComponentFilter.Name = values.EventComponentName
|
||||
|
||||
// filter down the events to only those that fall within the time range
|
||||
query.Filter.ComponentFilter.ComponentFilter.TimeRange = new(TimeRange)
|
||||
query.Filter.ComponentFilter.ComponentFilter.TimeRange.StartTime = dtstart
|
||||
query.Filter.ComponentFilter.ComponentFilter.TimeRange.EndTime = dtend
|
||||
|
||||
// return the event query
|
||||
return query, nil
|
||||
|
||||
}
|
62
vendor/github.com/dolanor/caldav-go/caldav/entities/filter.go
generated
vendored
Normal file
62
vendor/github.com/dolanor/caldav-go/caldav/entities/filter.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/dolanor/caldav-go/caldav/values"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
)
|
||||
|
||||
// a CalDAV query filter entity
|
||||
type Filter struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav filter"`
|
||||
ComponentFilter *ComponentFilter `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// used to filter down calendar components, such as VCALENDAR > VEVENT
|
||||
type ComponentFilter struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav comp-filter"`
|
||||
Name values.ComponentName `xml:"name,attr"`
|
||||
ComponentFilter *ComponentFilter `xml:",omitempty"`
|
||||
TimeRange *TimeRange `xml:",omitempty"`
|
||||
PropertyFilter *PropertyFilter `xml:",omitempty"`
|
||||
ParameterFilter *ParameterFilter `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// used to restrict component filters to a particular time range
|
||||
type TimeRange struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav time-range"`
|
||||
StartTime *values.DateTime `xml:"start,attr"`
|
||||
EndTime *values.DateTime `xml:"end,attr"`
|
||||
}
|
||||
|
||||
// used to restrict component filters to a property value
|
||||
type PropertyFilter struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav prop-filter"`
|
||||
Name properties.PropertyName `xml:"name,attr"`
|
||||
TextMatch *TextMatch `xml:",omitempty"`
|
||||
ParameterFilter *ParameterFilter `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// used to restrict component filters to a parameter value
|
||||
type ParameterFilter struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav param-filter"`
|
||||
Name properties.ParameterName `xml:"name,attr"`
|
||||
TextMatch *TextMatch `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// used to match properties by text value
|
||||
type TextMatch struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav text-match"`
|
||||
Collation values.TextCollation `xml:"collation,attr,omitempty"`
|
||||
NegateCondition values.HumanBoolean `xml:"attr,negate-condition,omitempty"`
|
||||
Content string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
// creates a new CalDAV property value matcher
|
||||
func NewPropertyMatcher(name properties.PropertyName, content string) *PropertyFilter {
|
||||
pf := new(PropertyFilter)
|
||||
pf.Name = name
|
||||
pf.TextMatch = new(TextMatch)
|
||||
pf.TextMatch.Content = content
|
||||
return pf
|
||||
}
|
23
vendor/github.com/dolanor/caldav-go/caldav/entities/multistatus.go
generated
vendored
Normal file
23
vendor/github.com/dolanor/caldav-go/caldav/entities/multistatus.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package entities
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// metadata about a property
|
||||
type PropStat struct {
|
||||
XMLName xml.Name `xml:"propstat"`
|
||||
Status string `xml:"status"`
|
||||
Prop *Prop `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// a multistatus response entity
|
||||
type Response struct {
|
||||
XMLName xml.Name `xml:"response"`
|
||||
Href string `xml:"href"`
|
||||
PropStats []*PropStat `xml:"propstat,omitempty"`
|
||||
}
|
||||
|
||||
// a request to find properties on an an entity or collection
|
||||
type Multistatus struct {
|
||||
XMLName xml.Name `xml:"DAV: multistatus"`
|
||||
Responses []*Response `xml:"response,omitempty"`
|
||||
}
|
23
vendor/github.com/dolanor/caldav-go/caldav/entities/prop.go
generated
vendored
Normal file
23
vendor/github.com/dolanor/caldav-go/caldav/entities/prop.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/dolanor/caldav-go/webdav/entities"
|
||||
)
|
||||
|
||||
// a CalDAV Property resource
|
||||
type Prop struct {
|
||||
XMLName xml.Name `xml:"DAV: prop"`
|
||||
GetContentType string `xml:"getcontenttype,omitempty"`
|
||||
DisplayName string `xml:"displayname,omitempty"`
|
||||
CalendarData *CalendarData `xml:",omitempty"`
|
||||
ResourceType *entities.ResourceType `xml:",omitempty"`
|
||||
CTag string `xml:"http://calendarserver.org/ns/ getctag,omitempty"`
|
||||
ETag string `xml:"http://calendarserver.org/ns/ getetag,omitempty"`
|
||||
}
|
||||
|
||||
// used to restrict properties returned in calendar data
|
||||
type PropertyName struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav prop"`
|
||||
Name string `xml:"name,attr"`
|
||||
}
|
56
vendor/github.com/dolanor/caldav-go/caldav/request.go
generated
vendored
Normal file
56
vendor/github.com/dolanor/caldav-go/caldav/request.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package caldav
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/dolanor/caldav-go/http"
|
||||
"github.com/dolanor/caldav-go/icalendar"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"github.com/dolanor/caldav-go/webdav"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// an CalDAV request object
|
||||
type Request webdav.Request
|
||||
|
||||
// downcasts the request to the WebDAV interface
|
||||
func (r *Request) WebDAV() *webdav.Request {
|
||||
return (*webdav.Request)(r)
|
||||
}
|
||||
|
||||
// creates a new CalDAV request object
|
||||
func NewRequest(method string, urlstr string, icaldata ...interface{}) (*Request, error) {
|
||||
if buffer, err := icalToReadCloser(icaldata...); err != nil {
|
||||
return nil, utils.NewError(NewRequest, "unable to encode icalendar data", icaldata, err)
|
||||
} else if r, err := http.NewRequest(method, urlstr, buffer); err != nil {
|
||||
return nil, utils.NewError(NewRequest, "unable to create request", urlstr, err)
|
||||
} else {
|
||||
if buffer != nil {
|
||||
// set the content type to XML if we have a body
|
||||
r.Native().Header.Set("Content-Type", "text/calendar; charset=UTF-8")
|
||||
}
|
||||
return (*Request)(r), nil
|
||||
}
|
||||
}
|
||||
|
||||
func icalToReadCloser(icaldata ...interface{}) (io.ReadCloser, error) {
|
||||
var buffer []string
|
||||
for _, icaldatum := range icaldata {
|
||||
if encoded, err := icalendar.Marshal(icaldatum); err != nil {
|
||||
return nil, utils.NewError(icalToReadCloser, "unable to encode as icalendar data", icaldatum, err)
|
||||
} else {
|
||||
// log.Printf("OUT: %+v", encoded)
|
||||
buffer = append(buffer, encoded)
|
||||
}
|
||||
}
|
||||
if len(buffer) > 0 {
|
||||
var encoded = strings.Join(buffer, "\n")
|
||||
return ioutil.NopCloser(bytes.NewBuffer([]byte(encoded))), nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
40
vendor/github.com/dolanor/caldav-go/caldav/response.go
generated
vendored
Normal file
40
vendor/github.com/dolanor/caldav-go/caldav/response.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package caldav
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/icalendar"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"github.com/dolanor/caldav-go/webdav"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// a WebDAV response object
|
||||
type Response webdav.Response
|
||||
|
||||
// downcasts the response to the WebDAV interface
|
||||
func (r *Response) WebDAV() *webdav.Response {
|
||||
return (*webdav.Response)(r)
|
||||
}
|
||||
|
||||
// decodes a CalDAV iCalendar response into the provided interface
|
||||
func (r *Response) Decode(into interface{}) error {
|
||||
if body := r.Body; body == nil {
|
||||
return nil
|
||||
} else if encoded, err := ioutil.ReadAll(body); err != nil {
|
||||
return utils.NewError(r.Decode, "unable to read response body", r, err)
|
||||
} else {
|
||||
// log.Printf("IN: %+v", string(encoded))
|
||||
if err := icalendar.Unmarshal(string(encoded), into); err != nil {
|
||||
return utils.NewError(r.Decode, "unable to decode response body", r, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new WebDAV response object
|
||||
func NewResponse(response *webdav.Response) *Response {
|
||||
return (*Response)(response)
|
||||
}
|
30
vendor/github.com/dolanor/caldav-go/caldav/server.go
generated
vendored
Normal file
30
vendor/github.com/dolanor/caldav-go/caldav/server.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package caldav
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"github.com/dolanor/caldav-go/webdav"
|
||||
)
|
||||
|
||||
// a server that accepts CalDAV requests
|
||||
type Server webdav.Server
|
||||
|
||||
// NewServer creates a reference to a CalDAV server.
|
||||
// host is the url to access the server, stopping at the port:
|
||||
// https://user:password@host:port/
|
||||
func NewServer(host string) (*Server, error) {
|
||||
if s, err := webdav.NewServer(host); err != nil {
|
||||
return nil, utils.NewError(NewServer, "unable to create WebDAV server", host, err)
|
||||
} else {
|
||||
return (*Server)(s), nil
|
||||
}
|
||||
}
|
||||
|
||||
// downcasts the server to the WebDAV interface
|
||||
func (s *Server) WebDAV() *webdav.Server {
|
||||
return (*webdav.Server)(s)
|
||||
}
|
||||
|
||||
// creates a new CalDAV request object
|
||||
func (s *Server) NewRequest(method string, path string, icaldata ...interface{}) (*Request, error) {
|
||||
return NewRequest(method, s.WebDAV().Http().AbsUrlStr(path), icaldata...)
|
||||
}
|
8
vendor/github.com/dolanor/caldav-go/caldav/values/component_name.go
generated
vendored
Normal file
8
vendor/github.com/dolanor/caldav-go/caldav/values/component_name.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package values
|
||||
|
||||
type ComponentName string
|
||||
|
||||
const (
|
||||
CalendarComponentName ComponentName = "VCALENDAR"
|
||||
EventComponentName = "VEVENT"
|
||||
)
|
31
vendor/github.com/dolanor/caldav-go/caldav/values/datetime.go
generated
vendored
Normal file
31
vendor/github.com/dolanor/caldav-go/caldav/values/datetime.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"github.com/dolanor/caldav-go/icalendar/values"
|
||||
"time"
|
||||
)
|
||||
|
||||
// a representation of a date and time for iCalendar
|
||||
type DateTime struct {
|
||||
name string
|
||||
t time.Time
|
||||
}
|
||||
|
||||
// creates a new caldav datetime representation, must be in UTC
|
||||
func NewDateTime(name string, t time.Time) (*DateTime, error) {
|
||||
if t.Location() != time.UTC {
|
||||
return nil, errors.New("CalDAV datetime must be in UTC")
|
||||
} else {
|
||||
return &DateTime{name: name, t: t.Truncate(time.Second)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// encodes the datetime value for the iCalendar specification
|
||||
func (d *DateTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
|
||||
layout := values.UTCDateTimeFormatString
|
||||
value := d.t.Format(layout)
|
||||
attr := xml.Attr{Name: name, Value: value}
|
||||
return attr, nil
|
||||
}
|
8
vendor/github.com/dolanor/caldav-go/caldav/values/human_boolean.go
generated
vendored
Normal file
8
vendor/github.com/dolanor/caldav-go/caldav/values/human_boolean.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package values
|
||||
|
||||
type HumanBoolean string
|
||||
|
||||
const (
|
||||
YesHumanBoolean HumanBoolean = "yes"
|
||||
NoHumanBoolean = "no"
|
||||
)
|
8
vendor/github.com/dolanor/caldav-go/caldav/values/text_collation.go
generated
vendored
Normal file
8
vendor/github.com/dolanor/caldav-go/caldav/values/text_collation.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package values
|
||||
|
||||
type TextCollation string
|
||||
|
||||
const (
|
||||
OctetTextCollation TextCollation = "i;octet"
|
||||
ASCIICaseMapCollation = "i;ascii-casemap"
|
||||
)
|
53
vendor/github.com/dolanor/caldav-go/http/client.go
generated
vendored
Normal file
53
vendor/github.com/dolanor/caldav-go/http/client.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// a client for making HTTP requests
|
||||
type Client struct {
|
||||
native *http.Client
|
||||
server *Server
|
||||
requestHeaders map[string]string
|
||||
}
|
||||
|
||||
func (c *Client) SetHeader(key string, value string) {
|
||||
if c.requestHeaders == nil {
|
||||
c.requestHeaders = map[string]string{}
|
||||
}
|
||||
c.requestHeaders[key] = value
|
||||
}
|
||||
|
||||
// downcasts to the native HTTP interface
|
||||
func (c *Client) Native() *http.Client {
|
||||
return c.native
|
||||
}
|
||||
|
||||
// returns the embedded HTTP server reference
|
||||
func (c *Client) Server() *Server {
|
||||
return c.server
|
||||
}
|
||||
|
||||
// executes an HTTP request
|
||||
func (c *Client) Do(req *Request) (*Response, error) {
|
||||
for key, value := range c.requestHeaders {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
if resp, err := c.Native().Do((*http.Request)(req)); err != nil {
|
||||
return nil, utils.NewError(c.Do, "unable to execute HTTP request", c, err)
|
||||
} else {
|
||||
return NewResponse(resp), nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new client for communicating with an HTTP server
|
||||
func NewClient(server *Server, native *http.Client) *Client {
|
||||
return &Client{server: server, native: native}
|
||||
}
|
||||
|
||||
// creates a new client for communicating with a server
|
||||
// uses the default HTTP client from net/http
|
||||
func NewDefaultClient(server *Server) *Client {
|
||||
return NewClient(server, http.DefaultClient)
|
||||
}
|
39
vendor/github.com/dolanor/caldav-go/http/request.go
generated
vendored
Normal file
39
vendor/github.com/dolanor/caldav-go/http/request.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// an HTTP request object
|
||||
type Request http.Request
|
||||
|
||||
// downcasts the request to the native HTTP interface
|
||||
func (r *Request) Native() *http.Request {
|
||||
return (*http.Request)(r)
|
||||
}
|
||||
|
||||
// creates a new HTTP request object
|
||||
func NewRequest(method string, urlstr string, body ...io.ReadCloser) (*Request, error) {
|
||||
|
||||
var err error
|
||||
var r = new(http.Request)
|
||||
|
||||
if len(body) > 0 && body[0] != nil {
|
||||
r, err = http.NewRequest(method, urlstr, body[0])
|
||||
} else {
|
||||
r, err = http.NewRequest(method, urlstr, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, utils.NewError(NewRequest, "unable to create request", urlstr, err)
|
||||
} else if auth := r.URL.User; auth != nil {
|
||||
pass, _ := auth.Password()
|
||||
r.SetBasicAuth(auth.Username(), pass)
|
||||
r.URL.User = nil
|
||||
}
|
||||
|
||||
return (*Request)(r), nil
|
||||
|
||||
}
|
18
vendor/github.com/dolanor/caldav-go/http/response.go
generated
vendored
Normal file
18
vendor/github.com/dolanor/caldav-go/http/response.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// an HTTP response object
|
||||
type Response http.Response
|
||||
|
||||
// downcasts the response to the native HTTP interface
|
||||
func (r *Response) Native() *http.Response {
|
||||
return (*http.Response)(r)
|
||||
}
|
||||
|
||||
// creates a new HTTP response object
|
||||
func NewResponse(response *http.Response) *Response {
|
||||
return (*Response)(response)
|
||||
}
|
48
vendor/github.com/dolanor/caldav-go/http/server.go
generated
vendored
Normal file
48
vendor/github.com/dolanor/caldav-go/http/server.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
spath "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// a server that accepts HTTP requests
|
||||
type Server struct {
|
||||
baseUrl *url.URL
|
||||
}
|
||||
|
||||
// creates a reference to an http server
|
||||
func NewServer(baseUrlStr string) (*Server, error) {
|
||||
var err error
|
||||
var s = new(Server)
|
||||
if s.baseUrl, err = url.Parse(baseUrlStr); err != nil {
|
||||
return nil, utils.NewError(NewServer, "unable to parse server base url", baseUrlStr, err)
|
||||
} else {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
|
||||
// converts a path name to an absolute URL
|
||||
func (s *Server) UserInfo() *url.Userinfo {
|
||||
return s.baseUrl.User
|
||||
}
|
||||
|
||||
// converts a path name to an absolute URL
|
||||
func (s *Server) AbsUrlStr(path string) string {
|
||||
uri := *s.baseUrl
|
||||
uri.Path = spath.Join(uri.Path, path)
|
||||
if strings.HasSuffix(path, "/") {
|
||||
uri.Path = uri.Path + "/"
|
||||
}
|
||||
return uri.String()
|
||||
}
|
||||
|
||||
// creates a new HTTP request object
|
||||
func (s *Server) NewRequest(method string, path string, body ...io.ReadCloser) (*Request, error) {
|
||||
return NewRequest(method, s.AbsUrlStr(path), body...)
|
||||
}
|
87
vendor/github.com/dolanor/caldav-go/icalendar/components/calendar.go
generated
vendored
Normal file
87
vendor/github.com/dolanor/caldav-go/icalendar/components/calendar.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/values"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Calendar struct {
|
||||
|
||||
// specifies the identifier corresponding to the highest version number or the minimum and maximum
|
||||
// range of the iCalendar specification that is required in order to interpret the iCalendar object.
|
||||
Version string `ical:",2.0"`
|
||||
|
||||
// specifies the identifier for the product that created the iCalendar object
|
||||
ProductId string `ical:"prodid,-//dolanor/caldav-go//NONSGML v1.0.0//EN"`
|
||||
|
||||
// specifies the text value that uniquely identifies the "VTIMEZONE" calendar component.
|
||||
TimeZoneId string `ical:"tzid,omitempty"`
|
||||
|
||||
// defines the iCalendar object method associated with the calendar object.
|
||||
Method values.Method `ical:",omitempty"`
|
||||
|
||||
// defines the calendar scale used for the calendar information specified in the iCalendar object.
|
||||
CalScale values.CalScale `ical:",omitempty"`
|
||||
|
||||
// defines the different timezones used by the various components nested within
|
||||
TimeZones []*TimeZone `ical:",omitempty"`
|
||||
|
||||
// unique events to be stored together in the icalendar file
|
||||
Events []*Event `ical:",omitempty"`
|
||||
}
|
||||
|
||||
func (c *Calendar) UseTimeZone(location *time.Location) *TimeZone {
|
||||
tz := NewDynamicTimeZone(location)
|
||||
c.TimeZones = append(c.TimeZones, tz)
|
||||
c.TimeZoneId = tz.Id
|
||||
return tz
|
||||
}
|
||||
|
||||
func (c *Calendar) UsingTimeZone() bool {
|
||||
return len(c.TimeZoneId) > 0
|
||||
}
|
||||
|
||||
func (c *Calendar) UsingGlobalTimeZone() bool {
|
||||
return c.UsingTimeZone() && c.TimeZoneId[0] == '/'
|
||||
}
|
||||
|
||||
func (c *Calendar) ValidateICalValue() error {
|
||||
|
||||
for i, e := range c.Events {
|
||||
|
||||
if e == nil {
|
||||
continue // skip nil events
|
||||
}
|
||||
|
||||
if err := e.ValidateICalValue(); err != nil {
|
||||
msg := fmt.Sprintf("event %d failed validation", i)
|
||||
return utils.NewError(c.ValidateICalValue, msg, c, err)
|
||||
}
|
||||
|
||||
if e.DateStart == nil && c.Method == "" {
|
||||
msg := fmt.Sprintf("no value for method and no start date defined on event %d", i)
|
||||
return utils.NewError(c.ValidateICalValue, msg, c, nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if c.UsingTimeZone() && !c.UsingGlobalTimeZone() {
|
||||
for i, t := range c.TimeZones {
|
||||
if t == nil || t.Id != c.TimeZoneId {
|
||||
msg := fmt.Sprintf("timezone ID does not match timezone %d", i)
|
||||
return utils.NewError(c.ValidateICalValue, msg, c, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func NewCalendar(events ...*Event) *Calendar {
|
||||
cal := new(Calendar)
|
||||
cal.Events = events
|
||||
return cal
|
||||
}
|
172
vendor/github.com/dolanor/caldav-go/icalendar/components/event.go
generated
vendored
Normal file
172
vendor/github.com/dolanor/caldav-go/icalendar/components/event.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/icalendar/values"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
|
||||
// defines the persistent, globally unique identifier for the calendar component.
|
||||
UID string `ical:",required"`
|
||||
|
||||
// indicates the date/time that the instance of the iCalendar object was created.
|
||||
DateStamp *values.DateTime `ical:"dtstamp,required"`
|
||||
|
||||
// specifies when the calendar component begins.
|
||||
DateStart *values.DateTime `ical:"dtstart,required"`
|
||||
|
||||
// specifies the date and time that a calendar component ends.
|
||||
DateEnd *values.DateTime `ical:"dtend,omitempty"`
|
||||
|
||||
// specifies a positive duration of time.
|
||||
Duration *values.Duration `ical:",omitempty"`
|
||||
|
||||
// defines the access classification for a calendar component.
|
||||
AccessClassification values.EventAccessClassification `ical:"class,omitempty"`
|
||||
|
||||
// specifies the date and time that the calendar information was created by the calendar user agent in the
|
||||
// calendar store.
|
||||
// Note: This is analogous to the creation date and time for a file in the file system.
|
||||
Created *values.DateTime `ical:",omitempty"`
|
||||
|
||||
// provides a more complete description of the calendar component, than that provided by the Summary property.
|
||||
Description string `ical:",omitempty"`
|
||||
|
||||
// specifies information related to the global position for the activity specified by a calendar component.
|
||||
Geo *values.Geo `ical:",omitempty"`
|
||||
|
||||
// specifies the date and time that the information associated with the calendar component was last revised in the
|
||||
// calendar store.
|
||||
// Note: This is analogous to the modification date and time for a file in the file system.
|
||||
LastModified *values.DateTime `ical:"last-modified,omitempty"`
|
||||
|
||||
// defines the intended venue for the activity defined by a calendar component.
|
||||
Location *values.Location `ical:",omitempty"`
|
||||
|
||||
// defines the organizer for a calendar component.
|
||||
Organizer *values.OrganizerContact `ical:",omitempty"`
|
||||
|
||||
// defines the relative priority for a calendar component.
|
||||
Priority int `ical:",omitempty"`
|
||||
|
||||
// defines the revision sequence number of the calendar component within a sequence of revisions.
|
||||
Sequence int `ical:",omitempty"`
|
||||
|
||||
// efines the overall status or confirmation for the calendar component.
|
||||
Status values.EventStatus `ical:",omitempty"`
|
||||
|
||||
// defines a short summary or subject for the calendar component.
|
||||
Summary string `ical:",omitempty"`
|
||||
|
||||
// defines whether an event is transparent or not to busy time searches.
|
||||
values.TimeTransparency `ical:"transp,omitempty"`
|
||||
|
||||
// defines a Uniform Resource Locator (URL) associated with the iCalendar object.
|
||||
Url *values.Url `ical:",omitempty"`
|
||||
|
||||
// used in conjunction with the "UID" and "SEQUENCE" property to identify a specific instance of a recurring
|
||||
// event calendar component. The property value is the effective value of the DateStart property of the
|
||||
// recurrence instance.
|
||||
RecurrenceId *values.DateTime `ical:"recurrence_id,omitempty"`
|
||||
|
||||
// defines a rule or repeating pattern for recurring events, to-dos, or time zone definitions.
|
||||
RecurrenceRules []*values.RecurrenceRule `ical:",omitempty"`
|
||||
|
||||
// property provides the capability to associate a document object with a calendar component.
|
||||
Attachment *values.Url `ical:"attach,omitempty"`
|
||||
|
||||
// defines an "Attendee" within a calendar component.
|
||||
Attendees []*values.AttendeeContact `ical:",omitempty"`
|
||||
|
||||
// defines the categories for a calendar component.
|
||||
Categories *values.CSV `ical:",omitempty"`
|
||||
|
||||
// specifies non-processing information intended to provide a comment to the calendar user.
|
||||
Comments []values.Comment `ical:",omitempty"`
|
||||
|
||||
// used to represent contact information or alternately a reference to contact information associated with the calendar component.
|
||||
ContactInfo *values.CSV `ical:"contact,omitempty"`
|
||||
|
||||
// defines the list of date/time exceptions for a recurring calendar component.
|
||||
*values.ExceptionDateTimes `ical:",omitempty"`
|
||||
|
||||
// defines the list of date/times for a recurrence set.
|
||||
*values.RecurrenceDateTimes `ical:",omitempty"`
|
||||
|
||||
// used to represent a relationship or reference between one calendar component and another.
|
||||
RelatedTo *values.Url `ical:"related-to,omitempty"`
|
||||
|
||||
// defines the equipment or resources anticipated for an activity specified by a calendar entity.
|
||||
Resources *values.CSV `ical:",omitempty"`
|
||||
}
|
||||
|
||||
// validates the event internals
|
||||
func (e *Event) ValidateICalValue() error {
|
||||
|
||||
if e.UID == "" {
|
||||
return utils.NewError(e.ValidateICalValue, "the UID value must be set", e, nil)
|
||||
}
|
||||
|
||||
if e.DateStart == nil {
|
||||
return utils.NewError(e.ValidateICalValue, "event start date must be set", e, nil)
|
||||
}
|
||||
|
||||
if e.DateEnd == nil && e.Duration == nil {
|
||||
return utils.NewError(e.ValidateICalValue, "event end date or duration must be set", e, nil)
|
||||
}
|
||||
|
||||
if e.DateEnd != nil && e.Duration != nil {
|
||||
return utils.NewError(e.ValidateICalValue, "event end date and duration are mutually exclusive fields", e, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// adds one or more recurrence rule to the event
|
||||
func (e *Event) AddRecurrenceRules(r ...*values.RecurrenceRule) {
|
||||
e.RecurrenceRules = append(e.RecurrenceRules, r...)
|
||||
}
|
||||
|
||||
// adds one or more recurrence rule exception to the event
|
||||
func (e *Event) AddRecurrenceExceptions(d ...*values.DateTime) {
|
||||
if e.ExceptionDateTimes == nil {
|
||||
e.ExceptionDateTimes = new(values.ExceptionDateTimes)
|
||||
}
|
||||
*e.ExceptionDateTimes = append(*e.ExceptionDateTimes, d...)
|
||||
}
|
||||
|
||||
// checks to see if the event is a recurrence
|
||||
func (e *Event) IsRecurrence() bool {
|
||||
return e.RecurrenceId != nil
|
||||
}
|
||||
|
||||
// checks to see if the event is a recurrence override
|
||||
func (e *Event) IsOverride() bool {
|
||||
return e.IsRecurrence() && !e.RecurrenceId.Equals(e.DateStart)
|
||||
}
|
||||
|
||||
// creates a new iCalendar event with no end time
|
||||
func NewEvent(uid string, start time.Time) *Event {
|
||||
e := new(Event)
|
||||
e.UID = uid
|
||||
e.DateStamp = values.NewDateTime(time.Now().UTC())
|
||||
e.DateStart = values.NewDateTime(start)
|
||||
return e
|
||||
}
|
||||
|
||||
// creates a new iCalendar event that lasts a certain duration
|
||||
func NewEventWithDuration(uid string, start time.Time, duration time.Duration) *Event {
|
||||
e := NewEvent(uid, start)
|
||||
e.Duration = values.NewDuration(duration)
|
||||
return e
|
||||
}
|
||||
|
||||
// creates a new iCalendar event that has an explicit start and end time
|
||||
func NewEventWithEnd(uid string, start time.Time, end time.Time) *Event {
|
||||
e := NewEvent(uid, start)
|
||||
e.DateEnd = values.NewDateTime(end)
|
||||
return e
|
||||
}
|
40
vendor/github.com/dolanor/caldav-go/icalendar/components/timezone.go
generated
vendored
Normal file
40
vendor/github.com/dolanor/caldav-go/icalendar/components/timezone.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/values"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TimeZone struct {
|
||||
|
||||
// defines the persistent, globally unique identifier for the calendar component.
|
||||
Id string `ical:"tzid,required"`
|
||||
|
||||
// the location name, as defined by the standards body
|
||||
ExtLocationName string `ical:"x-lic-location,omitempty"`
|
||||
|
||||
// defines a Uniform Resource Locator (URL) associated with the iCalendar object.
|
||||
Url *values.Url `ical:"tzurl,omitempty"`
|
||||
|
||||
// specifies the date and time that the information associated with the calendar component was last revised in the
|
||||
// calendar store.
|
||||
// Note: This is analogous to the modification date and time for a file in the file system.
|
||||
LastModified *values.DateTime `ical:"last-modified,omitempty"`
|
||||
|
||||
// TODO need to figure out how to handle standard and daylight savings time
|
||||
|
||||
}
|
||||
|
||||
func NewDynamicTimeZone(location *time.Location) *TimeZone {
|
||||
t := new(TimeZone)
|
||||
t.Id = location.String()
|
||||
t.ExtLocationName = location.String()
|
||||
t.Url = values.NewUrl(url.URL{
|
||||
Scheme: "http",
|
||||
Host: "tzurl.org",
|
||||
Path: fmt.Sprintf("/zoneinfo/%s", t.Id),
|
||||
})
|
||||
return t
|
||||
}
|
196
vendor/github.com/dolanor/caldav-go/icalendar/marshal.go
generated
vendored
Normal file
196
vendor/github.com/dolanor/caldav-go/icalendar/marshal.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
package icalendar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
Newline = "\r\n"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
type encoder func(reflect.Value) (string, error)
|
||||
|
||||
func tagAndJoinValue(v reflect.Value, in []string) (string, error) {
|
||||
if tag, err := extractTagFromValue(v); err != nil {
|
||||
return "", utils.NewError(tagAndJoinValue, "unable to extract tag from value", v, err)
|
||||
} else {
|
||||
var out []string
|
||||
out = append(out, properties.MarshalProperty(properties.NewProperty("begin", tag)))
|
||||
out = append(out, in...)
|
||||
out = append(out, properties.MarshalProperty(properties.NewProperty("end", tag)))
|
||||
return strings.Join(out, Newline), nil
|
||||
}
|
||||
}
|
||||
|
||||
func marshalCollection(v reflect.Value) (string, error) {
|
||||
|
||||
var out []string
|
||||
|
||||
for i, n := 0, v.Len(); i < n; i++ {
|
||||
vi := v.Index(i).Interface()
|
||||
if encoded, err := Marshal(vi); err != nil {
|
||||
msg := fmt.Sprintf("unable to encode interface at index %d", i)
|
||||
return "", utils.NewError(marshalCollection, msg, vi, err)
|
||||
} else if encoded != "" {
|
||||
out = append(out, encoded)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(out, Newline), nil
|
||||
|
||||
}
|
||||
|
||||
func marshalStruct(v reflect.Value) (string, error) {
|
||||
|
||||
var out []string
|
||||
|
||||
// iterate over all fields
|
||||
vtype := v.Type()
|
||||
n := vtype.NumField()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
// keep a reference to the field value and definition
|
||||
fv := v.Field(i)
|
||||
fs := vtype.Field(i)
|
||||
|
||||
// use the field definition to extract out property defaults
|
||||
p := properties.PropertyFromStructField(fs)
|
||||
if p == nil {
|
||||
continue // skip explicitly ignored fields and private members
|
||||
}
|
||||
|
||||
fi := fv.Interface()
|
||||
|
||||
// some fields are not properties, but actually nested objects.
|
||||
// detect those early using the property and object encoder...
|
||||
if _, ok := fi.(properties.CanEncodeValue); !ok && !isInvalidOrEmptyValue(fv) {
|
||||
if encoded, err := encode(fv, objectEncoder); err != nil {
|
||||
msg := fmt.Sprintf("unable to encode field %s", fs.Name)
|
||||
return "", utils.NewError(marshalStruct, msg, v.Interface(), err)
|
||||
} else if encoded != "" {
|
||||
// encoding worked! no need to process as a property
|
||||
out = append(out, encoded)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// now check to see if the field value overrides the defaults...
|
||||
if !isInvalidOrEmptyValue(fv) {
|
||||
// first, check the field value interface for overrides...
|
||||
if overrides, err := properties.PropertyFromInterface(fi); err != nil {
|
||||
msg := fmt.Sprintf("field %s failed validation", fs.Name)
|
||||
return "", utils.NewError(marshalStruct, msg, v.Interface(), err)
|
||||
} else if p.Merge(overrides); p.Value == "" {
|
||||
// then, if we couldn't find an override from the interface,
|
||||
// try the simple string encoder...
|
||||
if p.Value, err = stringEncoder(fv); err != nil {
|
||||
msg := fmt.Sprintf("unable to encode field %s", fs.Name)
|
||||
return "", utils.NewError(marshalStruct, msg, v.Interface(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have a value by this point
|
||||
if !p.HasNameAndValue() {
|
||||
if p.OmitEmpty {
|
||||
continue
|
||||
} else if p.DefaultValue != "" {
|
||||
p.Value = p.DefaultValue
|
||||
} else if p.Required {
|
||||
msg := fmt.Sprintf("missing value for required field %s", fs.Name)
|
||||
return "", utils.NewError(Marshal, msg, v.Interface(), nil)
|
||||
}
|
||||
}
|
||||
|
||||
// encode in the property
|
||||
out = append(out, properties.MarshalProperty(p))
|
||||
|
||||
}
|
||||
|
||||
// wrap the fields in the enclosing struct tags
|
||||
return tagAndJoinValue(v, out)
|
||||
|
||||
}
|
||||
|
||||
func objectEncoder(v reflect.Value) (string, error) {
|
||||
|
||||
// decompose the value into its interface parts
|
||||
v = dereferencePointerValue(v)
|
||||
|
||||
// encode the value based off of its type
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
return marshalCollection(v)
|
||||
case reflect.Struct:
|
||||
return marshalStruct(v)
|
||||
}
|
||||
|
||||
return "", nil
|
||||
|
||||
}
|
||||
|
||||
func stringEncoder(v reflect.Value) (string, error) {
|
||||
return fmt.Sprintf("%v", v.Interface()), nil
|
||||
}
|
||||
|
||||
func propertyEncoder(v reflect.Value) (string, error) {
|
||||
|
||||
vi := v.Interface()
|
||||
if p, err := properties.PropertyFromInterface(vi); err != nil {
|
||||
|
||||
// return early if interface fails its own validation
|
||||
return "", err
|
||||
|
||||
} else if p.HasNameAndValue() {
|
||||
|
||||
// if an interface encodes its own name and value, it's a property
|
||||
return properties.MarshalProperty(p), nil
|
||||
|
||||
}
|
||||
|
||||
return "", nil
|
||||
|
||||
}
|
||||
|
||||
func encode(v reflect.Value, encoders ...encoder) (string, error) {
|
||||
|
||||
for _, encode := range encoders {
|
||||
if encoded, err := encode(v); err != nil {
|
||||
return "", err
|
||||
} else if encoded != "" {
|
||||
return encoded, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
|
||||
}
|
||||
|
||||
// converts an iCalendar component into its string representation
|
||||
func Marshal(target interface{}) (string, error) {
|
||||
|
||||
// don't do anything with invalid interfaces
|
||||
v := reflect.ValueOf(target)
|
||||
if isInvalidOrEmptyValue(v) {
|
||||
return "", utils.NewError(Marshal, "unable to marshal empty or invalid values", target, nil)
|
||||
}
|
||||
|
||||
if encoded, err := encode(v, propertyEncoder, objectEncoder, stringEncoder); err != nil {
|
||||
return "", err
|
||||
} else if encoded == "" {
|
||||
return "", utils.NewError(Marshal, "unable to encode interface, all methods exhausted", v.Interface(), nil)
|
||||
} else {
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
}
|
29
vendor/github.com/dolanor/caldav-go/icalendar/properties/interfaces.go
generated
vendored
Normal file
29
vendor/github.com/dolanor/caldav-go/icalendar/properties/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package properties
|
||||
|
||||
type CanValidateValue interface {
|
||||
ValidateICalValue() error
|
||||
}
|
||||
|
||||
type CanDecodeValue interface {
|
||||
DecodeICalValue(string) error
|
||||
}
|
||||
|
||||
type CanDecodeParams interface {
|
||||
DecodeICalParams(Params) error
|
||||
}
|
||||
|
||||
type CanEncodeTag interface {
|
||||
EncodeICalTag() (string, error)
|
||||
}
|
||||
|
||||
type CanEncodeValue interface {
|
||||
EncodeICalValue() (string, error)
|
||||
}
|
||||
|
||||
type CanEncodeName interface {
|
||||
EncodeICalName() (PropertyName, error)
|
||||
}
|
||||
|
||||
type CanEncodeParams interface {
|
||||
EncodeICalParams() (Params, error)
|
||||
}
|
31
vendor/github.com/dolanor/caldav-go/icalendar/properties/names.go
generated
vendored
Normal file
31
vendor/github.com/dolanor/caldav-go/icalendar/properties/names.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package properties
|
||||
|
||||
import "strings"
|
||||
|
||||
type PropertyName string
|
||||
|
||||
const (
|
||||
UIDPropertyName PropertyName = "UID"
|
||||
CommentPropertyName = "COMMENT"
|
||||
OrganizerPropertyName = "ORGANIZER"
|
||||
AttendeePropertyName = "ATTENDEE"
|
||||
ExceptionDateTimesPropertyName = "EXDATE"
|
||||
RecurrenceDateTimesPropertyName = "RDATE"
|
||||
RecurrenceRulePropertyName = "RRULE"
|
||||
LocationPropertyName = "LOCATION"
|
||||
)
|
||||
|
||||
type ParameterName string
|
||||
|
||||
const (
|
||||
CanonicalNameParameterName ParameterName = "CN"
|
||||
TimeZoneIdPropertyName = "TZID"
|
||||
ValuePropertyName = "VALUE"
|
||||
AlternateRepresentationName = "ALTREP"
|
||||
)
|
||||
|
||||
type Params map[ParameterName]string
|
||||
|
||||
func (p PropertyName) Equals(test string) bool {
|
||||
return strings.EqualFold(string(p), test)
|
||||
}
|
177
vendor/github.com/dolanor/caldav-go/icalendar/properties/property.go
generated
vendored
Normal file
177
vendor/github.com/dolanor/caldav-go/icalendar/properties/property.go
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
package properties
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
var propNameSanitizer = strings.NewReplacer(
|
||||
"_", "-",
|
||||
":", "\\:",
|
||||
)
|
||||
|
||||
var propValueSanitizer = strings.NewReplacer(
|
||||
"\"", "'",
|
||||
"\\", "\\\\",
|
||||
"\n", "\\n",
|
||||
)
|
||||
|
||||
var propNameDesanitizer = strings.NewReplacer(
|
||||
"-", "_",
|
||||
"\\:", ":",
|
||||
)
|
||||
|
||||
var propValueDesanitizer = strings.NewReplacer(
|
||||
"'", "\"",
|
||||
"\\\\", "\\",
|
||||
"\\n", "\n",
|
||||
)
|
||||
|
||||
type Property struct {
|
||||
Name PropertyName
|
||||
Value, DefaultValue string
|
||||
Params Params
|
||||
OmitEmpty, Required bool
|
||||
}
|
||||
|
||||
func (p *Property) HasNameAndValue() bool {
|
||||
return p.Name != "" && p.Value != ""
|
||||
}
|
||||
|
||||
func (p *Property) Merge(override *Property) {
|
||||
if override.Name != "" {
|
||||
p.Name = override.Name
|
||||
}
|
||||
if override.Value != "" {
|
||||
p.Value = override.Value
|
||||
}
|
||||
if override.Params != nil {
|
||||
p.Params = override.Params
|
||||
}
|
||||
}
|
||||
|
||||
func PropertyFromStructField(fs reflect.StructField) (p *Property) {
|
||||
|
||||
ftag := fs.Tag.Get("ical")
|
||||
if fs.PkgPath != "" || ftag == "-" {
|
||||
return
|
||||
}
|
||||
|
||||
p = new(Property)
|
||||
|
||||
// parse the field tag
|
||||
if ftag != "" {
|
||||
tags := strings.Split(ftag, ",")
|
||||
p.Name = PropertyName(tags[0])
|
||||
if len(tags) > 1 {
|
||||
if tags[1] == "omitempty" {
|
||||
p.OmitEmpty = true
|
||||
} else if tags[1] == "required" {
|
||||
p.Required = true
|
||||
} else {
|
||||
p.DefaultValue = tags[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have a name
|
||||
if p.Name == "" {
|
||||
p.Name = PropertyName(fs.Name)
|
||||
}
|
||||
|
||||
p.Name = PropertyName(strings.ToUpper(string(p.Name)))
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func MarshalProperty(p *Property) string {
|
||||
name := strings.ToUpper(propNameSanitizer.Replace(string(p.Name)))
|
||||
value := propValueSanitizer.Replace(p.Value)
|
||||
keys := []string{name}
|
||||
for name, value := range p.Params {
|
||||
name = ParameterName(strings.ToUpper(propNameSanitizer.Replace(string(name))))
|
||||
value = propValueSanitizer.Replace(value)
|
||||
if strings.ContainsAny(value, " :") {
|
||||
keys = append(keys, fmt.Sprintf("%s=\"%s\"", name, value))
|
||||
} else {
|
||||
keys = append(keys, fmt.Sprintf("%s=%s", name, value))
|
||||
}
|
||||
}
|
||||
name = strings.Join(keys, ";")
|
||||
return fmt.Sprintf("%s:%s", name, value)
|
||||
}
|
||||
|
||||
func PropertyFromInterface(target interface{}) (p *Property, err error) {
|
||||
|
||||
var ierr error
|
||||
if va, ok := target.(CanValidateValue); ok {
|
||||
if ierr = va.ValidateICalValue(); ierr != nil {
|
||||
err = utils.NewError(PropertyFromInterface, "interface failed validation", target, ierr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
p = new(Property)
|
||||
|
||||
if enc, ok := target.(CanEncodeName); ok {
|
||||
if p.Name, ierr = enc.EncodeICalName(); ierr != nil {
|
||||
err = utils.NewError(PropertyFromInterface, "interface failed name encoding", target, ierr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if enc, ok := target.(CanEncodeParams); ok {
|
||||
if p.Params, ierr = enc.EncodeICalParams(); ierr != nil {
|
||||
err = utils.NewError(PropertyFromInterface, "interface failed params encoding", target, ierr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if enc, ok := target.(CanEncodeValue); ok {
|
||||
if p.Value, ierr = enc.EncodeICalValue(); ierr != nil {
|
||||
err = utils.NewError(PropertyFromInterface, "interface failed value encoding", target, ierr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func UnmarshalProperty(line string) *Property {
|
||||
nvp := strings.SplitN(line, ":", 2)
|
||||
prop := new(Property)
|
||||
if len(nvp) > 1 {
|
||||
prop.Value = strings.TrimSpace(nvp[1])
|
||||
}
|
||||
npp := strings.Split(nvp[0], ";")
|
||||
if len(npp) > 1 {
|
||||
prop.Params = make(map[ParameterName]string, 0)
|
||||
for i := 1; i < len(npp); i++ {
|
||||
var key, value string
|
||||
kvp := strings.Split(npp[i], "=")
|
||||
key = strings.TrimSpace(kvp[0])
|
||||
key = propNameDesanitizer.Replace(key)
|
||||
if len(kvp) > 1 {
|
||||
value = strings.TrimSpace(kvp[1])
|
||||
value = propValueDesanitizer.Replace(value)
|
||||
value = strings.Trim(value, "\"")
|
||||
}
|
||||
prop.Params[ParameterName(key)] = value
|
||||
}
|
||||
}
|
||||
prop.Name = PropertyName(strings.TrimSpace(npp[0]))
|
||||
prop.Name = PropertyName(propNameDesanitizer.Replace(string(prop.Name)))
|
||||
prop.Value = propValueDesanitizer.Replace(prop.Value)
|
||||
return prop
|
||||
}
|
||||
|
||||
func NewProperty(name, value string) *Property {
|
||||
return &Property{Name: PropertyName(name), Value: value}
|
||||
}
|
78
vendor/github.com/dolanor/caldav-go/icalendar/reflect.go
generated
vendored
Normal file
78
vendor/github.com/dolanor/caldav-go/icalendar/reflect.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
package icalendar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
func isInvalidOrEmptyValue(v reflect.Value) bool {
|
||||
if !v.IsValid() {
|
||||
return true
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func newValue(in reflect.Value) (out reflect.Value, isArrayElement bool) {
|
||||
|
||||
typ := in.Type()
|
||||
kind := typ.Kind()
|
||||
|
||||
for {
|
||||
if kind == reflect.Array || kind == reflect.Slice {
|
||||
isArrayElement = true
|
||||
} else if kind != reflect.Ptr {
|
||||
break
|
||||
}
|
||||
typ = typ.Elem()
|
||||
kind = typ.Kind()
|
||||
}
|
||||
|
||||
out = reflect.New(typ)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func dereferencePointerValue(v reflect.Value) reflect.Value {
|
||||
for (v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr) && v.Elem().IsValid() {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func extractTagFromValue(v reflect.Value) (string, error) {
|
||||
|
||||
vdref := dereferencePointerValue(v)
|
||||
vtemp, _ := newValue(vdref)
|
||||
|
||||
if encoder, ok := vtemp.Interface().(properties.CanEncodeTag); ok {
|
||||
if tag, err := encoder.EncodeICalTag(); err != nil {
|
||||
return "", utils.NewError(extractTagFromValue, "unable to extract tag from interface", v.Interface(), err)
|
||||
} else {
|
||||
return strings.ToUpper(tag), nil
|
||||
}
|
||||
} else {
|
||||
typ := vtemp.Elem().Type()
|
||||
return strings.ToUpper(fmt.Sprintf("v%s", typ.Name())), nil
|
||||
}
|
||||
|
||||
}
|
365
vendor/github.com/dolanor/caldav-go/icalendar/unmarshal.go
generated
vendored
Normal file
365
vendor/github.com/dolanor/caldav-go/icalendar/unmarshal.go
generated
vendored
Normal file
@ -0,0 +1,365 @@
|
||||
package icalendar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
var splitter = regexp.MustCompile("\r?\n")
|
||||
|
||||
type token struct {
|
||||
name string
|
||||
components map[string][]*token
|
||||
properties map[properties.PropertyName][]*properties.Property
|
||||
}
|
||||
|
||||
func tokenize(encoded string) (*token, error) {
|
||||
if encoded = strings.TrimSpace(encoded); encoded == "" {
|
||||
return nil, utils.NewError(tokenize, "no content to tokenize", encoded, nil)
|
||||
}
|
||||
return tokenizeSlice(splitter.Split(encoded, -1))
|
||||
}
|
||||
|
||||
func tokenizeSlice(slice []string, name ...string) (*token, error) {
|
||||
|
||||
tok := new(token)
|
||||
size := len(slice)
|
||||
|
||||
if len(name) > 0 {
|
||||
tok.name = name[0]
|
||||
} else if size <= 0 {
|
||||
return nil, utils.NewError(tokenizeSlice, "token has no content", slice, nil)
|
||||
}
|
||||
|
||||
tok.properties = make(map[properties.PropertyName][]*properties.Property, 0)
|
||||
tok.components = make(map[string][]*token, 0)
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
|
||||
// Handle iCalendar's space-indented line break format
|
||||
// See: https://www.ietf.org/rfc/rfc2445.txt section 4.1
|
||||
// "a long line can be split between any two characters by inserting a CRLF immediately followed by a single
|
||||
// linear white space character"
|
||||
line := slice[i]
|
||||
for ; i < size-1 && strings.HasPrefix(slice[i+1], " "); i++ {
|
||||
next := slice[i+1]
|
||||
line += next[1:len(next)]
|
||||
}
|
||||
|
||||
prop := properties.UnmarshalProperty(line)
|
||||
|
||||
if prop.Name.Equals("begin") {
|
||||
for j := i; j < size; j++ {
|
||||
end := strings.Replace(line, "BEGIN", "END", 1)
|
||||
if slice[j] == end {
|
||||
if component, err := tokenizeSlice(slice[i+1:j], prop.Value); err != nil {
|
||||
msg := fmt.Sprintf("unable to tokenize %s component", prop.Value)
|
||||
return nil, utils.NewError(tokenizeSlice, msg, slice, err)
|
||||
} else {
|
||||
existing, _ := tok.components[prop.Value]
|
||||
tok.components[prop.Value] = append(existing, component)
|
||||
i = j
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if existing, ok := tok.properties[prop.Name]; ok {
|
||||
tok.properties[prop.Name] = []*properties.Property{prop}
|
||||
} else {
|
||||
tok.properties[prop.Name] = append(existing, prop)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return tok, nil
|
||||
|
||||
}
|
||||
|
||||
func hydrateInterface(v reflect.Value, prop *properties.Property) (bool, error) {
|
||||
|
||||
// unable to decode into empty values
|
||||
if isInvalidOrEmptyValue(v) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var i = v.Interface()
|
||||
var hasValue = false
|
||||
|
||||
// decode a value if possible
|
||||
if decoder, ok := i.(properties.CanDecodeValue); ok {
|
||||
if err := decoder.DecodeICalValue(prop.Value); err != nil {
|
||||
return false, utils.NewError(hydrateInterface, "error decoding property value", v, err)
|
||||
} else {
|
||||
hasValue = true
|
||||
}
|
||||
}
|
||||
|
||||
// decode any params, if supported
|
||||
if len(prop.Params) > 0 {
|
||||
if decoder, ok := i.(properties.CanDecodeParams); ok {
|
||||
if err := decoder.DecodeICalParams(prop.Params); err != nil {
|
||||
return false, utils.NewError(hydrateInterface, "error decoding property parameters", v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finish with any validation
|
||||
if validator, ok := i.(properties.CanValidateValue); ok {
|
||||
if err := validator.ValidateICalValue(); err != nil {
|
||||
return false, utils.NewError(hydrateInterface, "error validating property value", v, err)
|
||||
}
|
||||
}
|
||||
|
||||
return hasValue, nil
|
||||
|
||||
}
|
||||
|
||||
func hydrateLiteral(v reflect.Value, prop *properties.Property) (reflect.Value, error) {
|
||||
|
||||
literal := dereferencePointerValue(v)
|
||||
|
||||
switch literal.Kind() {
|
||||
case reflect.Bool:
|
||||
if i, err := strconv.ParseBool(prop.Value); err != nil {
|
||||
return literal, utils.NewError(hydrateLiteral, "unable to decode bool "+prop.Value, literal.Interface(), err)
|
||||
} else {
|
||||
literal.SetBool(i)
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if i, err := strconv.ParseInt(prop.Value, 10, 64); err != nil {
|
||||
return literal, utils.NewError(hydrateLiteral, "unable to decode int "+prop.Value, literal.Interface(), err)
|
||||
} else {
|
||||
literal.SetInt(i)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
if i, err := strconv.ParseUint(prop.Value, 10, 64); err != nil {
|
||||
return literal, utils.NewError(hydrateLiteral, "unable to decode uint "+prop.Value, literal.Interface(), err)
|
||||
} else {
|
||||
literal.SetUint(i)
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if i, err := strconv.ParseFloat(prop.Value, 64); err != nil {
|
||||
return literal, utils.NewError(hydrateLiteral, "unable to decode float "+prop.Value, literal.Interface(), err)
|
||||
} else {
|
||||
literal.SetFloat(i)
|
||||
}
|
||||
case reflect.String:
|
||||
literal.SetString(prop.Value)
|
||||
default:
|
||||
return literal, utils.NewError(hydrateLiteral, "unable to decode value as literal "+prop.Value, literal.Interface(), nil)
|
||||
}
|
||||
|
||||
return literal, nil
|
||||
|
||||
}
|
||||
|
||||
func hydrateProperty(v reflect.Value, prop *properties.Property) error {
|
||||
|
||||
// check to see if the interface handles it's own hydration
|
||||
if handled, err := hydrateInterface(v, prop); err != nil {
|
||||
return utils.NewError(hydrateProperty, "unable to hydrate interface", v, err)
|
||||
} else if handled {
|
||||
return nil // exit early if handled by the interface
|
||||
}
|
||||
|
||||
// if we got here, we need to create a new instance to
|
||||
// set into the property.
|
||||
var vnew, varr = newValue(v)
|
||||
var vlit bool
|
||||
|
||||
// check to see if the new value handles it's own hydration
|
||||
if handled, err := hydrateInterface(vnew, prop); err != nil {
|
||||
return utils.NewError(hydrateProperty, "unable to hydrate new interface value", vnew, err)
|
||||
} else if vlit = !handled; vlit {
|
||||
// if not, treat it as a literal
|
||||
if vnewlit, err := hydrateLiteral(vnew, prop); err != nil {
|
||||
return utils.NewError(hydrateProperty, "unable to hydrate new literal value", vnew, err)
|
||||
} else if _, err := hydrateInterface(vnewlit, prop); err != nil {
|
||||
return utils.NewError(hydrateProperty, "unable to hydrate new literal interface value", vnewlit, err)
|
||||
}
|
||||
}
|
||||
|
||||
// now we can set the value
|
||||
vnewval := dereferencePointerValue(vnew)
|
||||
voldval := dereferencePointerValue(v)
|
||||
|
||||
// make sure we can set the new value into the provided pointer
|
||||
|
||||
if varr {
|
||||
// for arrays, append the new value into the array structure
|
||||
if !voldval.CanSet() {
|
||||
return utils.NewError(hydrateProperty, "unable to set array value", v, nil)
|
||||
} else {
|
||||
voldval.Set(reflect.Append(voldval, vnewval))
|
||||
}
|
||||
} else if vlit {
|
||||
// for literals, set the dereferenced value
|
||||
if !voldval.CanSet() {
|
||||
return utils.NewError(hydrateProperty, "unable to set literal value", v, nil)
|
||||
} else {
|
||||
voldval.Set(vnewval)
|
||||
}
|
||||
} else if !v.CanSet() {
|
||||
return utils.NewError(hydrateProperty, "unable to set pointer value", v, nil)
|
||||
} else {
|
||||
// everything else should be a pointer, set it directly
|
||||
v.Set(vnew)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func hydrateNestedComponent(v reflect.Value, component *token) error {
|
||||
|
||||
// create a new object to hold the property value
|
||||
var vnew, varr = newValue(v)
|
||||
if err := hydrateComponent(vnew, component); err != nil {
|
||||
return utils.NewError(hydrateNestedComponent, "unable to decode component", component, err)
|
||||
}
|
||||
|
||||
if varr {
|
||||
// for arrays, append the new value into the array structure
|
||||
voldval := dereferencePointerValue(v)
|
||||
if !voldval.CanSet() {
|
||||
return utils.NewError(hydrateNestedComponent, "unable to set array value", v, nil)
|
||||
} else {
|
||||
voldval.Set(reflect.Append(voldval, vnew))
|
||||
}
|
||||
} else if !v.CanSet() {
|
||||
return utils.NewError(hydrateNestedComponent, "unable to set pointer value", v, nil)
|
||||
} else {
|
||||
// everything else should be a pointer, set it directly
|
||||
v.Set(vnew)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func hydrateProperties(v reflect.Value, component *token) error {
|
||||
|
||||
vdref := dereferencePointerValue(v)
|
||||
vtype := vdref.Type()
|
||||
vkind := vdref.Kind()
|
||||
|
||||
if vkind != reflect.Struct {
|
||||
return utils.NewError(hydrateProperties, "unable to hydrate properties of non-struct", v, nil)
|
||||
}
|
||||
|
||||
n := vtype.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
prop := properties.PropertyFromStructField(vtype.Field(i))
|
||||
if prop == nil {
|
||||
continue // skip if field is ignored
|
||||
}
|
||||
|
||||
vfield := vdref.Field(i)
|
||||
|
||||
// first try to hydrate property values
|
||||
if properties, ok := component.properties[prop.Name]; ok {
|
||||
for _, prop := range properties {
|
||||
if err := hydrateProperty(vfield, prop); err != nil {
|
||||
msg := fmt.Sprintf("unable to hydrate property %s", prop.Name)
|
||||
return utils.NewError(hydrateProperties, msg, v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then try to hydrate components
|
||||
vtemp, _ := newValue(vfield)
|
||||
if tag, err := extractTagFromValue(vtemp); err != nil {
|
||||
msg := fmt.Sprintf("unable to extract tag from property %s", prop.Name)
|
||||
return utils.NewError(hydrateProperties, msg, v, err)
|
||||
} else if components, ok := component.components[tag]; ok {
|
||||
for _, comp := range components {
|
||||
if err := hydrateNestedComponent(vfield, comp); err != nil {
|
||||
msg := fmt.Sprintf("unable to hydrate component %s", prop.Name)
|
||||
return utils.NewError(hydrateProperties, msg, v, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func hydrateComponent(v reflect.Value, component *token) error {
|
||||
if tag, err := extractTagFromValue(v); err != nil {
|
||||
return utils.NewError(hydrateComponent, "error extracting tag from value", component, err)
|
||||
} else if tag != component.name {
|
||||
msg := fmt.Sprintf("expected %s and found %s", tag, component.name)
|
||||
return utils.NewError(hydrateComponent, msg, component, nil)
|
||||
} else if err := hydrateProperties(v, component); err != nil {
|
||||
return utils.NewError(hydrateComponent, "unable to hydrate properties", component, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hydrateComponents(v reflect.Value, components []*token) error {
|
||||
vdref := dereferencePointerValue(v)
|
||||
for i, component := range components {
|
||||
velem := reflect.New(vdref.Type().Elem())
|
||||
if err := hydrateComponent(velem, component); err != nil {
|
||||
msg := fmt.Sprintf("unable to hydrate component %d", i)
|
||||
return utils.NewError(hydrateComponent, msg, component, err)
|
||||
} else {
|
||||
v.Set(reflect.Append(vdref, velem))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hydrateValue(v reflect.Value, component *token) error {
|
||||
|
||||
if !v.IsValid() || v.Kind() != reflect.Ptr {
|
||||
return utils.NewError(hydrateValue, "unmarshal target must be a valid pointer", v, nil)
|
||||
}
|
||||
|
||||
// handle any encodable properties
|
||||
if encoder, isprop := v.Interface().(properties.CanEncodeName); isprop {
|
||||
if name, err := encoder.EncodeICalName(); err != nil {
|
||||
return utils.NewError(hydrateValue, "unable to lookup property name", v, err)
|
||||
} else if properties, found := component.properties[name]; !found || len(properties) == 0 {
|
||||
return utils.NewError(hydrateValue, "no matching propery values found for "+string(name), v, nil)
|
||||
} else if len(properties) > 1 {
|
||||
return utils.NewError(hydrateValue, "more than one property value matches single property interface", v, nil)
|
||||
} else {
|
||||
return hydrateProperty(v, properties[0])
|
||||
}
|
||||
}
|
||||
|
||||
// handle components
|
||||
vkind := dereferencePointerValue(v).Kind()
|
||||
if tag, err := extractTagFromValue(v); err != nil {
|
||||
return utils.NewError(hydrateValue, "unable to extract component tag", v, err)
|
||||
} else if components, found := component.components[tag]; !found || len(components) == 0 {
|
||||
msg := fmt.Sprintf("unable to find matching component for %s", tag)
|
||||
return utils.NewError(hydrateValue, msg, v, nil)
|
||||
} else if vkind == reflect.Array || vkind == reflect.Slice {
|
||||
return hydrateComponents(v, components)
|
||||
} else if len(components) > 1 {
|
||||
return utils.NewError(hydrateValue, "non-array interface provided but more than one component found!", v, nil)
|
||||
} else {
|
||||
return hydrateComponent(v, components[0])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// decodes encoded icalendar data into a native interface
|
||||
func Unmarshal(encoded string, into interface{}) error {
|
||||
if component, err := tokenize(encoded); err != nil {
|
||||
return utils.NewError(Unmarshal, "unable to tokenize encoded data", encoded, err)
|
||||
} else {
|
||||
return hydrateValue(reflect.ValueOf(into), component)
|
||||
}
|
||||
}
|
7
vendor/github.com/dolanor/caldav-go/icalendar/values/cal_scale.go
generated
vendored
Normal file
7
vendor/github.com/dolanor/caldav-go/icalendar/values/cal_scale.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
package values
|
||||
|
||||
type CalScale string
|
||||
|
||||
const (
|
||||
GregorianCalScale CalScale = "GREGORIAN"
|
||||
)
|
33
vendor/github.com/dolanor/caldav-go/icalendar/values/comment.go
generated
vendored
Normal file
33
vendor/github.com/dolanor/caldav-go/icalendar/values/comment.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
)
|
||||
|
||||
// specifies non-processing information intended to provide a comment to the calendar user.
|
||||
type Comment string
|
||||
|
||||
// encodes the comment value for the iCalendar specification
|
||||
func (c Comment) EncodeICalValue() (string, error) {
|
||||
return string(c), nil
|
||||
}
|
||||
|
||||
// decodes the comment value from the iCalendar specification
|
||||
func (c Comment) DecodeICalValue(value string) error {
|
||||
c = Comment(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes the comment value for the iCalendar specification
|
||||
func (c Comment) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.CommentPropertyName, nil
|
||||
}
|
||||
|
||||
// creates a list of comments from strings
|
||||
func NewComments(comments ...string) []Comment {
|
||||
var _comments []Comment
|
||||
for _, comment := range comments {
|
||||
_comments = append(_comments, Comment(comment))
|
||||
}
|
||||
return _comments
|
||||
}
|
139
vendor/github.com/dolanor/caldav-go/icalendar/values/contact.go
generated
vendored
Normal file
139
vendor/github.com/dolanor/caldav-go/icalendar/values/contact.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"net/mail"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// Specifies the organizer of a group scheduled calendar entity. The property is specified within the "VFREEBUSY"
|
||||
// calendar component to specify the calendar user requesting the free or busy time. When publishing a "VFREEBUSY"
|
||||
// calendar component, the property is used to specify the calendar that the published busy time came from.
|
||||
//
|
||||
// The property has the property parameters CN, for specifying the common or display name associated with the
|
||||
// "Organizer", DIR, for specifying a pointer to the directory information associated with the "Organizer",
|
||||
// SENT-BY, for specifying another calendar user that is acting on behalf of the "Organizer". The non-standard
|
||||
// parameters may also be specified on this property. If the LANGUAGE property parameter is specified, the identified
|
||||
// language applies to the CN parameter value.
|
||||
type Contact struct {
|
||||
Entry mail.Address
|
||||
}
|
||||
|
||||
type AttendeeContact Contact
|
||||
type OrganizerContact Contact
|
||||
|
||||
// creates a new icalendar attendee representation
|
||||
func NewAttendeeContact(name, email string) *AttendeeContact {
|
||||
return &AttendeeContact{Entry: mail.Address{Name: name, Address: email}}
|
||||
}
|
||||
|
||||
// creates a new icalendar organizer representation
|
||||
func NewOrganizerContact(name, email string) *OrganizerContact {
|
||||
return &OrganizerContact{Entry: mail.Address{Name: name, Address: email}}
|
||||
}
|
||||
|
||||
// validates the contact value for the iCalendar specification
|
||||
func (c *Contact) ValidateICalValue() error {
|
||||
email := c.Entry.String()
|
||||
if _, err := mail.ParseAddress(email); err != nil {
|
||||
msg := fmt.Sprintf("unable to validate address %s", email)
|
||||
return utils.NewError(c.ValidateICalValue, msg, c, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// encodes the contact value for the iCalendar specification
|
||||
func (c *Contact) EncodeICalValue() (string, error) {
|
||||
return fmt.Sprintf("MAILTO:%s", c.Entry.Address), nil
|
||||
}
|
||||
|
||||
// encodes the contact params for the iCalendar specification
|
||||
func (c *Contact) EncodeICalParams() (params properties.Params, err error) {
|
||||
if c.Entry.Name != "" {
|
||||
params = properties.Params{properties.CanonicalNameParameterName: c.Entry.Name}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes the contact value from the iCalendar specification
|
||||
func (c *Contact) DecodeICalValue(value string) error {
|
||||
parts := strings.SplitN(value, ":", 2)
|
||||
if len(parts) > 1 {
|
||||
c.Entry.Address = parts[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodes the contact params from the iCalendar specification
|
||||
func (c *Contact) DecodeICalParams(params properties.Params) error {
|
||||
if name, found := params[properties.CanonicalNameParameterName]; found {
|
||||
c.Entry.Name = name
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validates the contact value for the iCalendar specification
|
||||
func (c *OrganizerContact) ValidateICalValue() error {
|
||||
return (*Contact)(c).ValidateICalValue()
|
||||
}
|
||||
|
||||
// encodes the contact value for the iCalendar specification
|
||||
func (c *OrganizerContact) EncodeICalValue() (string, error) {
|
||||
return (*Contact)(c).EncodeICalValue()
|
||||
}
|
||||
|
||||
// encodes the contact params for the iCalendar specification
|
||||
func (c *OrganizerContact) EncodeICalParams() (params properties.Params, err error) {
|
||||
return (*Contact)(c).EncodeICalParams()
|
||||
}
|
||||
|
||||
// decodes the contact value from the iCalendar specification
|
||||
func (c *OrganizerContact) DecodeICalValue(value string) error {
|
||||
return (*Contact)(c).DecodeICalValue(value)
|
||||
}
|
||||
|
||||
// decodes the contact params from the iCalendar specification
|
||||
func (c *OrganizerContact) DecodeICalParams(params properties.Params) error {
|
||||
return (*Contact)(c).DecodeICalParams(params)
|
||||
}
|
||||
|
||||
// encodes the contact property name for the iCalendar specification
|
||||
func (o *OrganizerContact) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.OrganizerPropertyName, nil
|
||||
}
|
||||
|
||||
// validates the contact value for the iCalendar specification
|
||||
func (c *AttendeeContact) ValidateICalValue() error {
|
||||
return (*Contact)(c).ValidateICalValue()
|
||||
}
|
||||
|
||||
// encodes the contact value for the iCalendar specification
|
||||
func (c *AttendeeContact) EncodeICalValue() (string, error) {
|
||||
return (*Contact)(c).EncodeICalValue()
|
||||
}
|
||||
|
||||
// encodes the contact params for the iCalendar specification
|
||||
func (c *AttendeeContact) EncodeICalParams() (params properties.Params, err error) {
|
||||
return (*Contact)(c).EncodeICalParams()
|
||||
}
|
||||
|
||||
// decodes the contact value from the iCalendar specification
|
||||
func (c *AttendeeContact) DecodeICalValue(value string) error {
|
||||
return (*Contact)(c).DecodeICalValue(value)
|
||||
}
|
||||
|
||||
// decodes the contact params from the iCalendar specification
|
||||
func (c *AttendeeContact) DecodeICalParams(params properties.Params) error {
|
||||
return (*Contact)(c).DecodeICalParams(params)
|
||||
}
|
||||
|
||||
// encodes the contact property name for the iCalendar specification
|
||||
func (o *AttendeeContact) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.AttendeePropertyName, nil
|
||||
}
|
24
vendor/github.com/dolanor/caldav-go/icalendar/values/csv.go
generated
vendored
Normal file
24
vendor/github.com/dolanor/caldav-go/icalendar/values/csv.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
type CSV []string
|
||||
|
||||
func (csv *CSV) EncodeICalValue() (string, error) {
|
||||
return strings.Join(*csv, ","), nil
|
||||
}
|
||||
|
||||
func (csv *CSV) DecodeICalValue(value string) error {
|
||||
value = strings.TrimSpace(value)
|
||||
*csv = CSV(strings.Split(value, ","))
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCSV(items ...string) *CSV {
|
||||
return (*CSV)(&items)
|
||||
}
|
265
vendor/github.com/dolanor/caldav-go/icalendar/values/datetime.go
generated
vendored
Normal file
265
vendor/github.com/dolanor/caldav-go/icalendar/values/datetime.go
generated
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
const DateFormatString = "20060102"
|
||||
const DateTimeFormatString = "20060102T150405"
|
||||
const UTCDateTimeFormatString = "20060102T150405Z"
|
||||
|
||||
// a representation of a date and time for iCalendar
|
||||
type DateTime struct {
|
||||
t time.Time
|
||||
}
|
||||
|
||||
type DateTimes []*DateTime
|
||||
|
||||
// The exception dates, if specified, are used in computing the recurrence set. The recurrence set is the complete set
|
||||
// of recurrence instances for a calendar component. The recurrence set is generated by considering the initial
|
||||
// "DTSTART" property along with the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained within the iCalendar
|
||||
// object. The "DTSTART" property defines the first instance in the recurrence set. Multiple instances of the "RRULE"
|
||||
// and "EXRULE" properties can also be specified to define more sophisticated recurrence sets. The final recurrence set
|
||||
// is generated by gathering all of the start date-times generated by any of the specified "RRULE" and "RDATE"
|
||||
// properties, and then excluding any start date and times which fall within the union of start date and times
|
||||
// generated by any specified "EXRULE" and "EXDATE" properties. This implies that start date and times within exclusion
|
||||
// related properties (i.e., "EXDATE" and "EXRULE") take precedence over those specified by inclusion properties
|
||||
// (i.e., "RDATE" and "RRULE"). Where duplicate instances are generated by the "RRULE" and "RDATE" properties, only
|
||||
// one recurrence is considered. Duplicate instances are ignored.
|
||||
//
|
||||
// The "EXDATE" property can be used to exclude the value specified in "DTSTART". However, in such cases the original
|
||||
// "DTSTART" date MUST still be maintained by the calendaring and scheduling system because the original "DTSTART"
|
||||
// value has inherent usage dependencies by other properties such as the "RECURRENCE-ID".
|
||||
type ExceptionDateTimes DateTimes
|
||||
|
||||
// The recurrence dates, if specified, are used in computing the recurrence set. The recurrence set is the complete set
|
||||
// of recurrence instances for a calendar component. The recurrence set is generated by considering the initial
|
||||
// "DTSTART" property along with the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained within the iCalendar
|
||||
// object. The "DTSTART" property defines the first instance in the recurrence set. Multiple instances of the "RRULE"
|
||||
// and "EXRULE" properties can also be specified to define more sophisticated recurrence sets. The final recurrence set
|
||||
// is generated by gathering all of the start date-times generated by any of the specified "RRULE" and "RDATE"
|
||||
// properties, and then excluding any start date and times which fall within the union of start date and times
|
||||
// generated by any specified "EXRULE" and "EXDATE" properties. This implies that start date and times within exclusion
|
||||
// related properties (i.e., "EXDATE" and "EXRULE") take precedence over those specified by inclusion properties
|
||||
// (i.e., "RDATE" and "RRULE"). Where duplicate instances are generated by the "RRULE" and "RDATE" properties, only
|
||||
// one recurrence is considered. Duplicate instances are ignored.
|
||||
type RecurrenceDateTimes DateTimes
|
||||
|
||||
// creates a new icalendar datetime representation
|
||||
func NewDateTime(t time.Time) *DateTime {
|
||||
return &DateTime{t: t.Truncate(time.Second)}
|
||||
}
|
||||
|
||||
// creates a new icalendar datetime array representation
|
||||
func NewDateTimes(dates ...*DateTime) DateTimes {
|
||||
return DateTimes(dates)
|
||||
}
|
||||
|
||||
// creates a new icalendar datetime array representation
|
||||
func NewExceptionDateTimes(dates ...*DateTime) *ExceptionDateTimes {
|
||||
datetimes := NewDateTimes(dates...)
|
||||
return (*ExceptionDateTimes)(&datetimes)
|
||||
}
|
||||
|
||||
// creates a new icalendar datetime array representation
|
||||
func NewRecurrenceDateTimes(dates ...*DateTime) *RecurrenceDateTimes {
|
||||
datetimes := NewDateTimes(dates...)
|
||||
return (*RecurrenceDateTimes)(&datetimes)
|
||||
}
|
||||
|
||||
// checks to see if two datetimes are equal
|
||||
func (d *DateTime) Equals(test *DateTime) bool {
|
||||
return d.t.Equal(test.t)
|
||||
}
|
||||
|
||||
// returns the native time for the datetime object
|
||||
func (d *DateTime) NativeTime() time.Time {
|
||||
return d.t
|
||||
}
|
||||
|
||||
// encodes the datetime value for the iCalendar specification
|
||||
func (d *DateTime) EncodeICalValue() (string, error) {
|
||||
val := d.t.Format(DateTimeFormatString)
|
||||
loc := d.t.Location()
|
||||
if loc == time.UTC {
|
||||
val = fmt.Sprintf("%sZ", val)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// decodes the datetime value from the iCalendar specification
|
||||
func (d *DateTime) DecodeICalValue(value string) error {
|
||||
layout := DateTimeFormatString
|
||||
if strings.HasSuffix(value, "Z") {
|
||||
layout = UTCDateTimeFormatString
|
||||
} else if len(value) == 8 {
|
||||
layout = DateFormatString
|
||||
}
|
||||
var err error
|
||||
d.t, err = time.ParseInLocation(layout, value, time.UTC)
|
||||
if err != nil {
|
||||
return utils.NewError(d.DecodeICalValue, "unable to parse datetime value", d, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// encodes the datetime params for the iCalendar specification
|
||||
func (d *DateTime) EncodeICalParams() (params properties.Params, err error) {
|
||||
loc := d.t.Location()
|
||||
if loc != time.UTC {
|
||||
params = properties.Params{properties.TimeZoneIdPropertyName: loc.String()}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes the datetime params from the iCalendar specification
|
||||
func (d *DateTime) DecodeICalParams(params properties.Params) error {
|
||||
layout := DateTimeFormatString
|
||||
value := d.t.Format(layout)
|
||||
if name, found := params[properties.TimeZoneIdPropertyName]; !found {
|
||||
return nil
|
||||
} else if loc, err := time.LoadLocation(name); err != nil {
|
||||
return utils.NewError(d.DecodeICalValue, "unable to parse timezone", d, err)
|
||||
} else if t, err := time.ParseInLocation(layout, value, loc); err != nil {
|
||||
return utils.NewError(d.DecodeICalValue, "unable to parse datetime value", d, err)
|
||||
} else {
|
||||
d.t = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// validates the datetime value against the iCalendar specification
|
||||
func (d *DateTime) ValidateICalValue() error {
|
||||
|
||||
loc := d.t.Location()
|
||||
|
||||
if loc == time.Local {
|
||||
msg := "DateTime location may not Local, please use UTC or explicit Location"
|
||||
return utils.NewError(d.ValidateICalValue, msg, d, nil)
|
||||
}
|
||||
|
||||
if loc.String() == "" {
|
||||
msg := "DateTime location must have a valid name"
|
||||
return utils.NewError(d.ValidateICalValue, msg, d, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes the datetime value for the iCalendar specification
|
||||
func (d *DateTime) String() string {
|
||||
if s, err := d.EncodeICalValue(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// encodes a list of datetime values for the iCalendar specification
|
||||
func (ds *DateTimes) EncodeICalValue() (string, error) {
|
||||
var csv CSV
|
||||
for i, d := range *ds {
|
||||
if s, err := d.EncodeICalValue(); err != nil {
|
||||
msg := fmt.Sprintf("unable to encode datetime at index %d", i)
|
||||
return "", utils.NewError(ds.EncodeICalValue, msg, ds, err)
|
||||
} else {
|
||||
csv = append(csv, s)
|
||||
}
|
||||
}
|
||||
return csv.EncodeICalValue()
|
||||
}
|
||||
|
||||
// encodes a list of datetime params for the iCalendar specification
|
||||
func (ds *DateTimes) EncodeICalParams() (params properties.Params, err error) {
|
||||
if len(*ds) > 0 {
|
||||
params, err = (*ds)[0].EncodeICalParams()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes a list of datetime params from the iCalendar specification
|
||||
func (ds *DateTimes) DecodeICalParams(params properties.Params) error {
|
||||
for i, d := range *ds {
|
||||
if err := d.DecodeICalParams(params); err != nil {
|
||||
msg := fmt.Sprintf("unable to decode datetime params for index %d", i)
|
||||
return utils.NewError(ds.DecodeICalValue, msg, ds, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes a list of datetime values for the iCalendar specification
|
||||
func (ds *DateTimes) DecodeICalValue(value string) error {
|
||||
csv := new(CSV)
|
||||
if err := csv.DecodeICalValue(value); err != nil {
|
||||
return utils.NewError(ds.DecodeICalValue, "unable to decode datetime list as CSV", ds, err)
|
||||
}
|
||||
for i, value := range *csv {
|
||||
d := new(DateTime)
|
||||
if err := d.DecodeICalValue(value); err != nil {
|
||||
msg := fmt.Sprintf("unable to decode datetime at index %d", i)
|
||||
return utils.NewError(ds.DecodeICalValue, msg, ds, err)
|
||||
} else {
|
||||
*ds = append(*ds, d)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes exception date times property name for icalendar
|
||||
func (e *ExceptionDateTimes) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.ExceptionDateTimesPropertyName, nil
|
||||
}
|
||||
|
||||
// encodes recurrence date times property name for icalendar
|
||||
func (r *RecurrenceDateTimes) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.RecurrenceDateTimesPropertyName, nil
|
||||
}
|
||||
|
||||
// encodes exception date times property value for icalendar
|
||||
func (e *ExceptionDateTimes) EncodeICalValue() (string, error) {
|
||||
return (*DateTimes)(e).EncodeICalValue()
|
||||
}
|
||||
|
||||
// encodes recurrence date times property value for icalendar
|
||||
func (r *RecurrenceDateTimes) EncodeICalValue() (string, error) {
|
||||
return (*DateTimes)(r).EncodeICalValue()
|
||||
}
|
||||
|
||||
// decodes exception date times property value for icalendar
|
||||
func (e *ExceptionDateTimes) DecodeICalValue(value string) error {
|
||||
return (*DateTimes)(e).DecodeICalValue(value)
|
||||
}
|
||||
|
||||
// decodes recurrence date times property value for icalendar
|
||||
func (r *RecurrenceDateTimes) DecodeICalValue(value string) error {
|
||||
return (*DateTimes)(r).DecodeICalValue(value)
|
||||
}
|
||||
|
||||
// encodes exception date times property params for icalendar
|
||||
func (e *ExceptionDateTimes) EncodeICalParams() (params properties.Params, err error) {
|
||||
return (*DateTimes)(e).EncodeICalParams()
|
||||
}
|
||||
|
||||
// encodes recurrence date times property params for icalendar
|
||||
func (r *RecurrenceDateTimes) EncodeICalParams() (params properties.Params, err error) {
|
||||
return (*DateTimes)(r).EncodeICalParams()
|
||||
}
|
||||
|
||||
// encodes exception date times property params for icalendar
|
||||
func (e *ExceptionDateTimes) DecodeICalParams(params properties.Params) error {
|
||||
return (*DateTimes)(e).DecodeICalParams(params)
|
||||
}
|
||||
|
||||
// encodes recurrence date times property params for icalendar
|
||||
func (r *RecurrenceDateTimes) DecodeICalParams(params properties.Params) error {
|
||||
return (*DateTimes)(r).DecodeICalParams(params)
|
||||
}
|
132
vendor/github.com/dolanor/caldav-go/icalendar/values/duration.go
generated
vendored
Normal file
132
vendor/github.com/dolanor/caldav-go/icalendar/values/duration.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// a representation of duration for iCalendar
|
||||
type Duration struct {
|
||||
d time.Duration
|
||||
}
|
||||
|
||||
// breaks apart the duration into its component time parts
|
||||
func (d *Duration) Decompose() (weeks, days, hours, minutes, seconds int64) {
|
||||
|
||||
// chip away at this
|
||||
rem := time.Duration(math.Abs(float64(d.d)))
|
||||
|
||||
div := time.Hour * 24 * 7
|
||||
weeks = int64(rem / div)
|
||||
rem = rem % div
|
||||
div = div / 7
|
||||
days = int64(rem / div)
|
||||
rem = rem % div
|
||||
div = div / 24
|
||||
hours = int64(rem / div)
|
||||
rem = rem % div
|
||||
div = div / 60
|
||||
minutes = int64(rem / div)
|
||||
rem = rem % div
|
||||
div = div / 60
|
||||
seconds = int64(rem / div)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// returns the native golang duration
|
||||
func (d *Duration) NativeDuration() time.Duration {
|
||||
return d.d
|
||||
}
|
||||
|
||||
// returns true if the duration is negative
|
||||
func (d *Duration) IsPast() bool {
|
||||
return d.d < 0
|
||||
}
|
||||
|
||||
// encodes the duration of time into iCalendar format
|
||||
func (d *Duration) EncodeICalValue() (string, error) {
|
||||
var parts []string
|
||||
weeks, days, hours, minutes, seconds := d.Decompose()
|
||||
if d.IsPast() {
|
||||
parts = append(parts, "-")
|
||||
}
|
||||
parts = append(parts, "P")
|
||||
if weeks > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%dW", weeks))
|
||||
}
|
||||
if days > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%dD", days))
|
||||
}
|
||||
if hours > 0 || minutes > 0 || seconds > 0 {
|
||||
parts = append(parts, "T")
|
||||
if hours > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%dH", hours))
|
||||
}
|
||||
if minutes > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%dM", minutes))
|
||||
}
|
||||
if seconds > 0 {
|
||||
parts = append(parts, fmt.Sprintf("%dS", seconds))
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, ""), nil
|
||||
}
|
||||
|
||||
var durationRegEx = regexp.MustCompile("(\\d+)(\\w)")
|
||||
|
||||
// decodes the duration of time from iCalendar format
|
||||
func (d *Duration) DecodeICalValue(value string) error {
|
||||
var seconds int64
|
||||
var isPast = strings.HasPrefix(value, "-P")
|
||||
var matches = durationRegEx.FindAllStringSubmatch(value, -1)
|
||||
for _, match := range matches {
|
||||
var multiplier int64
|
||||
ivalue, err := strconv.ParseInt(match[1], 10, 64)
|
||||
if err != nil {
|
||||
return utils.NewError(d.DecodeICalValue, "unable to decode duration value "+match[1], d, nil)
|
||||
}
|
||||
switch match[2] {
|
||||
case "S":
|
||||
multiplier = 1
|
||||
case "M":
|
||||
multiplier = 60
|
||||
case "H":
|
||||
multiplier = 60 * 60
|
||||
case "D":
|
||||
multiplier = 60 * 60 * 24
|
||||
case "W":
|
||||
multiplier = 60 * 60 * 24 * 7
|
||||
default:
|
||||
return utils.NewError(d.DecodeICalValue, "unable to decode duration segment "+match[2], d, nil)
|
||||
}
|
||||
seconds = seconds + multiplier*ivalue
|
||||
}
|
||||
d.d = time.Duration(seconds) * time.Second
|
||||
if isPast {
|
||||
d.d = -d.d
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Duration) String() string {
|
||||
if s, err := d.EncodeICalValue(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new iCalendar duration representation
|
||||
func NewDuration(d time.Duration) *Duration {
|
||||
return &Duration{d: d}
|
||||
}
|
17
vendor/github.com/dolanor/caldav-go/icalendar/values/event_access_classification.go
generated
vendored
Normal file
17
vendor/github.com/dolanor/caldav-go/icalendar/values/event_access_classification.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
package values
|
||||
|
||||
// An access classification is only one component of the general security system within a calendar application.
|
||||
// It provides a method of capturing the scope of the access the calendar owner intends for information within an
|
||||
// individual calendar entry. The access classification of an individual iCalendar component is useful when measured
|
||||
// along with the other security components of a calendar system (e.g., calendar user authentication, authorization,
|
||||
// access rights, access role, etc.). Hence, the semantics of the individual access classifications cannot be completely
|
||||
// defined by this memo alone. Additionally, due to the "blind" nature of most exchange processes using this memo, these
|
||||
// access classifications cannot serve as an enforcement statement for a system receiving an iCalendar object. Rather,
|
||||
// they provide a method for capturing the intention of the calendar owner for the access to the calendar component.
|
||||
type EventAccessClassification string
|
||||
|
||||
const (
|
||||
PublicEventAccessClassification EventAccessClassification = "PUBLIC"
|
||||
PrivateEventAccessClassification = "PRIVATE"
|
||||
ConfidentialEventAccessClassification = "CONFIDENTIAL"
|
||||
)
|
13
vendor/github.com/dolanor/caldav-go/icalendar/values/event_status.go
generated
vendored
Normal file
13
vendor/github.com/dolanor/caldav-go/icalendar/values/event_status.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package values
|
||||
|
||||
// In a group scheduled calendar component, the property is used by the "Organizer" to provide a confirmation of the
|
||||
// event to the "Attendees".
|
||||
// For example in an Event calendar component, the "Organizer" can indicate that a meeting is tentative, confirmed or
|
||||
// cancelled.
|
||||
type EventStatus string
|
||||
|
||||
const (
|
||||
TentativeEventStatus EventStatus = "TENTATIVE" // Indicates event is tentative.
|
||||
ConfirmedEventStatus = "CONFIRMED" // Indicates event is definite.
|
||||
CancelledEventStatus = "CANCELLED" // Indicates event is cancelled.
|
||||
)
|
69
vendor/github.com/dolanor/caldav-go/icalendar/values/geo.go
generated
vendored
Normal file
69
vendor/github.com/dolanor/caldav-go/icalendar/values/geo.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// a representation of a geographical point for iCalendar
|
||||
type Geo struct {
|
||||
coords []float64
|
||||
}
|
||||
|
||||
// creates a new icalendar geo representation
|
||||
func NewGeo(lat, lng float64) *Geo {
|
||||
return &Geo{coords: []float64{lat, lng}}
|
||||
}
|
||||
|
||||
// returns the latitude encoded into the geo point
|
||||
func (g *Geo) Lat() float64 {
|
||||
return g.coords[0]
|
||||
}
|
||||
|
||||
// returns the longitude encoded into the geo point
|
||||
func (g *Geo) Lng() float64 {
|
||||
return g.coords[1]
|
||||
}
|
||||
|
||||
// validates the geo value against the iCalendar specification
|
||||
func (g *Geo) ValidateICalValue() error {
|
||||
|
||||
if len(g.coords) != 2 {
|
||||
return utils.NewError(g.ValidateICalValue, "geo value must have length of 2", g, nil)
|
||||
}
|
||||
|
||||
if g.Lat() < -90 || g.Lat() > 90 {
|
||||
return utils.NewError(g.ValidateICalValue, "geo latitude must be between -90 and 90 degrees", g, nil)
|
||||
}
|
||||
|
||||
if g.Lng() < -180 || g.Lng() > 180 {
|
||||
return utils.NewError(g.ValidateICalValue, "geo longitude must be between -180 and 180 degrees", g, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// encodes the geo value for the iCalendar specification
|
||||
func (g *Geo) EncodeICalValue() (string, error) {
|
||||
return fmt.Sprintf("%f %f", g.Lat(), g.Lng()), nil
|
||||
}
|
||||
|
||||
// decodes the geo value from the iCalendar specification
|
||||
func (g *Geo) DecodeICalValue(value string) error {
|
||||
if latlng := strings.Split(value, " "); len(latlng) < 2 {
|
||||
return utils.NewError(g.DecodeICalValue, "geo value must have both a latitude and longitude component", g, nil)
|
||||
} else if lat, err := strconv.ParseFloat(latlng[0], 64); err != nil {
|
||||
return utils.NewError(g.DecodeICalValue, "unable to decode latitude component", g, err)
|
||||
} else if lng, err := strconv.ParseFloat(latlng[1], 64); err != nil {
|
||||
return utils.NewError(g.DecodeICalValue, "unable to decode latitude component", g, err)
|
||||
} else {
|
||||
*g = Geo{coords: []float64{lat, lng}}
|
||||
return nil
|
||||
}
|
||||
}
|
78
vendor/github.com/dolanor/caldav-go/icalendar/values/location.go
generated
vendored
Normal file
78
vendor/github.com/dolanor/caldav-go/icalendar/values/location.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// Specific venues such as conference or meeting rooms may be explicitly specified using this property. An alternate
|
||||
// representation may be specified that is a URI that points to directory information with more structured specification
|
||||
// of the location. For example, the alternate representation may specify either an LDAP URI pointing to an LDAP server
|
||||
// entry or a CID URI pointing to a MIME body part containing a vCard [RFC 2426] for the location.
|
||||
type Location struct {
|
||||
value string
|
||||
altrep *url.URL
|
||||
}
|
||||
|
||||
// creates a new icalendar location representation
|
||||
func NewLocation(value string, altrep ...*url.URL) *Location {
|
||||
loc := &Location{value: value}
|
||||
if len(altrep) > 0 {
|
||||
loc.altrep = altrep[0]
|
||||
}
|
||||
return loc
|
||||
}
|
||||
|
||||
// returns an alternate representation for the location
|
||||
// if one exists
|
||||
func (l *Location) AltRep() *url.URL {
|
||||
return l.altrep
|
||||
}
|
||||
|
||||
// encodes the location for the iCalendar specification
|
||||
func (l *Location) EncodeICalValue() (string, error) {
|
||||
return l.value, nil
|
||||
}
|
||||
|
||||
// decodes the location from the iCalendar specification
|
||||
func (l *Location) DecodeICalValue(value string) error {
|
||||
l.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodes the location params for the iCalendar specification
|
||||
func (l *Location) EncodeICalParams() (params properties.Params, err error) {
|
||||
if l.altrep != nil {
|
||||
params = properties.Params{properties.AlternateRepresentationName: l.altrep.String()}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes the location params from the iCalendar specification
|
||||
func (l *Location) DecodeICalParams(params properties.Params) error {
|
||||
if rep, found := params[properties.AlternateRepresentationName]; !found {
|
||||
return nil
|
||||
} else if altrep, err := url.Parse(rep); err != nil {
|
||||
return utils.NewError(l.DecodeICalValue, "unable to parse alternate representation", l, err)
|
||||
} else {
|
||||
l.altrep = altrep
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// validates the location against the iCalendar specification
|
||||
func (l *Location) ValidateICalValue() error {
|
||||
|
||||
if l.altrep != nil {
|
||||
if _, err := url.Parse(l.altrep.String()); err != nil {
|
||||
msg := "location alternate representation must be a valid url"
|
||||
return utils.NewError(l.ValidateICalValue, msg, l, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
7
vendor/github.com/dolanor/caldav-go/icalendar/values/method.go
generated
vendored
Normal file
7
vendor/github.com/dolanor/caldav-go/icalendar/values/method.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
package values
|
||||
|
||||
type Method string
|
||||
|
||||
const (
|
||||
PublishMethod Method = "PUBLISH"
|
||||
)
|
434
vendor/github.com/dolanor/caldav-go/icalendar/values/recurrence_rule.go
generated
vendored
Normal file
434
vendor/github.com/dolanor/caldav-go/icalendar/values/recurrence_rule.go
generated
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The recurrence rule, if specified, is used in computing the recurrence set. The recurrence set is the complete set
|
||||
// of recurrence instances for a calendar component. The recurrence set is generated by considering the initial
|
||||
// "DTSTART" property along with the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained within the iCalendar
|
||||
// object. The "DTSTART" property defines the first instance in the recurrence set. Multiple instances of the "RRULE"
|
||||
// and "EXRULE" properties can also be specified to define more sophisticated recurrence sets. The final recurrence
|
||||
// set is generated by gathering all of the start date/times generated by any of the specified "RRULE" and "RDATE"
|
||||
// properties, and excluding any start date/times which fall within the union of start date/times generated by any
|
||||
// specified "EXRULE" and "EXDATE" properties. This implies that start date/times within exclusion related properties
|
||||
// (i.e., "EXDATE" and "EXRULE") take precedence over those specified by inclusion properties
|
||||
// (i.e., "RDATE" and "RRULE"). Where duplicate instances are generated by the "RRULE" and "RDATE" properties, only
|
||||
// one recurrence is considered. Duplicate instances are ignored.
|
||||
|
||||
// The "DTSTART" and "DTEND" property pair or "DTSTART" and "DURATION" property pair, specified within the iCalendar
|
||||
// object defines the first instance of the recurrence. When used with a recurrence rule, the "DTSTART" and "DTEND"
|
||||
// properties MUST be specified in local time and the appropriate set of "VTIMEZONE" calendar components MUST be
|
||||
// included. For detail on the usage of the "VTIMEZONE" calendar component, see the "VTIMEZONE" calendar component
|
||||
// definition.
|
||||
|
||||
// Any duration associated with the iCalendar object applies to all members of the generated recurrence set. Any
|
||||
// modified duration for specific recurrences MUST be explicitly specified using the "RDATE" property.
|
||||
type RecurrenceRule struct {
|
||||
Frequency RecurrenceFrequency
|
||||
Until *DateTime
|
||||
Count int
|
||||
Interval int
|
||||
BySecond []int
|
||||
ByMinute []int
|
||||
ByHour []int
|
||||
ByDay []RecurrenceWeekday
|
||||
ByMonthDay []int
|
||||
ByYearDay []int
|
||||
ByWeekNumber []int
|
||||
ByMonth []int
|
||||
BySetPosition []int
|
||||
WeekStart RecurrenceWeekday
|
||||
}
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// the frequency an event recurs
|
||||
type RecurrenceFrequency string
|
||||
|
||||
const (
|
||||
SecondRecurrenceFrequency RecurrenceFrequency = "SECONDLY"
|
||||
MinuteRecurrenceFrequency = "MINUTELY"
|
||||
HourRecurrenceFrequency = "HOURLY"
|
||||
DayRecurrenceFrequency = "DAILY"
|
||||
WeekRecurrenceFrequency = "WEEKLY"
|
||||
MonthRecurrenceFrequency = "MONTHLY"
|
||||
YearRecurrenceFrequency = "YEARLY"
|
||||
)
|
||||
|
||||
// the frequency an event recurs
|
||||
type RecurrenceWeekday string
|
||||
|
||||
const (
|
||||
MondayRecurrenceWeekday RecurrenceWeekday = "MO"
|
||||
TuesdayRecurrenceWeekday = "TU"
|
||||
WednesdayRecurrenceWeekday = "WE"
|
||||
ThursdayRecurrenceWeekday = "TH"
|
||||
FridayRecurrenceWeekday = "FR"
|
||||
SaturdayRecurrenceWeekday = "SA"
|
||||
SundayRecurrenceWeekday = "SU"
|
||||
)
|
||||
|
||||
// creates a new recurrence rule object for iCalendar
|
||||
func NewRecurrenceRule(frequency RecurrenceFrequency) *RecurrenceRule {
|
||||
return &RecurrenceRule{Frequency: frequency}
|
||||
}
|
||||
|
||||
var weekdayRegExp = regexp.MustCompile("MO|TU|WE|TH|FR|SA|SU")
|
||||
|
||||
// returns true if weekday is a valid constant
|
||||
func (r RecurrenceWeekday) IsValidWeekDay() bool {
|
||||
return weekdayRegExp.MatchString(strings.ToUpper(string(r)))
|
||||
}
|
||||
|
||||
var frequencyRegExp = regexp.MustCompile("SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY")
|
||||
|
||||
// returns true if weekday is a valid constant
|
||||
func (r RecurrenceFrequency) IsValidFrequency() bool {
|
||||
return frequencyRegExp.MatchString(strings.ToUpper(string(r)))
|
||||
}
|
||||
|
||||
// returns the recurrence rule name for the iCalendar specification
|
||||
func (r *RecurrenceRule) EncodeICalName() (properties.PropertyName, error) {
|
||||
return properties.RecurrenceRulePropertyName, nil
|
||||
}
|
||||
|
||||
// encodes the recurrence rule value for the iCalendar specification
|
||||
func (r *RecurrenceRule) EncodeICalValue() (string, error) {
|
||||
|
||||
out := []string{fmt.Sprintf("FREQ=%s", strings.ToUpper(string(r.Frequency)))}
|
||||
|
||||
if r.Until != nil {
|
||||
if encoded, err := r.Until.EncodeICalValue(); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode until date", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("UNTIL=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if r.Count > 0 {
|
||||
out = append(out, fmt.Sprintf("COUNT=%d", r.Count))
|
||||
}
|
||||
|
||||
if r.Interval > 0 {
|
||||
out = append(out, fmt.Sprintf("INTERVAL=%d", r.Interval))
|
||||
}
|
||||
|
||||
if len(r.BySecond) > 0 {
|
||||
if encoded, err := intsToCSV(r.BySecond); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by second value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYSECOND=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ByMinute) > 0 {
|
||||
if encoded, err := intsToCSV(r.ByMinute); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by minute value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYMINUTE=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ByHour) > 0 {
|
||||
if encoded, err := intsToCSV(r.ByHour); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by hour value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYHOUR=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ByDay) > 0 {
|
||||
if encoded, err := daysToCSV(r.ByDay); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by day value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYDAY=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ByMonthDay) > 0 {
|
||||
if encoded, err := intsToCSV(r.ByMonthDay); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by month day value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYMONTHDAY=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ByYearDay) > 0 {
|
||||
if encoded, err := intsToCSV(r.ByYearDay); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by year day value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYYEARDAY=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ByWeekNumber) > 0 {
|
||||
if encoded, err := intsToCSV(r.ByWeekNumber); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by week number value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYWEEKNO=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ByMonth) > 0 {
|
||||
if encoded, err := intsToCSV(r.ByMonth); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by month value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYMONTH=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.BySetPosition) > 0 {
|
||||
if encoded, err := intsToCSV(r.BySetPosition); err != nil {
|
||||
return "", utils.NewError(r.EncodeICalValue, "unable to encode by set position value", r, err)
|
||||
} else {
|
||||
out = append(out, fmt.Sprintf("BYSETPOS=%s", encoded))
|
||||
}
|
||||
}
|
||||
|
||||
if r.WeekStart != "" {
|
||||
out = append(out, fmt.Sprintf("WKST=%s", r.WeekStart))
|
||||
}
|
||||
|
||||
return strings.Join(out, ";"), nil
|
||||
}
|
||||
|
||||
var rruleParamRegExp = regexp.MustCompile("(\\w+)\\s*=\\s*([^;]+)")
|
||||
|
||||
// decodes the recurrence rule value from the iCalendar specification
|
||||
func (r *RecurrenceRule) DecodeICalValue(value string) error {
|
||||
|
||||
matches := rruleParamRegExp.FindAllStringSubmatch(value, -1)
|
||||
if len(matches) <= 0 {
|
||||
return utils.NewError(r.DecodeICalValue, "no recurrence rules found", r, nil)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
if err := r.decodeICalValue(match[1], match[2]); err != nil {
|
||||
msg := fmt.Sprintf("unable to decode %s value", match[1])
|
||||
return utils.NewError(r.DecodeICalValue, msg, r, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *RecurrenceRule) decodeICalValue(name string, value string) error {
|
||||
|
||||
switch name {
|
||||
case "FREQ":
|
||||
r.Frequency = RecurrenceFrequency(value)
|
||||
case "UNTIL":
|
||||
until := new(DateTime)
|
||||
if err := until.DecodeICalValue(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid until value "+value, r, err)
|
||||
} else {
|
||||
r.Until = until
|
||||
}
|
||||
case "COUNT":
|
||||
if count, err := strconv.ParseInt(value, 10, 64); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid count value "+value, r, err)
|
||||
} else {
|
||||
r.Count = int(count)
|
||||
}
|
||||
case "INTERVAL":
|
||||
if interval, err := strconv.ParseInt(value, 10, 64); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid interval value "+value, r, err)
|
||||
} else {
|
||||
r.Interval = int(interval)
|
||||
}
|
||||
case "BYSECOND":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid by second value "+value, r, err)
|
||||
} else {
|
||||
r.BySecond = ints
|
||||
}
|
||||
case "BYMINUTE":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid by minute value "+value, r, err)
|
||||
} else {
|
||||
r.ByMinute = ints
|
||||
}
|
||||
case "BYHOUR":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid by hour value "+value, r, err)
|
||||
} else {
|
||||
r.ByHour = ints
|
||||
}
|
||||
case "BYDAY":
|
||||
if days, err := csvToDays(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid by day value "+value, r, err)
|
||||
} else {
|
||||
r.ByDay = days
|
||||
}
|
||||
case "BYMONTHDAY":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid by month day value "+value, r, err)
|
||||
} else {
|
||||
r.ByMonthDay = ints
|
||||
}
|
||||
case "BYYEARDAY":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid by year day value "+value, r, err)
|
||||
} else {
|
||||
r.ByYearDay = ints
|
||||
}
|
||||
case "BYWEEKNO":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "invalid by week number value "+value, r, err)
|
||||
} else {
|
||||
r.ByWeekNumber = ints
|
||||
}
|
||||
case "BYMONTH":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "unable to encode by month value "+value, r, err)
|
||||
} else {
|
||||
r.ByMonth = ints
|
||||
}
|
||||
case "BYSETPOS":
|
||||
if ints, err := csvToInts(value); err != nil {
|
||||
return utils.NewError(r.decodeICalValue, "unable to encode by set position value "+value, r, err)
|
||||
} else {
|
||||
r.BySetPosition = ints
|
||||
}
|
||||
case "WKST":
|
||||
r.WeekStart = RecurrenceWeekday(value)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// validates the recurrence rule value against the iCalendar specification
|
||||
func (r *RecurrenceRule) ValidateICalValue() error {
|
||||
if !r.Frequency.IsValidFrequency() {
|
||||
return utils.NewError(r.ValidateICalValue, "a frequency is required in all recurrence rules", r, nil)
|
||||
} else if r.Until != nil && r.Count > 0 {
|
||||
return utils.NewError(r.ValidateICalValue, "until and count values are mutually exclusive", r, nil)
|
||||
} else if found, fine := intsInRange(r.BySecond, 59); !fine {
|
||||
msg := fmt.Sprintf("by second value of %d is out of bounds", found)
|
||||
return utils.NewError(r.ValidateICalValue, msg, r, nil)
|
||||
} else if found, fine := intsInRange(r.ByMinute, 59); !fine {
|
||||
msg := fmt.Sprintf("by minute value of %d is out of bounds", found)
|
||||
return utils.NewError(r.ValidateICalValue, msg, r, nil)
|
||||
} else if found, fine := intsInRange(r.ByHour, 23); !fine {
|
||||
msg := fmt.Sprintf("by hour value of %d is out of bounds", found)
|
||||
return utils.NewError(r.ValidateICalValue, msg, r, nil)
|
||||
} else if err := daysInRange(r.ByDay); err != nil {
|
||||
return utils.NewError(r.ValidateICalValue, "by day value not in range", r, err)
|
||||
} else if found, fine := intsInRange(r.ByMonthDay, 31); !fine {
|
||||
msg := fmt.Sprintf("by month day value of %d is out of bounds", found)
|
||||
return utils.NewError(r.ValidateICalValue, msg, r, nil)
|
||||
} else if found, fine := intsInRange(r.ByYearDay, 366); !fine {
|
||||
msg := fmt.Sprintf("by year day value of %d is out of bounds", found)
|
||||
return utils.NewError(r.ValidateICalValue, msg, r, nil)
|
||||
} else if found, fine := intsInRange(r.ByMonth, 12); !fine {
|
||||
msg := fmt.Sprintf("by month value of %d is out of bounds", found)
|
||||
return utils.NewError(r.ValidateICalValue, msg, r, nil)
|
||||
} else if found, fine := intsInRange(r.BySetPosition, 366); !fine {
|
||||
msg := fmt.Sprintf("by month value of %d is out of bounds", found)
|
||||
return utils.NewError(r.ValidateICalValue, msg, r, nil)
|
||||
} else if err := dayInRange(r.WeekStart); r.WeekStart != "" && err != nil {
|
||||
return utils.NewError(r.ValidateICalValue, "week start value not in range", r, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func intsToCSV(ints []int) (string, error) {
|
||||
csv := new(CSV)
|
||||
for _, i := range ints {
|
||||
*csv = append(*csv, fmt.Sprintf("%d", i))
|
||||
}
|
||||
return csv.EncodeICalValue()
|
||||
}
|
||||
|
||||
func csvToInts(value string) (ints []int, err error) {
|
||||
csv := new(CSV)
|
||||
if ierr := csv.DecodeICalValue(value); err != nil {
|
||||
err = utils.NewError(csvToInts, "unable to decode CSV value", value, ierr)
|
||||
return
|
||||
}
|
||||
for _, v := range *csv {
|
||||
if i, ierr := strconv.ParseInt(v, 10, 64); err != nil {
|
||||
err = utils.NewError(csvToInts, "unable to parse int value "+v, value, ierr)
|
||||
return
|
||||
} else {
|
||||
ints = append(ints, int(i))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func intsInRange(ints []int, max int) (int, bool) {
|
||||
for _, i := range ints {
|
||||
if i < -max || i > max {
|
||||
return i, false
|
||||
}
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
|
||||
func daysInRange(days []RecurrenceWeekday) error {
|
||||
for _, day := range days {
|
||||
if err := dayInRange(day); err != nil {
|
||||
msg := fmt.Sprintf("day value %s is not in range", day)
|
||||
return utils.NewError(dayInRange, msg, days, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var dayRegExp = regexp.MustCompile("(\\d{1,2})?(\\w{2})")
|
||||
|
||||
func dayInRange(day RecurrenceWeekday) error {
|
||||
var ordinal, weekday string
|
||||
if matches := dayRegExp.FindAllStringSubmatch(string(day), -1); len(matches) <= 0 {
|
||||
msg := fmt.Sprintf("weekday value %s is not in valid format", day)
|
||||
return utils.NewError(dayInRange, msg, day, nil)
|
||||
} else if len(matches[0]) > 2 {
|
||||
ordinal = matches[0][1]
|
||||
weekday = matches[0][2]
|
||||
} else {
|
||||
weekday = matches[0][1]
|
||||
}
|
||||
if !RecurrenceWeekday(weekday).IsValidWeekDay() {
|
||||
msg := fmt.Sprintf("weekday value %s is not valid", weekday)
|
||||
return utils.NewError(dayInRange, msg, day, nil)
|
||||
} else if i, err := strconv.ParseInt(ordinal, 10, 64); ordinal != "" && err != nil {
|
||||
msg := fmt.Sprintf("weekday ordinal value %d is not valid", i)
|
||||
return utils.NewError(dayInRange, msg, day, err)
|
||||
} else if i < -53 || i > 53 {
|
||||
msg := fmt.Sprintf("weekday ordinal value %d is not in range", i)
|
||||
return utils.NewError(dayInRange, msg, day, nil)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func daysToCSV(days []RecurrenceWeekday) (string, error) {
|
||||
csv := new(CSV)
|
||||
for _, day := range days {
|
||||
*csv = append(*csv, strings.ToUpper(string(day)))
|
||||
}
|
||||
return csv.EncodeICalValue()
|
||||
}
|
||||
|
||||
func csvToDays(value string) (days []RecurrenceWeekday, err error) {
|
||||
csv := new(CSV)
|
||||
if ierr := csv.DecodeICalValue(value); err != nil {
|
||||
err = utils.NewError(csvToInts, "unable to decode CSV value", value, ierr)
|
||||
return
|
||||
}
|
||||
for _, v := range *csv {
|
||||
days = append(days, RecurrenceWeekday(v))
|
||||
}
|
||||
return
|
||||
}
|
12
vendor/github.com/dolanor/caldav-go/icalendar/values/time_transparency.go
generated
vendored
Normal file
12
vendor/github.com/dolanor/caldav-go/icalendar/values/time_transparency.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package values
|
||||
|
||||
// Time Transparency is the characteristic of an event that determines whether it appears to consume time on a calendar.
|
||||
// Events that consume actual time for the individual or resource associated with the calendar SHOULD be recorded as
|
||||
// OPAQUE, allowing them to be detected by free-busy time searches. Other events, which do not take up the individual's
|
||||
// (or resource's) time SHOULD be recorded as TRANSPARENT, making them invisible to free-busy time searches.
|
||||
type TimeTransparency string
|
||||
|
||||
const (
|
||||
OpaqueTimeTransparency TimeTransparency = "OPAQUE" // Blocks or opaque on busy time searches. DEFAULT
|
||||
TransparentTimeTransparency = "TRANSPARENT" // Transparent on busy time searches.
|
||||
)
|
49
vendor/github.com/dolanor/caldav-go/icalendar/values/url.go
generated
vendored
Normal file
49
vendor/github.com/dolanor/caldav-go/icalendar/values/url.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package values
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/icalendar/properties"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// a representation of duration for iCalendar
|
||||
type Url struct {
|
||||
u url.URL
|
||||
}
|
||||
|
||||
// encodes the URL into iCalendar format
|
||||
func (u *Url) EncodeICalValue() (string, error) {
|
||||
return u.u.String(), nil
|
||||
}
|
||||
|
||||
// encodes the url params for the iCalendar specification
|
||||
func (u *Url) EncodeICalParams() (params properties.Params, err error) {
|
||||
params = properties.Params{
|
||||
properties.ValuePropertyName: "URI",
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes the URL from iCalendar format
|
||||
func (u *Url) DecodeICalValue(value string) error {
|
||||
if parsed, err := url.Parse(value); err != nil {
|
||||
return utils.NewError(u.ValidateICalValue, "unable to parse url", u, err)
|
||||
} else {
|
||||
u.u = *parsed
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// validates the URL for iCalendar format
|
||||
func (u *Url) ValidateICalValue() error {
|
||||
if _, err := url.Parse(u.u.String()); err != nil {
|
||||
return utils.NewError(u.ValidateICalValue, "invalid URL object", u, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new iCalendar duration representation
|
||||
func NewUrl(u url.URL) *Url {
|
||||
return &Url{u: u}
|
||||
}
|
37
vendor/github.com/dolanor/caldav-go/utils/error.go
generated
vendored
Normal file
37
vendor/github.com/dolanor/caldav-go/utils/error.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
method interface{}
|
||||
message string
|
||||
context interface{}
|
||||
cause error
|
||||
}
|
||||
|
||||
func NewError(method interface{}, message string, context interface{}, cause error) *Error {
|
||||
e := new(Error)
|
||||
e.method = method
|
||||
e.message = message
|
||||
e.context = context
|
||||
e.cause = cause
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
pc := reflect.ValueOf(e.method).Pointer()
|
||||
fn := runtime.FuncForPC(pc).Name()
|
||||
msg := fmt.Sprintf("error: %s\nfunc: %s", e.message, fn)
|
||||
if e.context != nil {
|
||||
tname := reflect.ValueOf(e.context).Type()
|
||||
msg = fmt.Sprintf("%s\ncontext: %s", msg, tname.String())
|
||||
}
|
||||
if e.cause != nil {
|
||||
msg = fmt.Sprintf("%s\ncause: %s", msg, e.cause.Error())
|
||||
}
|
||||
return msg
|
||||
}
|
119
vendor/github.com/dolanor/caldav-go/webdav/client.go
generated
vendored
Normal file
119
vendor/github.com/dolanor/caldav-go/webdav/client.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dolanor/caldav-go/http"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"github.com/dolanor/caldav-go/webdav/entities"
|
||||
nhttp "net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
StatusMulti = 207
|
||||
)
|
||||
|
||||
// a client for making WebDAV requests
|
||||
type Client http.Client
|
||||
|
||||
// downcasts the client to the local HTTP interface
|
||||
func (c *Client) Http() *http.Client {
|
||||
return (*http.Client)(c)
|
||||
}
|
||||
|
||||
// returns the embedded WebDav server reference
|
||||
func (c *Client) Server() *Server {
|
||||
return (*Server)(c.Http().Server())
|
||||
}
|
||||
|
||||
// executes a WebDAV request
|
||||
func (c *Client) Do(req *Request) (*Response, error) {
|
||||
if resp, err := c.Http().Do((*http.Request)(req)); err != nil {
|
||||
return nil, utils.NewError(c.Do, "unable to execute WebDAV request", c, err)
|
||||
} else {
|
||||
return NewResponse(resp), nil
|
||||
}
|
||||
}
|
||||
|
||||
// checks if a resource exists given a particular path
|
||||
func (c *Client) Exists(path string) (bool, error) {
|
||||
if req, err := c.Server().NewRequest("HEAD", path); err != nil {
|
||||
return false, utils.NewError(c.Exists, "unable to create request", c, err)
|
||||
} else if resp, err := c.Do(req); err != nil {
|
||||
return false, utils.NewError(c.Exists, "unable to execute request", c, err)
|
||||
} else {
|
||||
return resp.StatusCode != nhttp.StatusNotFound, nil
|
||||
}
|
||||
}
|
||||
|
||||
// deletes a resource if it exists on a particular path
|
||||
func (c *Client) Delete(path string) error {
|
||||
if req, err := c.Server().NewRequest("DELETE", path); err != nil {
|
||||
return utils.NewError(c.Delete, "unable to create request", c, err)
|
||||
} else if resp, err := c.Do(req); err != nil {
|
||||
return utils.NewError(c.Delete, "unable to execute request", c, err)
|
||||
} else if resp.StatusCode != nhttp.StatusNoContent && resp.StatusCode != nhttp.StatusNotFound {
|
||||
err := new(entities.Error)
|
||||
resp.Decode(err)
|
||||
msg := fmt.Sprintf("unexpected server response %s", resp.Status)
|
||||
return utils.NewError(c.Delete, msg, c, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// fetches a list of WebDAV features supported by the server
|
||||
// returns an error if the server does not support DAV
|
||||
func (c *Client) Features(path string) ([]string, error) {
|
||||
if req, err := c.Server().NewRequest("OPTIONS", path); err != nil {
|
||||
return []string{}, utils.NewError(c.Features, "unable to create request", c, err)
|
||||
} else if resp, err := c.Do(req); err != nil {
|
||||
return []string{}, utils.NewError(c.Features, "unable to execute request", c, err)
|
||||
} else {
|
||||
return resp.Features(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// returns an error if the server does not support WebDAV
|
||||
func (c *Client) ValidateServer(path string) error {
|
||||
if features, err := c.Features(path); err != nil {
|
||||
return utils.NewError(c.ValidateServer, "feature detection failed", c, err)
|
||||
} else if len(features) <= 0 {
|
||||
return utils.NewError(c.ValidateServer, "no DAV headers found", c, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// executes a PROPFIND request against the WebDAV server
|
||||
// returns a multistatus XML entity
|
||||
func (c *Client) Propfind(path string, depth Depth, pf *entities.Propfind) (*entities.Multistatus, error) {
|
||||
|
||||
ms := new(entities.Multistatus)
|
||||
|
||||
if req, err := c.Server().NewRequest("PROPFIND", path, pf); err != nil {
|
||||
return nil, utils.NewError(c.Propfind, "unable to create request", c, err)
|
||||
} else if req.Http().Native().Header.Set("Depth", string(depth)); depth == "" {
|
||||
return nil, utils.NewError(c.Propfind, "search depth must be defined", c, nil)
|
||||
} else if resp, err := c.Do(req); err != nil {
|
||||
return nil, utils.NewError(c.Propfind, "unable to execute request", c, err)
|
||||
} else if resp.StatusCode != StatusMulti {
|
||||
msg := fmt.Sprintf("unexpected status: %s", resp.Status)
|
||||
return nil, utils.NewError(c.Propfind, msg, c, nil)
|
||||
} else if err := resp.Decode(ms); err != nil {
|
||||
return nil, utils.NewError(c.Propfind, "unable to decode response", c, err)
|
||||
}
|
||||
|
||||
return ms, nil
|
||||
|
||||
}
|
||||
|
||||
// creates a new client for communicating with an WebDAV server
|
||||
func NewClient(server *Server, native *nhttp.Client) *Client {
|
||||
return (*Client)(http.NewClient((*http.Server)(server), native))
|
||||
}
|
||||
|
||||
// creates a new client for communicating with a WebDAV server
|
||||
// uses the default HTTP client from net/http
|
||||
func NewDefaultClient(server *Server) *Client {
|
||||
return NewClient(server, nhttp.DefaultClient)
|
||||
}
|
9
vendor/github.com/dolanor/caldav-go/webdav/depth.go
generated
vendored
Normal file
9
vendor/github.com/dolanor/caldav-go/webdav/depth.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
package webdav
|
||||
|
||||
type Depth string
|
||||
|
||||
const (
|
||||
Depth0 Depth = "0"
|
||||
Depth1 = "1"
|
||||
DepthInfinity = "infinity"
|
||||
)
|
18
vendor/github.com/dolanor/caldav-go/webdav/entities/error.go
generated
vendored
Normal file
18
vendor/github.com/dolanor/caldav-go/webdav/entities/error.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package entities
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// a WebDAV error
|
||||
type Error struct {
|
||||
XMLName xml.Name `xml:"DAV: error"`
|
||||
Description string `xml:"error-description,omitempty"`
|
||||
Message string `xml:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if e.Description != "" {
|
||||
return e.Description
|
||||
} else {
|
||||
return e.Message
|
||||
}
|
||||
}
|
23
vendor/github.com/dolanor/caldav-go/webdav/entities/multistatus.go
generated
vendored
Normal file
23
vendor/github.com/dolanor/caldav-go/webdav/entities/multistatus.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package entities
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// metadata about a property
|
||||
type PropStat struct {
|
||||
XMLName xml.Name `xml:"propstat"`
|
||||
Status string `xml:"status"`
|
||||
Prop *Prop `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// a multistatus response entity
|
||||
type Response struct {
|
||||
XMLName xml.Name `xml:"response"`
|
||||
Href string `xml:"href"`
|
||||
PropStats []*PropStat `xml:"propstat,omitempty"`
|
||||
}
|
||||
|
||||
// a request to find properties on an an entity or collection
|
||||
type Multistatus struct {
|
||||
XMLName xml.Name `xml:"DAV: multistatus"`
|
||||
Responses []*Response `xml:"response,omitempty"`
|
||||
}
|
32
vendor/github.com/dolanor/caldav-go/webdav/entities/prop.go
generated
vendored
Normal file
32
vendor/github.com/dolanor/caldav-go/webdav/entities/prop.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
// a property of a resource
|
||||
type Prop struct {
|
||||
XMLName xml.Name `xml:"DAV: prop"`
|
||||
GetContentType string `xml:"getcontenttype,omitempty"`
|
||||
DisplayName string `xml:"displayname,omitempty"`
|
||||
ResourceType *ResourceType `xml:",omitempty"`
|
||||
CTag string `xml:"http://calendarserver.org/ns/ getctag,omitempty"`
|
||||
ETag string `xml:"http://calendarserver.org/ns/ getetag,omitempty"`
|
||||
}
|
||||
|
||||
// the type of a resource
|
||||
type ResourceType struct {
|
||||
XMLName xml.Name `xml:"resourcetype"`
|
||||
Collection *ResourceTypeCollection `xml:",omitempty"`
|
||||
Calendar *ResourceTypeCalendar `xml:",omitempty"`
|
||||
}
|
||||
|
||||
// A calendar resource type
|
||||
type ResourceTypeCalendar struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav calendar"`
|
||||
}
|
||||
|
||||
// A collection resource type
|
||||
type ResourceTypeCollection struct {
|
||||
XMLName xml.Name `xml:"collection"`
|
||||
}
|
20
vendor/github.com/dolanor/caldav-go/webdav/entities/propfind.go
generated
vendored
Normal file
20
vendor/github.com/dolanor/caldav-go/webdav/entities/propfind.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package entities
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// a request to find properties on an an entity or collection
|
||||
type Propfind struct {
|
||||
XMLName xml.Name `xml:"DAV: propfind"`
|
||||
AllProp *AllProp `xml:",omitempty"`
|
||||
Props []*Prop `xml:"prop,omitempty"`
|
||||
}
|
||||
|
||||
// a propfind property representing all properties
|
||||
type AllProp struct {
|
||||
XMLName xml.Name `xml:"allprop"`
|
||||
}
|
||||
|
||||
// a convenience method for searching all properties
|
||||
func NewAllPropsFind() *Propfind {
|
||||
return &Propfind{AllProp: new(AllProp)}
|
||||
}
|
56
vendor/github.com/dolanor/caldav-go/webdav/request.go
generated
vendored
Normal file
56
vendor/github.com/dolanor/caldav-go/webdav/request.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"github.com/dolanor/caldav-go/http"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
|
||||
// an WebDAV request object
|
||||
type Request http.Request
|
||||
|
||||
// downcasts the request to the local HTTP interface
|
||||
func (r *Request) Http() *http.Request {
|
||||
return (*http.Request)(r)
|
||||
}
|
||||
|
||||
// creates a new WebDAV request object
|
||||
func NewRequest(method string, urlstr string, xmldata ...interface{}) (*Request, error) {
|
||||
if buffer, length, err := xmlToReadCloser(xmldata); err != nil {
|
||||
return nil, utils.NewError(NewRequest, "unable to encode xml data", xmldata, err)
|
||||
} else if r, err := http.NewRequest(method, urlstr, buffer); err != nil {
|
||||
return nil, utils.NewError(NewRequest, "unable to create request", urlstr, err)
|
||||
} else {
|
||||
if buffer != nil {
|
||||
// set the content type to XML if we have a body
|
||||
r.Native().Header.Set("Content-Type", "text/xml; charset=UTF-8")
|
||||
r.ContentLength = int64(length)
|
||||
}
|
||||
return (*Request)(r), nil
|
||||
}
|
||||
}
|
||||
|
||||
func xmlToReadCloser(xmldata ...interface{}) (io.ReadCloser, int, error) {
|
||||
var buffer []string
|
||||
for _, xmldatum := range xmldata {
|
||||
if encoded, err := xml.Marshal(xmldatum); err != nil {
|
||||
return nil, 0, utils.NewError(xmlToReadCloser, "unable to encode as xml", xmldatum, err)
|
||||
} else {
|
||||
buffer = append(buffer, string(encoded))
|
||||
}
|
||||
}
|
||||
if len(buffer) > 0 {
|
||||
var encoded = strings.Join(buffer, "\n")
|
||||
// log.Printf("[WebDAV Request]\n%+v\n", encoded)
|
||||
return ioutil.NopCloser(bytes.NewBuffer([]byte(encoded))), len(encoded), nil
|
||||
} else {
|
||||
return nil, 0, nil
|
||||
}
|
||||
}
|
54
vendor/github.com/dolanor/caldav-go/webdav/response.go
generated
vendored
Normal file
54
vendor/github.com/dolanor/caldav-go/webdav/response.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/dolanor/caldav-go/http"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = log.Print
|
||||
var _ = ioutil.ReadAll
|
||||
|
||||
// a WebDAV response object
|
||||
type Response http.Response
|
||||
|
||||
// downcasts the response to the local HTTP interface
|
||||
func (r *Response) Http() *http.Response {
|
||||
return (*http.Response)(r)
|
||||
}
|
||||
|
||||
// returns a list of WebDAV features found in the response
|
||||
func (r *Response) Features() (features []string) {
|
||||
if dav := r.Header.Get("DAV"); dav != "" {
|
||||
features = strings.Split(dav, ", ")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// decodes a WebDAV XML response into the provided interface
|
||||
func (r *Response) Decode(into interface{}) error {
|
||||
// data, _ := ioutil.ReadAll(r.Body)
|
||||
// log.Printf("[WebDAV Response]\n%+v\n", string(data))
|
||||
// if err := xml.Unmarshal(data, into); err != nil {
|
||||
// return utils.NewError(r.Decode, "unable to decode response body", r, err)
|
||||
// } else {
|
||||
// return nil
|
||||
// }
|
||||
if body := r.Body; body == nil {
|
||||
return nil
|
||||
} else if decoder := xml.NewDecoder(body); decoder == nil {
|
||||
return nil
|
||||
} else if err := decoder.Decode(into); err != nil {
|
||||
return utils.NewError(r.Decode, "unable to decode response body", r, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new WebDAV response object
|
||||
func NewResponse(response *http.Response) *Response {
|
||||
return (*Response)(response)
|
||||
}
|
28
vendor/github.com/dolanor/caldav-go/webdav/server.go
generated
vendored
Normal file
28
vendor/github.com/dolanor/caldav-go/webdav/server.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"github.com/dolanor/caldav-go/http"
|
||||
"github.com/dolanor/caldav-go/utils"
|
||||
)
|
||||
|
||||
// a server that accepts WebDAV requests
|
||||
type Server http.Server
|
||||
|
||||
// creates a reference to an WebDAV server
|
||||
func NewServer(baseUrlStr string) (*Server, error) {
|
||||
if s, err := http.NewServer(baseUrlStr); err != nil {
|
||||
return nil, utils.NewError(NewServer, "unable to create WebDAV server", baseUrlStr, err)
|
||||
} else {
|
||||
return (*Server)(s), nil
|
||||
}
|
||||
}
|
||||
|
||||
// downcasts the server to the local HTTP interface
|
||||
func (s *Server) Http() *http.Server {
|
||||
return (*http.Server)(s)
|
||||
}
|
||||
|
||||
// creates a new WebDAV request object
|
||||
func (s *Server) NewRequest(method string, path string, xmldata ...interface{}) (*Request, error) {
|
||||
return NewRequest(method, s.Http().AbsUrlStr(path), xmldata...)
|
||||
}
|
19
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
Normal file
19
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 100 # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
|
||||
# Also update COVER_IGNORE_PKGS in the Makefile.
|
||||
ignore:
|
||||
- /internal/gen-atomicint/
|
||||
- /internal/gen-valuewrapper/
|
12
vendor/go.uber.org/atomic/.gitignore
generated
vendored
Normal file
12
vendor/go.uber.org/atomic/.gitignore
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
/bin
|
||||
.DS_Store
|
||||
/vendor
|
||||
cover.html
|
||||
cover.out
|
||||
lint.log
|
||||
|
||||
# Binaries
|
||||
*.test
|
||||
|
||||
# Profiling output
|
||||
*.prof
|
27
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
Normal file
27
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/atomic
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: oldstable
|
||||
- go: stable
|
||||
env: LINT=1
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
||||
script:
|
||||
- test -z "$LINT" || make lint
|
||||
- make cover
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
76
vendor/go.uber.org/atomic/CHANGELOG.md
generated
vendored
Normal file
76
vendor/go.uber.org/atomic/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.7.0] - 2020-09-14
|
||||
### Added
|
||||
- Support JSON serialization and deserialization of primitive atomic types.
|
||||
- Support Text marshalling and unmarshalling for string atomics.
|
||||
|
||||
### Changed
|
||||
- Disallow incorrect comparison of atomic values in a non-atomic way.
|
||||
|
||||
### Removed
|
||||
- Remove dependency on `golang.org/x/{lint, tools}`.
|
||||
|
||||
## [1.6.0] - 2020-02-24
|
||||
### Changed
|
||||
- Drop library dependency on `golang.org/x/{lint, tools}`.
|
||||
|
||||
## [1.5.1] - 2019-11-19
|
||||
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
|
||||
causing `CAS` to fail even though the old value matches.
|
||||
|
||||
## [1.5.0] - 2019-10-29
|
||||
### Changed
|
||||
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
|
||||
If you need to use the old import path, please add a `replace` directive to
|
||||
your `go.mod`.
|
||||
|
||||
## [1.4.0] - 2019-05-01
|
||||
### Added
|
||||
- Add `atomic.Error` type for atomic operations on `error` values.
|
||||
|
||||
## [1.3.2] - 2018-05-02
|
||||
### Added
|
||||
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
|
||||
|
||||
## [1.3.1] - 2017-11-14
|
||||
### Fixed
|
||||
- Revert optimization for `atomic.String.Store("")` which caused data races.
|
||||
|
||||
## [1.3.0] - 2017-11-13
|
||||
### Added
|
||||
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
|
||||
|
||||
### Changed
|
||||
- Optimize `atomic.String.Store("")` by avoiding an allocation.
|
||||
|
||||
## [1.2.0] - 2017-04-12
|
||||
### Added
|
||||
- Shadow `atomic.Value` from `sync/atomic`.
|
||||
|
||||
## [1.1.0] - 2017-03-10
|
||||
### Added
|
||||
- Add atomic `Float64` type.
|
||||
|
||||
### Changed
|
||||
- Support new `go.uber.org/atomic` import path.
|
||||
|
||||
## [1.0.0] - 2016-07-18
|
||||
|
||||
- Initial release.
|
||||
|
||||
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
|
||||
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
|
||||
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
|
||||
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
|
||||
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
|
||||
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
|
||||
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
|
||||
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
|
||||
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
|
||||
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
|
||||
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0
|
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016 Uber Technologies, Inc.
|
||||
|
||||
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.
|
78
vendor/go.uber.org/atomic/Makefile
generated
vendored
Normal file
78
vendor/go.uber.org/atomic/Makefile
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
# Directory to place `go install`ed binaries into.
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
|
||||
GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
|
||||
STATICCHECK = $(GOBIN)/staticcheck
|
||||
|
||||
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
|
||||
|
||||
# Also update ignore section in .codecov.yml.
|
||||
COVER_IGNORE_PKGS = \
|
||||
go.uber.org/atomic/internal/gen-atomicint \
|
||||
go.uber.org/atomic/internal/gen-atomicwrapper
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build ./...
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race ./...
|
||||
|
||||
.PHONY: gofmt
|
||||
gofmt:
|
||||
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||
gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
|
||||
|
||||
$(GOLINT):
|
||||
cd tools && go install golang.org/x/lint/golint
|
||||
|
||||
$(STATICCHECK):
|
||||
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
$(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
|
||||
go build -o $@ ./internal/gen-atomicwrapper
|
||||
|
||||
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
|
||||
go build -o $@ ./internal/gen-atomicint
|
||||
|
||||
.PHONY: golint
|
||||
golint: $(GOLINT)
|
||||
$(GOLINT) ./...
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck: $(STATICCHECK)
|
||||
$(STATICCHECK) ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint: gofmt golint staticcheck generatenodirty
|
||||
|
||||
# comma separated list of packages to consider for code coverage.
|
||||
COVER_PKG = $(shell \
|
||||
go list -find ./... | \
|
||||
grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
|
||||
paste -sd, -)
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
|
||||
go generate ./...
|
||||
|
||||
.PHONY: generatenodirty
|
||||
generatenodirty:
|
||||
@[ -z "$$(git status --porcelain)" ] || ( \
|
||||
echo "Working tree is dirty. Commit your changes first."; \
|
||||
exit 1 )
|
||||
@make generate
|
||||
@status=$$(git status --porcelain); \
|
||||
[ -z "$$status" ] || ( \
|
||||
echo "Working tree is dirty after `make generate`:"; \
|
||||
echo "$$status"; \
|
||||
echo "Please ensure that the generated code is up-to-date." )
|
63
vendor/go.uber.org/atomic/README.md
generated
vendored
Normal file
63
vendor/go.uber.org/atomic/README.md
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
|
||||
|
||||
Simple wrappers for primitive types to enforce atomic access.
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
$ go get -u go.uber.org/atomic@v1
|
||||
```
|
||||
|
||||
### Legacy Import Path
|
||||
|
||||
As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
|
||||
of using this package. If you are using Go modules, this package will fail to
|
||||
compile with the legacy import path path `github.com/uber-go/atomic`.
|
||||
|
||||
We recommend migrating your code to the new import path but if you're unable
|
||||
to do so, or if your dependencies are still using the old import path, you
|
||||
will have to add a `replace` directive to your `go.mod` file downgrading the
|
||||
legacy import path to an older version.
|
||||
|
||||
```
|
||||
replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
|
||||
```
|
||||
|
||||
You can do so automatically by running the following command.
|
||||
|
||||
```shell
|
||||
$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The standard library's `sync/atomic` is powerful, but it's easy to forget which
|
||||
variables must be accessed atomically. `go.uber.org/atomic` preserves all the
|
||||
functionality of the standard library, but wraps the primitive types to
|
||||
provide a safer, more convenient API.
|
||||
|
||||
```go
|
||||
var atom atomic.Uint32
|
||||
atom.Store(42)
|
||||
atom.Sub(2)
|
||||
atom.CAS(40, 11)
|
||||
```
|
||||
|
||||
See the [documentation][doc] for a complete API specification.
|
||||
|
||||
## Development Status
|
||||
|
||||
Stable.
|
||||
|
||||
---
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/atomic
|
||||
[ci-img]: https://travis-ci.com/uber-go/atomic.svg?branch=master
|
||||
[ci]: https://travis-ci.com/uber-go/atomic
|
||||
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/atomic
|
||||
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
|
||||
[reportcard]: https://goreportcard.com/report/go.uber.org/atomic
|
81
vendor/go.uber.org/atomic/bool.go
generated
vendored
Normal file
81
vendor/go.uber.org/atomic/bool.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Bool is an atomic type-safe wrapper for bool values.
|
||||
type Bool struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Uint32
|
||||
}
|
||||
|
||||
var _zeroBool bool
|
||||
|
||||
// NewBool creates a new Bool.
|
||||
func NewBool(v bool) *Bool {
|
||||
x := &Bool{}
|
||||
if v != _zeroBool {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped bool.
|
||||
func (x *Bool) Load() bool {
|
||||
return truthy(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed bool.
|
||||
func (x *Bool) Store(v bool) {
|
||||
x.v.Store(boolToInt(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for bool values.
|
||||
func (x *Bool) CAS(o, n bool) bool {
|
||||
return x.v.CAS(boolToInt(o), boolToInt(n))
|
||||
}
|
||||
|
||||
// Swap atomically stores the given bool and returns the old
|
||||
// value.
|
||||
func (x *Bool) Swap(o bool) bool {
|
||||
return truthy(x.v.Swap(boolToInt(o)))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped bool into JSON.
|
||||
func (x *Bool) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a bool from JSON.
|
||||
func (x *Bool) UnmarshalJSON(b []byte) error {
|
||||
var v bool
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
53
vendor/go.uber.org/atomic/bool_ext.go
generated
vendored
Normal file
53
vendor/go.uber.org/atomic/bool_ext.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
|
||||
|
||||
func truthy(n uint32) bool {
|
||||
return n == 1
|
||||
}
|
||||
|
||||
func boolToInt(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Toggle atomically negates the Boolean and returns the previous value.
|
||||
func (b *Bool) Toggle() bool {
|
||||
for {
|
||||
old := b.Load()
|
||||
if b.CAS(old, !old) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (b *Bool) String() string {
|
||||
return strconv.FormatBool(b.Load())
|
||||
}
|
23
vendor/go.uber.org/atomic/doc.go
generated
vendored
Normal file
23
vendor/go.uber.org/atomic/doc.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Package atomic provides simple wrappers around numerics to enforce atomic
|
||||
// access.
|
||||
package atomic
|
82
vendor/go.uber.org/atomic/duration.go
generated
vendored
Normal file
82
vendor/go.uber.org/atomic/duration.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Duration is an atomic type-safe wrapper for time.Duration values.
|
||||
type Duration struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Int64
|
||||
}
|
||||
|
||||
var _zeroDuration time.Duration
|
||||
|
||||
// NewDuration creates a new Duration.
|
||||
func NewDuration(v time.Duration) *Duration {
|
||||
x := &Duration{}
|
||||
if v != _zeroDuration {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped time.Duration.
|
||||
func (x *Duration) Load() time.Duration {
|
||||
return time.Duration(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed time.Duration.
|
||||
func (x *Duration) Store(v time.Duration) {
|
||||
x.v.Store(int64(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for time.Duration values.
|
||||
func (x *Duration) CAS(o, n time.Duration) bool {
|
||||
return x.v.CAS(int64(o), int64(n))
|
||||
}
|
||||
|
||||
// Swap atomically stores the given time.Duration and returns the old
|
||||
// value.
|
||||
func (x *Duration) Swap(o time.Duration) time.Duration {
|
||||
return time.Duration(x.v.Swap(int64(o)))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped time.Duration into JSON.
|
||||
func (x *Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a time.Duration from JSON.
|
||||
func (x *Duration) UnmarshalJSON(b []byte) error {
|
||||
var v time.Duration
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
40
vendor/go.uber.org/atomic/duration_ext.go
generated
vendored
Normal file
40
vendor/go.uber.org/atomic/duration_ext.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import "time"
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
|
||||
|
||||
// Add atomically adds to the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Add(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Add(int64(n)))
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Sub(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Sub(int64(n)))
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (d *Duration) String() string {
|
||||
return d.Load().String()
|
||||
}
|
51
vendor/go.uber.org/atomic/error.go
generated
vendored
Normal file
51
vendor/go.uber.org/atomic/error.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
// Error is an atomic type-safe wrapper for error values.
|
||||
type Error struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Value
|
||||
}
|
||||
|
||||
var _zeroError error
|
||||
|
||||
// NewError creates a new Error.
|
||||
func NewError(v error) *Error {
|
||||
x := &Error{}
|
||||
if v != _zeroError {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped error.
|
||||
func (x *Error) Load() error {
|
||||
return unpackError(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed error.
|
||||
func (x *Error) Store(v error) {
|
||||
x.v.Store(packError(v))
|
||||
}
|
39
vendor/go.uber.org/atomic/error_ext.go
generated
vendored
Normal file
39
vendor/go.uber.org/atomic/error_ext.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
// atomic.Value panics on nil inputs, or if the underlying type changes.
|
||||
// Stabilize by always storing a custom struct that we control.
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -file=error.go
|
||||
|
||||
type packedError struct{ Value error }
|
||||
|
||||
func packError(v error) interface{} {
|
||||
return packedError{v}
|
||||
}
|
||||
|
||||
func unpackError(v interface{}) error {
|
||||
if err, ok := v.(packedError); ok {
|
||||
return err.Value
|
||||
}
|
||||
return nil
|
||||
}
|
76
vendor/go.uber.org/atomic/float64.go
generated
vendored
Normal file
76
vendor/go.uber.org/atomic/float64.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Float64 is an atomic type-safe wrapper for float64 values.
|
||||
type Float64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Uint64
|
||||
}
|
||||
|
||||
var _zeroFloat64 float64
|
||||
|
||||
// NewFloat64 creates a new Float64.
|
||||
func NewFloat64(v float64) *Float64 {
|
||||
x := &Float64{}
|
||||
if v != _zeroFloat64 {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped float64.
|
||||
func (x *Float64) Load() float64 {
|
||||
return math.Float64frombits(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed float64.
|
||||
func (x *Float64) Store(v float64) {
|
||||
x.v.Store(math.Float64bits(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for float64 values.
|
||||
func (x *Float64) CAS(o, n float64) bool {
|
||||
return x.v.CAS(math.Float64bits(o), math.Float64bits(n))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped float64 into JSON.
|
||||
func (x *Float64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a float64 from JSON.
|
||||
func (x *Float64) UnmarshalJSON(b []byte) error {
|
||||
var v float64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
47
vendor/go.uber.org/atomic/float64_ext.go
generated
vendored
Normal file
47
vendor/go.uber.org/atomic/float64_ext.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import "strconv"
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -cas -json -imports math -file=float64.go
|
||||
|
||||
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Add(s float64) float64 {
|
||||
for {
|
||||
old := f.Load()
|
||||
new := old + s
|
||||
if f.CAS(old, new) {
|
||||
return new
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Sub(s float64) float64 {
|
||||
return f.Add(-s)
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (f *Float64) String() string {
|
||||
// 'g' is the behavior for floats with %v.
|
||||
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
|
||||
}
|
26
vendor/go.uber.org/atomic/gen.go
generated
vendored
Normal file
26
vendor/go.uber.org/atomic/gen.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
//go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
|
||||
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
||||
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
||||
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
102
vendor/go.uber.org/atomic/int32.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/int32.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Int32 is an atomic wrapper around int32.
|
||||
type Int32 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v int32
|
||||
}
|
||||
|
||||
// NewInt32 creates a new Int32.
|
||||
func NewInt32(i int32) *Int32 {
|
||||
return &Int32{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int32) Load() int32 {
|
||||
return atomic.LoadInt32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Add(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Sub(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Inc() int32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Dec() int32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int32) CAS(old, new int32) bool {
|
||||
return atomic.CompareAndSwapInt32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int32) Store(n int32) {
|
||||
atomic.StoreInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||
func (i *Int32) Swap(n int32) int32 {
|
||||
return atomic.SwapInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int32 into JSON.
|
||||
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int32.
|
||||
func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||
var v int32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
102
vendor/go.uber.org/atomic/int64.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/int64.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Int64 is an atomic wrapper around int64.
|
||||
type Int64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v int64
|
||||
}
|
||||
|
||||
// NewInt64 creates a new Int64.
|
||||
func NewInt64(i int64) *Int64 {
|
||||
return &Int64{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int64) Load() int64 {
|
||||
return atomic.LoadInt64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Add(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Sub(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Inc() int64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Dec() int64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int64) CAS(old, new int64) bool {
|
||||
return atomic.CompareAndSwapInt64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int64) Store(n int64) {
|
||||
atomic.StoreInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||
func (i *Int64) Swap(n int64) int64 {
|
||||
return atomic.SwapInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int64 into JSON.
|
||||
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int64.
|
||||
func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||
var v int64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
35
vendor/go.uber.org/atomic/nocmp.go
generated
vendored
Normal file
35
vendor/go.uber.org/atomic/nocmp.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
// nocmp is an uncomparable struct. Embed this inside another struct to make
|
||||
// it uncomparable.
|
||||
//
|
||||
// type Foo struct {
|
||||
// nocmp
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// This DOES NOT:
|
||||
//
|
||||
// - Disallow shallow copies of structs
|
||||
// - Disallow comparison of pointers to uncomparable structs
|
||||
type nocmp [0]func()
|
54
vendor/go.uber.org/atomic/string.go
generated
vendored
Normal file
54
vendor/go.uber.org/atomic/string.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
// String is an atomic type-safe wrapper for string values.
|
||||
type String struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Value
|
||||
}
|
||||
|
||||
var _zeroString string
|
||||
|
||||
// NewString creates a new String.
|
||||
func NewString(v string) *String {
|
||||
x := &String{}
|
||||
if v != _zeroString {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped string.
|
||||
func (x *String) Load() string {
|
||||
if v := x.v.Load(); v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
return _zeroString
|
||||
}
|
||||
|
||||
// Store atomically stores the passed string.
|
||||
func (x *String) Store(v string) {
|
||||
x.v.Store(v)
|
||||
}
|
43
vendor/go.uber.org/atomic/string_ext.go
generated
vendored
Normal file
43
vendor/go.uber.org/atomic/string_ext.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped=Value -file=string.go
|
||||
|
||||
// String returns the wrapped value.
|
||||
func (s *String) String() string {
|
||||
return s.Load()
|
||||
}
|
||||
|
||||
// MarshalText encodes the wrapped string into a textual form.
|
||||
//
|
||||
// This makes it encodable as JSON, YAML, XML, and more.
|
||||
func (s *String) MarshalText() ([]byte, error) {
|
||||
return []byte(s.Load()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText decodes text and replaces the wrapped string with it.
|
||||
//
|
||||
// This makes it decodable from JSON, YAML, XML, and more.
|
||||
func (s *String) UnmarshalText(b []byte) error {
|
||||
s.Store(string(b))
|
||||
return nil
|
||||
}
|
102
vendor/go.uber.org/atomic/uint32.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/uint32.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Uint32 is an atomic wrapper around uint32.
|
||||
type Uint32 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v uint32
|
||||
}
|
||||
|
||||
// NewUint32 creates a new Uint32.
|
||||
func NewUint32(i uint32) *Uint32 {
|
||||
return &Uint32{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint32) Load() uint32 {
|
||||
return atomic.LoadUint32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Add(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Sub(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Inc() uint32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Dec() uint32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint32) CAS(old, new uint32) bool {
|
||||
return atomic.CompareAndSwapUint32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint32) Store(n uint32) {
|
||||
atomic.StoreUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||
func (i *Uint32) Swap(n uint32) uint32 {
|
||||
return atomic.SwapUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint32 into JSON.
|
||||
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint32.
|
||||
func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||
var v uint32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
102
vendor/go.uber.org/atomic/uint64.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/uint64.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Uint64 is an atomic wrapper around uint64.
|
||||
type Uint64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v uint64
|
||||
}
|
||||
|
||||
// NewUint64 creates a new Uint64.
|
||||
func NewUint64(i uint64) *Uint64 {
|
||||
return &Uint64{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint64) Load() uint64 {
|
||||
return atomic.LoadUint64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Add(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Sub(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Inc() uint64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Dec() uint64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint64) CAS(old, new uint64) bool {
|
||||
return atomic.CompareAndSwapUint64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint64) Store(n uint64) {
|
||||
atomic.StoreUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||
func (i *Uint64) Swap(n uint64) uint64 {
|
||||
return atomic.SwapUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint64 into JSON.
|
||||
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint64.
|
||||
func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||
var v uint64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
31
vendor/go.uber.org/atomic/value.go
generated
vendored
Normal file
31
vendor/go.uber.org/atomic/value.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// Value shadows the type of the same name from sync/atomic
|
||||
// https://godoc.org/sync/atomic#Value
|
||||
type Value struct {
|
||||
atomic.Value
|
||||
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
}
|
15
vendor/go.uber.org/multierr/.codecov.yml
generated
vendored
Normal file
15
vendor/go.uber.org/multierr/.codecov.yml
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 100 # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
|
4
vendor/go.uber.org/multierr/.gitignore
generated
vendored
Normal file
4
vendor/go.uber.org/multierr/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/vendor
|
||||
cover.html
|
||||
cover.out
|
||||
/bin
|
23
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
Normal file
23
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/multierr
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
go:
|
||||
- oldstable
|
||||
- stable
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
||||
script:
|
||||
- |
|
||||
set -e
|
||||
make lint
|
||||
make cover
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
60
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
Normal file
60
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
Releases
|
||||
========
|
||||
|
||||
v1.6.0 (2020-09-14)
|
||||
===================
|
||||
|
||||
- Actually drop library dependency on development-time tooling.
|
||||
|
||||
|
||||
v1.5.0 (2020-02-24)
|
||||
===================
|
||||
|
||||
- Drop library dependency on development-time tooling.
|
||||
|
||||
|
||||
v1.4.0 (2019-11-04)
|
||||
===================
|
||||
|
||||
- Add `AppendInto` function to more ergonomically build errors inside a
|
||||
loop.
|
||||
|
||||
|
||||
v1.3.0 (2019-10-29)
|
||||
===================
|
||||
|
||||
- Switch to Go modules.
|
||||
|
||||
|
||||
v1.2.0 (2019-09-26)
|
||||
===================
|
||||
|
||||
- Support extracting and matching against wrapped errors with `errors.As`
|
||||
and `errors.Is`.
|
||||
|
||||
|
||||
v1.1.0 (2017-06-30)
|
||||
===================
|
||||
|
||||
- Added an `Errors(error) []error` function to extract the underlying list of
|
||||
errors for a multierr error.
|
||||
|
||||
|
||||
v1.0.0 (2017-05-31)
|
||||
===================
|
||||
|
||||
No changes since v0.2.0. This release is committing to making no breaking
|
||||
changes to the current API in the 1.X series.
|
||||
|
||||
|
||||
v0.2.0 (2017-04-11)
|
||||
===================
|
||||
|
||||
- Repeatedly appending to the same error is now faster due to fewer
|
||||
allocations.
|
||||
|
||||
|
||||
v0.1.0 (2017-31-03)
|
||||
===================
|
||||
|
||||
- Initial release
|
19
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017 Uber Technologies, Inc.
|
||||
|
||||
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.
|
42
vendor/go.uber.org/multierr/Makefile
generated
vendored
Normal file
42
vendor/go.uber.org/multierr/Makefile
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# Directory to put `go install`ed binaries in.
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build ./...
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race ./...
|
||||
|
||||
.PHONY: gofmt
|
||||
gofmt:
|
||||
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||
@gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false)
|
||||
|
||||
.PHONY: golint
|
||||
golint:
|
||||
@cd tools && go install golang.org/x/lint/golint
|
||||
@$(GOBIN)/golint ./...
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck:
|
||||
@cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
@$(GOBIN)/staticcheck ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint: gofmt golint staticcheck
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=cover.out -coverpkg=./... -v ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
update-license:
|
||||
@cd tools && go install go.uber.org/tools/update-license
|
||||
@$(GOBIN)/update-license $(GO_FILES)
|
23
vendor/go.uber.org/multierr/README.md
generated
vendored
Normal file
23
vendor/go.uber.org/multierr/README.md
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
`multierr` allows combining one or more Go `error`s together.
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u go.uber.org/multierr
|
||||
|
||||
## Status
|
||||
|
||||
Stable: No breaking changes will be made before 2.0.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Released under the [MIT License].
|
||||
|
||||
[MIT License]: LICENSE.txt
|
||||
[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/multierr
|
||||
[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master
|
||||
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
|
||||
[ci]: https://travis-ci.com/uber-go/multierr
|
||||
[cov]: https://codecov.io/gh/uber-go/multierr
|
449
vendor/go.uber.org/multierr/error.go
generated
vendored
Normal file
449
vendor/go.uber.org/multierr/error.go
generated
vendored
Normal file
@ -0,0 +1,449 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Package multierr allows combining one or more errors together.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// Errors can be combined with the use of the Combine function.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// conn.Close(),
|
||||
// )
|
||||
//
|
||||
// If only two errors are being combined, the Append function may be used
|
||||
// instead.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// This makes it possible to record resource cleanup failures from deferred
|
||||
// blocks with the help of named return values.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, conn.Close())
|
||||
// }()
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// The underlying list of errors for a returned error object may be retrieved
|
||||
// with the Errors function.
|
||||
//
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:", errors)
|
||||
// }
|
||||
//
|
||||
// Advanced Usage
|
||||
//
|
||||
// Errors returned by Combine and Append MAY implement the following
|
||||
// interface.
|
||||
//
|
||||
// type errorGroup interface {
|
||||
// // Returns a slice containing the underlying list of errors.
|
||||
// //
|
||||
// // This slice MUST NOT be modified by the caller.
|
||||
// Errors() []error
|
||||
// }
|
||||
//
|
||||
// Note that if you need access to list of errors behind a multierr error, you
|
||||
// should prefer using the Errors function. That said, if you need cheap
|
||||
// read-only access to the underlying errors slice, you can attempt to cast
|
||||
// the error to this interface. You MUST handle the failure case gracefully
|
||||
// because errors returned by Combine and Append are not guaranteed to
|
||||
// implement this interface.
|
||||
//
|
||||
// var errors []error
|
||||
// group, ok := err.(errorGroup)
|
||||
// if ok {
|
||||
// errors = group.Errors()
|
||||
// } else {
|
||||
// errors = []error{err}
|
||||
// }
|
||||
package multierr // import "go.uber.org/multierr"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
// Separator for single-line error messages.
|
||||
_singlelineSeparator = []byte("; ")
|
||||
|
||||
// Prefix for multi-line messages
|
||||
_multilinePrefix = []byte("the following errors occurred:")
|
||||
|
||||
// Prefix for the first and following lines of an item in a list of
|
||||
// multi-line error messages.
|
||||
//
|
||||
// For example, if a single item is:
|
||||
//
|
||||
// foo
|
||||
// bar
|
||||
//
|
||||
// It will become,
|
||||
//
|
||||
// - foo
|
||||
// bar
|
||||
_multilineSeparator = []byte("\n - ")
|
||||
_multilineIndent = []byte(" ")
|
||||
)
|
||||
|
||||
// _bufferPool is a pool of bytes.Buffers.
|
||||
var _bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &bytes.Buffer{}
|
||||
},
|
||||
}
|
||||
|
||||
type errorGroup interface {
|
||||
Errors() []error
|
||||
}
|
||||
|
||||
// Errors returns a slice containing zero or more errors that the supplied
|
||||
// error is composed of. If the error is nil, a nil slice is returned.
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// errors := multierr.Errors(err)
|
||||
//
|
||||
// If the error is not composed of other errors, the returned slice contains
|
||||
// just the error that was passed in.
|
||||
//
|
||||
// Callers of this function are free to modify the returned slice.
|
||||
func Errors(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Note that we're casting to multiError, not errorGroup. Our contract is
|
||||
// that returned errors MAY implement errorGroup. Errors, however, only
|
||||
// has special behavior for multierr-specific error objects.
|
||||
//
|
||||
// This behavior can be expanded in the future but I think it's prudent to
|
||||
// start with as little as possible in terms of contract and possibility
|
||||
// of misuse.
|
||||
eg, ok := err.(*multiError)
|
||||
if !ok {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
errors := eg.Errors()
|
||||
result := make([]error, len(errors))
|
||||
copy(result, errors)
|
||||
return result
|
||||
}
|
||||
|
||||
// multiError is an error that holds one or more errors.
|
||||
//
|
||||
// An instance of this is guaranteed to be non-empty and flattened. That is,
|
||||
// none of the errors inside multiError are other multiErrors.
|
||||
//
|
||||
// multiError formats to a semi-colon delimited list of error messages with
|
||||
// %v and with a more readable multi-line format with %+v.
|
||||
type multiError struct {
|
||||
copyNeeded atomic.Bool
|
||||
errors []error
|
||||
}
|
||||
|
||||
var _ errorGroup = (*multiError)(nil)
|
||||
|
||||
// Errors returns the list of underlying errors.
|
||||
//
|
||||
// This slice MUST NOT be modified.
|
||||
func (merr *multiError) Errors() []error {
|
||||
if merr == nil {
|
||||
return nil
|
||||
}
|
||||
return merr.errors
|
||||
}
|
||||
|
||||
func (merr *multiError) Error() string {
|
||||
if merr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
buff := _bufferPool.Get().(*bytes.Buffer)
|
||||
buff.Reset()
|
||||
|
||||
merr.writeSingleline(buff)
|
||||
|
||||
result := buff.String()
|
||||
_bufferPool.Put(buff)
|
||||
return result
|
||||
}
|
||||
|
||||
func (merr *multiError) Format(f fmt.State, c rune) {
|
||||
if c == 'v' && f.Flag('+') {
|
||||
merr.writeMultiline(f)
|
||||
} else {
|
||||
merr.writeSingleline(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (merr *multiError) writeSingleline(w io.Writer) {
|
||||
first := true
|
||||
for _, item := range merr.errors {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.Write(_singlelineSeparator)
|
||||
}
|
||||
io.WriteString(w, item.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (merr *multiError) writeMultiline(w io.Writer) {
|
||||
w.Write(_multilinePrefix)
|
||||
for _, item := range merr.errors {
|
||||
w.Write(_multilineSeparator)
|
||||
writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
|
||||
}
|
||||
}
|
||||
|
||||
// Writes s to the writer with the given prefix added before each line after
|
||||
// the first.
|
||||
func writePrefixLine(w io.Writer, prefix []byte, s string) {
|
||||
first := true
|
||||
for len(s) > 0 {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.Write(prefix)
|
||||
}
|
||||
|
||||
idx := strings.IndexByte(s, '\n')
|
||||
if idx < 0 {
|
||||
idx = len(s) - 1
|
||||
}
|
||||
|
||||
io.WriteString(w, s[:idx+1])
|
||||
s = s[idx+1:]
|
||||
}
|
||||
}
|
||||
|
||||
type inspectResult struct {
|
||||
// Number of top-level non-nil errors
|
||||
Count int
|
||||
|
||||
// Total number of errors including multiErrors
|
||||
Capacity int
|
||||
|
||||
// Index of the first non-nil error in the list. Value is meaningless if
|
||||
// Count is zero.
|
||||
FirstErrorIdx int
|
||||
|
||||
// Whether the list contains at least one multiError
|
||||
ContainsMultiError bool
|
||||
}
|
||||
|
||||
// Inspects the given slice of errors so that we can efficiently allocate
|
||||
// space for it.
|
||||
func inspect(errors []error) (res inspectResult) {
|
||||
first := true
|
||||
for i, err := range errors {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Count++
|
||||
if first {
|
||||
first = false
|
||||
res.FirstErrorIdx = i
|
||||
}
|
||||
|
||||
if merr, ok := err.(*multiError); ok {
|
||||
res.Capacity += len(merr.errors)
|
||||
res.ContainsMultiError = true
|
||||
} else {
|
||||
res.Capacity++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fromSlice converts the given list of errors into a single error.
|
||||
func fromSlice(errors []error) error {
|
||||
res := inspect(errors)
|
||||
switch res.Count {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
// only one non-nil entry
|
||||
return errors[res.FirstErrorIdx]
|
||||
case len(errors):
|
||||
if !res.ContainsMultiError {
|
||||
// already flat
|
||||
return &multiError{errors: errors}
|
||||
}
|
||||
}
|
||||
|
||||
nonNilErrs := make([]error, 0, res.Capacity)
|
||||
for _, err := range errors[res.FirstErrorIdx:] {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if nested, ok := err.(*multiError); ok {
|
||||
nonNilErrs = append(nonNilErrs, nested.errors...)
|
||||
} else {
|
||||
nonNilErrs = append(nonNilErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return &multiError{errors: nonNilErrs}
|
||||
}
|
||||
|
||||
// Combine combines the passed errors into a single error.
|
||||
//
|
||||
// If zero arguments were passed or if all items are nil, a nil error is
|
||||
// returned.
|
||||
//
|
||||
// Combine(nil, nil) // == nil
|
||||
//
|
||||
// If only a single error was passed, it is returned as-is.
|
||||
//
|
||||
// Combine(err) // == err
|
||||
//
|
||||
// Combine skips over nil arguments so this function may be used to combine
|
||||
// together errors from operations that fail independently of each other.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// pipe.Close(),
|
||||
// )
|
||||
//
|
||||
// If any of the passed errors is a multierr error, it will be flattened along
|
||||
// with the other errors.
|
||||
//
|
||||
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||
// // is the same as
|
||||
// multierr.Combine(err1, err2, err3)
|
||||
//
|
||||
// The returned error formats into a readable multi-line error message if
|
||||
// formatted with %+v.
|
||||
//
|
||||
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||
func Combine(errors ...error) error {
|
||||
return fromSlice(errors)
|
||||
}
|
||||
|
||||
// Append appends the given errors together. Either value may be nil.
|
||||
//
|
||||
// This function is a specialization of Combine for the common case where
|
||||
// there are only two errors.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The following pattern may also be used to record failure of deferred
|
||||
// operations without losing information about the original error.
|
||||
//
|
||||
// func doSomething(..) (err error) {
|
||||
// f := acquireResource()
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, f.Close())
|
||||
// }()
|
||||
func Append(left error, right error) error {
|
||||
switch {
|
||||
case left == nil:
|
||||
return right
|
||||
case right == nil:
|
||||
return left
|
||||
}
|
||||
|
||||
if _, ok := right.(*multiError); !ok {
|
||||
if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
|
||||
// Common case where the error on the left is constantly being
|
||||
// appended to.
|
||||
errs := append(l.errors, right)
|
||||
return &multiError{errors: errs}
|
||||
} else if !ok {
|
||||
// Both errors are single errors.
|
||||
return &multiError{errors: []error{left, right}}
|
||||
}
|
||||
}
|
||||
|
||||
// Either right or both, left and right, are multiErrors. Rely on usual
|
||||
// expensive logic.
|
||||
errors := [2]error{left, right}
|
||||
return fromSlice(errors[0:])
|
||||
}
|
||||
|
||||
// AppendInto appends an error into the destination of an error pointer and
|
||||
// returns whether the error being appended was non-nil.
|
||||
//
|
||||
// var err error
|
||||
// multierr.AppendInto(&err, r.Close())
|
||||
// multierr.AppendInto(&err, w.Close())
|
||||
//
|
||||
// The above is equivalent to,
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
//
|
||||
// As AppendInto reports whether the provided error was non-nil, it may be
|
||||
// used to build a multierr error in a loop more ergonomically. For example:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if multierr.AppendInto(&err, parse(line, &item)) {
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
//
|
||||
// Compare this with a verison that relies solely on Append:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if parseErr := parse(line, &item); parseErr != nil {
|
||||
// err = multierr.Append(err, parseErr)
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
func AppendInto(into *error, err error) (errored bool) {
|
||||
if into == nil {
|
||||
// We panic if 'into' is nil. This is not documented above
|
||||
// because suggesting that the pointer must be non-nil may
|
||||
// confuse users into thinking that the error that it points
|
||||
// to must be non-nil.
|
||||
panic("misuse of multierr.AppendInto: into pointer must not be nil")
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
*into = Append(*into, err)
|
||||
return true
|
||||
}
|
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
Normal file
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package: go.uber.org/multierr
|
||||
import:
|
||||
- package: go.uber.org/atomic
|
||||
version: ^1
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
52
vendor/go.uber.org/multierr/go113.go
generated
vendored
Normal file
52
vendor/go.uber.org/multierr/go113.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// +build go1.13
|
||||
|
||||
package multierr
|
||||
|
||||
import "errors"
|
||||
|
||||
// As attempts to find the first error in the error list that matches the type
|
||||
// of the value that target points to.
|
||||
//
|
||||
// This function allows errors.As to traverse the values stored on the
|
||||
// multierr error.
|
||||
func (merr *multiError) As(target interface{}) bool {
|
||||
for _, err := range merr.Errors() {
|
||||
if errors.As(err, target) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Is attempts to match the provided error against errors in the error list.
|
||||
//
|
||||
// This function allows errors.Is to traverse the values stored on the
|
||||
// multierr error.
|
||||
func (merr *multiError) Is(target error) bool {
|
||||
for _, err := range merr.Errors() {
|
||||
if errors.Is(err, target) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
17
vendor/go.uber.org/zap/.codecov.yml
generated
vendored
Normal file
17
vendor/go.uber.org/zap/.codecov.yml
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 95% # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
ignore:
|
||||
- internal/readme/readme.go
|
||||
|
32
vendor/go.uber.org/zap/.gitignore
generated
vendored
Normal file
32
vendor/go.uber.org/zap/.gitignore
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
vendor
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
*.pprof
|
||||
*.out
|
||||
*.log
|
||||
|
||||
/bin
|
||||
cover.out
|
||||
cover.html
|
109
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
Normal file
109
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
Blazing fast, structured, leveled logging in Go.
|
||||
|
||||
## Installation
|
||||
|
||||
`go get -u go.uber.org/zap`
|
||||
|
||||
Note that zap only supports the two most recent minor versions of Go.
|
||||
|
||||
## Quick Start
|
||||
|
||||
In contexts where performance is nice, but not critical, use the
|
||||
`SugaredLogger`. It's 4-10x faster than other structured logging
|
||||
packages and includes both structured and `printf`-style APIs.
|
||||
|
||||
```go
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync() // flushes buffer, if any
|
||||
sugar := logger.Sugar()
|
||||
sugar.Infow("failed to fetch URL",
|
||||
// Structured context as loosely typed key-value pairs.
|
||||
"url", url,
|
||||
"attempt", 3,
|
||||
"backoff", time.Second,
|
||||
)
|
||||
sugar.Infof("Failed to fetch URL: %s", url)
|
||||
```
|
||||
|
||||
When performance and type safety are critical, use the `Logger`. It's even
|
||||
faster than the `SugaredLogger` and allocates far less, but it only supports
|
||||
structured logging.
|
||||
|
||||
```go
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
logger.Info("failed to fetch URL",
|
||||
// Structured context as strongly typed Field values.
|
||||
zap.String("url", url),
|
||||
zap.Int("attempt", 3),
|
||||
zap.Duration("backoff", time.Second),
|
||||
)
|
||||
```
|
||||
|
||||
See the [documentation][doc] and [FAQ](FAQ.md) for more details.
|
||||
|
||||
## Performance
|
||||
|
||||
For applications that log in the hot path, reflection-based serialization and
|
||||
string formatting are prohibitively expensive — they're CPU-intensive
|
||||
and make many small allocations. Put differently, using `encoding/json` and
|
||||
`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
|
||||
|
||||
Zap takes a different approach. It includes a reflection-free, zero-allocation
|
||||
JSON encoder, and the base `Logger` strives to avoid serialization overhead
|
||||
and allocations wherever possible. By building the high-level `SugaredLogger`
|
||||
on that foundation, zap lets users *choose* when they need to count every
|
||||
allocation and when they'd prefer a more familiar, loosely typed API.
|
||||
|
||||
As measured by its own [benchmarking suite][], not only is zap more performant
|
||||
than comparable structured logging packages — it's also faster than the
|
||||
standard library. Like all benchmarks, take these with a grain of salt.<sup
|
||||
id="anchor-versions">[1](#footnote-versions)</sup>
|
||||
|
||||
Log a message and 10 fields:
|
||||
|
||||
{{.BenchmarkAddingFields}}
|
||||
|
||||
Log a message with a logger that already has 10 fields of context:
|
||||
|
||||
{{.BenchmarkAccumulatedContext}}
|
||||
|
||||
Log a static string, without any context or `printf`-style templating:
|
||||
|
||||
{{.BenchmarkWithoutFields}}
|
||||
|
||||
## Development Status: Stable
|
||||
|
||||
All APIs are finalized, and no breaking changes will be made in the 1.x series
|
||||
of releases. Users of semver-aware dependency management systems should pin
|
||||
zap to `^1`.
|
||||
|
||||
## Contributing
|
||||
|
||||
We encourage and support an active, healthy community of contributors —
|
||||
including you! Details are in the [contribution guide](CONTRIBUTING.md) and
|
||||
the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on
|
||||
issues and pull requests, but you can also report any negative conduct to
|
||||
oss-conduct@uber.com. That email list is a private, safe space; even the zap
|
||||
maintainers don't have access, so don't hesitate to hold us to a high
|
||||
standard.
|
||||
|
||||
<hr>
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
|
||||
benchmarking against slightly older versions of other packages. Versions are
|
||||
pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)
|
||||
|
||||
[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap
|
||||
[doc]: https://pkg.go.dev/go.uber.org/zap
|
||||
[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg
|
||||
[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml
|
||||
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/zap
|
||||
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
|
||||
[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod
|
||||
|
564
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
Normal file
564
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,564 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 1.21.0 (7 Feb 2022)
|
||||
|
||||
Enhancements:
|
||||
* [#1047][]: Add `zapcore.ParseLevel` to parse a `Level` from a string.
|
||||
* [#1048][]: Add `zap.ParseAtomicLevel` to parse an `AtomicLevel` from a
|
||||
string.
|
||||
|
||||
Bugfixes:
|
||||
* [#1058][]: Fix panic in JSON encoder when `EncodeLevel` is unset.
|
||||
|
||||
Other changes:
|
||||
* [#1052][]: Improve encoding performance when the `AddCaller` and
|
||||
`AddStacktrace` options are used together.
|
||||
|
||||
[#1047]: https://github.com/uber-go/zap/pull/1047
|
||||
[#1048]: https://github.com/uber-go/zap/pull/1048
|
||||
[#1052]: https://github.com/uber-go/zap/pull/1052
|
||||
[#1058]: https://github.com/uber-go/zap/pull/1058
|
||||
|
||||
Thanks to @aerosol and @Techassi for their contributions to this release.
|
||||
|
||||
## 1.20.0 (4 Jan 2022)
|
||||
|
||||
Enhancements:
|
||||
* [#989][]: Add `EncoderConfig.SkipLineEnding` flag to disable adding newline
|
||||
characters between log statements.
|
||||
* [#1039][]: Add `EncoderConfig.NewReflectedEncoder` field to customize JSON
|
||||
encoding of reflected log fields.
|
||||
|
||||
Bugfixes:
|
||||
* [#1011][]: Fix inaccurate precision when encoding complex64 as JSON.
|
||||
* [#554][], [#1017][]: Close JSON namespaces opened in `MarshalLogObject`
|
||||
methods when the methods return.
|
||||
* [#1033][]: Avoid panicking in Sampler core if `thereafter` is zero.
|
||||
|
||||
Other changes:
|
||||
* [#1028][]: Drop support for Go < 1.15.
|
||||
|
||||
[#554]: https://github.com/uber-go/zap/pull/554
|
||||
[#989]: https://github.com/uber-go/zap/pull/989
|
||||
[#1011]: https://github.com/uber-go/zap/pull/1011
|
||||
[#1017]: https://github.com/uber-go/zap/pull/1017
|
||||
[#1028]: https://github.com/uber-go/zap/pull/1028
|
||||
[#1033]: https://github.com/uber-go/zap/pull/1033
|
||||
[#1039]: https://github.com/uber-go/zap/pull/1039
|
||||
|
||||
Thanks to @psrajat, @lruggieri, @sammyrnycreal for their contributions to this release.
|
||||
|
||||
## 1.19.1 (8 Sep 2021)
|
||||
|
||||
Bugfixes:
|
||||
* [#1001][]: JSON: Fix complex number encoding with negative imaginary part. Thanks to @hemantjadon.
|
||||
* [#1003][]: JSON: Fix inaccurate precision when encoding float32.
|
||||
|
||||
[#1001]: https://github.com/uber-go/zap/pull/1001
|
||||
[#1003]: https://github.com/uber-go/zap/pull/1003
|
||||
|
||||
## 1.19.0 (9 Aug 2021)
|
||||
|
||||
Enhancements:
|
||||
* [#975][]: Avoid panicking in Sampler core if the level is out of bounds.
|
||||
* [#984][]: Reduce the size of BufferedWriteSyncer by aligning the fields
|
||||
better.
|
||||
|
||||
[#975]: https://github.com/uber-go/zap/pull/975
|
||||
[#984]: https://github.com/uber-go/zap/pull/984
|
||||
|
||||
Thanks to @lancoLiu and @thockin for their contributions to this release.
|
||||
|
||||
## 1.18.1 (28 Jun 2021)
|
||||
|
||||
Bugfixes:
|
||||
* [#974][]: Fix nil dereference in logger constructed by `zap.NewNop`.
|
||||
|
||||
[#974]: https://github.com/uber-go/zap/pull/974
|
||||
|
||||
## 1.18.0 (28 Jun 2021)
|
||||
|
||||
Enhancements:
|
||||
* [#961][]: Add `zapcore.BufferedWriteSyncer`, a new `WriteSyncer` that buffers
|
||||
messages in-memory and flushes them periodically.
|
||||
* [#971][]: Add `zapio.Writer` to use a Zap logger as an `io.Writer`.
|
||||
* [#897][]: Add `zap.WithClock` option to control the source of time via the
|
||||
new `zapcore.Clock` interface.
|
||||
* [#949][]: Avoid panicking in `zap.SugaredLogger` when arguments of `*w`
|
||||
methods don't match expectations.
|
||||
* [#943][]: Add support for filtering by level or arbitrary matcher function to
|
||||
`zaptest/observer`.
|
||||
* [#691][]: Comply with `io.StringWriter` and `io.ByteWriter` in Zap's
|
||||
`buffer.Buffer`.
|
||||
|
||||
Thanks to @atrn0, @ernado, @heyanfu, @hnlq715, @zchee
|
||||
for their contributions to this release.
|
||||
|
||||
[#691]: https://github.com/uber-go/zap/pull/691
|
||||
[#897]: https://github.com/uber-go/zap/pull/897
|
||||
[#943]: https://github.com/uber-go/zap/pull/943
|
||||
[#949]: https://github.com/uber-go/zap/pull/949
|
||||
[#961]: https://github.com/uber-go/zap/pull/961
|
||||
[#971]: https://github.com/uber-go/zap/pull/971
|
||||
|
||||
## 1.17.0 (25 May 2021)
|
||||
|
||||
Bugfixes:
|
||||
* [#867][]: Encode `<nil>` for nil `error` instead of a panic.
|
||||
* [#931][], [#936][]: Update minimum version constraints to address
|
||||
vulnerabilities in dependencies.
|
||||
|
||||
Enhancements:
|
||||
* [#865][]: Improve alignment of fields of the Logger struct, reducing its
|
||||
size from 96 to 80 bytes.
|
||||
* [#881][]: Support `grpclog.LoggerV2` in zapgrpc.
|
||||
* [#903][]: Support URL-encoded POST requests to the AtomicLevel HTTP handler
|
||||
with the `application/x-www-form-urlencoded` content type.
|
||||
* [#912][]: Support multi-field encoding with `zap.Inline`.
|
||||
* [#913][]: Speed up SugaredLogger for calls with a single string.
|
||||
* [#928][]: Add support for filtering by field name to `zaptest/observer`.
|
||||
|
||||
Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release.
|
||||
|
||||
## 1.16.0 (1 Sep 2020)
|
||||
|
||||
Bugfixes:
|
||||
* [#828][]: Fix missing newline in IncreaseLevel error messages.
|
||||
* [#835][]: Fix panic in JSON encoder when encoding times or durations
|
||||
without specifying a time or duration encoder.
|
||||
* [#843][]: Honor CallerSkip when taking stack traces.
|
||||
* [#862][]: Fix the default file permissions to use `0666` and rely on the umask instead.
|
||||
* [#854][]: Encode `<nil>` for nil `Stringer` instead of a panic error log.
|
||||
|
||||
Enhancements:
|
||||
* [#629][]: Added `zapcore.TimeEncoderOfLayout` to easily create time encoders
|
||||
for custom layouts.
|
||||
* [#697][]: Added support for a configurable delimiter in the console encoder.
|
||||
* [#852][]: Optimize console encoder by pooling the underlying JSON encoder.
|
||||
* [#844][]: Add ability to include the calling function as part of logs.
|
||||
* [#843][]: Add `StackSkip` for including truncated stacks as a field.
|
||||
* [#861][]: Add options to customize Fatal behaviour for better testability.
|
||||
|
||||
Thanks to @SteelPhase, @tmshn, @lixingwang, @wyxloading, @moul, @segevfiner, @andy-retailnext and @jcorbin for their contributions to this release.
|
||||
|
||||
## 1.15.0 (23 Apr 2020)
|
||||
|
||||
Bugfixes:
|
||||
* [#804][]: Fix handling of `Time` values out of `UnixNano` range.
|
||||
* [#812][]: Fix `IncreaseLevel` being reset after a call to `With`.
|
||||
|
||||
Enhancements:
|
||||
* [#806][]: Add `WithCaller` option to supersede the `AddCaller` option. This
|
||||
allows disabling annotation of log entries with caller information if
|
||||
previously enabled with `AddCaller`.
|
||||
* [#813][]: Deprecate `NewSampler` constructor in favor of
|
||||
`NewSamplerWithOptions` which supports a `SamplerHook` option. This option
|
||||
adds support for monitoring sampling decisions through a hook.
|
||||
|
||||
Thanks to @danielbprice for their contributions to this release.
|
||||
|
||||
## 1.14.1 (14 Mar 2020)
|
||||
|
||||
Bugfixes:
|
||||
* [#791][]: Fix panic on attempting to build a logger with an invalid Config.
|
||||
* [#795][]: Vendoring Zap with `go mod vendor` no longer includes Zap's
|
||||
development-time dependencies.
|
||||
* [#799][]: Fix issue introduced in 1.14.0 that caused invalid JSON output to
|
||||
be generated for arrays of `time.Time` objects when using string-based time
|
||||
formats.
|
||||
|
||||
Thanks to @YashishDua for their contributions to this release.
|
||||
|
||||
## 1.14.0 (20 Feb 2020)
|
||||
|
||||
Enhancements:
|
||||
* [#771][]: Optimize calls for disabled log levels.
|
||||
* [#773][]: Add millisecond duration encoder.
|
||||
* [#775][]: Add option to increase the level of a logger.
|
||||
* [#786][]: Optimize time formatters using `Time.AppendFormat` where possible.
|
||||
|
||||
Thanks to @caibirdme for their contributions to this release.
|
||||
|
||||
## 1.13.0 (13 Nov 2019)
|
||||
|
||||
Enhancements:
|
||||
* [#758][]: Add `Intp`, `Stringp`, and other similar `*p` field constructors
|
||||
to log pointers to primitives with support for `nil` values.
|
||||
|
||||
Thanks to @jbizzle for their contributions to this release.
|
||||
|
||||
## 1.12.0 (29 Oct 2019)
|
||||
|
||||
Enhancements:
|
||||
* [#751][]: Migrate to Go modules.
|
||||
|
||||
## 1.11.0 (21 Oct 2019)
|
||||
|
||||
Enhancements:
|
||||
* [#725][]: Add `zapcore.OmitKey` to omit keys in an `EncoderConfig`.
|
||||
* [#736][]: Add `RFC3339` and `RFC3339Nano` time encoders.
|
||||
|
||||
Thanks to @juicemia, @uhthomas for their contributions to this release.
|
||||
|
||||
## 1.10.0 (29 Apr 2019)
|
||||
|
||||
Bugfixes:
|
||||
* [#657][]: Fix `MapObjectEncoder.AppendByteString` not adding value as a
|
||||
string.
|
||||
* [#706][]: Fix incorrect call depth to determine caller in Go 1.12.
|
||||
|
||||
Enhancements:
|
||||
* [#610][]: Add `zaptest.WrapOptions` to wrap `zap.Option` for creating test
|
||||
loggers.
|
||||
* [#675][]: Don't panic when encoding a String field.
|
||||
* [#704][]: Disable HTML escaping for JSON objects encoded using the
|
||||
reflect-based encoder.
|
||||
|
||||
Thanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions
|
||||
to this release.
|
||||
|
||||
## v1.9.1 (06 Aug 2018)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#614][]: MapObjectEncoder should not ignore empty slices.
|
||||
|
||||
## v1.9.0 (19 Jul 2018)
|
||||
|
||||
Enhancements:
|
||||
* [#602][]: Reduce number of allocations when logging with reflection.
|
||||
* [#572][], [#606][]: Expose a registry for third-party logging sinks.
|
||||
|
||||
Thanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and
|
||||
@dimroc for their contributions to this release.
|
||||
|
||||
## v1.8.0 (13 Apr 2018)
|
||||
|
||||
Enhancements:
|
||||
* [#508][]: Make log level configurable when redirecting the standard
|
||||
library's logger.
|
||||
* [#518][]: Add a logger that writes to a `*testing.TB`.
|
||||
* [#577][]: Add a top-level alias for `zapcore.Field` to clean up GoDoc.
|
||||
|
||||
Bugfixes:
|
||||
* [#574][]: Add a missing import comment to `go.uber.org/zap/buffer`.
|
||||
|
||||
Thanks to @DiSiqueira and @djui for their contributions to this release.
|
||||
|
||||
## v1.7.1 (25 Sep 2017)
|
||||
|
||||
Bugfixes:
|
||||
* [#504][]: Store strings when using AddByteString with the map encoder.
|
||||
|
||||
## v1.7.0 (21 Sep 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user
|
||||
to specify the level of the logged messages.
|
||||
|
||||
## v1.6.0 (30 Aug 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#491][]: Omit zap stack frames from stacktraces.
|
||||
* [#490][]: Add a `ContextMap` method to observer logs for simpler
|
||||
field validation in tests.
|
||||
|
||||
## v1.5.0 (22 Jul 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`.
|
||||
* [#465][]: Support user-supplied encoders for logger names.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#477][]: Fix a bug that incorrectly truncated deep stacktraces.
|
||||
|
||||
Thanks to @richard-tunein and @pavius for their contributions to this release.
|
||||
|
||||
## v1.4.1 (08 Jun 2017)
|
||||
|
||||
This release fixes two bugs.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#435][]: Support a variety of case conventions when unmarshaling levels.
|
||||
* [#444][]: Fix a panic in the observer.
|
||||
|
||||
## v1.4.0 (12 May 2017)
|
||||
|
||||
This release adds a few small features and is fully backward-compatible.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to
|
||||
override the Unix-style default.
|
||||
* [#425][]: Preserve time zones when logging times.
|
||||
* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a
|
||||
variety of operations a bit simpler.
|
||||
|
||||
## v1.3.0 (25 Apr 2017)
|
||||
|
||||
This release adds an enhancement to zap's testing helpers as well as the
|
||||
ability to marshal an AtomicLevel. It is fully backward-compatible.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#415][]: Add a substring-filtering helper to zap's observer. This is
|
||||
particularly useful when testing the `SugaredLogger`.
|
||||
* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`.
|
||||
|
||||
## v1.2.0 (13 Apr 2017)
|
||||
|
||||
This release adds a gRPC compatibility wrapper. It is fully backward-compatible.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements
|
||||
`grpclog.Logger`.
|
||||
|
||||
## v1.1.0 (31 Mar 2017)
|
||||
|
||||
This release fixes two bugs and adds some enhancements to zap's testing helpers.
|
||||
It is fully backward-compatible.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#385][]: Fix caller path trimming on Windows.
|
||||
* [#396][]: Fix a panic when attempting to use non-existent directories with
|
||||
zap's configuration struct.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#386][]: Add filtering helpers to zaptest's observing logger.
|
||||
|
||||
Thanks to @moitias for contributing to this release.
|
||||
|
||||
## v1.0.0 (14 Mar 2017)
|
||||
|
||||
This is zap's first stable release. All exported APIs are now final, and no
|
||||
further breaking changes will be made in the 1.x release series. Anyone using a
|
||||
semver-aware dependency manager should now pin to `^1`.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without
|
||||
casting from `[]byte` to `string`.
|
||||
* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`,
|
||||
`zap.Logger`, and `zap.SugaredLogger`.
|
||||
* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to
|
||||
clash with other testing helpers.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier
|
||||
for tab-separated console output.
|
||||
* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to
|
||||
work with concurrency-safe `WriteSyncer` implementations.
|
||||
* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux
|
||||
systems.
|
||||
* [#373][]: Report the correct caller from zap's standard library
|
||||
interoperability wrappers.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#348][]: Add a registry allowing third-party encodings to work with zap's
|
||||
built-in `Config`.
|
||||
* [#327][]: Make the representation of logger callers configurable (like times,
|
||||
levels, and durations).
|
||||
* [#376][]: Allow third-party encoders to use their own buffer pools, which
|
||||
removes the last performance advantage that zap's encoders have over plugins.
|
||||
* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple
|
||||
`WriteSyncer`s and lock the result.
|
||||
* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in
|
||||
Go 1.9).
|
||||
* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it
|
||||
easier for particularly punctilious users to unit test their application's
|
||||
logging.
|
||||
|
||||
Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their
|
||||
contributions to this release.
|
||||
|
||||
## v1.0.0-rc.3 (7 Mar 2017)
|
||||
|
||||
This is the third release candidate for zap's stable release. There are no
|
||||
breaking changes.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs
|
||||
rather than `[]uint8`.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#307][]: Users can opt into colored output for log levels.
|
||||
* [#353][]: In addition to hijacking the output of the standard library's
|
||||
package-global logging functions, users can now construct a zap-backed
|
||||
`log.Logger` instance.
|
||||
* [#311][]: Frames from common runtime functions and some of zap's internal
|
||||
machinery are now omitted from stacktraces.
|
||||
|
||||
Thanks to @ansel1 and @suyash for their contributions to this release.
|
||||
|
||||
## v1.0.0-rc.2 (21 Feb 2017)
|
||||
|
||||
This is the second release candidate for zap's stable release. It includes two
|
||||
breaking changes.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* [#316][]: Zap's global loggers are now fully concurrency-safe
|
||||
(previously, users had to ensure that `ReplaceGlobals` was called before the
|
||||
loggers were in use). However, they must now be accessed via the `L()` and
|
||||
`S()` functions. Users can update their projects with
|
||||
|
||||
```
|
||||
gofmt -r "zap.L -> zap.L()" -w .
|
||||
gofmt -r "zap.S -> zap.S()" -w .
|
||||
```
|
||||
* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid
|
||||
JSON and YAML struct tags on all config structs. This release fixes the tags
|
||||
and adds static analysis to prevent similar bugs in the future.
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#321][]: Redirecting the standard library's `log` output now
|
||||
correctly reports the logger's caller.
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#325][] and [#333][]: Zap now transparently supports non-standard, rich
|
||||
errors like those produced by `github.com/pkg/errors`.
|
||||
* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is
|
||||
now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) ->
|
||||
zap.NewNop()' -w .`.
|
||||
* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a
|
||||
more informative error.
|
||||
|
||||
Thanks to @skipor and @chapsuk for their contributions to this release.
|
||||
|
||||
## v1.0.0-rc.1 (14 Feb 2017)
|
||||
|
||||
This is the first release candidate for zap's stable release. There are multiple
|
||||
breaking changes and improvements from the pre-release version. Most notably:
|
||||
|
||||
* **Zap's import path is now "go.uber.org/zap"** — all users will
|
||||
need to update their code.
|
||||
* User-facing types and functions remain in the `zap` package. Code relevant
|
||||
largely to extension authors is now in the `zapcore` package.
|
||||
* The `zapcore.Core` type makes it easy for third-party packages to use zap's
|
||||
internals but provide a different user-facing API.
|
||||
* `Logger` is now a concrete type instead of an interface.
|
||||
* A less verbose (though slower) logging API is included by default.
|
||||
* Package-global loggers `L` and `S` are included.
|
||||
* A human-friendly console encoder is included.
|
||||
* A declarative config struct allows common logger configurations to be managed
|
||||
as configuration instead of code.
|
||||
* Sampling is more accurate, and doesn't depend on the standard library's shared
|
||||
timer heap.
|
||||
|
||||
## v0.1.0-beta.1 (6 Feb 2017)
|
||||
|
||||
This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and
|
||||
upgrade at their leisure. Since this is the first tagged release, there are no
|
||||
backward compatibility concerns and all functionality is new.
|
||||
|
||||
Early zap adopters should pin to the 0.1.x minor version until they're ready to
|
||||
upgrade to the upcoming stable release.
|
||||
|
||||
[#316]: https://github.com/uber-go/zap/pull/316
|
||||
[#309]: https://github.com/uber-go/zap/pull/309
|
||||
[#317]: https://github.com/uber-go/zap/pull/317
|
||||
[#321]: https://github.com/uber-go/zap/pull/321
|
||||
[#325]: https://github.com/uber-go/zap/pull/325
|
||||
[#333]: https://github.com/uber-go/zap/pull/333
|
||||
[#326]: https://github.com/uber-go/zap/pull/326
|
||||
[#300]: https://github.com/uber-go/zap/pull/300
|
||||
[#339]: https://github.com/uber-go/zap/pull/339
|
||||
[#307]: https://github.com/uber-go/zap/pull/307
|
||||
[#353]: https://github.com/uber-go/zap/pull/353
|
||||
[#311]: https://github.com/uber-go/zap/pull/311
|
||||
[#366]: https://github.com/uber-go/zap/pull/366
|
||||
[#364]: https://github.com/uber-go/zap/pull/364
|
||||
[#371]: https://github.com/uber-go/zap/pull/371
|
||||
[#362]: https://github.com/uber-go/zap/pull/362
|
||||
[#369]: https://github.com/uber-go/zap/pull/369
|
||||
[#347]: https://github.com/uber-go/zap/pull/347
|
||||
[#373]: https://github.com/uber-go/zap/pull/373
|
||||
[#348]: https://github.com/uber-go/zap/pull/348
|
||||
[#327]: https://github.com/uber-go/zap/pull/327
|
||||
[#376]: https://github.com/uber-go/zap/pull/376
|
||||
[#346]: https://github.com/uber-go/zap/pull/346
|
||||
[#365]: https://github.com/uber-go/zap/pull/365
|
||||
[#372]: https://github.com/uber-go/zap/pull/372
|
||||
[#385]: https://github.com/uber-go/zap/pull/385
|
||||
[#396]: https://github.com/uber-go/zap/pull/396
|
||||
[#386]: https://github.com/uber-go/zap/pull/386
|
||||
[#402]: https://github.com/uber-go/zap/pull/402
|
||||
[#415]: https://github.com/uber-go/zap/pull/415
|
||||
[#416]: https://github.com/uber-go/zap/pull/416
|
||||
[#424]: https://github.com/uber-go/zap/pull/424
|
||||
[#425]: https://github.com/uber-go/zap/pull/425
|
||||
[#431]: https://github.com/uber-go/zap/pull/431
|
||||
[#435]: https://github.com/uber-go/zap/pull/435
|
||||
[#444]: https://github.com/uber-go/zap/pull/444
|
||||
[#477]: https://github.com/uber-go/zap/pull/477
|
||||
[#465]: https://github.com/uber-go/zap/pull/465
|
||||
[#460]: https://github.com/uber-go/zap/pull/460
|
||||
[#470]: https://github.com/uber-go/zap/pull/470
|
||||
[#487]: https://github.com/uber-go/zap/pull/487
|
||||
[#490]: https://github.com/uber-go/zap/pull/490
|
||||
[#491]: https://github.com/uber-go/zap/pull/491
|
||||
[#504]: https://github.com/uber-go/zap/pull/504
|
||||
[#508]: https://github.com/uber-go/zap/pull/508
|
||||
[#518]: https://github.com/uber-go/zap/pull/518
|
||||
[#577]: https://github.com/uber-go/zap/pull/577
|
||||
[#574]: https://github.com/uber-go/zap/pull/574
|
||||
[#602]: https://github.com/uber-go/zap/pull/602
|
||||
[#572]: https://github.com/uber-go/zap/pull/572
|
||||
[#606]: https://github.com/uber-go/zap/pull/606
|
||||
[#614]: https://github.com/uber-go/zap/pull/614
|
||||
[#657]: https://github.com/uber-go/zap/pull/657
|
||||
[#706]: https://github.com/uber-go/zap/pull/706
|
||||
[#610]: https://github.com/uber-go/zap/pull/610
|
||||
[#675]: https://github.com/uber-go/zap/pull/675
|
||||
[#704]: https://github.com/uber-go/zap/pull/704
|
||||
[#725]: https://github.com/uber-go/zap/pull/725
|
||||
[#736]: https://github.com/uber-go/zap/pull/736
|
||||
[#751]: https://github.com/uber-go/zap/pull/751
|
||||
[#758]: https://github.com/uber-go/zap/pull/758
|
||||
[#771]: https://github.com/uber-go/zap/pull/771
|
||||
[#773]: https://github.com/uber-go/zap/pull/773
|
||||
[#775]: https://github.com/uber-go/zap/pull/775
|
||||
[#786]: https://github.com/uber-go/zap/pull/786
|
||||
[#791]: https://github.com/uber-go/zap/pull/791
|
||||
[#795]: https://github.com/uber-go/zap/pull/795
|
||||
[#799]: https://github.com/uber-go/zap/pull/799
|
||||
[#804]: https://github.com/uber-go/zap/pull/804
|
||||
[#812]: https://github.com/uber-go/zap/pull/812
|
||||
[#806]: https://github.com/uber-go/zap/pull/806
|
||||
[#813]: https://github.com/uber-go/zap/pull/813
|
||||
[#629]: https://github.com/uber-go/zap/pull/629
|
||||
[#697]: https://github.com/uber-go/zap/pull/697
|
||||
[#828]: https://github.com/uber-go/zap/pull/828
|
||||
[#835]: https://github.com/uber-go/zap/pull/835
|
||||
[#843]: https://github.com/uber-go/zap/pull/843
|
||||
[#844]: https://github.com/uber-go/zap/pull/844
|
||||
[#852]: https://github.com/uber-go/zap/pull/852
|
||||
[#854]: https://github.com/uber-go/zap/pull/854
|
||||
[#861]: https://github.com/uber-go/zap/pull/861
|
||||
[#862]: https://github.com/uber-go/zap/pull/862
|
||||
[#865]: https://github.com/uber-go/zap/pull/865
|
||||
[#867]: https://github.com/uber-go/zap/pull/867
|
||||
[#881]: https://github.com/uber-go/zap/pull/881
|
||||
[#903]: https://github.com/uber-go/zap/pull/903
|
||||
[#912]: https://github.com/uber-go/zap/pull/912
|
||||
[#913]: https://github.com/uber-go/zap/pull/913
|
||||
[#928]: https://github.com/uber-go/zap/pull/928
|
||||
[#931]: https://github.com/uber-go/zap/pull/931
|
||||
[#936]: https://github.com/uber-go/zap/pull/936
|
75
vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
generated
vendored
Normal file
75
vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age,
|
||||
body size, disability, ethnicity, gender identity and expression, level of
|
||||
experience, nationality, personal appearance, race, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an
|
||||
appointed representative at an online or offline event. Representation of a
|
||||
project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at oss-conduct@uber.com. The project
|
||||
team will review and investigate all complaints, and will respond in a way
|
||||
that it deems appropriate to the circumstances. The project team is obligated
|
||||
to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
[http://contributor-covenant.org/version/1/4][version].
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
75
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
Normal file
75
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
# Contributing
|
||||
|
||||
We'd love your help making zap the very best structured logging library in Go!
|
||||
|
||||
If you'd like to add new exported APIs, please [open an issue][open-issue]
|
||||
describing your proposal — discussing API changes ahead of time makes
|
||||
pull request review much smoother. In your issue, pull request, and any other
|
||||
communications, please remember to treat your fellow contributors with
|
||||
respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously.
|
||||
|
||||
Note that you'll need to sign [Uber's Contributor License Agreement][cla]
|
||||
before we can accept any of your contributions. If necessary, a bot will remind
|
||||
you to accept the CLA when you open your pull request.
|
||||
|
||||
## Setup
|
||||
|
||||
[Fork][fork], then clone the repository:
|
||||
|
||||
```
|
||||
mkdir -p $GOPATH/src/go.uber.org
|
||||
cd $GOPATH/src/go.uber.org
|
||||
git clone git@github.com:your_github_username/zap.git
|
||||
cd zap
|
||||
git remote add upstream https://github.com/uber-go/zap.git
|
||||
git fetch upstream
|
||||
```
|
||||
|
||||
Make sure that the tests and the linters pass:
|
||||
|
||||
```
|
||||
make test
|
||||
make lint
|
||||
```
|
||||
|
||||
If you're not using the minor version of Go specified in the Makefile's
|
||||
`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is
|
||||
fine, but it means that you'll only discover lint failures after you open your
|
||||
pull request.
|
||||
|
||||
## Making Changes
|
||||
|
||||
Start by creating a new branch for your changes:
|
||||
|
||||
```
|
||||
cd $GOPATH/src/go.uber.org/zap
|
||||
git checkout master
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
git checkout -b cool_new_feature
|
||||
```
|
||||
|
||||
Make your changes, then ensure that `make lint` and `make test` still pass. If
|
||||
you're satisfied with your changes, push them to your fork.
|
||||
|
||||
```
|
||||
git push origin cool_new_feature
|
||||
```
|
||||
|
||||
Then use the GitHub UI to open a pull request.
|
||||
|
||||
At this point, you're waiting on us to review your changes. We *try* to respond
|
||||
to issues and pull requests within a few business days, and we may suggest some
|
||||
improvements or alternatives. Once your changes are approved, one of the
|
||||
project maintainers will merge them.
|
||||
|
||||
We're much more likely to approve your changes if you:
|
||||
|
||||
* Add tests for new functionality.
|
||||
* Write a [good commit message][commit-message].
|
||||
* Maintain backward compatibility.
|
||||
|
||||
[fork]: https://github.com/uber-go/zap/fork
|
||||
[open-issue]: https://github.com/uber-go/zap/issues/new
|
||||
[cla]: https://cla-assistant.io/uber-go/zap
|
||||
[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
164
vendor/go.uber.org/zap/FAQ.md
generated
vendored
Normal file
164
vendor/go.uber.org/zap/FAQ.md
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
## Design
|
||||
|
||||
### Why spend so much effort on logger performance?
|
||||
|
||||
Of course, most applications won't notice the impact of a slow logger: they
|
||||
already take tens or hundreds of milliseconds for each operation, so an extra
|
||||
millisecond doesn't matter.
|
||||
|
||||
On the other hand, why *not* make structured logging fast? The `SugaredLogger`
|
||||
isn't any harder to use than other logging packages, and the `Logger` makes
|
||||
structured logging possible in performance-sensitive contexts. Across a fleet
|
||||
of Go microservices, making each application even slightly more efficient adds
|
||||
up quickly.
|
||||
|
||||
### Why aren't `Logger` and `SugaredLogger` interfaces?
|
||||
|
||||
Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and
|
||||
`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points
|
||||
out][go-proverbs], "The bigger the interface, the weaker the abstraction."
|
||||
Interfaces are also rigid — *any* change requires releasing a new major
|
||||
version, since it breaks all third-party implementations.
|
||||
|
||||
Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much
|
||||
abstraction, and it lets us add methods without introducing breaking changes.
|
||||
Your applications should define and depend upon an interface that includes
|
||||
just the methods you use.
|
||||
|
||||
### Why are some of my logs missing?
|
||||
|
||||
Logs are dropped intentionally by zap when sampling is enabled. The production
|
||||
configuration (as returned by `NewProductionConfig()` enables sampling which will
|
||||
cause repeated logs within a second to be sampled. See more details on why sampling
|
||||
is enabled in [Why sample application logs](https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs).
|
||||
|
||||
### Why sample application logs?
|
||||
|
||||
Applications often experience runs of errors, either because of a bug or
|
||||
because of a misbehaving user. Logging errors is usually a good idea, but it
|
||||
can easily make this bad situation worse: not only is your application coping
|
||||
with a flood of errors, it's also spending extra CPU cycles and I/O logging
|
||||
those errors. Since writes are typically serialized, logging limits throughput
|
||||
when you need it most.
|
||||
|
||||
Sampling fixes this problem by dropping repetitive log entries. Under normal
|
||||
conditions, your application writes out every entry. When similar entries are
|
||||
logged hundreds or thousands of times each second, though, zap begins dropping
|
||||
duplicates to preserve throughput.
|
||||
|
||||
### Why do the structured logging APIs take a message in addition to fields?
|
||||
|
||||
Subjectively, we find it helpful to accompany structured context with a brief
|
||||
description. This isn't critical during development, but it makes debugging
|
||||
and operating unfamiliar systems much easier.
|
||||
|
||||
More concretely, zap's sampling algorithm uses the message to identify
|
||||
duplicate entries. In our experience, this is a practical middle ground
|
||||
between random sampling (which often drops the exact entry that you need while
|
||||
debugging) and hashing the complete entry (which is prohibitively expensive).
|
||||
|
||||
### Why include package-global loggers?
|
||||
|
||||
Since so many other logging packages include a global logger, many
|
||||
applications aren't designed to accept loggers as explicit parameters.
|
||||
Changing function signatures is often a breaking change, so zap includes
|
||||
global loggers to simplify migration.
|
||||
|
||||
Avoid them where possible.
|
||||
|
||||
### Why include dedicated Panic and Fatal log levels?
|
||||
|
||||
In general, application code should handle errors gracefully instead of using
|
||||
`panic` or `os.Exit`. However, every rule has exceptions, and it's common to
|
||||
crash when an error is truly unrecoverable. To avoid losing any information
|
||||
— especially the reason for the crash — the logger must flush any
|
||||
buffered entries before the process exits.
|
||||
|
||||
Zap makes this easy by offering `Panic` and `Fatal` logging methods that
|
||||
automatically flush before exiting. Of course, this doesn't guarantee that
|
||||
logs will never be lost, but it eliminates a common error.
|
||||
|
||||
See the discussion in uber-go/zap#207 for more details.
|
||||
|
||||
### What's `DPanic`?
|
||||
|
||||
`DPanic` stands for "panic in development." In development, it logs at
|
||||
`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to
|
||||
catch errors that are theoretically possible, but shouldn't actually happen,
|
||||
*without* crashing in production.
|
||||
|
||||
If you've ever written code like this, you need `DPanic`:
|
||||
|
||||
```go
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("shouldn't ever get here: %v", err))
|
||||
}
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### What does the error `expects import "go.uber.org/zap"` mean?
|
||||
|
||||
Either zap was installed incorrectly or you're referencing the wrong package
|
||||
name in your code.
|
||||
|
||||
Zap's source code happens to be hosted on GitHub, but the [import
|
||||
path][import-path] is `go.uber.org/zap`. This gives us, the project
|
||||
maintainers, the freedom to move the source code if necessary. However, it
|
||||
means that you need to take a little care when installing and using the
|
||||
package.
|
||||
|
||||
If you follow two simple rules, everything should work: install zap with `go
|
||||
get -u go.uber.org/zap`, and always import it in your code with `import
|
||||
"go.uber.org/zap"`. Your code shouldn't contain *any* references to
|
||||
`github.com/uber-go/zap`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Does zap support log rotation?
|
||||
|
||||
Zap doesn't natively support rotating log files, since we prefer to leave this
|
||||
to an external program like `logrotate`.
|
||||
|
||||
However, it's easy to integrate a log rotation package like
|
||||
[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`.
|
||||
|
||||
```go
|
||||
// lumberjack.Logger is already safe for concurrent use, so we don't need to
|
||||
// lock it.
|
||||
w := zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: "/var/log/myapp/foo.log",
|
||||
MaxSize: 500, // megabytes
|
||||
MaxBackups: 3,
|
||||
MaxAge: 28, // days
|
||||
})
|
||||
core := zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
|
||||
w,
|
||||
zap.InfoLevel,
|
||||
)
|
||||
logger := zap.New(core)
|
||||
```
|
||||
|
||||
## Extensions
|
||||
|
||||
We'd love to support every logging need within zap itself, but we're only
|
||||
familiar with a handful of log ingestion systems, flag-parsing packages, and
|
||||
the like. Rather than merging code that we can't effectively debug and
|
||||
support, we'd rather grow an ecosystem of zap extensions.
|
||||
|
||||
We're aware of the following extensions, but haven't used them ourselves:
|
||||
|
||||
| Package | Integration |
|
||||
| --- | --- |
|
||||
| `github.com/tchap/zapext` | Sentry, syslog |
|
||||
| `github.com/fgrosse/zaptest` | Ginkgo |
|
||||
| `github.com/blendle/zapdriver` | Stackdriver |
|
||||
| `github.com/moul/zapgorm` | Gorm |
|
||||
| `github.com/moul/zapfilter` | Advanced filtering rules |
|
||||
|
||||
[go-proverbs]: https://go-proverbs.github.io/
|
||||
[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths
|
||||
[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2
|
19
vendor/go.uber.org/zap/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/zap/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016-2017 Uber Technologies, Inc.
|
||||
|
||||
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.
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user