diff options
Diffstat (limited to 'vendor/github.com')
121 files changed, 3427 insertions, 1750 deletions
diff --git a/vendor/github.com/bwmarrin/discordgo/components.go b/vendor/github.com/bwmarrin/discordgo/components.go index 6ee4e289..a5f86b34 100644 --- a/vendor/github.com/bwmarrin/discordgo/components.go +++ b/vendor/github.com/bwmarrin/discordgo/components.go @@ -10,10 +10,14 @@ type ComponentType uint // MessageComponent types. const ( - ActionsRowComponent ComponentType = 1 - ButtonComponent ComponentType = 2 - SelectMenuComponent ComponentType = 3 - TextInputComponent ComponentType = 4 + ActionsRowComponent ComponentType = 1 + ButtonComponent ComponentType = 2 + SelectMenuComponent ComponentType = 3 + TextInputComponent ComponentType = 4 + UserSelectMenuComponent ComponentType = 5 + RoleSelectMenuComponent ComponentType = 6 + MentionableSelectMenuComponent ComponentType = 7 + ChannelSelectMenuComponent ComponentType = 8 ) // MessageComponent is a base interface for all message components. @@ -41,7 +45,8 @@ func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error { umc.MessageComponent = &ActionsRow{} case ButtonComponent: umc.MessageComponent = &Button{} - case SelectMenuComponent: + case SelectMenuComponent, ChannelSelectMenuComponent, UserSelectMenuComponent, + RoleSelectMenuComponent, MentionableSelectMenuComponent: umc.MessageComponent = &SelectMenu{} case TextInputComponent: umc.MessageComponent = &TextInput{} @@ -169,8 +174,23 @@ type SelectMenuOption struct { Default bool `json:"default"` } +// SelectMenuType represents select menu type. +type SelectMenuType ComponentType + +// SelectMenu types. +const ( + StringSelectMenu = SelectMenuType(SelectMenuComponent) + UserSelectMenu = SelectMenuType(UserSelectMenuComponent) + RoleSelectMenu = SelectMenuType(RoleSelectMenuComponent) + MentionableSelectMenu = SelectMenuType(MentionableSelectMenuComponent) + ChannelSelectMenu = SelectMenuType(ChannelSelectMenuComponent) +) + // SelectMenu represents select menu component. type SelectMenu struct { + // Type of the select menu. + MenuType SelectMenuType `json:"type,omitempty"` + // CustomID is a developer-defined identifier for the select menu. CustomID string `json:"custom_id,omitempty"` // The text which will be shown in the menu if there's no default options or all options was deselected and component was closed. Placeholder string `json:"placeholder"` @@ -179,25 +199,31 @@ type SelectMenu struct { // This value determines the maximal amount of selected items in the menu. // If MaxValues or MinValues are greater than one then the user can select multiple items in the component. MaxValues int `json:"max_values,omitempty"` - Options []SelectMenuOption `json:"options"` + Options []SelectMenuOption `json:"options,omitempty"` Disabled bool `json:"disabled"` + + // NOTE: Can only be used in SelectMenu with Channel menu type. + ChannelTypes []ChannelType `json:"channel_types,omitempty"` } // Type is a method to get the type of a component. -func (SelectMenu) Type() ComponentType { +func (s SelectMenu) Type() ComponentType { + if s.MenuType != 0 { + return ComponentType(s.MenuType) + } return SelectMenuComponent } // MarshalJSON is a method for marshaling SelectMenu to a JSON object. -func (m SelectMenu) MarshalJSON() ([]byte, error) { +func (s SelectMenu) MarshalJSON() ([]byte, error) { type selectMenu SelectMenu return Marshal(struct { selectMenu Type ComponentType `json:"type"` }{ - selectMenu: selectMenu(m), - Type: m.Type(), + selectMenu: selectMenu(s), + Type: s.Type(), }) } diff --git a/vendor/github.com/bwmarrin/discordgo/discord.go b/vendor/github.com/bwmarrin/discordgo/discord.go index fc7b6d7a..0b571764 100644 --- a/vendor/github.com/bwmarrin/discordgo/discord.go +++ b/vendor/github.com/bwmarrin/discordgo/discord.go @@ -22,7 +22,7 @@ import ( ) // VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/) -const VERSION = "0.26.1" +const VERSION = "0.27.0" // New creates a new Discord session with provided token. // If the token is for a bot, it must be prefixed with "Bot " diff --git a/vendor/github.com/bwmarrin/discordgo/endpoints.go b/vendor/github.com/bwmarrin/discordgo/endpoints.go index 375b75b5..a2a05fe3 100644 --- a/vendor/github.com/bwmarrin/discordgo/endpoints.go +++ b/vendor/github.com/bwmarrin/discordgo/endpoints.go @@ -60,11 +60,12 @@ var ( return EndpointCDNBanners + uID + "/" + cID + ".gif" } - EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" } - EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID } - EndpointUserGuildMember = func(uID, gID string) string { return EndpointUserGuild(uID, gID) + "/member" } - EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" } - EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" } + EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" } + EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID } + EndpointUserGuildMember = func(uID, gID string) string { return EndpointUserGuild(uID, gID) + "/member" } + EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" } + EndpointUserApplicationRoleConnection = func(aID string) string { return EndpointUsers + "@me/applications/" + aID + "/role-connection" } + EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" } EndpointGuild = func(gID string) string { return EndpointGuilds + gID } EndpointGuildAutoModeration = func(gID string) string { return EndpointGuild(gID) + "/auto-moderation" } @@ -96,6 +97,7 @@ var ( EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" } EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID } EndpointGuildBanner = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".png" } + EndpointGuildBannerAnimated = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".gif" } EndpointGuildStickers = func(gID string) string { return EndpointGuilds + gID + "/stickers" } EndpointGuildSticker = func(gID, sID string) string { return EndpointGuilds + gID + "/stickers/" + sID } EndpointStageInstance = func(cID string) string { return EndpointStageInstances + "/" + cID } @@ -197,8 +199,9 @@ var ( EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" } EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" } - EndpointApplications = EndpointAPI + "applications" - EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } + EndpointApplications = EndpointAPI + "applications" + EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } + EndpointApplicationRoleConnectionMetadata = func(aID string) string { return EndpointApplication(aID) + "/role-connections/metadata" } EndpointOAuth2 = EndpointAPI + "oauth2/" EndpointOAuth2Applications = EndpointOAuth2 + "applications" diff --git a/vendor/github.com/bwmarrin/discordgo/events.go b/vendor/github.com/bwmarrin/discordgo/events.go index e5d83b9b..6608ab6e 100644 --- a/vendor/github.com/bwmarrin/discordgo/events.go +++ b/vendor/github.com/bwmarrin/discordgo/events.go @@ -36,13 +36,13 @@ type Event struct { // A Ready stores all data for the websocket READY event. type Ready struct { - Version int `json:"v"` - SessionID string `json:"session_id"` - User *User `json:"user"` - Guilds []*Guild `json:"guilds"` - PrivateChannels []*Channel `json:"private_channels"` - - // TODO: Application and Shard + Version int `json:"v"` + SessionID string `json:"session_id"` + User *User `json:"user"` + Shard *[2]int `json:"shard"` + Application *Application `json:"application"` + Guilds []*Guild `json:"guilds"` + PrivateChannels []*Channel `json:"private_channels"` } // ChannelCreate is the data for a ChannelCreate event. @@ -150,6 +150,7 @@ type GuildMemberAdd struct { // GuildMemberUpdate is the data for a GuildMemberUpdate event. type GuildMemberUpdate struct { *Member + BeforeUpdate *Member `json:"-"` } // GuildMemberRemove is the data for a GuildMemberRemove event. diff --git a/vendor/github.com/bwmarrin/discordgo/interactions.go b/vendor/github.com/bwmarrin/discordgo/interactions.go index 61a4e992..627e0c38 100644 --- a/vendor/github.com/bwmarrin/discordgo/interactions.go +++ b/vendor/github.com/bwmarrin/discordgo/interactions.go @@ -42,6 +42,7 @@ type ApplicationCommand struct { DefaultPermission *bool `json:"default_permission,omitempty"` DefaultMemberPermissions *int64 `json:"default_member_permissions,string,omitempty"` DMPermission *bool `json:"dm_permission,omitempty"` + NSFW *bool `json:"nsfw,omitempty"` // NOTE: Chat commands only. Otherwise it mustn't be set. @@ -343,13 +344,22 @@ func (ApplicationCommandInteractionData) Type() InteractionType { // MessageComponentInteractionData contains the data of message component interaction. type MessageComponentInteractionData struct { - CustomID string `json:"custom_id"` - ComponentType ComponentType `json:"component_type"` + CustomID string `json:"custom_id"` + ComponentType ComponentType `json:"component_type"` + Resolved MessageComponentInteractionDataResolved `json:"resolved"` // NOTE: Only filled when ComponentType is SelectMenuComponent (3). Otherwise is nil. Values []string `json:"values"` } +// MessageComponentInteractionDataResolved contains the resolved data of selected option. +type MessageComponentInteractionDataResolved struct { + Users map[string]*User `json:"users"` + Members map[string]*Member `json:"members"` + Roles map[string]*Role `json:"roles"` + Channels map[string]*Channel `json:"channels"` +} + // Type returns the type of interaction data. func (MessageComponentInteractionData) Type() InteractionType { return InteractionMessageComponent @@ -472,7 +482,7 @@ func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID strin return &Role{ID: roleID} } - r, err := s.State.Role(roleID, gID) + r, err := s.State.Role(gID, roleID) if err != nil { roles, err := s.GuildRoles(gID) if err == nil { diff --git a/vendor/github.com/bwmarrin/discordgo/message.go b/vendor/github.com/bwmarrin/discordgo/message.go index 1ba6e445..fec6f879 100644 --- a/vendor/github.com/bwmarrin/discordgo/message.go +++ b/vendor/github.com/bwmarrin/discordgo/message.go @@ -249,6 +249,10 @@ type MessageEdit struct { Embeds []*MessageEmbed `json:"embeds"` AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` Flags MessageFlags `json:"flags,omitempty"` + // Files to append to the message + Files []*File `json:"-"` + // Overwrite existing attachments + Attachments *[]*MessageAttachment `json:"attachments,omitempty"` ID string Channel string @@ -385,8 +389,8 @@ type MessageEmbedAuthor struct { // MessageEmbedField is a part of a MessageEmbed struct. type MessageEmbedField struct { - Name string `json:"name,omitempty"` - Value string `json:"value,omitempty"` + Name string `json:"name"` + Value string `json:"value"` Inline bool `json:"inline,omitempty"` } diff --git a/vendor/github.com/bwmarrin/discordgo/restapi.go b/vendor/github.com/bwmarrin/discordgo/restapi.go index 79af4680..b7d323e7 100644 --- a/vendor/github.com/bwmarrin/discordgo/restapi.go +++ b/vendor/github.com/bwmarrin/discordgo/restapi.go @@ -26,6 +26,8 @@ import ( "strconv" "strings" "time" + + "context" ) // All error constants @@ -92,13 +94,82 @@ func (e RateLimitError) Error() string { return "Rate limit exceeded on " + e.URL + ", retry after " + e.RetryAfter.String() } +// RequestConfig is an HTTP request configuration. +type RequestConfig struct { + Request *http.Request + ShouldRetryOnRateLimit bool + MaxRestRetries int + Client *http.Client +} + +// newRequestConfig returns a new HTTP request configuration based on parameters in Session. +func newRequestConfig(s *Session, req *http.Request) *RequestConfig { + return &RequestConfig{ + ShouldRetryOnRateLimit: s.ShouldRetryOnRateLimit, + MaxRestRetries: s.MaxRestRetries, + Client: s.Client, + Request: req, + } +} + +// RequestOption is a function which mutates request configuration. +// It can be supplied as an argument to any REST method. +type RequestOption func(cfg *RequestConfig) + +// WithClient changes the HTTP client used for the request. +func WithClient(client *http.Client) RequestOption { + return func(cfg *RequestConfig) { + if client != nil { + cfg.Client = client + } + } +} + +// WithRetryOnRatelimit controls whether session will retry the request on rate limit. +func WithRetryOnRatelimit(retry bool) RequestOption { + return func(cfg *RequestConfig) { + cfg.ShouldRetryOnRateLimit = retry + } +} + +// WithRestRetries changes maximum amount of retries if request fails. +func WithRestRetries(max int) RequestOption { + return func(cfg *RequestConfig) { + cfg.MaxRestRetries = max + } +} + +// WithHeader sets a header in the request. +func WithHeader(key, value string) RequestOption { + return func(cfg *RequestConfig) { + cfg.Request.Header.Set(key, value) + } +} + +// WithAuditLogReason changes audit log reason associated with the request. +func WithAuditLogReason(reason string) RequestOption { + return WithHeader("X-Audit-Log-Reason", reason) +} + +// WithLocale changes accepted locale of the request. +func WithLocale(locale Locale) RequestOption { + return WithHeader("X-Discord-Locale", string(locale)) +} + +// WithContext changes context of the request. +func WithContext(ctx context.Context) RequestOption { + return func(cfg *RequestConfig) { + cfg.Request = cfg.Request.WithContext(ctx) + } +} + // Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr -func (s *Session) Request(method, urlStr string, data interface{}) (response []byte, err error) { - return s.RequestWithBucketID(method, urlStr, data, strings.SplitN(urlStr, "?", 2)[0]) +func (s *Session) Request(method, urlStr string, data interface{}, options ...RequestOption) (response []byte, err error) { + return s.RequestWithBucketID(method, urlStr, data, strings.SplitN(urlStr, "?", 2)[0], options...) } // RequestWithBucketID makes a (GET/POST/...) Requests to Discord REST API with JSON data. -func (s *Session) RequestWithBucketID(method, urlStr string, data interface{}, bucketID string) (response []byte, err error) { +func (s *Session) RequestWithBucketID(method, urlStr string, data interface{}, bucketID string, options ...RequestOption) (response []byte, err error) { var body []byte if data != nil { body, err = Marshal(data) @@ -107,21 +178,21 @@ func (s *Session) RequestWithBucketID(method, urlStr string, data interface{}, b } } - return s.request(method, urlStr, "application/json", body, bucketID, 0) + return s.request(method, urlStr, "application/json", body, bucketID, 0, options...) } // request makes a (GET/POST/...) Requests to Discord REST API. // Sequence is the sequence number, if it fails with a 502 it will // retry with sequence+1 until it either succeeds or sequence >= session.MaxRestRetries -func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID string, sequence int) (response []byte, err error) { +func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID string, sequence int, options ...RequestOption) (response []byte, err error) { if bucketID == "" { bucketID = strings.SplitN(urlStr, "?", 2)[0] } - return s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucket(bucketID), sequence) + return s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucket(bucketID), sequence, options...) } // RequestWithLockedBucket makes a request using a bucket that's already been locked -func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b []byte, bucket *Bucket, sequence int) (response []byte, err error) { +func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b []byte, bucket *Bucket, sequence int, options ...RequestOption) (response []byte, err error) { if s.Debug { log.Printf("API REQUEST %8s :: %s\n", method, urlStr) log.Printf("API REQUEST PAYLOAD :: [%s]\n", string(b)) @@ -148,13 +219,18 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b // TODO: Make a configurable static variable. req.Header.Set("User-Agent", s.UserAgent) + cfg := newRequestConfig(s, req) + for _, opt := range options { + opt(cfg) + } + if s.Debug { for k, v := range req.Header { log.Printf("API REQUEST HEADER :: [%s] = %+v\n", k, v) } } - resp, err := s.Client.Do(req) + resp, err := cfg.Client.Do(req) if err != nil { bucket.Release(nil) return @@ -191,10 +267,10 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b case http.StatusNoContent: case http.StatusBadGateway: // Retry sending request if possible - if sequence < s.MaxRestRetries { + if sequence < cfg.MaxRestRetries { s.log(LogInformational, "%s Failed (%s), Retrying...", urlStr, resp.Status) - response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence+1) + response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence+1, options...) } else { err = fmt.Errorf("Exceeded Max retries HTTP %s, %s", resp.Status, response) } @@ -206,7 +282,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b return } - if s.ShouldRetryOnRateLimit { + if cfg.ShouldRetryOnRateLimit { s.log(LogInformational, "Rate Limiting %s, retry in %v", urlStr, rl.RetryAfter) s.handleEvent(rateLimitEventType, &RateLimit{TooManyRequests: &rl, URL: urlStr}) @@ -214,7 +290,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b // we can make the above smarter // this method can cause longer delays than required - response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence) + response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence, options...) } else { err = &RateLimitError{&RateLimit{TooManyRequests: &rl, URL: urlStr}} } @@ -246,9 +322,9 @@ func unmarshal(data []byte, v interface{}) error { // User returns the user details of the given userID // userID : A user ID or "@me" which is a shortcut of current user ID -func (s *Session) User(userID string) (st *User, err error) { +func (s *Session) User(userID string, options ...RequestOption) (st *User, err error) { - body, err := s.RequestWithBucketID("GET", EndpointUser(userID), nil, EndpointUsers) + body, err := s.RequestWithBucketID("GET", EndpointUser(userID), nil, EndpointUsers, options...) if err != nil { return } @@ -259,19 +335,19 @@ func (s *Session) User(userID string) (st *User, err error) { // UserAvatar is deprecated. Please use UserAvatarDecode // userID : A user ID or "@me" which is a shortcut of current user ID -func (s *Session) UserAvatar(userID string) (img image.Image, err error) { - u, err := s.User(userID) +func (s *Session) UserAvatar(userID string, options ...RequestOption) (img image.Image, err error) { + u, err := s.User(userID, options...) if err != nil { return } - img, err = s.UserAvatarDecode(u) + img, err = s.UserAvatarDecode(u, options...) return } // UserAvatarDecode returns an image.Image of a user's Avatar // user : The user which avatar should be retrieved -func (s *Session) UserAvatarDecode(u *User) (img image.Image, err error) { - body, err := s.RequestWithBucketID("GET", EndpointUserAvatar(u.ID, u.Avatar), nil, EndpointUserAvatar("", "")) +func (s *Session) UserAvatarDecode(u *User, options ...RequestOption) (img image.Image, err error) { + body, err := s.RequestWithBucketID("GET", EndpointUserAvatar(u.ID, u.Avatar), nil, EndpointUserAvatar("", ""), options...) if err != nil { return } @@ -281,7 +357,7 @@ func (s *Session) UserAvatarDecode(u *User) (img image.Image, err error) { } // UserUpdate updates current user settings. -func (s *Session) UserUpdate(username, avatar string) (st *User, err error) { +func (s *Session) UserUpdate(username, avatar string, options ...RequestOption) (st *User, err error) { // NOTE: Avatar must be either the hash/id of existing Avatar or // data:image/png;base64,BASE64_STRING_OF_NEW_AVATAR_PNG @@ -293,7 +369,7 @@ func (s *Session) UserUpdate(username, avatar string) (st *User, err error) { Avatar string `json:"avatar,omitempty"` }{username, avatar} - body, err := s.RequestWithBucketID("PATCH", EndpointUser("@me"), data, EndpointUsers) + body, err := s.RequestWithBucketID("PATCH", EndpointUser("@me"), data, EndpointUsers, options...) if err != nil { return } @@ -303,8 +379,8 @@ func (s *Session) UserUpdate(username, avatar string) (st *User, err error) { } // UserConnections returns the user's connections -func (s *Session) UserConnections() (conn []*UserConnection, err error) { - response, err := s.RequestWithBucketID("GET", EndpointUserConnections("@me"), nil, EndpointUserConnections("@me")) +func (s *Session) UserConnections(options ...RequestOption) (conn []*UserConnection, err error) { + response, err := s.RequestWithBucketID("GET", EndpointUserConnections("@me"), nil, EndpointUserConnections("@me"), options...) if err != nil { return nil, err } @@ -319,13 +395,13 @@ func (s *Session) UserConnections() (conn []*UserConnection, err error) { // UserChannelCreate creates a new User (Private) Channel with another User // recipientID : A user ID for the user to which this channel is opened with. -func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) { +func (s *Session) UserChannelCreate(recipientID string, options ...RequestOption) (st *Channel, err error) { data := struct { RecipientID string `json:"recipient_id"` }{recipientID} - body, err := s.RequestWithBucketID("POST", EndpointUserChannels("@me"), data, EndpointUserChannels("")) + body, err := s.RequestWithBucketID("POST", EndpointUserChannels("@me"), data, EndpointUserChannels(""), options...) if err != nil { return } @@ -336,8 +412,8 @@ func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) // UserGuildMember returns a guild member object for the current user in the given Guild. // guildID : ID of the guild -func (s *Session) UserGuildMember(guildID string) (st *Member, err error) { - body, err := s.RequestWithBucketID("GET", EndpointUserGuildMember("@me", guildID), nil, EndpointUserGuildMember("@me", guildID)) +func (s *Session) UserGuildMember(guildID string, options ...RequestOption) (st *Member, err error) { + body, err := s.RequestWithBucketID("GET", EndpointUserGuildMember("@me", guildID), nil, EndpointUserGuildMember("@me", guildID), options...) if err != nil { return } @@ -350,7 +426,7 @@ func (s *Session) UserGuildMember(guildID string) (st *Member, err error) { // limit : The number guilds that can be returned. (max 100) // beforeID : If provided all guilds returned will be before given ID. // afterID : If provided all guilds returned will be after given ID. -func (s *Session) UserGuilds(limit int, beforeID, afterID string) (st []*UserGuild, err error) { +func (s *Session) UserGuilds(limit int, beforeID, afterID string, options ...RequestOption) (st []*UserGuild, err error) { v := url.Values{} @@ -370,7 +446,7 @@ func (s *Session) UserGuilds(limit int, beforeID, afterID string) (st []*UserGui uri += "?" + v.Encode() } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointUserGuilds("")) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointUserGuilds(""), options...) if err != nil { return } @@ -380,12 +456,13 @@ func (s *Session) UserGuilds(limit int, beforeID, afterID string) (st []*UserGui } // UserChannelPermissions returns the permission of a user in a channel. -// userID : The ID of the user to calculate permissions for. -// channelID : The ID of the channel to calculate permission for. +// userID : The ID of the user to calculate permissions for. +// channelID : The ID of the channel to calculate permission for. +// fetchOptions : Options used to fetch guild, member or channel if they are not present in state. // // NOTE: This function is now deprecated and will be removed in the future. // Please see the same function inside state.go -func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int64, err error) { +func (s *Session) UserChannelPermissions(userID, channelID string, fetchOptions ...RequestOption) (apermissions int64, err error) { // Try to just get permissions from state. apermissions, err = s.State.UserChannelPermissions(userID, channelID) if err == nil { @@ -395,7 +472,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions // Otherwise try get as much data from state as possible, falling back to the network. channel, err := s.State.Channel(channelID) if err != nil || channel == nil { - channel, err = s.Channel(channelID) + channel, err = s.Channel(channelID, fetchOptions...) if err != nil { return } @@ -403,7 +480,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions guild, err := s.State.Guild(channel.GuildID) if err != nil || guild == nil { - guild, err = s.Guild(channel.GuildID) + guild, err = s.Guild(channel.GuildID, fetchOptions...) if err != nil { return } @@ -416,7 +493,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions member, err := s.State.Member(guild.ID, userID) if err != nil || member == nil { - member, err = s.GuildMember(guild.ID, userID) + member, err = s.GuildMember(guild.ID, userID, fetchOptions...) if err != nil { return } @@ -498,8 +575,8 @@ func memberPermissions(guild *Guild, channel *Channel, userID string, roles []st // Guild returns a Guild structure of a specific Guild. // guildID : The ID of a Guild -func (s *Session) Guild(guildID string) (st *Guild, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID)) +func (s *Session) Guild(guildID string, options ...RequestOption) (st *Guild, err error) { + body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID), options...) if err != nil { return } @@ -510,9 +587,9 @@ func (s *Session) Guild(guildID string) (st *Guild, err error) { // GuildWithCounts returns a Guild structure of a specific Guild with approximate member and presence counts. // guildID : The ID of a Guild -func (s *Session) GuildWithCounts(guildID string) (st *Guild, err error) { +func (s *Session) GuildWithCounts(guildID string, options ...RequestOption) (st *Guild, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID)+"?with_counts=true", nil, EndpointGuild(guildID)) + body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID)+"?with_counts=true", nil, EndpointGuild(guildID), options...) if err != nil { return } @@ -523,8 +600,8 @@ func (s *Session) GuildWithCounts(guildID string) (st *Guild, err error) { // GuildPreview returns a GuildPreview structure of a specific public Guild. // guildID : The ID of a Guild -func (s *Session) GuildPreview(guildID string) (st *GuildPreview, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildPreview(guildID), nil, EndpointGuildPreview(guildID)) +func (s *Session) GuildPreview(guildID string, options ...RequestOption) (st *GuildPreview, err error) { + body, err := s.RequestWithBucketID("GET", EndpointGuildPreview(guildID), nil, EndpointGuildPreview(guildID), options...) if err != nil { return } @@ -535,13 +612,13 @@ func (s *Session) GuildPreview(guildID string) (st *GuildPreview, err error) { // GuildCreate creates a new Guild // name : A name for the Guild (2-100 characters) -func (s *Session) GuildCreate(name string) (st *Guild, err error) { +func (s *Session) GuildCreate(name string, options ...RequestOption) (st *Guild, err error) { data := struct { Name string `json:"name"` }{name} - body, err := s.RequestWithBucketID("POST", EndpointGuildCreate, data, EndpointGuildCreate) + body, err := s.RequestWithBucketID("POST", EndpointGuildCreate, data, EndpointGuildCreate, options...) if err != nil { return } @@ -553,7 +630,7 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) { // GuildEdit edits a new Guild // guildID : The ID of a Guild // g : A GuildParams struct with the values Name, Region and VerificationLevel defined. -func (s *Session) GuildEdit(guildID string, g *GuildParams) (st *Guild, err error) { +func (s *Session) GuildEdit(guildID string, g *GuildParams, options ...RequestOption) (st *Guild, err error) { // Bounds checking for VerificationLevel, interval: [0, 4] if g.VerificationLevel != nil { @@ -567,7 +644,7 @@ func (s *Session) GuildEdit(guildID string, g *GuildParams) (st *Guild, err erro // Bounds checking for regions if g.Region != "" { isValid := false - regions, _ := s.VoiceRegions() + regions, _ := s.VoiceRegions(options...) for _, r := range regions { if g.Region == r.ID { isValid = true @@ -583,7 +660,7 @@ func (s *Session) GuildEdit(guildID string, g *GuildParams) (st *Guild, err erro } } - body, err := s.RequestWithBucketID("PATCH", EndpointGuild(guildID), g, EndpointGuild(guildID)) + body, err := s.RequestWithBucketID("PATCH", EndpointGuild(guildID), g, EndpointGuild(guildID), options...) if err != nil { return } @@ -594,9 +671,9 @@ func (s *Session) GuildEdit(guildID string, g *GuildParams) (st *Guild, err erro // GuildDelete deletes a Guild. // guildID : The ID of a Guild -func (s *Session) GuildDelete(guildID string) (st *Guild, err error) { +func (s *Session) GuildDelete(guildID string, options ...RequestOption) (st *Guild, err error) { - body, err := s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID)) + body, err := s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID), options...) if err != nil { return } @@ -607,18 +684,18 @@ func (s *Session) GuildDelete(guildID string) (st *Guild, err error) { // GuildLeave leaves a Guild. // guildID : The ID of a Guild -func (s *Session) GuildLeave(guildID string) (err error) { +func (s *Session) GuildLeave(guildID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointUserGuild("@me", guildID), nil, EndpointUserGuild("", guildID)) + _, err = s.RequestWithBucketID("DELETE", EndpointUserGuild("@me", guildID), nil, EndpointUserGuild("", guildID), options...) return } // GuildBans returns an array of GuildBan structures for bans in the given guild. -// guildID : The ID of a Guild -// limit : Max number of bans to return (max 1000) -// beforeID : If not empty all returned users will be after the given id -// afterID : If not empty all returned users will be before the given id -func (s *Session) GuildBans(guildID string, limit int, beforeID, afterID string) (st []*GuildBan, err error) { +// guildID : The ID of a Guild +// limit : Max number of bans to return (max 1000) +// beforeID : If not empty all returned users will be after the given id +// afterID : If not empty all returned users will be before the given id +func (s *Session) GuildBans(guildID string, limit int, beforeID, afterID string, options ...RequestOption) (st []*GuildBan, err error) { uri := EndpointGuildBans(guildID) v := url.Values{} @@ -636,7 +713,7 @@ func (s *Session) GuildBans(guildID string, limit int, beforeID, afterID string) uri += "?" + v.Encode() } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildBans(guildID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildBans(guildID), options...) if err != nil { return } @@ -650,14 +727,14 @@ func (s *Session) GuildBans(guildID string, limit int, beforeID, afterID string) // guildID : The ID of a Guild. // userID : The ID of a User // days : The number of days of previous comments to delete. -func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) { - return s.GuildBanCreateWithReason(guildID, userID, "", days) +func (s *Session) GuildBanCreate(guildID, userID string, days int, options ...RequestOption) (err error) { + return s.GuildBanCreateWithReason(guildID, userID, "", days, options...) } // GuildBan finds ban by given guild and user id and returns GuildBan structure -func (s *Session) GuildBan(guildID, userID string) (st *GuildBan, err error) { +func (s *Session) GuildBan(guildID, userID string, options ...RequestOption) (st *GuildBan, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, userID)) + body, err := s.RequestWithBucketID("GET", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, userID), options...) if err != nil { return } @@ -672,7 +749,7 @@ func (s *Session) GuildBan(guildID, userID string) (st *GuildBan, err error) { // userID : The ID of a User // reason : The reason for this ban // days : The number of days of previous comments to delete. -func (s *Session) GuildBanCreateWithReason(guildID, userID, reason string, days int) (err error) { +func (s *Session) GuildBanCreateWithReason(guildID, userID, reason string, days int, options ...RequestOption) (err error) { uri := EndpointGuildBan(guildID, userID) @@ -688,24 +765,24 @@ func (s *Session) GuildBanCreateWithReason(guildID, userID, reason string, days uri += "?" + queryParams.Encode() } - _, err = s.RequestWithBucketID("PUT", uri, nil, EndpointGuildBan(guildID, "")) + _, err = s.RequestWithBucketID("PUT", uri, nil, EndpointGuildBan(guildID, ""), options...) return } // GuildBanDelete removes the given user from the guild bans // guildID : The ID of a Guild. // userID : The ID of a User -func (s *Session) GuildBanDelete(guildID, userID string) (err error) { +func (s *Session) GuildBanDelete(guildID, userID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, "")) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, ""), options...) return } // GuildMembers returns a list of members for a guild. -// guildID : The ID of a Guild. -// after : The id of the member to return members after -// limit : max number of members to return (max 1000) -func (s *Session) GuildMembers(guildID string, after string, limit int) (st []*Member, err error) { +// guildID : The ID of a Guild. +// after : The id of the member to return members after +// limit : max number of members to return (max 1000) +func (s *Session) GuildMembers(guildID string, after string, limit int, options ...RequestOption) (st []*Member, err error) { uri := EndpointGuildMembers(guildID) @@ -723,7 +800,7 @@ func (s *Session) GuildMembers(guildID string, after string, limit int) (st []*M uri += "?" + v.Encode() } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildMembers(guildID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildMembers(guildID), options...) if err != nil { return } @@ -736,7 +813,7 @@ func (s *Session) GuildMembers(guildID string, after string, limit int) (st []*M // guildID : The ID of a Guild // query : Query string to match username(s) and nickname(s) against // limit : Max number of members to return (default 1, min 1, max 1000) -func (s *Session) GuildMembersSearch(guildID, query string, limit int) (st []*Member, err error) { +func (s *Session) GuildMembersSearch(guildID, query string, limit int, options ...RequestOption) (st []*Member, err error) { uri := EndpointGuildMembersSearch(guildID) @@ -746,7 +823,7 @@ func (s *Session) GuildMembersSearch(guildID, query string, limit int) (st []*Me queryParams.Set("limit", strconv.Itoa(limit)) } - body, err := s.RequestWithBucketID("GET", uri+"?"+queryParams.Encode(), nil, uri) + body, err := s.RequestWithBucketID("GET", uri+"?"+queryParams.Encode(), nil, uri, options...) if err != nil { return } @@ -756,11 +833,11 @@ func (s *Session) GuildMembersSearch(guildID, query string, limit int) (st []*Me } // GuildMember returns a member of a guild. -// guildID : The ID of a Guild. -// userID : The ID of a User -func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) { +// guildID : The ID of a Guild. +// userID : The ID of a User +func (s *Session) GuildMember(guildID, userID string, options ...RequestOption) (st *Member, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, "")) + body, err := s.RequestWithBucketID("GET", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, ""), options...) if err != nil { return } @@ -772,12 +849,12 @@ func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) { } // GuildMemberAdd force joins a user to the guild. -// guildID : The ID of a Guild. -// userID : The ID of a User. -// data : Parameters of the user to add. -func (s *Session) GuildMemberAdd(guildID, userID string, data *GuildMemberAddParams) (err error) { +// guildID : The ID of a Guild. +// userID : The ID of a User. +// data : Parameters of the user to add. +func (s *Session) GuildMemberAdd(guildID, userID string, data *GuildMemberAddParams, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("PUT", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + _, err = s.RequestWithBucketID("PUT", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""), options...) if err != nil { return err } @@ -788,23 +865,23 @@ func (s *Session) GuildMemberAdd(guildID, userID string, data *GuildMemberAddPar // GuildMemberDelete removes the given user from the given guild. // guildID : The ID of a Guild. // userID : The ID of a User -func (s *Session) GuildMemberDelete(guildID, userID string) (err error) { +func (s *Session) GuildMemberDelete(guildID, userID string, options ...RequestOption) (err error) { - return s.GuildMemberDeleteWithReason(guildID, userID, "") + return s.GuildMemberDeleteWithReason(guildID, userID, "", options...) } // GuildMemberDeleteWithReason removes the given user from the given guild. // guildID : The ID of a Guild. // userID : The ID of a User // reason : The reason for the kick -func (s *Session) GuildMemberDeleteWithReason(guildID, userID, reason string) (err error) { +func (s *Session) GuildMemberDeleteWithReason(guildID, userID, reason string, options ...RequestOption) (err error) { uri := EndpointGuildMember(guildID, userID) if reason != "" { uri += "?reason=" + url.QueryEscape(reason) } - _, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointGuildMember(guildID, "")) + _, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointGuildMember(guildID, ""), options...) return } @@ -812,9 +889,9 @@ func (s *Session) GuildMemberDeleteWithReason(guildID, userID, reason string) (e // guildID : The ID of a Guild. // userID : The ID of a User. // data : Updated GuildMember data. -func (s *Session) GuildMemberEdit(guildID, userID string, data *GuildMemberParams) (st *Member, err error) { +func (s *Session) GuildMemberEdit(guildID, userID string, data *GuildMemberParams, options ...RequestOption) (st *Member, err error) { var body []byte - body, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + body, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""), options...) if err != nil { return nil, err } @@ -825,25 +902,27 @@ func (s *Session) GuildMemberEdit(guildID, userID string, data *GuildMemberParam // GuildMemberEditComplex edits the nickname and roles of a member. // NOTE: deprecated, use GuildMemberEdit instead. +// // guildID : The ID of a Guild. // userID : The ID of a User. // data : A GuildMemberEditData struct with the new nickname and roles -func (s *Session) GuildMemberEditComplex(guildID, userID string, data *GuildMemberParams) (st *Member, err error) { - return s.GuildMemberEdit(guildID, userID, data) +func (s *Session) GuildMemberEditComplex(guildID, userID string, data *GuildMemberParams, options ...RequestOption) (st *Member, err error) { + return s.GuildMemberEdit(guildID, userID, data, options...) } // GuildMemberMove moves a guild member from one voice channel to another/none -// guildID : The ID of a Guild. -// userID : The ID of a User. -// channelID : The ID of a channel to move user to or nil to remove from voice channel +// guildID : The ID of a Guild. +// userID : The ID of a User. +// channelID : The ID of a channel to move user to or nil to remove from voice channel +// // NOTE : I am not entirely set on the name of this function and it may change // prior to the final 1.0.0 release of Discordgo -func (s *Session) GuildMemberMove(guildID string, userID string, channelID *string) (err error) { +func (s *Session) GuildMemberMove(guildID string, userID string, channelID *string, options ...RequestOption) (err error) { data := struct { ChannelID *string `json:"channel_id"` }{channelID} - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""), options...) return } @@ -852,7 +931,7 @@ func (s *Session) GuildMemberMove(guildID string, userID string, channelID *stri // userID : The ID of a user // userID : The ID of a user or "@me" which is a shortcut of the current user ID // nickname : The nickname of the member, "" will reset their nickname -func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err error) { +func (s *Session) GuildMemberNickname(guildID, userID, nickname string, options ...RequestOption) (err error) { data := struct { Nick string `json:"nick"` @@ -862,68 +941,67 @@ func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err err userID += "/nick" } - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""), options...) return } // GuildMemberMute server mutes a guild member -// guildID : The ID of a Guild. -// userID : The ID of a User. -// mute : boolean value for if the user should be muted -func (s *Session) GuildMemberMute(guildID string, userID string, mute bool) (err error) { +// guildID : The ID of a Guild. +// userID : The ID of a User. +// mute : boolean value for if the user should be muted +func (s *Session) GuildMemberMute(guildID string, userID string, mute bool, options ...RequestOption) (err error) { data := struct { Mute bool `json:"mute"` }{mute} - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""), options...) return } // GuildMemberTimeout times out a guild member -// guildID : The ID of a Guild. -// userID : The ID of a User. -// until : The timestamp for how long a member should be timed out. -// Set to nil to remove timeout. -func (s *Session) GuildMemberTimeout(guildID string, userID string, until *time.Time) (err error) { +// guildID : The ID of a Guild. +// userID : The ID of a User. +// until : The timestamp for how long a member should be timed out. Set to nil to remove timeout. +func (s *Session) GuildMemberTimeout(guildID string, userID string, until *time.Time, options ...RequestOption) (err error) { data := struct { CommunicationDisabledUntil *time.Time `json:"communication_disabled_until"` }{until} - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""), options...) return } // GuildMemberDeafen server deafens a guild member -// guildID : The ID of a Guild. -// userID : The ID of a User. -// deaf : boolean value for if the user should be deafened -func (s *Session) GuildMemberDeafen(guildID string, userID string, deaf bool) (err error) { +// guildID : The ID of a Guild. +// userID : The ID of a User. +// deaf : boolean value for if the user should be deafened +func (s *Session) GuildMemberDeafen(guildID string, userID string, deaf bool, options ...RequestOption) (err error) { data := struct { Deaf bool `json:"deaf"` }{deaf} - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""), options...) return } // GuildMemberRoleAdd adds the specified role to a given member -// guildID : The ID of a Guild. -// userID : The ID of a User. -// roleID : The ID of a Role to be assigned to the user. -func (s *Session) GuildMemberRoleAdd(guildID, userID, roleID string) (err error) { +// guildID : The ID of a Guild. +// userID : The ID of a User. +// roleID : The ID of a Role to be assigned to the user. +func (s *Session) GuildMemberRoleAdd(guildID, userID, roleID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("PUT", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", "")) + _, err = s.RequestWithBucketID("PUT", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", ""), options...) return } // GuildMemberRoleRemove removes the specified role to a given member -// guildID : The ID of a Guild. -// userID : The ID of a User. -// roleID : The ID of a Role to be removed from the user. -func (s *Session) GuildMemberRoleRemove(guildID, userID, roleID string) (err error) { +// guildID : The ID of a Guild. +// userID : The ID of a User. +// roleID : The ID of a Role to be removed from the user. +func (s *Session) GuildMemberRoleRemove(guildID, userID, roleID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", "")) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", ""), options...) return } @@ -931,9 +1009,9 @@ func (s *Session) GuildMemberRoleRemove(guildID, userID, roleID string) (err err // GuildChannels returns an array of Channel structures for all channels of a // given guild. // guildID : The ID of a Guild. -func (s *Session) GuildChannels(guildID string) (st []*Channel, err error) { +func (s *Session) GuildChannels(guildID string, options ...RequestOption) (st []*Channel, err error) { - body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil, EndpointGuildChannels(guildID), 0) + body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil, EndpointGuildChannels(guildID), 0, options...) if err != nil { return } @@ -960,8 +1038,8 @@ type GuildChannelCreateData struct { // GuildChannelCreateComplex creates a new channel in the given guild // guildID : The ID of a Guild // data : A data struct describing the new Channel, Name and Type are mandatory, other fields depending on the type -func (s *Session) GuildChannelCreateComplex(guildID string, data GuildChannelCreateData) (st *Channel, err error) { - body, err := s.RequestWithBucketID("POST", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID)) +func (s *Session) GuildChannelCreateComplex(guildID string, data GuildChannelCreateData, options ...RequestOption) (st *Channel, err error) { + body, err := s.RequestWithBucketID("POST", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID), options...) if err != nil { return } @@ -974,17 +1052,17 @@ func (s *Session) GuildChannelCreateComplex(guildID string, data GuildChannelCre // guildID : The ID of a Guild. // name : Name of the channel (2-100 chars length) // ctype : Type of the channel -func (s *Session) GuildChannelCreate(guildID, name string, ctype ChannelType) (st *Channel, err error) { +func (s *Session) GuildChannelCreate(guildID, name string, ctype ChannelType, options ...RequestOption) (st *Channel, err error) { return s.GuildChannelCreateComplex(guildID, GuildChannelCreateData{ Name: name, Type: ctype, - }) + }, options...) } // GuildChannelsReorder updates the order of channels in a guild // guildID : The ID of a Guild. // channels : Updated channels. -func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel) (err error) { +func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel, options ...RequestOption) (err error) { data := make([]struct { ID string `json:"id"` @@ -996,14 +1074,14 @@ func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel) (err data[i].Position = c.Position } - _, err = s.RequestWithBucketID("PATCH", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID)) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID), options...) return } // GuildInvites returns an array of Invite structures for the given guild // guildID : The ID of a Guild. -func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInvites(guildID)) +func (s *Session) GuildInvites(guildID string, options ...RequestOption) (st []*Invite, err error) { + body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInvites(guildID), options...) if err != nil { return } @@ -1014,9 +1092,9 @@ func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) { // GuildRoles returns all roles for a given guild. // guildID : The ID of a Guild. -func (s *Session) GuildRoles(guildID string) (st []*Role, err error) { +func (s *Session) GuildRoles(guildID string, options ...RequestOption) (st []*Role, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID)) + body, err := s.RequestWithBucketID("GET", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID), options...) if err != nil { return } @@ -1029,8 +1107,8 @@ func (s *Session) GuildRoles(guildID string) (st []*Role, err error) { // GuildRoleCreate creates a new Guild Role and returns it. // guildID : The ID of a Guild. // data : New Role parameters. -func (s *Session) GuildRoleCreate(guildID string, data *RoleParams) (st *Role, err error) { - body, err := s.RequestWithBucketID("POST", EndpointGuildRoles(guildID), data, EndpointGuildRoles(guildID)) +func (s *Session) GuildRoleCreate(guildID string, data *RoleParams, options ...RequestOption) (st *Role, err error) { + body, err := s.RequestWithBucketID("POST", EndpointGuildRoles(guildID), data, EndpointGuildRoles(guildID), options...) if err != nil { return } @@ -1044,14 +1122,14 @@ func (s *Session) GuildRoleCreate(guildID string, data *RoleParams) (st *Role, e // guildID : The ID of a Guild. // roleID : The ID of a Role. // data : Updated Role data. -func (s *Session) GuildRoleEdit(guildID, roleID string, data *RoleParams) (st *Role, err error) { +func (s *Session) GuildRoleEdit(guildID, roleID string, data *RoleParams, options ...RequestOption) (st *Role, err error) { // Prevent sending a color int that is too big. if data.Color != nil && *data.Color > 0xFFFFFF { return nil, fmt.Errorf("color value cannot be larger than 0xFFFFFF") } - body, err := s.RequestWithBucketID("PATCH", EndpointGuildRole(guildID, roleID), data, EndpointGuildRole(guildID, "")) + body, err := s.RequestWithBucketID("PATCH", EndpointGuildRole(guildID, roleID), data, EndpointGuildRole(guildID, ""), options...) if err != nil { return } @@ -1064,9 +1142,9 @@ func (s *Session) GuildRoleEdit(guildID, roleID string, data *RoleParams) (st *R // GuildRoleReorder reoders guild roles // guildID : The ID of a Guild. // roles : A list of ordered roles. -func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, err error) { +func (s *Session) GuildRoleReorder(guildID string, roles []*Role, options ...RequestOption) (st []*Role, err error) { - body, err := s.RequestWithBucketID("PATCH", EndpointGuildRoles(guildID), roles, EndpointGuildRoles(guildID)) + body, err := s.RequestWithBucketID("PATCH", EndpointGuildRoles(guildID), roles, EndpointGuildRoles(guildID), options...) if err != nil { return } @@ -1079,9 +1157,9 @@ func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, e // GuildRoleDelete deletes an existing role. // guildID : The ID of a Guild. // roleID : The ID of a Role. -func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) { +func (s *Session) GuildRoleDelete(guildID, roleID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointGuildRole(guildID, roleID), nil, EndpointGuildRole(guildID, "")) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildRole(guildID, roleID), nil, EndpointGuildRole(guildID, ""), options...) return } @@ -1090,7 +1168,7 @@ func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) { // Requires 'KICK_MEMBER' permission. // guildID : The ID of a Guild. // days : The number of days to count prune for (1 or more). -func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, err error) { +func (s *Session) GuildPruneCount(guildID string, days uint32, options ...RequestOption) (count uint32, err error) { count = 0 if days <= 0 { @@ -1103,7 +1181,7 @@ func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, er }{} uri := EndpointGuildPrune(guildID) + "?days=" + strconv.FormatUint(uint64(days), 10) - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID), options...) if err != nil { return } @@ -1122,7 +1200,7 @@ func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, er // Returns an object with one 'pruned' key indicating the number of members that were removed in the prune operation. // guildID : The ID of a Guild. // days : The number of days to count prune for (1 or more). -func (s *Session) GuildPrune(guildID string, days uint32) (count uint32, err error) { +func (s *Session) GuildPrune(guildID string, days uint32, options ...RequestOption) (count uint32, err error) { count = 0 @@ -1139,7 +1217,7 @@ func (s *Session) GuildPrune(guildID string, days uint32) (count uint32, err err Pruned uint32 `json:"pruned"` }{} - body, err := s.RequestWithBucketID("POST", EndpointGuildPrune(guildID), data, EndpointGuildPrune(guildID)) + body, err := s.RequestWithBucketID("POST", EndpointGuildPrune(guildID), data, EndpointGuildPrune(guildID), options...) if err != nil { return } @@ -1156,9 +1234,9 @@ func (s *Session) GuildPrune(guildID string, days uint32) (count uint32, err err // GuildIntegrations returns an array of Integrations for a guild. // guildID : The ID of a Guild. -func (s *Session) GuildIntegrations(guildID string) (st []*Integration, err error) { +func (s *Session) GuildIntegrations(guildID string, options ...RequestOption) (st []*Integration, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildIntegrations(guildID), nil, EndpointGuildIntegrations(guildID)) + body, err := s.RequestWithBucketID("GET", EndpointGuildIntegrations(guildID), nil, EndpointGuildIntegrations(guildID), options...) if err != nil { return } @@ -1172,14 +1250,14 @@ func (s *Session) GuildIntegrations(guildID string) (st []*Integration, err erro // guildID : The ID of a Guild. // integrationType : The Integration type. // integrationID : The ID of an integration. -func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID string) (err error) { +func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID string, options ...RequestOption) (err error) { data := struct { Type string `json:"type"` ID string `json:"id"` }{integrationType, integrationID} - _, err = s.RequestWithBucketID("POST", EndpointGuildIntegrations(guildID), data, EndpointGuildIntegrations(guildID)) + _, err = s.RequestWithBucketID("POST", EndpointGuildIntegrations(guildID), data, EndpointGuildIntegrations(guildID), options...) return } @@ -1190,7 +1268,7 @@ func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID // expireBehavior : The behavior when an integration subscription lapses (see the integration object documentation). // expireGracePeriod : Period (in seconds) where the integration will ignore lapsed subscriptions. // enableEmoticons : Whether emoticons should be synced for this integration (twitch only currently). -func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBehavior, expireGracePeriod int, enableEmoticons bool) (err error) { +func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBehavior, expireGracePeriod int, enableEmoticons bool, options ...RequestOption) (err error) { data := struct { ExpireBehavior int `json:"expire_behavior"` @@ -1198,23 +1276,23 @@ func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBeha EnableEmoticons bool `json:"enable_emoticons"` }{expireBehavior, expireGracePeriod, enableEmoticons} - _, err = s.RequestWithBucketID("PATCH", EndpointGuildIntegration(guildID, integrationID), data, EndpointGuildIntegration(guildID, "")) + _, err = s.RequestWithBucketID("PATCH", EndpointGuildIntegration(guildID, integrationID), data, EndpointGuildIntegration(guildID, ""), options...) return } // GuildIntegrationDelete removes the given integration from the Guild. // guildID : The ID of a Guild. // integrationID : The ID of an integration. -func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err error) { +func (s *Session) GuildIntegrationDelete(guildID, integrationID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointGuildIntegration(guildID, integrationID), nil, EndpointGuildIntegration(guildID, "")) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildIntegration(guildID, integrationID), nil, EndpointGuildIntegration(guildID, ""), options...) return } // GuildIcon returns an image.Image of a guild icon. // guildID : The ID of a Guild. -func (s *Session) GuildIcon(guildID string) (img image.Image, err error) { - g, err := s.Guild(guildID) +func (s *Session) GuildIcon(guildID string, options ...RequestOption) (img image.Image, err error) { + g, err := s.Guild(guildID, options...) if err != nil { return } @@ -1224,7 +1302,7 @@ func (s *Session) GuildIcon(guildID string) (img image.Image, err error) { return } - body, err := s.RequestWithBucketID("GET", EndpointGuildIcon(guildID, g.Icon), nil, EndpointGuildIcon(guildID, "")) + body, err := s.RequestWithBucketID("GET", EndpointGuildIcon(guildID, g.Icon), nil, EndpointGuildIcon(guildID, ""), options...) if err != nil { return } @@ -1235,8 +1313,8 @@ func (s *Session) GuildIcon(guildID string) (img image.Image, err error) { // GuildSplash returns an image.Image of a guild splash image. // guildID : The ID of a Guild. -func (s *Session) GuildSplash(guildID string) (img image.Image, err error) { - g, err := s.Guild(guildID) +func (s *Session) GuildSplash(guildID string, options ...RequestOption) (img image.Image, err error) { + g, err := s.Guild(guildID, options...) if err != nil { return } @@ -1246,7 +1324,7 @@ func (s *Session) GuildSplash(guildID string) (img image.Image, err error) { return } - body, err := s.RequestWithBucketID("GET", EndpointGuildSplash(guildID, g.Splash), nil, EndpointGuildSplash(guildID, "")) + body, err := s.RequestWithBucketID("GET", EndpointGuildSplash(guildID, g.Splash), nil, EndpointGuildSplash(guildID, ""), options...) if err != nil { return } @@ -1257,9 +1335,9 @@ func (s *Session) GuildSplash(guildID string) (img image.Image, err error) { // GuildEmbed returns the embed for a Guild. // guildID : The ID of a Guild. -func (s *Session) GuildEmbed(guildID string) (st *GuildEmbed, err error) { +func (s *Session) GuildEmbed(guildID string, options ...RequestOption) (st *GuildEmbed, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildEmbed(guildID), nil, EndpointGuildEmbed(guildID)) + body, err := s.RequestWithBucketID("GET", EndpointGuildEmbed(guildID), nil, EndpointGuildEmbed(guildID), options...) if err != nil { return } @@ -1271,8 +1349,8 @@ func (s *Session) GuildEmbed(guildID string) (st *GuildEmbed, err error) { // GuildEmbedEdit edits the embed of a Guild. // guildID : The ID of a Guild. // data : New GuildEmbed data. -func (s *Session) GuildEmbedEdit(guildID string, data *GuildEmbed) (err error) { - _, err = s.RequestWithBucketID("PATCH", EndpointGuildEmbed(guildID), data, EndpointGuildEmbed(guildID)) +func (s *Session) GuildEmbedEdit(guildID string, data *GuildEmbed, options ...RequestOption) (err error) { + _, err = s.RequestWithBucketID("PATCH", EndpointGuildEmbed(guildID), data, EndpointGuildEmbed(guildID), options...) return } @@ -1282,7 +1360,7 @@ func (s *Session) GuildEmbedEdit(guildID string, data *GuildEmbed) (err error) { // beforeID : If provided all log entries returned will be before the given ID. // actionType : If provided the log will be filtered for the given Action Type. // limit : The number messages that can be returned. (default 50, min 1, max 100) -func (s *Session) GuildAuditLog(guildID, userID, beforeID string, actionType, limit int) (st *GuildAuditLog, err error) { +func (s *Session) GuildAuditLog(guildID, userID, beforeID string, actionType, limit int, options ...RequestOption) (st *GuildAuditLog, err error) { uri := EndpointGuildAuditLogs(guildID) @@ -1303,7 +1381,7 @@ func (s *Session) GuildAuditLog(guildID, userID, beforeID string, actionType, li uri = fmt.Sprintf("%s?%s", uri, v.Encode()) } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildAuditLogs(guildID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildAuditLogs(guildID), options...) if err != nil { return } @@ -1314,9 +1392,9 @@ func (s *Session) GuildAuditLog(guildID, userID, beforeID string, actionType, li // GuildEmojis returns all emoji // guildID : The ID of a Guild. -func (s *Session) GuildEmojis(guildID string) (emoji []*Emoji, err error) { +func (s *Session) GuildEmojis(guildID string, options ...RequestOption) (emoji []*Emoji, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildEmojis(guildID), nil, EndpointGuildEmojis(guildID)) + body, err := s.RequestWithBucketID("GET", EndpointGuildEmojis(guildID), nil, EndpointGuildEmojis(guildID), options...) if err != nil { return } @@ -1328,9 +1406,9 @@ func (s *Session) GuildEmojis(guildID string) (emoji []*Emoji, err error) { // GuildEmoji returns specified emoji. // guildID : The ID of a Guild // emojiID : The ID of an Emoji to retrieve -func (s *Session) GuildEmoji(guildID, emojiID string) (emoji *Emoji, err error) { +func (s *Session) GuildEmoji(guildID, emojiID string, options ...RequestOption) (emoji *Emoji, err error) { var body []byte - body, err = s.RequestWithBucketID("GET", EndpointGuildEmoji(guildID, emojiID), nil, EndpointGuildEmoji(guildID, emojiID)) + body, err = s.RequestWithBucketID("GET", EndpointGuildEmoji(guildID, emojiID), nil, EndpointGuildEmoji(guildID, emojiID), options...) if err != nil { return } @@ -1342,8 +1420,8 @@ func (s *Session) GuildEmoji(guildID, emojiID string) (emoji *Emoji, err error) // GuildEmojiCreate creates a new Emoji. // guildID : The ID of a Guild. // data : New Emoji data. -func (s *Session) GuildEmojiCreate(guildID string, data *EmojiParams) (emoji *Emoji, err error) { - body, err := s.RequestWithBucketID("POST", EndpointGuildEmojis(guildID), data, EndpointGuildEmojis(guildID)) +func (s *Session) GuildEmojiCreate(guildID string, data *EmojiParams, options ...RequestOption) (emoji *Emoji, err error) { + body, err := s.RequestWithBucketID("POST", EndpointGuildEmojis(guildID), data, EndpointGuildEmojis(guildID), options...) if err != nil { return } @@ -1356,8 +1434,8 @@ func (s *Session) GuildEmojiCreate(guildID string, data *EmojiParams) (emoji *Em // guildID : The ID of a Guild. // emojiID : The ID of an Emoji. // data : Updated Emoji data. -func (s *Session) GuildEmojiEdit(guildID, emojiID string, data *EmojiParams) (emoji *Emoji, err error) { - body, err := s.RequestWithBucketID("PATCH", EndpointGuildEmoji(guildID, emojiID), data, EndpointGuildEmojis(guildID)) +func (s *Session) GuildEmojiEdit(guildID, emojiID string, data *EmojiParams, options ...RequestOption) (emoji *Emoji, err error) { + body, err := s.RequestWithBucketID("PATCH", EndpointGuildEmoji(guildID, emojiID), data, EndpointGuildEmojis(guildID), options...) if err != nil { return } @@ -1369,17 +1447,17 @@ func (s *Session) GuildEmojiEdit(guildID, emojiID string, data *EmojiParams) (em // GuildEmojiDelete deletes an Emoji. // guildID : The ID of a Guild. // emojiID : The ID of an Emoji. -func (s *Session) GuildEmojiDelete(guildID, emojiID string) (err error) { +func (s *Session) GuildEmojiDelete(guildID, emojiID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointGuildEmoji(guildID, emojiID), nil, EndpointGuildEmojis(guildID)) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildEmoji(guildID, emojiID), nil, EndpointGuildEmojis(guildID), options...) return } // GuildTemplate returns a GuildTemplate for the given code // templateCode: The Code of a GuildTemplate -func (s *Session) GuildTemplate(templateCode string) (st *GuildTemplate, err error) { +func (s *Session) GuildTemplate(templateCode string, options ...RequestOption) (st *GuildTemplate, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildTemplate(templateCode), nil, EndpointGuildTemplate(templateCode)) + body, err := s.RequestWithBucketID("GET", EndpointGuildTemplate(templateCode), nil, EndpointGuildTemplate(templateCode), options...) if err != nil { return } @@ -1392,14 +1470,14 @@ func (s *Session) GuildTemplate(templateCode string) (st *GuildTemplate, err err // templateCode: The Code of a GuildTemplate // name: The name of the guild (2-100) characters // icon: base64 encoded 128x128 image for the guild icon -func (s *Session) GuildCreateWithTemplate(templateCode, name, icon string) (st *Guild, err error) { +func (s *Session) GuildCreateWithTemplate(templateCode, name, icon string, options ...RequestOption) (st *Guild, err error) { data := struct { Name string `json:"name"` Icon string `json:"icon"` }{name, icon} - body, err := s.RequestWithBucketID("POST", EndpointGuildTemplate(templateCode), data, EndpointGuildTemplate(templateCode)) + body, err := s.RequestWithBucketID("POST", EndpointGuildTemplate(templateCode), data, EndpointGuildTemplate(templateCode), options...) if err != nil { return } @@ -1410,9 +1488,9 @@ func (s *Session) GuildCreateWithTemplate(templateCode, name, icon string) (st * // GuildTemplates returns all of GuildTemplates // guildID: The ID of the guild -func (s *Session) GuildTemplates(guildID string) (st []*GuildTemplate, err error) { +func (s *Session) GuildTemplates(guildID string, options ...RequestOption) (st []*GuildTemplate, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildTemplates(guildID), nil, EndpointGuildTemplates(guildID)) + body, err := s.RequestWithBucketID("GET", EndpointGuildTemplates(guildID), nil, EndpointGuildTemplates(guildID), options...) if err != nil { return } @@ -1424,8 +1502,8 @@ func (s *Session) GuildTemplates(guildID string) (st []*GuildTemplate, err error // GuildTemplateCreate creates a template for the guild // guildID : The ID of the guild // data : Template metadata -func (s *Session) GuildTemplateCreate(guildID string, data *GuildTemplateParams) (st *GuildTemplate) { - body, err := s.RequestWithBucketID("POST", EndpointGuildTemplates(guildID), data, EndpointGuildTemplates(guildID)) +func (s *Session) GuildTemplateCreate(guildID string, data *GuildTemplateParams, options ...RequestOption) (st *GuildTemplate) { + body, err := s.RequestWithBucketID("POST", EndpointGuildTemplates(guildID), data, EndpointGuildTemplates(guildID), options...) if err != nil { return } @@ -1437,9 +1515,9 @@ func (s *Session) GuildTemplateCreate(guildID string, data *GuildTemplateParams) // GuildTemplateSync syncs the template to the guild's current state // guildID: The ID of the guild // templateCode: The code of the template -func (s *Session) GuildTemplateSync(guildID, templateCode string) (err error) { +func (s *Session) GuildTemplateSync(guildID, templateCode string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("PUT", EndpointGuildTemplateSync(guildID, templateCode), nil, EndpointGuildTemplateSync(guildID, "")) + _, err = s.RequestWithBucketID("PUT", EndpointGuildTemplateSync(guildID, templateCode), nil, EndpointGuildTemplateSync(guildID, ""), options...) return } @@ -1447,9 +1525,9 @@ func (s *Session) GuildTemplateSync(guildID, templateCode string) (err error) { // guildID : The ID of the guild // templateCode : The code of the template // data : New template metadata -func (s *Session) GuildTemplateEdit(guildID, templateCode string, data *GuildTemplateParams) (st *GuildTemplate, err error) { +func (s *Session) GuildTemplateEdit(guildID, templateCode string, data *GuildTemplateParams, options ...RequestOption) (st *GuildTemplate, err error) { - body, err := s.RequestWithBucketID("PATCH", EndpointGuildTemplateSync(guildID, templateCode), data, EndpointGuildTemplateSync(guildID, "")) + body, err := s.RequestWithBucketID("PATCH", EndpointGuildTemplateSync(guildID, templateCode), data, EndpointGuildTemplateSync(guildID, ""), options...) if err != nil { return } @@ -1461,9 +1539,9 @@ func (s *Session) GuildTemplateEdit(guildID, templateCode string, data *GuildTem // GuildTemplateDelete deletes the template // guildID: The ID of the guild // templateCode: The code of the template -func (s *Session) GuildTemplateDelete(guildID, templateCode string) (err error) { +func (s *Session) GuildTemplateDelete(guildID, templateCode string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointGuildTemplateSync(guildID, templateCode), nil, EndpointGuildTemplateSync(guildID, "")) + _, err = s.RequestWithBucketID("DELETE", EndpointGuildTemplateSync(guildID, templateCode), nil, EndpointGuildTemplateSync(guildID, ""), options...) return } @@ -1473,8 +1551,8 @@ func (s *Session) GuildTemplateDelete(guildID, templateCode string) (err error) // Channel returns a Channel structure of a specific Channel. // channelID : The ID of the Channel you want returned. -func (s *Session) Channel(channelID string) (st *Channel, err error) { - body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID)) +func (s *Session) Channel(channelID string, options ...RequestOption) (st *Channel, err error) { + body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID), options...) if err != nil { return } @@ -1486,8 +1564,8 @@ func (s *Session) Channel(channelID string) (st *Channel, err error) { // ChannelEdit edits the given channel and returns the updated Channel data. // channelID : The ID of a Channel. // data : New Channel data. -func (s *Session) ChannelEdit(channelID string, data *ChannelEdit) (st *Channel, err error) { - body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID)) +func (s *Session) ChannelEdit(channelID string, data *ChannelEdit, options ...RequestOption) (st *Channel, err error) { + body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID), options...) if err != nil { return } @@ -1501,15 +1579,15 @@ func (s *Session) ChannelEdit(channelID string, data *ChannelEdit) (st *Channel, // NOTE: deprecated, use ChannelEdit instead // channelID : The ID of a Channel // data : The channel struct to send -func (s *Session) ChannelEditComplex(channelID string, data *ChannelEdit) (st *Channel, err error) { - return s.ChannelEdit(channelID, data) +func (s *Session) ChannelEditComplex(channelID string, data *ChannelEdit, options ...RequestOption) (st *Channel, err error) { + return s.ChannelEdit(channelID, data, options...) } // ChannelDelete deletes the given channel // channelID : The ID of a Channel -func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) { +func (s *Session) ChannelDelete(channelID string, options ...RequestOption) (st *Channel, err error) { - body, err := s.RequestWithBucketID("DELETE", EndpointChannel(channelID), nil, EndpointChannel(channelID)) + body, err := s.RequestWithBucketID("DELETE", EndpointChannel(channelID), nil, EndpointChannel(channelID), options...) if err != nil { return } @@ -1521,9 +1599,9 @@ func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) { // ChannelTyping broadcasts to all members that authenticated user is typing in // the given channel. // channelID : The ID of a Channel -func (s *Session) ChannelTyping(channelID string) (err error) { +func (s *Session) ChannelTyping(channelID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("POST", EndpointChannelTyping(channelID), nil, EndpointChannelTyping(channelID)) + _, err = s.RequestWithBucketID("POST", EndpointChannelTyping(channelID), nil, EndpointChannelTyping(channelID), options...) return } @@ -1534,7 +1612,7 @@ func (s *Session) ChannelTyping(channelID string) (err error) { // beforeID : If provided all messages returned will be before given ID. // afterID : If provided all messages returned will be after given ID. // aroundID : If provided all messages returned will be around given ID. -func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID, aroundID string) (st []*Message, err error) { +func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID, aroundID string, options ...RequestOption) (st []*Message, err error) { uri := EndpointChannelMessages(channelID) @@ -1555,7 +1633,7 @@ func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID uri += "?" + v.Encode() } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointChannelMessages(channelID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointChannelMessages(channelID), options...) if err != nil { return } @@ -1567,9 +1645,9 @@ func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID // ChannelMessage gets a single message by ID from a given channel. // channeld : The ID of a Channel // messageID : the ID of a Message -func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err error) { +func (s *Session) ChannelMessage(channelID, messageID string, options ...RequestOption) (st *Message, err error) { - response, err := s.RequestWithBucketID("GET", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, "")) + response, err := s.RequestWithBucketID("GET", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, ""), options...) if err != nil { return } @@ -1581,10 +1659,10 @@ func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err // ChannelMessageSend sends a message to the given channel. // channelID : The ID of a Channel. // content : The message to send. -func (s *Session) ChannelMessageSend(channelID string, content string) (*Message, error) { +func (s *Session) ChannelMessageSend(channelID string, content string, options ...RequestOption) (*Message, error) { return s.ChannelMessageSendComplex(channelID, &MessageSend{ Content: content, - }) + }, options...) } var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") @@ -1592,7 +1670,7 @@ var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") // ChannelMessageSendComplex sends a message to the given channel. // channelID : The ID of a Channel. // data : The message struct to send. -func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend) (st *Message, err error) { +func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend, options ...RequestOption) (st *Message, err error) { // TODO: Remove this when compatibility is not required. if data.Embed != nil { if data.Embeds == nil { @@ -1628,9 +1706,9 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend) return st, encodeErr } - response, err = s.request("POST", endpoint, contentType, body, endpoint, 0) + response, err = s.request("POST", endpoint, contentType, body, endpoint, 0, options...) } else { - response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint) + response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...) } if err != nil { return @@ -1643,63 +1721,63 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend) // ChannelMessageSendTTS sends a message to the given channel with Text to Speech. // channelID : The ID of a Channel. // content : The message to send. -func (s *Session) ChannelMessageSendTTS(channelID string, content string) (*Message, error) { +func (s *Session) ChannelMessageSendTTS(channelID string, content string, options ...RequestOption) (*Message, error) { return s.ChannelMessageSendComplex(channelID, &MessageSend{ Content: content, TTS: true, - }) + }, options...) } // ChannelMessageSendEmbed sends a message to the given channel with embedded data. // channelID : The ID of a Channel. // embed : The embed data to send. -func (s *Session) ChannelMessageSendEmbed(channelID string, embed *MessageEmbed) (*Message, error) { - return s.ChannelMessageSendEmbeds(channelID, []*MessageEmbed{embed}) +func (s *Session) ChannelMessageSendEmbed(channelID string, embed *MessageEmbed, options ...RequestOption) (*Message, error) { + return s.ChannelMessageSendEmbeds(channelID, []*MessageEmbed{embed}, options...) } // ChannelMessageSendEmbeds sends a message to the given channel with multiple embedded data. // channelID : The ID of a Channel. // embeds : The embeds data to send. -func (s *Session) ChannelMessageSendEmbeds(channelID string, embeds []*MessageEmbed) (*Message, error) { +func (s *Session) ChannelMessageSendEmbeds(channelID string, embeds []*MessageEmbed, options ...RequestOption) (*Message, error) { return s.ChannelMessageSendComplex(channelID, &MessageSend{ Embeds: embeds, - }) + }, options...) } // ChannelMessageSendReply sends a message to the given channel with reference data. // channelID : The ID of a Channel. // content : The message to send. // reference : The message reference to send. -func (s *Session) ChannelMessageSendReply(channelID string, content string, reference *MessageReference) (*Message, error) { +func (s *Session) ChannelMessageSendReply(channelID string, content string, reference *MessageReference, options ...RequestOption) (*Message, error) { if reference == nil { return nil, fmt.Errorf("reply attempted with nil message reference") } return s.ChannelMessageSendComplex(channelID, &MessageSend{ Content: content, Reference: reference, - }) + }, options...) } // ChannelMessageSendEmbedReply sends a message to the given channel with reference data and embedded data. // channelID : The ID of a Channel. // embed : The embed data to send. // reference : The message reference to send. -func (s *Session) ChannelMessageSendEmbedReply(channelID string, embed *MessageEmbed, reference *MessageReference) (*Message, error) { - return s.ChannelMessageSendEmbedsReply(channelID, []*MessageEmbed{embed}, reference) +func (s *Session) ChannelMessageSendEmbedReply(channelID string, embed *MessageEmbed, reference *MessageReference, options ...RequestOption) (*Message, error) { + return s.ChannelMessageSendEmbedsReply(channelID, []*MessageEmbed{embed}, reference, options...) } // ChannelMessageSendEmbedsReply sends a message to the given channel with reference data and multiple embedded data. // channelID : The ID of a Channel. // embeds : The embeds data to send. // reference : The message reference to send. -func (s *Session) ChannelMessageSendEmbedsReply(channelID string, embeds []*MessageEmbed, reference *MessageReference) (*Message, error) { +func (s *Session) ChannelMessageSendEmbedsReply(channelID string, embeds []*MessageEmbed, reference *MessageReference, options ...RequestOption) (*Message, error) { if reference == nil { return nil, fmt.Errorf("reply attempted with nil message reference") } return s.ChannelMessageSendComplex(channelID, &MessageSend{ Embeds: embeds, Reference: reference, - }) + }, options...) } // ChannelMessageEdit edits an existing message, replacing it entirely with @@ -1707,13 +1785,13 @@ func (s *Session) ChannelMessageSendEmbedsReply(channelID string, embeds []*Mess // channelID : The ID of a Channel // messageID : The ID of a Message // content : The contents of the message -func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (*Message, error) { - return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetContent(content)) +func (s *Session) ChannelMessageEdit(channelID, messageID, content string, options ...RequestOption) (*Message, error) { + return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetContent(content), options...) } // ChannelMessageEditComplex edits an existing message, replacing it entirely with // the given MessageEdit struct -func (s *Session) ChannelMessageEditComplex(m *MessageEdit) (st *Message, err error) { +func (s *Session) ChannelMessageEditComplex(m *MessageEdit, options ...RequestOption) (st *Message, err error) { // TODO: Remove this when compatibility is not required. if m.Embed != nil { if m.Embeds == nil { @@ -1729,7 +1807,19 @@ func (s *Session) ChannelMessageEditComplex(m *MessageEdit) (st *Message, err er embed.Type = "rich" } } - response, err := s.RequestWithBucketID("PATCH", EndpointChannelMessage(m.Channel, m.ID), m, EndpointChannelMessage(m.Channel, "")) + + endpoint := EndpointChannelMessage(m.Channel, m.ID) + + var response []byte + if len(m.Files) > 0 { + contentType, body, encodeErr := MultipartBodyWithJSON(m, m.Files) + if encodeErr != nil { + return st, encodeErr + } + response, err = s.request("PATCH", endpoint, contentType, body, EndpointChannelMessage(m.Channel, ""), 0, options...) + } else { + response, err = s.RequestWithBucketID("PATCH", endpoint, m, EndpointChannelMessage(m.Channel, ""), options...) + } if err != nil { return } @@ -1742,22 +1832,22 @@ func (s *Session) ChannelMessageEditComplex(m *MessageEdit) (st *Message, err er // channelID : The ID of a Channel // messageID : The ID of a Message // embed : The embed data to send -func (s *Session) ChannelMessageEditEmbed(channelID, messageID string, embed *MessageEmbed) (*Message, error) { - return s.ChannelMessageEditEmbeds(channelID, messageID, []*MessageEmbed{embed}) +func (s *Session) ChannelMessageEditEmbed(channelID, messageID string, embed *MessageEmbed, options ...RequestOption) (*Message, error) { + return s.ChannelMessageEditEmbeds(channelID, messageID, []*MessageEmbed{embed}, options...) } // ChannelMessageEditEmbeds edits an existing message with multiple embedded data. // channelID : The ID of a Channel // messageID : The ID of a Message // embeds : The embeds data to send -func (s *Session) ChannelMessageEditEmbeds(channelID, messageID string, embeds []*MessageEmbed) (*Message, error) { - return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetEmbeds(embeds)) +func (s *Session) ChannelMessageEditEmbeds(channelID, messageID string, embeds []*MessageEmbed, options ...RequestOption) (*Message, error) { + return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetEmbeds(embeds), options...) } // ChannelMessageDelete deletes a message from the Channel. -func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error) { +func (s *Session) ChannelMessageDelete(channelID, messageID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, "")) + _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, ""), options...) return } @@ -1766,14 +1856,14 @@ func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error) // If the slice is empty do nothing. // channelID : The ID of the channel for the messages to delete. // messages : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages. -func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string) (err error) { +func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string, options ...RequestOption) (err error) { if len(messages) == 0 { return } if len(messages) == 1 { - err = s.ChannelMessageDelete(channelID, messages[0]) + err = s.ChannelMessageDelete(channelID, messages[0], options...) return } @@ -1785,34 +1875,34 @@ func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string) Messages []string `json:"messages"` }{messages} - _, err = s.RequestWithBucketID("POST", EndpointChannelMessagesBulkDelete(channelID), data, EndpointChannelMessagesBulkDelete(channelID)) + _, err = s.RequestWithBucketID("POST", EndpointChannelMessagesBulkDelete(channelID), data, EndpointChannelMessagesBulkDelete(channelID), options...) return } // ChannelMessagePin pins a message within a given channel. // channelID: The ID of a channel. // messageID: The ID of a message. -func (s *Session) ChannelMessagePin(channelID, messageID string) (err error) { +func (s *Session) ChannelMessagePin(channelID, messageID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("PUT", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, "")) + _, err = s.RequestWithBucketID("PUT", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, ""), options...) return } // ChannelMessageUnpin unpins a message within a given channel. // channelID: The ID of a channel. // messageID: The ID of a message. -func (s *Session) ChannelMessageUnpin(channelID, messageID string) (err error) { +func (s *Session) ChannelMessageUnpin(channelID, messageID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, "")) + _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, ""), options...) return } // ChannelMessagesPinned returns an array of Message structures for pinned messages // within a given channel // channelID : The ID of a Channel. -func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err error) { +func (s *Session) ChannelMessagesPinned(channelID string, options ...RequestOption) (st []*Message, err error) { - body, err := s.RequestWithBucketID("GET", EndpointChannelMessagesPins(channelID), nil, EndpointChannelMessagesPins(channelID)) + body, err := s.RequestWithBucketID("GET", EndpointChannelMessagesPins(channelID), nil, EndpointChannelMessagesPins(channelID), options...) if err != nil { return @@ -1826,8 +1916,8 @@ func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err er // channelID : The ID of a Channel. // name: The name of the file. // io.Reader : A reader for the file contents. -func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (*Message, error) { - return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}}) +func (s *Session) ChannelFileSend(channelID, name string, r io.Reader, options ...RequestOption) (*Message, error) { + return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}}, options...) } // ChannelFileSendWithMessage sends a file to the given channel with an message. @@ -1836,15 +1926,15 @@ func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (*Message // content: Optional Message content. // name: The name of the file. // io.Reader : A reader for the file contents. -func (s *Session) ChannelFileSendWithMessage(channelID, content string, name string, r io.Reader) (*Message, error) { - return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}, Content: content}) +func (s *Session) ChannelFileSendWithMessage(channelID, content string, name string, r io.Reader, options ...RequestOption) (*Message, error) { + return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}, Content: content}, options...) } // ChannelInvites returns an array of Invite structures for the given channel // channelID : The ID of a Channel -func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) { +func (s *Session) ChannelInvites(channelID string, options ...RequestOption) (st []*Invite, err error) { - body, err := s.RequestWithBucketID("GET", EndpointChannelInvites(channelID), nil, EndpointChannelInvites(channelID)) + body, err := s.RequestWithBucketID("GET", EndpointChannelInvites(channelID), nil, EndpointChannelInvites(channelID), options...) if err != nil { return } @@ -1856,7 +1946,7 @@ func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) { // ChannelInviteCreate creates a new invite for the given channel. // channelID : The ID of a Channel // i : An Invite struct with the values MaxAge, MaxUses and Temporary defined. -func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, err error) { +func (s *Session) ChannelInviteCreate(channelID string, i Invite, options ...RequestOption) (st *Invite, err error) { data := struct { MaxAge int `json:"max_age"` @@ -1865,7 +1955,7 @@ func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, e Unique bool `json:"unique"` }{i.MaxAge, i.MaxUses, i.Temporary, i.Unique} - body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID)) + body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID), options...) if err != nil { return } @@ -1877,7 +1967,7 @@ func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, e // ChannelPermissionSet creates a Permission Override for the given channel. // NOTE: This func name may changed. Using Set instead of Create because // you can both create a new override or update an override with this function. -func (s *Session) ChannelPermissionSet(channelID, targetID string, targetType PermissionOverwriteType, allow, deny int64) (err error) { +func (s *Session) ChannelPermissionSet(channelID, targetID string, targetType PermissionOverwriteType, allow, deny int64, options ...RequestOption) (err error) { data := struct { ID string `json:"id"` @@ -1886,15 +1976,15 @@ func (s *Session) ChannelPermissionSet(channelID, targetID string, targetType Pe Deny int64 `json:"deny,string"` }{targetID, targetType, allow, deny} - _, err = s.RequestWithBucketID("PUT", EndpointChannelPermission(channelID, targetID), data, EndpointChannelPermission(channelID, "")) + _, err = s.RequestWithBucketID("PUT", EndpointChannelPermission(channelID, targetID), data, EndpointChannelPermission(channelID, ""), options...) return } // ChannelPermissionDelete deletes a specific permission override for the given channel. // NOTE: Name of this func may change. -func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error) { +func (s *Session) ChannelPermissionDelete(channelID, targetID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointChannelPermission(channelID, targetID), nil, EndpointChannelPermission(channelID, "")) + _, err = s.RequestWithBucketID("DELETE", EndpointChannelPermission(channelID, targetID), nil, EndpointChannelPermission(channelID, ""), options...) return } @@ -1902,11 +1992,11 @@ func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error // of the channel // channelID : The ID of a Channel // messageID : The ID of a Message -func (s *Session) ChannelMessageCrosspost(channelID, messageID string) (st *Message, err error) { +func (s *Session) ChannelMessageCrosspost(channelID, messageID string, options ...RequestOption) (st *Message, err error) { endpoint := EndpointChannelMessageCrosspost(channelID, messageID) - body, err := s.RequestWithBucketID("POST", endpoint, nil, endpoint) + body, err := s.RequestWithBucketID("POST", endpoint, nil, endpoint, options...) if err != nil { return } @@ -1918,7 +2008,7 @@ func (s *Session) ChannelMessageCrosspost(channelID, messageID string) (st *Mess // ChannelNewsFollow follows a news channel in the targetID // channelID : The ID of a News Channel // targetID : The ID of a Channel where the News Channel should post to -func (s *Session) ChannelNewsFollow(channelID, targetID string) (st *ChannelFollow, err error) { +func (s *Session) ChannelNewsFollow(channelID, targetID string, options ...RequestOption) (st *ChannelFollow, err error) { endpoint := EndpointChannelFollow(channelID) @@ -1926,7 +2016,7 @@ func (s *Session) ChannelNewsFollow(channelID, targetID string) (st *ChannelFoll WebhookChannelID string `json:"webhook_channel_id"` }{targetID} - body, err := s.RequestWithBucketID("POST", endpoint, data, endpoint) + body, err := s.RequestWithBucketID("POST", endpoint, data, endpoint, options...) if err != nil { return } @@ -1941,9 +2031,9 @@ func (s *Session) ChannelNewsFollow(channelID, targetID string) (st *ChannelFoll // Invite returns an Invite structure of the given invite // inviteID : The invite code -func (s *Session) Invite(inviteID string) (st *Invite, err error) { +func (s *Session) Invite(inviteID string, options ...RequestOption) (st *Invite, err error) { - body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite("")) + body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite(""), options...) if err != nil { return } @@ -1954,9 +2044,9 @@ func (s *Session) Invite(inviteID string) (st *Invite, err error) { // InviteWithCounts returns an Invite structure of the given invite including approximate member counts // inviteID : The invite code -func (s *Session) InviteWithCounts(inviteID string) (st *Invite, err error) { +func (s *Session) InviteWithCounts(inviteID string, options ...RequestOption) (st *Invite, err error) { - body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID)+"?with_counts=true", nil, EndpointInvite("")) + body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID)+"?with_counts=true", nil, EndpointInvite(""), options...) if err != nil { return } @@ -1966,11 +2056,11 @@ func (s *Session) InviteWithCounts(inviteID string) (st *Invite, err error) { } // InviteComplex returns an Invite structure of the given invite including specified fields. -// inviteID : The invite code -// guildScheduledEventID : If specified, includes specified guild scheduled event. -// withCounts : Whether to include approximate member counts or not -// withExpiration : Whether to include expiration time or not -func (s *Session) InviteComplex(inviteID, guildScheduledEventID string, withCounts, withExpiration bool) (st *Invite, err error) { +// inviteID : The invite code +// guildScheduledEventID : If specified, includes specified guild scheduled event. +// withCounts : Whether to include approximate member counts or not +// withExpiration : Whether to include expiration time or not +func (s *Session) InviteComplex(inviteID, guildScheduledEventID string, withCounts, withExpiration bool, options ...RequestOption) (st *Invite, err error) { endpoint := EndpointInvite(inviteID) v := url.Values{} if guildScheduledEventID != "" { @@ -1987,7 +2077,7 @@ func (s *Session) InviteComplex(inviteID, guildScheduledEventID string, withCoun endpoint += "?" + v.Encode() } - body, err := s.RequestWithBucketID("GET", endpoint, nil, EndpointInvite("")) + body, err := s.RequestWithBucketID("GET", endpoint, nil, EndpointInvite(""), options...) if err != nil { return } @@ -1998,9 +2088,9 @@ func (s *Session) InviteComplex(inviteID, guildScheduledEventID string, withCoun // InviteDelete deletes an existing invite // inviteID : the code of an invite -func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) { +func (s *Session) InviteDelete(inviteID string, options ...RequestOption) (st *Invite, err error) { - body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite("")) + body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite(""), options...) if err != nil { return } @@ -2011,9 +2101,9 @@ func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) { // InviteAccept accepts an Invite to a Guild or Channel // inviteID : The invite code -func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) { +func (s *Session) InviteAccept(inviteID string, options ...RequestOption) (st *Invite, err error) { - body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite("")) + body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite(""), options...) if err != nil { return } @@ -2027,9 +2117,9 @@ func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) { // ------------------------------------------------------------------------------------------------ // VoiceRegions returns the voice server regions -func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) { +func (s *Session) VoiceRegions(options ...RequestOption) (st []*VoiceRegion, err error) { - body, err := s.RequestWithBucketID("GET", EndpointVoiceRegions, nil, EndpointVoiceRegions) + body, err := s.RequestWithBucketID("GET", EndpointVoiceRegions, nil, EndpointVoiceRegions, options...) if err != nil { return } @@ -2043,9 +2133,9 @@ func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) { // ------------------------------------------------------------------------------------------------ // Gateway returns the websocket Gateway address -func (s *Session) Gateway() (gateway string, err error) { +func (s *Session) Gateway(options ...RequestOption) (gateway string, err error) { - response, err := s.RequestWithBucketID("GET", EndpointGateway, nil, EndpointGateway) + response, err := s.RequestWithBucketID("GET", EndpointGateway, nil, EndpointGateway, options...) if err != nil { return } @@ -2071,9 +2161,9 @@ func (s *Session) Gateway() (gateway string, err error) { } // GatewayBot returns the websocket Gateway address and the recommended number of shards -func (s *Session) GatewayBot() (st *GatewayBotResponse, err error) { +func (s *Session) GatewayBot(options ...RequestOption) (st *GatewayBotResponse, err error) { - response, err := s.RequestWithBucketID("GET", EndpointGatewayBot, nil, EndpointGatewayBot) + response, err := s.RequestWithBucketID("GET", EndpointGatewayBot, nil, EndpointGatewayBot, options...) if err != nil { return } @@ -2098,14 +2188,14 @@ func (s *Session) GatewayBot() (st *GatewayBotResponse, err error) { // channelID: The ID of a Channel. // name : The name of the webhook. // avatar : The avatar of the webhook. -func (s *Session) WebhookCreate(channelID, name, avatar string) (st *Webhook, err error) { +func (s *Session) WebhookCreate(channelID, name, avatar string, options ...RequestOption) (st *Webhook, err error) { data := struct { Name string `json:"name"` Avatar string `json:"avatar,omitempty"` }{name, avatar} - body, err := s.RequestWithBucketID("POST", EndpointChannelWebhooks(channelID), data, EndpointChannelWebhooks(channelID)) + body, err := s.RequestWithBucketID("POST", EndpointChannelWebhooks(channelID), data, EndpointChannelWebhooks(channelID), options...) if err != nil { return } @@ -2117,9 +2207,9 @@ func (s *Session) WebhookCreate(channelID, name, avatar string) (st *Webhook, er // ChannelWebhooks returns all webhooks for a given channel. // channelID: The ID of a channel. -func (s *Session) ChannelWebhooks(channelID string) (st []*Webhook, err error) { +func (s *Session) ChannelWebhooks(channelID string, options ...RequestOption) (st []*Webhook, err error) { - body, err := s.RequestWithBucketID("GET", EndpointChannelWebhooks(channelID), nil, EndpointChannelWebhooks(channelID)) + body, err := s.RequestWithBucketID("GET", EndpointChannelWebhooks(channelID), nil, EndpointChannelWebhooks(channelID), options...) if err != nil { return } @@ -2131,9 +2221,9 @@ func (s *Session) ChannelWebhooks(channelID string) (st []*Webhook, err error) { // GuildWebhooks returns all webhooks for a given guild. // guildID: The ID of a Guild. -func (s *Session) GuildWebhooks(guildID string) (st []*Webhook, err error) { +func (s *Session) GuildWebhooks(guildID string, options ...RequestOption) (st []*Webhook, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildWebhooks(guildID), nil, EndpointGuildWebhooks(guildID)) + body, err := s.RequestWithBucketID("GET", EndpointGuildWebhooks(guildID), nil, EndpointGuildWebhooks(guildID), options...) if err != nil { return } @@ -2145,9 +2235,9 @@ func (s *Session) GuildWebhooks(guildID string) (st []*Webhook, err error) { // Webhook returns a webhook for a given ID // webhookID: The ID of a webhook. -func (s *Session) Webhook(webhookID string) (st *Webhook, err error) { +func (s *Session) Webhook(webhookID string, options ...RequestOption) (st *Webhook, err error) { - body, err := s.RequestWithBucketID("GET", EndpointWebhook(webhookID), nil, EndpointWebhooks) + body, err := s.RequestWithBucketID("GET", EndpointWebhook(webhookID), nil, EndpointWebhooks, options...) if err != nil { return } @@ -2160,9 +2250,9 @@ func (s *Session) Webhook(webhookID string) (st *Webhook, err error) { // WebhookWithToken returns a webhook for a given ID // webhookID: The ID of a webhook. // token : The auth token for the webhook. -func (s *Session) WebhookWithToken(webhookID, token string) (st *Webhook, err error) { +func (s *Session) WebhookWithToken(webhookID, token string, options ...RequestOption) (st *Webhook, err error) { - body, err := s.RequestWithBucketID("GET", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", "")) + body, err := s.RequestWithBucketID("GET", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", ""), options...) if err != nil { return } @@ -2176,7 +2266,7 @@ func (s *Session) WebhookWithToken(webhookID, token string) (st *Webhook, err er // webhookID: The ID of a webhook. // name : The name of the webhook. // avatar : The avatar of the webhook. -func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string) (st *Role, err error) { +func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string, options ...RequestOption) (st *Role, err error) { data := struct { Name string `json:"name,omitempty"` @@ -2184,7 +2274,7 @@ func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string) (st *Ro ChannelID string `json:"channel_id,omitempty"` }{name, avatar, channelID} - body, err := s.RequestWithBucketID("PATCH", EndpointWebhook(webhookID), data, EndpointWebhooks) + body, err := s.RequestWithBucketID("PATCH", EndpointWebhook(webhookID), data, EndpointWebhooks, options...) if err != nil { return } @@ -2199,14 +2289,14 @@ func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string) (st *Ro // token : The auth token for the webhook. // name : The name of the webhook. // avatar : The avatar of the webhook. -func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string) (st *Role, err error) { +func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string, options ...RequestOption) (st *Role, err error) { data := struct { Name string `json:"name,omitempty"` Avatar string `json:"avatar,omitempty"` }{name, avatar} - body, err := s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", "")) + body, err := s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", ""), options...) if err != nil { return } @@ -2218,9 +2308,9 @@ func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string) (s // WebhookDelete deletes a webhook for a given ID // webhookID: The ID of a webhook. -func (s *Session) WebhookDelete(webhookID string) (err error) { +func (s *Session) WebhookDelete(webhookID string, options ...RequestOption) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks) + _, err = s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks, options...) return } @@ -2228,9 +2318,9 @@ func (s *Session) WebhookDelete(webhookID string) (err error) { // WebhookDeleteWithToken deletes a webhook for a given ID with an auth token. // webhookID: The ID of a webhook. // token : The auth token for the webhook. -func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook, err error) { +func (s *Session) WebhookDeleteWithToken(webhookID, token string, options ...RequestOption) (st *Webhook, err error) { - body, err := s.RequestWithBucketID("DELETE", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", "")) + body, err := s.RequestWithBucketID("DELETE", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", ""), options...) if err != nil { return } @@ -2240,7 +2330,7 @@ func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook, return } -func (s *Session) webhookExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams) (st *Message, err error) { +func (s *Session) webhookExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams, options ...RequestOption) (st *Message, err error) { uri := EndpointWebhookToken(webhookID, token) v := url.Values{} @@ -2262,9 +2352,9 @@ func (s *Session) webhookExecute(webhookID, token string, wait bool, threadID st return st, encodeErr } - response, err = s.request("POST", uri, contentType, body, uri, 0) + response, err = s.request("POST", uri, contentType, body, uri, 0, options...) } else { - response, err = s.RequestWithBucketID("POST", uri, data, uri) + response, err = s.RequestWithBucketID("POST", uri, data, uri, options...) } if !wait || err != nil { return @@ -2278,8 +2368,8 @@ func (s *Session) webhookExecute(webhookID, token string, wait bool, threadID st // webhookID: The ID of a webhook. // token : The auth token for the webhook // wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) -func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams) (st *Message, err error) { - return s.webhookExecute(webhookID, token, wait, "", data) +func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams, options ...RequestOption) (st *Message, err error) { + return s.webhookExecute(webhookID, token, wait, "", data, options...) } // WebhookThreadExecute executes a webhook in a thread. @@ -2287,18 +2377,18 @@ func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *Webho // token : The auth token for the webhook // wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) // threadID : Sends a message to the specified thread within a webhook's channel. The thread will automatically be unarchived. -func (s *Session) WebhookThreadExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams) (st *Message, err error) { - return s.webhookExecute(webhookID, token, wait, threadID, data) +func (s *Session) WebhookThreadExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams, options ...RequestOption) (st *Message, err error) { + return s.webhookExecute(webhookID, token, wait, threadID, data, options...) } // WebhookMessage gets a webhook message. // webhookID : The ID of a webhook // token : The auth token for the webhook // messageID : The ID of message to get -func (s *Session) WebhookMessage(webhookID, token, messageID string) (message *Message, err error) { +func (s *Session) WebhookMessage(webhookID, token, messageID string, options ...RequestOption) (message *Message, err error) { uri := EndpointWebhookMessage(webhookID, token, messageID) - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointWebhookToken("", "")) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointWebhookToken("", ""), options...) if err != nil { return } @@ -2312,7 +2402,7 @@ func (s *Session) WebhookMessage(webhookID, token, messageID string) (message *M // webhookID : The ID of a webhook // token : The auth token for the webhook // messageID : The ID of message to edit -func (s *Session) WebhookMessageEdit(webhookID, token, messageID string, data *WebhookEdit) (st *Message, err error) { +func (s *Session) WebhookMessageEdit(webhookID, token, messageID string, data *WebhookEdit, options ...RequestOption) (st *Message, err error) { uri := EndpointWebhookMessage(webhookID, token, messageID) var response []byte @@ -2322,12 +2412,12 @@ func (s *Session) WebhookMessageEdit(webhookID, token, messageID string, data *W return nil, err } - response, err = s.request("PATCH", uri, contentType, body, uri, 0) + response, err = s.request("PATCH", uri, contentType, body, uri, 0, options...) if err != nil { return nil, err } } else { - response, err = s.RequestWithBucketID("PATCH", uri, data, EndpointWebhookToken("", "")) + response, err = s.RequestWithBucketID("PATCH", uri, data, EndpointWebhookToken("", ""), options...) if err != nil { return nil, err @@ -2342,10 +2432,10 @@ func (s *Session) WebhookMessageEdit(webhookID, token, messageID string, data *W // webhookID : The ID of a webhook // token : The auth token for the webhook // messageID : The ID of a message to edit -func (s *Session) WebhookMessageDelete(webhookID, token, messageID string) (err error) { +func (s *Session) WebhookMessageDelete(webhookID, token, messageID string, options ...RequestOption) (err error) { uri := EndpointWebhookMessage(webhookID, token, messageID) - _, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointWebhookToken("", "")) + _, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointWebhookToken("", ""), options...) return } @@ -2353,11 +2443,11 @@ func (s *Session) WebhookMessageDelete(webhookID, token, messageID string) (err // channelID : The channel ID. // messageID : The message ID. // emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier in name:id format (e.g. "hello:1234567654321") -func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string) error { +func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string, options ...RequestOption) error { // emoji such as #⃣ need to have # escaped emojiID = strings.Replace(emojiID, "#", "%23", -1) - _, err := s.RequestWithBucketID("PUT", EndpointMessageReaction(channelID, messageID, emojiID, "@me"), nil, EndpointMessageReaction(channelID, "", "", "")) + _, err := s.RequestWithBucketID("PUT", EndpointMessageReaction(channelID, messageID, emojiID, "@me"), nil, EndpointMessageReaction(channelID, "", "", ""), options...) return err } @@ -2367,11 +2457,11 @@ func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string) error // messageID : The message ID. // emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier. // userID : @me or ID of the user to delete the reaction for. -func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID string) error { +func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID string, options ...RequestOption) error { // emoji such as #⃣ need to have # escaped emojiID = strings.Replace(emojiID, "#", "%23", -1) - _, err := s.RequestWithBucketID("DELETE", EndpointMessageReaction(channelID, messageID, emojiID, userID), nil, EndpointMessageReaction(channelID, "", "", "")) + _, err := s.RequestWithBucketID("DELETE", EndpointMessageReaction(channelID, messageID, emojiID, userID), nil, EndpointMessageReaction(channelID, "", "", ""), options...) return err } @@ -2379,9 +2469,9 @@ func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID st // MessageReactionsRemoveAll deletes all reactions from a message // channelID : The channel ID // messageID : The message ID. -func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error { +func (s *Session) MessageReactionsRemoveAll(channelID, messageID string, options ...RequestOption) error { - _, err := s.RequestWithBucketID("DELETE", EndpointMessageReactionsAll(channelID, messageID), nil, EndpointMessageReactionsAll(channelID, messageID)) + _, err := s.RequestWithBucketID("DELETE", EndpointMessageReactionsAll(channelID, messageID), nil, EndpointMessageReactionsAll(channelID, messageID), options...) return err } @@ -2390,11 +2480,11 @@ func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error { // channelID : The channel ID // messageID : The message ID // emojiID : The emoji ID -func (s *Session) MessageReactionsRemoveEmoji(channelID, messageID, emojiID string) error { +func (s *Session) MessageReactionsRemoveEmoji(channelID, messageID, emojiID string, options ...RequestOption) error { // emoji such as #⃣ need to have # escaped emojiID = strings.Replace(emojiID, "#", "%23", -1) - _, err := s.RequestWithBucketID("DELETE", EndpointMessageReactions(channelID, messageID, emojiID), nil, EndpointMessageReactions(channelID, messageID, emojiID)) + _, err := s.RequestWithBucketID("DELETE", EndpointMessageReactions(channelID, messageID, emojiID), nil, EndpointMessageReactions(channelID, messageID, emojiID), options...) return err } @@ -2406,7 +2496,7 @@ func (s *Session) MessageReactionsRemoveEmoji(channelID, messageID, emojiID stri // limit : max number of users to return (max 100) // beforeID : If provided all reactions returned will be before given ID. // afterID : If provided all reactions returned will be after given ID. -func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int, beforeID, afterID string) (st []*User, err error) { +func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int, beforeID, afterID string, options ...RequestOption) (st []*User, err error) { // emoji such as #⃣ need to have # escaped emojiID = strings.Replace(emojiID, "#", "%23", -1) uri := EndpointMessageReactions(channelID, messageID, emojiID) @@ -2428,7 +2518,7 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i uri += "?" + v.Encode() } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointMessageReaction(channelID, "", "", "")) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointMessageReaction(channelID, "", "", ""), options...) if err != nil { return } @@ -2445,10 +2535,10 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i // channelID : Channel to create thread in // messageID : Message to start thread from // data : Parameters of the thread -func (s *Session) MessageThreadStartComplex(channelID, messageID string, data *ThreadStart) (ch *Channel, err error) { +func (s *Session) MessageThreadStartComplex(channelID, messageID string, data *ThreadStart, options ...RequestOption) (ch *Channel, err error) { endpoint := EndpointChannelMessageThread(channelID, messageID) var body []byte - body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint) + body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...) if err != nil { return } @@ -2462,20 +2552,20 @@ func (s *Session) MessageThreadStartComplex(channelID, messageID string, data *T // messageID : Message to start thread from // name : Name of the thread // archiveDuration : Auto archive duration (in minutes) -func (s *Session) MessageThreadStart(channelID, messageID string, name string, archiveDuration int) (ch *Channel, err error) { +func (s *Session) MessageThreadStart(channelID, messageID string, name string, archiveDuration int, options ...RequestOption) (ch *Channel, err error) { return s.MessageThreadStartComplex(channelID, messageID, &ThreadStart{ Name: name, AutoArchiveDuration: archiveDuration, - }) + }, options...) } // ThreadStartComplex creates a new thread. // channelID : Channel to create thread in // data : Parameters of the thread -func (s *Session) ThreadStartComplex(channelID string, data *ThreadStart) (ch *Channel, err error) { +func (s *Session) ThreadStartComplex(channelID string, data *ThreadStart, options ...RequestOption) (ch *Channel, err error) { endpoint := EndpointChannelThreads(channelID) var body []byte - body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint) + body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...) if err != nil { return } @@ -2488,47 +2578,141 @@ func (s *Session) ThreadStartComplex(channelID string, data *ThreadStart) (ch *C // channelID : Channel to create thread in // name : Name of the thread // archiveDuration : Auto archive duration (in minutes) -func (s *Session) ThreadStart(channelID, name string, typ ChannelType, archiveDuration int) (ch *Channel, err error) { +func (s *Session) ThreadStart(channelID, name string, typ ChannelType, archiveDuration int, options ...RequestOption) (ch *Channel, err error) { return s.ThreadStartComplex(channelID, &ThreadStart{ Name: name, Type: typ, AutoArchiveDuration: archiveDuration, - }) + }, options...) +} + +// ForumThreadStartComplex starts a new thread (creates a post) in a forum channel. +// channelID : Channel to create thread in. +// threadData : Parameters of the thread. +// messageData : Parameters of the starting message. +func (s *Session) ForumThreadStartComplex(channelID string, threadData *ThreadStart, messageData *MessageSend, options ...RequestOption) (th *Channel, err error) { + endpoint := EndpointChannelThreads(channelID) + + // TODO: Remove this when compatibility is not required. + if messageData.Embed != nil { + if messageData.Embeds == nil { + messageData.Embeds = []*MessageEmbed{messageData.Embed} + } else { + err = fmt.Errorf("cannot specify both Embed and Embeds") + return + } + } + + for _, embed := range messageData.Embeds { + if embed.Type == "" { + embed.Type = "rich" + } + } + + // TODO: Remove this when compatibility is not required. + files := messageData.Files + if messageData.File != nil { + if files == nil { + files = []*File{messageData.File} + } else { + err = fmt.Errorf("cannot specify both File and Files") + return + } + } + + data := struct { + *ThreadStart + Message *MessageSend `json:"message"` + }{ThreadStart: threadData, Message: messageData} + + var response []byte + if len(files) > 0 { + contentType, body, encodeErr := MultipartBodyWithJSON(data, files) + if encodeErr != nil { + return th, encodeErr + } + + response, err = s.request("POST", endpoint, contentType, body, endpoint, 0, options...) + } else { + response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...) + } + if err != nil { + return + } + + err = unmarshal(response, &th) + return +} + +// ForumThreadStart starts a new thread (post) in a forum channel. +// channelID : Channel to create thread in. +// name : Name of the thread. +// archiveDuration : Auto archive duration. +// content : Content of the starting message. +func (s *Session) ForumThreadStart(channelID, name string, archiveDuration int, content string, options ...RequestOption) (th *Channel, err error) { + return s.ForumThreadStartComplex(channelID, &ThreadStart{ + Name: name, + AutoArchiveDuration: archiveDuration, + }, &MessageSend{Content: content}, options...) +} + +// ForumThreadStartEmbed starts a new thread (post) in a forum channel. +// channelID : Channel to create thread in. +// name : Name of the thread. +// archiveDuration : Auto archive duration. +// embed : Embed data of the starting message. +func (s *Session) ForumThreadStartEmbed(channelID, name string, archiveDuration int, embed *MessageEmbed, options ...RequestOption) (th *Channel, err error) { + return s.ForumThreadStartComplex(channelID, &ThreadStart{ + Name: name, + AutoArchiveDuration: archiveDuration, + }, &MessageSend{Embeds: []*MessageEmbed{embed}}, options...) +} + +// ForumThreadStartEmbeds starts a new thread (post) in a forum channel. +// channelID : Channel to create thread in. +// name : Name of the thread. +// archiveDuration : Auto archive duration. +// embeds : Embeds data of the starting message. +func (s *Session) ForumThreadStartEmbeds(channelID, name string, archiveDuration int, embeds []*MessageEmbed, options ...RequestOption) (th *Channel, err error) { + return s.ForumThreadStartComplex(channelID, &ThreadStart{ + Name: name, + AutoArchiveDuration: archiveDuration, + }, &MessageSend{Embeds: embeds}, options...) } // ThreadJoin adds current user to a thread -func (s *Session) ThreadJoin(id string) error { +func (s *Session) ThreadJoin(id string, options ...RequestOption) error { endpoint := EndpointThreadMember(id, "@me") - _, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint, options...) return err } // ThreadLeave removes current user to a thread -func (s *Session) ThreadLeave(id string) error { +func (s *Session) ThreadLeave(id string, options ...RequestOption) error { endpoint := EndpointThreadMember(id, "@me") - _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint, options...) return err } // ThreadMemberAdd adds another member to a thread -func (s *Session) ThreadMemberAdd(threadID, memberID string) error { +func (s *Session) ThreadMemberAdd(threadID, memberID string, options ...RequestOption) error { endpoint := EndpointThreadMember(threadID, memberID) - _, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint, options...) return err } // ThreadMemberRemove removes another member from a thread -func (s *Session) ThreadMemberRemove(threadID, memberID string) error { +func (s *Session) ThreadMemberRemove(threadID, memberID string, options ...RequestOption) error { endpoint := EndpointThreadMember(threadID, memberID) - _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint, options...) return err } // ThreadMember returns thread member object for the specified member of a thread -func (s *Session) ThreadMember(threadID, memberID string) (member *ThreadMember, err error) { +func (s *Session) ThreadMember(threadID, memberID string, options ...RequestOption) (member *ThreadMember, err error) { endpoint := EndpointThreadMember(threadID, memberID) var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return @@ -2539,9 +2723,9 @@ func (s *Session) ThreadMember(threadID, memberID string) (member *ThreadMember, } // ThreadMembers returns all members of specified thread. -func (s *Session) ThreadMembers(threadID string) (members []*ThreadMember, err error) { +func (s *Session) ThreadMembers(threadID string, options ...RequestOption) (members []*ThreadMember, err error) { var body []byte - body, err = s.RequestWithBucketID("GET", EndpointThreadMembers(threadID), nil, EndpointThreadMembers(threadID)) + body, err = s.RequestWithBucketID("GET", EndpointThreadMembers(threadID), nil, EndpointThreadMembers(threadID), options...) if err != nil { return @@ -2552,9 +2736,9 @@ func (s *Session) ThreadMembers(threadID string) (members []*ThreadMember, err e } // ThreadsActive returns all active threads for specified channel. -func (s *Session) ThreadsActive(channelID string) (threads *ThreadsList, err error) { +func (s *Session) ThreadsActive(channelID string, options ...RequestOption) (threads *ThreadsList, err error) { var body []byte - body, err = s.RequestWithBucketID("GET", EndpointChannelActiveThreads(channelID), nil, EndpointChannelActiveThreads(channelID)) + body, err = s.RequestWithBucketID("GET", EndpointChannelActiveThreads(channelID), nil, EndpointChannelActiveThreads(channelID), options...) if err != nil { return } @@ -2564,9 +2748,9 @@ func (s *Session) ThreadsActive(channelID string) (threads *ThreadsList, err err } // GuildThreadsActive returns all active threads for specified guild. -func (s *Session) GuildThreadsActive(guildID string) (threads *ThreadsList, err error) { +func (s *Session) GuildThreadsActive(guildID string, options ...RequestOption) (threads *ThreadsList, err error) { var body []byte - body, err = s.RequestWithBucketID("GET", EndpointGuildActiveThreads(guildID), nil, EndpointGuildActiveThreads(guildID)) + body, err = s.RequestWithBucketID("GET", EndpointGuildActiveThreads(guildID), nil, EndpointGuildActiveThreads(guildID), options...) if err != nil { return } @@ -2578,7 +2762,7 @@ func (s *Session) GuildThreadsActive(guildID string) (threads *ThreadsList, err // ThreadsArchived returns archived threads for specified channel. // before : If specified returns only threads before the timestamp // limit : Optional maximum amount of threads to return. -func (s *Session) ThreadsArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) { +func (s *Session) ThreadsArchived(channelID string, before *time.Time, limit int, options ...RequestOption) (threads *ThreadsList, err error) { endpoint := EndpointChannelPublicArchivedThreads(channelID) v := url.Values{} if before != nil { @@ -2594,7 +2778,7 @@ func (s *Session) ThreadsArchived(channelID string, before *time.Time, limit int } var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -2606,7 +2790,7 @@ func (s *Session) ThreadsArchived(channelID string, before *time.Time, limit int // ThreadsPrivateArchived returns archived private threads for specified channel. // before : If specified returns only threads before the timestamp // limit : Optional maximum amount of threads to return. -func (s *Session) ThreadsPrivateArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) { +func (s *Session) ThreadsPrivateArchived(channelID string, before *time.Time, limit int, options ...RequestOption) (threads *ThreadsList, err error) { endpoint := EndpointChannelPrivateArchivedThreads(channelID) v := url.Values{} if before != nil { @@ -2621,7 +2805,7 @@ func (s *Session) ThreadsPrivateArchived(channelID string, before *time.Time, li endpoint += "?" + v.Encode() } var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -2633,7 +2817,7 @@ func (s *Session) ThreadsPrivateArchived(channelID string, before *time.Time, li // ThreadsPrivateJoinedArchived returns archived joined private threads for specified channel. // before : If specified returns only threads before the timestamp // limit : Optional maximum amount of threads to return. -func (s *Session) ThreadsPrivateJoinedArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) { +func (s *Session) ThreadsPrivateJoinedArchived(channelID string, before *time.Time, limit int, options ...RequestOption) (threads *ThreadsList, err error) { endpoint := EndpointChannelJoinedPrivateArchivedThreads(channelID) v := url.Values{} if before != nil { @@ -2648,7 +2832,7 @@ func (s *Session) ThreadsPrivateJoinedArchived(channelID string, before *time.Ti endpoint += "?" + v.Encode() } var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -2665,13 +2849,13 @@ func (s *Session) ThreadsPrivateJoinedArchived(channelID string, before *time.Ti // appID : The application ID. // guildID : Guild ID to create guild-specific application command. If empty - creates global application command. // cmd : New application command data. -func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (ccmd *ApplicationCommand, err error) { +func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand, options ...RequestOption) (ccmd *ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { endpoint = EndpointApplicationGuildCommands(appID, guildID) } - body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) + body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint, options...) if err != nil { return } @@ -2686,13 +2870,13 @@ func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *Ap // cmdID : Application command ID to edit. // guildID : Guild ID to edit guild-specific application command. If empty - edits global application command. // cmd : Updated application command data. -func (s *Session) ApplicationCommandEdit(appID, guildID, cmdID string, cmd *ApplicationCommand) (updated *ApplicationCommand, err error) { +func (s *Session) ApplicationCommandEdit(appID, guildID, cmdID string, cmd *ApplicationCommand, options ...RequestOption) (updated *ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) + body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint, options...) if err != nil { return } @@ -2705,13 +2889,13 @@ func (s *Session) ApplicationCommandEdit(appID, guildID, cmdID string, cmd *Appl // ApplicationCommandBulkOverwrite Creates commands overwriting existing commands. Returns a list of commands. // appID : The application ID. // commands : The commands to create. -func (s *Session) ApplicationCommandBulkOverwrite(appID string, guildID string, commands []*ApplicationCommand) (createdCommands []*ApplicationCommand, err error) { +func (s *Session) ApplicationCommandBulkOverwrite(appID string, guildID string, commands []*ApplicationCommand, options ...RequestOption) (createdCommands []*ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { endpoint = EndpointApplicationGuildCommands(appID, guildID) } - body, err := s.RequestWithBucketID("PUT", endpoint, commands, endpoint) + body, err := s.RequestWithBucketID("PUT", endpoint, commands, endpoint, options...) if err != nil { return } @@ -2725,13 +2909,13 @@ func (s *Session) ApplicationCommandBulkOverwrite(appID string, guildID string, // appID : The application ID. // cmdID : Application command ID to delete. // guildID : Guild ID to delete guild-specific application command. If empty - deletes global application command. -func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { +func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string, options ...RequestOption) error { endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint, options...) return err } @@ -2740,13 +2924,13 @@ func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { // appID : The application ID. // cmdID : Application command ID. // guildID : Guild ID to retrieve guild-specific application command. If empty - retrieves global application command. -func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *ApplicationCommand, err error) { +func (s *Session) ApplicationCommand(appID, guildID, cmdID string, options ...RequestOption) (cmd *ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommand(appID, cmdID) if guildID != "" { endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) } - body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -2759,13 +2943,13 @@ func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *Applica // ApplicationCommands retrieves all commands in application. // appID : The application ID. // guildID : Guild ID to retrieve all guild-specific application commands. If empty - retrieves global application commands. -func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*ApplicationCommand, err error) { +func (s *Session) ApplicationCommands(appID, guildID string, options ...RequestOption) (cmd []*ApplicationCommand, err error) { endpoint := EndpointApplicationGlobalCommands(appID) if guildID != "" { endpoint = EndpointApplicationGuildCommands(appID, guildID) } - body, err := s.RequestWithBucketID("GET", endpoint+"?with_localizations=true", nil, "GET "+endpoint) + body, err := s.RequestWithBucketID("GET", endpoint+"?with_localizations=true", nil, "GET "+endpoint, options...) if err != nil { return } @@ -2778,11 +2962,11 @@ func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*Application // GuildApplicationCommandsPermissions returns permissions for application commands in a guild. // appID : The application ID // guildID : Guild ID to retrieve application commands permissions for. -func (s *Session) GuildApplicationCommandsPermissions(appID, guildID string) (permissions []*GuildApplicationCommandPermissions, err error) { +func (s *Session) GuildApplicationCommandsPermissions(appID, guildID string, options ...RequestOption) (permissions []*GuildApplicationCommandPermissions, err error) { endpoint := EndpointApplicationCommandsGuildPermissions(appID, guildID) var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -2795,11 +2979,11 @@ func (s *Session) GuildApplicationCommandsPermissions(appID, guildID string) (pe // appID : The Application ID // guildID : The guild ID containing the application command // cmdID : The command ID to retrieve the permissions of -func (s *Session) ApplicationCommandPermissions(appID, guildID, cmdID string) (permissions *GuildApplicationCommandPermissions, err error) { +func (s *Session) ApplicationCommandPermissions(appID, guildID, cmdID string, options ...RequestOption) (permissions *GuildApplicationCommandPermissions, err error) { endpoint := EndpointApplicationCommandPermissions(appID, guildID, cmdID) var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -2815,10 +2999,10 @@ func (s *Session) ApplicationCommandPermissions(appID, guildID, cmdID string) (p // permissions : An object containing a list of permissions for the application command // // NOTE: Requires OAuth2 token with applications.commands.permissions.update scope -func (s *Session) ApplicationCommandPermissionsEdit(appID, guildID, cmdID string, permissions *ApplicationCommandPermissionsList) (err error) { +func (s *Session) ApplicationCommandPermissionsEdit(appID, guildID, cmdID string, permissions *ApplicationCommandPermissionsList, options ...RequestOption) (err error) { endpoint := EndpointApplicationCommandPermissions(appID, guildID, cmdID) - _, err = s.RequestWithBucketID("PUT", endpoint, permissions, endpoint) + _, err = s.RequestWithBucketID("PUT", endpoint, permissions, endpoint, options...) return } @@ -2828,17 +3012,17 @@ func (s *Session) ApplicationCommandPermissionsEdit(appID, guildID, cmdID string // permissions : A list of permissions paired with a command ID, guild ID, and application ID per application command // // NOTE: This endpoint has been disabled with updates to command permissions (Permissions v2). Please use ApplicationCommandPermissionsEdit instead. -func (s *Session) ApplicationCommandPermissionsBatchEdit(appID, guildID string, permissions []*GuildApplicationCommandPermissions) (err error) { +func (s *Session) ApplicationCommandPermissionsBatchEdit(appID, guildID string, permissions []*GuildApplicationCommandPermissions, options ...RequestOption) (err error) { endpoint := EndpointApplicationCommandsGuildPermissions(appID, guildID) - _, err = s.RequestWithBucketID("PUT", endpoint, permissions, endpoint) + _, err = s.RequestWithBucketID("PUT", endpoint, permissions, endpoint, options...) return } // InteractionRespond creates the response to an interaction. // interaction : Interaction instance. // resp : Response message data. -func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) error { +func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse, options ...RequestOption) error { endpoint := EndpointInteractionResponse(interaction.ID, interaction.Token) if resp.Data != nil && len(resp.Data.Files) > 0 { @@ -2847,33 +3031,33 @@ func (s *Session) InteractionRespond(interaction *Interaction, resp *Interaction return err } - _, err = s.request("POST", endpoint, contentType, body, endpoint, 0) + _, err = s.request("POST", endpoint, contentType, body, endpoint, 0, options...) return err } - _, err := s.RequestWithBucketID("POST", endpoint, *resp, endpoint) + _, err := s.RequestWithBucketID("POST", endpoint, *resp, endpoint, options...) return err } // InteractionResponse gets the response to an interaction. // interaction : Interaction instance. -func (s *Session) InteractionResponse(interaction *Interaction) (*Message, error) { - return s.WebhookMessage(interaction.AppID, interaction.Token, "@original") +func (s *Session) InteractionResponse(interaction *Interaction, options ...RequestOption) (*Message, error) { + return s.WebhookMessage(interaction.AppID, interaction.Token, "@original", options...) } // InteractionResponseEdit edits the response to an interaction. // interaction : Interaction instance. // newresp : Updated response message data. -func (s *Session) InteractionResponseEdit(interaction *Interaction, newresp *WebhookEdit) (*Message, error) { - return s.WebhookMessageEdit(interaction.AppID, interaction.Token, "@original", newresp) +func (s *Session) InteractionResponseEdit(interaction *Interaction, newresp *WebhookEdit, options ...RequestOption) (*Message, error) { + return s.WebhookMessageEdit(interaction.AppID, interaction.Token, "@original", newresp, options...) } // InteractionResponseDelete deletes the response to an interaction. // interaction : Interaction instance. -func (s *Session) InteractionResponseDelete(interaction *Interaction) error { +func (s *Session) InteractionResponseDelete(interaction *Interaction, options ...RequestOption) error { endpoint := EndpointInteractionResponseActions(interaction.AppID, interaction.Token) - _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint, options...) return err } @@ -2882,23 +3066,23 @@ func (s *Session) InteractionResponseDelete(interaction *Interaction) error { // interaction : Interaction instance. // wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) // data : Data of the message to send. -func (s *Session) FollowupMessageCreate(interaction *Interaction, wait bool, data *WebhookParams) (*Message, error) { - return s.WebhookExecute(interaction.AppID, interaction.Token, wait, data) +func (s *Session) FollowupMessageCreate(interaction *Interaction, wait bool, data *WebhookParams, options ...RequestOption) (*Message, error) { + return s.WebhookExecute(interaction.AppID, interaction.Token, wait, data, options...) } // FollowupMessageEdit edits a followup message of an interaction. // interaction : Interaction instance. // messageID : The followup message ID. // data : Data to update the message -func (s *Session) FollowupMessageEdit(interaction *Interaction, messageID string, data *WebhookEdit) (*Message, error) { - return s.WebhookMessageEdit(interaction.AppID, interaction.Token, messageID, data) +func (s *Session) FollowupMessageEdit(interaction *Interaction, messageID string, data *WebhookEdit, options ...RequestOption) (*Message, error) { + return s.WebhookMessageEdit(interaction.AppID, interaction.Token, messageID, data, options...) } // FollowupMessageDelete deletes a followup message of an interaction. // interaction : Interaction instance. // messageID : The followup message ID. -func (s *Session) FollowupMessageDelete(interaction *Interaction, messageID string) error { - return s.WebhookMessageDelete(interaction.AppID, interaction.Token, messageID) +func (s *Session) FollowupMessageDelete(interaction *Interaction, messageID string, options ...RequestOption) error { + return s.WebhookMessageDelete(interaction.AppID, interaction.Token, messageID, options...) } // ------------------------------------------------------------------------------------------------ @@ -2908,8 +3092,8 @@ func (s *Session) FollowupMessageDelete(interaction *Interaction, messageID stri // StageInstanceCreate creates and returns a new Stage instance associated to a Stage channel. // data : Parameters needed to create a stage instance. // data : The data of the Stage instance to create -func (s *Session) StageInstanceCreate(data *StageInstanceParams) (si *StageInstance, err error) { - body, err := s.RequestWithBucketID("POST", EndpointStageInstances, data, EndpointStageInstances) +func (s *Session) StageInstanceCreate(data *StageInstanceParams, options ...RequestOption) (si *StageInstance, err error) { + body, err := s.RequestWithBucketID("POST", EndpointStageInstances, data, EndpointStageInstances, options...) if err != nil { return } @@ -2920,8 +3104,8 @@ func (s *Session) StageInstanceCreate(data *StageInstanceParams) (si *StageInsta // StageInstance will retrieve a Stage instance by ID of the Stage channel. // channelID : The ID of the Stage channel -func (s *Session) StageInstance(channelID string) (si *StageInstance, err error) { - body, err := s.RequestWithBucketID("GET", EndpointStageInstance(channelID), nil, EndpointStageInstance(channelID)) +func (s *Session) StageInstance(channelID string, options ...RequestOption) (si *StageInstance, err error) { + body, err := s.RequestWithBucketID("GET", EndpointStageInstance(channelID), nil, EndpointStageInstance(channelID), options...) if err != nil { return } @@ -2933,9 +3117,9 @@ func (s *Session) StageInstance(channelID string) (si *StageInstance, err error) // StageInstanceEdit will edit a Stage instance by ID of the Stage channel. // channelID : The ID of the Stage channel // data : The data to edit the Stage instance -func (s *Session) StageInstanceEdit(channelID string, data *StageInstanceParams) (si *StageInstance, err error) { +func (s *Session) StageInstanceEdit(channelID string, data *StageInstanceParams, options ...RequestOption) (si *StageInstance, err error) { - body, err := s.RequestWithBucketID("PATCH", EndpointStageInstance(channelID), data, EndpointStageInstance(channelID)) + body, err := s.RequestWithBucketID("PATCH", EndpointStageInstance(channelID), data, EndpointStageInstance(channelID), options...) if err != nil { return } @@ -2946,8 +3130,8 @@ func (s *Session) StageInstanceEdit(channelID string, data *StageInstanceParams) // StageInstanceDelete will delete a Stage instance by ID of the Stage channel. // channelID : The ID of the Stage channel -func (s *Session) StageInstanceDelete(channelID string) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointStageInstance(channelID), nil, EndpointStageInstance(channelID)) +func (s *Session) StageInstanceDelete(channelID string, options ...RequestOption) (err error) { + _, err = s.RequestWithBucketID("DELETE", EndpointStageInstance(channelID), nil, EndpointStageInstance(channelID), options...) return } @@ -2958,13 +3142,13 @@ func (s *Session) StageInstanceDelete(channelID string) (err error) { // GuildScheduledEvents returns an array of GuildScheduledEvent for a guild // guildID : The ID of a Guild // userCount : Whether to include the user count in the response -func (s *Session) GuildScheduledEvents(guildID string, userCount bool) (st []*GuildScheduledEvent, err error) { +func (s *Session) GuildScheduledEvents(guildID string, userCount bool, options ...RequestOption) (st []*GuildScheduledEvent, err error) { uri := EndpointGuildScheduledEvents(guildID) if userCount { uri += "?with_user_count=true" } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvents(guildID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvents(guildID), options...) if err != nil { return } @@ -2977,13 +3161,13 @@ func (s *Session) GuildScheduledEvents(guildID string, userCount bool) (st []*Gu // guildID : The ID of a Guild // eventID : The ID of the event // userCount : Whether to include the user count in the response -func (s *Session) GuildScheduledEvent(guildID, eventID string, userCount bool) (st *GuildScheduledEvent, err error) { +func (s *Session) GuildScheduledEvent(guildID, eventID string, userCount bool, options ...RequestOption) (st *GuildScheduledEvent, err error) { uri := EndpointGuildScheduledEvent(guildID, eventID) if userCount { uri += "?with_user_count=true" } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvent(guildID, eventID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvent(guildID, eventID), options...) if err != nil { return } @@ -2995,8 +3179,8 @@ func (s *Session) GuildScheduledEvent(guildID, eventID string, userCount bool) ( // GuildScheduledEventCreate creates a GuildScheduledEvent for a guild and returns it // guildID : The ID of a Guild // eventID : The ID of the event -func (s *Session) GuildScheduledEventCreate(guildID string, event *GuildScheduledEventParams) (st *GuildScheduledEvent, err error) { - body, err := s.RequestWithBucketID("POST", EndpointGuildScheduledEvents(guildID), event, EndpointGuildScheduledEvents(guildID)) +func (s *Session) GuildScheduledEventCreate(guildID string, event *GuildScheduledEventParams, options ...RequestOption) (st *GuildScheduledEvent, err error) { + body, err := s.RequestWithBucketID("POST", EndpointGuildScheduledEvents(guildID), event, EndpointGuildScheduledEvents(guildID), options...) if err != nil { return } @@ -3008,8 +3192,8 @@ func (s *Session) GuildScheduledEventCreate(guildID string, event *GuildSchedule // GuildScheduledEventEdit updates a specific event for a guild and returns it. // guildID : The ID of a Guild // eventID : The ID of the event -func (s *Session) GuildScheduledEventEdit(guildID, eventID string, event *GuildScheduledEventParams) (st *GuildScheduledEvent, err error) { - body, err := s.RequestWithBucketID("PATCH", EndpointGuildScheduledEvent(guildID, eventID), event, EndpointGuildScheduledEvent(guildID, eventID)) +func (s *Session) GuildScheduledEventEdit(guildID, eventID string, event *GuildScheduledEventParams, options ...RequestOption) (st *GuildScheduledEvent, err error) { + body, err := s.RequestWithBucketID("PATCH", EndpointGuildScheduledEvent(guildID, eventID), event, EndpointGuildScheduledEvent(guildID, eventID), options...) if err != nil { return } @@ -3021,8 +3205,8 @@ func (s *Session) GuildScheduledEventEdit(guildID, eventID string, event *GuildS // GuildScheduledEventDelete deletes a specific GuildScheduledEvent in a guild // guildID : The ID of a Guild // eventID : The ID of the event -func (s *Session) GuildScheduledEventDelete(guildID, eventID string) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointGuildScheduledEvent(guildID, eventID), nil, EndpointGuildScheduledEvent(guildID, eventID)) +func (s *Session) GuildScheduledEventDelete(guildID, eventID string, options ...RequestOption) (err error) { + _, err = s.RequestWithBucketID("DELETE", EndpointGuildScheduledEvent(guildID, eventID), nil, EndpointGuildScheduledEvent(guildID, eventID), options...) return } @@ -3033,7 +3217,7 @@ func (s *Session) GuildScheduledEventDelete(guildID, eventID string) (err error) // withMember : Whether to include the member object in the response // beforeID : If is not empty all returned users entries will be before the given ID // afterID : If is not empty all returned users entries will be after the given ID -func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, withMember bool, beforeID, afterID string) (st []*GuildScheduledEventUser, err error) { +func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, withMember bool, beforeID, afterID string, options ...RequestOption) (st []*GuildScheduledEventUser, err error) { uri := EndpointGuildScheduledEventUsers(guildID, eventID) queryParams := url.Values{} @@ -3054,7 +3238,7 @@ func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, w uri += "?" + queryParams.Encode() } - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEventUsers(guildID, eventID)) + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEventUsers(guildID, eventID), options...) if err != nil { return } @@ -3069,11 +3253,11 @@ func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, w // AutoModerationRules returns a list of auto moderation rules. // guildID : ID of the guild -func (s *Session) AutoModerationRules(guildID string) (st []*AutoModerationRule, err error) { +func (s *Session) AutoModerationRules(guildID string, options ...RequestOption) (st []*AutoModerationRule, err error) { endpoint := EndpointGuildAutoModerationRules(guildID) var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -3085,11 +3269,11 @@ func (s *Session) AutoModerationRules(guildID string) (st []*AutoModerationRule, // AutoModerationRule returns an auto moderation rule. // guildID : ID of the guild // ruleID : ID of the auto moderation rule -func (s *Session) AutoModerationRule(guildID, ruleID string) (st *AutoModerationRule, err error) { +func (s *Session) AutoModerationRule(guildID, ruleID string, options ...RequestOption) (st *AutoModerationRule, err error) { endpoint := EndpointGuildAutoModerationRule(guildID, ruleID) var body []byte - body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...) if err != nil { return } @@ -3101,11 +3285,11 @@ func (s *Session) AutoModerationRule(guildID, ruleID string) (st *AutoModeration // AutoModerationRuleCreate creates an auto moderation rule with the given data and returns it. // guildID : ID of the guild // rule : Rule data -func (s *Session) AutoModerationRuleCreate(guildID string, rule *AutoModerationRule) (st *AutoModerationRule, err error) { +func (s *Session) AutoModerationRuleCreate(guildID string, rule *AutoModerationRule, options ...RequestOption) (st *AutoModerationRule, err error) { endpoint := EndpointGuildAutoModerationRules(guildID) var body []byte - body, err = s.RequestWithBucketID("POST", endpoint, rule, endpoint) + body, err = s.RequestWithBucketID("POST", endpoint, rule, endpoint, options...) if err != nil { return } @@ -3118,11 +3302,11 @@ func (s *Session) AutoModerationRuleCreate(guildID string, rule *AutoModerationR // guildID : ID of the guild // ruleID : ID of the auto moderation rule // rule : New rule data -func (s *Session) AutoModerationRuleEdit(guildID, ruleID string, rule *AutoModerationRule) (st *AutoModerationRule, err error) { +func (s *Session) AutoModerationRuleEdit(guildID, ruleID string, rule *AutoModerationRule, options ...RequestOption) (st *AutoModerationRule, err error) { endpoint := EndpointGuildAutoModerationRule(guildID, ruleID) var body []byte - body, err = s.RequestWithBucketID("PATCH", endpoint, rule, endpoint) + body, err = s.RequestWithBucketID("PATCH", endpoint, rule, endpoint, options...) if err != nil { return } @@ -3134,8 +3318,67 @@ func (s *Session) AutoModerationRuleEdit(guildID, ruleID string, rule *AutoModer // AutoModerationRuleDelete deletes an auto moderation rule. // guildID : ID of the guild // ruleID : ID of the auto moderation rule -func (s *Session) AutoModerationRuleDelete(guildID, ruleID string) (err error) { +func (s *Session) AutoModerationRuleDelete(guildID, ruleID string, options ...RequestOption) (err error) { endpoint := EndpointGuildAutoModerationRule(guildID, ruleID) - _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + _, err = s.RequestWithBucketID("DELETE", endpoint, nil, endpoint, options...) + return +} + +// ApplicationRoleConnectionMetadata returns application role connection metadata. +// appID : ID of the application +func (s *Session) ApplicationRoleConnectionMetadata(appID string) (st []*ApplicationRoleConnectionMetadata, err error) { + endpoint := EndpointApplicationRoleConnectionMetadata(appID) + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// ApplicationRoleConnectionMetadataUpdate updates and returns application role connection metadata. +// appID : ID of the application +// metadata : New metadata +func (s *Session) ApplicationRoleConnectionMetadataUpdate(appID string, metadata []*ApplicationRoleConnectionMetadata) (st []*ApplicationRoleConnectionMetadata, err error) { + endpoint := EndpointApplicationRoleConnectionMetadata(appID) + var body []byte + body, err = s.RequestWithBucketID("PUT", endpoint, metadata, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// UserApplicationRoleConnection returns user role connection to the specified application. +// appID : ID of the application +func (s *Session) UserApplicationRoleConnection(appID string) (st *ApplicationRoleConnection, err error) { + endpoint := EndpointUserApplicationRoleConnection(appID) + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &st) + return + +} + +// UserApplicationRoleConnectionUpdate updates and returns user role connection to the specified application. +// appID : ID of the application +// connection : New ApplicationRoleConnection data +func (s *Session) UserApplicationRoleConnectionUpdate(appID string, rconn *ApplicationRoleConnection) (st *ApplicationRoleConnection, err error) { + endpoint := EndpointUserApplicationRoleConnection(appID) + var body []byte + body, err = s.RequestWithBucketID("PUT", endpoint, rconn, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &st) return } diff --git a/vendor/github.com/bwmarrin/discordgo/state.go b/vendor/github.com/bwmarrin/discordgo/state.go index 6404b71d..d0c7b422 100644 --- a/vendor/github.com/bwmarrin/discordgo/state.go +++ b/vendor/github.com/bwmarrin/discordgo/state.go @@ -207,6 +207,15 @@ func (s *State) presenceAdd(guildID string, presence *Presence) error { if presence.Status != "" { guild.Presences[i].Status = presence.Status } + if presence.ClientStatus.Desktop != "" { + guild.Presences[i].ClientStatus.Desktop = presence.ClientStatus.Desktop + } + if presence.ClientStatus.Mobile != "" { + guild.Presences[i].ClientStatus.Mobile = presence.ClientStatus.Mobile + } + if presence.ClientStatus.Web != "" { + guild.Presences[i].ClientStatus.Web = presence.ClientStatus.Web + } //Update the optionally sent user information //ID Is a mandatory field so you should not need to check if it is empty @@ -909,9 +918,11 @@ func (s *State) onReady(se *Session, r *Ready) (err error) { // if state is disabled, store the bare essentials. if !se.StateEnabled { ready := Ready{ - Version: r.Version, - SessionID: r.SessionID, - User: r.User, + Version: r.Version, + SessionID: r.SessionID, + User: r.User, + Shard: r.Shard, + Application: r.Application, } s.Ready = ready @@ -981,6 +992,13 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) { } case *GuildMemberUpdate: if s.TrackMembers { + var old *Member + old, err = s.Member(t.GuildID, t.User.ID) + if err == nil { + oldCopy := *old + t.BeforeUpdate = &oldCopy + } + err = s.MemberAdd(t.Member) } case *GuildMemberRemove: @@ -1023,7 +1041,14 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) { } case *GuildEmojisUpdate: if s.TrackEmojis { - err = s.EmojisAdd(t.GuildID, t.Emojis) + var guild *Guild + guild, err = s.Guild(t.GuildID) + if err != nil { + return err + } + s.Lock() + defer s.Unlock() + guild.Emojis = t.Emojis } case *ChannelCreate: if s.TrackChannels { diff --git a/vendor/github.com/bwmarrin/discordgo/structs.go b/vendor/github.com/bwmarrin/discordgo/structs.go index 26f507a9..1c29a7e9 100644 --- a/vendor/github.com/bwmarrin/discordgo/structs.go +++ b/vendor/github.com/bwmarrin/discordgo/structs.go @@ -17,7 +17,6 @@ import ( "math" "net/http" "regexp" - "strings" "sync" "time" @@ -156,6 +155,38 @@ type Application struct { Flags int `json:"flags,omitempty"` } +// ApplicationRoleConnectionMetadataType represents the type of application role connection metadata. +type ApplicationRoleConnectionMetadataType int + +// Application role connection metadata types. +const ( + ApplicationRoleConnectionMetadataIntegerLessThanOrEqual ApplicationRoleConnectionMetadataType = 1 + ApplicationRoleConnectionMetadataIntegerGreaterThanOrEqual ApplicationRoleConnectionMetadataType = 2 + ApplicationRoleConnectionMetadataIntegerEqual ApplicationRoleConnectionMetadataType = 3 + ApplicationRoleConnectionMetadataIntegerNotEqual ApplicationRoleConnectionMetadataType = 4 + ApplicationRoleConnectionMetadataDatetimeLessThanOrEqual ApplicationRoleConnectionMetadataType = 5 + ApplicationRoleConnectionMetadataDatetimeGreaterThanOrEqual ApplicationRoleConnectionMetadataType = 6 + ApplicationRoleConnectionMetadataBooleanEqual ApplicationRoleConnectionMetadataType = 7 + ApplicationRoleConnectionMetadataBooleanNotEqual ApplicationRoleConnectionMetadataType = 8 +) + +// ApplicationRoleConnectionMetadata stores application role connection metadata. +type ApplicationRoleConnectionMetadata struct { + Type ApplicationRoleConnectionMetadataType `json:"type"` + Key string `json:"key"` + Name string `json:"name"` + NameLocalizations map[Locale]string `json:"name_localizations"` + Description string `json:"description"` + DescriptionLocalizations map[Locale]string `json:"description_localizations"` +} + +// ApplicationRoleConnection represents the role connection that an application has attached to a user. +type ApplicationRoleConnection struct { + PlatformName string `json:"platform_name"` + PlatformUsername string `json:"platform_username"` + Metadata map[string]string `json:"metadata"` +} + // UserConnection is a Connection returned from the UserConnections endpoint type UserConnection struct { ID string `json:"id"` @@ -254,6 +285,42 @@ const ( ChannelTypeGuildPublicThread ChannelType = 11 ChannelTypeGuildPrivateThread ChannelType = 12 ChannelTypeGuildStageVoice ChannelType = 13 + ChannelTypeGuildForum ChannelType = 15 +) + +// ChannelFlags represent flags of a channel/thread. +type ChannelFlags int + +// Block containing known ChannelFlags values. +const ( + // ChannelFlagPinned indicates whether the thread is pinned in the forum channel. + // NOTE: forum threads only. + ChannelFlagPinned ChannelFlags = 1 << 1 + // ChannelFlagRequireTag indicates whether a tag is required to be specified when creating a thread. + // NOTE: forum channels only. + ChannelFlagRequireTag ChannelFlags = 1 << 4 +) + +// ForumSortOrderType represents sort order of a forum channel. +type ForumSortOrderType int + +const ( + // ForumSortOrderLatestActivity sorts posts by activity. + ForumSortOrderLatestActivity ForumSortOrderType = 0 + // ForumSortOrderCreationDate sorts posts by creation time (from most recent to oldest). + ForumSortOrderCreationDate ForumSortOrderType = 1 +) + +// ForumLayout represents layout of a forum channel. +type ForumLayout int + +const ( + // ForumLayoutNotSet represents no default layout. + ForumLayoutNotSet ForumLayout = 0 + // ForumLayoutListView displays forum posts as a list. + ForumLayoutListView ForumLayout = 1 + // ForumLayoutGalleryView displays forum posts as a collection of tiles. + ForumLayoutGalleryView ForumLayout = 2 ) // A Channel holds all data related to an individual Discord channel. @@ -332,6 +399,30 @@ type Channel struct { // All thread members. State channels only. Members []*ThreadMember `json:"-"` + + // Channel flags. + Flags ChannelFlags `json:"flags"` + + // The set of tags that can be used in a forum channel. + AvailableTags []ForumTag `json:"available_tags"` + + // The IDs of the set of tags that have been applied to a thread in a forum channel. + AppliedTags []string `json:"applied_tags"` + + // Emoji to use as the default reaction to a forum post. + DefaultReactionEmoji ForumDefaultReaction `json:"default_reaction_emoji"` + + // The initial RateLimitPerUser to set on newly created threads in a channel. + // This field is copied to the thread at creation time and does not live update. + DefaultThreadRateLimitPerUser int `json:"default_thread_rate_limit_per_user"` + + // The default sort order type used to order posts in forum channels. + // Defaults to null, which indicates a preferred sort order hasn't been set by a channel admin. + DefaultSortOrder *ForumSortOrderType `json:"default_sort_order"` + + // The default forum layout view used to display posts in forum channels. + // Defaults to ForumLayoutNotSet, which indicates a layout view has not been set by a channel admin. + DefaultForumLayout ForumLayout `json:"default_forum_layout"` } // Mention returns a string which mentions the channel @@ -346,15 +437,17 @@ func (c *Channel) IsThread() bool { // A ChannelEdit holds Channel Field data for a channel edit. type ChannelEdit struct { - Name string `json:"name,omitempty"` - Topic string `json:"topic,omitempty"` - NSFW *bool `json:"nsfw,omitempty"` - Position int `json:"position"` - Bitrate int `json:"bitrate,omitempty"` - UserLimit int `json:"user_limit,omitempty"` - PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"` - ParentID string `json:"parent_id,omitempty"` - RateLimitPerUser *int `json:"rate_limit_per_user,omitempty"` + Name string `json:"name,omitempty"` + Topic string `json:"topic,omitempty"` + NSFW *bool `json:"nsfw,omitempty"` + Position int `json:"position"` + Bitrate int `json:"bitrate,omitempty"` + UserLimit int `json:"user_limit,omitempty"` + PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"` + ParentID string `json:"parent_id,omitempty"` + RateLimitPerUser *int `json:"rate_limit_per_user,omitempty"` + Flags *ChannelFlags `json:"flags,omitempty"` + DefaultThreadRateLimitPerUser *int `json:"default_thread_rate_limit_per_user,omitempty"` // NOTE: threads only @@ -362,6 +455,16 @@ type ChannelEdit struct { AutoArchiveDuration int `json:"auto_archive_duration,omitempty"` Locked *bool `json:"locked,omitempty"` Invitable *bool `json:"invitable,omitempty"` + + // NOTE: forum channels only + + AvailableTags *[]ForumTag `json:"available_tags,omitempty"` + DefaultReactionEmoji *ForumDefaultReaction `json:"default_reaction_emoji,omitempty"` + DefaultSortOrder *ForumSortOrderType `json:"default_sort_order,omitempty"` // TODO: null + DefaultForumLayout *ForumLayout `json:"default_forum_layout,omitempty"` + + // NOTE: forum threads only + AppliedTags *[]string `json:"applied_tags,omitempty"` } // A ChannelFollow holds data returned after following a news channel @@ -395,6 +498,9 @@ type ThreadStart struct { Type ChannelType `json:"type,omitempty"` Invitable bool `json:"invitable"` RateLimitPerUser int `json:"rate_limit_per_user,omitempty"` + + // NOTE: forum threads only + AppliedTags []string `json:"applied_tags,omitempty"` } // ThreadMetadata contains a number of thread-specific channel fields that are not needed by other channel types. @@ -438,6 +544,24 @@ type AddedThreadMember struct { Presence *Presence `json:"presence"` } +// ForumDefaultReaction specifies emoji to use as the default reaction to a forum post. +// NOTE: Exactly one of EmojiID and EmojiName must be set. +type ForumDefaultReaction struct { + // The id of a guild's custom emoji. + EmojiID string `json:"emoji_id,omitempty"` + // The unicode character of the emoji. + EmojiName string `json:"emoji_name,omitempty"` +} + +// ForumTag represents a tag that is able to be applied to a thread in a forum channel. +type ForumTag struct { + ID string `json:"id,omitempty"` + Name string `json:"name"` + Moderated bool `json:"moderated"` + EmojiID string `json:"emoji_id,omitempty"` + EmojiName string `json:"emoji_name,omitempty"` +} + // Emoji struct holds data related to Emoji's type Emoji struct { ID string `json:"id"` @@ -452,7 +576,7 @@ type Emoji struct { // EmojiRegex is the regex used to find and identify emojis in messages var ( - EmojiRegex = regexp.MustCompile(`<(a|):[A-z0-9_~]+:[0-9]{18}>`) + EmojiRegex = regexp.MustCompile(`<(a|):[A-z0-9_~]+:[0-9]{18,20}>`) ) // MessageFormat returns a correctly formatted Emoji for use in Message content and embeds @@ -792,16 +916,11 @@ type GuildPreview struct { } // IconURL returns a URL to the guild's icon. -func (g *GuildPreview) IconURL() string { - if g.Icon == "" { - return "" - } - - if strings.HasPrefix(g.Icon, "a_") { - return EndpointGuildIconAnimated(g.ID, g.Icon) - } - - return EndpointGuildIcon(g.ID, g.Icon) +// +// size: The size of the desired icon image as a power of two +// Image size can be any power of two between 16 and 4096. +func (g *GuildPreview) IconURL(size string) string { + return iconURL(g.Icon, EndpointGuildIcon(g.ID, g.Icon), EndpointGuildIconAnimated(g.ID, g.Icon), size) } // GuildScheduledEvent is a representation of a scheduled event in a guild. Only for retrieval of the data. @@ -917,13 +1036,13 @@ type GuildScheduledEventStatus int const ( // GuildScheduledEventStatusScheduled represents the current event is in scheduled state - GuildScheduledEventStatusScheduled = 1 + GuildScheduledEventStatusScheduled GuildScheduledEventStatus = 1 // GuildScheduledEventStatusActive represents the current event is in active state - GuildScheduledEventStatusActive = 2 + GuildScheduledEventStatusActive GuildScheduledEventStatus = 2 // GuildScheduledEventStatusCompleted represents the current event is in completed state - GuildScheduledEventStatusCompleted = 3 + GuildScheduledEventStatusCompleted GuildScheduledEventStatus = 3 // GuildScheduledEventStatusCanceled represents the current event is in canceled state - GuildScheduledEventStatusCanceled = 4 + GuildScheduledEventStatusCanceled GuildScheduledEventStatus = 4 ) // GuildScheduledEventEntityType is the type of entity associated with a guild scheduled event. @@ -932,11 +1051,11 @@ type GuildScheduledEventEntityType int const ( // GuildScheduledEventEntityTypeStageInstance represents a stage channel - GuildScheduledEventEntityTypeStageInstance = 1 + GuildScheduledEventEntityTypeStageInstance GuildScheduledEventEntityType = 1 // GuildScheduledEventEntityTypeVoice represents a voice channel - GuildScheduledEventEntityTypeVoice = 2 + GuildScheduledEventEntityTypeVoice GuildScheduledEventEntityType = 2 // GuildScheduledEventEntityTypeExternal represents an external event - GuildScheduledEventEntityTypeExternal = 3 + GuildScheduledEventEntityTypeExternal GuildScheduledEventEntityType = 3 ) // GuildScheduledEventUser is a user subscribed to a scheduled event. @@ -1007,29 +1126,26 @@ type SystemChannelFlag int // Block containing known SystemChannelFlag values const ( - SystemChannelFlagsSuppressJoin SystemChannelFlag = 1 << 0 - SystemChannelFlagsSuppressPremium SystemChannelFlag = 1 << 1 + SystemChannelFlagsSuppressJoinNotifications SystemChannelFlag = 1 << 0 + SystemChannelFlagsSuppressPremium SystemChannelFlag = 1 << 1 + SystemChannelFlagsSuppressGuildReminderNotifications SystemChannelFlag = 1 << 2 + SystemChannelFlagsSuppressJoinNotificationReplies SystemChannelFlag = 1 << 3 ) // IconURL returns a URL to the guild's icon. -func (g *Guild) IconURL() string { - if g.Icon == "" { - return "" - } - - if strings.HasPrefix(g.Icon, "a_") { - return EndpointGuildIconAnimated(g.ID, g.Icon) - } - - return EndpointGuildIcon(g.ID, g.Icon) +// +// size: The size of the desired icon image as a power of two +// Image size can be any power of two between 16 and 4096. +func (g *Guild) IconURL(size string) string { + return iconURL(g.Icon, EndpointGuildIcon(g.ID, g.Icon), EndpointGuildIconAnimated(g.ID, g.Icon), size) } // BannerURL returns a URL to the guild's banner. -func (g *Guild) BannerURL() string { - if g.Banner == "" { - return "" - } - return EndpointGuildBanner(g.ID, g.Banner) +// +// size: The size of the desired banner image as a power of two +// Image size can be any power of two between 16 and 4096. +func (g *Guild) BannerURL(size string) string { + return bannerURL(g.Banner, EndpointGuildBanner(g.ID, g.Banner), EndpointGuildBannerAnimated(g.ID, g.Banner), size) } // A UserGuild holds a brief version of a Guild @@ -1076,12 +1192,22 @@ type GuildParams struct { Region string `json:"region,omitempty"` VerificationLevel *VerificationLevel `json:"verification_level,omitempty"` DefaultMessageNotifications int `json:"default_message_notifications,omitempty"` // TODO: Separate type? + ExplicitContentFilter int `json:"explicit_content_filter,omitempty"` AfkChannelID string `json:"afk_channel_id,omitempty"` AfkTimeout int `json:"afk_timeout,omitempty"` Icon string `json:"icon,omitempty"` OwnerID string `json:"owner_id,omitempty"` Splash string `json:"splash,omitempty"` + DiscoverySplash string `json:"discovery_splash,omitempty"` Banner string `json:"banner,omitempty"` + SystemChannelID string `json:"system_channel_id,omitempty"` + SystemChannelFlags SystemChannelFlag `json:"system_channel_flags,omitempty"` + RulesChannelID string `json:"rules_channel_id,omitempty"` + PublicUpdatesChannelID string `json:"public_updates_channel_id,omitempty"` + PreferredLocale Locale `json:"preferred_locale,omitempty"` + Features []GuildFeature `json:"features,omitempty"` + Description string `json:"description,omitempty"` + PremiumProgressBarEnabled *bool `json:"premium_progress_bar_enabled,omitempty"` } // A Role stores information about Discord guild member roles. @@ -1167,10 +1293,11 @@ type VoiceState struct { // A Presence stores the online, offline, or idle and game status of Guild members. type Presence struct { - User *User `json:"user"` - Status Status `json:"status"` - Activities []*Activity `json:"activities"` - Since *int `json:"since"` + User *User `json:"user"` + Status Status `json:"status"` + Activities []*Activity `json:"activities"` + Since *int `json:"since"` + ClientStatus ClientStatus `json:"client_status"` } // A TimeStamps struct contains start and end times used in the rich presence "playing .." Game @@ -1249,9 +1376,10 @@ func (m *Member) Mention() string { } // AvatarURL returns the URL of the member's avatar -// size: The size of the user's avatar as a power of two -// if size is an empty string, no size parameter will -// be added to the URL. +// +// size: The size of the user's avatar as a power of two +// if size is an empty string, no size parameter will +// be added to the URL. func (m *Member) AvatarURL(size string) string { if m.Avatar == "" { return m.User.AvatarURL(size) @@ -1262,6 +1390,13 @@ func (m *Member) AvatarURL(size string) string { } +// ClientStatus stores the online, offline, idle, or dnd status of each device of a Guild member. +type ClientStatus struct { + Desktop Status `json:"desktop"` + Mobile Status `json:"mobile"` + Web Status `json:"web"` +} + // Status type definition type Status string @@ -1371,9 +1506,21 @@ type AutoModerationTriggerMetadata struct { // Substrings which will be searched for in content. // NOTE: should be only used with keyword trigger type. KeywordFilter []string `json:"keyword_filter,omitempty"` + // Regular expression patterns which will be matched against content (maximum of 10). + // NOTE: should be only used with keyword trigger type. + RegexPatterns []string `json:"regex_patterns,omitempty"` + // Internally pre-defined wordsets which will be searched for in content. // NOTE: should be only used with keyword preset trigger type. Presets []AutoModerationKeywordPreset `json:"presets,omitempty"` + + // Substrings which should not trigger the rule. + // NOTE: should be only used with keyword or keyword preset trigger type. + AllowList *[]string `json:"allow_list,omitempty"` + + // Total number of unique role and user mentions allowed per message. + // NOTE: should be only used with mention spam trigger type. + MentionTotalLimit int `json:"mention_total_limit,omitempty"` } // AutoModerationActionType represents an action which will execute whenever a rule is triggered. @@ -2074,6 +2221,7 @@ const ( ErrCodeUnknownGuildWelcomeScreen = 10069 ErrCodeUnknownGuildScheduledEvent = 10070 ErrCodeUnknownGuildScheduledEventUser = 10071 + ErrUnknownTag = 10087 ErrCodeBotsCannotUseEndpoint = 20001 ErrCodeOnlyBotsCanUseEndpoint = 20002 @@ -2087,28 +2235,30 @@ const ( ErrCodeStageTopicContainsNotAllowedWordsForPublicStages = 20031 ErrCodeGuildPremiumSubscriptionLevelTooLow = 20035 - ErrCodeMaximumGuildsReached = 30001 - ErrCodeMaximumPinsReached = 30003 - ErrCodeMaximumNumberOfRecipientsReached = 30004 - ErrCodeMaximumGuildRolesReached = 30005 - ErrCodeMaximumNumberOfWebhooksReached = 30007 - ErrCodeMaximumNumberOfEmojisReached = 30008 - ErrCodeTooManyReactions = 30010 - ErrCodeMaximumNumberOfGuildChannelsReached = 30013 - ErrCodeMaximumNumberOfAttachmentsInAMessageReached = 30015 - ErrCodeMaximumNumberOfInvitesReached = 30016 - ErrCodeMaximumNumberOfAnimatedEmojisReached = 30018 - ErrCodeMaximumNumberOfServerMembersReached = 30019 - ErrCodeMaximumNumberOfGuildDiscoverySubcategoriesReached = 30030 - ErrCodeGuildAlreadyHasATemplate = 30031 - ErrCodeMaximumNumberOfThreadParticipantsReached = 30033 - ErrCodeMaximumNumberOfBansForNonGuildMembersHaveBeenExceeded = 30035 - ErrCodeMaximumNumberOfBansFetchesHasBeenReached = 30037 - ErrCodeMaximumNumberOfUncompletedGuildScheduledEventsReached = 30038 - ErrCodeMaximumNumberOfStickersReached = 30039 - ErrCodeMaximumNumberOfPruneRequestsHasBeenReached = 30040 - ErrCodeMaximumNumberOfGuildWidgetSettingsUpdatesHasBeenReached = 30042 - ErrCodeMaximumNumberOfEditsToMessagesOlderThanOneHourReached = 30046 + ErrCodeMaximumGuildsReached = 30001 + ErrCodeMaximumPinsReached = 30003 + ErrCodeMaximumNumberOfRecipientsReached = 30004 + ErrCodeMaximumGuildRolesReached = 30005 + ErrCodeMaximumNumberOfWebhooksReached = 30007 + ErrCodeMaximumNumberOfEmojisReached = 30008 + ErrCodeTooManyReactions = 30010 + ErrCodeMaximumNumberOfGuildChannelsReached = 30013 + ErrCodeMaximumNumberOfAttachmentsInAMessageReached = 30015 + ErrCodeMaximumNumberOfInvitesReached = 30016 + ErrCodeMaximumNumberOfAnimatedEmojisReached = 30018 + ErrCodeMaximumNumberOfServerMembersReached = 30019 + ErrCodeMaximumNumberOfGuildDiscoverySubcategoriesReached = 30030 + ErrCodeGuildAlreadyHasATemplate = 30031 + ErrCodeMaximumNumberOfThreadParticipantsReached = 30033 + ErrCodeMaximumNumberOfBansForNonGuildMembersHaveBeenExceeded = 30035 + ErrCodeMaximumNumberOfBansFetchesHasBeenReached = 30037 + ErrCodeMaximumNumberOfUncompletedGuildScheduledEventsReached = 30038 + ErrCodeMaximumNumberOfStickersReached = 30039 + ErrCodeMaximumNumberOfPruneRequestsHasBeenReached = 30040 + ErrCodeMaximumNumberOfGuildWidgetSettingsUpdatesHasBeenReached = 30042 + ErrCodeMaximumNumberOfEditsToMessagesOlderThanOneHourReached = 30046 + ErrCodeMaximumNumberOfPinnedThreadsInForumChannelHasBeenReached = 30047 + ErrCodeMaximumNumberOfTagsInForumChannelHasBeenReached = 30048 ErrCodeUnauthorized = 40001 ErrCodeActionRequiredVerifiedAccount = 40002 @@ -2121,6 +2271,7 @@ const ( ErrCodeMessageAlreadyCrossposted = 40033 ErrCodeAnApplicationWithThatNameAlreadyExists = 40041 ErrCodeInteractionHasAlreadyBeenAcknowledged = 40060 + ErrCodeTagNamesMustBeUnique = 40061 ErrCodeMissingAccess = 50001 ErrCodeInvalidAccountType = 50002 diff --git a/vendor/github.com/bwmarrin/discordgo/util.go b/vendor/github.com/bwmarrin/discordgo/util.go index 86f43b5f..957f3018 100644 --- a/vendor/github.com/bwmarrin/discordgo/util.go +++ b/vendor/github.com/bwmarrin/discordgo/util.go @@ -51,7 +51,7 @@ func MultipartBodyWithJSON(data interface{}, files []*File) (requestContentType for i, file := range files { h := make(textproto.MIMEHeader) - h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file%d"; filename="%s"`, i, quoteEscaper.Replace(file.Name))) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="files[%d]"; filename="%s"`, i, quoteEscaper.Replace(file.Name))) contentType := file.ContentType if contentType == "" { contentType = "application/octet-stream" @@ -107,3 +107,19 @@ func bannerURL(bannerHash, staticBannerURL, animatedBannerURL, size string) stri } return URL } + +func iconURL(iconHash, staticIconURL, animatedIconURL, size string) string { + var URL string + if iconHash == "" { + return "" + } else if strings.HasPrefix(iconHash, "a_") { + URL = animatedIconURL + } else { + URL = staticIconURL + } + + if size != "" { + return URL + "?size=" + size + } + return URL +} diff --git a/vendor/github.com/bwmarrin/discordgo/voice.go b/vendor/github.com/bwmarrin/discordgo/voice.go index efd88090..87e84b12 100644 --- a/vendor/github.com/bwmarrin/discordgo/voice.go +++ b/vendor/github.com/bwmarrin/discordgo/voice.go @@ -360,6 +360,25 @@ func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{} v.wsConn = nil v.Unlock() + // Wait for VOICE_SERVER_UPDATE. + // When the bot is moved by the user to another voice channel, + // VOICE_SERVER_UPDATE is received after the code 4014. + for i := 0; i < 5; i++ { // TODO: temp, wait for VoiceServerUpdate. + <-time.After(1 * time.Second) + + v.RLock() + reconnected := v.wsConn != nil + v.RUnlock() + if !reconnected { + continue + } + v.log(LogInformational, "successfully reconnected after 4014 manual disconnection") + return + } + + // When VOICE_SERVER_UPDATE is not received, disconnect as usual. + v.log(LogInformational, "disconnect due to 4014 manual disconnection") + v.session.Lock() delete(v.session.VoiceConnections, v.GuildID) v.session.Unlock() @@ -835,7 +854,7 @@ func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct if opus, ok := secretbox.Open(nil, recvbuf[12:rlen], &nonce, &v.op4.SecretKey); ok { p.Opus = opus } else { - return + continue } // extension bit set, and not a RTCP packet diff --git a/vendor/github.com/bwmarrin/discordgo/wsapi.go b/vendor/github.com/bwmarrin/discordgo/wsapi.go index 2579ee42..6c823884 100644 --- a/vendor/github.com/bwmarrin/discordgo/wsapi.go +++ b/vendor/github.com/bwmarrin/discordgo/wsapi.go @@ -320,7 +320,7 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{} } } -// UpdateStatusData ia provided to UpdateStatusComplex() +// UpdateStatusData is provided to UpdateStatusComplex() type UpdateStatusData struct { IdleSince *int `json:"since"` Activities []*Activity `json:"activities"` @@ -361,6 +361,14 @@ func (s *Session) UpdateGameStatus(idle int, name string) (err error) { return s.UpdateStatusComplex(*newUpdateStatusData(idle, ActivityTypeGame, name, "")) } +// UpdateWatchStatus is used to update the user's watch status. +// If idle>0 then set status to idle. +// If name!="" then set movie/stream. +// if otherwise, set status to active, and no activity. +func (s *Session) UpdateWatchStatus(idle int, name string) (err error) { + return s.UpdateStatusComplex(*newUpdateStatusData(idle, ActivityTypeWatching, name, "")) +} + // UpdateStreamingStatus is used to update the user's streaming status. // If idle>0 then set status to idle. // If name!="" then set game. diff --git a/vendor/github.com/hashicorp/golang-lru/.golangci.yml b/vendor/github.com/hashicorp/golang-lru/.golangci.yml new file mode 100644 index 00000000..49202fc4 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/.golangci.yml @@ -0,0 +1,30 @@ +linters: + enable: + - megacheck + - revive + - govet + - unconvert + - megacheck + - gas + - gocyclo + - dupl + - misspell + - unparam + - unused + - typecheck + - ineffassign + - stylecheck + - exportloopref + - gocritic + - nakedret + - gosimple + - prealloc + fast: false + disable-all: true + +issues: + exclude-rules: + - path: _test\.go + linters: + - dupl + exclude-use-default: false diff --git a/vendor/github.com/hashicorp/golang-lru/2q.go b/vendor/github.com/hashicorp/golang-lru/2q.go index e474cd07..15fcad03 100644 --- a/vendor/github.com/hashicorp/golang-lru/2q.go +++ b/vendor/github.com/hashicorp/golang-lru/2q.go @@ -44,7 +44,7 @@ func New2Q(size int) (*TwoQueueCache, error) { // New2QParams creates a new TwoQueueCache using the provided // parameter values. -func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) { +func New2QParams(size int, recentRatio, ghostRatio float64) (*TwoQueueCache, error) { if size <= 0 { return nil, fmt.Errorf("invalid size") } @@ -138,7 +138,6 @@ func (c *TwoQueueCache) Add(key, value interface{}) { // Add to the recently seen list c.ensureSpace(false) c.recent.Add(key, value) - return } // ensureSpace is used to ensure we have space in the cache diff --git a/vendor/github.com/hashicorp/golang-lru/LICENSE b/vendor/github.com/hashicorp/golang-lru/LICENSE index be2cc4df..0e5d580e 100644 --- a/vendor/github.com/hashicorp/golang-lru/LICENSE +++ b/vendor/github.com/hashicorp/golang-lru/LICENSE @@ -1,3 +1,5 @@ +Copyright (c) 2014 HashiCorp, Inc. + Mozilla Public License, version 2.0 1. Definitions diff --git a/vendor/github.com/hashicorp/golang-lru/README.md b/vendor/github.com/hashicorp/golang-lru/README.md index 33e58cfa..063bb160 100644 --- a/vendor/github.com/hashicorp/golang-lru/README.md +++ b/vendor/github.com/hashicorp/golang-lru/README.md @@ -7,7 +7,7 @@ thread safe LRU cache. It is based on the cache in Groupcache. Documentation ============= -Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru) +Full docs are available on [Godoc](https://pkg.go.dev/github.com/hashicorp/golang-lru) Example ======= diff --git a/vendor/github.com/hashicorp/golang-lru/arc.go b/vendor/github.com/hashicorp/golang-lru/arc.go index 555225a2..e396f842 100644 --- a/vendor/github.com/hashicorp/golang-lru/arc.go +++ b/vendor/github.com/hashicorp/golang-lru/arc.go @@ -173,7 +173,6 @@ func (c *ARCCache) Add(key, value interface{}) { // Add to the recently seen list c.t1.Add(key, value) - return } // replace is used to adaptively evict from either T1 or T2 diff --git a/vendor/github.com/hashicorp/golang-lru/lru.go b/vendor/github.com/hashicorp/golang-lru/lru.go index 4e5e9d8f..895d8e3e 100644 --- a/vendor/github.com/hashicorp/golang-lru/lru.go +++ b/vendor/github.com/hashicorp/golang-lru/lru.go @@ -6,10 +6,17 @@ import ( "github.com/hashicorp/golang-lru/simplelru" ) +const ( + // DefaultEvictedBufferSize defines the default buffer size to store evicted key/val + DefaultEvictedBufferSize = 16 +) + // Cache is a thread-safe fixed size LRU cache. type Cache struct { - lru simplelru.LRUCache - lock sync.RWMutex + lru *simplelru.LRU + evictedKeys, evictedVals []interface{} + onEvictedCB func(k, v interface{}) + lock sync.RWMutex } // New creates an LRU of the given size. @@ -19,30 +26,63 @@ func New(size int) (*Cache, error) { // NewWithEvict constructs a fixed size cache with the given eviction // callback. -func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) { - lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted)) - if err != nil { - return nil, err +func NewWithEvict(size int, onEvicted func(key, value interface{})) (c *Cache, err error) { + // create a cache with default settings + c = &Cache{ + onEvictedCB: onEvicted, } - c := &Cache{ - lru: lru, + if onEvicted != nil { + c.initEvictBuffers() + onEvicted = c.onEvicted } - return c, nil + c.lru, err = simplelru.NewLRU(size, onEvicted) + return +} + +func (c *Cache) initEvictBuffers() { + c.evictedKeys = make([]interface{}, 0, DefaultEvictedBufferSize) + c.evictedVals = make([]interface{}, 0, DefaultEvictedBufferSize) +} + +// onEvicted save evicted key/val and sent in externally registered callback +// outside of critical section +func (c *Cache) onEvicted(k, v interface{}) { + c.evictedKeys = append(c.evictedKeys, k) + c.evictedVals = append(c.evictedVals, v) } // Purge is used to completely clear the cache. func (c *Cache) Purge() { + var ks, vs []interface{} c.lock.Lock() c.lru.Purge() + if c.onEvictedCB != nil && len(c.evictedKeys) > 0 { + ks, vs = c.evictedKeys, c.evictedVals + c.initEvictBuffers() + } c.lock.Unlock() + // invoke callback outside of critical section + if c.onEvictedCB != nil { + for i := 0; i < len(ks); i++ { + c.onEvictedCB(ks[i], vs[i]) + } + } } // Add adds a value to the cache. Returns true if an eviction occurred. func (c *Cache) Add(key, value interface{}) (evicted bool) { + var k, v interface{} c.lock.Lock() evicted = c.lru.Add(key, value) + if c.onEvictedCB != nil && evicted { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } c.lock.Unlock() - return evicted + if c.onEvictedCB != nil && evicted { + c.onEvictedCB(k, v) + } + return } // Get looks up a key's value from the cache. @@ -75,13 +115,21 @@ func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) { // recent-ness or deleting it for being stale, and if not, adds the value. // Returns whether found and whether an eviction occurred. func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) { + var k, v interface{} c.lock.Lock() - defer c.lock.Unlock() - if c.lru.Contains(key) { + c.lock.Unlock() return true, false } evicted = c.lru.Add(key, value) + if c.onEvictedCB != nil && evicted { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } + c.lock.Unlock() + if c.onEvictedCB != nil && evicted { + c.onEvictedCB(k, v) + } return false, evicted } @@ -89,47 +137,80 @@ func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) { // recent-ness or deleting it for being stale, and if not, adds the value. // Returns whether found and whether an eviction occurred. func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) { + var k, v interface{} c.lock.Lock() - defer c.lock.Unlock() - previous, ok = c.lru.Peek(key) if ok { + c.lock.Unlock() return previous, true, false } - evicted = c.lru.Add(key, value) + if c.onEvictedCB != nil && evicted { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } + c.lock.Unlock() + if c.onEvictedCB != nil && evicted { + c.onEvictedCB(k, v) + } return nil, false, evicted } // Remove removes the provided key from the cache. func (c *Cache) Remove(key interface{}) (present bool) { + var k, v interface{} c.lock.Lock() present = c.lru.Remove(key) + if c.onEvictedCB != nil && present { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } c.lock.Unlock() + if c.onEvictedCB != nil && present { + c.onEvictedCB(k, v) + } return } // Resize changes the cache size. func (c *Cache) Resize(size int) (evicted int) { + var ks, vs []interface{} c.lock.Lock() evicted = c.lru.Resize(size) + if c.onEvictedCB != nil && evicted > 0 { + ks, vs = c.evictedKeys, c.evictedVals + c.initEvictBuffers() + } c.lock.Unlock() + if c.onEvictedCB != nil && evicted > 0 { + for i := 0; i < len(ks); i++ { + c.onEvictedCB(ks[i], vs[i]) + } + } return evicted } // RemoveOldest removes the oldest item from the cache. -func (c *Cache) RemoveOldest() (key interface{}, value interface{}, ok bool) { +func (c *Cache) RemoveOldest() (key, value interface{}, ok bool) { + var k, v interface{} c.lock.Lock() key, value, ok = c.lru.RemoveOldest() + if c.onEvictedCB != nil && ok { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } c.lock.Unlock() + if c.onEvictedCB != nil && ok { + c.onEvictedCB(k, v) + } return } // GetOldest returns the oldest entry -func (c *Cache) GetOldest() (key interface{}, value interface{}, ok bool) { - c.lock.Lock() +func (c *Cache) GetOldest() (key, value interface{}, ok bool) { + c.lock.RLock() key, value, ok = c.lru.GetOldest() - c.lock.Unlock() + c.lock.RUnlock() return } diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go index a86c8539..9233583c 100644 --- a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go +++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go @@ -25,7 +25,7 @@ type entry struct { // NewLRU constructs an LRU of the given size func NewLRU(size int, onEvict EvictCallback) (*LRU, error) { if size <= 0 { - return nil, errors.New("Must provide a positive size") + return nil, errors.New("must provide a positive size") } c := &LRU{ size: size, @@ -109,7 +109,7 @@ func (c *LRU) Remove(key interface{}) (present bool) { } // RemoveOldest removes the oldest item from the cache. -func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) { +func (c *LRU) RemoveOldest() (key, value interface{}, ok bool) { ent := c.evictList.Back() if ent != nil { c.removeElement(ent) @@ -120,7 +120,7 @@ func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) { } // GetOldest returns the oldest entry -func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) { +func (c *LRU) GetOldest() (key, value interface{}, ok bool) { ent := c.evictList.Back() if ent != nil { kv := ent.Value.(*entry) diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go index 92d70934..cb7f8caf 100644 --- a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go +++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go @@ -1,3 +1,4 @@ +// Package simplelru provides simple LRU implementation based on build-in container/list. package simplelru // LRUCache is the interface for simple LRU cache. @@ -34,6 +35,6 @@ type LRUCache interface { // Clears all cache entries. Purge() - // Resizes cache, returning number evicted - Resize(int) int + // Resizes cache, returning number evicted + Resize(int) int } diff --git a/vendor/github.com/hashicorp/golang-lru/testing.go b/vendor/github.com/hashicorp/golang-lru/testing.go new file mode 100644 index 00000000..49276078 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/testing.go @@ -0,0 +1,16 @@ +package lru + +import ( + "crypto/rand" + "math" + "math/big" + "testing" +) + +func getRand(tb testing.TB) int64 { + out, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + tb.Fatal(err) + } + return out.Int64() +} diff --git a/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go b/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go index 129c43fa..bb2d26b8 100644 --- a/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go +++ b/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/chat.go @@ -122,7 +122,8 @@ func (a *API) GetTextMessages(channel chat1.ChatChannel, unreadOnly bool) ([]cha return res, nil } -func (a *API) SendMessage(channel chat1.ChatChannel, body string, args ...interface{}) (SendResponse, error) { +func (a *API) SendMessage(channel chat1.ChatChannel, body string, args ...interface{}) (resp SendResponse, err error) { + defer a.Trace(&err, "SendMessage")() arg := newSendArg(sendMessageOptions{ Channel: channel, Message: sendMessageBody{ @@ -139,7 +140,8 @@ func (a *API) Broadcast(body string, args ...interface{}) (SendResponse, error) }, fmt.Sprintf(body, args...)) } -func (a *API) SendMessageByConvID(convID chat1.ConvIDStr, body string, args ...interface{}) (SendResponse, error) { +func (a *API) SendMessageByConvID(convID chat1.ConvIDStr, body string, args ...interface{}) (resp SendResponse, err error) { + defer a.Trace(&err, "SendMessageByConvID")() arg := newSendArg(sendMessageOptions{ ConversationID: convID, Message: sendMessageBody{ @@ -150,7 +152,8 @@ func (a *API) SendMessageByConvID(convID chat1.ConvIDStr, body string, args ...i } // SendMessageByTlfName sends a message on the given TLF name -func (a *API) SendMessageByTlfName(tlfName string, body string, args ...interface{}) (SendResponse, error) { +func (a *API) SendMessageByTlfName(tlfName string, body string, args ...interface{}) (resp SendResponse, err error) { + defer a.Trace(&err, "SendMessageByTlfName")() arg := newSendArg(sendMessageOptions{ Channel: chat1.ChatChannel{ Name: tlfName, @@ -162,7 +165,8 @@ func (a *API) SendMessageByTlfName(tlfName string, body string, args ...interfac return a.doSend(arg) } -func (a *API) SendMessageByTeamName(teamName string, inChannel *string, body string, args ...interface{}) (SendResponse, error) { +func (a *API) SendMessageByTeamName(teamName string, inChannel *string, body string, args ...interface{}) (resp SendResponse, err error) { + defer a.Trace(&err, "SendMessageByTeamName")() channel := "general" if inChannel != nil { channel = *inChannel diff --git a/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go b/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go index b8d9eb3c..b5248106 100644 --- a/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go +++ b/vendor/github.com/keybase/go-keybase-chat-bot/kbchat/kbchat.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "math" "os" "os/exec" "runtime" @@ -45,10 +46,10 @@ type Subscription struct { } func NewSubscription() *Subscription { - newMsgsCh := make(chan SubscriptionMessage, 100) - newConvsCh := make(chan SubscriptionConversation, 100) - newWalletCh := make(chan SubscriptionWalletEvent, 100) - errorCh := make(chan error, 100) + newMsgsCh := make(chan SubscriptionMessage, 250) + newConvsCh := make(chan SubscriptionConversation, 250) + newWalletCh := make(chan SubscriptionWalletEvent, 250) + errorCh := make(chan error, 250) shutdownCh := make(chan struct{}) return &Subscription{ DebugOutput: NewDebugOutput("Subscription"), @@ -137,6 +138,8 @@ type RunOptions struct { EnableTyping bool // Disable bot lite mode DisableBotLiteMode bool + // Number of processes to spin up to connect to the keybase service + NumPipes int } func (r RunOptions) Location() string { @@ -164,13 +167,20 @@ func Start(runOpts RunOptions, opts ...func(*API)) (*API, error) { return api, nil } +type apiPipe struct { + sync.Mutex + input io.Writer + output *bufio.Reader + cmd *exec.Cmd +} + // API is the main object used for communicating with the Keybase JSON API type API struct { sync.Mutex *DebugOutput - apiInput io.Writer - apiOutput *bufio.Reader - apiCmd *exec.Cmd + // Round robin hand out API pipes to allow concurrent API requests. + pipeIdx int + pipes []*apiPipe username string runOpts RunOptions subscriptions []*Subscription @@ -282,12 +292,15 @@ func (a *API) auth() (string, error) { func (a *API) startPipes() (err error) { a.Lock() defer a.Unlock() - if a.apiCmd != nil { - if err := a.apiCmd.Process.Kill(); err != nil { - return fmt.Errorf("unable to kill previous API command %v", err) + for _, pipe := range a.pipes { + if pipe.cmd != nil { + if err := pipe.cmd.Process.Kill(); err != nil { + return fmt.Errorf("unable to kill previous API command %v", err) + } } + pipe.cmd = nil } - a.apiCmd = nil + a.pipes = nil if a.runOpts.StartService { args := []string{fmt.Sprintf("-enable-bot-lite-mode=%v", a.runOpts.DisableBotLiteMode), "service"} @@ -306,30 +319,39 @@ func (a *API) startPipes() (err error) { a.Debug("unable to set notifiation settings %v", err) } - a.apiCmd = a.runOpts.Command("chat", "api") - if a.apiInput, err = a.apiCmd.StdinPipe(); err != nil { - return fmt.Errorf("unable to get api stdin: %v", err) - } - output, err := a.apiCmd.StdoutPipe() - if err != nil { - return fmt.Errorf("unable to get api stdout: %v", err) - } - if runtime.GOOS != "windows" { - a.apiCmd.ExtraFiles = []*os.File{output.(*os.File)} - } - if err := a.apiCmd.Start(); err != nil { - return fmt.Errorf("unable to run chat api cmd: %v", err) + // Startup NumPipes processes to the keybase chat api + for i := 0; i < int(math.Max(float64(a.runOpts.NumPipes), 1)); i++ { + pipe := apiPipe{} + pipe.cmd = a.runOpts.Command("chat", "api") + if pipe.input, err = pipe.cmd.StdinPipe(); err != nil { + return fmt.Errorf("unable to get api stdin: %v", err) + } + output, err := pipe.cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("unable to get api stdout: %v", err) + } + if runtime.GOOS != "windows" { + pipe.cmd.ExtraFiles = []*os.File{output.(*os.File)} + } + if err := pipe.cmd.Start(); err != nil { + return fmt.Errorf("unable to run chat api cmd: %v", err) + } + pipe.output = bufio.NewReader(output) + a.pipes = append(a.pipes, &pipe) } - a.apiOutput = bufio.NewReader(output) return nil } -func (a *API) getAPIPipesLocked() (io.Writer, *bufio.Reader, error) { - // this should only be called inside a lock - if a.apiCmd == nil { - return nil, nil, errAPIDisconnected +func (a *API) getAPIPipes() (*apiPipe, error) { + a.Lock() + defer a.Unlock() + idx := a.pipeIdx % len(a.pipes) + a.pipeIdx++ + pipe := a.pipes[idx] + if pipe.cmd == nil { + return nil, errAPIDisconnected } - return a.apiInput, a.apiOutput, nil + return pipe, nil } func (a *API) GetUsername() string { @@ -337,21 +359,21 @@ func (a *API) GetUsername() string { } func (a *API) doSend(arg interface{}) (resp SendResponse, err error) { - a.Lock() - defer a.Unlock() - bArg, err := json.Marshal(arg) if err != nil { return SendResponse{}, fmt.Errorf("unable to send arg: %+v: %v", arg, err) } - input, output, err := a.getAPIPipesLocked() + pipe, err := a.getAPIPipes() if err != nil { return SendResponse{}, err } - if _, err := io.WriteString(input, string(bArg)); err != nil { + pipe.Lock() + defer pipe.Unlock() + + if _, err := io.WriteString(pipe.input, string(bArg)); err != nil { return SendResponse{}, err } - responseRaw, err := output.ReadBytes('\n') + responseRaw, err := pipe.output.ReadBytes('\n') if err != nil { return SendResponse{}, err } @@ -364,17 +386,17 @@ func (a *API) doSend(arg interface{}) (resp SendResponse, err error) { } func (a *API) doFetch(apiInput string) ([]byte, error) { - a.Lock() - defer a.Unlock() - - input, output, err := a.getAPIPipesLocked() + pipe, err := a.getAPIPipes() if err != nil { return nil, err } - if _, err := io.WriteString(input, apiInput); err != nil { + pipe.Lock() + defer pipe.Unlock() + + if _, err := io.WriteString(pipe.input, apiInput); err != nil { return nil, err } - byteOutput, err := output.ReadBytes('\n') + byteOutput, err := pipe.output.ReadBytes('\n') if err != nil { return nil, err } @@ -412,16 +434,22 @@ func (a *API) Listen(opts ListenOptions) (*Subscription, error) { } boutput.Scan() t := boutput.Text() + submitErr := func(err error) { + if len(sub.errorCh)*2 > cap(sub.errorCh) { + a.Debug("large errorCh queue: len: %d cap: %d ", len(sub.errorCh), cap(sub.errorCh)) + } + sub.errorCh <- err + } var typeHolder TypeHolder if err := json.Unmarshal([]byte(t), &typeHolder); err != nil { - sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) + submitErr(fmt.Errorf("err: %v, data: %v", err, t)) break } switch typeHolder.Type { case "chat": var notification chat1.MsgNotification if err := json.Unmarshal([]byte(t), ¬ification); err != nil { - sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) + submitErr(fmt.Errorf("err: %v, data: %v", err, t)) break } if notification.Error != nil { @@ -434,12 +462,15 @@ func (a *API) Listen(opts ListenOptions) (*Subscription, error) { Channel: notification.Msg.Channel, }, } + if len(sub.newMsgsCh)*2 > cap(sub.newMsgsCh) { + a.Debug("large newMsgsCh queue: len: %d cap: %d ", len(sub.newMsgsCh), cap(sub.newMsgsCh)) + } sub.newMsgsCh <- subscriptionMessage } case "chat_conv": var notification chat1.ConvNotification if err := json.Unmarshal([]byte(t), ¬ification); err != nil { - sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) + submitErr(fmt.Errorf("err: %v, data: %v", err, t)) break } if notification.Error != nil { @@ -448,15 +479,21 @@ func (a *API) Listen(opts ListenOptions) (*Subscription, error) { subscriptionConv := SubscriptionConversation{ Conversation: *notification.Conv, } + if len(sub.newConvsCh)*2 > cap(sub.newConvsCh) { + a.Debug("large newConvsCh queue: len: %d cap: %d ", len(sub.newConvsCh), cap(sub.newConvsCh)) + } sub.newConvsCh <- subscriptionConv } case "wallet": var holder PaymentHolder if err := json.Unmarshal([]byte(t), &holder); err != nil { - sub.errorCh <- fmt.Errorf("err: %v, data: %v", err, t) + submitErr(fmt.Errorf("err: %v, data: %v", err, t)) break } subscriptionPayment := SubscriptionWalletEvent(holder) + if len(sub.newWalletCh)*2 > cap(sub.newWalletCh) { + a.Debug("large newWalletCh queue: len: %d cap: %d ", len(sub.newWalletCh), cap(sub.newWalletCh)) + } sub.newWalletCh <- subscriptionPayment default: continue @@ -518,7 +555,6 @@ func (a *API) Listen(opts ListenOptions) (*Subscription, error) { } boutput := bufio.NewScanner(output) if err := p.Start(); err != nil { - a.Debug("Listen: failed to make listen scanner: %s", err) time.Sleep(pause) continue @@ -568,10 +604,12 @@ func (a *API) Shutdown() (err error) { for _, sub := range a.subscriptions { sub.Shutdown() } - if a.apiCmd != nil { - a.Debug("waiting for API command") - if err := a.apiCmd.Wait(); err != nil { - return err + for _, pipe := range a.pipes { + if pipe.cmd != nil { + a.Debug("waiting for API command") + if err := pipe.cmd.Wait(); err != nil { + return err + } } } diff --git a/vendor/github.com/kyokomi/emoji/v2/README.md b/vendor/github.com/kyokomi/emoji/v2/README.md index e6045985..7cf9ba57 100644 --- a/vendor/github.com/kyokomi/emoji/v2/README.md +++ b/vendor/github.com/kyokomi/emoji/v2/README.md @@ -1,7 +1,7 @@ # Emoji Emoji is a simple golang package. -[![wercker status](https://app.wercker.com/status/7bef60de2c6d3e0e6c13d56b2393c5d8/s/master "wercker status")](https://app.wercker.com/project/byKey/7bef60de2c6d3e0e6c13d56b2393c5d8) +![master workflow](https://github.com/kyokomi/emoji/actions/workflows/go.yml/badge.svg) [![Coverage Status](https://coveralls.io/repos/kyokomi/emoji/badge.png?branch=master)](https://coveralls.io/r/kyokomi/emoji?branch=master) [![GoDoc](https://pkg.go.dev/badge/github.com/kyokomi/emoji.svg)](https://pkg.go.dev/github.com/kyokomi/emoji/v2) diff --git a/vendor/github.com/kyokomi/emoji/v2/emoji_codemap.go b/vendor/github.com/kyokomi/emoji/v2/emoji_codemap.go index 278b711c..c44cb618 100644 --- a/vendor/github.com/kyokomi/emoji/v2/emoji_codemap.go +++ b/vendor/github.com/kyokomi/emoji/v2/emoji_codemap.go @@ -84,6 +84,7 @@ func emojiCode() map[string]string { ":UP!_button:": "\U0001f199", ":VS_button:": "\U0001f19a", ":Virgo:": "\u264d", + ":ZZZ:": "\U0001f4a4", ":a:": "\U0001f170\ufe0f", ":ab:": "\U0001f18e", ":abacus:": "\U0001f9ee", @@ -298,6 +299,7 @@ func emojiCode() map[string]string { ":birthday_cake:": "\U0001f382", ":bison:": "\U0001f9ac", ":biting_lip:": "\U0001fae6", + ":black_bird:": "\U0001f426\u200d\u2b1b", ":black_cat:": "\U0001f408\u200d\u2b1b", ":black_circle:": "\u26ab", ":black_circle_for_record:": "\u23fa\ufe0f", @@ -779,6 +781,7 @@ func emojiCode() map[string]string { ":dolphin:": "\U0001f42c", ":dominica:": "\U0001f1e9\U0001f1f2", ":dominican_republic:": "\U0001f1e9\U0001f1f4", + ":donkey:": "\U0001facf", ":door:": "\U0001f6aa", ":dotted_line_face:": "\U0001fae5", ":dotted_six-pointed_star:": "\U0001f52f", @@ -850,6 +853,7 @@ func emojiCode() map[string]string { ":empty_nest:": "\U0001fab9", ":end:": "\U0001f51a", ":england:": "\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f", + ":enraged_face:": "\U0001f621", ":envelope:": "\u2709", ":envelope_with_arrow:": "\U0001f4e9", ":equatorial_guinea:": "\U0001f1ec\U0001f1f6", @@ -1837,12 +1841,14 @@ func emojiCode() map[string]string { ":flower_playing_cards:": "\U0001f3b4", ":flushed:": "\U0001f633", ":flushed_face:": "\U0001f633", + ":flute:": "\U0001fa88", ":fly:": "\U0001fab0", ":flying_disc:": "\U0001f94f", ":flying_saucer:": "\U0001f6f8", ":fog:": "\U0001f32b\ufe0f", ":foggy:": "\U0001f301", ":folded_hands:": "\U0001f64f", + ":folding_hand_fan:": "\U0001faad", ":fondue:": "\U0001fad5", ":foot:": "\U0001f9b6", ":football:": "\U0001f3c8", @@ -1906,6 +1912,7 @@ func emojiCode() map[string]string { ":gibraltar:": "\U0001f1ec\U0001f1ee", ":gift:": "\U0001f381", ":gift_heart:": "\U0001f49d", + ":ginger_root:": "\U0001fada", ":giraffe:": "\U0001f992", ":giraffe_face:": "\U0001f992", ":girl:": "\U0001f467", @@ -1932,6 +1939,7 @@ func emojiCode() map[string]string { ":golfing:": "\U0001f3cc\ufe0f", ":golfing_man:": "\U0001f3cc\ufe0f\u200d\u2642\ufe0f", ":golfing_woman:": "\U0001f3cc\ufe0f\u200d\u2640\ufe0f", + ":goose:": "\U0001fabf", ":gorilla:": "\U0001f98d", ":graduation_cap:": "\U0001f393", ":grapes:": "\U0001f347", @@ -1945,6 +1953,7 @@ func emojiCode() map[string]string { ":greenland:": "\U0001f1ec\U0001f1f1", ":grenada:": "\U0001f1ec\U0001f1e9", ":grey_exclamation:": "\u2755", + ":grey_heart:": "\U0001fa76", ":grey_question:": "\u2754", ":grimacing:": "\U0001f62c", ":grimacing_face:": "\U0001f62c", @@ -1976,6 +1985,7 @@ func emojiCode() map[string]string { ":guitar:": "\U0001f3b8", ":gun:": "\U0001f52b", ":guyana:": "\U0001f1ec\U0001f1fe", + ":hair_pick:": "\U0001faae", ":haircut:": "\U0001f487\u200d\u2640\ufe0f", ":haircut_man:": "\U0001f487\u200d\u2642\ufe0f", ":haircut_woman:": "\U0001f487\u200d\u2640\ufe0f", @@ -2094,6 +2104,7 @@ func emojiCode() map[string]string { ":hushed:": "\U0001f62f", ":hushed_face:": "\U0001f62f", ":hut:": "\U0001f6d6", + ":hyacinth:": "\U0001fabb", ":i_love_you_hand_sign:": "\U0001f91f", ":ice:": "\U0001f9ca", ":ice_cream:": "\U0001f368", @@ -2142,6 +2153,7 @@ func emojiCode() map[string]string { ":japanese_ogre:": "\U0001f479", ":jar:": "\U0001fad9", ":jeans:": "\U0001f456", + ":jellyfish:": "\U0001fabc", ":jersey:": "\U0001f1ef\U0001f1ea", ":jigsaw:": "\U0001f9e9", ":joker:": "\U0001f0cf", @@ -2175,6 +2187,7 @@ func emojiCode() map[string]string { ":keycap_9:": "9\ufe0f\u20e3", ":keycap_star:": "*\ufe0f\u20e3", ":keycap_ten:": "\U0001f51f", + ":khanda:": "\U0001faaf", ":kick_scooter:": "\U0001f6f4", ":kimono:": "\U0001f458", ":kiribati:": "\U0001f1f0\U0001f1ee", @@ -2261,6 +2274,7 @@ func emojiCode() map[string]string { ":left_speech_bubble:": "\U0001f5e8\ufe0f", ":leftwards_arrow_with_hook:": "\u21a9\ufe0f", ":leftwards_hand:": "\U0001faf2", + ":leftwards_pushing_hand:": "\U0001faf7", ":leg:": "\U0001f9b5", ":lemon:": "\U0001f34b", ":leo:": "\u264c", @@ -2271,6 +2285,7 @@ func emojiCode() map[string]string { ":libra:": "\u264e", ":libya:": "\U0001f1f1\U0001f1fe", ":liechtenstein:": "\U0001f1f1\U0001f1ee", + ":light_blue_heart:": "\U0001fa75", ":light_bulb:": "\U0001f4a1", ":light_rail:": "\U0001f688", ":lightning:": "\U0001f329\ufe0f", @@ -2807,6 +2822,7 @@ func emojiCode() map[string]string { ":map:": "\U0001f5fa", ":map_of_Japan:": "\U0001f5fe", ":maple_leaf:": "\U0001f341", + ":maracas:": "\U0001fa87", ":marshall_islands:": "\U0001f1f2\U0001f1ed", ":martial_arts_uniform:": "\U0001f94b", ":martinique:": "\U0001f1f2\U0001f1f6", @@ -2911,6 +2927,7 @@ func emojiCode() map[string]string { ":moon:": "\U0001f314", ":moon_cake:": "\U0001f96e", ":moon_viewing_ceremony:": "\U0001f391", + ":moose:": "\U0001face", ":morocco:": "\U0001f1f2\U0001f1e6", ":mortar_board:": "\U0001f393", ":mosque:": "\U0001f54c", @@ -3159,6 +3176,7 @@ func emojiCode() map[string]string { ":passport_control:": "\U0001f6c2", ":pause_button:": "\u23f8", ":paw_prints:": "\U0001f43e", + ":pea_pod:": "\U0001fadb", ":peace:": "\u262e", ":peace_symbol:": "\u262e\ufe0f", ":peach:": "\U0001f351", @@ -3410,6 +3428,7 @@ func emojiCode() map[string]string { ":pine_decoration:": "\U0001f38d", ":pineapple:": "\U0001f34d", ":ping_pong:": "\U0001f3d3", + ":pink_heart:": "\U0001fa77", ":pirate_flag:": "\U0001f3f4\u200d\u2620\ufe0f", ":pisces:": "\u2653", ":pitcairn_islands:": "\U0001f1f5\U0001f1f3", @@ -3486,7 +3505,7 @@ func emojiCode() map[string]string { ":pouring_liquid:": "\U0001fad7", ":pout:": "\U0001f621", ":pouting_cat:": "\U0001f63e", - ":pouting_face:": "\U0001f621", + ":pouting_face:": "\U0001f64e", ":pouting_man:": "\U0001f64e\u200d\u2642\ufe0f", ":pouting_woman:": "\U0001f64e\u200d\u2640\ufe0f", ":pray:": "\U0001f64f", @@ -3641,6 +3660,7 @@ func emojiCode() map[string]string { ":right_facing_fist_tone4:": "\U0001f91c\U0001f3fe", ":right_facing_fist_tone5:": "\U0001f91c\U0001f3ff", ":rightwards_hand:": "\U0001faf1", + ":rightwards_pushing_hand:": "\U0001faf8", ":ring:": "\U0001f48d", ":ring_buoy:": "\U0001f6df", ":ringed_planet:": "\U0001fa90", @@ -3747,6 +3767,7 @@ func emojiCode() map[string]string { ":seven_o’clock:": "\U0001f556", ":sewing_needle:": "\U0001faa1", ":seychelles:": "\U0001f1f8\U0001f1e8", + ":shaking_face:": "\U0001fae8", ":shallow_pan_of_food:": "\U0001f958", ":shamrock:": "\u2618\ufe0f", ":shark:": "\U0001f988", @@ -4275,9 +4296,11 @@ func emojiCode() map[string]string { ":wind_face:": "\U0001f32c", ":window:": "\U0001fa9f", ":wine_glass:": "\U0001f377", + ":wing:": "\U0001fabd", ":wink:": "\U0001f609", ":winking_face:": "\U0001f609", ":winking_face_with_tongue:": "\U0001f61c", + ":wireless:": "\U0001f6dc", ":wolf:": "\U0001f43a", ":woman:": "\U0001f469", ":woman-biking:": "\U0001f6b4\u200d\u2640\ufe0f", @@ -4708,6 +4731,7 @@ func emojiCode() map[string]string { ":writing_hand_tone5:": "\u270d\U0001f3ff", ":x:": "\u274c", ":x-ray:": "\U0001fa7b", + ":x_ray:": "\U0001fa7b", ":yarn:": "\U0001f9f6", ":yawning_face:": "\U0001f971", ":yellow_circle:": "\U0001f7e1", @@ -5470,6 +5494,7 @@ func emojiRevCode() map[string][]string { "\U0001f424": {":baby_chick:"}, "\U0001f425": {":hatched_chick:", ":front-facing_baby_chick:"}, "\U0001f426": {":bird:"}, + "\U0001f426\u200d\u2b1b": {":black_bird:"}, "\U0001f427": {":penguin:"}, "\U0001f428": {":koala:"}, "\U0001f429": {":poodle:"}, @@ -6104,7 +6129,7 @@ func emojiRevCode() map[string][]string { "\U0001f4a1": {":bulb:", ":light_bulb:"}, "\U0001f4a2": {":anger:", ":anger_symbol:"}, "\U0001f4a3": {":bomb:"}, - "\U0001f4a4": {":zzz:"}, + "\U0001f4a4": {":ZZZ:", ":zzz:"}, "\U0001f4a5": {":boom:", ":collision:"}, "\U0001f4a6": {":sweat_drops:", ":sweat_droplets:"}, "\U0001f4a7": {":droplet:"}, @@ -6438,7 +6463,7 @@ func emojiRevCode() map[string][]string { "\U0001f61e": {":disappointed:", ":disappointed_face:"}, "\U0001f61f": {":worried:", ":worried_face:"}, "\U0001f620": {":angry:", ":angry_face:"}, - "\U0001f621": {":pout:", ":rage:", ":pouting_face:"}, + "\U0001f621": {":pout:", ":rage:", ":enraged_face:"}, "\U0001f622": {":cry:", ":crying_face:"}, "\U0001f623": {":persevere:", ":persevering_face:"}, "\U0001f624": {":triumph:", ":face_with_steam_from_nose:"}, @@ -6576,7 +6601,7 @@ func emojiRevCode() map[string][]string { "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f": {":man_frowning_tone5:"}, "\U0001f64d\u200d\u2640\ufe0f": {":frowning_woman:", ":woman-frowning:", ":woman_frowning:", ":person_frowning:"}, "\U0001f64d\u200d\u2642\ufe0f": {":frowning_man:", ":man-frowning:", ":man_frowning:"}, - "\U0001f64e": {":person_pouting:"}, + "\U0001f64e": {":pouting_face:", ":person_pouting:"}, "\U0001f64e\U0001f3fb": {":person_pouting_tone1:"}, "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f": {":woman_pouting_tone1:"}, "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f": {":man_pouting_tone1:"}, @@ -6761,6 +6786,7 @@ func emojiRevCode() map[string][]string { "\U0001f6d5": {":hindu_temple:"}, "\U0001f6d6": {":hut:"}, "\U0001f6d7": {":elevator:"}, + "\U0001f6dc": {":wireless:"}, "\U0001f6dd": {":playground_slide:"}, "\U0001f6de": {":wheel:"}, "\U0001f6df": {":ring_buoy:"}, @@ -7422,10 +7448,13 @@ func emojiRevCode() map[string][]string { "\U0001fa72": {":briefs:", ":swim_brief:"}, "\U0001fa73": {":shorts:"}, "\U0001fa74": {":thong_sandal:"}, + "\U0001fa75": {":light_blue_heart:"}, + "\U0001fa76": {":grey_heart:"}, + "\U0001fa77": {":pink_heart:"}, "\U0001fa78": {":drop_of_blood:"}, "\U0001fa79": {":adhesive_bandage:"}, "\U0001fa7a": {":stethoscope:"}, - "\U0001fa7b": {":x-ray:"}, + "\U0001fa7b": {":x-ray:", ":x_ray:"}, "\U0001fa7c": {":crutch:"}, "\U0001fa80": {":yo-yo:", ":yo_yo:"}, "\U0001fa81": {":kite:"}, @@ -7434,6 +7463,8 @@ func emojiRevCode() map[string][]string { "\U0001fa84": {":magic_wand:"}, "\U0001fa85": {":pinata:", ":piñata:"}, "\U0001fa86": {":nesting_dolls:"}, + "\U0001fa87": {":maracas:"}, + "\U0001fa88": {":flute:"}, "\U0001fa90": {":ringed_planet:"}, "\U0001fa91": {":chair:"}, "\U0001fa92": {":razor:"}, @@ -7463,6 +7494,9 @@ func emojiRevCode() map[string][]string { "\U0001faaa": {":identification_card:"}, "\U0001faab": {":low_battery:"}, "\U0001faac": {":hamsa:"}, + "\U0001faad": {":folding_hand_fan:"}, + "\U0001faae": {":hair_pick:"}, + "\U0001faaf": {":khanda:"}, "\U0001fab0": {":fly:"}, "\U0001fab1": {":worm:"}, "\U0001fab2": {":beetle:"}, @@ -7474,12 +7508,18 @@ func emojiRevCode() map[string][]string { "\U0001fab8": {":coral:"}, "\U0001fab9": {":empty_nest:"}, "\U0001faba": {":nest_with_eggs:"}, + "\U0001fabb": {":hyacinth:"}, + "\U0001fabc": {":jellyfish:"}, + "\U0001fabd": {":wing:"}, + "\U0001fabf": {":goose:"}, "\U0001fac0": {":anatomical_heart:"}, "\U0001fac1": {":lungs:"}, "\U0001fac2": {":people_hugging:"}, "\U0001fac3": {":pregnant_man:"}, "\U0001fac4": {":pregnant_person:"}, "\U0001fac5": {":person_with_crown:"}, + "\U0001face": {":moose:"}, + "\U0001facf": {":donkey:"}, "\U0001fad0": {":blueberries:"}, "\U0001fad1": {":bell_pepper:"}, "\U0001fad2": {":olive:"}, @@ -7490,6 +7530,8 @@ func emojiRevCode() map[string][]string { "\U0001fad7": {":pouring_liquid:"}, "\U0001fad8": {":beans:"}, "\U0001fad9": {":jar:"}, + "\U0001fada": {":ginger_root:"}, + "\U0001fadb": {":pea_pod:"}, "\U0001fae0": {":melting_face:"}, "\U0001fae1": {":saluting_face:"}, "\U0001fae2": {":face_with_open_eyes_and_hand_over_mouth:"}, @@ -7498,6 +7540,7 @@ func emojiRevCode() map[string][]string { "\U0001fae5": {":dotted_line_face:"}, "\U0001fae6": {":biting_lip:"}, "\U0001fae7": {":bubbles:"}, + "\U0001fae8": {":shaking_face:"}, "\U0001faf0": {":hand_with_index_finger_and_thumb_crossed:"}, "\U0001faf1": {":rightwards_hand:"}, "\U0001faf2": {":leftwards_hand:"}, @@ -7505,6 +7548,8 @@ func emojiRevCode() map[string][]string { "\U0001faf4": {":palm_up_hand:"}, "\U0001faf5": {":index_pointing_at_the_viewer:"}, "\U0001faf6": {":heart_hands:"}, + "\U0001faf7": {":leftwards_pushing_hand:"}, + "\U0001faf8": {":rightwards_pushing_hand:"}, "\u00a9\ufe0f": {":copyright:"}, "\u00ae\ufe0f": {":registered:"}, "\u203c": {":double_exclamation_mark:"}, diff --git a/vendor/github.com/kyokomi/emoji/v2/wercker.yml b/vendor/github.com/kyokomi/emoji/v2/wercker.yml deleted file mode 100644 index 82bd2099..00000000 --- a/vendor/github.com/kyokomi/emoji/v2/wercker.yml +++ /dev/null @@ -1,33 +0,0 @@ -box: golang -build: - steps: - - setup-go-workspace - - script: - name: go version - code: go version - - script: - name: install tools - code: | - go install github.com/mattn/goveralls@latest - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - - script: - name: go get - code: | - go get ./... - - script: - name: go build - code: | - go build ./... - - script: - name: golangci-lint - code: | - golangci-lint run - - script: - name: go test - code: | - go test ./... - - script: - name: coveralls - code: | - goveralls -v -service wercker.com -repotoken $COVERALLS_TOKEN - diff --git a/vendor/github.com/labstack/echo/v4/CHANGELOG.md b/vendor/github.com/labstack/echo/v4/CHANGELOG.md index 8b71fb8e..c1c3c107 100644 --- a/vendor/github.com/labstack/echo/v4/CHANGELOG.md +++ b/vendor/github.com/labstack/echo/v4/CHANGELOG.md @@ -1,5 +1,42 @@ # Changelog +## v4.10.0 - 2022-12-27 + +**Security** + +* We are deprecating JWT middleware in this repository. Please use https://github.com/labstack/echo-jwt instead. + + JWT middleware is moved to separate repository to allow us to bump/upgrade version of JWT implementation (`github.com/golang-jwt/jwt`) we are using +which we can not do in Echo core because this would break backwards compatibility guarantees we try to maintain. + +* This minor version bumps minimum Go version to 1.17 (from 1.16) due `golang.org/x/` packages we depend on. There are + several vulnerabilities fixed in these libraries. + + Echo still tries to support last 4 Go versions but there are occasions we can not guarantee this promise. + + +**Enhancements** + +* Bump x/text to 0.3.8 [#2305](https://github.com/labstack/echo/pull/2305) +* Bump dependencies and add notes about Go releases we support [#2336](https://github.com/labstack/echo/pull/2336) +* Add helper interface for ProxyBalancer interface [#2316](https://github.com/labstack/echo/pull/2316) +* Expose `middleware.CreateExtractors` function so we can use it from echo-contrib repository [#2338](https://github.com/labstack/echo/pull/2338) +* Refactor func(Context) error to HandlerFunc [#2315](https://github.com/labstack/echo/pull/2315) +* Improve function comments [#2329](https://github.com/labstack/echo/pull/2329) +* Add new method HTTPError.WithInternal [#2340](https://github.com/labstack/echo/pull/2340) +* Replace io/ioutil package usages [#2342](https://github.com/labstack/echo/pull/2342) +* Add staticcheck to CI flow [#2343](https://github.com/labstack/echo/pull/2343) +* Replace relative path determination from proprietary to std [#2345](https://github.com/labstack/echo/pull/2345) +* Remove square brackets from ipv6 addresses in XFF (X-Forwarded-For header) [#2182](https://github.com/labstack/echo/pull/2182) +* Add testcases for some BodyLimit middleware configuration options [#2350](https://github.com/labstack/echo/pull/2350) +* Additional configuration options for RequestLogger and Logger middleware [#2341](https://github.com/labstack/echo/pull/2341) +* Add route to request log [#2162](https://github.com/labstack/echo/pull/2162) +* GitHub Workflows security hardening [#2358](https://github.com/labstack/echo/pull/2358) +* Add govulncheck to CI and bump dependencies [#2362](https://github.com/labstack/echo/pull/2362) +* Fix rate limiter docs [#2366](https://github.com/labstack/echo/pull/2366) +* Refactor how `e.Routes()` work and introduce `e.OnAddRouteHandler` callback [#2337](https://github.com/labstack/echo/pull/2337) + + ## v4.9.1 - 2022-10-12 **Fixes** diff --git a/vendor/github.com/labstack/echo/v4/Makefile b/vendor/github.com/labstack/echo/v4/Makefile index a6c4aaa9..6aff6a89 100644 --- a/vendor/github.com/labstack/echo/v4/Makefile +++ b/vendor/github.com/labstack/echo/v4/Makefile @@ -10,8 +10,10 @@ check: lint vet race ## Check project init: @go install golang.org/x/lint/golint@latest + @go install honnef.co/go/tools/cmd/staticcheck@latest lint: ## Lint the files + @staticcheck ${PKG_LIST} @golint -set_exit_status ${PKG_LIST} vet: ## Vet the files @@ -29,6 +31,6 @@ benchmark: ## Run benchmarks help: ## Display this help screen @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -goversion ?= "1.16" -test_version: ## Run tests inside Docker with given version (defaults to 1.15 oldest supported). Example: make test_version goversion=1.16 +goversion ?= "1.17" +test_version: ## Run tests inside Docker with given version (defaults to 1.17 oldest supported). Example: make test_version goversion=1.17 @docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make init check" diff --git a/vendor/github.com/labstack/echo/v4/context.go b/vendor/github.com/labstack/echo/v4/context.go index 5567100b..b3a7ce8d 100644 --- a/vendor/github.com/labstack/echo/v4/context.go +++ b/vendor/github.com/labstack/echo/v4/context.go @@ -169,7 +169,11 @@ type ( // Redirect redirects the request to a provided URL with status code. Redirect(code int, url string) error - // Error invokes the registered HTTP error handler. Generally used by middleware. + // Error invokes the registered global HTTP error handler. Generally used by middleware. + // A side-effect of calling global error handler is that now Response has been committed (sent to the client) and + // middlewares up in chain can not change Response status code or Response body anymore. + // + // Avoid using this method in handlers as no middleware will be able to effectively handle errors after that. Error(err error) // Handler returns the matched handler by router. @@ -282,11 +286,16 @@ func (c *context) RealIP() string { if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" { i := strings.IndexAny(ip, ",") if i > 0 { - return strings.TrimSpace(ip[:i]) + xffip := strings.TrimSpace(ip[:i]) + xffip = strings.TrimPrefix(xffip, "[") + xffip = strings.TrimSuffix(xffip, "]") + return xffip } return ip } if ip := c.request.Header.Get(HeaderXRealIP); ip != "" { + ip = strings.TrimPrefix(ip, "[") + ip = strings.TrimSuffix(ip, "]") return ip } ra, _, _ := net.SplitHostPort(c.request.RemoteAddr) diff --git a/vendor/github.com/labstack/echo/v4/echo.go b/vendor/github.com/labstack/echo/v4/echo.go index 5ae8a142..f6d89b96 100644 --- a/vendor/github.com/labstack/echo/v4/echo.go +++ b/vendor/github.com/labstack/echo/v4/echo.go @@ -3,50 +3,49 @@ Package echo implements high performance, minimalist Go web framework. Example: - package main + package main - import ( - "net/http" + import ( + "net/http" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - ) + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + ) - // Handler - func hello(c echo.Context) error { - return c.String(http.StatusOK, "Hello, World!") - } + // Handler + func hello(c echo.Context) error { + return c.String(http.StatusOK, "Hello, World!") + } - func main() { - // Echo instance - e := echo.New() + func main() { + // Echo instance + e := echo.New() - // Middleware - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) + // Middleware + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) - // Routes - e.GET("/", hello) + // Routes + e.GET("/", hello) - // Start server - e.Logger.Fatal(e.Start(":1323")) - } + // Start server + e.Logger.Fatal(e.Start(":1323")) + } Learn more at https://echo.labstack.com */ package echo import ( - "bytes" stdContext "context" "crypto/tls" "errors" "fmt" "io" - "io/ioutil" stdLog "log" "net" "net/http" + "os" "reflect" "runtime" "sync" @@ -62,20 +61,28 @@ import ( type ( // Echo is the top-level framework instance. + // + // Goroutine safety: Do not mutate Echo instance fields after server has started. Accessing these + // fields from handlers/middlewares and changing field values at the same time leads to data-races. + // Adding new routes after the server has been started is also not safe! Echo struct { filesystem common // startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get // listener address info (on which interface/port was listener binded) without having data races. - startupMutex sync.RWMutex + startupMutex sync.RWMutex + colorer *color.Color + + // premiddleware are middlewares that are run before routing is done. In case a pre-middleware returns + // an error the router is not executed and the request will end up in the global error handler. + premiddleware []MiddlewareFunc + middleware []MiddlewareFunc + maxParam *int + router *Router + routers map[string]*Router + pool sync.Pool + StdLogger *stdLog.Logger - colorer *color.Color - premiddleware []MiddlewareFunc - middleware []MiddlewareFunc - maxParam *int - router *Router - routers map[string]*Router - pool sync.Pool Server *http.Server TLSServer *http.Server Listener net.Listener @@ -93,6 +100,9 @@ type ( Logger Logger IPExtractor IPExtractor ListenerNetwork string + + // OnAddRouteHandler is called when Echo adds new route to specific host router. + OnAddRouteHandler func(host string, route Route, handler HandlerFunc, middleware []MiddlewareFunc) } // Route contains a handler and information for matching against requests. @@ -116,7 +126,7 @@ type ( HandlerFunc func(c Context) error // HTTPErrorHandler is a centralized HTTP error handler. - HTTPErrorHandler func(error, Context) + HTTPErrorHandler func(err error, c Context) // Validator is the interface that wraps the Validate function. Validator interface { @@ -248,7 +258,7 @@ const ( const ( // Version of Echo - Version = "4.9.0" + Version = "4.10.0" website = "https://echo.labstack.com" // http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo banner = ` @@ -527,21 +537,20 @@ func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route { return e.file(path, file, e.GET, m...) } -func (e *Echo) add(host, method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route { - name := handlerName(handler) +func (e *Echo) add(host, method, path string, handler HandlerFunc, middlewares ...MiddlewareFunc) *Route { router := e.findRouter(host) - // FIXME: when handler+middleware are both nil ... make it behave like handler removal - router.Add(method, path, func(c Context) error { - h := applyMiddleware(handler, middleware...) + //FIXME: when handler+middleware are both nil ... make it behave like handler removal + name := handlerName(handler) + route := router.add(method, path, name, func(c Context) error { + h := applyMiddleware(handler, middlewares...) return h(c) }) - r := &Route{ - Method: method, - Path: path, - Name: name, + + if e.OnAddRouteHandler != nil { + e.OnAddRouteHandler(host, *route, handler, middlewares) } - e.router.routes[method+path] = r - return r + + return route } // Add registers a new route for an HTTP method and path with matching handler @@ -565,7 +574,7 @@ func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) { return } -// URI generates a URI from handler. +// URI generates an URI from handler. func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string { name := handlerName(handler) return e.Reverse(name, params...) @@ -578,35 +587,13 @@ func (e *Echo) URL(h HandlerFunc, params ...interface{}) string { // Reverse generates an URL from route name and provided parameters. func (e *Echo) Reverse(name string, params ...interface{}) string { - uri := new(bytes.Buffer) - ln := len(params) - n := 0 - for _, r := range e.router.routes { - if r.Name == name { - for i, l := 0, len(r.Path); i < l; i++ { - if (r.Path[i] == ':' || r.Path[i] == '*') && n < ln { - for ; i < l && r.Path[i] != '/'; i++ { - } - uri.WriteString(fmt.Sprintf("%v", params[n])) - n++ - } - if i < l { - uri.WriteByte(r.Path[i]) - } - } - break - } - } - return uri.String() + return e.router.Reverse(name, params...) } -// Routes returns the registered routes. +// Routes returns the registered routes for default router. +// In case when Echo serves multiple hosts/domains use `e.Routers()["domain2.site"].Routes()` to get specific host routes. func (e *Echo) Routes() []*Route { - routes := make([]*Route, 0, len(e.router.routes)) - for _, v := range e.router.routes { - routes = append(routes, v) - } - return routes + return e.router.Routes() } // AcquireContext returns an empty `Context` instance from the pool. @@ -626,7 +613,7 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Acquire context c := e.pool.Get().(*context) c.Reset(r, w) - var h func(Context) error + var h HandlerFunc if e.premiddleware == nil { e.findRouter(r.Host).Find(r.Method, GetPath(r), c) @@ -700,7 +687,7 @@ func (e *Echo) StartTLS(address string, certFile, keyFile interface{}) (err erro func filepathOrContent(fileOrContent interface{}) (content []byte, err error) { switch v := fileOrContent.(type) { case string: - return ioutil.ReadFile(v) + return os.ReadFile(v) case []byte: return v, nil default: @@ -884,6 +871,15 @@ func (he *HTTPError) SetInternal(err error) *HTTPError { return he } +// WithInternal returns clone of HTTPError with err set to HTTPError.Internal field +func (he *HTTPError) WithInternal(err error) *HTTPError { + return &HTTPError{ + Code: he.Code, + Message: he.Message, + Internal: err, + } +} + // Unwrap satisfies the Go 1.13 error wrapper interface. func (he *HTTPError) Unwrap() error { return he.Internal @@ -913,8 +909,8 @@ func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc { // GetPath returns RawPath, if it's empty returns Path from URL // Difference between RawPath and Path is: -// * Path is where request path is stored. Value is stored in decoded form: /%47%6f%2f becomes /Go/. -// * RawPath is an optional field which only gets set if the default encoding is different from Path. +// - Path is where request path is stored. Value is stored in decoded form: /%47%6f%2f becomes /Go/. +// - RawPath is an optional field which only gets set if the default encoding is different from Path. func GetPath(r *http.Request) string { path := r.URL.RawPath if path == "" { diff --git a/vendor/github.com/labstack/echo/v4/echo_fs.go b/vendor/github.com/labstack/echo/v4/echo_fs.go index b8526da9..9f83a035 100644 --- a/vendor/github.com/labstack/echo/v4/echo_fs.go +++ b/vendor/github.com/labstack/echo/v4/echo_fs.go @@ -7,7 +7,6 @@ import ( "net/url" "os" "path/filepath" - "runtime" "strings" ) @@ -125,7 +124,7 @@ func subFS(currentFs fs.FS, root string) (fs.FS, error) { // we need to make exception for `defaultFS` instances as it interprets root prefix differently from fs.FS. // fs.Fs.Open does not like relative paths ("./", "../") and absolute paths at all but prior echo.Filesystem we // were able to use paths like `./myfile.log`, `/etc/hosts` and these would work fine with `os.Open` but not with fs.Fs - if isRelativePath(root) { + if !filepath.IsAbs(root) { root = filepath.Join(dFS.prefix, root) } return &defaultFS{ @@ -136,21 +135,6 @@ func subFS(currentFs fs.FS, root string) (fs.FS, error) { return fs.Sub(currentFs, root) } -func isRelativePath(path string) bool { - if path == "" { - return true - } - if path[0] == '/' { - return false - } - if runtime.GOOS == "windows" && strings.IndexByte(path, ':') != -1 { - // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#file_and_directory_names - // https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats - return false - } - return true -} - // MustSubFS creates sub FS from current filesystem or panic on failure. // Panic happens when `fsRoot` contains invalid path according to `fs.ValidPath` rules. // diff --git a/vendor/github.com/labstack/echo/v4/ip.go b/vendor/github.com/labstack/echo/v4/ip.go index 46d464cf..1bcd756a 100644 --- a/vendor/github.com/labstack/echo/v4/ip.go +++ b/vendor/github.com/labstack/echo/v4/ip.go @@ -227,6 +227,8 @@ func ExtractIPFromRealIPHeader(options ...TrustOption) IPExtractor { return func(req *http.Request) string { realIP := req.Header.Get(HeaderXRealIP) if realIP != "" { + realIP = strings.TrimPrefix(realIP, "[") + realIP = strings.TrimSuffix(realIP, "]") if ip := net.ParseIP(realIP); ip != nil && checker.trust(ip) { return realIP } @@ -248,7 +250,10 @@ func ExtractIPFromXFFHeader(options ...TrustOption) IPExtractor { } ips := append(strings.Split(strings.Join(xffs, ","), ","), directIP) for i := len(ips) - 1; i >= 0; i-- { - ip := net.ParseIP(strings.TrimSpace(ips[i])) + ips[i] = strings.TrimSpace(ips[i]) + ips[i] = strings.TrimPrefix(ips[i], "[") + ips[i] = strings.TrimSuffix(ips[i], "]") + ip := net.ParseIP(ips[i]) if ip == nil { // Unable to parse IP; cannot trust entire records return directIP diff --git a/vendor/github.com/labstack/echo/v4/middleware/body_dump.go b/vendor/github.com/labstack/echo/v4/middleware/body_dump.go index ebd0d0ab..fa7891b1 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/body_dump.go +++ b/vendor/github.com/labstack/echo/v4/middleware/body_dump.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "io" - "io/ioutil" "net" "net/http" @@ -68,9 +67,9 @@ func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc { // Request reqBody := []byte{} if c.Request().Body != nil { // Read - reqBody, _ = ioutil.ReadAll(c.Request().Body) + reqBody, _ = io.ReadAll(c.Request().Body) } - c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // Reset + c.Request().Body = io.NopCloser(bytes.NewBuffer(reqBody)) // Reset // Response resBody := new(bytes.Buffer) diff --git a/vendor/github.com/labstack/echo/v4/middleware/compress.go b/vendor/github.com/labstack/echo/v4/middleware/compress.go index ac6672e9..9e5f6106 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/compress.go +++ b/vendor/github.com/labstack/echo/v4/middleware/compress.go @@ -4,7 +4,6 @@ import ( "bufio" "compress/gzip" "io" - "io/ioutil" "net" "net/http" "strings" @@ -89,7 +88,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc { // nothing is written to body or error is returned. // See issue #424, #407. res.Writer = rw - w.Reset(ioutil.Discard) + w.Reset(io.Discard) } w.Close() pool.Put(w) @@ -135,7 +134,7 @@ func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error { func gzipCompressPool(config GzipConfig) sync.Pool { return sync.Pool{ New: func() interface{} { - w, err := gzip.NewWriterLevel(ioutil.Discard, config.Level) + w, err := gzip.NewWriterLevel(io.Discard, config.Level) if err != nil { return err } diff --git a/vendor/github.com/labstack/echo/v4/middleware/csrf.go b/vendor/github.com/labstack/echo/v4/middleware/csrf.go index ea90fdba..8661c9f8 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/csrf.go +++ b/vendor/github.com/labstack/echo/v4/middleware/csrf.go @@ -119,7 +119,7 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc { config.CookieSecure = true } - extractors, err := createExtractors(config.TokenLookup, "") + extractors, err := CreateExtractors(config.TokenLookup) if err != nil { panic(err) } diff --git a/vendor/github.com/labstack/echo/v4/middleware/extractor.go b/vendor/github.com/labstack/echo/v4/middleware/extractor.go index afdfd819..5d9cee6d 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/extractor.go +++ b/vendor/github.com/labstack/echo/v4/middleware/extractor.go @@ -24,6 +24,26 @@ var errFormExtractorValueMissing = errors.New("missing value in the form") // ValuesExtractor defines a function for extracting values (keys/tokens) from the given context. type ValuesExtractor func(c echo.Context) ([]string, error) +// CreateExtractors creates ValuesExtractors from given lookups. +// Lookups is a string in the form of "<source>:<name>" or "<source>:<name>,<source>:<name>" that is used +// to extract key from the request. +// Possible values: +// - "header:<name>" or "header:<name>:<cut-prefix>" +// `<cut-prefix>` is argument value to cut/trim prefix of the extracted value. This is useful if header +// value has static prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we +// want to cut is `<auth-scheme> ` note the space at the end. +// In case of basic authentication `Authorization: Basic <credentials>` prefix we want to remove is `Basic `. +// - "query:<name>" +// - "param:<name>" +// - "form:<name>" +// - "cookie:<name>" +// +// Multiple sources example: +// - "header:Authorization,header:X-Api-Key" +func CreateExtractors(lookups string) ([]ValuesExtractor, error) { + return createExtractors(lookups, "") +} + func createExtractors(lookups string, authScheme string) ([]ValuesExtractor, error) { if lookups == "" { return nil, nil diff --git a/vendor/github.com/labstack/echo/v4/middleware/jwt.go b/vendor/github.com/labstack/echo/v4/middleware/jwt.go index bec5167e..bd628264 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/jwt.go +++ b/vendor/github.com/labstack/echo/v4/middleware/jwt.go @@ -154,6 +154,8 @@ var ( // // See: https://jwt.io/introduction // See `JWTConfig.TokenLookup` +// +// Deprecated: Please use https://github.com/labstack/echo-jwt instead func JWT(key interface{}) echo.MiddlewareFunc { c := DefaultJWTConfig c.SigningKey = key @@ -162,6 +164,8 @@ func JWT(key interface{}) echo.MiddlewareFunc { // JWTWithConfig returns a JWT auth middleware with config. // See: `JWT()`. +// +// Deprecated: Please use https://github.com/labstack/echo-jwt instead func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { // Defaults if config.Skipper == nil { @@ -262,7 +266,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc { } func (config *JWTConfig) defaultParseToken(auth string, c echo.Context) (interface{}, error) { - token := new(jwt.Token) + var token *jwt.Token var err error // Issue #647, #656 if _, ok := config.Claims.(jwt.MapClaims); ok { diff --git a/vendor/github.com/labstack/echo/v4/middleware/logger.go b/vendor/github.com/labstack/echo/v4/middleware/logger.go index a21df8f3..7958d873 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/logger.go +++ b/vendor/github.com/labstack/echo/v4/middleware/logger.go @@ -35,6 +35,7 @@ type ( // - host // - method // - path + // - route // - protocol // - referer // - user_agent @@ -47,6 +48,7 @@ type ( // - header:<NAME> // - query:<NAME> // - form:<NAME> + // - custom (see CustomTagFunc field) // // Example "${remote_ip} ${status}" // @@ -56,6 +58,11 @@ type ( // Optional. Default value DefaultLoggerConfig.CustomTimeFormat. CustomTimeFormat string `yaml:"custom_time_format"` + // CustomTagFunc is function called for `${custom}` tag to output user implemented text by writing it to buf. + // Make sure that outputted text creates valid JSON string with other logged tags. + // Optional. + CustomTagFunc func(c echo.Context, buf *bytes.Buffer) (int, error) + // Output is a writer where logs in JSON format are written. // Optional. Default value os.Stdout. Output io.Writer @@ -126,6 +133,11 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { if _, err = config.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { switch tag { + case "custom": + if config.CustomTagFunc == nil { + return 0, nil + } + return config.CustomTagFunc(c, buf) case "time_unix": return buf.WriteString(strconv.FormatInt(time.Now().Unix(), 10)) case "time_unix_milli": @@ -162,6 +174,8 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc { p = "/" } return buf.WriteString(p) + case "route": + return buf.WriteString(c.Path()) case "protocol": return buf.WriteString(req.Proto) case "referer": diff --git a/vendor/github.com/labstack/echo/v4/middleware/proxy.go b/vendor/github.com/labstack/echo/v4/middleware/proxy.go index 6cfd6731..d2cd2aa6 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/proxy.go +++ b/vendor/github.com/labstack/echo/v4/middleware/proxy.go @@ -72,6 +72,11 @@ type ( Next(echo.Context) *ProxyTarget } + // TargetProvider defines an interface that gives the opportunity for balancer to return custom errors when selecting target. + TargetProvider interface { + NextTarget(echo.Context) (*ProxyTarget, error) + } + commonBalancer struct { targets []*ProxyTarget mutex sync.RWMutex @@ -223,6 +228,7 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { } } + provider, isTargetProvider := config.Balancer.(TargetProvider) return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) (err error) { if config.Skipper(c) { @@ -231,7 +237,16 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc { req := c.Request() res := c.Response() - tgt := config.Balancer.Next(c) + + var tgt *ProxyTarget + if isTargetProvider { + tgt, err = provider.NextTarget(c) + if err != nil { + return err + } + } else { + tgt = config.Balancer.Next(c) + } c.Set(config.ContextKey, tgt) if err := rewriteURL(config.RegexRewrite, req); err != nil { diff --git a/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go b/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go index be2b348d..f7fae83c 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go +++ b/vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go @@ -155,7 +155,7 @@ type ( RateLimiterMemoryStore struct { visitors map[string]*Visitor mutex sync.Mutex - rate rate.Limit //for more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/rate#Limit. + rate rate.Limit // for more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/rate#Limit. burst int expiresIn time.Duration @@ -170,15 +170,16 @@ type ( /* NewRateLimiterMemoryStore returns an instance of RateLimiterMemoryStore with -the provided rate (as req/s). The provided rate less than 1 will be treated as zero. +the provided rate (as req/s). for more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/rate#Limit. Burst and ExpiresIn will be set to default values. +Note that if the provided rate is a float number and Burst is zero, Burst will be treated as the rounded down value of the rate. + Example (with 20 requests/sec): limiterStore := middleware.NewRateLimiterMemoryStore(20) - */ func NewRateLimiterMemoryStore(rate rate.Limit) (store *RateLimiterMemoryStore) { return NewRateLimiterMemoryStoreWithConfig(RateLimiterMemoryStoreConfig{ @@ -188,7 +189,7 @@ func NewRateLimiterMemoryStore(rate rate.Limit) (store *RateLimiterMemoryStore) /* NewRateLimiterMemoryStoreWithConfig returns an instance of RateLimiterMemoryStore -with the provided configuration. Rate must be provided. Burst will be set to the value of +with the provided configuration. Rate must be provided. Burst will be set to the rounded down value of the configured rate if not provided or set to 0. The build-in memory store is usually capable for modest loads. For higher loads other @@ -225,7 +226,7 @@ func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (s // RateLimiterMemoryStoreConfig represents configuration for RateLimiterMemoryStore type RateLimiterMemoryStoreConfig struct { Rate rate.Limit // Rate of requests allowed to pass as req/s. For more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/rate#Limit. - Burst int // Burst additionally allows a number of requests to pass when rate limit is reached + Burst int // Burst is maximum number of requests to pass at the same moment. It additionally allows a number of requests to pass when rate limit is reached. ExpiresIn time.Duration // ExpiresIn is the duration after that a rate limiter is cleaned up } diff --git a/vendor/github.com/labstack/echo/v4/middleware/request_logger.go b/vendor/github.com/labstack/echo/v4/middleware/request_logger.go index 7a4d9822..b9e36925 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/request_logger.go +++ b/vendor/github.com/labstack/echo/v4/middleware/request_logger.go @@ -10,10 +10,16 @@ import ( // Example for `fmt.Printf` // e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ -// LogStatus: true, -// LogURI: true, +// LogStatus: true, +// LogURI: true, +// LogError: true, +// HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code // LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { -// fmt.Printf("REQUEST: uri: %v, status: %v\n", v.URI, v.Status) +// if v.Error == nil { +// fmt.Printf("REQUEST: uri: %v, status: %v\n", v.URI, v.Status) +// } else { +// fmt.Printf("REQUEST_ERROR: uri: %v, status: %v, err: %v\n", v.URI, v.Status, v.Error) +// } // return nil // }, // })) @@ -21,14 +27,23 @@ import ( // Example for Zerolog (https://github.com/rs/zerolog) // logger := zerolog.New(os.Stdout) // e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ -// LogURI: true, -// LogStatus: true, +// LogURI: true, +// LogStatus: true, +// LogError: true, +// HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code // LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { -// logger.Info(). -// Str("URI", v.URI). -// Int("status", v.Status). -// Msg("request") -// +// if v.Error == nil { +// logger.Info(). +// Str("URI", v.URI). +// Int("status", v.Status). +// Msg("request") +// } else { +// logger.Error(). +// Err(v.Error). +// Str("URI", v.URI). +// Int("status", v.Status). +// Msg("request error") +// } // return nil // }, // })) @@ -36,29 +51,47 @@ import ( // Example for Zap (https://github.com/uber-go/zap) // logger, _ := zap.NewProduction() // e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ -// LogURI: true, -// LogStatus: true, +// LogURI: true, +// LogStatus: true, +// LogError: true, +// HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code // LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { -// logger.Info("request", -// zap.String("URI", v.URI), -// zap.Int("status", v.Status), -// ) -// +// if v.Error == nil { +// logger.Info("request", +// zap.String("URI", v.URI), +// zap.Int("status", v.Status), +// ) +// } else { +// logger.Error("request error", +// zap.String("URI", v.URI), +// zap.Int("status", v.Status), +// zap.Error(v.Error), +// ) +// } // return nil // }, // })) // // Example for Logrus (https://github.com/sirupsen/logrus) -// log := logrus.New() +// log := logrus.New() // e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ -// LogURI: true, -// LogStatus: true, -// LogValuesFunc: func(c echo.Context, values middleware.RequestLoggerValues) error { -// log.WithFields(logrus.Fields{ -// "URI": values.URI, -// "status": values.Status, -// }).Info("request") -// +// LogURI: true, +// LogStatus: true, +// LogError: true, +// HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code +// LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { +// if v.Error == nil { +// log.WithFields(logrus.Fields{ +// "URI": v.URI, +// "status": v.Status, +// }).Info("request") +// } else { +// log.WithFields(logrus.Fields{ +// "URI": v.URI, +// "status": v.Status, +// "error": v.Error, +// }).Error("request error") +// } // return nil // }, // })) @@ -74,6 +107,13 @@ type RequestLoggerConfig struct { // Mandatory. LogValuesFunc func(c echo.Context, v RequestLoggerValues) error + // HandleError instructs logger to call global error handler when next middleware/handler returns an error. + // This is useful when you have custom error handler that can decide to use different status codes. + // + // A side-effect of calling global error handler is that now Response has been committed and sent to the client + // and middlewares up in chain can not change Response status code or response body. + HandleError bool + // LogLatency instructs logger to record duration it took to execute rest of the handler chain (next(c) call). LogLatency bool // LogProtocol instructs logger to extract request protocol (i.e. `HTTP/1.1` or `HTTP/2`) @@ -217,6 +257,9 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) { config.BeforeNextFunc(c) } err := next(c) + if config.HandleError { + c.Error(err) + } v := RequestLoggerValues{ StartTime: start, @@ -264,7 +307,9 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) { } if config.LogStatus { v.Status = res.Status - if err != nil { + if err != nil && !config.HandleError { + // this block should not be executed in case of HandleError=true as the global error handler will decide + // the status code. In that case status code could be different from what err contains. var httpErr *echo.HTTPError if errors.As(err, &httpErr) { v.Status = httpErr.Code @@ -310,6 +355,9 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) { return errOnLog } + // in case of HandleError=true we are returning the error that we already have handled with global error handler + // this is deliberate as this error could be useful for upstream middlewares and default global error handler + // will ignore that error when it bubbles up in middleware chain. return err } }, nil diff --git a/vendor/github.com/labstack/echo/v4/middleware/slash.go b/vendor/github.com/labstack/echo/v4/middleware/slash.go index 4188675b..a3bf807e 100644 --- a/vendor/github.com/labstack/echo/v4/middleware/slash.go +++ b/vendor/github.com/labstack/echo/v4/middleware/slash.go @@ -33,7 +33,7 @@ func AddTrailingSlash() echo.MiddlewareFunc { return AddTrailingSlashWithConfig(DefaultTrailingSlashConfig) } -// AddTrailingSlashWithConfig returns a AddTrailingSlash middleware with config. +// AddTrailingSlashWithConfig returns an AddTrailingSlash middleware with config. // See `AddTrailingSlash()`. func AddTrailingSlashWithConfig(config TrailingSlashConfig) echo.MiddlewareFunc { // Defaults diff --git a/vendor/github.com/labstack/echo/v4/router.go b/vendor/github.com/labstack/echo/v4/router.go index 23c5bd3b..86a986a2 100644 --- a/vendor/github.com/labstack/echo/v4/router.go +++ b/vendor/github.com/labstack/echo/v4/router.go @@ -2,6 +2,7 @@ package echo import ( "bytes" + "fmt" "net/http" ) @@ -141,6 +142,51 @@ func NewRouter(e *Echo) *Router { } } +// Routes returns the registered routes. +func (r *Router) Routes() []*Route { + routes := make([]*Route, 0, len(r.routes)) + for _, v := range r.routes { + routes = append(routes, v) + } + return routes +} + +// Reverse generates an URL from route name and provided parameters. +func (r *Router) Reverse(name string, params ...interface{}) string { + uri := new(bytes.Buffer) + ln := len(params) + n := 0 + for _, route := range r.routes { + if route.Name == name { + for i, l := 0, len(route.Path); i < l; i++ { + if (route.Path[i] == ':' || route.Path[i] == '*') && n < ln { + for ; i < l && route.Path[i] != '/'; i++ { + } + uri.WriteString(fmt.Sprintf("%v", params[n])) + n++ + } + if i < l { + uri.WriteByte(route.Path[i]) + } + } + break + } + } + return uri.String() +} + +func (r *Router) add(method, path, name string, h HandlerFunc) *Route { + r.Add(method, path, h) + + route := &Route{ + Method: method, + Path: path, + Name: name, + } + r.routes[method+path] = route + return route +} + // Add registers a new route for method and path with matching handler. func (r *Router) Add(method, path string, h HandlerFunc) { // Validate path diff --git a/vendor/github.com/lrstanley/girc/.editorconfig b/vendor/github.com/lrstanley/girc/.editorconfig index 6df59d5c..32ecf3ee 100644 --- a/vendor/github.com/lrstanley/girc/.editorconfig +++ b/vendor/github.com/lrstanley/girc/.editorconfig @@ -22,7 +22,7 @@ indent_size = 4 [*.md] trim_trailing_whitespace = false -[*.{md,py,sh,yml,yaml,js,ts,vue,css}] +[*.{md,py,sh,yml,yaml,cjs,js,ts,vue,css}] max_line_length = 105 [*.{yml,yaml,toml}] @@ -36,7 +36,7 @@ insert_final_newline = ignore max_line_length = 140 indent_size = 2 -[*.{js,ts,vue,css}] +[*.{cjs,js,ts,vue,css}] indent_size = 2 [Makefile] diff --git a/vendor/github.com/lrstanley/girc/README.md b/vendor/github.com/lrstanley/girc/README.md index 19670eae..ddb0bc1b 100644 --- a/vendor/github.com/lrstanley/girc/README.md +++ b/vendor/github.com/lrstanley/girc/README.md @@ -12,7 +12,7 @@ </a> <a href="https://github.com/lrstanley/girc/actions?query=workflow%3Atest+event%3Apush"> - <img title="GitHub Workflow Status (test @ master)" src="https://img.shields.io/github/workflow/status/lrstanley/girc/test/master?label=test&style=flat-square&event=push"> + <img title="GitHub Workflow Status (test @ master)" src="https://img.shields.io/github/actions/workflow/status/lrstanley/girc/test.yml?branch=master&label=test&style=flat-square"> </a> <a href="https://codecov.io/gh/lrstanley/girc"> diff --git a/vendor/github.com/magiconair/properties/.travis.yml b/vendor/github.com/magiconair/properties/.travis.yml deleted file mode 100644 index baf9031d..00000000 --- a/vendor/github.com/magiconair/properties/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go -go: - - 1.3.x - - 1.4.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x - - "1.10.x" - - "1.11.x" - - "1.12.x" - - "1.13.x" - - "1.14.x" - - "1.15.x" - - "1.16.x" - - tip diff --git a/vendor/github.com/magiconair/properties/CHANGELOG.md b/vendor/github.com/magiconair/properties/CHANGELOG.md index ff8d0253..842e8e24 100644 --- a/vendor/github.com/magiconair/properties/CHANGELOG.md +++ b/vendor/github.com/magiconair/properties/CHANGELOG.md @@ -1,5 +1,50 @@ ## Changelog +### [1.8.7](https://github.com/magiconair/properties/tree/v1.8.7) - 08 Dec 2022 + + * [PR #65](https://github.com/magiconair/properties/pull/65): Speedup Merge + + Thanks to [@AdityaVallabh](https://github.com/AdityaVallabh) for the patch. + + * [PR #66](https://github.com/magiconair/properties/pull/66): use github actions + +### [1.8.6](https://github.com/magiconair/properties/tree/v1.8.6) - 23 Feb 2022 + + * [PR #57](https://github.com/magiconair/properties/pull/57):Fix "unreachable code" lint error + + Thanks to [@ellie](https://github.com/ellie) for the patch. + + * [PR #63](https://github.com/magiconair/properties/pull/63): Make TestMustGetParsedDuration backwards compatible + + This patch ensures that the `TestMustGetParsedDuration` still works with `go1.3` to make the + author happy until it affects real users. + + Thanks to [@maage](https://github.com/maage) for the patch. + +### [1.8.5](https://github.com/magiconair/properties/tree/v1.8.5) - 24 Mar 2021 + + * [PR #55](https://github.com/magiconair/properties/pull/55): Fix: Encoding Bug in Comments + + When reading comments \ are loaded correctly, but when writing they are then + replaced by \\. This leads to wrong comments when writing and reading multiple times. + + Thanks to [@doxsch](https://github.com/doxsch) for the patch. + +### [1.8.4](https://github.com/magiconair/properties/tree/v1.8.4) - 23 Sep 2020 + + * [PR #50](https://github.com/magiconair/properties/pull/50): enhance error message for circular references + + Thanks to [@sriv](https://github.com/sriv) for the patch. + +### [1.8.3](https://github.com/magiconair/properties/tree/v1.8.3) - 14 Sep 2020 + + * [PR #49](https://github.com/magiconair/properties/pull/49): Include the key in error message causing the circular reference + + The change is include the key in the error message which is causing the circular + reference when parsing/loading the properties files. + + Thanks to [@haroon-sheikh](https://github.com/haroon-sheikh) for the patch. + ### [1.8.2](https://github.com/magiconair/properties/tree/v1.8.2) - 25 Aug 2020 * [PR #36](https://github.com/magiconair/properties/pull/36): Escape backslash on write diff --git a/vendor/github.com/magiconair/properties/decode.go b/vendor/github.com/magiconair/properties/decode.go index 3ebf8049..8e6aa441 100644 --- a/vendor/github.com/magiconair/properties/decode.go +++ b/vendor/github.com/magiconair/properties/decode.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -48,49 +48,49 @@ import ( // // Examples: // -// // Field is ignored. -// Field int `properties:"-"` +// // Field is ignored. +// Field int `properties:"-"` // -// // Field is assigned value of 'Field'. -// Field int +// // Field is assigned value of 'Field'. +// Field int // -// // Field is assigned value of 'myName'. -// Field int `properties:"myName"` +// // Field is assigned value of 'myName'. +// Field int `properties:"myName"` // -// // Field is assigned value of key 'myName' and has a default -// // value 15 if the key does not exist. -// Field int `properties:"myName,default=15"` +// // Field is assigned value of key 'myName' and has a default +// // value 15 if the key does not exist. +// Field int `properties:"myName,default=15"` // -// // Field is assigned value of key 'Field' and has a default -// // value 15 if the key does not exist. -// Field int `properties:",default=15"` +// // Field is assigned value of key 'Field' and has a default +// // value 15 if the key does not exist. +// Field int `properties:",default=15"` // -// // Field is assigned value of key 'date' and the date -// // is in format 2006-01-02 -// Field time.Time `properties:"date,layout=2006-01-02"` +// // Field is assigned value of key 'date' and the date +// // is in format 2006-01-02 +// Field time.Time `properties:"date,layout=2006-01-02"` // -// // Field is assigned the non-empty and whitespace trimmed -// // values of key 'Field' split by commas. -// Field []string +// // Field is assigned the non-empty and whitespace trimmed +// // values of key 'Field' split by commas. +// Field []string // -// // Field is assigned the non-empty and whitespace trimmed -// // values of key 'Field' split by commas and has a default -// // value ["a", "b", "c"] if the key does not exist. -// Field []string `properties:",default=a;b;c"` +// // Field is assigned the non-empty and whitespace trimmed +// // values of key 'Field' split by commas and has a default +// // value ["a", "b", "c"] if the key does not exist. +// Field []string `properties:",default=a;b;c"` // -// // Field is decoded recursively with "Field." as key prefix. -// Field SomeStruct +// // Field is decoded recursively with "Field." as key prefix. +// Field SomeStruct // -// // Field is decoded recursively with "myName." as key prefix. -// Field SomeStruct `properties:"myName"` +// // Field is decoded recursively with "myName." as key prefix. +// Field SomeStruct `properties:"myName"` // -// // Field is decoded recursively with "Field." as key prefix -// // and the next dotted element of the key as map key. -// Field map[string]string +// // Field is decoded recursively with "Field." as key prefix +// // and the next dotted element of the key as map key. +// Field map[string]string // -// // Field is decoded recursively with "myName." as key prefix -// // and the next dotted element of the key as map key. -// Field map[string]string `properties:"myName"` +// // Field is decoded recursively with "myName." as key prefix +// // and the next dotted element of the key as map key. +// Field map[string]string `properties:"myName"` func (p *Properties) Decode(x interface{}) error { t, v := reflect.TypeOf(x), reflect.ValueOf(x) if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct { diff --git a/vendor/github.com/magiconair/properties/doc.go b/vendor/github.com/magiconair/properties/doc.go index f8822da2..7c797931 100644 --- a/vendor/github.com/magiconair/properties/doc.go +++ b/vendor/github.com/magiconair/properties/doc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -13,7 +13,7 @@ // // To load a single properties file use MustLoadFile(): // -// p := properties.MustLoadFile(filename, properties.UTF8) +// p := properties.MustLoadFile(filename, properties.UTF8) // // To load multiple properties files use MustLoadFiles() // which loads the files in the given order and merges the @@ -23,25 +23,25 @@ // Filenames can contain environment variables which are expanded // before loading. // -// f1 := "/etc/myapp/myapp.conf" -// f2 := "/home/${USER}/myapp.conf" -// p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true) +// f1 := "/etc/myapp/myapp.conf" +// f2 := "/home/${USER}/myapp.conf" +// p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true) // // All of the different key/value delimiters ' ', ':' and '=' are // supported as well as the comment characters '!' and '#' and // multi-line values. // -// ! this is a comment -// # and so is this +// ! this is a comment +// # and so is this // -// # the following expressions are equal -// key value -// key=value -// key:value -// key = value -// key : value -// key = val\ -// ue +// # the following expressions are equal +// key value +// key=value +// key:value +// key = value +// key : value +// key = val\ +// ue // // Properties stores all comments preceding a key and provides // GetComments() and SetComments() methods to retrieve and @@ -55,62 +55,62 @@ // and malformed expressions are not allowed and cause an // error. Expansion of environment variables is supported. // -// # standard property -// key = value +// # standard property +// key = value // -// # property expansion: key2 = value -// key2 = ${key} +// # property expansion: key2 = value +// key2 = ${key} // -// # recursive expansion: key3 = value -// key3 = ${key2} +// # recursive expansion: key3 = value +// key3 = ${key2} // -// # circular reference (error) -// key = ${key} +// # circular reference (error) +// key = ${key} // -// # malformed expression (error) -// key = ${ke +// # malformed expression (error) +// key = ${ke // -// # refers to the users' home dir -// home = ${HOME} +// # refers to the users' home dir +// home = ${HOME} // -// # local key takes precedence over env var: u = foo -// USER = foo -// u = ${USER} +// # local key takes precedence over env var: u = foo +// USER = foo +// u = ${USER} // // The default property expansion format is ${key} but can be // changed by setting different pre- and postfix values on the // Properties object. // -// p := properties.NewProperties() -// p.Prefix = "#[" -// p.Postfix = "]#" +// p := properties.NewProperties() +// p.Prefix = "#[" +// p.Postfix = "]#" // // Properties provides convenience functions for getting typed // values with default values if the key does not exist or the // type conversion failed. // -// # Returns true if the value is either "1", "on", "yes" or "true" -// # Returns false for every other value and the default value if -// # the key does not exist. -// v = p.GetBool("key", false) +// # Returns true if the value is either "1", "on", "yes" or "true" +// # Returns false for every other value and the default value if +// # the key does not exist. +// v = p.GetBool("key", false) // -// # Returns the value if the key exists and the format conversion -// # was successful. Otherwise, the default value is returned. -// v = p.GetInt64("key", 999) -// v = p.GetUint64("key", 999) -// v = p.GetFloat64("key", 123.0) -// v = p.GetString("key", "def") -// v = p.GetDuration("key", 999) +// # Returns the value if the key exists and the format conversion +// # was successful. Otherwise, the default value is returned. +// v = p.GetInt64("key", 999) +// v = p.GetUint64("key", 999) +// v = p.GetFloat64("key", 123.0) +// v = p.GetString("key", "def") +// v = p.GetDuration("key", 999) // // As an alternative properties may be applied with the standard // library's flag implementation at any time. // -// # Standard configuration -// v = flag.Int("key", 999, "help message") -// flag.Parse() +// # Standard configuration +// v = flag.Int("key", 999, "help message") +// flag.Parse() // -// # Merge p into the flag set -// p.MustFlag(flag.CommandLine) +// # Merge p into the flag set +// p.MustFlag(flag.CommandLine) // // Properties provides several MustXXX() convenience functions // which will terminate the app if an error occurs. The behavior @@ -119,30 +119,30 @@ // of logging the error set a different ErrorHandler before // you use the Properties package. // -// properties.ErrorHandler = properties.PanicHandler +// properties.ErrorHandler = properties.PanicHandler // -// # Will panic instead of logging an error -// p := properties.MustLoadFile("config.properties") +// # Will panic instead of logging an error +// p := properties.MustLoadFile("config.properties") // // You can also provide your own ErrorHandler function. The only requirement // is that the error handler function must exit after handling the error. // -// properties.ErrorHandler = func(err error) { -// fmt.Println(err) -// os.Exit(1) -// } +// properties.ErrorHandler = func(err error) { +// fmt.Println(err) +// os.Exit(1) +// } // -// # Will write to stdout and then exit -// p := properties.MustLoadFile("config.properties") +// # Will write to stdout and then exit +// p := properties.MustLoadFile("config.properties") // // Properties can also be loaded into a struct via the `Decode` // method, e.g. // -// type S struct { -// A string `properties:"a,default=foo"` -// D time.Duration `properties:"timeout,default=5s"` -// E time.Time `properties:"expires,layout=2006-01-02,default=2015-01-01"` -// } +// type S struct { +// A string `properties:"a,default=foo"` +// D time.Duration `properties:"timeout,default=5s"` +// E time.Time `properties:"expires,layout=2006-01-02,default=2015-01-01"` +// } // // See `Decode()` method for the full documentation. // @@ -152,5 +152,4 @@ // http://en.wikipedia.org/wiki/.properties // // http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29 -// package properties diff --git a/vendor/github.com/magiconair/properties/integrate.go b/vendor/github.com/magiconair/properties/integrate.go index 74d38dc6..35d0ae97 100644 --- a/vendor/github.com/magiconair/properties/integrate.go +++ b/vendor/github.com/magiconair/properties/integrate.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -10,8 +10,9 @@ import "flag" // the respective key for flag.Flag.Name. // // It's use is recommended with command line arguments as in: -// flag.Parse() -// p.MustFlag(flag.CommandLine) +// +// flag.Parse() +// p.MustFlag(flag.CommandLine) func (p *Properties) MustFlag(dst *flag.FlagSet) { m := make(map[string]*flag.Flag) dst.VisitAll(func(f *flag.Flag) { diff --git a/vendor/github.com/magiconair/properties/lex.go b/vendor/github.com/magiconair/properties/lex.go index e1e9dd7b..3d15a1f6 100644 --- a/vendor/github.com/magiconair/properties/lex.go +++ b/vendor/github.com/magiconair/properties/lex.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/vendor/github.com/magiconair/properties/load.go b/vendor/github.com/magiconair/properties/load.go index c83c2dad..635368dc 100644 --- a/vendor/github.com/magiconair/properties/load.go +++ b/vendor/github.com/magiconair/properties/load.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/vendor/github.com/magiconair/properties/parser.go b/vendor/github.com/magiconair/properties/parser.go index 430e4fcd..fccfd39f 100644 --- a/vendor/github.com/magiconair/properties/parser.go +++ b/vendor/github.com/magiconair/properties/parser.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/vendor/github.com/magiconair/properties/properties.go b/vendor/github.com/magiconair/properties/properties.go index 62ae2d67..fb2f7b40 100644 --- a/vendor/github.com/magiconair/properties/properties.go +++ b/vendor/github.com/magiconair/properties/properties.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -700,22 +700,17 @@ func (p *Properties) Delete(key string) { // Merge merges properties, comments and keys from other *Properties into p func (p *Properties) Merge(other *Properties) { + for _, k := range other.k { + if _, ok := p.m[k]; !ok { + p.k = append(p.k, k) + } + } for k, v := range other.m { p.m[k] = v } for k, v := range other.c { p.c[k] = v } - -outer: - for _, otherKey := range other.k { - for _, key := range p.k { - if otherKey == key { - continue outer - } - } - p.k = append(p.k, otherKey) - } } // ---------------------------------------------------------------------------- diff --git a/vendor/github.com/magiconair/properties/rangecheck.go b/vendor/github.com/magiconair/properties/rangecheck.go index b013a2e5..dbd60b36 100644 --- a/vendor/github.com/magiconair/properties/rangecheck.go +++ b/vendor/github.com/magiconair/properties/rangecheck.go @@ -1,4 +1,4 @@ -// Copyright 2018 Frank Schroeder. All rights reserved. +// Copyright 2013-2022 Frank Schroeder. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml index 793fb184..3aa1840e 100644 --- a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml +++ b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml @@ -16,7 +16,11 @@ builds: mod_timestamp: '{{ .CommitTimestamp }}' targets: - linux_amd64 + - linux_arm64 + - linux_arm - windows_amd64 + - windows_arm64 + - windows_arm - darwin_amd64 - darwin_arm64 - id: tomljson @@ -31,7 +35,11 @@ builds: mod_timestamp: '{{ .CommitTimestamp }}' targets: - linux_amd64 + - linux_arm64 + - linux_arm - windows_amd64 + - windows_arm64 + - windows_arm - darwin_amd64 - darwin_arm64 - id: jsontoml @@ -46,7 +54,11 @@ builds: mod_timestamp: '{{ .CommitTimestamp }}' targets: - linux_amd64 + - linux_arm64 + - linux_arm - windows_amd64 + - windows_arm64 + - windows_arm - darwin_amd64 - darwin_arm64 universal_binaries: diff --git a/vendor/github.com/pelletier/go-toml/v2/README.md b/vendor/github.com/pelletier/go-toml/v2/README.md index a63c3a79..9f8439cc 100644 --- a/vendor/github.com/pelletier/go-toml/v2/README.md +++ b/vendor/github.com/pelletier/go-toml/v2/README.md @@ -140,6 +140,17 @@ fmt.Println(string(b)) [marshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Marshal +## Unstable API + +This API does not yet follow the backward compatibility guarantees of this +library. They provide early access to features that may have rough edges or an +API subject to change. + +### Parser + +Parser is the unstable API that allows iterative parsing of a TOML document at +the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable. + ## Benchmarks Execution time speedup compared to other Go TOML libraries: diff --git a/vendor/github.com/pelletier/go-toml/v2/decode.go b/vendor/github.com/pelletier/go-toml/v2/decode.go index 4af96536..3a860d0f 100644 --- a/vendor/github.com/pelletier/go-toml/v2/decode.go +++ b/vendor/github.com/pelletier/go-toml/v2/decode.go @@ -5,6 +5,8 @@ import ( "math" "strconv" "time" + + "github.com/pelletier/go-toml/v2/unstable" ) func parseInteger(b []byte) (int64, error) { @@ -32,7 +34,7 @@ func parseLocalDate(b []byte) (LocalDate, error) { var date LocalDate if len(b) != 10 || b[4] != '-' || b[7] != '-' { - return date, newDecodeError(b, "dates are expected to have the format YYYY-MM-DD") + return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD") } var err error @@ -53,7 +55,7 @@ func parseLocalDate(b []byte) (LocalDate, error) { } if !isValidDate(date.Year, date.Month, date.Day) { - return LocalDate{}, newDecodeError(b, "impossible date") + return LocalDate{}, unstable.NewParserError(b, "impossible date") } return date, nil @@ -64,7 +66,7 @@ func parseDecimalDigits(b []byte) (int, error) { for i, c := range b { if c < '0' || c > '9' { - return 0, newDecodeError(b[i:i+1], "expected digit (0-9)") + return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)") } v *= 10 v += int(c - '0') @@ -97,7 +99,7 @@ func parseDateTime(b []byte) (time.Time, error) { } else { const dateTimeByteLen = 6 if len(b) != dateTimeByteLen { - return time.Time{}, newDecodeError(b, "invalid date-time timezone") + return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone") } var direction int switch b[0] { @@ -106,11 +108,11 @@ func parseDateTime(b []byte) (time.Time, error) { case '+': direction = +1 default: - return time.Time{}, newDecodeError(b[:1], "invalid timezone offset character") + return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character") } if b[3] != ':' { - return time.Time{}, newDecodeError(b[3:4], "expected a : separator") + return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator") } hours, err := parseDecimalDigits(b[1:3]) @@ -118,7 +120,7 @@ func parseDateTime(b []byte) (time.Time, error) { return time.Time{}, err } if hours > 23 { - return time.Time{}, newDecodeError(b[:1], "invalid timezone offset hours") + return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours") } minutes, err := parseDecimalDigits(b[4:6]) @@ -126,7 +128,7 @@ func parseDateTime(b []byte) (time.Time, error) { return time.Time{}, err } if minutes > 59 { - return time.Time{}, newDecodeError(b[:1], "invalid timezone offset minutes") + return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes") } seconds := direction * (hours*3600 + minutes*60) @@ -139,7 +141,7 @@ func parseDateTime(b []byte) (time.Time, error) { } if len(b) > 0 { - return time.Time{}, newDecodeError(b, "extra bytes at the end of the timezone") + return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone") } t := time.Date( @@ -160,7 +162,7 @@ func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { const localDateTimeByteMinLen = 11 if len(b) < localDateTimeByteMinLen { - return dt, nil, newDecodeError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") + return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") } date, err := parseLocalDate(b[:10]) @@ -171,7 +173,7 @@ func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { sep := b[10] if sep != 'T' && sep != ' ' && sep != 't' { - return dt, nil, newDecodeError(b[10:11], "datetime separator is expected to be T or a space") + return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space") } t, rest, err := parseLocalTime(b[11:]) @@ -195,7 +197,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { // check if b matches to have expected format HH:MM:SS[.NNNNNN] const localTimeByteLen = 8 if len(b) < localTimeByteLen { - return t, nil, newDecodeError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") + return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") } var err error @@ -206,10 +208,10 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { } if t.Hour > 23 { - return t, nil, newDecodeError(b[0:2], "hour cannot be greater 23") + return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23") } if b[2] != ':' { - return t, nil, newDecodeError(b[2:3], "expecting colon between hours and minutes") + return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes") } t.Minute, err = parseDecimalDigits(b[3:5]) @@ -217,10 +219,10 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { return t, nil, err } if t.Minute > 59 { - return t, nil, newDecodeError(b[3:5], "minutes cannot be greater 59") + return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59") } if b[5] != ':' { - return t, nil, newDecodeError(b[5:6], "expecting colon between minutes and seconds") + return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds") } t.Second, err = parseDecimalDigits(b[6:8]) @@ -229,7 +231,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { } if t.Second > 60 { - return t, nil, newDecodeError(b[6:8], "seconds cannot be greater 60") + return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60") } b = b[8:] @@ -242,7 +244,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { for i, c := range b[1:] { if !isDigit(c) { if i == 0 { - return t, nil, newDecodeError(b[0:1], "need at least one digit after fraction point") + return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point") } break } @@ -266,7 +268,7 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { } if precision == 0 { - return t, nil, newDecodeError(b[:1], "nanoseconds need at least one digit") + return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit") } t.Nanosecond = frac * nspow[precision] @@ -289,24 +291,24 @@ func parseFloat(b []byte) (float64, error) { } if cleaned[0] == '.' { - return 0, newDecodeError(b, "float cannot start with a dot") + return 0, unstable.NewParserError(b, "float cannot start with a dot") } if cleaned[len(cleaned)-1] == '.' { - return 0, newDecodeError(b, "float cannot end with a dot") + return 0, unstable.NewParserError(b, "float cannot end with a dot") } dotAlreadySeen := false for i, c := range cleaned { if c == '.' { if dotAlreadySeen { - return 0, newDecodeError(b[i:i+1], "float can have at most one decimal point") + return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point") } if !isDigit(cleaned[i-1]) { - return 0, newDecodeError(b[i-1:i+1], "float decimal point must be preceded by a digit") + return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit") } if !isDigit(cleaned[i+1]) { - return 0, newDecodeError(b[i:i+2], "float decimal point must be followed by a digit") + return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit") } dotAlreadySeen = true } @@ -317,12 +319,12 @@ func parseFloat(b []byte) (float64, error) { start = 1 } if cleaned[start] == '0' && isDigit(cleaned[start+1]) { - return 0, newDecodeError(b, "float integer part cannot have leading zeroes") + return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes") } f, err := strconv.ParseFloat(string(cleaned), 64) if err != nil { - return 0, newDecodeError(b, "unable to parse float: %w", err) + return 0, unstable.NewParserError(b, "unable to parse float: %w", err) } return f, nil @@ -336,7 +338,7 @@ func parseIntHex(b []byte) (int64, error) { i, err := strconv.ParseInt(string(cleaned), 16, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse hexadecimal number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err) } return i, nil @@ -350,7 +352,7 @@ func parseIntOct(b []byte) (int64, error) { i, err := strconv.ParseInt(string(cleaned), 8, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse octal number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err) } return i, nil @@ -364,7 +366,7 @@ func parseIntBin(b []byte) (int64, error) { i, err := strconv.ParseInt(string(cleaned), 2, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse binary number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err) } return i, nil @@ -387,12 +389,12 @@ func parseIntDec(b []byte) (int64, error) { } if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' { - return 0, newDecodeError(b, "leading zero not allowed on decimal number") + return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number") } i, err := strconv.ParseInt(string(cleaned), 10, 64) if err != nil { - return 0, newDecodeError(b, "couldn't parse decimal number: %w", err) + return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err) } return i, nil @@ -409,11 +411,11 @@ func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { } if b[start] == '_' { - return nil, newDecodeError(b[start:start+1], "number cannot start with underscore") + return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore") } if b[len(b)-1] == '_' { - return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore") + return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") } // fast path @@ -435,7 +437,7 @@ func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { c := b[i] if c == '_' { if !before { - return nil, newDecodeError(b[i-1:i+1], "number must have at least one digit between underscores") + return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") } before = false } else { @@ -449,11 +451,11 @@ func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { if b[0] == '_' { - return nil, newDecodeError(b[0:1], "number cannot start with underscore") + return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore") } if b[len(b)-1] == '_' { - return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore") + return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") } // fast path @@ -476,10 +478,10 @@ func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { switch c { case '_': if !before { - return nil, newDecodeError(b[i-1:i+1], "number must have at least one digit between underscores") + return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") } if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') { - return nil, newDecodeError(b[i+1:i+2], "cannot have underscore before exponent") + return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent") } before = false case '+', '-': @@ -488,15 +490,15 @@ func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { before = false case 'e', 'E': if i < len(b)-1 && b[i+1] == '_' { - return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after exponent") + return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent") } cleaned = append(cleaned, c) case '.': if i < len(b)-1 && b[i+1] == '_' { - return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after decimal point") + return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point") } if i > 0 && b[i-1] == '_' { - return nil, newDecodeError(b[i-1:i], "cannot have underscore before decimal point") + return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point") } cleaned = append(cleaned, c) default: @@ -542,3 +544,7 @@ func daysIn(m int, year int) int { func isLeap(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) } + +func isDigit(r byte) bool { + return r >= '0' && r <= '9' +} diff --git a/vendor/github.com/pelletier/go-toml/v2/errors.go b/vendor/github.com/pelletier/go-toml/v2/errors.go index 5e6635c3..309733f1 100644 --- a/vendor/github.com/pelletier/go-toml/v2/errors.go +++ b/vendor/github.com/pelletier/go-toml/v2/errors.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/pelletier/go-toml/v2/internal/danger" + "github.com/pelletier/go-toml/v2/unstable" ) // DecodeError represents an error encountered during the parsing or decoding @@ -55,25 +56,6 @@ func (s *StrictMissingError) String() string { type Key []string -// internal version of DecodeError that is used as the base to create a -// DecodeError with full context. -type decodeError struct { - highlight []byte - message string - key Key // optional -} - -func (de *decodeError) Error() string { - return de.message -} - -func newDecodeError(highlight []byte, format string, args ...interface{}) error { - return &decodeError{ - highlight: highlight, - message: fmt.Errorf(format, args...).Error(), - } -} - // Error returns the error message contained in the DecodeError. func (e *DecodeError) Error() string { return "toml: " + e.message @@ -103,13 +85,14 @@ func (e *DecodeError) Key() Key { // // The function copies all bytes used in DecodeError, so that document and // highlight can be freely deallocated. +// //nolint:funlen -func wrapDecodeError(document []byte, de *decodeError) *DecodeError { - offset := danger.SubsliceOffset(document, de.highlight) +func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError { + offset := danger.SubsliceOffset(document, de.Highlight) errMessage := de.Error() errLine, errColumn := positionAtEnd(document[:offset]) - before, after := linesOfContext(document, de.highlight, offset, 3) + before, after := linesOfContext(document, de.Highlight, offset, 3) var buf strings.Builder @@ -139,7 +122,7 @@ func wrapDecodeError(document []byte, de *decodeError) *DecodeError { buf.Write(before[0]) } - buf.Write(de.highlight) + buf.Write(de.Highlight) if len(after) > 0 { buf.Write(after[0]) @@ -157,7 +140,7 @@ func wrapDecodeError(document []byte, de *decodeError) *DecodeError { buf.WriteString(strings.Repeat(" ", len(before[0]))) } - buf.WriteString(strings.Repeat("~", len(de.highlight))) + buf.WriteString(strings.Repeat("~", len(de.Highlight))) if len(errMessage) > 0 { buf.WriteString(" ") @@ -182,7 +165,7 @@ func wrapDecodeError(document []byte, de *decodeError) *DecodeError { message: errMessage, line: errLine, column: errColumn, - key: de.key, + key: de.Key, human: buf.String(), } } diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go b/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go deleted file mode 100644 index 120f16e5..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/internal/ast/builder.go +++ /dev/null @@ -1,51 +0,0 @@ -package ast - -type Reference int - -const InvalidReference Reference = -1 - -func (r Reference) Valid() bool { - return r != InvalidReference -} - -type Builder struct { - tree Root - lastIdx int -} - -func (b *Builder) Tree() *Root { - return &b.tree -} - -func (b *Builder) NodeAt(ref Reference) *Node { - return b.tree.at(ref) -} - -func (b *Builder) Reset() { - b.tree.nodes = b.tree.nodes[:0] - b.lastIdx = 0 -} - -func (b *Builder) Push(n Node) Reference { - b.lastIdx = len(b.tree.nodes) - b.tree.nodes = append(b.tree.nodes, n) - return Reference(b.lastIdx) -} - -func (b *Builder) PushAndChain(n Node) Reference { - newIdx := len(b.tree.nodes) - b.tree.nodes = append(b.tree.nodes, n) - if b.lastIdx >= 0 { - b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx - } - b.lastIdx = newIdx - return Reference(b.lastIdx) -} - -func (b *Builder) AttachChild(parent Reference, child Reference) { - b.tree.nodes[parent].child = int(child) - int(parent) -} - -func (b *Builder) Chain(from Reference, to Reference) { - b.tree.nodes[from].next = int(to) - int(from) -} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go new file mode 100644 index 00000000..80f698db --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go @@ -0,0 +1,42 @@ +package characters + +var invalidAsciiTable = [256]bool{ + 0x00: true, + 0x01: true, + 0x02: true, + 0x03: true, + 0x04: true, + 0x05: true, + 0x06: true, + 0x07: true, + 0x08: true, + // 0x09 TAB + // 0x0A LF + 0x0B: true, + 0x0C: true, + // 0x0D CR + 0x0E: true, + 0x0F: true, + 0x10: true, + 0x11: true, + 0x12: true, + 0x13: true, + 0x14: true, + 0x15: true, + 0x16: true, + 0x17: true, + 0x18: true, + 0x19: true, + 0x1A: true, + 0x1B: true, + 0x1C: true, + 0x1D: true, + 0x1E: true, + 0x1F: true, + // 0x20 - 0x7E Printable ASCII characters + 0x7F: true, +} + +func InvalidAscii(b byte) bool { + return invalidAsciiTable[b] +} diff --git a/vendor/github.com/pelletier/go-toml/v2/utf8.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go index d47a4f20..db4f45ac 100644 --- a/vendor/github.com/pelletier/go-toml/v2/utf8.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go @@ -1,4 +1,4 @@ -package toml +package characters import ( "unicode/utf8" @@ -32,7 +32,7 @@ func (u utf8Err) Zero() bool { // 0x9 => tab, ok // 0xA - 0x1F => invalid // 0x7F => invalid -func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { +func Utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { // Fast path. Check for and skip 8 bytes of ASCII characters per iteration. offset := 0 for len(p) >= 8 { @@ -48,7 +48,7 @@ func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { } for i, b := range p[:8] { - if invalidAscii(b) { + if InvalidAscii(b) { err.Index = offset + i err.Size = 1 return @@ -62,7 +62,7 @@ func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { for i := 0; i < n; { pi := p[i] if pi < utf8.RuneSelf { - if invalidAscii(pi) { + if InvalidAscii(pi) { err.Index = offset + i err.Size = 1 return @@ -106,11 +106,11 @@ func utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { } // Return the size of the next rune if valid, 0 otherwise. -func utf8ValidNext(p []byte) int { +func Utf8ValidNext(p []byte) int { c := p[0] if c < utf8.RuneSelf { - if invalidAscii(c) { + if InvalidAscii(c) { return 0 } return 1 @@ -140,47 +140,6 @@ func utf8ValidNext(p []byte) int { return size } -var invalidAsciiTable = [256]bool{ - 0x00: true, - 0x01: true, - 0x02: true, - 0x03: true, - 0x04: true, - 0x05: true, - 0x06: true, - 0x07: true, - 0x08: true, - // 0x09 TAB - // 0x0A LF - 0x0B: true, - 0x0C: true, - // 0x0D CR - 0x0E: true, - 0x0F: true, - 0x10: true, - 0x11: true, - 0x12: true, - 0x13: true, - 0x14: true, - 0x15: true, - 0x16: true, - 0x17: true, - 0x18: true, - 0x19: true, - 0x1A: true, - 0x1B: true, - 0x1C: true, - 0x1D: true, - 0x1E: true, - 0x1F: true, - // 0x20 - 0x7E Printable ASCII characters - 0x7F: true, -} - -func invalidAscii(b byte) bool { - return invalidAsciiTable[b] -} - // acceptRange gives the range of valid values for the second byte in a UTF-8 // sequence. type acceptRange struct { diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go index 7c148f48..149b17f5 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go @@ -1,8 +1,6 @@ package tracker -import ( - "github.com/pelletier/go-toml/v2/internal/ast" -) +import "github.com/pelletier/go-toml/v2/unstable" // KeyTracker is a tracker that keeps track of the current Key as the AST is // walked. @@ -11,19 +9,19 @@ type KeyTracker struct { } // UpdateTable sets the state of the tracker with the AST table node. -func (t *KeyTracker) UpdateTable(node *ast.Node) { +func (t *KeyTracker) UpdateTable(node *unstable.Node) { t.reset() t.Push(node) } // UpdateArrayTable sets the state of the tracker with the AST array table node. -func (t *KeyTracker) UpdateArrayTable(node *ast.Node) { +func (t *KeyTracker) UpdateArrayTable(node *unstable.Node) { t.reset() t.Push(node) } // Push the given key on the stack. -func (t *KeyTracker) Push(node *ast.Node) { +func (t *KeyTracker) Push(node *unstable.Node) { it := node.Key() for it.Next() { t.k = append(t.k, string(it.Node().Data)) @@ -31,7 +29,7 @@ func (t *KeyTracker) Push(node *ast.Node) { } // Pop key from stack. -func (t *KeyTracker) Pop(node *ast.Node) { +func (t *KeyTracker) Pop(node *unstable.Node) { it := node.Key() for it.Next() { t.k = t.k[:len(t.k)-1] diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go index a7ee05ba..40e23f83 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go @@ -5,7 +5,7 @@ import ( "fmt" "sync" - "github.com/pelletier/go-toml/v2/internal/ast" + "github.com/pelletier/go-toml/v2/unstable" ) type keyKind uint8 @@ -150,23 +150,23 @@ func (s *SeenTracker) setExplicitFlag(parentIdx int) { // CheckExpression takes a top-level node and checks that it does not contain // keys that have been seen in previous calls, and validates that types are // consistent. -func (s *SeenTracker) CheckExpression(node *ast.Node) error { +func (s *SeenTracker) CheckExpression(node *unstable.Node) error { if s.entries == nil { s.reset() } switch node.Kind { - case ast.KeyValue: + case unstable.KeyValue: return s.checkKeyValue(node) - case ast.Table: + case unstable.Table: return s.checkTable(node) - case ast.ArrayTable: + case unstable.ArrayTable: return s.checkArrayTable(node) default: panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind)) } } -func (s *SeenTracker) checkTable(node *ast.Node) error { +func (s *SeenTracker) checkTable(node *unstable.Node) error { if s.currentIdx >= 0 { s.setExplicitFlag(s.currentIdx) } @@ -219,7 +219,7 @@ func (s *SeenTracker) checkTable(node *ast.Node) error { return nil } -func (s *SeenTracker) checkArrayTable(node *ast.Node) error { +func (s *SeenTracker) checkArrayTable(node *unstable.Node) error { if s.currentIdx >= 0 { s.setExplicitFlag(s.currentIdx) } @@ -267,7 +267,7 @@ func (s *SeenTracker) checkArrayTable(node *ast.Node) error { return nil } -func (s *SeenTracker) checkKeyValue(node *ast.Node) error { +func (s *SeenTracker) checkKeyValue(node *unstable.Node) error { parentIdx := s.currentIdx it := node.Key() @@ -297,26 +297,26 @@ func (s *SeenTracker) checkKeyValue(node *ast.Node) error { value := node.Value() switch value.Kind { - case ast.InlineTable: + case unstable.InlineTable: return s.checkInlineTable(value) - case ast.Array: + case unstable.Array: return s.checkArray(value) } return nil } -func (s *SeenTracker) checkArray(node *ast.Node) error { +func (s *SeenTracker) checkArray(node *unstable.Node) error { it := node.Children() for it.Next() { n := it.Node() switch n.Kind { - case ast.InlineTable: + case unstable.InlineTable: err := s.checkInlineTable(n) if err != nil { return err } - case ast.Array: + case unstable.Array: err := s.checkArray(n) if err != nil { return err @@ -326,7 +326,7 @@ func (s *SeenTracker) checkArray(node *ast.Node) error { return nil } -func (s *SeenTracker) checkInlineTable(node *ast.Node) error { +func (s *SeenTracker) checkInlineTable(node *unstable.Node) error { if pool.New == nil { pool.New = func() interface{} { return &SeenTracker{} diff --git a/vendor/github.com/pelletier/go-toml/v2/localtime.go b/vendor/github.com/pelletier/go-toml/v2/localtime.go index 30a31dcb..a856bfdb 100644 --- a/vendor/github.com/pelletier/go-toml/v2/localtime.go +++ b/vendor/github.com/pelletier/go-toml/v2/localtime.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" "time" + + "github.com/pelletier/go-toml/v2/unstable" ) // LocalDate represents a calendar day in no specific timezone. @@ -75,7 +77,7 @@ func (d LocalTime) MarshalText() ([]byte, error) { func (d *LocalTime) UnmarshalText(b []byte) error { res, left, err := parseLocalTime(b) if err == nil && len(left) != 0 { - err = newDecodeError(left, "extra characters") + err = unstable.NewParserError(left, "extra characters") } if err != nil { return err @@ -109,7 +111,7 @@ func (d LocalDateTime) MarshalText() ([]byte, error) { func (d *LocalDateTime) UnmarshalText(data []byte) error { res, left, err := parseLocalDateTime(data) if err == nil && len(left) != 0 { - err = newDecodeError(left, "extra characters") + err = unstable.NewParserError(left, "extra characters") } if err != nil { return err diff --git a/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/marshaler.go index c14c1cea..07aceb90 100644 --- a/vendor/github.com/pelletier/go-toml/v2/marshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/marshaler.go @@ -12,6 +12,8 @@ import ( "strings" "time" "unicode" + + "github.com/pelletier/go-toml/v2/internal/characters" ) // Marshal serializes a Go value as a TOML document. @@ -54,7 +56,7 @@ func NewEncoder(w io.Writer) *Encoder { // This behavior can be controlled on an individual struct field basis with the // inline tag: // -// MyField `inline:"true"` +// MyField `toml:",inline"` func (enc *Encoder) SetTablesInline(inline bool) *Encoder { enc.tablesInline = inline return enc @@ -65,7 +67,7 @@ func (enc *Encoder) SetTablesInline(inline bool) *Encoder { // // This behavior can be controlled on an individual struct field basis with the multiline tag: // -// MyField `multiline:"true"` +// MyField `multiline:"true"` func (enc *Encoder) SetArraysMultiline(multiline bool) *Encoder { enc.arraysMultiline = multiline return enc @@ -89,7 +91,7 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder { // // If v cannot be represented to TOML it returns an error. // -// Encoding rules +// # Encoding rules // // A top level slice containing only maps or structs is encoded as [[table // array]]. @@ -107,10 +109,30 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder { // a newline character or a single quote. In that case they are emitted as // quoted strings. // +// Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so +// results in an error. This rule exists because the TOML specification only +// requires parsers to support at least the 64 bits integer range. Allowing +// larger numbers would create non-standard TOML documents, which may not be +// readable (at best) by other implementations. To encode such numbers, a +// solution is a custom type that implements encoding.TextMarshaler. +// // When encoding structs, fields are encoded in order of definition, with their // exact name. // -// Struct tags +// Tables and array tables are separated by empty lines. However, consecutive +// subtables definitions are not. For example: +// +// [top1] +// +// [top2] +// [top2.child1] +// +// [[array]] +// +// [[array]] +// [array.child2] +// +// # Struct tags // // The encoding of each public struct field can be customized by the format // string in the "toml" key of the struct field's tag. This follows @@ -303,7 +325,11 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e b = append(b, "false"...) } case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: - b = strconv.AppendUint(b, v.Uint(), 10) + x := v.Uint() + if x > uint64(math.MaxInt64) { + return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64)) + } + b = strconv.AppendUint(b, x, 10) case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: b = strconv.AppendInt(b, v.Int(), 10) default: @@ -322,13 +348,13 @@ func isNil(v reflect.Value) bool { } } +func shouldOmitEmpty(options valueOptions, v reflect.Value) bool { + return options.omitempty && isEmptyValue(v) +} + func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) { var err error - if (ctx.options.omitempty || options.omitempty) && isEmptyValue(v) { - return b, nil - } - if !ctx.inline { b = enc.encodeComment(ctx.indent, options.comment, b) } @@ -354,6 +380,8 @@ func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v r func isEmptyValue(v reflect.Value) bool { switch v.Kind() { + case reflect.Struct: + return isEmptyStruct(v) case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: @@ -370,6 +398,34 @@ func isEmptyValue(v reflect.Value) bool { return false } +func isEmptyStruct(v reflect.Value) bool { + // TODO: merge with walkStruct and cache. + typ := v.Type() + for i := 0; i < typ.NumField(); i++ { + fieldType := typ.Field(i) + + // only consider exported fields + if fieldType.PkgPath != "" { + continue + } + + tag := fieldType.Tag.Get("toml") + + // special field name to skip field + if tag == "-" { + continue + } + + f := v.Field(i) + + if !isEmptyValue(f) { + return false + } + } + + return true +} + const literalQuote = '\'' func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte { @@ -383,7 +439,7 @@ func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byt func needsQuoting(v string) bool { // TODO: vectorize for _, b := range []byte(v) { - if b == '\'' || b == '\r' || b == '\n' || invalidAscii(b) { + if b == '\'' || b == '\r' || b == '\n' || characters.InvalidAscii(b) { return true } } @@ -399,7 +455,6 @@ func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte { return b } -//nolint:cyclop func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte { stringQuote := `"` @@ -746,7 +801,13 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro } ctx.skipTableHeader = false + hasNonEmptyKV := false for _, kv := range t.kvs { + if shouldOmitEmpty(kv.Options, kv.Value) { + continue + } + hasNonEmptyKV = true + ctx.setKey(kv.Key) b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value) @@ -757,7 +818,20 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro b = append(b, '\n') } + first := true for _, table := range t.tables { + if shouldOmitEmpty(table.Options, table.Value) { + continue + } + if first { + first = false + if hasNonEmptyKV { + b = append(b, '\n') + } + } else { + b = append(b, "\n"...) + } + ctx.setKey(table.Key) ctx.options = table.Options @@ -766,8 +840,6 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro if err != nil { return nil, err } - - b = append(b, '\n') } return b, nil @@ -780,6 +852,10 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte first := true for _, kv := range t.kvs { + if shouldOmitEmpty(kv.Options, kv.Value) { + continue + } + if first { first = false } else { @@ -795,7 +871,7 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte } if len(t.tables) > 0 { - panic("inline table cannot contain nested tables, online key-values") + panic("inline table cannot contain nested tables, only key-values") } b = append(b, "}"...) @@ -894,6 +970,10 @@ func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect. b = enc.encodeComment(ctx.indent, ctx.options.comment, b) for i := 0; i < v.Len(); i++ { + if i != 0 { + b = append(b, "\n"...) + } + b = append(b, scratch...) var err error diff --git a/vendor/github.com/pelletier/go-toml/v2/strict.go b/vendor/github.com/pelletier/go-toml/v2/strict.go index b7830d13..802e7e4d 100644 --- a/vendor/github.com/pelletier/go-toml/v2/strict.go +++ b/vendor/github.com/pelletier/go-toml/v2/strict.go @@ -1,9 +1,9 @@ package toml import ( - "github.com/pelletier/go-toml/v2/internal/ast" "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" + "github.com/pelletier/go-toml/v2/unstable" ) type strict struct { @@ -12,10 +12,10 @@ type strict struct { // Tracks the current key being processed. key tracker.KeyTracker - missing []decodeError + missing []unstable.ParserError } -func (s *strict) EnterTable(node *ast.Node) { +func (s *strict) EnterTable(node *unstable.Node) { if !s.Enabled { return } @@ -23,7 +23,7 @@ func (s *strict) EnterTable(node *ast.Node) { s.key.UpdateTable(node) } -func (s *strict) EnterArrayTable(node *ast.Node) { +func (s *strict) EnterArrayTable(node *unstable.Node) { if !s.Enabled { return } @@ -31,7 +31,7 @@ func (s *strict) EnterArrayTable(node *ast.Node) { s.key.UpdateArrayTable(node) } -func (s *strict) EnterKeyValue(node *ast.Node) { +func (s *strict) EnterKeyValue(node *unstable.Node) { if !s.Enabled { return } @@ -39,7 +39,7 @@ func (s *strict) EnterKeyValue(node *ast.Node) { s.key.Push(node) } -func (s *strict) ExitKeyValue(node *ast.Node) { +func (s *strict) ExitKeyValue(node *unstable.Node) { if !s.Enabled { return } @@ -47,27 +47,27 @@ func (s *strict) ExitKeyValue(node *ast.Node) { s.key.Pop(node) } -func (s *strict) MissingTable(node *ast.Node) { +func (s *strict) MissingTable(node *unstable.Node) { if !s.Enabled { return } - s.missing = append(s.missing, decodeError{ - highlight: keyLocation(node), - message: "missing table", - key: s.key.Key(), + s.missing = append(s.missing, unstable.ParserError{ + Highlight: keyLocation(node), + Message: "missing table", + Key: s.key.Key(), }) } -func (s *strict) MissingField(node *ast.Node) { +func (s *strict) MissingField(node *unstable.Node) { if !s.Enabled { return } - s.missing = append(s.missing, decodeError{ - highlight: keyLocation(node), - message: "missing field", - key: s.key.Key(), + s.missing = append(s.missing, unstable.ParserError{ + Highlight: keyLocation(node), + Message: "missing field", + Key: s.key.Key(), }) } @@ -88,7 +88,7 @@ func (s *strict) Error(doc []byte) error { return err } -func keyLocation(node *ast.Node) []byte { +func keyLocation(node *unstable.Node) []byte { k := node.Key() hasOne := k.Next() diff --git a/vendor/github.com/pelletier/go-toml/v2/types.go b/vendor/github.com/pelletier/go-toml/v2/types.go index 630a4546..3c6b8fe5 100644 --- a/vendor/github.com/pelletier/go-toml/v2/types.go +++ b/vendor/github.com/pelletier/go-toml/v2/types.go @@ -6,9 +6,9 @@ import ( "time" ) -var timeType = reflect.TypeOf(time.Time{}) -var textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() -var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() -var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{}) -var sliceInterfaceType = reflect.TypeOf([]interface{}{}) +var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() +var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() +var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}(nil)) +var sliceInterfaceType = reflect.TypeOf([]interface{}(nil)) var stringType = reflect.TypeOf("") diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go index b3596f6d..70f6ec57 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go @@ -12,16 +12,16 @@ import ( "sync/atomic" "time" - "github.com/pelletier/go-toml/v2/internal/ast" "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" + "github.com/pelletier/go-toml/v2/unstable" ) // Unmarshal deserializes a TOML document into a Go value. // // It is a shortcut for Decoder.Decode() with the default options. func Unmarshal(data []byte, v interface{}) error { - p := parser{} + p := unstable.Parser{} p.Reset(data) d := decoder{p: &p} @@ -79,29 +79,29 @@ func (d *Decoder) DisallowUnknownFields() *Decoder { // strict mode and a field is missing, a `toml.StrictMissingError` is // returned. In any other case, this function returns a standard Go error. // -// Type mapping +// # Type mapping // // List of supported TOML types and their associated accepted Go types: // -// String -> string -// Integer -> uint*, int*, depending on size -// Float -> float*, depending on size -// Boolean -> bool -// Offset Date-Time -> time.Time -// Local Date-time -> LocalDateTime, time.Time -// Local Date -> LocalDate, time.Time -// Local Time -> LocalTime, time.Time -// Array -> slice and array, depending on elements types -// Table -> map and struct -// Inline Table -> same as Table -// Array of Tables -> same as Array and Table +// String -> string +// Integer -> uint*, int*, depending on size +// Float -> float*, depending on size +// Boolean -> bool +// Offset Date-Time -> time.Time +// Local Date-time -> LocalDateTime, time.Time +// Local Date -> LocalDate, time.Time +// Local Time -> LocalTime, time.Time +// Array -> slice and array, depending on elements types +// Table -> map and struct +// Inline Table -> same as Table +// Array of Tables -> same as Array and Table func (d *Decoder) Decode(v interface{}) error { b, err := ioutil.ReadAll(d.r) if err != nil { return fmt.Errorf("toml: %w", err) } - p := parser{} + p := unstable.Parser{} p.Reset(b) dec := decoder{ p: &p, @@ -115,7 +115,7 @@ func (d *Decoder) Decode(v interface{}) error { type decoder struct { // Which parser instance in use for this decoding session. - p *parser + p *unstable.Parser // Flag indicating that the current expression is stashed. // If set to true, calling nextExpr will not actually pull a new expression @@ -123,7 +123,7 @@ type decoder struct { stashedExpr bool // Skip expressions until a table is found. This is set to true when a - // table could not be create (missing field in map), so all KV expressions + // table could not be created (missing field in map), so all KV expressions // need to be skipped. skipUntilTable bool @@ -157,7 +157,7 @@ func (d *decoder) typeMismatchError(toml string, target reflect.Type) error { return fmt.Errorf("toml: cannot decode TOML %s into a Go value of type %s", toml, target) } -func (d *decoder) expr() *ast.Node { +func (d *decoder) expr() *unstable.Node { return d.p.Expression() } @@ -208,12 +208,12 @@ func (d *decoder) FromParser(v interface{}) error { err := d.fromParser(r) if err == nil { - return d.strict.Error(d.p.data) + return d.strict.Error(d.p.Data()) } - var e *decodeError + var e *unstable.ParserError if errors.As(err, &e) { - return wrapDecodeError(d.p.data, e) + return wrapDecodeError(d.p.Data(), e) } return err @@ -234,16 +234,16 @@ func (d *decoder) fromParser(root reflect.Value) error { Rules for the unmarshal code: - The stack is used to keep track of which values need to be set where. -- handle* functions <=> switch on a given ast.Kind. +- handle* functions <=> switch on a given unstable.Kind. - unmarshalX* functions need to unmarshal a node of kind X. - An "object" is either a struct or a map. */ -func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { +func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) error { var x reflect.Value var err error - if !(d.skipUntilTable && expr.Kind == ast.KeyValue) { + if !(d.skipUntilTable && expr.Kind == unstable.KeyValue) { err = d.seen.CheckExpression(expr) if err != nil { return err @@ -251,16 +251,16 @@ func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { } switch expr.Kind { - case ast.KeyValue: + case unstable.KeyValue: if d.skipUntilTable { return nil } x, err = d.handleKeyValue(expr, v) - case ast.Table: + case unstable.Table: d.skipUntilTable = false d.strict.EnterTable(expr) x, err = d.handleTable(expr.Key(), v) - case ast.ArrayTable: + case unstable.ArrayTable: d.skipUntilTable = false d.strict.EnterArrayTable(expr) x, err = d.handleArrayTable(expr.Key(), v) @@ -269,7 +269,7 @@ func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { } if d.skipUntilTable { - if expr.Kind == ast.Table || expr.Kind == ast.ArrayTable { + if expr.Kind == unstable.Table || expr.Kind == unstable.ArrayTable { d.strict.MissingTable(expr) } } else if err == nil && x.IsValid() { @@ -279,14 +279,14 @@ func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { return err } -func (d *decoder) handleArrayTable(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if key.Next() { return d.handleArrayTablePart(key, v) } return d.handleKeyValues(v) } -func (d *decoder) handleArrayTableCollectionLast(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTableCollectionLast(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { switch v.Kind() { case reflect.Interface: elem := v.Elem() @@ -339,21 +339,21 @@ func (d *decoder) handleArrayTableCollectionLast(key ast.Iterator, v reflect.Val case reflect.Array: idx := d.arrayIndex(true, v) if idx >= v.Len() { - return v, fmt.Errorf("toml: cannot decode array table into %s at position %d", v.Type(), idx) + return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) } elem := v.Index(idx) _, err := d.handleArrayTable(key, elem) return v, err + default: + return reflect.Value{}, d.typeMismatchError("array table", v.Type()) } - - return d.handleArrayTable(key, v) } // When parsing an array table expression, each part of the key needs to be // evaluated like a normal key, but if it returns a collection, it also needs to // point to the last element of the collection. Unless it is the last part of // the key, then it needs to create a new element at the end. -func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTableCollection(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if key.IsLast() { return d.handleArrayTableCollectionLast(key, v) } @@ -390,7 +390,7 @@ func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) case reflect.Array: idx := d.arrayIndex(false, v) if idx >= v.Len() { - return v, fmt.Errorf("toml: cannot decode array table into %s at position %d", v.Type(), idx) + return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) } elem := v.Index(idx) _, err := d.handleArrayTable(key, elem) @@ -400,7 +400,7 @@ func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) return d.handleArrayTable(key, v) } -func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { +func (d *decoder) handleKeyPart(key unstable.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { var rv reflect.Value // First, dispatch over v to make sure it is a valid object. @@ -483,7 +483,7 @@ func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handle d.errorContext.Struct = t d.errorContext.Field = path - f := v.FieldByIndex(path) + f := fieldByIndex(v, path) x, err := nextFn(key, f) if err != nil || d.skipUntilTable { return reflect.Value{}, err @@ -518,7 +518,7 @@ func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handle // HandleArrayTablePart navigates the Go structure v using the key v. It is // only used for the prefix (non-last) parts of an array-table. When // encountering a collection, it should go to the last element. -func (d *decoder) handleArrayTablePart(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleArrayTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { var makeFn valueMakerFn if key.IsLast() { makeFn = makeSliceInterface @@ -530,10 +530,10 @@ func (d *decoder) handleArrayTablePart(key ast.Iterator, v reflect.Value) (refle // HandleTable returns a reference when it has checked the next expression but // cannot handle it. -func (d *decoder) handleTable(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if v.Kind() == reflect.Slice { if v.Len() == 0 { - return reflect.Value{}, newDecodeError(key.Node().Data, "cannot store a table in a slice") + return reflect.Value{}, unstable.NewParserError(key.Node().Data, "cannot store a table in a slice") } elem := v.Index(v.Len() - 1) x, err := d.handleTable(key, elem) @@ -560,7 +560,7 @@ func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) { var rv reflect.Value for d.nextExpr() { expr := d.expr() - if expr.Kind != ast.KeyValue { + if expr.Kind != unstable.KeyValue { // Stash the expression so that fromParser can just loop and use // the right handler. // We could just recurse ourselves here, but at least this gives a @@ -587,7 +587,7 @@ func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) { } type ( - handlerFn func(key ast.Iterator, v reflect.Value) (reflect.Value, error) + handlerFn func(key unstable.Iterator, v reflect.Value) (reflect.Value, error) valueMakerFn func() reflect.Value ) @@ -599,11 +599,11 @@ func makeSliceInterface() reflect.Value { return reflect.MakeSlice(sliceInterfaceType, 0, 16) } -func (d *decoder) handleTablePart(key ast.Iterator, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { return d.handleKeyPart(key, v, d.handleTable, makeMapStringInterface) } -func (d *decoder) tryTextUnmarshaler(node *ast.Node, v reflect.Value) (bool, error) { +func (d *decoder) tryTextUnmarshaler(node *unstable.Node, v reflect.Value) (bool, error) { // Special case for time, because we allow to unmarshal to it from // different kind of AST nodes. if v.Type() == timeType { @@ -613,7 +613,7 @@ func (d *decoder) tryTextUnmarshaler(node *ast.Node, v reflect.Value) (bool, err if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) if err != nil { - return false, newDecodeError(d.p.Raw(node.Raw), "%w", err) + return false, unstable.NewParserError(d.p.Raw(node.Raw), "%w", err) } return true, nil @@ -622,7 +622,7 @@ func (d *decoder) tryTextUnmarshaler(node *ast.Node, v reflect.Value) (bool, err return false, nil } -func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error { +func (d *decoder) handleValue(value *unstable.Node, v reflect.Value) error { for v.Kind() == reflect.Ptr { v = initAndDereferencePointer(v) } @@ -633,32 +633,32 @@ func (d *decoder) handleValue(value *ast.Node, v reflect.Value) error { } switch value.Kind { - case ast.String: + case unstable.String: return d.unmarshalString(value, v) - case ast.Integer: + case unstable.Integer: return d.unmarshalInteger(value, v) - case ast.Float: + case unstable.Float: return d.unmarshalFloat(value, v) - case ast.Bool: + case unstable.Bool: return d.unmarshalBool(value, v) - case ast.DateTime: + case unstable.DateTime: return d.unmarshalDateTime(value, v) - case ast.LocalDate: + case unstable.LocalDate: return d.unmarshalLocalDate(value, v) - case ast.LocalTime: + case unstable.LocalTime: return d.unmarshalLocalTime(value, v) - case ast.LocalDateTime: + case unstable.LocalDateTime: return d.unmarshalLocalDateTime(value, v) - case ast.InlineTable: + case unstable.InlineTable: return d.unmarshalInlineTable(value, v) - case ast.Array: + case unstable.Array: return d.unmarshalArray(value, v) default: panic(fmt.Errorf("handleValue not implemented for %s", value.Kind)) } } -func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalArray(array *unstable.Node, v reflect.Value) error { switch v.Kind() { case reflect.Slice: if v.IsNil() { @@ -729,7 +729,7 @@ func (d *decoder) unmarshalArray(array *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalInlineTable(itable *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) error { // Make sure v is an initialized object. switch v.Kind() { case reflect.Map: @@ -746,7 +746,7 @@ func (d *decoder) unmarshalInlineTable(itable *ast.Node, v reflect.Value) error } return d.unmarshalInlineTable(itable, elem) default: - return newDecodeError(itable.Data, "cannot store inline table in Go type %s", v.Kind()) + return unstable.NewParserError(itable.Data, "cannot store inline table in Go type %s", v.Kind()) } it := itable.Children() @@ -765,7 +765,7 @@ func (d *decoder) unmarshalInlineTable(itable *ast.Node, v reflect.Value) error return nil } -func (d *decoder) unmarshalDateTime(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error { dt, err := parseDateTime(value.Data) if err != nil { return err @@ -775,7 +775,7 @@ func (d *decoder) unmarshalDateTime(value *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalLocalDate(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) error { ld, err := parseLocalDate(value.Data) if err != nil { return err @@ -792,28 +792,28 @@ func (d *decoder) unmarshalLocalDate(value *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalLocalTime(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) error { lt, rest, err := parseLocalTime(value.Data) if err != nil { return err } if len(rest) > 0 { - return newDecodeError(rest, "extra characters at the end of a local time") + return unstable.NewParserError(rest, "extra characters at the end of a local time") } v.Set(reflect.ValueOf(lt)) return nil } -func (d *decoder) unmarshalLocalDateTime(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value) error { ldt, rest, err := parseLocalDateTime(value.Data) if err != nil { return err } if len(rest) > 0 { - return newDecodeError(rest, "extra characters at the end of a local date time") + return unstable.NewParserError(rest, "extra characters at the end of a local date time") } if v.Type() == timeType { @@ -828,7 +828,7 @@ func (d *decoder) unmarshalLocalDateTime(value *ast.Node, v reflect.Value) error return nil } -func (d *decoder) unmarshalBool(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalBool(value *unstable.Node, v reflect.Value) error { b := value.Data[0] == 't' switch v.Kind() { @@ -837,13 +837,13 @@ func (d *decoder) unmarshalBool(value *ast.Node, v reflect.Value) error { case reflect.Interface: v.Set(reflect.ValueOf(b)) default: - return newDecodeError(value.Data, "cannot assign boolean to a %t", b) + return unstable.NewParserError(value.Data, "cannot assign boolean to a %t", b) } return nil } -func (d *decoder) unmarshalFloat(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalFloat(value *unstable.Node, v reflect.Value) error { f, err := parseFloat(value.Data) if err != nil { return err @@ -854,13 +854,13 @@ func (d *decoder) unmarshalFloat(value *ast.Node, v reflect.Value) error { v.SetFloat(f) case reflect.Float32: if f > math.MaxFloat32 { - return newDecodeError(value.Data, "number %f does not fit in a float32", f) + return unstable.NewParserError(value.Data, "number %f does not fit in a float32", f) } v.SetFloat(f) case reflect.Interface: v.Set(reflect.ValueOf(f)) default: - return newDecodeError(value.Data, "float cannot be assigned to %s", v.Kind()) + return unstable.NewParserError(value.Data, "float cannot be assigned to %s", v.Kind()) } return nil @@ -886,7 +886,7 @@ func init() { } } -func (d *decoder) unmarshalInteger(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error { i, err := parseInteger(value.Data) if err != nil { return err @@ -967,20 +967,20 @@ func (d *decoder) unmarshalInteger(value *ast.Node, v reflect.Value) error { return nil } -func (d *decoder) unmarshalString(value *ast.Node, v reflect.Value) error { +func (d *decoder) unmarshalString(value *unstable.Node, v reflect.Value) error { switch v.Kind() { case reflect.String: v.SetString(string(value.Data)) case reflect.Interface: v.Set(reflect.ValueOf(string(value.Data))) default: - return newDecodeError(d.p.Raw(value.Raw), "cannot store TOML string into a Go %s", v.Kind()) + return unstable.NewParserError(d.p.Raw(value.Raw), "cannot store TOML string into a Go %s", v.Kind()) } return nil } -func (d *decoder) handleKeyValue(expr *ast.Node, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleKeyValue(expr *unstable.Node, v reflect.Value) (reflect.Value, error) { d.strict.EnterKeyValue(expr) v, err := d.handleKeyValueInner(expr.Key(), expr.Value(), v) @@ -994,7 +994,7 @@ func (d *decoder) handleKeyValue(expr *ast.Node, v reflect.Value) (reflect.Value return v, err } -func (d *decoder) handleKeyValueInner(key ast.Iterator, value *ast.Node, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleKeyValueInner(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { if key.Next() { // Still scoping the key return d.handleKeyValuePart(key, value, v) @@ -1004,7 +1004,7 @@ func (d *decoder) handleKeyValueInner(key ast.Iterator, value *ast.Node, v refle return reflect.Value{}, d.handleValue(value, v) } -func (d *decoder) handleKeyValuePart(key ast.Iterator, value *ast.Node, v reflect.Value) (reflect.Value, error) { +func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { // contains the replacement for v var rv reflect.Value @@ -1071,7 +1071,7 @@ func (d *decoder) handleKeyValuePart(key ast.Iterator, value *ast.Node, v reflec d.errorContext.Struct = t d.errorContext.Field = path - f := v.FieldByIndex(path) + f := fieldByIndex(v, path) x, err := d.handleKeyValueInner(key, value, f) if err != nil { return reflect.Value{}, err @@ -1135,6 +1135,21 @@ func initAndDereferencePointer(v reflect.Value) reflect.Value { return elem } +// Same as reflect.Value.FieldByIndex, but creates pointers if needed. +func fieldByIndex(v reflect.Value, path []int) reflect.Value { + for i, x := range path { + v = v.Field(x) + + if i < len(path)-1 && v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + } + return v +} + type fieldPathsMap = map[string][]int var globalFieldPathsCache atomic.Value // map[danger.TypeID]fieldPathsMap @@ -1192,7 +1207,14 @@ func forEachField(t reflect.Type, path []int, do func(name string, path []int)) } if f.Anonymous && name == "" { - forEachField(f.Type, fieldPath, do) + t2 := f.Type + if t2.Kind() == reflect.Ptr { + t2 = t2.Elem() + } + + if t2.Kind() == reflect.Struct { + forEachField(t2, fieldPath, do) + } continue } diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go index 33c7f915..b60d9bfd 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/ast/ast.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go @@ -1,4 +1,4 @@ -package ast +package unstable import ( "fmt" @@ -7,14 +7,17 @@ import ( "github.com/pelletier/go-toml/v2/internal/danger" ) -// Iterator starts uninitialized, you need to call Next() first. +// Iterator over a sequence of nodes. +// +// Starts uninitialized, you need to call Next() first. // // For example: // -// it := n.Children() -// for it.Next() { -// it.Node() -// } +// it := n.Children() +// for it.Next() { +// n := it.Node() +// // do something with n +// } type Iterator struct { started bool node *Node @@ -32,42 +35,31 @@ func (c *Iterator) Next() bool { } // IsLast returns true if the current node of the iterator is the last -// one. Subsequent call to Next() will return false. +// one. Subsequent calls to Next() will return false. func (c *Iterator) IsLast() bool { return c.node.next == 0 } -// Node returns a copy of the node pointed at by the iterator. +// Node returns a pointer to the node pointed at by the iterator. func (c *Iterator) Node() *Node { return c.node } -// Root contains a full AST. +// Node in a TOML expression AST. // -// It is immutable once constructed with Builder. -type Root struct { - nodes []Node -} - -// Iterator over the top level nodes. -func (r *Root) Iterator() Iterator { - it := Iterator{} - if len(r.nodes) > 0 { - it.node = &r.nodes[0] - } - return it -} - -func (r *Root) at(idx Reference) *Node { - return &r.nodes[idx] -} - -// Arrays have one child per element in the array. InlineTables have -// one child per key-value pair in the table. KeyValues have at least -// two children. The first one is the value. The rest make a -// potentially dotted key. Table and Array table have one child per -// element of the key they represent (same as KeyValue, but without -// the last node being the value). +// Depending on Kind, its sequence of children should be interpreted +// differently. +// +// - Array have one child per element in the array. +// - InlineTable have one child per key-value in the table (each of kind +// InlineTable). +// - KeyValue have at least two children. The first one is the value. The rest +// make a potentially dotted key. +// - Table and ArrayTable's children represent a dotted key (same as +// KeyValue, but without the first node being the value). +// +// When relevant, Raw describes the range of bytes this node is refering to in +// the input document. Use Parser.Raw() to retrieve the actual bytes. type Node struct { Kind Kind Raw Range // Raw bytes from the input. @@ -80,13 +72,13 @@ type Node struct { child int // 0 if no child } +// Range of bytes in the document. type Range struct { Offset uint32 Length uint32 } -// Next returns a copy of the next node, or an invalid Node if there -// is no next node. +// Next returns a pointer to the next node, or nil if there is no next node. func (n *Node) Next() *Node { if n.next == 0 { return nil @@ -96,9 +88,9 @@ func (n *Node) Next() *Node { return (*Node)(danger.Stride(ptr, size, n.next)) } -// Child returns a copy of the first child node of this node. Other -// children can be accessed calling Next on the first child. Returns -// an invalid Node if there is none. +// Child returns a pointer to the first child node of this node. Other children +// can be accessed calling Next on the first child. Returns an nil if this Node +// has no child. func (n *Node) Child() *Node { if n.child == 0 { return nil @@ -113,9 +105,9 @@ func (n *Node) Valid() bool { return n != nil } -// Key returns the child nodes making the Key on a supported -// node. Panics otherwise. They are guaranteed to be all be of the -// Kind Key. A simple key would return just one element. +// Key returns the children nodes making the Key on a supported node. Panics +// otherwise. They are guaranteed to be all be of the Kind Key. A simple key +// would return just one element. func (n *Node) Key() Iterator { switch n.Kind { case KeyValue: diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go b/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go new file mode 100644 index 00000000..9538e30d --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go @@ -0,0 +1,71 @@ +package unstable + +// root contains a full AST. +// +// It is immutable once constructed with Builder. +type root struct { + nodes []Node +} + +// Iterator over the top level nodes. +func (r *root) Iterator() Iterator { + it := Iterator{} + if len(r.nodes) > 0 { + it.node = &r.nodes[0] + } + return it +} + +func (r *root) at(idx reference) *Node { + return &r.nodes[idx] +} + +type reference int + +const invalidReference reference = -1 + +func (r reference) Valid() bool { + return r != invalidReference +} + +type builder struct { + tree root + lastIdx int +} + +func (b *builder) Tree() *root { + return &b.tree +} + +func (b *builder) NodeAt(ref reference) *Node { + return b.tree.at(ref) +} + +func (b *builder) Reset() { + b.tree.nodes = b.tree.nodes[:0] + b.lastIdx = 0 +} + +func (b *builder) Push(n Node) reference { + b.lastIdx = len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + return reference(b.lastIdx) +} + +func (b *builder) PushAndChain(n Node) reference { + newIdx := len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + if b.lastIdx >= 0 { + b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx + } + b.lastIdx = newIdx + return reference(b.lastIdx) +} + +func (b *builder) AttachChild(parent reference, child reference) { + b.tree.nodes[parent].child = int(child) - int(parent) +} + +func (b *builder) Chain(from reference, to reference) { + b.tree.nodes[from].next = int(to) - int(from) +} diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go b/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go new file mode 100644 index 00000000..7ff26c53 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go @@ -0,0 +1,3 @@ +// Package unstable provides APIs that do not meet the backward compatibility +// guarantees yet. +package unstable diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go index 2b50c67f..ff9df1be 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/ast/kind.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go @@ -1,25 +1,26 @@ -package ast +package unstable import "fmt" +// Kind represents the type of TOML structure contained in a given Node. type Kind int const ( - // meta + // Meta Invalid Kind = iota Comment Key - // top level structures + // Top level structures Table ArrayTable KeyValue - // containers values + // Containers values Array InlineTable - // values + // Values String Bool Float @@ -30,6 +31,7 @@ const ( DateTime ) +// String implementation of fmt.Stringer. func (k Kind) String() string { switch k { case Invalid: diff --git a/vendor/github.com/pelletier/go-toml/v2/parser.go b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go index 9859a795..52db88e7 100644 --- a/vendor/github.com/pelletier/go-toml/v2/parser.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go @@ -1,50 +1,108 @@ -package toml +package unstable import ( "bytes" + "fmt" "unicode" - "github.com/pelletier/go-toml/v2/internal/ast" + "github.com/pelletier/go-toml/v2/internal/characters" "github.com/pelletier/go-toml/v2/internal/danger" ) -type parser struct { - builder ast.Builder - ref ast.Reference +// ParserError describes an error relative to the content of the document. +// +// It cannot outlive the instance of Parser it refers to, and may cause panics +// if the parser is reset. +type ParserError struct { + Highlight []byte + Message string + Key []string // optional +} + +// Error is the implementation of the error interface. +func (e *ParserError) Error() string { + return e.Message +} + +// NewParserError is a convenience function to create a ParserError +// +// Warning: Highlight needs to be a subslice of Parser.data, so only slices +// returned by Parser.Raw are valid candidates. +func NewParserError(highlight []byte, format string, args ...interface{}) error { + return &ParserError{ + Highlight: highlight, + Message: fmt.Errorf(format, args...).Error(), + } +} + +// Parser scans over a TOML-encoded document and generates an iterative AST. +// +// To prime the Parser, first reset it with the contents of a TOML document. +// Then, process all top-level expressions sequentially. See Example. +// +// Don't forget to check Error() after you're done parsing. +// +// Each top-level expression needs to be fully processed before calling +// NextExpression() again. Otherwise, calls to various Node methods may panic if +// the parser has moved on the next expression. +// +// For performance reasons, go-toml doesn't make a copy of the input bytes to +// the parser. Make sure to copy all the bytes you need to outlive the slice +// given to the parser. +// +// The parser doesn't provide nodes for comments yet, nor for whitespace. +type Parser struct { data []byte + builder builder + ref reference left []byte err error first bool } -func (p *parser) Range(b []byte) ast.Range { - return ast.Range{ +// Data returns the slice provided to the last call to Reset. +func (p *Parser) Data() []byte { + return p.data +} + +// Range returns a range description that corresponds to a given slice of the +// input. If the argument is not a subslice of the parser input, this function +// panics. +func (p *Parser) Range(b []byte) Range { + return Range{ Offset: uint32(danger.SubsliceOffset(p.data, b)), Length: uint32(len(b)), } } -func (p *parser) Raw(raw ast.Range) []byte { +// Raw returns the slice corresponding to the bytes in the given range. +func (p *Parser) Raw(raw Range) []byte { return p.data[raw.Offset : raw.Offset+raw.Length] } -func (p *parser) Reset(b []byte) { +// Reset brings the parser to its initial state for a given input. It wipes an +// reuses internal storage to reduce allocation. +func (p *Parser) Reset(b []byte) { p.builder.Reset() - p.ref = ast.InvalidReference + p.ref = invalidReference p.data = b p.left = b p.err = nil p.first = true } -//nolint:cyclop -func (p *parser) NextExpression() bool { +// NextExpression parses the next top-level expression. If an expression was +// successfully parsed, it returns true. If the parser is at the end of the +// document or an error occurred, it returns false. +// +// Retrieve the parsed expression with Expression(). +func (p *Parser) NextExpression() bool { if len(p.left) == 0 || p.err != nil { return false } p.builder.Reset() - p.ref = ast.InvalidReference + p.ref = invalidReference for { if len(p.left) == 0 || p.err != nil { @@ -73,15 +131,18 @@ func (p *parser) NextExpression() bool { } } -func (p *parser) Expression() *ast.Node { +// Expression returns a pointer to the node representing the last successfully +// parsed expresion. +func (p *Parser) Expression() *Node { return p.builder.NodeAt(p.ref) } -func (p *parser) Error() error { +// Error returns any error that has occured during parsing. +func (p *Parser) Error() error { return p.err } -func (p *parser) parseNewline(b []byte) ([]byte, error) { +func (p *Parser) parseNewline(b []byte) ([]byte, error) { if b[0] == '\n' { return b[1:], nil } @@ -91,14 +152,14 @@ func (p *parser) parseNewline(b []byte) ([]byte, error) { return rest, err } - return nil, newDecodeError(b[0:1], "expected newline but got %#U", b[0]) + return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0]) } -func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { // expression = ws [ comment ] // expression =/ ws keyval ws [ comment ] // expression =/ ws table ws [ comment ] - ref := ast.InvalidReference + ref := invalidReference b = p.parseWhitespace(b) @@ -136,7 +197,7 @@ func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) { return ref, b, nil } -func (p *parser) parseTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseTable(b []byte) (reference, []byte, error) { // table = std-table / array-table if len(b) > 1 && b[1] == '[' { return p.parseArrayTable(b) @@ -145,12 +206,12 @@ func (p *parser) parseTable(b []byte) (ast.Reference, []byte, error) { return p.parseStdTable(b) } -func (p *parser) parseArrayTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseArrayTable(b []byte) (reference, []byte, error) { // array-table = array-table-open key array-table-close // array-table-open = %x5B.5B ws ; [[ Double left square bracket // array-table-close = ws %x5D.5D ; ]] Double right square bracket - ref := p.builder.Push(ast.Node{ - Kind: ast.ArrayTable, + ref := p.builder.Push(Node{ + Kind: ArrayTable, }) b = b[2:] @@ -174,12 +235,12 @@ func (p *parser) parseArrayTable(b []byte) (ast.Reference, []byte, error) { return ref, b, err } -func (p *parser) parseStdTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseStdTable(b []byte) (reference, []byte, error) { // std-table = std-table-open key std-table-close // std-table-open = %x5B ws ; [ Left square bracket // std-table-close = ws %x5D ; ] Right square bracket - ref := p.builder.Push(ast.Node{ - Kind: ast.Table, + ref := p.builder.Push(Node{ + Kind: Table, }) b = b[1:] @@ -199,15 +260,15 @@ func (p *parser) parseStdTable(b []byte) (ast.Reference, []byte, error) { return ref, b, err } -func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) { // keyval = key keyval-sep val - ref := p.builder.Push(ast.Node{ - Kind: ast.KeyValue, + ref := p.builder.Push(Node{ + Kind: KeyValue, }) key, b, err := p.parseKey(b) if err != nil { - return ast.InvalidReference, nil, err + return invalidReference, nil, err } // keyval-sep = ws %x3D ws ; = @@ -215,12 +276,12 @@ func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) { b = p.parseWhitespace(b) if len(b) == 0 { - return ast.InvalidReference, nil, newDecodeError(b, "expected = after a key, but the document ends there") + return invalidReference, nil, NewParserError(b, "expected = after a key, but the document ends there") } b, err = expect('=', b) if err != nil { - return ast.InvalidReference, nil, err + return invalidReference, nil, err } b = p.parseWhitespace(b) @@ -237,12 +298,12 @@ func (p *parser) parseKeyval(b []byte) (ast.Reference, []byte, error) { } //nolint:cyclop,funlen -func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseVal(b []byte) (reference, []byte, error) { // val = string / boolean / array / inline-table / date-time / float / integer - ref := ast.InvalidReference + ref := invalidReference if len(b) == 0 { - return ref, nil, newDecodeError(b, "expected value, not eof") + return ref, nil, NewParserError(b, "expected value, not eof") } var err error @@ -259,8 +320,8 @@ func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { } if err == nil { - ref = p.builder.Push(ast.Node{ - Kind: ast.String, + ref = p.builder.Push(Node{ + Kind: String, Raw: p.Range(raw), Data: v, }) @@ -277,8 +338,8 @@ func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { } if err == nil { - ref = p.builder.Push(ast.Node{ - Kind: ast.String, + ref = p.builder.Push(Node{ + Kind: String, Raw: p.Range(raw), Data: v, }) @@ -287,22 +348,22 @@ func (p *parser) parseVal(b []byte) (ast.Reference, []byte, error) { return ref, b, err case 't': if !scanFollowsTrue(b) { - return ref, nil, newDecodeError(atmost(b, 4), "expected 'true'") + return ref, nil, NewParserError(atmost(b, 4), "expected 'true'") } - ref = p.builder.Push(ast.Node{ - Kind: ast.Bool, + ref = p.builder.Push(Node{ + Kind: Bool, Data: b[:4], }) return ref, b[4:], nil case 'f': if !scanFollowsFalse(b) { - return ref, nil, newDecodeError(atmost(b, 5), "expected 'false'") + return ref, nil, NewParserError(atmost(b, 5), "expected 'false'") } - ref = p.builder.Push(ast.Node{ - Kind: ast.Bool, + ref = p.builder.Push(Node{ + Kind: Bool, Data: b[:5], }) @@ -324,7 +385,7 @@ func atmost(b []byte, n int) []byte { return b[:n] } -func (p *parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { v, rest, err := scanLiteralString(b) if err != nil { return nil, nil, nil, err @@ -333,19 +394,19 @@ func (p *parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { return v, v[1 : len(v)-1], rest, nil } -func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) { // inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close // inline-table-open = %x7B ws ; { // inline-table-close = ws %x7D ; } // inline-table-sep = ws %x2C ws ; , Comma // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] - parent := p.builder.Push(ast.Node{ - Kind: ast.InlineTable, + parent := p.builder.Push(Node{ + Kind: InlineTable, }) first := true - var child ast.Reference + var child reference b = b[1:] @@ -356,7 +417,7 @@ func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { b = p.parseWhitespace(b) if len(b) == 0 { - return parent, nil, newDecodeError(previousB[:1], "inline table is incomplete") + return parent, nil, NewParserError(previousB[:1], "inline table is incomplete") } if b[0] == '}' { @@ -371,7 +432,7 @@ func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { b = p.parseWhitespace(b) } - var kv ast.Reference + var kv reference kv, b, err = p.parseKeyval(b) if err != nil { @@ -394,7 +455,7 @@ func (p *parser) parseInlineTable(b []byte) (ast.Reference, []byte, error) { } //nolint:funlen,cyclop -func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { // array = array-open [ array-values ] ws-comment-newline array-close // array-open = %x5B ; [ // array-close = %x5D ; ] @@ -405,13 +466,13 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { arrayStart := b b = b[1:] - parent := p.builder.Push(ast.Node{ - Kind: ast.Array, + parent := p.builder.Push(Node{ + Kind: Array, }) first := true - var lastChild ast.Reference + var lastChild reference var err error for len(b) > 0 { @@ -421,7 +482,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { } if len(b) == 0 { - return parent, nil, newDecodeError(arrayStart[:1], "array is incomplete") + return parent, nil, NewParserError(arrayStart[:1], "array is incomplete") } if b[0] == ']' { @@ -430,7 +491,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { if b[0] == ',' { if first { - return parent, nil, newDecodeError(b[0:1], "array cannot start with comma") + return parent, nil, NewParserError(b[0:1], "array cannot start with comma") } b = b[1:] @@ -439,7 +500,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { return parent, nil, err } } else if !first { - return parent, nil, newDecodeError(b[0:1], "array elements must be separated by commas") + return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas") } // TOML allows trailing commas in arrays. @@ -447,7 +508,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { break } - var valueRef ast.Reference + var valueRef reference valueRef, b, err = p.parseVal(b) if err != nil { return parent, nil, err @@ -472,7 +533,7 @@ func (p *parser) parseValArray(b []byte) (ast.Reference, []byte, error) { return parent, rest, err } -func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) { +func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) { for len(b) > 0 { var err error b = p.parseWhitespace(b) @@ -501,7 +562,7 @@ func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error) return b, nil } -func (p *parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { token, rest, err := scanMultilineLiteralString(b) if err != nil { return nil, nil, nil, err @@ -520,7 +581,7 @@ func (p *parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, } //nolint:funlen,gocognit,cyclop -func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body // ml-basic-string-delim // ml-basic-string-delim = 3quotation-mark @@ -551,11 +612,11 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er if !escaped { str := token[startIdx:endIdx] - verr := utf8TomlValidAlreadyEscaped(str) + verr := characters.Utf8TomlValidAlreadyEscaped(str) if verr.Zero() { return token, str, rest, nil } - return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") + return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } var builder bytes.Buffer @@ -635,13 +696,13 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er builder.WriteRune(x) i += 8 default: - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) } i++ } else { - size := utf8ValidNext(token[i:]) + size := characters.Utf8ValidNext(token[i:]) if size == 0 { - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) } builder.Write(token[i : i+size]) i += size @@ -651,7 +712,7 @@ func (p *parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, er return token, builder.Bytes(), rest, nil } -func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseKey(b []byte) (reference, []byte, error) { // key = simple-key / dotted-key // simple-key = quoted-key / unquoted-key // @@ -662,11 +723,11 @@ func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { // dot-sep = ws %x2E ws ; . Period raw, key, b, err := p.parseSimpleKey(b) if err != nil { - return ast.InvalidReference, nil, err + return invalidReference, nil, err } - ref := p.builder.Push(ast.Node{ - Kind: ast.Key, + ref := p.builder.Push(Node{ + Kind: Key, Raw: p.Range(raw), Data: key, }) @@ -681,8 +742,8 @@ func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { return ref, nil, err } - p.builder.PushAndChain(ast.Node{ - Kind: ast.Key, + p.builder.PushAndChain(Node{ + Kind: Key, Raw: p.Range(raw), Data: key, }) @@ -694,9 +755,9 @@ func (p *parser) parseKey(b []byte) (ast.Reference, []byte, error) { return ref, b, nil } -func (p *parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { +func (p *Parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { if len(b) == 0 { - return nil, nil, nil, newDecodeError(b, "expected key but found none") + return nil, nil, nil, NewParserError(b, "expected key but found none") } // simple-key = quoted-key / unquoted-key @@ -711,12 +772,12 @@ func (p *parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { key, rest = scanUnquotedKey(b) return key, key, rest, nil default: - return nil, nil, nil, newDecodeError(b[0:1], "invalid character at start of key: %c", b[0]) + return nil, nil, nil, NewParserError(b[0:1], "invalid character at start of key: %c", b[0]) } } //nolint:funlen,cyclop -func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { +func (p *Parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { // basic-string = quotation-mark *basic-char quotation-mark // quotation-mark = %x22 ; " // basic-char = basic-unescaped / escaped @@ -744,11 +805,11 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { // validate the string and return a direct reference to the buffer. if !escaped { str := token[startIdx:endIdx] - verr := utf8TomlValidAlreadyEscaped(str) + verr := characters.Utf8TomlValidAlreadyEscaped(str) if verr.Zero() { return token, str, rest, nil } - return nil, nil, nil, newDecodeError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") + return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } i := startIdx @@ -795,13 +856,13 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { builder.WriteRune(x) i += 8 default: - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid escaped character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) } i++ } else { - size := utf8ValidNext(token[i:]) + size := characters.Utf8ValidNext(token[i:]) if size == 0 { - return nil, nil, nil, newDecodeError(token[i:i+1], "invalid character %#U", c) + return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) } builder.Write(token[i : i+size]) i += size @@ -813,7 +874,7 @@ func (p *parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { func hexToRune(b []byte, length int) (rune, error) { if len(b) < length { - return -1, newDecodeError(b, "unicode point needs %d character, not %d", length, len(b)) + return -1, NewParserError(b, "unicode point needs %d character, not %d", length, len(b)) } b = b[:length] @@ -828,19 +889,19 @@ func hexToRune(b []byte, length int) (rune, error) { case 'A' <= c && c <= 'F': d = uint32(c - 'A' + 10) default: - return -1, newDecodeError(b[i:i+1], "non-hex character") + return -1, NewParserError(b[i:i+1], "non-hex character") } r = r*16 + d } if r > unicode.MaxRune || 0xD800 <= r && r < 0xE000 { - return -1, newDecodeError(b, "escape sequence is invalid Unicode code point") + return -1, NewParserError(b, "escape sequence is invalid Unicode code point") } return rune(r), nil } -func (p *parser) parseWhitespace(b []byte) []byte { +func (p *Parser) parseWhitespace(b []byte) []byte { // ws = *wschar // wschar = %x20 ; Space // wschar =/ %x09 ; Horizontal tab @@ -850,24 +911,24 @@ func (p *parser) parseWhitespace(b []byte) []byte { } //nolint:cyclop -func (p *parser) parseIntOrFloatOrDateTime(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error) { switch b[0] { case 'i': if !scanFollowsInf(b) { - return ast.InvalidReference, nil, newDecodeError(atmost(b, 3), "expected 'inf'") + return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'inf'") } - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:3], }), b[3:], nil case 'n': if !scanFollowsNan(b) { - return ast.InvalidReference, nil, newDecodeError(atmost(b, 3), "expected 'nan'") + return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'nan'") } - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:3], }), b[3:], nil case '+', '-': @@ -898,7 +959,7 @@ func (p *parser) parseIntOrFloatOrDateTime(b []byte) (ast.Reference, []byte, err return p.scanIntOrFloat(b) } -func (p *parser) scanDateTime(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) scanDateTime(b []byte) (reference, []byte, error) { // scans for contiguous characters in [0-9T:Z.+-], and up to one space if // followed by a digit. hasDate := false @@ -941,30 +1002,30 @@ byteLoop: } } - var kind ast.Kind + var kind Kind if hasTime { if hasDate { if hasTz { - kind = ast.DateTime + kind = DateTime } else { - kind = ast.LocalDateTime + kind = LocalDateTime } } else { - kind = ast.LocalTime + kind = LocalTime } } else { - kind = ast.LocalDate + kind = LocalDate } - return p.builder.Push(ast.Node{ + return p.builder.Push(Node{ Kind: kind, Data: b[:i], }), b[i:], nil } //nolint:funlen,gocognit,cyclop -func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) { +func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) { i := 0 if len(b) > 2 && b[0] == '0' && b[1] != '.' && b[1] != 'e' && b[1] != 'E' { @@ -990,8 +1051,8 @@ func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) { } } - return p.builder.Push(ast.Node{ - Kind: ast.Integer, + return p.builder.Push(Node{ + Kind: Integer, Data: b[:i], }), b[i:], nil } @@ -1013,40 +1074,40 @@ func (p *parser) scanIntOrFloat(b []byte) (ast.Reference, []byte, error) { if c == 'i' { if scanFollowsInf(b[i:]) { - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:i+3], }), b[i+3:], nil } - return ast.InvalidReference, nil, newDecodeError(b[i:i+1], "unexpected character 'i' while scanning for a number") + return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'i' while scanning for a number") } if c == 'n' { if scanFollowsNan(b[i:]) { - return p.builder.Push(ast.Node{ - Kind: ast.Float, + return p.builder.Push(Node{ + Kind: Float, Data: b[:i+3], }), b[i+3:], nil } - return ast.InvalidReference, nil, newDecodeError(b[i:i+1], "unexpected character 'n' while scanning for a number") + return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'n' while scanning for a number") } break } if i == 0 { - return ast.InvalidReference, b, newDecodeError(b, "incomplete number") + return invalidReference, b, NewParserError(b, "incomplete number") } - kind := ast.Integer + kind := Integer if isFloat { - kind = ast.Float + kind = Float } - return p.builder.Push(ast.Node{ + return p.builder.Push(Node{ Kind: kind, Data: b[:i], }), b[i:], nil @@ -1075,11 +1136,11 @@ func isValidBinaryRune(r byte) bool { func expect(x byte, b []byte) ([]byte, error) { if len(b) == 0 { - return nil, newDecodeError(b, "expected character %c but the document ended here", x) + return nil, NewParserError(b, "expected character %c but the document ended here", x) } if b[0] != x { - return nil, newDecodeError(b[0:1], "expected character %c", x) + return nil, NewParserError(b[0:1], "expected character %c", x) } return b[1:], nil diff --git a/vendor/github.com/pelletier/go-toml/v2/scanner.go b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go index bb445fab..af22ebbe 100644 --- a/vendor/github.com/pelletier/go-toml/v2/scanner.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go @@ -1,4 +1,6 @@ -package toml +package unstable + +import "github.com/pelletier/go-toml/v2/internal/characters" func scanFollows(b []byte, pattern string) bool { n := len(pattern) @@ -54,16 +56,16 @@ func scanLiteralString(b []byte) ([]byte, []byte, error) { case '\'': return b[:i+1], b[i+1:], nil case '\n', '\r': - return nil, nil, newDecodeError(b[i:i+1], "literal strings cannot have new lines") + return nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines") } - size := utf8ValidNext(b[i:]) + size := characters.Utf8ValidNext(b[i:]) if size == 0 { - return nil, nil, newDecodeError(b[i:i+1], "invalid character") + return nil, nil, NewParserError(b[i:i+1], "invalid character") } i += size } - return nil, nil, newDecodeError(b[len(b):], "unterminated literal string") + return nil, nil, NewParserError(b[len(b):], "unterminated literal string") } func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { @@ -98,39 +100,39 @@ func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { i++ if i < len(b) && b[i] == '\'' { - return nil, nil, newDecodeError(b[i-3:i+1], "''' not allowed in multiline literal string") + return nil, nil, NewParserError(b[i-3:i+1], "''' not allowed in multiline literal string") } return b[:i], b[i:], nil } case '\r': if len(b) < i+2 { - return nil, nil, newDecodeError(b[len(b):], `need a \n after \r`) + return nil, nil, NewParserError(b[len(b):], `need a \n after \r`) } if b[i+1] != '\n' { - return nil, nil, newDecodeError(b[i:i+2], `need a \n after \r`) + return nil, nil, NewParserError(b[i:i+2], `need a \n after \r`) } i += 2 // skip the \n continue } - size := utf8ValidNext(b[i:]) + size := characters.Utf8ValidNext(b[i:]) if size == 0 { - return nil, nil, newDecodeError(b[i:i+1], "invalid character") + return nil, nil, NewParserError(b[i:i+1], "invalid character") } i += size } - return nil, nil, newDecodeError(b[len(b):], `multiline literal string not terminated by '''`) + return nil, nil, NewParserError(b[len(b):], `multiline literal string not terminated by '''`) } func scanWindowsNewline(b []byte) ([]byte, []byte, error) { const lenCRLF = 2 if len(b) < lenCRLF { - return nil, nil, newDecodeError(b, "windows new line expected") + return nil, nil, NewParserError(b, "windows new line expected") } if b[1] != '\n' { - return nil, nil, newDecodeError(b, `windows new line should be \r\n`) + return nil, nil, NewParserError(b, `windows new line should be \r\n`) } return b[:lenCRLF], b[lenCRLF:], nil @@ -165,11 +167,11 @@ func scanComment(b []byte) ([]byte, []byte, error) { if i+1 < len(b) && b[i+1] == '\n' { return b[:i+1], b[i+1:], nil } - return nil, nil, newDecodeError(b[i:i+1], "invalid character in comment") + return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") } - size := utf8ValidNext(b[i:]) + size := characters.Utf8ValidNext(b[i:]) if size == 0 { - return nil, nil, newDecodeError(b[i:i+1], "invalid character in comment") + return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") } i += size @@ -192,17 +194,17 @@ func scanBasicString(b []byte) ([]byte, bool, []byte, error) { case '"': return b[:i+1], escaped, b[i+1:], nil case '\n', '\r': - return nil, escaped, nil, newDecodeError(b[i:i+1], "basic strings cannot have new lines") + return nil, escaped, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines") case '\\': if len(b) < i+2 { - return nil, escaped, nil, newDecodeError(b[i:i+1], "need a character after \\") + return nil, escaped, nil, NewParserError(b[i:i+1], "need a character after \\") } escaped = true i++ // skip the next character } } - return nil, escaped, nil, newDecodeError(b[len(b):], `basic string not terminated by "`) + return nil, escaped, nil, NewParserError(b[len(b):], `basic string not terminated by "`) } func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) { @@ -243,27 +245,27 @@ func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) { i++ if i < len(b) && b[i] == '"' { - return nil, escaped, nil, newDecodeError(b[i-3:i+1], `""" not allowed in multiline basic string`) + return nil, escaped, nil, NewParserError(b[i-3:i+1], `""" not allowed in multiline basic string`) } return b[:i], escaped, b[i:], nil } case '\\': if len(b) < i+2 { - return nil, escaped, nil, newDecodeError(b[len(b):], "need a character after \\") + return nil, escaped, nil, NewParserError(b[len(b):], "need a character after \\") } escaped = true i++ // skip the next character case '\r': if len(b) < i+2 { - return nil, escaped, nil, newDecodeError(b[len(b):], `need a \n after \r`) + return nil, escaped, nil, NewParserError(b[len(b):], `need a \n after \r`) } if b[i+1] != '\n' { - return nil, escaped, nil, newDecodeError(b[i:i+2], `need a \n after \r`) + return nil, escaped, nil, NewParserError(b[i:i+2], `need a \n after \r`) } i++ // skip the \n } } - return nil, escaped, nil, newDecodeError(b[len(b):], `multiline basic string not terminated by """`) + return nil, escaped, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`) } diff --git a/vendor/github.com/saintfish/chardet/README.md b/vendor/github.com/saintfish/chardet/README.md index 4281eecc..41f58ee0 100644 --- a/vendor/github.com/saintfish/chardet/README.md +++ b/vendor/github.com/saintfish/chardet/README.md @@ -7,4 +7,4 @@ in [ICU](http://icu-project.org/)'s implementation. ## Documentation and Usage -See [pkgdoc](http://go.pkgdoc.org/github.com/saintfish/chardet) +See [pkgdoc](https://pkg.go.dev/github.com/saintfish/chardet) diff --git a/vendor/github.com/slack-go/slack/auth.go b/vendor/github.com/slack-go/slack/auth.go index f4f7f003..bf6e80d3 100644 --- a/vendor/github.com/slack-go/slack/auth.go +++ b/vendor/github.com/slack-go/slack/auth.go @@ -38,3 +38,37 @@ func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*Au return api.authRequest(ctx, "auth.revoke", values) } + +type listTeamsResponse struct { + Teams []Team `json:"teams"` + SlackResponse +} + +type ListTeamsParameters struct { + Limit int + Cursor string +} + +// ListTeams returns all workspaces a token can access. +// More info: https://api.slack.com/methods/admin.teams.list +func (api *Client) ListTeams(params ListTeamsParameters) ([]Team, string, error) { + return api.ListTeamsContext(context.Background(), params) +} + +// ListTeams returns all workspaces a token can access with a custom context. +func (api *Client) ListTeamsContext(ctx context.Context, params ListTeamsParameters) ([]Team, string, error) { + values := url.Values{ + "token": {api.token}, + } + if params.Cursor != "" { + values.Add("cursor", params.Cursor) + } + + response := &listTeamsResponse{} + err := api.postMethod(ctx, "auth.teams.list", values, response) + if err != nil { + return nil, "", err + } + + return response.Teams, response.ResponseMetadata.Cursor, response.Err() +} diff --git a/vendor/github.com/slack-go/slack/block_conv.go b/vendor/github.com/slack-go/slack/block_conv.go index 1a2c57e9..555e4811 100644 --- a/vendor/github.com/slack-go/slack/block_conv.go +++ b/vendor/github.com/slack-go/slack/block_conv.go @@ -112,6 +112,10 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error { e = &TimePickerBlockElement{} case "plain_text_input": e = &PlainTextInputBlockElement{} + case "email_text_input": + e = &EmailTextInputBlockElement{} + case "url_text_input": + e = &URLTextInputBlockElement{} case "static_select", "external_select", "users_select", "conversations_select", "channels_select": e = &SelectBlockElement{} case "multi_static_select", "multi_external_select", "multi_users_select", "multi_conversations_select", "multi_channels_select": @@ -122,6 +126,8 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error { e = &OverflowBlockElement{} case "radio_buttons": e = &RadioButtonsBlockElement{} + case "number_input": + e = &NumberInputBlockElement{} default: return errors.New("unsupported block element type") } @@ -186,12 +192,18 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error { blockElement = &TimePickerBlockElement{} case "plain_text_input": blockElement = &PlainTextInputBlockElement{} + case "email_text_input": + blockElement = &EmailTextInputBlockElement{} + case "url_text_input": + blockElement = &URLTextInputBlockElement{} case "checkboxes": blockElement = &CheckboxGroupsBlockElement{} case "radio_buttons": blockElement = &RadioButtonsBlockElement{} case "static_select", "external_select", "users_select", "conversations_select", "channels_select": blockElement = &SelectBlockElement{} + case "number_input": + blockElement = &NumberInputBlockElement{} default: return fmt.Errorf("unsupported block element type %v", blockElementType) } diff --git a/vendor/github.com/slack-go/slack/block_element.go b/vendor/github.com/slack-go/slack/block_element.go index 643529ff..aba29c6b 100644 --- a/vendor/github.com/slack-go/slack/block_element.go +++ b/vendor/github.com/slack-go/slack/block_element.go @@ -11,6 +11,9 @@ const ( METTimepicker MessageElementType = "timepicker" METPlainTextInput MessageElementType = "plain_text_input" METRadioButtons MessageElementType = "radio_buttons" + METEmailTextInput MessageElementType = "email_text_input" + METURLTextInput MessageElementType = "url_text_input" + METNumber MessageElementType = "number_input" MixedElementImage MixedElementType = "mixed_image" MixedElementText MixedElementType = "mixed_text" @@ -389,6 +392,64 @@ func NewTimePickerBlockElement(actionID string) *TimePickerBlockElement { } } +// EmailTextInputBlockElement creates a field where a user can enter email +// data. +// email-text-input elements are currently only available in modals. +// +// More Information: https://api.slack.com/reference/block-kit/block-elements#email +type EmailTextInputBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id,omitempty"` + Placeholder *TextBlockObject `json:"placeholder,omitempty"` + InitialValue string `json:"initial_value,omitempty"` + DispatchActionConfig *DispatchActionConfig `json:"dispatch_action_config,omitempty"` + FocusOnLoad bool `json:"focus_on_load,omitempty"` +} + +// ElementType returns the type of the Element +func (s EmailTextInputBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewEmailTextInputBlockElement returns an instance of a plain-text input +// element +func NewEmailTextInputBlockElement(placeholder *TextBlockObject, actionID string) *EmailTextInputBlockElement { + return &EmailTextInputBlockElement{ + Type: METEmailTextInput, + ActionID: actionID, + Placeholder: placeholder, + } +} + +// URLTextInputBlockElement creates a field where a user can enter url data. +// +// url-text-input elements are currently only available in modals. +// +// More Information: https://api.slack.com/reference/block-kit/block-elements#url +type URLTextInputBlockElement struct { + Type MessageElementType `json:"type"` + ActionID string `json:"action_id,omitempty"` + Placeholder *TextBlockObject `json:"placeholder,omitempty"` + InitialValue string `json:"initial_value,omitempty"` + DispatchActionConfig *DispatchActionConfig `json:"dispatch_action_config,omitempty"` + FocusOnLoad bool `json:"focus_on_load,omitempty"` +} + +// ElementType returns the type of the Element +func (s URLTextInputBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewURLTextInputBlockElement returns an instance of a plain-text input +// element +func NewURLTextInputBlockElement(placeholder *TextBlockObject, actionID string) *URLTextInputBlockElement { + return &URLTextInputBlockElement{ + Type: METURLTextInput, + ActionID: actionID, + Placeholder: placeholder, + } +} + // PlainTextInputBlockElement creates a field where a user can enter freeform // data. // Plain-text input elements are currently only available in modals. @@ -475,3 +536,34 @@ func NewRadioButtonsBlockElement(actionID string, options ...*OptionBlockObject) Options: options, } } + +// NumberInputBlockElement creates a field where a user can enter number +// data. +// Number input elements are currently only available in modals. +// +// More Information: https://api.slack.com/reference/block-kit/block-elements#number +type NumberInputBlockElement struct { + Type MessageElementType `json:"type"` + IsDecimalAllowed bool `json:"is_decimal_allowed"` + ActionID string `json:"action_id,omitempty"` + Placeholder *TextBlockObject `json:"placeholder,omitempty"` + InitialValue string `json:"initial_value,omitempty"` + MinValue string `json:"min_value,omitempty"` + MaxValue string `json:"max_value,omitempty"` + DispatchActionConfig *DispatchActionConfig `json:"dispatch_action_config,omitempty"` +} + +// ElementType returns the type of the Element +func (s NumberInputBlockElement) ElementType() MessageElementType { + return s.Type +} + +// NewNumberInputBlockElement returns an instance of a number input element +func NewNumberInputBlockElement(placeholder *TextBlockObject, actionID string, isDecimalAllowed bool) *NumberInputBlockElement { + return &NumberInputBlockElement{ + Type: METNumber, + ActionID: actionID, + Placeholder: placeholder, + IsDecimalAllowed: isDecimalAllowed, + } +} diff --git a/vendor/github.com/slack-go/slack/block_rich_text.go b/vendor/github.com/slack-go/slack/block_rich_text.go index 281db213..555a6198 100644 --- a/vendor/github.com/slack-go/slack/block_rich_text.go +++ b/vendor/github.com/slack-go/slack/block_rich_text.go @@ -327,17 +327,17 @@ func NewRichTextSectionUserGroupElement(usergroupID string) *RichTextSectionUser type RichTextSectionDateElement struct { Type RichTextSectionElementType `json:"type"` - Timestamp string `json:"timestamp"` + Timestamp JSONTime `json:"timestamp"` } func (r RichTextSectionDateElement) RichTextSectionElementType() RichTextSectionElementType { return r.Type } -func NewRichTextSectionDateElement(timestamp string) *RichTextSectionDateElement { +func NewRichTextSectionDateElement(timestamp int64) *RichTextSectionDateElement { return &RichTextSectionDateElement{ Type: RTSEDate, - Timestamp: timestamp, + Timestamp: JSONTime(timestamp), } } diff --git a/vendor/github.com/slack-go/slack/conversation.go b/vendor/github.com/slack-go/slack/conversation.go index e523716c..44f06ea9 100644 --- a/vendor/github.com/slack-go/slack/conversation.go +++ b/vendor/github.com/slack-go/slack/conversation.go @@ -2,6 +2,7 @@ package slack import ( "context" + "errors" "net/url" "strconv" "strings" @@ -71,6 +72,7 @@ type GetConversationsForUserParameters struct { Types []string Limit int ExcludeArchived bool + TeamID string } type responseMetaData struct { @@ -137,6 +139,10 @@ func (api *Client) GetConversationsForUserContext(ctx context.Context, params *G if params.ExcludeArchived { values.Add("exclude_archived", "true") } + if params.TeamID != "" { + values.Add("team_id", params.TeamID) + } + response := struct { Channels []Channel `json:"channels"` ResponseMetaData responseMetaData `json:"response_metadata"` @@ -337,17 +343,26 @@ func (api *Client) CloseConversationContext(ctx context.Context, channelID strin return response.NoOp, response.AlreadyClosed, response.Err() } +type CreateConversationParams struct { + ChannelName string + IsPrivate bool + TeamID string +} + // CreateConversation initiates a public or private channel-based conversation -func (api *Client) CreateConversation(channelName string, isPrivate bool) (*Channel, error) { - return api.CreateConversationContext(context.Background(), channelName, isPrivate) +func (api *Client) CreateConversation(params CreateConversationParams) (*Channel, error) { + return api.CreateConversationContext(context.Background(), params) } // CreateConversationContext initiates a public or private channel-based conversation with a custom context -func (api *Client) CreateConversationContext(ctx context.Context, channelName string, isPrivate bool) (*Channel, error) { +func (api *Client) CreateConversationContext(ctx context.Context, params CreateConversationParams) (*Channel, error) { values := url.Values{ "token": {api.token}, - "name": {channelName}, - "is_private": {strconv.FormatBool(isPrivate)}, + "name": {params.ChannelName}, + "is_private": {strconv.FormatBool(params.IsPrivate)}, + } + if params.TeamID != "" { + values.Set("team_id", params.TeamID) } response, err := api.channelRequest(ctx, "conversations.create", values) if err != nil { @@ -357,17 +372,33 @@ func (api *Client) CreateConversationContext(ctx context.Context, channelName st return &response.Channel, nil } +// GetConversationInfoInput Defines the parameters of a GetConversationInfo and GetConversationInfoContext function +type GetConversationInfoInput struct { + ChannelID string + IncludeLocale bool + IncludeNumMembers bool +} + // GetConversationInfo retrieves information about a conversation -func (api *Client) GetConversationInfo(channelID string, includeLocale bool) (*Channel, error) { - return api.GetConversationInfoContext(context.Background(), channelID, includeLocale) +func (api *Client) GetConversationInfo(input *GetConversationInfoInput) (*Channel, error) { + return api.GetConversationInfoContext(context.Background(), input) } // GetConversationInfoContext retrieves information about a conversation with a custom context -func (api *Client) GetConversationInfoContext(ctx context.Context, channelID string, includeLocale bool) (*Channel, error) { +func (api *Client) GetConversationInfoContext(ctx context.Context, input *GetConversationInfoInput) (*Channel, error) { + if input == nil { + return nil, errors.New("GetConversationInfoInput must not be nil") + } + + if input.ChannelID == "" { + return nil, errors.New("ChannelID must be defined") + } + values := url.Values{ - "token": {api.token}, - "channel": {channelID}, - "include_locale": {strconv.FormatBool(includeLocale)}, + "token": {api.token}, + "channel": {input.ChannelID}, + "include_locale": {strconv.FormatBool(input.IncludeLocale)}, + "include_num_members": {strconv.FormatBool(input.IncludeNumMembers)}, } response, err := api.channelRequest(ctx, "conversations.info", values) if err != nil { @@ -398,13 +429,14 @@ func (api *Client) LeaveConversationContext(ctx context.Context, channelID strin } type GetConversationRepliesParameters struct { - ChannelID string - Timestamp string - Cursor string - Inclusive bool - Latest string - Limit int - Oldest string + ChannelID string + Timestamp string + Cursor string + Inclusive bool + Latest string + Limit int + Oldest string + IncludeAllMetadata bool } // GetConversationReplies retrieves a thread of messages posted to a conversation @@ -436,6 +468,11 @@ func (api *Client) GetConversationRepliesContext(ctx context.Context, params *Ge } else { values.Add("inclusive", "0") } + if params.IncludeAllMetadata { + values.Add("include_all_metadata", "1") + } else { + values.Add("include_all_metadata", "0") + } response := struct { SlackResponse HasMore bool `json:"has_more"` diff --git a/vendor/github.com/slack-go/slack/files.go b/vendor/github.com/slack-go/slack/files.go index e7cfe1fe..35628442 100644 --- a/vendor/github.com/slack-go/slack/files.go +++ b/vendor/github.com/slack-go/slack/files.go @@ -2,6 +2,7 @@ package slack import ( "context" + "encoding/json" "fmt" "io" "net/url" @@ -145,6 +146,58 @@ type ListFilesParameters struct { Cursor string } +type UploadFileV2Parameters struct { + File string + FileSize int + Content string + Reader io.Reader + Filename string + Title string + InitialComment string + Channel string + ThreadTimestamp string + AltTxt string + SnippetText string +} + +type getUploadURLExternalParameters struct { + altText string + fileSize int + fileName string + snippetText string +} + +type getUploadURLExternalResponse struct { + UploadURL string `json:"upload_url"` + FileID string `json:"file_id"` + SlackResponse +} + +type uploadToURLParameters struct { + UploadURL string + Reader io.Reader + File string + Content string + Filename string +} + +type FileSummary struct { + ID string `json:"id"` + Title string `json:"title"` +} + +type completeUploadExternalParameters struct { + title string + channel string + initialComment string + threadTimestamp string +} + +type completeUploadExternalResponse struct { + SlackResponse + Files []FileSummary `json:"files"` +} + type fileResponseFull struct { File `json:"file"` Paging `json:"paging"` @@ -416,3 +469,129 @@ func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string) } return &response.File, response.Comments, &response.Paging, nil } + +// getUploadURLExternal gets a URL and fileID from slack which can later be used to upload a file +func (api *Client) getUploadURLExternal(ctx context.Context, params getUploadURLExternalParameters) (*getUploadURLExternalResponse, error) { + values := url.Values{ + "token": {api.token}, + "filename": {params.fileName}, + "length": {strconv.Itoa(params.fileSize)}, + } + if params.altText != "" { + values.Add("initial_comment", params.altText) + } + if params.snippetText != "" { + values.Add("thread_ts", params.snippetText) + } + response := &getUploadURLExternalResponse{} + err := api.postMethod(ctx, "files.getUploadURLExternal", values, response) + if err != nil { + return nil, err + } + + return response, response.Err() +} + +// uploadToURL uploads the file to the provided URL using post method +func (api *Client) uploadToURL(ctx context.Context, params uploadToURLParameters) (err error) { + values := url.Values{} + if params.Content != "" { + values.Add("content", params.Content) + values.Add("token", api.token) + err = postForm(ctx, api.httpclient, params.UploadURL, values, nil, api) + } else if params.File != "" { + err = postLocalWithMultipartResponse(ctx, api.httpclient, params.UploadURL, params.File, "file", api.token, values, nil, api) + } else if params.Reader != nil { + err = postWithMultipartResponse(ctx, api.httpclient, params.UploadURL, params.Filename, "file", api.token, values, params.Reader, nil, api) + } + return err +} + +// completeUploadExternal once files are uploaded, this completes the upload and shares it to the specified channel +func (api *Client) completeUploadExternal(ctx context.Context, fileID string, params completeUploadExternalParameters) (file *completeUploadExternalResponse, err error) { + request := []FileSummary{{ID: fileID, Title: params.title}} + requestBytes, err := json.Marshal(request) + if err != nil { + return nil, err + } + values := url.Values{ + "token": {api.token}, + "files": {string(requestBytes)}, + "channel_id": {params.channel}, + } + + if params.initialComment != "" { + values.Add("initial_comment", params.initialComment) + } + if params.threadTimestamp != "" { + values.Add("thread_ts", params.threadTimestamp) + } + response := &completeUploadExternalResponse{} + err = api.postMethod(ctx, "files.completeUploadExternal", values, response) + if err != nil { + return nil, err + } + if response.Err() != nil { + return nil, response.Err() + } + return response, nil +} + +// UploadFileV2 uploads file to a given slack channel using 3 steps - +// 1. Get an upload URL using files.getUploadURLExternal API +// 2. Send the file as a post to the URL provided by slack +// 3. Complete the upload and share it to the specified channel using files.completeUploadExternal +func (api *Client) UploadFileV2(params UploadFileV2Parameters) (*FileSummary, error) { + return api.UploadFileV2Context(context.Background(), params) +} + +// UploadFileV2 uploads file to a given slack channel using 3 steps with a custom context - +// 1. Get an upload URL using files.getUploadURLExternal API +// 2. Send the file as a post to the URL provided by slack +// 3. Complete the upload and share it to the specified channel using files.completeUploadExternal +func (api *Client) UploadFileV2Context(ctx context.Context, params UploadFileV2Parameters) (file *FileSummary, err error) { + if params.Filename == "" { + return nil, fmt.Errorf("file.upload.v2: filename cannot be empty") + } + if params.FileSize == 0 { + return nil, fmt.Errorf("file.upload.v2: file size cannot be 0") + } + if params.Channel == "" { + return nil, fmt.Errorf("file.upload.v2: channel cannot be empty") + } + u, err := api.getUploadURLExternal(ctx, getUploadURLExternalParameters{ + altText: params.AltTxt, + fileName: params.Filename, + fileSize: params.FileSize, + snippetText: params.SnippetText, + }) + if err != nil { + return nil, err + } + + err = api.uploadToURL(ctx, uploadToURLParameters{ + UploadURL: u.UploadURL, + Reader: params.Reader, + File: params.File, + Content: params.Content, + Filename: params.Filename, + }) + if err != nil { + return nil, err + } + + c, err := api.completeUploadExternal(ctx, u.FileID, completeUploadExternalParameters{ + title: params.Title, + channel: params.Channel, + initialComment: params.InitialComment, + threadTimestamp: params.ThreadTimestamp, + }) + if err != nil { + return nil, err + } + if len(c.Files) != 1 { + return nil, fmt.Errorf("file.upload.v2: something went wrong; received %d files instead of 1", len(c.Files)) + } + + return &c.Files[0], nil +} diff --git a/vendor/github.com/slack-go/slack/misc.go b/vendor/github.com/slack-go/slack/misc.go index 9180116a..7e5a8d54 100644 --- a/vendor/github.com/slack-go/slack/misc.go +++ b/vendor/github.com/slack-go/slack/misc.go @@ -307,6 +307,9 @@ type responseParser func(*http.Response) error func newJSONParser(dst interface{}) responseParser { return func(resp *http.Response) error { + if dst == nil { + return nil + } return json.NewDecoder(resp.Body).Decode(dst) } } diff --git a/vendor/github.com/slack-go/slack/team.go b/vendor/github.com/slack-go/slack/team.go index 029e2b5b..d21a1b64 100644 --- a/vendor/github.com/slack-go/slack/team.go +++ b/vendor/github.com/slack-go/slack/team.go @@ -24,6 +24,26 @@ type TeamInfo struct { Icon map[string]interface{} `json:"icon"` } +type TeamProfileResponse struct { + Profile TeamProfile `json:"profile"` + SlackResponse +} + +type TeamProfile struct { + Fields []TeamProfileField `json:"fields"` +} + +type TeamProfileField struct { + ID string `json:"id"` + Ordering int `json:"ordering"` + Label string `json:"label"` + Hint string `json:"hint"` + Type string `json:"type"` + PossibleValues []string `json:"possible_values"` + IsHidden bool `json:"is_hidden"` + Options map[string]bool `json:"options"` +} + type LoginResponse struct { Logins []Login `json:"logins"` Paging `json:"paging"` @@ -95,11 +115,41 @@ func (api *Client) accessLogsRequest(ctx context.Context, path string, values ur return response, response.Err() } +func (api *Client) teamProfileRequest(ctx context.Context, client httpClient, path string, values url.Values) (*TeamProfileResponse, error) { + response := &TeamProfileResponse{} + err := api.postMethod(ctx, path, values, response) + if err != nil { + return nil, err + } + return response, response.Err() +} + // GetTeamInfo gets the Team Information of the user func (api *Client) GetTeamInfo() (*TeamInfo, error) { return api.GetTeamInfoContext(context.Background()) } +// GetOtherTeamInfoContext gets Team information for any team with a custom context +func (api *Client) GetOtherTeamInfoContext(ctx context.Context, team string) (*TeamInfo, error) { + if team == "" { + return api.GetTeamInfoContext(ctx) + } + values := url.Values{ + "token": {api.token}, + } + values.Add("team", team) + response, err := api.teamRequest(ctx, "team.info", values) + if err != nil { + return nil, err + } + return &response.Team, nil +} + +// GetOtherTeamInfo gets Team information for any team +func (api *Client) GetOtherTeamInfo(team string) (*TeamInfo, error) { + return api.GetOtherTeamInfoContext(context.Background(), team) +} + // GetTeamInfoContext gets the Team Information of the user with a custom context func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) { values := url.Values{ @@ -113,6 +163,25 @@ func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) { return &response.Team, nil } +// GetTeamProfile gets the Team Profile settings of the user +func (api *Client) GetTeamProfile() (*TeamProfile, error) { + return api.GetTeamProfileContext(context.Background()) +} + +// GetTeamProfileContext gets the Team Profile settings of the user with a custom context +func (api *Client) GetTeamProfileContext(ctx context.Context) (*TeamProfile, error) { + values := url.Values{ + "token": {api.token}, + } + + response, err := api.teamProfileRequest(ctx, api.httpclient, "team.profile.get", values) + if err != nil { + return nil, err + } + return &response.Profile, nil + +} + // GetAccessLogs retrieves a page of logins according to the parameters given func (api *Client) GetAccessLogs(params AccessLogParameters) ([]Login, *Paging, error) { return api.GetAccessLogsContext(context.Background(), params) diff --git a/vendor/github.com/slack-go/slack/users.go b/vendor/github.com/slack-go/slack/users.go index d22b44f8..55f42118 100644 --- a/vendor/github.com/slack-go/slack/users.go +++ b/vendor/github.com/slack-go/slack/users.go @@ -17,30 +17,38 @@ const ( // UserProfile contains all the information details of a given user type UserProfile struct { - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - RealName string `json:"real_name"` - RealNameNormalized string `json:"real_name_normalized"` - DisplayName string `json:"display_name"` - DisplayNameNormalized string `json:"display_name_normalized"` - Email string `json:"email"` - Skype string `json:"skype"` - Phone string `json:"phone"` - Image24 string `json:"image_24"` - Image32 string `json:"image_32"` - Image48 string `json:"image_48"` - Image72 string `json:"image_72"` - Image192 string `json:"image_192"` - Image512 string `json:"image_512"` - ImageOriginal string `json:"image_original"` - Title string `json:"title"` - BotID string `json:"bot_id,omitempty"` - ApiAppID string `json:"api_app_id,omitempty"` - StatusText string `json:"status_text,omitempty"` - StatusEmoji string `json:"status_emoji,omitempty"` - StatusExpiration int `json:"status_expiration"` - Team string `json:"team"` - Fields UserProfileCustomFields `json:"fields"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + RealName string `json:"real_name"` + RealNameNormalized string `json:"real_name_normalized"` + DisplayName string `json:"display_name"` + DisplayNameNormalized string `json:"display_name_normalized"` + Email string `json:"email"` + Skype string `json:"skype"` + Phone string `json:"phone"` + Image24 string `json:"image_24"` + Image32 string `json:"image_32"` + Image48 string `json:"image_48"` + Image72 string `json:"image_72"` + Image192 string `json:"image_192"` + Image512 string `json:"image_512"` + ImageOriginal string `json:"image_original"` + Title string `json:"title"` + BotID string `json:"bot_id,omitempty"` + ApiAppID string `json:"api_app_id,omitempty"` + StatusText string `json:"status_text,omitempty"` + StatusEmoji string `json:"status_emoji,omitempty"` + StatusEmojiDisplayInfo []UserProfileStatusEmojiDisplayInfo `json:"status_emoji_display_info,omitempty"` + StatusExpiration int `json:"status_expiration"` + Team string `json:"team"` + Fields UserProfileCustomFields `json:"fields"` +} + +type UserProfileStatusEmojiDisplayInfo struct { + EmojiName string `json:"emoji_name"` + DisplayAlias string `json:"display_alias,omitempty"` + DisplayURL string `json:"display_url,omitempty"` + Unicode string `json:"unicode,omitempty"` } // UserProfileCustomFields represents user profile's custom fields. @@ -556,6 +564,55 @@ func (api *Client) SetUserRealNameContextWithUser(ctx context.Context, user, rea return response.Err() } +// SetUserCustomFields sets Custom Profile fields on the provided users account. Due to the non-repeating elements +// within the request, a map fields is required. The key in the map signifies the field that will be updated. +// +// Note: You may need to change the way the custom field is populated within the Profile section of the Admin Console from +// SCIM or User Entered to API. +// +// See GetTeamProfile for information to retrieve possible fields for your account. +func (api *Client) SetUserCustomFields(userID string, customFields map[string]UserProfileCustomField) error { + return api.SetUserCustomFieldsContext(context.Background(), userID, customFields) +} + +// SetUserCustomFieldsContext will set a users custom profile field with context. +// +// For more information see SetUserCustomFields +func (api *Client) SetUserCustomFieldsContext(ctx context.Context, userID string, customFields map[string]UserProfileCustomField) error { + + // Convert data to data type with custom marshall / unmarshall + // For more information, see UserProfileCustomFields definition. + updateFields := UserProfileCustomFields{} + updateFields.SetMap(customFields) + + // This anonymous struct is needed to set the fields level of the request data. The base struct for + // UserProfileCustomFields has an unexported variable named fields that does not contain a struct tag, + // which has resulted in this configuration. + profile, err := json.Marshal(&struct { + Fields UserProfileCustomFields `json:"fields"` + }{ + Fields: updateFields, + }) + + if err != nil { + return err + } + + values := url.Values{ + "token": {api.token}, + "user": {userID}, + "profile": {string(profile)}, + } + + response := &userResponseFull{} + if err := postForm(ctx, api.httpclient, APIURL+"users.profile.set", values, response, api); err != nil { + return err + } + + return response.Err() + +} + // SetUserCustomStatus will set a custom status and emoji for the currently // authenticated user. If statusEmoji is "" and statusText is not, the Slack API // will automatically set it to ":speech_balloon:". Otherwise, if both are "" diff --git a/vendor/github.com/slack-go/slack/webhooks.go b/vendor/github.com/slack-go/slack/webhooks.go index 2f8fb47f..e3233536 100644 --- a/vendor/github.com/slack-go/slack/webhooks.go +++ b/vendor/github.com/slack-go/slack/webhooks.go @@ -21,8 +21,8 @@ type WebhookMessage struct { Parse string `json:"parse,omitempty"` Blocks *Blocks `json:"blocks,omitempty"` ResponseType string `json:"response_type,omitempty"` - ReplaceOriginal bool `json:"replace_original,omitempty"` - DeleteOriginal bool `json:"delete_original,omitempty"` + ReplaceOriginal bool `json:"replace_original"` + DeleteOriginal bool `json:"delete_original"` ReplyBroadcast bool `json:"reply_broadcast,omitempty"` } diff --git a/vendor/github.com/slack-go/slack/websocket_reactions.go b/vendor/github.com/slack-go/slack/websocket_reactions.go index e4973878..6098a6ca 100644 --- a/vendor/github.com/slack-go/slack/websocket_reactions.go +++ b/vendor/github.com/slack-go/slack/websocket_reactions.go @@ -1,7 +1,7 @@ package slack -// reactionItem is a lighter-weight item than is returned by the reactions list. -type reactionItem struct { +// ReactionItem is a lighter-weight item than is returned by the reactions list. +type ReactionItem struct { Type string `json:"type"` Channel string `json:"channel,omitempty"` File string `json:"file,omitempty"` @@ -9,17 +9,17 @@ type reactionItem struct { Timestamp string `json:"ts,omitempty"` } -type reactionEvent struct { +type ReactionEvent struct { Type string `json:"type"` User string `json:"user"` ItemUser string `json:"item_user"` - Item reactionItem `json:"item"` + Item ReactionItem `json:"item"` Reaction string `json:"reaction"` EventTimestamp string `json:"event_ts"` } // ReactionAddedEvent represents the Reaction added event -type ReactionAddedEvent reactionEvent +type ReactionAddedEvent ReactionEvent // ReactionRemovedEvent represents the Reaction removed event -type ReactionRemovedEvent reactionEvent +type ReactionRemovedEvent ReactionEvent diff --git a/vendor/github.com/slack-go/slack/workflow_step_execute.go b/vendor/github.com/slack-go/slack/workflow_step_execute.go index 18516f23..32db9ba0 100644 --- a/vendor/github.com/slack-go/slack/workflow_step_execute.go +++ b/vendor/github.com/slack-go/slack/workflow_step_execute.go @@ -19,10 +19,10 @@ type ( } ) -type WorkflowStepCompletedRequestOption func(opt WorkflowStepCompletedRequest) error +type WorkflowStepCompletedRequestOption func(opt *WorkflowStepCompletedRequest) error func WorkflowStepCompletedRequestOptionOutput(outputs map[string]string) WorkflowStepCompletedRequestOption { - return func(opt WorkflowStepCompletedRequest) error { + return func(opt *WorkflowStepCompletedRequest) error { if len(outputs) > 0 { opt.Outputs = outputs } @@ -33,7 +33,7 @@ func WorkflowStepCompletedRequestOptionOutput(outputs map[string]string) Workflo // WorkflowStepCompleted indicates step is completed func (api *Client) WorkflowStepCompleted(workflowStepExecuteID string, options ...WorkflowStepCompletedRequestOption) error { // More information: https://api.slack.com/methods/workflows.stepCompleted - r := WorkflowStepCompletedRequest{ + r := &WorkflowStepCompletedRequest{ WorkflowStepExecuteID: workflowStepExecuteID, } for _, option := range options { diff --git a/vendor/github.com/spf13/afero/.travis.yml b/vendor/github.com/spf13/afero/.travis.yml deleted file mode 100644 index e944f594..00000000 --- a/vendor/github.com/spf13/afero/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -sudo: false -language: go -arch: - - amd64 - - ppc64e - -go: - - "1.14" - - "1.15" - - "1.16" - - tip - -os: - - linux - - osx - -matrix: - allow_failures: - - go: tip - fast_finish: true - -script: - - go build -v ./... - - go test -count=1 -cover -race -v ./... - - go vet ./... - - FILES=$(gofmt -s -l . zipfs sftpfs mem tarfs); if [[ -n "${FILES}" ]]; then echo "You have go format errors; gofmt your changes"; exit 1; fi diff --git a/vendor/github.com/spf13/afero/README.md b/vendor/github.com/spf13/afero/README.md index cab257f5..3bafbfdf 100644 --- a/vendor/github.com/spf13/afero/README.md +++ b/vendor/github.com/spf13/afero/README.md @@ -2,7 +2,7 @@ A FileSystem Abstraction System for Go -[![Build Status](https://travis-ci.org/spf13/afero.svg)](https://travis-ci.org/spf13/afero) [![Build status](https://ci.appveyor.com/api/projects/status/github/spf13/afero?branch=master&svg=true)](https://ci.appveyor.com/project/spf13/afero) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Test](https://github.com/spf13/afero/actions/workflows/test.yml/badge.svg)](https://github.com/spf13/afero/actions/workflows/test.yml) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Overview diff --git a/vendor/github.com/spf13/afero/afero.go b/vendor/github.com/spf13/afero/afero.go index 469ff7d2..199480cd 100644 --- a/vendor/github.com/spf13/afero/afero.go +++ b/vendor/github.com/spf13/afero/afero.go @@ -103,8 +103,8 @@ type Fs interface { var ( ErrFileClosed = errors.New("File is closed") - ErrOutOfRange = errors.New("Out of range") - ErrTooLarge = errors.New("Too large") + ErrOutOfRange = errors.New("out of range") + ErrTooLarge = errors.New("too large") ErrFileNotFound = os.ErrNotExist ErrFileExists = os.ErrExist ErrDestinationExists = os.ErrExist diff --git a/vendor/github.com/spf13/afero/appveyor.yml b/vendor/github.com/spf13/afero/appveyor.yml index 5d2f34bf..65e20e8c 100644 --- a/vendor/github.com/spf13/afero/appveyor.yml +++ b/vendor/github.com/spf13/afero/appveyor.yml @@ -1,3 +1,5 @@ +# This currently does nothing. We have moved to GitHub action, but this is kept +# until spf13 has disabled this project in AppVeyor. version: '{build}' clone_folder: C:\gopath\src\github.com\spf13\afero environment: @@ -6,10 +8,3 @@ build_script: - cmd: >- go version - go env - - go get -v github.com/spf13/afero/... - - go build -v github.com/spf13/afero/... -test_script: -- cmd: go test -count=1 -cover -race -v github.com/spf13/afero/... diff --git a/vendor/github.com/spf13/afero/basepath.go b/vendor/github.com/spf13/afero/basepath.go index 4f983282..70a1d916 100644 --- a/vendor/github.com/spf13/afero/basepath.go +++ b/vendor/github.com/spf13/afero/basepath.go @@ -1,6 +1,7 @@ package afero import ( + "io/fs" "os" "path/filepath" "runtime" @@ -8,7 +9,10 @@ import ( "time" ) -var _ Lstater = (*BasePathFs)(nil) +var ( + _ Lstater = (*BasePathFs)(nil) + _ fs.ReadDirFile = (*BasePathFile)(nil) +) // The BasePathFs restricts all operations to a given path within an Fs. // The given file name to the operations on this Fs will be prepended with @@ -33,6 +37,14 @@ func (f *BasePathFile) Name() string { return strings.TrimPrefix(sourcename, filepath.Clean(f.path)) } +func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) { + if rdf, ok := f.File.(fs.ReadDirFile); ok { + return rdf.ReadDir(n) + + } + return readDirFile{f.File}.ReadDir(n) +} + func NewBasePathFs(source Fs, path string) Fs { return &BasePathFs{source: source, path: path} } diff --git a/vendor/github.com/spf13/afero/const_bsds.go b/vendor/github.com/spf13/afero/const_bsds.go index 18b45824..eed0f225 100644 --- a/vendor/github.com/spf13/afero/const_bsds.go +++ b/vendor/github.com/spf13/afero/const_bsds.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build aix || darwin || openbsd || freebsd || netbsd || dragonfly // +build aix darwin openbsd freebsd netbsd dragonfly package afero diff --git a/vendor/github.com/spf13/afero/const_win_unix.go b/vendor/github.com/spf13/afero/const_win_unix.go index 2b850e4d..004d57e2 100644 --- a/vendor/github.com/spf13/afero/const_win_unix.go +++ b/vendor/github.com/spf13/afero/const_win_unix.go @@ -10,12 +10,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// +build !darwin -// +build !openbsd -// +build !freebsd -// +build !dragonfly -// +build !netbsd -// +build !aix +//go:build !darwin && !openbsd && !freebsd && !dragonfly && !netbsd && !aix +// +build !darwin,!openbsd,!freebsd,!dragonfly,!netbsd,!aix package afero diff --git a/vendor/github.com/spf13/afero/httpFs.go b/vendor/github.com/spf13/afero/httpFs.go index 2b86e30d..ac0de6d5 100644 --- a/vendor/github.com/spf13/afero/httpFs.go +++ b/vendor/github.com/spf13/afero/httpFs.go @@ -29,7 +29,7 @@ type httpDir struct { } func (d httpDir) Open(name string) (http.File, error) { - if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || + if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) || strings.Contains(name, "\x00") { return nil, errors.New("http: invalid character in file path") } diff --git a/vendor/github.com/spf13/afero/internal/common/adapters.go b/vendor/github.com/spf13/afero/internal/common/adapters.go new file mode 100644 index 00000000..60685caa --- /dev/null +++ b/vendor/github.com/spf13/afero/internal/common/adapters.go @@ -0,0 +1,27 @@ +// Copyright © 2022 Steve Francia <spf@spf13.com>. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import "io/fs" + +// FileInfoDirEntry provides an adapter from os.FileInfo to fs.DirEntry +type FileInfoDirEntry struct { + fs.FileInfo +} + +var _ fs.DirEntry = FileInfoDirEntry{} + +func (d FileInfoDirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() } + +func (d FileInfoDirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil } diff --git a/vendor/github.com/spf13/afero/iofs.go b/vendor/github.com/spf13/afero/iofs.go index c8034553..938b9316 100644 --- a/vendor/github.com/spf13/afero/iofs.go +++ b/vendor/github.com/spf13/afero/iofs.go @@ -1,3 +1,4 @@ +//go:build go1.16 // +build go1.16 package afero @@ -7,7 +8,10 @@ import ( "io/fs" "os" "path" + "sort" "time" + + "github.com/spf13/afero/internal/common" ) // IOFS adopts afero.Fs to stdlib io/fs.FS @@ -66,14 +70,31 @@ func (iofs IOFS) Glob(pattern string) ([]string, error) { } func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) { - items, err := ReadDir(iofs.Fs, name) + f, err := iofs.Fs.Open(name) if err != nil { return nil, iofs.wrapError("readdir", name, err) } + defer f.Close() + + if rdf, ok := f.(fs.ReadDirFile); ok { + items, err := rdf.ReadDir(-1) + if err != nil { + return nil, iofs.wrapError("readdir", name, err) + } + sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() }) + return items, nil + } + + items, err := f.Readdir(-1) + if err != nil { + return nil, iofs.wrapError("readdir", name, err) + } + sort.Sort(byName(items)) + ret := make([]fs.DirEntry, len(items)) for i := range items { - ret[i] = dirEntry{items[i]} + ret[i] = common.FileInfoDirEntry{FileInfo: items[i]} } return ret, nil @@ -108,17 +129,6 @@ func (IOFS) wrapError(op, path string, err error) error { } } -// dirEntry provides adapter from os.FileInfo to fs.DirEntry -type dirEntry struct { - fs.FileInfo -} - -var _ fs.DirEntry = dirEntry{} - -func (d dirEntry) Type() fs.FileMode { return d.FileInfo.Mode().Type() } - -func (d dirEntry) Info() (fs.FileInfo, error) { return d.FileInfo, nil } - // readDirFile provides adapter from afero.File to fs.ReadDirFile needed for correct Open type readDirFile struct { File @@ -134,7 +144,7 @@ func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) { ret := make([]fs.DirEntry, len(items)) for i := range items { - ret[i] = dirEntry{items[i]} + ret[i] = common.FileInfoDirEntry{FileInfo: items[i]} } return ret, nil diff --git a/vendor/github.com/spf13/afero/ioutil.go b/vendor/github.com/spf13/afero/ioutil.go index a403133e..386c9cdc 100644 --- a/vendor/github.com/spf13/afero/ioutil.go +++ b/vendor/github.com/spf13/afero/ioutil.go @@ -141,7 +141,7 @@ func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error { // We generate random temporary file names so that there's a good // chance the file doesn't exist yet - keeps the number of tries in // TempFile to a minimum. -var rand uint32 +var randNum uint32 var randmu sync.Mutex func reseed() uint32 { @@ -150,12 +150,12 @@ func reseed() uint32 { func nextRandom() string { randmu.Lock() - r := rand + r := randNum if r == 0 { r = reseed() } r = r*1664525 + 1013904223 // constants from Numerical Recipes - rand = r + randNum = r randmu.Unlock() return strconv.Itoa(int(1e9 + r%1e9))[1:] } @@ -194,7 +194,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) { if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() - rand = reseed() + randNum = reseed() randmu.Unlock() } continue @@ -226,7 +226,7 @@ func TempDir(fs Fs, dir, prefix string) (name string, err error) { if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() - rand = reseed() + randNum = reseed() randmu.Unlock() } continue diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go index 5ef8b6a3..3cf4693b 100644 --- a/vendor/github.com/spf13/afero/mem/file.go +++ b/vendor/github.com/spf13/afero/mem/file.go @@ -18,15 +18,20 @@ import ( "bytes" "errors" "io" + "io/fs" "os" "path/filepath" "sync" "sync/atomic" "time" + + "github.com/spf13/afero/internal/common" ) const FilePathSeparator = string(filepath.Separator) +var _ fs.ReadDirFile = &File{} + type File struct { // atomic requires 64-bit alignment for struct field access at int64 @@ -183,10 +188,23 @@ func (f *File) Readdirnames(n int) (names []string, err error) { return names, err } +// Implements fs.ReadDirFile +func (f *File) ReadDir(n int) ([]fs.DirEntry, error) { + fi, err := f.Readdir(n) + if err != nil { + return nil, err + } + di := make([]fs.DirEntry, len(fi)) + for i, f := range fi { + di[i] = common.FileInfoDirEntry{FileInfo: f} + } + return di, nil +} + func (f *File) Read(b []byte) (n int, err error) { f.fileData.Lock() defer f.fileData.Unlock() - if f.closed == true { + if f.closed { return 0, ErrFileClosed } if len(b) > 0 && int(f.at) == len(f.fileData.data) { @@ -214,7 +232,7 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) { } func (f *File) Truncate(size int64) error { - if f.closed == true { + if f.closed { return ErrFileClosed } if f.readOnly { @@ -236,7 +254,7 @@ func (f *File) Truncate(size int64) error { } func (f *File) Seek(offset int64, whence int) (int64, error) { - if f.closed == true { + if f.closed { return 0, ErrFileClosed } switch whence { @@ -251,7 +269,7 @@ func (f *File) Seek(offset int64, whence int) (int64, error) { } func (f *File) Write(b []byte) (n int, err error) { - if f.closed == true { + if f.closed { return 0, ErrFileClosed } if f.readOnly { @@ -330,8 +348,8 @@ func (s *FileInfo) Size() int64 { var ( ErrFileClosed = errors.New("File is closed") - ErrOutOfRange = errors.New("Out of range") - ErrTooLarge = errors.New("Too large") + ErrOutOfRange = errors.New("out of range") + ErrTooLarge = errors.New("too large") ErrFileNotFound = os.ErrNotExist ErrFileExists = os.ErrExist ErrDestinationExists = os.ErrExist diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go index ea0798d8..d06975e7 100644 --- a/vendor/github.com/spf13/afero/memmap.go +++ b/vendor/github.com/spf13/afero/memmap.go @@ -142,6 +142,11 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error { } m.mu.Lock() + // Dobule check that it doesn't exist. + if _, ok := m.getData()[name]; ok { + m.mu.Unlock() + return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists} + } item := mem.CreateDir(name) mem.SetMode(item, os.ModeDir|perm) m.getData()[name] = item diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go index 34f99a40..333d367f 100644 --- a/vendor/github.com/spf13/afero/unionFile.go +++ b/vendor/github.com/spf13/afero/unionFile.go @@ -65,7 +65,7 @@ func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) { if f.Layer != nil { n, err := f.Layer.ReadAt(s, o) if (err == nil || err == io.EOF) && f.Base != nil { - _, err = f.Base.Seek(o+int64(n), os.SEEK_SET) + _, err = f.Base.Seek(o+int64(n), io.SeekStart) } return n, err } diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go index 4f253f48..cb7de23f 100644 --- a/vendor/github.com/spf13/afero/util.go +++ b/vendor/github.com/spf13/afero/util.go @@ -25,6 +25,7 @@ import ( "strings" "unicode" + "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" ) @@ -158,16 +159,12 @@ func UnicodeSanitize(s string) string { // Transform characters with accents into plain forms. func NeuterAccents(s string) string { - t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) + t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC) result, _, _ := transform.String(t, string(s)) return result } -func isMn(r rune) bool { - return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks -} - func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) { return FileContainsBytes(a.Fs, filename, subslice) } @@ -299,6 +296,9 @@ func IsEmpty(fs Fs, path string) (bool, error) { } defer f.Close() list, err := f.Readdir(-1) + if err != nil { + return false, err + } return len(list) == 0, nil } return fi.Size() == 0, nil diff --git a/vendor/github.com/spf13/viper/Makefile b/vendor/github.com/spf13/viper/Makefile index 02d3e371..3f4234d3 100644 --- a/vendor/github.com/spf13/viper/Makefile +++ b/vendor/github.com/spf13/viper/Makefile @@ -16,7 +16,7 @@ endif # Dependency versions GOTESTSUM_VERSION = 1.8.0 -GOLANGCI_VERSION = 1.45.2 +GOLANGCI_VERSION = 1.50.1 # Add the ability to override some variables # Use with care @@ -48,7 +48,7 @@ bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION} @ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint bin/golangci-lint-${GOLANGCI_VERSION}: @mkdir -p bin - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION} + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION} @mv bin/golangci-lint "$@" .PHONY: lint diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index c14e8927..cd392905 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -8,10 +8,10 @@ [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration) [![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go) -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) -![Go Version](https://img.shields.io/badge/go%20version-%3E=1.15-61CFDD.svg?style=flat-square) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square) [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper) **Go configuration with fangs!** @@ -40,8 +40,8 @@ go get github.com/spf13/viper ## What is Viper? -Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed -to work within an application, and can handle all types of configuration needs +Viper is a complete configuration solution for Go applications including [12-Factor apps](https://12factor.net/#the_twelve_factors). +It is designed to work within an application, and can handle all types of configuration needs and formats. It supports: * setting defaults @@ -119,7 +119,7 @@ viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search viper.AddConfigPath(".") // optionally look for config in the working directory err := viper.ReadInConfig() // Find and read the config file if err != nil { // Handle errors reading the config file - panic(fmt.Errorf("Fatal error config file: %w \n", err)) + panic(fmt.Errorf("fatal error config file: %w", err)) } ``` @@ -447,6 +447,13 @@ viper.SetConfigType("json") // because there is no file extension in a stream of err := viper.ReadRemoteConfig() ``` +#### etcd3 +```go +viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json") +viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" +err := viper.ReadRemoteConfig() +``` + #### Consul You need to set a key to Consul key/value storage with JSON value containing your desired config. For example, create a Consul key/value store key `MY_CONSUL_KEY` with value: @@ -594,7 +601,7 @@ configuration level. Viper can access array indices by using numbers in the path. For example: -```json +```jsonc { "host": { "address": "localhost", @@ -622,7 +629,7 @@ GetInt("host.ports.1") // returns 6029 Lastly, if there exists a key that matches the delimited key path, its value will be returned instead. E.g. -```json +```jsonc { "datastore.metric.host": "0.0.0.0", "host": { diff --git a/vendor/github.com/spf13/viper/internal/encoding/toml/codec.go b/vendor/github.com/spf13/viper/internal/encoding/toml/codec.go index 45fddc8b..a993c599 100644 --- a/vendor/github.com/spf13/viper/internal/encoding/toml/codec.go +++ b/vendor/github.com/spf13/viper/internal/encoding/toml/codec.go @@ -1,39 +1,16 @@ -//go:build viper_toml1 -// +build viper_toml1 - package toml import ( - "github.com/pelletier/go-toml" + "github.com/pelletier/go-toml/v2" ) // Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding. type Codec struct{} func (Codec) Encode(v map[string]interface{}) ([]byte, error) { - t, err := toml.TreeFromMap(v) - if err != nil { - return nil, err - } - - s, err := t.ToTomlString() - if err != nil { - return nil, err - } - - return []byte(s), nil + return toml.Marshal(v) } func (Codec) Decode(b []byte, v map[string]interface{}) error { - tree, err := toml.LoadBytes(b) - if err != nil { - return err - } - - tmap := tree.ToMap() - for key, value := range tmap { - v[key] = value - } - - return nil + return toml.Unmarshal(b, &v) } diff --git a/vendor/github.com/spf13/viper/internal/encoding/toml/codec2.go b/vendor/github.com/spf13/viper/internal/encoding/toml/codec2.go deleted file mode 100644 index 112c6d37..00000000 --- a/vendor/github.com/spf13/viper/internal/encoding/toml/codec2.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !viper_toml1 -// +build !viper_toml1 - -package toml - -import ( - "github.com/pelletier/go-toml/v2" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding. -type Codec struct{} - -func (Codec) Encode(v map[string]interface{}) ([]byte, error) { - return toml.Marshal(v) -} - -func (Codec) Decode(b []byte, v map[string]interface{}) error { - return toml.Unmarshal(b, &v) -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go b/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go index 24cc19df..82dc136a 100644 --- a/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go +++ b/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go @@ -1,6 +1,6 @@ package yaml -// import "gopkg.in/yaml.v2" +import "gopkg.in/yaml.v3" // Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding. type Codec struct{} diff --git a/vendor/github.com/spf13/viper/internal/encoding/yaml/yaml2.go b/vendor/github.com/spf13/viper/internal/encoding/yaml/yaml2.go deleted file mode 100644 index 4c398c2f..00000000 --- a/vendor/github.com/spf13/viper/internal/encoding/yaml/yaml2.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build viper_yaml2 -// +build viper_yaml2 - -package yaml - -import yamlv2 "gopkg.in/yaml.v2" - -var yaml = struct { - Marshal func(in interface{}) (out []byte, err error) - Unmarshal func(in []byte, out interface{}) (err error) -}{ - Marshal: yamlv2.Marshal, - Unmarshal: yamlv2.Unmarshal, -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/yaml/yaml3.go b/vendor/github.com/spf13/viper/internal/encoding/yaml/yaml3.go deleted file mode 100644 index 3a4775ce..00000000 --- a/vendor/github.com/spf13/viper/internal/encoding/yaml/yaml3.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build !viper_yaml2 -// +build !viper_yaml2 - -package yaml - -import yamlv3 "gopkg.in/yaml.v3" - -var yaml = struct { - Marshal func(in interface{}) (out []byte, err error) - Unmarshal func(in []byte, out interface{}) (err error) -}{ - Marshal: yamlv3.Marshal, - Unmarshal: yamlv3.Unmarshal, -} diff --git a/vendor/github.com/spf13/viper/logger.go b/vendor/github.com/spf13/viper/logger.go index 0115067a..a64e1446 100644 --- a/vendor/github.com/spf13/viper/logger.go +++ b/vendor/github.com/spf13/viper/logger.go @@ -7,8 +7,8 @@ import ( ) // Logger is a unified interface for various logging use cases and practices, including: -// - leveled logging -// - structured logging +// - leveled logging +// - structured logging type Logger interface { // Trace logs a Trace event. // diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go index ee7a86d9..64e65750 100644 --- a/vendor/github.com/spf13/viper/util.go +++ b/vendor/github.com/spf13/viper/util.go @@ -64,18 +64,25 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} { return nm } +func insensitiviseVal(val interface{}) interface{} { + switch val.(type) { + case map[interface{}]interface{}: + // nested map: cast and recursively insensitivise + val = cast.ToStringMap(val) + insensitiviseMap(val.(map[string]interface{})) + case map[string]interface{}: + // nested map: recursively insensitivise + insensitiviseMap(val.(map[string]interface{})) + case []interface{}: + // nested array: recursively insensitivise + insensitiveArray(val.([]interface{})) + } + return val +} + func insensitiviseMap(m map[string]interface{}) { for key, val := range m { - switch val.(type) { - case map[interface{}]interface{}: - // nested map: cast and recursively insensitivise - val = cast.ToStringMap(val) - insensitiviseMap(val.(map[string]interface{})) - case map[string]interface{}: - // nested map: recursively insensitivise - insensitiviseMap(val.(map[string]interface{})) - } - + val = insensitiviseVal(val) lower := strings.ToLower(key) if key != lower { // remove old key (not lower-cased) @@ -86,6 +93,12 @@ func insensitiviseMap(m map[string]interface{}) { } } +func insensitiveArray(a []interface{}) { + for i, val := range a { + a[i] = insensitiviseVal(val) + } +} + func absPathify(logger Logger, inPath string) string { logger.Info("trying to resolve absolute path", "path", inPath) diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index a3812e92..06610fc5 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -132,10 +132,10 @@ type DecoderConfigOption func(*mapstructure.DecoderConfig) // DecodeHook returns a DecoderConfigOption which overrides the default // DecoderConfig.DecodeHook value, the default is: // -// mapstructure.ComposeDecodeHookFunc( -// mapstructure.StringToTimeDurationHookFunc(), -// mapstructure.StringToSliceHookFunc(","), -// ) +// mapstructure.ComposeDecodeHookFunc( +// mapstructure.StringToTimeDurationHookFunc(), +// mapstructure.StringToSliceHookFunc(","), +// ) func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption { return func(c *mapstructure.DecoderConfig) { c.DecodeHook = hook @@ -156,18 +156,18 @@ func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption { // // For example, if values from the following sources were loaded: // -// Defaults : { -// "secret": "", -// "user": "default", -// "endpoint": "https://localhost" -// } -// Config : { -// "user": "root" -// "secret": "defaultsecret" -// } -// Env : { -// "secret": "somesecretkey" -// } +// Defaults : { +// "secret": "", +// "user": "default", +// "endpoint": "https://localhost" +// } +// Config : { +// "user": "root" +// "secret": "defaultsecret" +// } +// Env : { +// "secret": "somesecretkey" +// } // // The resulting config will have the following values: // @@ -300,7 +300,7 @@ func NewWithOptions(opts ...Option) *Viper { func Reset() { v = New() SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} - SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} + SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"} } // TODO: make this lazy initialization instead @@ -419,15 +419,20 @@ type RemoteProvider interface { var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} // SupportedRemoteProviders are universally supported remote providers. -var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} +var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"} +// OnConfigChange sets the event handler that is called when a config file changes. func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } + +// OnConfigChange sets the event handler that is called when a config file changes. func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { v.onConfigChange = run } +// WatchConfig starts watching a config file for changes. func WatchConfig() { v.WatchConfig() } +// WatchConfig starts watching a config file for changes. func (v *Viper) WatchConfig() { initWG := sync.WaitGroup{} initWG.Add(1) @@ -463,9 +468,8 @@ func (v *Viper) WatchConfig() { // we only care about the config file with the following cases: // 1 - if the config file was modified or created // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement) - const writeOrCreateMask = fsnotify.Write | fsnotify.Create if (filepath.Clean(event.Name) == configFile && - event.Op&writeOrCreateMask != 0) || + (event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) || (currentConfigFile != "" && currentConfigFile != realConfigFile) { realConfigFile = currentConfigFile err := v.ReadInConfig() @@ -475,8 +479,7 @@ func (v *Viper) WatchConfig() { if v.onConfigChange != nil { v.onConfigChange(event) } - } else if filepath.Clean(event.Name) == configFile && - event.Op&fsnotify.Remove != 0 { + } else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) { eventsWG.Done() return } @@ -573,7 +576,7 @@ func (v *Viper) AddConfigPath(in string) { // AddRemoteProvider adds a remote configuration source. // Remote Providers are searched in the order they are added. -// provider is a string value: "etcd", "consul" or "firestore" are currently supported. +// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported. // endpoint is the url. etcd requires http://ip:port consul requires ip:port // path is the path in the k/v store to retrieve configuration // To retrieve a config file called myapp.json from /configs/myapp.json @@ -604,7 +607,7 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { // AddSecureRemoteProvider adds a remote configuration source. // Secure Remote Providers are searched in the order they are added. -// provider is a string value: "etcd", "consul" or "firestore" are currently supported. +// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported. // endpoint is the url. etcd requires http://ip:port consul requires ip:port // secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg // path is the path in the k/v store to retrieve configuration @@ -785,7 +788,8 @@ func (v *Viper) searchMapWithPathPrefixes( // isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere // on its path in the map. // e.g., if "foo.bar" has a value in the given map, it “shadows” -// "foo.bar.baz" in a lower-priority map +// +// "foo.bar.baz" in a lower-priority map func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string { var parentVal interface{} for i := 1; i < len(path); i++ { @@ -810,7 +814,8 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) // isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere // in a sub-path of the map. // e.g., if "foo.bar" has a value in the given map, it “shadows” -// "foo.bar.baz" in a lower-priority map +// +// "foo.bar.baz" in a lower-priority map func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string { // unify input map var m map[string]interface{} @@ -835,7 +840,8 @@ func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string { // isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere // in the environment, when automatic env is on. // e.g., if "foo.bar" has a value in the environment, it “shadows” -// "foo.bar.baz" in a lower-priority map +// +// "foo.bar.baz" in a lower-priority map func (v *Viper) isPathShadowedInAutoEnv(path []string) string { var parentKey string for i := 1; i < len(path); i++ { @@ -856,11 +862,11 @@ func (v *Viper) isPathShadowedInAutoEnv(path []string) string { // would return a string slice for the key if the key's type is inferred by // the default value and the Get function would return: // -// []string {"a", "b", "c"} +// []string {"a", "b", "c"} // // Otherwise the Get function would return: // -// "a b c" +// "a b c" func SetTypeByDefaultValue(enable bool) { v.SetTypeByDefaultValue(enable) } func (v *Viper) SetTypeByDefaultValue(enable bool) { @@ -988,6 +994,13 @@ func (v *Viper) GetUint(key string) uint { return cast.ToUint(v.Get(key)) } +// GetUint16 returns the value associated with the key as an unsigned integer. +func GetUint16(key string) uint16 { return v.GetUint16(key) } + +func (v *Viper) GetUint16(key string) uint16 { + return cast.ToUint16(v.Get(key)) +} + // GetUint32 returns the value associated with the key as an unsigned integer. func GetUint32(key string) uint32 { return v.GetUint32(key) } @@ -1137,9 +1150,8 @@ func (v *Viper) BindPFlags(flags *pflag.FlagSet) error { // BindPFlag binds a specific key to a pflag (as used by cobra). // Example (where serverCmd is a Cobra instance): // -// serverCmd.Flags().Int("port", 1138, "Port to run Application server on") -// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) -// +// serverCmd.Flags().Int("port", 1138, "Port to run Application server on") +// Viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) } func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error { @@ -1870,6 +1882,10 @@ func (v *Viper) getKeyValueConfig() error { return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'") } + if len(v.remoteProviders) == 0 { + return RemoteConfigError("No Remote Providers") + } + for _, rp := range v.remoteProviders { val, err := v.getRemoteConfig(rp) if err != nil { @@ -1896,6 +1912,10 @@ func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{} // Retrieve the first found remote configuration. func (v *Viper) watchKeyValueConfigOnChannel() error { + if len(v.remoteProviders) == 0 { + return RemoteConfigError("No Remote Providers") + } + for _, rp := range v.remoteProviders { respc, _ := RemoteConfig.WatchChannel(rp) // Todo: Add quit channel @@ -1913,9 +1933,15 @@ func (v *Viper) watchKeyValueConfigOnChannel() error { // Retrieve the first found remote configuration. func (v *Viper) watchKeyValueConfig() error { + if len(v.remoteProviders) == 0 { + return RemoteConfigError("No Remote Providers") + } + for _, rp := range v.remoteProviders { val, err := v.watchRemoteConfig(rp) if err != nil { + v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error()) + continue } v.kvstore = val @@ -1958,9 +1984,10 @@ func (v *Viper) AllKeys() []string { // flattenAndMergeMap recursively flattens the given map into a map[string]bool // of key paths (used as a set, easier to manipulate than a []string): -// - each path is merged into a single key string, delimited with v.keyDelim -// - if a path is shadowed by an earlier value in the initial shadow map, -// it is skipped. +// - each path is merged into a single key string, delimited with v.keyDelim +// - if a path is shadowed by an earlier value in the initial shadow map, +// it is skipped. +// // The resulting set of paths is merged to the given shadow set at the same time. func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool { if shadow != nil && prefix != "" && shadow[prefix] { @@ -2111,14 +2138,17 @@ func (v *Viper) getConfigFile() (string, error) { // Debug prints all configuration registries for debugging // purposes. -func Debug() { v.Debug() } - -func (v *Viper) Debug() { - fmt.Printf("Aliases:\n%#v\n", v.aliases) - fmt.Printf("Override:\n%#v\n", v.override) - fmt.Printf("PFlags:\n%#v\n", v.pflags) - fmt.Printf("Env:\n%#v\n", v.env) - fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore) - fmt.Printf("Config:\n%#v\n", v.config) - fmt.Printf("Defaults:\n%#v\n", v.defaults) +func Debug() { v.Debug() } +func DebugTo(w io.Writer) { v.DebugTo(w) } + +func (v *Viper) Debug() { v.DebugTo(os.Stdout) } + +func (v *Viper) DebugTo(w io.Writer) { + fmt.Fprintf(w, "Aliases:\n%#v\n", v.aliases) + fmt.Fprintf(w, "Override:\n%#v\n", v.override) + fmt.Fprintf(w, "PFlags:\n%#v\n", v.pflags) + fmt.Fprintf(w, "Env:\n%#v\n", v.env) + fmt.Fprintf(w, "Key/Value Store:\n%#v\n", v.kvstore) + fmt.Fprintf(w, "Config:\n%#v\n", v.config) + fmt.Fprintf(w, "Defaults:\n%#v\n", v.defaults) } diff --git a/vendor/github.com/spf13/viper/watch.go b/vendor/github.com/spf13/viper/watch.go index b5523b8f..1ce84eaf 100644 --- a/vendor/github.com/spf13/viper/watch.go +++ b/vendor/github.com/spf13/viper/watch.go @@ -1,5 +1,5 @@ -//go:build !js -// +build !js +//go:build darwin || dragonfly || freebsd || openbsd || linux || netbsd || solaris || windows +// +build darwin dragonfly freebsd openbsd linux netbsd solaris windows package viper diff --git a/vendor/github.com/spf13/viper/watch_wasm.go b/vendor/github.com/spf13/viper/watch_unsupported.go index 8e47e6a9..7e271537 100644 --- a/vendor/github.com/spf13/viper/watch_wasm.go +++ b/vendor/github.com/spf13/viper/watch_unsupported.go @@ -1,13 +1,19 @@ -// +build js,wasm +//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows) +// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows package viper import ( - "errors" + "fmt" + "runtime" "github.com/fsnotify/fsnotify" ) +func newWatcher() (*watcher, error) { + return &watcher{}, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) +} + type watcher struct { Events chan fsnotify.Event Errors chan error @@ -24,7 +30,3 @@ func (*watcher) Add(name string) error { func (*watcher) Remove(name string) error { return nil } - -func newWatcher() (*watcher, error) { - return &watcher{}, errors.New("fsnotify is not supported on WASM") -} diff --git a/vendor/github.com/subosito/gotenv/.gitignore b/vendor/github.com/subosito/gotenv/.gitignore index 2b8d4561..7db37c1d 100644 --- a/vendor/github.com/subosito/gotenv/.gitignore +++ b/vendor/github.com/subosito/gotenv/.gitignore @@ -1,3 +1,4 @@ *.test *.out annotate.json +profile.cov diff --git a/vendor/github.com/subosito/gotenv/.golangci.yaml b/vendor/github.com/subosito/gotenv/.golangci.yaml new file mode 100644 index 00000000..8c82a762 --- /dev/null +++ b/vendor/github.com/subosito/gotenv/.golangci.yaml @@ -0,0 +1,7 @@ +# Options for analysis running. +run: + timeout: 1m + +linters-settings: + gofmt: + simplify: true diff --git a/vendor/github.com/subosito/gotenv/CHANGELOG.md b/vendor/github.com/subosito/gotenv/CHANGELOG.md index 24b096b9..757caad2 100644 --- a/vendor/github.com/subosito/gotenv/CHANGELOG.md +++ b/vendor/github.com/subosito/gotenv/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [1.4.0] - 2022-06-02 + +### Added + +- Add `Marshal` and `Unmarshal` helpers + +### Changed + +- The CI will now run a linter and the tests on PRs. + ## [1.3.0] - 2022-05-23 ### Added @@ -41,7 +51,7 @@ ### Added - Supports carriage return in env -- Handle files with UTF-8 BOM +- Handle files with UTF-8 BOM ### Changed diff --git a/vendor/github.com/subosito/gotenv/README.md b/vendor/github.com/subosito/gotenv/README.md index 3ce9a410..fc9616e3 100644 --- a/vendor/github.com/subosito/gotenv/README.md +++ b/vendor/github.com/subosito/gotenv/README.md @@ -28,7 +28,7 @@ Once loaded you can use `os.Getenv()` to get the value of the variable. Let's say you have `.env` file: -``` +```sh APP_ID=1234567 APP_SECRET=abcdef ``` @@ -78,7 +78,6 @@ Besides above functions, `gotenv` also provides another functions that overrides - `gotenv.OverLoad` - `gotenv.OverApply` - Here's the example of this overrides behavior: ```go diff --git a/vendor/github.com/subosito/gotenv/gotenv.go b/vendor/github.com/subosito/gotenv/gotenv.go index c4c1e50e..dc013e1e 100644 --- a/vendor/github.com/subosito/gotenv/gotenv.go +++ b/vendor/github.com/subosito/gotenv/gotenv.go @@ -3,10 +3,14 @@ package gotenv import ( "bufio" + "bytes" "fmt" "io" "os" + "path/filepath" "regexp" + "sort" + "strconv" "strings" ) @@ -24,41 +28,31 @@ const ( // Env holds key/value pair of valid environment variable type Env map[string]string -/* -Load is a function to load a file or multiple files and then export the valid variables into environment variables if they do not exist. -When it's called with no argument, it will load `.env` file on the current path and set the environment variables. -Otherwise, it will loop over the filenames parameter and set the proper environment variables. -*/ +// Load is a function to load a file or multiple files and then export the valid variables into environment variables if they do not exist. +// When it's called with no argument, it will load `.env` file on the current path and set the environment variables. +// Otherwise, it will loop over the filenames parameter and set the proper environment variables. func Load(filenames ...string) error { return loadenv(false, filenames...) } -/* -OverLoad is a function to load a file or multiple files and then export and override the valid variables into environment variables. -*/ +// OverLoad is a function to load a file or multiple files and then export and override the valid variables into environment variables. func OverLoad(filenames ...string) error { return loadenv(true, filenames...) } -/* -Must is wrapper function that will panic when supplied function returns an error. -*/ +// Must is wrapper function that will panic when supplied function returns an error. func Must(fn func(filenames ...string) error, filenames ...string) { if err := fn(filenames...); err != nil { panic(err.Error()) } } -/* -Apply is a function to load an io Reader then export the valid variables into environment variables if they do not exist. -*/ +// Apply is a function to load an io Reader then export the valid variables into environment variables if they do not exist. func Apply(r io.Reader) error { return parset(r, false) } -/* -OverApply is a function to load an io Reader then export and override the valid variables into environment variables. -*/ +// OverApply is a function to load an io Reader then export and override the valid variables into environment variables. func OverApply(r io.Reader) error { return parset(r, true) } @@ -75,11 +69,10 @@ func loadenv(override bool, filenames ...string) error { } err = parset(f, override) + f.Close() if err != nil { return err } - - f.Close() } return nil @@ -124,9 +117,94 @@ func StrictParse(r io.Reader) (Env, error) { return strictParse(r, false) } +// Read is a function to parse a file line by line and returns the valid Env key/value pair of valid variables. +// It expands the value of a variable from the environment variable but does not set the value to the environment itself. +// This function is skipping any invalid lines and only processing the valid one. +func Read(filename string) (Env, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + return strictParse(f, false) +} + +// Unmarshal reads a string line by line and returns the valid Env key/value pair of valid variables. +// It expands the value of a variable from the environment variable but does not set the value to the environment itself. +// This function is returning an error if there are any invalid lines. +func Unmarshal(str string) (Env, error) { + return strictParse(strings.NewReader(str), false) +} + +// Marshal outputs the given environment as a env file. +// Variables will be sorted by name. +func Marshal(env Env) (string, error) { + lines := make([]string, 0, len(env)) + for k, v := range env { + if d, err := strconv.Atoi(v); err == nil { + lines = append(lines, fmt.Sprintf(`%s=%d`, k, d)) + } else { + lines = append(lines, fmt.Sprintf(`%s=%q`, k, v)) + } + } + sort.Strings(lines) + return strings.Join(lines, "\n"), nil +} + +// Write serializes the given environment and writes it to a file +func Write(env Env, filename string) error { + content, err := Marshal(env) + if err != nil { + return err + } + // ensure the path exists + if err := os.MkdirAll(filepath.Dir(filename), 0o775); err != nil { + return err + } + // create or truncate the file + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + _, err = file.WriteString(content + "\n") + if err != nil { + return err + } + + return file.Sync() +} + +// splitLines is a valid SplitFunc for a bufio.Scanner. It will split lines on CR ('\r'), LF ('\n') or CRLF (any of the three sequences). +// If a CR is immediately followed by a LF, it is treated as a CRLF (one single line break). +func splitLines(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, bufio.ErrFinalToken + } + + idx := bytes.IndexAny(data, "\r\n") + switch { + case atEOF && idx < 0: + return len(data), data, bufio.ErrFinalToken + + case idx < 0: + return 0, nil, nil + } + + // consume CR or LF + eol := idx + 1 + // detect CRLF + if len(data) > eol && data[eol-1] == '\r' && data[eol] == '\n' { + eol++ + } + + return eol, data[:idx], nil +} + func strictParse(r io.Reader, override bool) (Env, error) { env := make(Env) scanner := bufio.NewScanner(r) + scanner.Split(splitLines) firstLine := true @@ -143,28 +221,34 @@ func strictParse(r io.Reader, override bool) (Env, error) { } quote := "" + // look for the delimiter character idx := strings.Index(line, "=") if idx == -1 { idx = strings.Index(line, ":") } + // look for a quote character if idx > 0 && idx < len(line)-1 { val := strings.TrimSpace(line[idx+1:]) if val[0] == '"' || val[0] == '\'' { quote = val[:1] + // look for the closing quote character within the same line idx = strings.LastIndex(strings.TrimSpace(val[1:]), quote) if idx >= 0 && val[idx] != '\\' { quote = "" } } } + // look for the closing quote character for quote != "" && scanner.Scan() { l := scanner.Text() line += "\n" + l idx := strings.LastIndex(l, quote) if idx > 0 && l[idx-1] == '\\' { + // foud a matching quote character but it's escaped continue } if idx >= 0 { + // foud a matching quote quote = "" } } @@ -195,21 +279,23 @@ func parseLine(s string, env Env, override bool) error { return checkFormat(s, env) } - key := rm[1] - val := rm[2] - - // trim whitespace - val = strings.TrimSpace(val) + key := strings.TrimSpace(rm[1]) + val := strings.TrimSpace(rm[2]) - // determine if string has quote prefix - hdq := strings.HasPrefix(val, `"`) + var hsq, hdq bool - // determine if string has single quote prefix - hsq := strings.HasPrefix(val, `'`) + // check if the value is quoted + if l := len(val); l >= 2 { + l -= 1 + // has double quotes + hdq = val[0] == '"' && val[l] == '"' + // has single quotes + hsq = val[0] == '\'' && val[l] == '\'' - // remove quotes '' or "" - if l := len(val); (hsq || hdq) && l >= 2 { - val = val[1 : l-1] + // remove quotes '' or "" + if hsq || hdq { + val = val[1:l] + } } if hdq { @@ -220,13 +306,11 @@ func parseLine(s string, env Env, override bool) error { val = unescapeRgx.ReplaceAllString(val, "$1") } - fv := func(s string) string { - return varReplacement(s, hsq, env, override) - } - if !hsq { + fv := func(s string) string { + return varReplacement(s, hsq, env, override) + } val = varRgx.ReplaceAllStringFunc(val, fv) - val = parseVal(val, env, hdq, override) } env[key] = val @@ -250,8 +334,13 @@ func parseExport(st string, env Env) error { var varNameRgx = regexp.MustCompile(`(\$)(\{?([A-Z0-9_]+)\}?)`) func varReplacement(s string, hsq bool, env Env, override bool) string { - if strings.HasPrefix(s, "\\") { - return strings.TrimPrefix(s, "\\") + if s == "" { + return s + } + + if s[0] == '\\' { + // the dollar sign is escaped + return s[1:] } if hsq { @@ -270,18 +359,17 @@ func varReplacement(s string, hsq bool, env Env, override bool) string { return replace } - replace, ok := env[v] - if !ok { - replace = os.Getenv(v) + if replace, ok := env[v]; ok { + return replace } - return replace + return os.Getenv(v) } func checkFormat(s string, env Env) error { st := strings.TrimSpace(s) - if (st == "") || strings.HasPrefix(st, "#") { + if st == "" || st[0] == '#' { return nil } @@ -291,18 +379,3 @@ func checkFormat(s string, env Env) error { return fmt.Errorf("line `%s` doesn't match format", s) } - -func parseVal(val string, env Env, ignoreNewlines bool, override bool) string { - if strings.Contains(val, "=") && !ignoreNewlines { - kv := strings.Split(val, "\r") - - if len(kv) > 1 { - val = kv[0] - for _, l := range kv[1:] { - _ = parseLine(l, env, override) - } - } - } - - return val -} diff --git a/vendor/github.com/valyala/fasttemplate/template.go b/vendor/github.com/valyala/fasttemplate/template.go index 18620013..f2d3261f 100644 --- a/vendor/github.com/valyala/fasttemplate/template.go +++ b/vendor/github.com/valyala/fasttemplate/template.go @@ -112,8 +112,7 @@ func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string { // but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString // it just returns an empty string and the error f returned func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) { - tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag)) - if tagsCount == 0 { + if n := bytes.Index(unsafeString2Bytes(template), unsafeString2Bytes(startTag)); n < 0 { return template, nil } |