6 Commits

147 changed files with 14214 additions and 126 deletions

View File

@ -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
}

View File

@ -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
View File

@ -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
View File

@ -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
View 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
}

View File

@ -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
View 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
View 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)
}

View 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"`
}

View 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
}

View 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
}

View 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"`
}

View 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
View 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
View 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
View 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...)
}

View File

@ -0,0 +1,8 @@
package values
type ComponentName string
const (
CalendarComponentName ComponentName = "VCALENDAR"
EventComponentName = "VEVENT"
)

View 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
}

View File

@ -0,0 +1,8 @@
package values
type HumanBoolean string
const (
YesHumanBoolean HumanBoolean = "yes"
NoHumanBoolean = "no"
)

View 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
View 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
View 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
View 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
View 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...)
}

View 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
}

View 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
}

View 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
}

View 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
}
}

View 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)
}

View 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)
}

View 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}
}

View 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
}
}

View 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)
}
}

View File

@ -0,0 +1,7 @@
package values
type CalScale string
const (
GregorianCalScale CalScale = "GREGORIAN"
)

View 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
}

View 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
}

View 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)
}

View 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)
}

View 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}
}

View 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"
)

View 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.
)

View 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
}
}

View 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
}

View File

@ -0,0 +1,7 @@
package values
type Method string
const (
PublishMethod Method = "PUBLISH"
)

View 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
}

View 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.
)

View 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
View 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
View 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
View File

@ -0,0 +1,9 @@
package webdav
type Depth string
const (
Depth0 Depth = "0"
Depth1 = "1"
DepthInfinity = "infinity"
)

View 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
}
}

View 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"`
}

View 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"`
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
/vendor
cover.html
cover.out
/bin

23
vendor/go.uber.org/multierr/.travis.yml generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &mdash; 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 &mdash; 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 &mdash;
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
View 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"** &mdash; 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
View 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
View 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 &mdash; 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
View 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 &mdash; *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
&mdash; especially the reason for the crash &mdash; 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
View 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