Merge branch 'master' into master
This commit is contained in:
10
.github/workflows/development.yml
vendored
10
.github/workflows/development.yml
vendored
@@ -12,11 +12,11 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: "-v --new-from-rev HEAD~5"
|
||||
args: "-v --new-from-rev HEAD~5 --timeout=5m"
|
||||
test-build-upload:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
go-version: [1.22.x]
|
||||
platform: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
@@ -39,19 +39,19 @@ jobs:
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
|
||||
- name: Upload linux 64-bit
|
||||
if: startsWith(matrix.go-version,'1.20')
|
||||
if: startsWith(matrix.go-version,'1.22')
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: matterbridge-linux-64bit
|
||||
path: output/lin
|
||||
- name: Upload windows 64-bit
|
||||
if: startsWith(matrix.go-version,'1.20')
|
||||
if: startsWith(matrix.go-version,'1.22')
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: matterbridge-windows-64bit
|
||||
path: output/win
|
||||
- name: Upload darwin 64-bit
|
||||
if: startsWith(matrix.go-version,'1.20')
|
||||
if: startsWith(matrix.go-version,'1.22')
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: matterbridge-darwin-64bit
|
||||
|
||||
@@ -212,6 +212,9 @@ linters:
|
||||
- execinquery
|
||||
- nosnakecase
|
||||
- exhaustive
|
||||
- testifylint
|
||||
- mnd
|
||||
- depguard
|
||||
# rules to deal with reported isues
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
|
||||
10
README.md
10
README.md
@@ -115,19 +115,21 @@ And more...
|
||||
### 3rd party via matterbridge api
|
||||
|
||||
- [Delta Chat](https://github.com/deltachat-bot/matterdelta)
|
||||
- [Minecraft](https://github.com/raws/mattercraft)
|
||||
- [Minecraft](https://gitlab.com/Programie/MatterBukkit)
|
||||
|
||||
#### Past 3rd party projects
|
||||
- [Discourse](https://github.com/DeclanHoare/matterbabble)
|
||||
- [Facebook messenger](https://github.com/powerjungle/fbridge-asyncio)
|
||||
- [Facebook messenger](https://github.com/VictorNine/fbridge)
|
||||
- [Minecraft](https://github.com/elytra/MatterLink)
|
||||
- [Minecraft](https://github.com/raws/mattercraft)
|
||||
- [Minecraft](https://gitlab.com/Programie/MatterBukkit)
|
||||
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
|
||||
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
|
||||
- [MatterAMXX](https://github.com/GabeIggy/MatterAMXX)
|
||||
- [MatterAMXX](https://github.com/andrewlindberg/MatterAMXX): [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
|
||||
- [Vintage Story](https://github.com/NikkyAI/vs-matterbridge)
|
||||
- [Ultima Online Emulator](https://github.com/kuoushi/ServUO-Matterbridge)
|
||||
- [Teamspeak](https://github.com/Archeb/ts-matterbridge)
|
||||
|
||||
|
||||
### API
|
||||
|
||||
The API is basic at the moment.
|
||||
|
||||
@@ -121,6 +121,7 @@ type Protocol struct {
|
||||
MessageLength int // IRC, max length of a message allowed
|
||||
MessageQueue int // IRC, size of message queue for flood control
|
||||
MessageSplit bool // IRC, split long messages with newlines on MessageLength instead of clipping
|
||||
MessageSplitMaxCount int // discord, split long messages into at most this many messages instead of clipping (MessageLength=1950 cannot be configured)
|
||||
Muc string // xmpp
|
||||
MxID string // matrix
|
||||
Name string // all protocols
|
||||
|
||||
@@ -90,7 +90,7 @@ func (b *Bdiscord) Connect() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guilds, err := b.c.UserGuilds(100, "", "")
|
||||
guilds, err := b.c.UserGuilds(100, "", "", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -316,6 +316,7 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
||||
// Upload a file if it exists
|
||||
if msg.Extra != nil {
|
||||
for _, rmsg := range helper.HandleExtra(msg, b.General) {
|
||||
// TODO: Use ClipOrSplitMessage
|
||||
rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength, b.GetString("MessageClipped"))
|
||||
if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil {
|
||||
b.Log.Errorf("Could not send message %#v: %s", rmsg, err)
|
||||
@@ -327,35 +328,53 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
||||
}
|
||||
}
|
||||
|
||||
msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped"))
|
||||
msg.Text = b.replaceUserMentions(msg.Text)
|
||||
|
||||
// Edit message
|
||||
if msg.ID != "" {
|
||||
_, err := b.c.ChannelMessageEdit(channelID, msg.ID, msg.Username+msg.Text)
|
||||
return msg.ID, err
|
||||
}
|
||||
|
||||
m := discordgo.MessageSend{
|
||||
Content: msg.Username + msg.Text,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
}
|
||||
|
||||
if msg.ParentValid() {
|
||||
m.Reference = &discordgo.MessageReference{
|
||||
MessageID: msg.ParentID,
|
||||
ChannelID: channelID,
|
||||
GuildID: b.guildID,
|
||||
// Exploit that a discord message ID is actually just a large number, and we encode a list of IDs by separating them with ";".
|
||||
msgIds := strings.Split(msg.ID, ";")
|
||||
msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), len(msgIds))
|
||||
for len(msgParts) < len(msgIds) {
|
||||
msgParts = append(msgParts, "((obsoleted by edit))")
|
||||
}
|
||||
for i := range msgParts {
|
||||
// In case of split-messages where some parts remain the same (i.e. only a typo-fix in a huge message), this causes some noop-updates.
|
||||
// TODO: Optimize away noop-updates of un-edited messages
|
||||
// TODO: Use RemoteNickFormat instead of this broken concatenation
|
||||
_, err := b.c.ChannelMessageEdit(channelID, msgIds[i], msg.Username+msgParts[i])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return msg.ID, nil
|
||||
}
|
||||
|
||||
// Post normal message
|
||||
res, err := b.c.ChannelMessageSendComplex(channelID, &m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), b.GetInt("MessageSplitMaxCount"))
|
||||
msgIds := []string{}
|
||||
|
||||
for _, msgPart := range msgParts {
|
||||
m := discordgo.MessageSend{
|
||||
Content: msg.Username + msgPart,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
}
|
||||
|
||||
if msg.ParentValid() {
|
||||
m.Reference = &discordgo.MessageReference{
|
||||
MessageID: msg.ParentID,
|
||||
ChannelID: channelID,
|
||||
GuildID: b.guildID,
|
||||
}
|
||||
}
|
||||
|
||||
// Post normal message
|
||||
res, err := b.c.ChannelMessageSendComplex(channelID, &m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
msgIds = append(msgIds, res.ID)
|
||||
}
|
||||
|
||||
return res.ID, nil
|
||||
// Exploit that a discord message ID is actually just a large number, so we encode a list of IDs by separating them with ";".
|
||||
return strings.Join(msgIds, ";"), nil
|
||||
}
|
||||
|
||||
// handleUploadFile handles native upload of files
|
||||
|
||||
@@ -68,7 +68,7 @@ func (b *Bdiscord) getGuildMemberByNick(nick string) (*discordgo.Member, error)
|
||||
b.membersMutex.RLock()
|
||||
defer b.membersMutex.RUnlock()
|
||||
|
||||
if member, ok := b.nickMemberMap[nick]; ok {
|
||||
if member, ok := b.nickMemberMap[strings.TrimSpace(nick)]; ok {
|
||||
return member, nil
|
||||
}
|
||||
return nil, errors.New("Couldn't find guild member with nick " + nick) // This will most likely get ignored by the caller
|
||||
|
||||
@@ -2,6 +2,7 @@ package bdiscord
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
@@ -42,14 +43,66 @@ func (b *Bdiscord) maybeGetLocalAvatar(msg *config.Message) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Bdiscord) webhookSendTextOnly(msg *config.Message, channelID string) (string, error) {
|
||||
msgParts := helper.ClipOrSplitMessage(msg.Text, MessageLength, b.GetString("MessageClipped"), b.GetInt("MessageSplitMaxCount"))
|
||||
msgIds := []string{}
|
||||
for _, msgPart := range msgParts {
|
||||
res, err := b.transmitter.Send(
|
||||
channelID,
|
||||
&discordgo.WebhookParams{
|
||||
Content: msgPart,
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
msgIds = append(msgIds, res.ID)
|
||||
}
|
||||
}
|
||||
// Exploit that a discord message ID is actually just a large number, so we encode a list of IDs by separating them with ";".
|
||||
return strings.Join(msgIds, ";"), nil
|
||||
}
|
||||
|
||||
func (b *Bdiscord) webhookSendFilesOnly(msg *config.Message, channelID string) error {
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fi := f.(config.FileInfo) //nolint:forcetypeassert
|
||||
file := discordgo.File{
|
||||
Name: fi.Name,
|
||||
ContentType: "",
|
||||
Reader: bytes.NewReader(*fi.Data),
|
||||
}
|
||||
content := fi.Comment
|
||||
|
||||
// Cannot use the resulting ID for any edits anyway, so throw it away.
|
||||
// This has to be re-enabled when we implement message deletion.
|
||||
_, err := b.transmitter.Send(
|
||||
channelID,
|
||||
&discordgo.WebhookParams{
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
Files: []*discordgo.File{&file},
|
||||
Content: content,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// webhookSend send one or more message via webhook, taking care of file
|
||||
// uploads (from slack, telegram or mattermost).
|
||||
// Returns messageID and error.
|
||||
func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordgo.Message, error) {
|
||||
func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (string, error) {
|
||||
var (
|
||||
res *discordgo.Message
|
||||
res2 *discordgo.Message
|
||||
err error
|
||||
res string
|
||||
err error
|
||||
)
|
||||
|
||||
// If avatar is unset, mutate the message to include the local avatar (but only if settings say we should do this)
|
||||
@@ -61,48 +114,11 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg
|
||||
|
||||
// We can't send empty messages.
|
||||
if msg.Text != "" {
|
||||
res, err = b.transmitter.Send(
|
||||
channelID,
|
||||
&discordgo.WebhookParams{
|
||||
Content: msg.Text,
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Could not send text (%s) for message %#v: %s", msg.Text, msg, err)
|
||||
}
|
||||
res, err = b.webhookSendTextOnly(msg, channelID)
|
||||
}
|
||||
|
||||
if msg.Extra != nil {
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fi := f.(config.FileInfo)
|
||||
file := discordgo.File{
|
||||
Name: fi.Name,
|
||||
ContentType: "",
|
||||
Reader: bytes.NewReader(*fi.Data),
|
||||
}
|
||||
content := fi.Comment
|
||||
|
||||
res2, err = b.transmitter.Send(
|
||||
channelID,
|
||||
&discordgo.WebhookParams{
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
Files: []*discordgo.File{&file},
|
||||
Content: content,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if msg.Text == "" {
|
||||
res = res2
|
||||
if err == nil && msg.Extra != nil {
|
||||
err = b.webhookSendFilesOnly(msg, channelID)
|
||||
}
|
||||
|
||||
return res, err
|
||||
@@ -120,35 +136,44 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st
|
||||
return "", nil
|
||||
}
|
||||
|
||||
msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped"))
|
||||
msg.Text = b.replaceUserMentions(msg.Text)
|
||||
// discord username must be [0..32] max
|
||||
if len(msg.Username) > 32 {
|
||||
msg.Username = msg.Username[0:32]
|
||||
}
|
||||
|
||||
if msg.ID != "" {
|
||||
// Exploit that a discord message ID is actually just a large number, and we encode a list of IDs by separating them with ";".
|
||||
msgIds := strings.Split(msg.ID, ";")
|
||||
msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), len(msgIds))
|
||||
for len(msgParts) < len(msgIds) {
|
||||
msgParts = append(msgParts, "((obsoleted by edit))")
|
||||
}
|
||||
b.Log.Debugf("Editing webhook message")
|
||||
err := b.transmitter.Edit(channelID, msg.ID, &discordgo.WebhookParams{
|
||||
Content: msg.Text,
|
||||
Username: msg.Username,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
})
|
||||
if err == nil {
|
||||
var editErr error = nil
|
||||
for i := range msgParts {
|
||||
// In case of split-messages where some parts remain the same (i.e. only a typo-fix in a huge message), this causes some noop-updates.
|
||||
// TODO: Optimize away noop-updates of un-edited messages
|
||||
editErr = b.transmitter.Edit(channelID, msgIds[i], &discordgo.WebhookParams{
|
||||
Content: msgParts[i],
|
||||
Username: msg.Username,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
})
|
||||
if editErr != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if editErr == nil {
|
||||
return msg.ID, nil
|
||||
}
|
||||
b.Log.Errorf("Could not edit webhook message: %s", err)
|
||||
b.Log.Errorf("Could not edit webhook message(s): %s; sending as new message(s) instead", editErr)
|
||||
}
|
||||
|
||||
b.Log.Debugf("Processing webhook sending for message %#v", msg)
|
||||
discordMsg, err := b.webhookSend(msg, channelID)
|
||||
msg.Text = b.replaceUserMentions(msg.Text)
|
||||
msgID, err := b.webhookSend(msg, channelID)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msg, err)
|
||||
b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msgID, err)
|
||||
return "", err
|
||||
}
|
||||
if discordMsg == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return discordMsg.ID, nil
|
||||
return msgID, nil
|
||||
}
|
||||
|
||||
@@ -211,14 +211,51 @@ func ClipMessage(text string, length int, clippingMessage string) string {
|
||||
|
||||
if len(text) > length {
|
||||
text = text[:length-len(clippingMessage)]
|
||||
if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
|
||||
text = text[:len(text)-size]
|
||||
for len(text) > 0 {
|
||||
if r, _ := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
|
||||
text = text[:len(text)-1]
|
||||
// Note: DecodeLastRuneInString only returns the constant value "1" in
|
||||
// case of an error. We do not yet know whether the last rune is now
|
||||
// actually valid. Example: "€" is 0xE2 0x82 0xAC. If we happen to split
|
||||
// the string just before 0xAC, and go back only one byte, that would
|
||||
// leave us with a string that ends in the byte 0xE2, which is not a valid
|
||||
// rune, so we need to try again.
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
text += clippingMessage
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func ClipOrSplitMessage(text string, length int, clippingMessage string, splitMax int) []string {
|
||||
var msgParts []string
|
||||
remainingText := text
|
||||
// Invariant of this splitting loop: No text is lost (msgParts+remainingText is the original text),
|
||||
// and all parts is guaranteed to satisfy the length requirement.
|
||||
for len(msgParts) < splitMax-1 && len(remainingText) > length {
|
||||
// Decision: The text needs to be split (again).
|
||||
var chunk string
|
||||
wasted := 0
|
||||
// The longest UTF-8 encoding of a valid rune is 4 bytes (0xF4 0x8F 0xBF 0xBF, encoding U+10FFFF),
|
||||
// so we should never need to waste 4 or more bytes at a time.
|
||||
for wasted < 4 && wasted < length {
|
||||
chunk = remainingText[:length-wasted]
|
||||
if r, _ := utf8.DecodeLastRuneInString(chunk); r == utf8.RuneError {
|
||||
wasted += 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Note: At this point, "chunk" might still be invalid, if "text" is very broken.
|
||||
msgParts = append(msgParts, chunk)
|
||||
remainingText = remainingText[len(chunk):]
|
||||
}
|
||||
msgParts = append(msgParts, ClipMessage(remainingText, length, clippingMessage))
|
||||
return msgParts
|
||||
}
|
||||
|
||||
// ParseMarkdown takes in an input string as markdown and parses it to html
|
||||
func ParseMarkdown(input string) string {
|
||||
extensions := parser.HardLineBreak | parser.NoIntraEmphasis | parser.FencedCode
|
||||
|
||||
@@ -88,6 +88,15 @@ var lineSplittingTestCases = map[string]struct {
|
||||
},
|
||||
nonSplitOutput: []string{"不布人個我此而及單石業喜資富下我河下日沒一我臺空達的常景便物沒為……子大我別名解成?生賣的全直黑,我自我結毛分洲了世當,是政福那是東;斯說"},
|
||||
},
|
||||
"Long message, clip three-byte rune after two bytes": {
|
||||
input: "x 人人生而自由,在尊嚴和權利上一律平等。 他們都具有理性和良知,應該以兄弟情誼的精神對待彼此。",
|
||||
splitOutput: []string{
|
||||
"x 人人生而自由,在尊嚴和權利上 <clipped message>",
|
||||
"一律平等。 他們都具有理性和良知 <clipped message>",
|
||||
",應該以兄弟情誼的精神對待彼此。",
|
||||
},
|
||||
nonSplitOutput: []string{"x 人人生而自由,在尊嚴和權利上一律平等。 他們都具有理性和良知,應該以兄弟情誼的精神對待彼此。"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestGetSubLines(t *testing.T) {
|
||||
@@ -125,3 +134,105 @@ func TestConvertWebPToPNG(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
var clippingOrSplittingTestCases = map[string]struct {
|
||||
inputText string
|
||||
clipSplitLength int
|
||||
clippingMessage string
|
||||
splitMax int
|
||||
expectedOutput []string
|
||||
}{
|
||||
"Short single-line message, split 3": {
|
||||
inputText: "short",
|
||||
clipSplitLength: 20,
|
||||
clippingMessage: "?!?!",
|
||||
splitMax: 3,
|
||||
expectedOutput: []string{"short"},
|
||||
},
|
||||
"Short single-line message, split 1": {
|
||||
inputText: "short",
|
||||
clipSplitLength: 20,
|
||||
clippingMessage: "?!?!",
|
||||
splitMax: 1,
|
||||
expectedOutput: []string{"short"},
|
||||
},
|
||||
"Short single-line message, split 0": {
|
||||
// Mainly check that we don't crash.
|
||||
inputText: "short",
|
||||
clipSplitLength: 20,
|
||||
clippingMessage: "?!?!",
|
||||
splitMax: 0,
|
||||
expectedOutput: []string{"short"},
|
||||
},
|
||||
"Long single-line message, noclip": {
|
||||
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
clipSplitLength: 50,
|
||||
clippingMessage: "?!?!",
|
||||
splitMax: 10,
|
||||
expectedOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing",
|
||||
" elit, sed do eiusmod tempor incididunt ut labore ",
|
||||
"et dolore magna aliqua.",
|
||||
},
|
||||
},
|
||||
"Long single-line message, noclip tight": {
|
||||
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
clipSplitLength: 50,
|
||||
clippingMessage: "?!?!",
|
||||
splitMax: 3,
|
||||
expectedOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing",
|
||||
" elit, sed do eiusmod tempor incididunt ut labore ",
|
||||
"et dolore magna aliqua.",
|
||||
},
|
||||
},
|
||||
"Long single-line message, clip custom": {
|
||||
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
clipSplitLength: 50,
|
||||
clippingMessage: "?!?!",
|
||||
splitMax: 2,
|
||||
expectedOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing",
|
||||
" elit, sed do eiusmod tempor incididunt ut lab?!?!",
|
||||
},
|
||||
},
|
||||
"Long single-line message, clip built-in": {
|
||||
inputText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
clipSplitLength: 50,
|
||||
clippingMessage: "",
|
||||
splitMax: 2,
|
||||
expectedOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing",
|
||||
" elit, sed do eiusmod tempor inc <clipped message>",
|
||||
},
|
||||
},
|
||||
"Short multi-line message": {
|
||||
inputText: "I\ncan't\nget\nno\nsatisfaction!",
|
||||
clipSplitLength: 50,
|
||||
clippingMessage: "",
|
||||
splitMax: 2,
|
||||
expectedOutput: []string{"I\ncan't\nget\nno\nsatisfaction!"},
|
||||
},
|
||||
"Long message containing UTF-8 multi-byte runes": {
|
||||
inputText: "人人生而自由,在尊嚴和權利上一律平等。 他們都具有理性和良知,應該以兄弟情誼的精神對待彼此。",
|
||||
clipSplitLength: 50,
|
||||
clippingMessage: "",
|
||||
splitMax: 10,
|
||||
expectedOutput: []string{
|
||||
"人人生而自由,在尊嚴和權利上一律", // Note: only 48 bytes!
|
||||
"平等。 他們都具有理性和良知,應該", // Note: only 49 bytes!
|
||||
"以兄弟情誼的精神對待彼此。",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestClipOrSplitMessage(t *testing.T) {
|
||||
for testname, testcase := range clippingOrSplittingTestCases {
|
||||
actualOutput := ClipOrSplitMessage(testcase.inputText, testcase.clipSplitLength, testcase.clippingMessage, testcase.splitMax)
|
||||
assert.Equalf(t, testcase.expectedOutput, actualOutput, "'%s' testcase should give expected lines with clipping+splitting.", testname)
|
||||
for _, splitLine := range testcase.expectedOutput {
|
||||
byteLength := len([]byte(splitLine))
|
||||
assert.True(t, byteLength <= testcase.clipSplitLength, "Splitted line '%s' of testcase '%s' should not exceed the maximum byte-length (%d vs. %d).", splitLine, testname, testcase.clipSplitLength, byteLength)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,18 @@ func (b *Birc) handleNewConnection(client *girc.Client, event girc.Event) {
|
||||
i := b.i
|
||||
b.Nick = event.Params[0]
|
||||
|
||||
b.Log.Debug("Clearing handlers before adding in case of BNC reconnect")
|
||||
i.Handlers.Clear("PRIVMSG")
|
||||
i.Handlers.Clear("CTCP_ACTION")
|
||||
i.Handlers.Clear(girc.RPL_TOPICWHOTIME)
|
||||
i.Handlers.Clear(girc.NOTICE)
|
||||
i.Handlers.Clear("JOIN")
|
||||
i.Handlers.Clear("PART")
|
||||
i.Handlers.Clear("QUIT")
|
||||
i.Handlers.Clear("KICK")
|
||||
i.Handlers.Clear("INVITE")
|
||||
|
||||
i.Handlers.AddBg("PRIVMSG", b.handlePrivMsg)
|
||||
i.Handlers.AddBg("CTCP_ACTION", b.handlePrivMsg)
|
||||
i.Handlers.Add(girc.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
|
||||
i.Handlers.AddBg(girc.NOTICE, b.handleNotice)
|
||||
i.Handlers.AddBg("JOIN", b.handleJoinPart)
|
||||
@@ -195,7 +205,11 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
||||
b.Log.Debugf("== Receiving PRIVMSG: %s %s %#v", event.Source.Name, event.Last(), event)
|
||||
|
||||
// set action event
|
||||
if event.IsAction() {
|
||||
if ok, ctcp := event.IsCTCP(); ok {
|
||||
if ctcp.Command != girc.CTCP_ACTION {
|
||||
b.Log.Debugf("dropping user ctcp, command: %s", ctcp.Command)
|
||||
return
|
||||
}
|
||||
rmsg.Event = config.EventUserAction
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package bmattermost
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
"github.com/matterbridge/matterclient"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
// handleDownloadAvatar downloads the avatar of userid from channel
|
||||
@@ -25,7 +27,7 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
data, _, err = b.mc.Client.GetProfileImage(userid, "")
|
||||
data, _, err = b.mc.Client.GetProfileImage(context.TODO(), userid, "")
|
||||
if err != nil {
|
||||
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
|
||||
return
|
||||
@@ -43,8 +45,8 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
||||
|
||||
//nolint:wrapcheck
|
||||
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
|
||||
url, _, _ := b.mc.Client.GetFileLink(id)
|
||||
finfo, _, err := b.mc.Client.GetFileInfo(id)
|
||||
url, _, _ := b.mc.Client.GetFileLink(context.TODO(), id)
|
||||
finfo, _, err := b.mc.Client.GetFileInfo(context.TODO(), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -52,7 +54,7 @@ func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, _, err := b.mc.Client.DownloadFile(id, true)
|
||||
data, _, err := b.mc.Client.DownloadFile(context.TODO(), id, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
"github.com/42wim/matterbridge/matterhook"
|
||||
"github.com/matterbridge/matterclient"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
func (b *Bmattermost) doConnectWebhookBind() error {
|
||||
@@ -171,12 +171,23 @@ func (b *Bmattermost) sendWebhook(msg config.Message) (string, error) {
|
||||
}
|
||||
|
||||
// skipMessages returns true if this message should not be handled
|
||||
//
|
||||
//nolint:gocyclo,cyclop
|
||||
func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
||||
// Handle join/leave
|
||||
if message.Type == "system_join_leave" ||
|
||||
message.Type == "system_join_channel" ||
|
||||
message.Type == "system_leave_channel" {
|
||||
skipJoinMessageTypes := map[string]struct{}{
|
||||
"system_join_leave": {}, // deprecated for system_add_to_channel
|
||||
"system_leave_channel": {}, // deprecated for system_remove_from_channel
|
||||
"system_join_channel": {},
|
||||
"system_add_to_channel": {},
|
||||
"system_remove_from_channel": {},
|
||||
"system_add_to_team": {},
|
||||
"system_remove_from_team": {},
|
||||
}
|
||||
|
||||
// dirty hack to efficiently check if this element is in the map without writing a contains func
|
||||
// can be replaced with native slice.contains with go 1.21
|
||||
if _, ok := skipJoinMessageTypes[message.Type]; ok {
|
||||
if b.GetBool("nosendjoinpart") {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package bmattermost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -157,7 +158,7 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
||||
|
||||
// we only can reply to the root of the thread, not to a specific ID (like discord for example does)
|
||||
if msg.ParentID != "" {
|
||||
post, _, err := b.mc.Client.GetPost(msg.ParentID, "")
|
||||
post, _, err := b.mc.Client.GetPost(context.TODO(), msg.ParentID, "")
|
||||
if err != nil {
|
||||
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ func (b *Brocketchat) uploadFile(fi *config.FileInfo, channel string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -101,7 +101,9 @@ func (b *Bslack) populateMessageWithBotInfo(ev *slack.MessageEvent, rmsg *config
|
||||
var err error
|
||||
var bot *slack.Bot
|
||||
for {
|
||||
bot, err = b.rtm.GetBotInfo(ev.BotID)
|
||||
bot, err = b.rtm.GetBotInfo(slack.GetBotInfoParameters{
|
||||
Bot: ev.BotID,
|
||||
})
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ func isGroupJid(identifier string) bool {
|
||||
func (b *Bwhatsapp) getDevice() (*store.Device, error) {
|
||||
device := &store.Device{}
|
||||
|
||||
storeContainer, err := sqlstore.New("sqlite", "file:"+b.Config.GetString("sessionfile")+".db?_foreign_keys=on&_pragma=busy_timeout=10000", nil)
|
||||
storeContainer, err := sqlstore.New("sqlite", "file:"+b.Config.GetString("sessionfile")+".db?_pragma=foreign_keys(1)&_pragma=busy_timeout=10000", nil)
|
||||
if err != nil {
|
||||
return device, fmt.Errorf("failed to connect to database: %v", err)
|
||||
}
|
||||
@@ -151,7 +151,6 @@ func (b *Bwhatsapp) getDevice() (*store.Device, error) {
|
||||
|
||||
func (b *Bwhatsapp) getNewReplyContext(parentID string) (*proto.ContextInfo, error) {
|
||||
replyInfo, err := b.parseMessageID(parentID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
154
go.mod
154
go.mod
@@ -5,29 +5,29 @@ require (
|
||||
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
|
||||
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
|
||||
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
|
||||
github.com/SevereCloud/vksdk/v2 v2.16.0
|
||||
github.com/bwmarrin/discordgo v0.27.1
|
||||
github.com/d5/tengo/v2 v2.16.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12
|
||||
github.com/SevereCloud/vksdk/v2 v2.16.1
|
||||
github.com/bwmarrin/discordgo v0.28.1
|
||||
github.com/d5/tengo/v2 v2.17.0
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2
|
||||
github.com/google/gops v0.3.27
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/gorilla/schema v1.3.0
|
||||
github.com/harmony-development/shibshib v0.0.0-20220101224523-c98059d09cfa
|
||||
github.com/hashicorp/golang-lru v0.6.0
|
||||
github.com/hashicorp/golang-lru v1.0.2
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20221220212439-e48d9abd2c20
|
||||
github.com/kyokomi/emoji/v2 v2.2.12
|
||||
github.com/labstack/echo/v4 v4.11.1
|
||||
github.com/lrstanley/girc v0.0.0-20230729130341-dd5853a5f1a6
|
||||
github.com/kyokomi/emoji/v2 v2.2.13
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/lrstanley/girc v0.0.0-20240519163535-a518c5b87a79
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20240523230155-7154bfeb76e8
|
||||
github.com/matterbridge/gomatrix v0.0.0-20220411225302-271e5088ea27
|
||||
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
|
||||
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
|
||||
github.com/matterbridge/matterclient v0.0.0-20230329213635-bc6e42a4a84a
|
||||
github.com/matterbridge/matterclient v0.0.0-20240523235056-57f299489168
|
||||
github.com/matterbridge/telegram-bot-api/v6 v6.5.0
|
||||
github.com/mattermost/mattermost-server/v6 v6.7.2
|
||||
github.com/mattermost/mattermost/server/public v0.1.3
|
||||
github.com/mattn/godown v0.0.1
|
||||
github.com/mdp/qrterminal v1.0.1
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
@@ -39,21 +39,21 @@ require (
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
|
||||
github.com/shazow/ssh-chat v1.10.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/slack-go/slack v0.12.2
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/slack-go/slack v0.13.0
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/vincent-petithory/dataurl v1.0.0
|
||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
||||
github.com/yaegashi/msgraph.go v0.1.4
|
||||
github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289
|
||||
go.mau.fi/whatsmeow v0.0.0-20230805111647-405414b9b5c0
|
||||
golang.org/x/image v0.11.0
|
||||
golang.org/x/oauth2 v0.11.0
|
||||
golang.org/x/text v0.12.0
|
||||
go.mau.fi/whatsmeow v0.0.0-20240520180327-81f8f07f1dfb
|
||||
golang.org/x/image v0.16.0
|
||||
golang.org/x/oauth2 v0.20.0
|
||||
golang.org/x/text v0.15.0
|
||||
gomod.garykim.dev/nc-talk v0.3.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14
|
||||
modernc.org/sqlite v1.25.0
|
||||
modernc.org/sqlite v1.29.10
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -62,90 +62,92 @@ require (
|
||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||
github.com/apex/log v1.9.0 // indirect
|
||||
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 // indirect
|
||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gopackage/ddp v0.0.3 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/graph-gophers/graphql-go v1.3.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
||||
github.com/klauspost/compress v1.16.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
|
||||
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
||||
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 // indirect
|
||||
github.com/mattermost/logr/v2 v2.0.21 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.24 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/monaco-io/request v1.0.5 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rickb777/date v1.12.4 // indirect
|
||||
github.com/rickb777/plural v1.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rs/zerolog v1.32.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
|
||||
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.9 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wiggin77/merror v1.0.3 // indirect
|
||||
github.com/wiggin77/merror v1.0.5 // indirect
|
||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||
go.mau.fi/libsignal v0.1.0 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
go.mau.fi/util v0.4.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/term v0.20.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/grpc v1.62.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.24.1 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.6.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.49.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
rsc.io/qr v0.2.0 // indirect
|
||||
)
|
||||
|
||||
//replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
|
||||
|
||||
go 1.19
|
||||
go 1.22.0
|
||||
|
||||
@@ -163,7 +163,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -301,7 +301,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#Nicks you want to replace.
|
||||
#See ReplaceMessages for syntaxA
|
||||
#See ReplaceMessages for syntax
|
||||
#OPTIONAL (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -468,7 +468,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -586,7 +586,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -725,7 +725,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -925,10 +925,17 @@ ShowTopicChange=false
|
||||
# Supported from the following bridges: slack
|
||||
SyncTopic=false
|
||||
|
||||
#Message to show when a message is too big
|
||||
#Default "<clipped message>"
|
||||
# Message to show when a message is too big
|
||||
# Default "<clipped message>"
|
||||
MessageClipped="<clipped message>"
|
||||
|
||||
# Before clipping, try to split messages into at most this many parts. 0 is treated like 1.
|
||||
# Be careful with large numbers, as this might cause flooding.
|
||||
# Example: A maximum telegram message of 4096 bytes is received. This requires 3 Discord
|
||||
# messages (each capped at a hardcoded 1950 bytes).
|
||||
# Default 1
|
||||
MessageSplitMaxCount=3
|
||||
|
||||
###################################################################
|
||||
#telegram section
|
||||
###################################################################
|
||||
@@ -1033,7 +1040,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -1174,7 +1181,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -1286,7 +1293,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -1388,7 +1395,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
@@ -1597,7 +1604,7 @@ IgnoreMessages="^~~ badword"
|
||||
ReplaceMessages=[ ["cat","dog"] ]
|
||||
|
||||
#nicks you want to replace.
|
||||
#see replacemessages for syntaxa
|
||||
#see replacemessages for syntax
|
||||
#optional (default empty)
|
||||
ReplaceNicks=[ ["user--","user"] ]
|
||||
|
||||
|
||||
2
vendor/github.com/SevereCloud/vksdk/v2/doc.go
generated
vendored
2
vendor/github.com/SevereCloud/vksdk/v2/doc.go
generated
vendored
@@ -7,6 +7,6 @@ package vksdk
|
||||
|
||||
// Module constants.
|
||||
const (
|
||||
Version = "2.16.0"
|
||||
Version = "2.16.1"
|
||||
API = "5.131"
|
||||
)
|
||||
|
||||
2
vendor/github.com/SevereCloud/vksdk/v2/object/board.go
generated
vendored
2
vendor/github.com/SevereCloud/vksdk/v2/object/board.go
generated
vendored
@@ -36,5 +36,5 @@ type BoardTopicPoll struct {
|
||||
OwnerID int `json:"owner_id"` // Poll owner's ID
|
||||
PollID int `json:"poll_id"` // Poll ID
|
||||
Question string `json:"question"` // Poll question
|
||||
Votes string `json:"votes"` // Votes number
|
||||
Votes int `json:"votes"` // Votes number
|
||||
}
|
||||
|
||||
4
vendor/github.com/SevereCloud/vksdk/v2/object/object.go
generated
vendored
4
vendor/github.com/SevereCloud/vksdk/v2/object/object.go
generated
vendored
@@ -285,8 +285,8 @@ type BaseLinkProduct struct {
|
||||
|
||||
// BaseLinkRating struct.
|
||||
type BaseLinkRating struct {
|
||||
ReviewsCount int `json:"reviews_count"`
|
||||
Stars float64 `json:"stars"`
|
||||
ReviewsCount json.Number `json:"reviews_count"`
|
||||
Stars float64 `json:"stars"`
|
||||
}
|
||||
|
||||
// BasePlace struct.
|
||||
|
||||
21
vendor/github.com/blang/semver/.travis.yml
generated
vendored
21
vendor/github.com/blang/semver/.travis.yml
generated
vendored
@@ -1,21 +0,0 @@
|
||||
language: go
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.4.3
|
||||
- go: 1.5.4
|
||||
- go: 1.6.3
|
||||
- go: 1.7
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- go: tip
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
script:
|
||||
- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci
|
||||
-repotoken $COVERALLS_TOKEN
|
||||
- echo "Build examples" ; cd examples && go build
|
||||
- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .)
|
||||
env:
|
||||
global:
|
||||
secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw=
|
||||
194
vendor/github.com/blang/semver/README.md
generated
vendored
194
vendor/github.com/blang/semver/README.md
generated
vendored
@@ -1,194 +0,0 @@
|
||||
semver for golang [](https://travis-ci.org/blang/semver) [](https://godoc.org/github.com/blang/semver) [](https://coveralls.io/r/blang/semver?branch=master)
|
||||
======
|
||||
|
||||
semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`.
|
||||
|
||||
Usage
|
||||
-----
|
||||
```bash
|
||||
$ go get github.com/blang/semver
|
||||
```
|
||||
Note: Always vendor your dependencies or fix on a specific version tag.
|
||||
|
||||
```go
|
||||
import github.com/blang/semver
|
||||
v1, err := semver.Make("1.0.0-beta")
|
||||
v2, err := semver.Make("2.0.0-beta")
|
||||
v1.Compare(v2)
|
||||
```
|
||||
|
||||
Also check the [GoDocs](http://godoc.org/github.com/blang/semver).
|
||||
|
||||
Why should I use this lib?
|
||||
-----
|
||||
|
||||
- Fully spec compatible
|
||||
- No reflection
|
||||
- No regex
|
||||
- Fully tested (Coverage >99%)
|
||||
- Readable parsing/validation errors
|
||||
- Fast (See [Benchmarks](#benchmarks))
|
||||
- Only Stdlib
|
||||
- Uses values instead of pointers
|
||||
- Many features, see below
|
||||
|
||||
|
||||
Features
|
||||
-----
|
||||
|
||||
- Parsing and validation at all levels
|
||||
- Comparator-like comparisons
|
||||
- Compare Helper Methods
|
||||
- InPlace manipulation
|
||||
- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1`
|
||||
- Wildcards `>=1.x`, `<=2.5.x`
|
||||
- Sortable (implements sort.Interface)
|
||||
- database/sql compatible (sql.Scanner/Valuer)
|
||||
- encoding/json compatible (json.Marshaler/Unmarshaler)
|
||||
|
||||
Ranges
|
||||
------
|
||||
|
||||
A `Range` is a set of conditions which specify which versions satisfy the range.
|
||||
|
||||
A condition is composed of an operator and a version. The supported operators are:
|
||||
|
||||
- `<1.0.0` Less than `1.0.0`
|
||||
- `<=1.0.0` Less than or equal to `1.0.0`
|
||||
- `>1.0.0` Greater than `1.0.0`
|
||||
- `>=1.0.0` Greater than or equal to `1.0.0`
|
||||
- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0`
|
||||
- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`.
|
||||
|
||||
Note that spaces between the operator and the version will be gracefully tolerated.
|
||||
|
||||
A `Range` can link multiple `Ranges` separated by space:
|
||||
|
||||
Ranges can be linked by logical AND:
|
||||
|
||||
- `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0`
|
||||
- `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2`
|
||||
|
||||
Ranges can also be linked by logical OR:
|
||||
|
||||
- `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x`
|
||||
|
||||
AND has a higher precedence than OR. It's not possible to use brackets.
|
||||
|
||||
Ranges can be combined by both AND and OR
|
||||
|
||||
- `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
|
||||
|
||||
Range usage:
|
||||
|
||||
```
|
||||
v, err := semver.Parse("1.2.3")
|
||||
range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0")
|
||||
if range(v) {
|
||||
//valid
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Example
|
||||
-----
|
||||
|
||||
Have a look at full examples in [examples/main.go](examples/main.go)
|
||||
|
||||
```go
|
||||
import github.com/blang/semver
|
||||
|
||||
v, err := semver.Make("0.0.1-alpha.preview+123.github")
|
||||
fmt.Printf("Major: %d\n", v.Major)
|
||||
fmt.Printf("Minor: %d\n", v.Minor)
|
||||
fmt.Printf("Patch: %d\n", v.Patch)
|
||||
fmt.Printf("Pre: %s\n", v.Pre)
|
||||
fmt.Printf("Build: %s\n", v.Build)
|
||||
|
||||
// Prerelease versions array
|
||||
if len(v.Pre) > 0 {
|
||||
fmt.Println("Prerelease versions:")
|
||||
for i, pre := range v.Pre {
|
||||
fmt.Printf("%d: %q\n", i, pre)
|
||||
}
|
||||
}
|
||||
|
||||
// Build meta data array
|
||||
if len(v.Build) > 0 {
|
||||
fmt.Println("Build meta data:")
|
||||
for i, build := range v.Build {
|
||||
fmt.Printf("%d: %q\n", i, build)
|
||||
}
|
||||
}
|
||||
|
||||
v001, err := semver.Make("0.0.1")
|
||||
// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE
|
||||
v001.GT(v) == true
|
||||
v.LT(v001) == true
|
||||
v.GTE(v) == true
|
||||
v.LTE(v) == true
|
||||
|
||||
// Or use v.Compare(v2) for comparisons (-1, 0, 1):
|
||||
v001.Compare(v) == 1
|
||||
v.Compare(v001) == -1
|
||||
v.Compare(v) == 0
|
||||
|
||||
// Manipulate Version in place:
|
||||
v.Pre[0], err = semver.NewPRVersion("beta")
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing pre release version: %q", err)
|
||||
}
|
||||
|
||||
fmt.Println("\nValidate versions:")
|
||||
v.Build[0] = "?"
|
||||
|
||||
err = v.Validate()
|
||||
if err != nil {
|
||||
fmt.Printf("Validation failed: %s\n", err)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Benchmarks
|
||||
-----
|
||||
|
||||
BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op
|
||||
BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op
|
||||
BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op
|
||||
BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op
|
||||
BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op
|
||||
BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op
|
||||
BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op
|
||||
BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op
|
||||
BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op
|
||||
|
||||
See benchmark cases at [semver_test.go](semver_test.go)
|
||||
|
||||
|
||||
Motivation
|
||||
-----
|
||||
|
||||
I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like.
|
||||
|
||||
|
||||
Contribution
|
||||
-----
|
||||
|
||||
Feel free to make a pull request. For bigger changes create a issue first to discuss about it.
|
||||
|
||||
|
||||
License
|
||||
-----
|
||||
|
||||
See [LICENSE](LICENSE) file.
|
||||
17
vendor/github.com/blang/semver/package.json
generated
vendored
17
vendor/github.com/blang/semver/package.json
generated
vendored
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"author": "blang",
|
||||
"bugs": {
|
||||
"URL": "https://github.com/blang/semver/issues",
|
||||
"url": "https://github.com/blang/semver/issues"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/blang/semver"
|
||||
},
|
||||
"gxVersion": "0.10.0",
|
||||
"language": "go",
|
||||
"license": "MIT",
|
||||
"name": "semver",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "3.5.1"
|
||||
}
|
||||
|
||||
0
vendor/github.com/blang/semver/LICENSE → vendor/github.com/blang/semver/v4/LICENSE
generated
vendored
0
vendor/github.com/blang/semver/LICENSE → vendor/github.com/blang/semver/v4/LICENSE
generated
vendored
0
vendor/github.com/blang/semver/json.go → vendor/github.com/blang/semver/v4/json.go
generated
vendored
0
vendor/github.com/blang/semver/json.go → vendor/github.com/blang/semver/v4/json.go
generated
vendored
2
vendor/github.com/blang/semver/range.go → vendor/github.com/blang/semver/v4/range.go
generated
vendored
2
vendor/github.com/blang/semver/range.go → vendor/github.com/blang/semver/v4/range.go
generated
vendored
@@ -327,7 +327,7 @@ func expandWildcardVersion(parts [][]string) ([][]string, error) {
|
||||
for _, p := range parts {
|
||||
var newParts []string
|
||||
for _, ap := range p {
|
||||
if strings.Index(ap, "x") != -1 {
|
||||
if strings.Contains(ap, "x") {
|
||||
opStr, vStr, err := splitComparatorVersion(ap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
72
vendor/github.com/blang/semver/semver.go → vendor/github.com/blang/semver/v4/semver.go
generated
vendored
72
vendor/github.com/blang/semver/semver.go → vendor/github.com/blang/semver/v4/semver.go
generated
vendored
@@ -26,7 +26,7 @@ type Version struct {
|
||||
Minor uint64
|
||||
Patch uint64
|
||||
Pre []PRVersion
|
||||
Build []string //No Precendence
|
||||
Build []string //No Precedence
|
||||
}
|
||||
|
||||
// Version to string
|
||||
@@ -61,6 +61,18 @@ func (v Version) String() string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// FinalizeVersion discards prerelease and build number and only returns
|
||||
// major, minor and patch number.
|
||||
func (v Version) FinalizeVersion() string {
|
||||
b := make([]byte, 0, 5)
|
||||
b = strconv.AppendUint(b, v.Major, 10)
|
||||
b = append(b, '.')
|
||||
b = strconv.AppendUint(b, v.Minor, 10)
|
||||
b = append(b, '.')
|
||||
b = strconv.AppendUint(b, v.Patch, 10)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Equals checks if v is equal to o.
|
||||
func (v Version) Equals(o Version) bool {
|
||||
return (v.Compare(o) == 0)
|
||||
@@ -161,6 +173,27 @@ func (v Version) Compare(o Version) int {
|
||||
|
||||
}
|
||||
|
||||
// IncrementPatch increments the patch version
|
||||
func (v *Version) IncrementPatch() error {
|
||||
v.Patch++
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncrementMinor increments the minor version
|
||||
func (v *Version) IncrementMinor() error {
|
||||
v.Minor++
|
||||
v.Patch = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncrementMajor increments the major version
|
||||
func (v *Version) IncrementMajor() error {
|
||||
v.Major++
|
||||
v.Minor = 0
|
||||
v.Patch = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates v and returns error in case
|
||||
func (v Version) Validate() error {
|
||||
// Major, Minor, Patch already validated using uint64
|
||||
@@ -189,10 +222,10 @@ func (v Version) Validate() error {
|
||||
}
|
||||
|
||||
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
|
||||
func New(s string) (vp *Version, err error) {
|
||||
func New(s string) (*Version, error) {
|
||||
v, err := Parse(s)
|
||||
vp = &v
|
||||
return
|
||||
vp := &v
|
||||
return vp, err
|
||||
}
|
||||
|
||||
// Make is an alias for Parse, parses version string and returns a validated Version or error
|
||||
@@ -202,14 +235,25 @@ func Make(s string) (Version, error) {
|
||||
|
||||
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
|
||||
// specs to be parsed by this library. It does so by normalizing versions before passing them to
|
||||
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
|
||||
// with only major and minor components specified
|
||||
// Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions
|
||||
// with only major and minor components specified, and removes leading 0s.
|
||||
func ParseTolerant(s string) (Version, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
s = strings.TrimPrefix(s, "v")
|
||||
|
||||
// Split into major.minor.(patch+pr+meta)
|
||||
parts := strings.SplitN(s, ".", 3)
|
||||
// Remove leading zeros.
|
||||
for i, p := range parts {
|
||||
if len(p) > 1 {
|
||||
p = strings.TrimLeft(p, "0")
|
||||
if len(p) == 0 || !strings.ContainsAny(p[0:1], "0123456789") {
|
||||
p = "0" + p
|
||||
}
|
||||
parts[i] = p
|
||||
}
|
||||
}
|
||||
// Fill up shortened versions.
|
||||
if len(parts) < 3 {
|
||||
if strings.ContainsAny(parts[len(parts)-1], "+-") {
|
||||
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
|
||||
@@ -217,8 +261,8 @@ func ParseTolerant(s string) (Version, error) {
|
||||
for len(parts) < 3 {
|
||||
parts = append(parts, "0")
|
||||
}
|
||||
s = strings.Join(parts, ".")
|
||||
}
|
||||
s = strings.Join(parts, ".")
|
||||
|
||||
return Parse(s)
|
||||
}
|
||||
@@ -416,3 +460,17 @@ func NewBuildVersion(s string) (string, error) {
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// FinalizeVersion returns the major, minor and patch number only and discards
|
||||
// prerelease and build number.
|
||||
func FinalizeVersion(s string) (string, error) {
|
||||
v, err := Parse(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
v.Pre = nil
|
||||
v.Build = nil
|
||||
|
||||
finalVer := v.String()
|
||||
return finalVer, nil
|
||||
}
|
||||
0
vendor/github.com/blang/semver/sort.go → vendor/github.com/blang/semver/v4/sort.go
generated
vendored
0
vendor/github.com/blang/semver/sort.go → vendor/github.com/blang/semver/v4/sort.go
generated
vendored
2
vendor/github.com/blang/semver/sql.go → vendor/github.com/blang/semver/v4/sql.go
generated
vendored
2
vendor/github.com/blang/semver/sql.go → vendor/github.com/blang/semver/v4/sql.go
generated
vendored
@@ -14,7 +14,7 @@ func (v *Version) Scan(src interface{}) (err error) {
|
||||
case []byte:
|
||||
str = string(src)
|
||||
default:
|
||||
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
|
||||
return fmt.Errorf("version.Scan: cannot convert %T to string", src)
|
||||
}
|
||||
|
||||
if t, err := Parse(str); err == nil {
|
||||
44
vendor/github.com/bwmarrin/discordgo/components.go
generated
vendored
44
vendor/github.com/bwmarrin/discordgo/components.go
generated
vendored
@@ -132,10 +132,10 @@ type ComponentEmoji struct {
|
||||
|
||||
// Button represents button component.
|
||||
type Button struct {
|
||||
Label string `json:"label"`
|
||||
Style ButtonStyle `json:"style"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Emoji ComponentEmoji `json:"emoji"`
|
||||
Label string `json:"label"`
|
||||
Style ButtonStyle `json:"style"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Emoji *ComponentEmoji `json:"emoji,omitempty"`
|
||||
|
||||
// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
|
||||
URL string `json:"url,omitempty"`
|
||||
@@ -166,14 +166,32 @@ func (Button) Type() ComponentType {
|
||||
|
||||
// SelectMenuOption represents an option for a select menu.
|
||||
type SelectMenuOption struct {
|
||||
Label string `json:"label,omitempty"`
|
||||
Value string `json:"value"`
|
||||
Description string `json:"description"`
|
||||
Emoji ComponentEmoji `json:"emoji"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Value string `json:"value"`
|
||||
Description string `json:"description"`
|
||||
Emoji *ComponentEmoji `json:"emoji,omitempty"`
|
||||
// Determines whenever option is selected by default or not.
|
||||
Default bool `json:"default"`
|
||||
}
|
||||
|
||||
// SelectMenuDefaultValueType represents the type of an entity selected by default in auto-populated select menus.
|
||||
type SelectMenuDefaultValueType string
|
||||
|
||||
// SelectMenuDefaultValue types.
|
||||
const (
|
||||
SelectMenuDefaultValueUser SelectMenuDefaultValueType = "user"
|
||||
SelectMenuDefaultValueRole SelectMenuDefaultValueType = "role"
|
||||
SelectMenuDefaultValueChannel SelectMenuDefaultValueType = "channel"
|
||||
)
|
||||
|
||||
// SelectMenuDefaultValue represents an entity selected by default in auto-populated select menus.
|
||||
type SelectMenuDefaultValue struct {
|
||||
// ID of the entity.
|
||||
ID string `json:"id"`
|
||||
// Type of the entity.
|
||||
Type SelectMenuDefaultValueType `json:"type"`
|
||||
}
|
||||
|
||||
// SelectMenuType represents select menu type.
|
||||
type SelectMenuType ComponentType
|
||||
|
||||
@@ -198,9 +216,13 @@ type SelectMenu struct {
|
||||
MinValues *int `json:"min_values,omitempty"`
|
||||
// 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,omitempty"`
|
||||
Disabled bool `json:"disabled"`
|
||||
MaxValues int `json:"max_values,omitempty"`
|
||||
// List of default values for auto-populated select menus.
|
||||
// NOTE: Number of entries should be in the range defined by MinValues and MaxValues.
|
||||
DefaultValues []SelectMenuDefaultValue `json:"default_values,omitempty"`
|
||||
|
||||
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"`
|
||||
|
||||
31
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
31
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
||||
const VERSION = "0.27.1"
|
||||
const VERSION = "0.28.1"
|
||||
|
||||
// New creates a new Discord session with provided token.
|
||||
// If the token is for a bot, it must be prefixed with "Bot "
|
||||
@@ -33,20 +33,21 @@ func New(token string) (s *Session, err error) {
|
||||
|
||||
// Create an empty Session interface.
|
||||
s = &Session{
|
||||
State: NewState(),
|
||||
Ratelimiter: NewRatelimiter(),
|
||||
StateEnabled: true,
|
||||
Compress: true,
|
||||
ShouldReconnectOnError: true,
|
||||
ShouldRetryOnRateLimit: true,
|
||||
ShardID: 0,
|
||||
ShardCount: 1,
|
||||
MaxRestRetries: 3,
|
||||
Client: &http.Client{Timeout: (20 * time.Second)},
|
||||
Dialer: websocket.DefaultDialer,
|
||||
UserAgent: "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")",
|
||||
sequence: new(int64),
|
||||
LastHeartbeatAck: time.Now().UTC(),
|
||||
State: NewState(),
|
||||
Ratelimiter: NewRatelimiter(),
|
||||
StateEnabled: true,
|
||||
Compress: true,
|
||||
ShouldReconnectOnError: true,
|
||||
ShouldReconnectVoiceOnSessionError: true,
|
||||
ShouldRetryOnRateLimit: true,
|
||||
ShardID: 0,
|
||||
ShardCount: 1,
|
||||
MaxRestRetries: 3,
|
||||
Client: &http.Client{Timeout: (20 * time.Second)},
|
||||
Dialer: websocket.DefaultDialer,
|
||||
UserAgent: "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")",
|
||||
sequence: new(int64),
|
||||
LastHeartbeatAck: time.Now().UTC(),
|
||||
}
|
||||
|
||||
// Initialize the Identify Package with defaults
|
||||
|
||||
13
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
13
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
@@ -42,6 +42,7 @@ var (
|
||||
EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"
|
||||
EndpointCDNBanners = EndpointCDN + "banners/"
|
||||
EndpointCDNGuilds = EndpointCDN + "guilds/"
|
||||
EndpointCDNRoleIcons = EndpointCDN + "role-icons/"
|
||||
|
||||
EndpointVoice = EndpointAPI + "/voice/"
|
||||
EndpointVoiceRegions = EndpointVoice + "regions"
|
||||
@@ -49,9 +50,8 @@ var (
|
||||
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
||||
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
|
||||
EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
|
||||
EndpointDefaultUserAvatar = func(uDiscriminator string) string {
|
||||
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator)
|
||||
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
|
||||
EndpointDefaultUserAvatar = func(idx int) string {
|
||||
return EndpointCDN + "embed/avatars/" + strconv.Itoa(idx) + ".png"
|
||||
}
|
||||
EndpointUserBanner = func(uID, cID string) string {
|
||||
return EndpointCDNBanners + uID + "/" + cID + ".png"
|
||||
@@ -104,7 +104,8 @@ var (
|
||||
EndpointGuildScheduledEvents = func(gID string) string { return EndpointGuilds + gID + "/scheduled-events" }
|
||||
EndpointGuildScheduledEvent = func(gID, eID string) string { return EndpointGuilds + gID + "/scheduled-events/" + eID }
|
||||
EndpointGuildScheduledEventUsers = func(gID, eID string) string { return EndpointGuildScheduledEvent(gID, eID) + "/users" }
|
||||
EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "/templates/" + tID }
|
||||
EndpointGuildOnboarding = func(gID string) string { return EndpointGuilds + gID + "/onboarding" }
|
||||
EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "templates/" + tID }
|
||||
EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" }
|
||||
EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID }
|
||||
EndpointGuildMemberAvatar = func(gId, uID, aID string) string {
|
||||
@@ -114,6 +115,10 @@ var (
|
||||
return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".gif"
|
||||
}
|
||||
|
||||
EndpointRoleIcon = func(rID, hash string) string {
|
||||
return EndpointCDNRoleIcons + rID + "/" + hash + ".png"
|
||||
}
|
||||
|
||||
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
|
||||
EndpointChannelThreads = func(cID string) string { return EndpointChannel(cID) + "/threads" }
|
||||
EndpointChannelActiveThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/active" }
|
||||
|
||||
24
vendor/github.com/bwmarrin/discordgo/eventhandlers.go
generated
vendored
24
vendor/github.com/bwmarrin/discordgo/eventhandlers.go
generated
vendored
@@ -19,6 +19,7 @@ const (
|
||||
connectEventType = "__CONNECT__"
|
||||
disconnectEventType = "__DISCONNECT__"
|
||||
eventEventType = "__EVENT__"
|
||||
guildAuditLogEntryCreateEventType = "GUILD_AUDIT_LOG_ENTRY_CREATE"
|
||||
guildBanAddEventType = "GUILD_BAN_ADD"
|
||||
guildBanRemoveEventType = "GUILD_BAN_REMOVE"
|
||||
guildCreateEventType = "GUILD_CREATE"
|
||||
@@ -294,6 +295,26 @@ func (eh eventEventHandler) Handle(s *Session, i interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// guildAuditLogEntryCreateEventHandler is an event handler for GuildAuditLogEntryCreate events.
|
||||
type guildAuditLogEntryCreateEventHandler func(*Session, *GuildAuditLogEntryCreate)
|
||||
|
||||
// Type returns the event type for GuildAuditLogEntryCreate events.
|
||||
func (eh guildAuditLogEntryCreateEventHandler) Type() string {
|
||||
return guildAuditLogEntryCreateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildAuditLogEntryCreate.
|
||||
func (eh guildAuditLogEntryCreateEventHandler) New() interface{} {
|
||||
return &GuildAuditLogEntryCreate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildAuditLogEntryCreate events.
|
||||
func (eh guildAuditLogEntryCreateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildAuditLogEntryCreate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildBanAddEventHandler is an event handler for GuildBanAdd events.
|
||||
type guildBanAddEventHandler func(*Session, *GuildBanAdd)
|
||||
|
||||
@@ -1277,6 +1298,8 @@ func handlerForInterface(handler interface{}) EventHandler {
|
||||
return disconnectEventHandler(v)
|
||||
case func(*Session, *Event):
|
||||
return eventEventHandler(v)
|
||||
case func(*Session, *GuildAuditLogEntryCreate):
|
||||
return guildAuditLogEntryCreateEventHandler(v)
|
||||
case func(*Session, *GuildBanAdd):
|
||||
return guildBanAddEventHandler(v)
|
||||
case func(*Session, *GuildBanRemove):
|
||||
@@ -1388,6 +1411,7 @@ func init() {
|
||||
registerInterfaceProvider(channelDeleteEventHandler(nil))
|
||||
registerInterfaceProvider(channelPinsUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(channelUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(guildAuditLogEntryCreateEventHandler(nil))
|
||||
registerInterfaceProvider(guildBanAddEventHandler(nil))
|
||||
registerInterfaceProvider(guildBanRemoveEventHandler(nil))
|
||||
registerInterfaceProvider(guildCreateEventHandler(nil))
|
||||
|
||||
5
vendor/github.com/bwmarrin/discordgo/events.go
generated
vendored
5
vendor/github.com/bwmarrin/discordgo/events.go
generated
vendored
@@ -401,3 +401,8 @@ type AutoModerationActionExecution struct {
|
||||
MatchedKeyword string `json:"matched_keyword"`
|
||||
MatchedContent string `json:"matched_content"`
|
||||
}
|
||||
|
||||
// GuildAuditLogEntryCreate is the data for a GuildAuditLogEntryCreate event.
|
||||
type GuildAuditLogEntryCreate struct {
|
||||
*AuditLogEntry
|
||||
}
|
||||
|
||||
8
vendor/github.com/bwmarrin/discordgo/interactions.go
generated
vendored
8
vendor/github.com/bwmarrin/discordgo/interactions.go
generated
vendored
@@ -314,9 +314,10 @@ type InteractionData interface {
|
||||
|
||||
// ApplicationCommandInteractionData contains the data of application command interaction.
|
||||
type ApplicationCommandInteractionData struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Resolved *ApplicationCommandInteractionDataResolved `json:"resolved"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CommandType ApplicationCommandType `json:"type"`
|
||||
Resolved *ApplicationCommandInteractionDataResolved `json:"resolved"`
|
||||
|
||||
// Slash command options
|
||||
Options []*ApplicationCommandInteractionDataOption `json:"options"`
|
||||
@@ -553,6 +554,7 @@ type InteractionResponseData struct {
|
||||
Embeds []*MessageEmbed `json:"embeds"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
Files []*File `json:"-"`
|
||||
Attachments *[]*MessageAttachment `json:"attachments,omitempty"`
|
||||
|
||||
// NOTE: only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set.
|
||||
Flags MessageFlags `json:"flags,omitempty"`
|
||||
|
||||
2
vendor/github.com/bwmarrin/discordgo/locales.go
generated
vendored
2
vendor/github.com/bwmarrin/discordgo/locales.go
generated
vendored
@@ -39,6 +39,7 @@ const (
|
||||
Romanian Locale = "ro"
|
||||
Russian Locale = "ru"
|
||||
SpanishES Locale = "es-ES"
|
||||
SpanishLATAM Locale = "es-419"
|
||||
Swedish Locale = "sv-SE"
|
||||
Thai Locale = "th"
|
||||
Turkish Locale = "tr"
|
||||
@@ -74,6 +75,7 @@ var Locales = map[Locale]string{
|
||||
Romanian: "Romanian",
|
||||
Russian: "Russian",
|
||||
SpanishES: "Spanish (Spain)",
|
||||
SpanishLATAM: "Spanish (LATAM)",
|
||||
Swedish: "Swedish",
|
||||
Thai: "Thai",
|
||||
Turkish: "Turkish",
|
||||
|
||||
46
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
46
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
@@ -148,8 +148,8 @@ type Message struct {
|
||||
// The thread that was started from this message, includes thread member object
|
||||
Thread *Channel `json:"thread,omitempty"`
|
||||
|
||||
// An array of Sticker objects, if any were sent.
|
||||
StickerItems []*Sticker `json:"sticker_items"`
|
||||
// An array of StickerItem objects, representing sent stickers, if there were any.
|
||||
StickerItems []*StickerItem `json:"sticker_items"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a helper function to unmarshal the Message.
|
||||
@@ -215,6 +215,10 @@ const (
|
||||
MessageFlagsLoading MessageFlags = 1 << 7
|
||||
// MessageFlagsFailedToMentionSomeRolesInThread this message failed to mention some roles and add their members to the thread.
|
||||
MessageFlagsFailedToMentionSomeRolesInThread MessageFlags = 1 << 8
|
||||
// MessageFlagsSuppressNotifications this message will not trigger push and desktop notifications.
|
||||
MessageFlagsSuppressNotifications MessageFlags = 1 << 12
|
||||
// MessageFlagsIsVoiceMessage this message is a voice message.
|
||||
MessageFlagsIsVoiceMessage MessageFlags = 1 << 13
|
||||
)
|
||||
|
||||
// File stores info about files you e.g. send in messages.
|
||||
@@ -233,6 +237,8 @@ type MessageSend struct {
|
||||
Files []*File `json:"-"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
Reference *MessageReference `json:"message_reference,omitempty"`
|
||||
StickerIDs []string `json:"sticker_ids"`
|
||||
Flags MessageFlags `json:"flags,omitempty"`
|
||||
|
||||
// TODO: Remove this when compatibility is not required.
|
||||
File *File `json:"-"`
|
||||
@@ -245,8 +251,8 @@ type MessageSend struct {
|
||||
// is also where you should get the instance from.
|
||||
type MessageEdit struct {
|
||||
Content *string `json:"content,omitempty"`
|
||||
Components []MessageComponent `json:"components"`
|
||||
Embeds []*MessageEmbed `json:"embeds"`
|
||||
Components *[]MessageComponent `json:"components,omitempty"`
|
||||
Embeds *[]*MessageEmbed `json:"embeds,omitempty"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
Flags MessageFlags `json:"flags,omitempty"`
|
||||
// Files to append to the message
|
||||
@@ -280,14 +286,14 @@ func (m *MessageEdit) SetContent(str string) *MessageEdit {
|
||||
// SetEmbed is a convenience function for setting the embed,
|
||||
// so you can chain commands.
|
||||
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
|
||||
m.Embeds = []*MessageEmbed{embed}
|
||||
m.Embeds = &[]*MessageEmbed{embed}
|
||||
return m
|
||||
}
|
||||
|
||||
// SetEmbeds is a convenience function for setting the embeds,
|
||||
// so you can chain commands.
|
||||
func (m *MessageEdit) SetEmbeds(embeds []*MessageEmbed) *MessageEdit {
|
||||
m.Embeds = embeds
|
||||
m.Embeds = &embeds
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -460,20 +466,32 @@ type MessageApplication struct {
|
||||
|
||||
// MessageReference contains reference data sent with crossposted messages
|
||||
type MessageReference struct {
|
||||
MessageID string `json:"message_id"`
|
||||
ChannelID string `json:"channel_id,omitempty"`
|
||||
GuildID string `json:"guild_id,omitempty"`
|
||||
MessageID string `json:"message_id"`
|
||||
ChannelID string `json:"channel_id,omitempty"`
|
||||
GuildID string `json:"guild_id,omitempty"`
|
||||
FailIfNotExists *bool `json:"fail_if_not_exists,omitempty"`
|
||||
}
|
||||
|
||||
// Reference returns MessageReference of given message
|
||||
func (m *Message) Reference() *MessageReference {
|
||||
func (m *Message) reference(failIfNotExists bool) *MessageReference {
|
||||
return &MessageReference{
|
||||
GuildID: m.GuildID,
|
||||
ChannelID: m.ChannelID,
|
||||
MessageID: m.ID,
|
||||
GuildID: m.GuildID,
|
||||
ChannelID: m.ChannelID,
|
||||
MessageID: m.ID,
|
||||
FailIfNotExists: &failIfNotExists,
|
||||
}
|
||||
}
|
||||
|
||||
// Reference returns a MessageReference of the given message.
|
||||
func (m *Message) Reference() *MessageReference {
|
||||
return m.reference(true)
|
||||
}
|
||||
|
||||
// SoftReference returns a MessageReference of the given message.
|
||||
// If the message doesn't exist it will instead be sent as a non-reply message.
|
||||
func (m *Message) SoftReference() *MessageReference {
|
||||
return m.reference(false)
|
||||
}
|
||||
|
||||
// ContentWithMentionsReplaced will replace all @<id> mentions with the
|
||||
// username of the mention.
|
||||
func (m *Message) ContentWithMentionsReplaced() (content string) {
|
||||
|
||||
120
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
120
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
@@ -424,10 +424,11 @@ func (s *Session) UserGuildMember(guildID string, options ...RequestOption) (st
|
||||
}
|
||||
|
||||
// UserGuilds returns an array of UserGuild structures for all guilds.
|
||||
// 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, options ...RequestOption) (st []*UserGuild, err error) {
|
||||
// limit : The number guilds that can be returned. (max 200)
|
||||
// beforeID : If provided all guilds returned will be before given ID.
|
||||
// afterID : If provided all guilds returned will be after given ID.
|
||||
// withCounts : Whether to include approximate member and presence counts or not.
|
||||
func (s *Session) UserGuilds(limit int, beforeID, afterID string, withCounts bool, options ...RequestOption) (st []*UserGuild, err error) {
|
||||
|
||||
v := url.Values{}
|
||||
|
||||
@@ -440,6 +441,9 @@ func (s *Session) UserGuilds(limit int, beforeID, afterID string, options ...Req
|
||||
if beforeID != "" {
|
||||
v.Set("before", beforeID)
|
||||
}
|
||||
if withCounts {
|
||||
v.Set("with_counts", "true")
|
||||
}
|
||||
|
||||
uri := EndpointUserGuilds("@me")
|
||||
|
||||
@@ -672,14 +676,9 @@ func (s *Session) GuildEdit(guildID string, g *GuildParams, options ...RequestOp
|
||||
|
||||
// GuildDelete deletes a Guild.
|
||||
// guildID : The ID of a Guild
|
||||
func (s *Session) GuildDelete(guildID string, options ...RequestOption) (st *Guild, err error) {
|
||||
func (s *Session) GuildDelete(guildID string, options ...RequestOption) (err error) {
|
||||
|
||||
body, err := s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID), options...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &st)
|
||||
_, err = s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID), options...)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1700,13 +1699,19 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend,
|
||||
}
|
||||
}
|
||||
|
||||
if data.StickerIDs != nil {
|
||||
if len(data.StickerIDs) > 3 {
|
||||
err = fmt.Errorf("cannot send more than 3 stickers")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var response []byte
|
||||
if len(files) > 0 {
|
||||
contentType, body, encodeErr := MultipartBodyWithJSON(data, files)
|
||||
if encodeErr != nil {
|
||||
return st, encodeErr
|
||||
}
|
||||
|
||||
response, err = s.request("POST", endpoint, contentType, body, endpoint, 0, options...)
|
||||
} else {
|
||||
response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint, options...)
|
||||
@@ -1796,16 +1801,18 @@ func (s *Session) ChannelMessageEditComplex(m *MessageEdit, options ...RequestOp
|
||||
// TODO: Remove this when compatibility is not required.
|
||||
if m.Embed != nil {
|
||||
if m.Embeds == nil {
|
||||
m.Embeds = []*MessageEmbed{m.Embed}
|
||||
m.Embeds = &[]*MessageEmbed{m.Embed}
|
||||
} else {
|
||||
err = fmt.Errorf("cannot specify both Embed and Embeds")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, embed := range m.Embeds {
|
||||
if embed.Type == "" {
|
||||
embed.Type = "rich"
|
||||
if m.Embeds != nil {
|
||||
for _, embed := range *m.Embeds {
|
||||
if embed.Type == "" {
|
||||
embed.Type = "rich"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2267,7 +2274,7 @@ func (s *Session) WebhookWithToken(webhookID, token string, options ...RequestOp
|
||||
// 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, options ...RequestOption) (st *Role, err error) {
|
||||
func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string, options ...RequestOption) (st *Webhook, err error) {
|
||||
|
||||
data := struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
@@ -2290,14 +2297,15 @@ func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string, options
|
||||
// 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, options ...RequestOption) (st *Role, err error) {
|
||||
func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string, options ...RequestOption) (st *Webhook, 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("", ""), options...)
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", ""), options...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -2709,11 +2717,22 @@ func (s *Session) ThreadMemberRemove(threadID, memberID string, options ...Reque
|
||||
return err
|
||||
}
|
||||
|
||||
// ThreadMember returns thread member object for the specified member of a thread
|
||||
func (s *Session) ThreadMember(threadID, memberID string, options ...RequestOption) (member *ThreadMember, err error) {
|
||||
endpoint := EndpointThreadMember(threadID, memberID)
|
||||
// ThreadMember returns thread member object for the specified member of a thread.
|
||||
// withMember : Whether to include a guild member object.
|
||||
func (s *Session) ThreadMember(threadID, memberID string, withMember bool, options ...RequestOption) (member *ThreadMember, err error) {
|
||||
uri := EndpointThreadMember(threadID, memberID)
|
||||
|
||||
queryParams := url.Values{}
|
||||
if withMember {
|
||||
queryParams.Set("with_member", "true")
|
||||
}
|
||||
|
||||
if len(queryParams) > 0 {
|
||||
uri += "?" + queryParams.Encode()
|
||||
}
|
||||
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...)
|
||||
body, err = s.RequestWithBucketID("GET", uri, nil, uri, options...)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
@@ -2724,9 +2743,29 @@ func (s *Session) ThreadMember(threadID, memberID string, options ...RequestOpti
|
||||
}
|
||||
|
||||
// ThreadMembers returns all members of specified thread.
|
||||
func (s *Session) ThreadMembers(threadID string, options ...RequestOption) (members []*ThreadMember, err error) {
|
||||
// limit : Max number of thread members to return (1-100). Defaults to 100.
|
||||
// afterID : Get thread members after this user ID.
|
||||
// withMember : Whether to include a guild member object for each thread member.
|
||||
func (s *Session) ThreadMembers(threadID string, limit int, withMember bool, afterID string, options ...RequestOption) (members []*ThreadMember, err error) {
|
||||
uri := EndpointThreadMembers(threadID)
|
||||
|
||||
queryParams := url.Values{}
|
||||
if withMember {
|
||||
queryParams.Set("with_member", "true")
|
||||
}
|
||||
if limit > 0 {
|
||||
queryParams.Set("limit", strconv.Itoa(limit))
|
||||
}
|
||||
if afterID != "" {
|
||||
queryParams.Set("after", afterID)
|
||||
}
|
||||
|
||||
if len(queryParams) > 0 {
|
||||
uri += "?" + queryParams.Encode()
|
||||
}
|
||||
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", EndpointThreadMembers(threadID), nil, EndpointThreadMembers(threadID), options...)
|
||||
body, err = s.RequestWithBucketID("GET", uri, nil, uri, options...)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
@@ -3248,6 +3287,37 @@ func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, w
|
||||
return
|
||||
}
|
||||
|
||||
// GuildOnboarding returns onboarding configuration of a guild.
|
||||
// guildID : The ID of the guild
|
||||
func (s *Session) GuildOnboarding(guildID string, options ...RequestOption) (onboarding *GuildOnboarding, err error) {
|
||||
endpoint := EndpointGuildOnboarding(guildID)
|
||||
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint, options...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &onboarding)
|
||||
return
|
||||
}
|
||||
|
||||
// GuildOnboardingEdit edits onboarding configuration of a guild.
|
||||
// guildID : The ID of the guild
|
||||
// o : New GuildOnboarding data
|
||||
func (s *Session) GuildOnboardingEdit(guildID string, o *GuildOnboarding, options ...RequestOption) (onboarding *GuildOnboarding, err error) {
|
||||
endpoint := EndpointGuildOnboarding(guildID)
|
||||
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("PUT", endpoint, o, endpoint, options...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &onboarding)
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Functions specific to auto moderation
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
245
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
245
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
@@ -42,6 +42,9 @@ type Session struct {
|
||||
// Should the session reconnect the websocket on errors.
|
||||
ShouldReconnectOnError bool
|
||||
|
||||
// Should voice connections reconnect on a session reconnect.
|
||||
ShouldReconnectVoiceOnSessionError bool
|
||||
|
||||
// Should the session retry requests when rate limited.
|
||||
ShouldRetryOnRateLimit bool
|
||||
|
||||
@@ -285,7 +288,9 @@ const (
|
||||
ChannelTypeGuildPublicThread ChannelType = 11
|
||||
ChannelTypeGuildPrivateThread ChannelType = 12
|
||||
ChannelTypeGuildStageVoice ChannelType = 13
|
||||
ChannelTypeGuildDirectory ChannelType = 14
|
||||
ChannelTypeGuildForum ChannelType = 15
|
||||
ChannelTypeGuildMedia ChannelType = 16
|
||||
)
|
||||
|
||||
// ChannelFlags represent flags of a channel/thread.
|
||||
@@ -440,7 +445,7 @@ type ChannelEdit struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
NSFW *bool `json:"nsfw,omitempty"`
|
||||
Position int `json:"position"`
|
||||
Position *int `json:"position,omitempty"`
|
||||
Bitrate int `json:"bitrate,omitempty"`
|
||||
UserLimit int `json:"user_limit,omitempty"`
|
||||
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
|
||||
@@ -528,6 +533,10 @@ type ThreadMember struct {
|
||||
JoinTimestamp time.Time `json:"join_timestamp"`
|
||||
// Any user-thread settings, currently only used for notifications
|
||||
Flags int `json:"flags"`
|
||||
// Additional information about the user.
|
||||
// NOTE: only present if the withMember parameter is set to true
|
||||
// when calling Session.ThreadMembers or Session.ThreadMember.
|
||||
Member *Member `json:"member,omitempty"`
|
||||
}
|
||||
|
||||
// ThreadsList represents a list of threads alongisde with thread member objects for the current user.
|
||||
@@ -649,6 +658,13 @@ type Sticker struct {
|
||||
SortValue int `json:"sort_value"`
|
||||
}
|
||||
|
||||
// StickerItem represents the smallest amount of data required to render a sticker. A partial sticker object.
|
||||
type StickerItem struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
FormatType StickerFormat `json:"format_type"`
|
||||
}
|
||||
|
||||
// StickerPack represents a pack of standard stickers.
|
||||
type StickerPack struct {
|
||||
ID string `json:"id"`
|
||||
@@ -1067,6 +1083,109 @@ type GuildScheduledEventUser struct {
|
||||
Member *Member `json:"member"`
|
||||
}
|
||||
|
||||
// GuildOnboardingMode defines the criteria used to satisfy constraints that are required for enabling onboarding.
|
||||
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-onboarding-mode
|
||||
type GuildOnboardingMode int
|
||||
|
||||
// Block containing known GuildOnboardingMode values.
|
||||
const (
|
||||
// GuildOnboardingModeDefault counts default channels towards constraints.
|
||||
GuildOnboardingModeDefault GuildOnboardingMode = 0
|
||||
// GuildOnboardingModeAdvanced counts default channels and questions towards constraints.
|
||||
GuildOnboardingModeAdvanced GuildOnboardingMode = 1
|
||||
)
|
||||
|
||||
// GuildOnboarding represents the onboarding flow for a guild.
|
||||
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object
|
||||
type GuildOnboarding struct {
|
||||
// ID of the guild this onboarding flow is part of.
|
||||
GuildID string `json:"guild_id,omitempty"`
|
||||
|
||||
// Prompts shown during onboarding and in the customize community (Channels & Roles) tab.
|
||||
Prompts *[]GuildOnboardingPrompt `json:"prompts,omitempty"`
|
||||
|
||||
// Channel IDs that members get opted into automatically.
|
||||
DefaultChannelIDs []string `json:"default_channel_ids,omitempty"`
|
||||
|
||||
// Whether onboarding is enabled in the guild.
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
|
||||
// Mode of onboarding.
|
||||
Mode *GuildOnboardingMode `json:"mode,omitempty"`
|
||||
}
|
||||
|
||||
// GuildOnboardingPromptType is the type of an onboarding prompt.
|
||||
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-prompt-types
|
||||
type GuildOnboardingPromptType int
|
||||
|
||||
// Block containing known GuildOnboardingPromptType values.
|
||||
const (
|
||||
GuildOnboardingPromptTypeMultipleChoice GuildOnboardingPromptType = 0
|
||||
GuildOnboardingPromptTypeDropdown GuildOnboardingPromptType = 1
|
||||
)
|
||||
|
||||
// GuildOnboardingPrompt is a prompt shown during onboarding and in the customize community (Channels & Roles) tab.
|
||||
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-onboarding-prompt-structure
|
||||
type GuildOnboardingPrompt struct {
|
||||
// ID of the prompt.
|
||||
// NOTE: always requires to be a valid snowflake (e.g. "0"), see
|
||||
// https://github.com/discord/discord-api-docs/issues/6320 for more information.
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// Type of the prompt.
|
||||
Type GuildOnboardingPromptType `json:"type"`
|
||||
|
||||
// Options available within the prompt.
|
||||
Options []GuildOnboardingPromptOption `json:"options"`
|
||||
|
||||
// Title of the prompt.
|
||||
Title string `json:"title"`
|
||||
|
||||
// Indicates whether users are limited to selecting one option for the prompt.
|
||||
SingleSelect bool `json:"single_select"`
|
||||
|
||||
// Indicates whether the prompt is required before a user completes the onboarding flow.
|
||||
Required bool `json:"required"`
|
||||
|
||||
// Indicates whether the prompt is present in the onboarding flow.
|
||||
// If false, the prompt will only appear in the customize community (Channels & Roles) tab.
|
||||
InOnboarding bool `json:"in_onboarding"`
|
||||
}
|
||||
|
||||
// GuildOnboardingPromptOption is an option available within an onboarding prompt.
|
||||
// https://discord.com/developers/docs/resources/guild#guild-onboarding-object-prompt-option-structure
|
||||
type GuildOnboardingPromptOption struct {
|
||||
// ID of the prompt option.
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// IDs for channels a member is added to when the option is selected.
|
||||
ChannelIDs []string `json:"channel_ids"`
|
||||
|
||||
// IDs for roles assigned to a member when the option is selected.
|
||||
RoleIDs []string `json:"role_ids"`
|
||||
|
||||
// Emoji of the option.
|
||||
// NOTE: when creating or updating a prompt option
|
||||
// EmojiID, EmojiName and EmojiAnimated should be used instead.
|
||||
Emoji *Emoji `json:"emoji,omitempty"`
|
||||
|
||||
// Title of the option.
|
||||
Title string `json:"title"`
|
||||
|
||||
// Description of the option.
|
||||
Description string `json:"description"`
|
||||
|
||||
// ID of the option's emoji.
|
||||
// NOTE: only used when creating or updating a prompt option.
|
||||
EmojiID string `json:"emoji_id,omitempty"`
|
||||
// Name of the option's emoji.
|
||||
// NOTE: only used when creating or updating a prompt option.
|
||||
EmojiName string `json:"emoji_name,omitempty"`
|
||||
// Whether the option's emoji is animated.
|
||||
// NOTE: only used when creating or updating a prompt option.
|
||||
EmojiAnimated *bool `json:"emoji_animated,omitempty"`
|
||||
}
|
||||
|
||||
// A GuildTemplate represents a replicable template for guild creation
|
||||
type GuildTemplate struct {
|
||||
// The unique code for the guild template
|
||||
@@ -1157,6 +1276,14 @@ type UserGuild struct {
|
||||
Owner bool `json:"owner"`
|
||||
Permissions int64 `json:"permissions,string"`
|
||||
Features []GuildFeature `json:"features"`
|
||||
|
||||
// Approximate number of members in this guild.
|
||||
// NOTE: this field is only filled when withCounts is true.
|
||||
ApproximateMemberCount int `json:"approximate_member_count"`
|
||||
|
||||
// Approximate number of non-offline members in this guild.
|
||||
// NOTE: this field is only filled when withCounts is true.
|
||||
ApproximatePresenceCount int `json:"approximate_presence_count"`
|
||||
}
|
||||
|
||||
// GuildFeature indicates the presence of a feature in a guild
|
||||
@@ -1239,13 +1366,51 @@ type Role struct {
|
||||
// This is a combination of bit masks; the presence of a certain permission can
|
||||
// be checked by performing a bitwise AND between this int and the permission.
|
||||
Permissions int64 `json:"permissions,string"`
|
||||
|
||||
// The hash of the role icon. Use Role.IconURL to retrieve the icon's URL.
|
||||
Icon string `json:"icon"`
|
||||
|
||||
// The emoji assigned to this role.
|
||||
UnicodeEmoji string `json:"unicode_emoji"`
|
||||
|
||||
// The flags of the role, which describe its extra features.
|
||||
// This is a combination of bit masks; the presence of a certain flag can
|
||||
// be checked by performing a bitwise AND between this int and the flag.
|
||||
Flags RoleFlags `json:"flags"`
|
||||
}
|
||||
|
||||
// RoleFlags represent the flags of a Role.
|
||||
// https://discord.com/developers/docs/topics/permissions#role-object-role-flags
|
||||
type RoleFlags int
|
||||
|
||||
// Block containing known RoleFlags values.
|
||||
const (
|
||||
// RoleFlagInPrompt indicates whether the Role is selectable by members in an onboarding prompt.
|
||||
RoleFlagInPrompt RoleFlags = 1 << 0
|
||||
)
|
||||
|
||||
// Mention returns a string which mentions the role
|
||||
func (r *Role) Mention() string {
|
||||
return fmt.Sprintf("<@&%s>", r.ID)
|
||||
}
|
||||
|
||||
// IconURL returns the URL of the role's icon.
|
||||
//
|
||||
// size: The size of the desired role icon as a power of two
|
||||
// Image size can be any power of two between 16 and 4096.
|
||||
func (r *Role) IconURL(size string) string {
|
||||
if r.Icon == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
URL := EndpointRoleIcon(r.ID, r.Icon)
|
||||
|
||||
if size != "" {
|
||||
return URL + "?size=" + size
|
||||
}
|
||||
return URL
|
||||
}
|
||||
|
||||
// RoleParams represents the parameters needed to create or update a Role
|
||||
type RoleParams struct {
|
||||
// The role's name
|
||||
@@ -1258,6 +1423,12 @@ type RoleParams struct {
|
||||
Permissions *int64 `json:"permissions,omitempty,string"`
|
||||
// Whether this role is mentionable
|
||||
Mentionable *bool `json:"mentionable,omitempty"`
|
||||
// The role's unicode emoji.
|
||||
// NOTE: can only be set if the guild has the ROLE_ICONS feature.
|
||||
UnicodeEmoji *string `json:"unicode_emoji,omitempty"`
|
||||
// The role's icon image encoded in base64.
|
||||
// NOTE: can only be set if the guild has the ROLE_ICONS feature.
|
||||
Icon *string `json:"icon,omitempty"`
|
||||
}
|
||||
|
||||
// Roles are a collection of Role
|
||||
@@ -1330,6 +1501,22 @@ type Assets struct {
|
||||
SmallText string `json:"small_text,omitempty"`
|
||||
}
|
||||
|
||||
// MemberFlags represent flags of a guild member.
|
||||
// https://discord.com/developers/docs/resources/guild#guild-member-object-guild-member-flags
|
||||
type MemberFlags int
|
||||
|
||||
// Block containing known MemberFlags values.
|
||||
const (
|
||||
// MemberFlagDidRejoin indicates whether the Member has left and rejoined the guild.
|
||||
MemberFlagDidRejoin MemberFlags = 1 << 0
|
||||
// MemberFlagCompletedOnboarding indicates whether the Member has completed onboarding.
|
||||
MemberFlagCompletedOnboarding MemberFlags = 1 << 1
|
||||
// MemberFlagBypassesVerification indicates whether the Member is exempt from guild verification requirements.
|
||||
MemberFlagBypassesVerification MemberFlags = 1 << 2
|
||||
// MemberFlagStartedOnboarding indicates whether the Member has started onboarding.
|
||||
MemberFlagStartedOnboarding MemberFlags = 1 << 3
|
||||
)
|
||||
|
||||
// A Member stores user information for Guild members. A guild
|
||||
// member represents a certain user's presence in a guild.
|
||||
type Member struct {
|
||||
@@ -1360,6 +1547,10 @@ type Member struct {
|
||||
// When the user used their Nitro boost on the server
|
||||
PremiumSince *time.Time `json:"premium_since"`
|
||||
|
||||
// The flags of this member. This is a combination of bit masks; the presence of a certain
|
||||
// flag can be checked by performing a bitwise AND between this int and the flag.
|
||||
Flags MemberFlags `json:"flags"`
|
||||
|
||||
// Is true while the member hasn't accepted the membership screen.
|
||||
Pending bool `json:"pending"`
|
||||
|
||||
@@ -1391,6 +1582,15 @@ func (m *Member) AvatarURL(size string) string {
|
||||
|
||||
}
|
||||
|
||||
// DisplayName returns the member's guild nickname if they have one,
|
||||
// otherwise it returns their discord display name.
|
||||
func (m *Member) DisplayName() string {
|
||||
if m.Nick != "" {
|
||||
return m.Nick
|
||||
}
|
||||
return m.User.GlobalName
|
||||
}
|
||||
|
||||
// ClientStatus stores the online, offline, idle, or dnd status of each device of a Guild member.
|
||||
type ClientStatus struct {
|
||||
Desktop Status `json:"desktop"`
|
||||
@@ -1738,14 +1938,18 @@ const (
|
||||
// AuditLogOptions optional data for the AuditLog
|
||||
// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info
|
||||
type AuditLogOptions struct {
|
||||
DeleteMemberDays string `json:"delete_member_days"`
|
||||
MembersRemoved string `json:"members_removed"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
MessageID string `json:"message_id"`
|
||||
Count string `json:"count"`
|
||||
ID string `json:"id"`
|
||||
Type *AuditLogOptionsType `json:"type"`
|
||||
RoleName string `json:"role_name"`
|
||||
DeleteMemberDays string `json:"delete_member_days"`
|
||||
MembersRemoved string `json:"members_removed"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
MessageID string `json:"message_id"`
|
||||
Count string `json:"count"`
|
||||
ID string `json:"id"`
|
||||
Type *AuditLogOptionsType `json:"type"`
|
||||
RoleName string `json:"role_name"`
|
||||
ApplicationID string `json:"application_id"`
|
||||
AutoModerationRuleName string `json:"auto_moderation_rule_name"`
|
||||
AutoModerationRuleTriggerType string `json:"auto_moderation_rule_trigger_type"`
|
||||
IntegrationType string `json:"integration_type"`
|
||||
}
|
||||
|
||||
// AuditLogOptionsType of the AuditLogOption
|
||||
@@ -1754,8 +1958,8 @@ type AuditLogOptionsType string
|
||||
|
||||
// Valid Types for AuditLogOptionsType
|
||||
const (
|
||||
AuditLogOptionsTypeMember AuditLogOptionsType = "member"
|
||||
AuditLogOptionsTypeRole AuditLogOptionsType = "role"
|
||||
AuditLogOptionsTypeRole AuditLogOptionsType = "0"
|
||||
AuditLogOptionsTypeMember AuditLogOptionsType = "1"
|
||||
)
|
||||
|
||||
// AuditLogAction is the Action of the AuditLog (see AuditLogAction* consts)
|
||||
@@ -1816,7 +2020,7 @@ const (
|
||||
AuditLogActionStickerDelete AuditLogAction = 92
|
||||
|
||||
AuditLogGuildScheduledEventCreate AuditLogAction = 100
|
||||
AuditLogGuildScheduledEventUpdare AuditLogAction = 101
|
||||
AuditLogGuildScheduledEventUpdate AuditLogAction = 101
|
||||
AuditLogGuildScheduledEventDelete AuditLogAction = 102
|
||||
|
||||
AuditLogActionThreadCreate AuditLogAction = 110
|
||||
@@ -1824,6 +2028,16 @@ const (
|
||||
AuditLogActionThreadDelete AuditLogAction = 112
|
||||
|
||||
AuditLogActionApplicationCommandPermissionUpdate AuditLogAction = 121
|
||||
|
||||
AuditLogActionAutoModerationRuleCreate AuditLogAction = 140
|
||||
AuditLogActionAutoModerationRuleUpdate AuditLogAction = 141
|
||||
AuditLogActionAutoModerationRuleDelete AuditLogAction = 142
|
||||
AuditLogActionAutoModerationBlockMessage AuditLogAction = 143
|
||||
AuditLogActionAutoModerationFlagToChannel AuditLogAction = 144
|
||||
AuditLogActionAutoModerationUserCommunicationDisabled AuditLogAction = 145
|
||||
|
||||
AuditLogActionCreatorMonetizationRequestCreated AuditLogAction = 150
|
||||
AuditLogActionCreatorMonetizationTermsAccepted AuditLogAction = 151
|
||||
)
|
||||
|
||||
// GuildMemberParams stores data needed to update a member
|
||||
@@ -2341,6 +2555,9 @@ const (
|
||||
|
||||
ErrCodeCannotUpdateAFinishedEvent = 180000
|
||||
ErrCodeFailedToCreateStageNeededForStageEvent = 180002
|
||||
|
||||
ErrCodeCannotEnableOnboardingRequirementsAreNotMet = 350000
|
||||
ErrCodeCannotUpdateOnboardingWhileBelowRequirements = 350001
|
||||
)
|
||||
|
||||
// Intent is the type of a Gateway Intent
|
||||
@@ -2351,7 +2568,7 @@ type Intent int
|
||||
const (
|
||||
IntentGuilds Intent = 1 << 0
|
||||
IntentGuildMembers Intent = 1 << 1
|
||||
IntentGuildBans Intent = 1 << 2
|
||||
IntentGuildModeration Intent = 1 << 2
|
||||
IntentGuildEmojis Intent = 1 << 3
|
||||
IntentGuildIntegrations Intent = 1 << 4
|
||||
IntentGuildWebhooks Intent = 1 << 5
|
||||
@@ -2371,6 +2588,8 @@ const (
|
||||
|
||||
// TODO: remove when compatibility is not needed
|
||||
|
||||
IntentGuildBans Intent = IntentGuildModeration
|
||||
|
||||
IntentsGuilds Intent = 1 << 0
|
||||
IntentsGuildMembers Intent = 1 << 1
|
||||
IntentsGuildBans Intent = 1 << 2
|
||||
|
||||
63
vendor/github.com/bwmarrin/discordgo/user.go
generated
vendored
63
vendor/github.com/bwmarrin/discordgo/user.go
generated
vendored
@@ -1,5 +1,9 @@
|
||||
package discordgo
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// UserFlags is the flags of "user" (see UserFlags* consts)
|
||||
// https://discord.com/developers/docs/resources/user#user-object-user-flags
|
||||
type UserFlags int
|
||||
@@ -20,6 +24,20 @@ const (
|
||||
UserFlagVerifiedBot UserFlags = 1 << 16
|
||||
UserFlagVerifiedBotDeveloper UserFlags = 1 << 17
|
||||
UserFlagDiscordCertifiedModerator UserFlags = 1 << 18
|
||||
UserFlagBotHTTPInteractions UserFlags = 1 << 19
|
||||
UserFlagActiveBotDeveloper UserFlags = 1 << 22
|
||||
)
|
||||
|
||||
// UserPremiumType is the type of premium (nitro) subscription a user has (see UserPremiumType* consts).
|
||||
// https://discord.com/developers/docs/resources/user#user-object-premium-types
|
||||
type UserPremiumType int
|
||||
|
||||
// Valid UserPremiumType values.
|
||||
const (
|
||||
UserPremiumTypeNone UserPremiumType = 0
|
||||
UserPremiumTypeNitroClassic UserPremiumType = 1
|
||||
UserPremiumTypeNitro UserPremiumType = 2
|
||||
UserPremiumTypeNitroBasic UserPremiumType = 3
|
||||
)
|
||||
|
||||
// A User stores all data for an individual Discord user.
|
||||
@@ -44,6 +62,10 @@ type User struct {
|
||||
// The discriminator of the user (4 numbers after name).
|
||||
Discriminator string `json:"discriminator"`
|
||||
|
||||
// The user's display name, if it is set.
|
||||
// For bots, this is the application name.
|
||||
GlobalName string `json:"global_name"`
|
||||
|
||||
// The token of the user. This is only present for
|
||||
// the user represented by the current session.
|
||||
Token string `json:"token"`
|
||||
@@ -70,7 +92,7 @@ type User struct {
|
||||
|
||||
// The type of Nitro subscription on a user's account.
|
||||
// Only available when the request is authorized via a Bearer token.
|
||||
PremiumType int `json:"premium_type"`
|
||||
PremiumType UserPremiumType `json:"premium_type"`
|
||||
|
||||
// Whether the user is an Official Discord System user (part of the urgent message system).
|
||||
System bool `json:"system"`
|
||||
@@ -81,7 +103,14 @@ type User struct {
|
||||
}
|
||||
|
||||
// String returns a unique identifier of the form username#discriminator
|
||||
// or just username, if the discriminator is set to "0".
|
||||
func (u *User) String() string {
|
||||
// If the user has been migrated from the legacy username system, their discriminator is "0".
|
||||
// See https://support-dev.discord.com/hc/en-us/articles/13667755828631
|
||||
if u.Discriminator == "0" {
|
||||
return u.Username
|
||||
}
|
||||
|
||||
return u.Username + "#" + u.Discriminator
|
||||
}
|
||||
|
||||
@@ -91,17 +120,35 @@ func (u *User) Mention() string {
|
||||
}
|
||||
|
||||
// AvatarURL returns a URL to the user'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 (u *User) AvatarURL(size string) string {
|
||||
return avatarURL(u.Avatar, EndpointDefaultUserAvatar(u.Discriminator),
|
||||
EndpointUserAvatar(u.ID, u.Avatar), EndpointUserAvatarAnimated(u.ID, u.Avatar), size)
|
||||
return avatarURL(
|
||||
u.Avatar,
|
||||
EndpointDefaultUserAvatar(u.DefaultAvatarIndex()),
|
||||
EndpointUserAvatar(u.ID, u.Avatar),
|
||||
EndpointUserAvatarAnimated(u.ID, u.Avatar),
|
||||
size,
|
||||
)
|
||||
}
|
||||
|
||||
// BannerURL returns the URL of the users's banner image.
|
||||
// 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.
|
||||
//
|
||||
// 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 (u *User) BannerURL(size string) string {
|
||||
return bannerURL(u.Banner, EndpointUserBanner(u.ID, u.Banner), EndpointUserBannerAnimated(u.ID, u.Banner), size)
|
||||
}
|
||||
|
||||
// DefaultAvatarIndex returns the index of the user's default avatar.
|
||||
func (u *User) DefaultAvatarIndex() int {
|
||||
if u.Discriminator == "0" {
|
||||
id, _ := strconv.ParseUint(u.ID, 10, 64)
|
||||
return int((id >> 22) % 6)
|
||||
}
|
||||
|
||||
id, _ := strconv.Atoi(u.Discriminator)
|
||||
return id % 5
|
||||
}
|
||||
|
||||
6
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
6
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
@@ -76,7 +76,7 @@ type VoiceSpeakingUpdateHandler func(vc *VoiceConnection, vs *VoiceSpeakingUpdat
|
||||
// Speaking sends a speaking notification to Discord over the voice websocket.
|
||||
// This must be sent as true prior to sending audio and should be set to false
|
||||
// once finished sending audio.
|
||||
// b : Send true if speaking, false if not.
|
||||
// b : Send true if speaking, false if not.
|
||||
func (v *VoiceConnection) Speaking(b bool) (err error) {
|
||||
|
||||
v.log(LogDebug, "called (%t)", b)
|
||||
@@ -294,11 +294,15 @@ func (v *VoiceConnection) open() (err error) {
|
||||
if v.sessionID != "" {
|
||||
break
|
||||
}
|
||||
|
||||
if i > 20 { // only loop for up to 1 second total
|
||||
return fmt.Errorf("did not receive voice Session ID in time")
|
||||
}
|
||||
// Release the lock, so sessionID can be populated upon receiving a VoiceStateUpdate event.
|
||||
v.Unlock()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
i++
|
||||
v.Lock()
|
||||
}
|
||||
|
||||
// Connect to VoiceConnection Websocket
|
||||
|
||||
5
vendor/github.com/bwmarrin/discordgo/webhook.go
generated
vendored
5
vendor/github.com/bwmarrin/discordgo/webhook.go
generated
vendored
@@ -34,10 +34,14 @@ type WebhookParams struct {
|
||||
Files []*File `json:"-"`
|
||||
Components []MessageComponent `json:"components"`
|
||||
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||
Attachments []*MessageAttachment `json:"attachments,omitempty"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
// Only MessageFlagsSuppressEmbeds and MessageFlagsEphemeral can be set.
|
||||
// MessageFlagsEphemeral can only be set when using Followup Message Create endpoint.
|
||||
Flags MessageFlags `json:"flags,omitempty"`
|
||||
// Name of the thread to create.
|
||||
// NOTE: can only be set if the webhook channel is a forum.
|
||||
ThreadName string `json:"thread_name,omitempty"`
|
||||
}
|
||||
|
||||
// WebhookEdit stores data for editing of a webhook message.
|
||||
@@ -46,5 +50,6 @@ type WebhookEdit struct {
|
||||
Components *[]MessageComponent `json:"components,omitempty"`
|
||||
Embeds *[]*MessageEmbed `json:"embeds,omitempty"`
|
||||
Files []*File `json:"-"`
|
||||
Attachments *[]*MessageAttachment `json:"attachments,omitempty"`
|
||||
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||
}
|
||||
|
||||
39
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
39
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
@@ -389,6 +389,26 @@ func (s *Session) UpdateListeningStatus(name string) (err error) {
|
||||
return s.UpdateStatusComplex(*newUpdateStatusData(0, ActivityTypeListening, name, ""))
|
||||
}
|
||||
|
||||
// UpdateCustomStatus is used to update the user's custom status.
|
||||
// If state!="" then set the custom status.
|
||||
// Else, set user to active and remove the custom status.
|
||||
func (s *Session) UpdateCustomStatus(state string) (err error) {
|
||||
data := UpdateStatusData{
|
||||
Status: "online",
|
||||
}
|
||||
|
||||
if state != "" {
|
||||
// Discord requires a non-empty activity name, therefore we provide "Custom Status" as a placeholder.
|
||||
data.Activities = []*Activity{{
|
||||
Name: "Custom Status",
|
||||
Type: ActivityTypeCustom,
|
||||
State: state,
|
||||
}}
|
||||
}
|
||||
|
||||
return s.UpdateStatusComplex(data)
|
||||
}
|
||||
|
||||
// UpdateStatusComplex allows for sending the raw status update data untouched by discordgo.
|
||||
func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) {
|
||||
// The comment does say "untouched by discordgo", but we might need to lie a bit here.
|
||||
@@ -862,17 +882,18 @@ func (s *Session) reconnect() {
|
||||
// However, there seems to be cases where something "weird"
|
||||
// happens. So we're doing this for now just to improve
|
||||
// stability in those edge cases.
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
for _, v := range s.VoiceConnections {
|
||||
if s.ShouldReconnectVoiceOnSessionError {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
for _, v := range s.VoiceConnections {
|
||||
|
||||
s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID)
|
||||
go v.reconnect()
|
||||
|
||||
// This is here just to prevent violently spamming the
|
||||
// voice reconnects
|
||||
time.Sleep(1 * time.Second)
|
||||
s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID)
|
||||
go v.reconnect()
|
||||
|
||||
// This is here just to prevent violently spamming the
|
||||
// voice reconnects
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
4
vendor/github.com/d5/tengo/v2/.gitignore
generated
vendored
4
vendor/github.com/d5/tengo/v2/.gitignore
generated
vendored
@@ -1 +1,3 @@
|
||||
dist/
|
||||
dist/
|
||||
|
||||
.idea
|
||||
6
vendor/github.com/d5/tengo/v2/instructions.go
generated
vendored
6
vendor/github.com/d5/tengo/v2/instructions.go
generated
vendored
@@ -28,6 +28,12 @@ func MakeInstruction(opcode parser.Opcode, operands ...int) []byte {
|
||||
n := uint16(o)
|
||||
instruction[offset] = byte(n >> 8)
|
||||
instruction[offset+1] = byte(n)
|
||||
case 4:
|
||||
n := uint32(o)
|
||||
instruction[offset] = byte(n >> 24)
|
||||
instruction[offset+1] = byte(n >> 16)
|
||||
instruction[offset+2] = byte(n >> 8)
|
||||
instruction[offset+3] = byte(n)
|
||||
}
|
||||
offset += width
|
||||
}
|
||||
|
||||
2
vendor/github.com/d5/tengo/v2/parser/expr.go
generated
vendored
2
vendor/github.com/d5/tengo/v2/parser/expr.go
generated
vendored
@@ -351,7 +351,7 @@ func (e *ImportExpr) End() Pos {
|
||||
}
|
||||
|
||||
func (e *ImportExpr) String() string {
|
||||
return `import("` + e.ModuleName + `")"`
|
||||
return `import("` + e.ModuleName + `")`
|
||||
}
|
||||
|
||||
// IndexExpr represents an index expression.
|
||||
|
||||
10
vendor/github.com/d5/tengo/v2/parser/opcodes.go
generated
vendored
10
vendor/github.com/d5/tengo/v2/parser/opcodes.go
generated
vendored
@@ -106,10 +106,10 @@ var OpcodeOperands = [...][]int{
|
||||
OpNotEqual: {},
|
||||
OpMinus: {},
|
||||
OpLNot: {},
|
||||
OpJumpFalsy: {2},
|
||||
OpAndJump: {2},
|
||||
OpOrJump: {2},
|
||||
OpJump: {2},
|
||||
OpJumpFalsy: {4},
|
||||
OpAndJump: {4},
|
||||
OpOrJump: {4},
|
||||
OpJump: {4},
|
||||
OpNull: {},
|
||||
OpGetGlobal: {2},
|
||||
OpSetGlobal: {2},
|
||||
@@ -149,6 +149,8 @@ func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) {
|
||||
operands = append(operands, int(ins[offset]))
|
||||
case 2:
|
||||
operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8)
|
||||
case 4:
|
||||
operands = append(operands, int(ins[offset+3])|int(ins[offset+2])<<8|int(ins[offset+1])<<16|int(ins[offset])<<24)
|
||||
}
|
||||
offset += width
|
||||
}
|
||||
|
||||
3
vendor/github.com/d5/tengo/v2/stdlib/os.go
generated
vendored
3
vendor/github.com/d5/tengo/v2/stdlib/os.go
generated
vendored
@@ -6,11 +6,14 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/d5/tengo/v2"
|
||||
)
|
||||
|
||||
var osModule = map[string]tengo.Object{
|
||||
"platform": &tengo.String{Value: runtime.GOOS},
|
||||
"arch": &tengo.String{Value: runtime.GOARCH},
|
||||
"o_rdonly": &tengo.Int{Value: int64(os.O_RDONLY)},
|
||||
"o_wronly": &tengo.Int{Value: int64(os.O_WRONLY)},
|
||||
"o_rdwr": &tengo.Int{Value: int64(os.O_RDWR)},
|
||||
|
||||
17
vendor/github.com/d5/tengo/v2/vm.go
generated
vendored
17
vendor/github.com/d5/tengo/v2/vm.go
generated
vendored
@@ -218,30 +218,30 @@ func (v *VM) run() {
|
||||
return
|
||||
}
|
||||
case parser.OpJumpFalsy:
|
||||
v.ip += 2
|
||||
v.ip += 4
|
||||
v.sp--
|
||||
if v.stack[v.sp].IsFalsy() {
|
||||
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
|
||||
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24
|
||||
v.ip = pos - 1
|
||||
}
|
||||
case parser.OpAndJump:
|
||||
v.ip += 2
|
||||
v.ip += 4
|
||||
if v.stack[v.sp-1].IsFalsy() {
|
||||
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
|
||||
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24
|
||||
v.ip = pos - 1
|
||||
} else {
|
||||
v.sp--
|
||||
}
|
||||
case parser.OpOrJump:
|
||||
v.ip += 2
|
||||
v.ip += 4
|
||||
if v.stack[v.sp-1].IsFalsy() {
|
||||
v.sp--
|
||||
} else {
|
||||
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
|
||||
pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 | int(v.curInsts[v.ip-2])<<16 | int(v.curInsts[v.ip-3])<<24
|
||||
v.ip = pos - 1
|
||||
}
|
||||
case parser.OpJump:
|
||||
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
||||
pos := int(v.curInsts[v.ip+4]) | int(v.curInsts[v.ip+3])<<8 | int(v.curInsts[v.ip+2])<<16 | int(v.curInsts[v.ip+1])<<24
|
||||
v.ip = pos - 1
|
||||
case parser.OpSetGlobal:
|
||||
v.ip += 2
|
||||
@@ -534,6 +534,9 @@ func (v *VM) run() {
|
||||
}
|
||||
v.stack[v.sp] = val
|
||||
v.sp++
|
||||
default:
|
||||
v.err = fmt.Errorf("not indexable: %s", left.TypeName())
|
||||
return
|
||||
}
|
||||
case parser.OpCall:
|
||||
numArgs := int(v.curInsts[v.ip+1])
|
||||
|
||||
301
vendor/github.com/dyatlov/go-opengraph/opengraph/opengraph.go
generated
vendored
301
vendor/github.com/dyatlov/go-opengraph/opengraph/opengraph.go
generated
vendored
@@ -8,81 +8,37 @@ import (
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/actor"
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/article"
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/audio"
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/book"
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/image"
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/music"
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/profile"
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/video"
|
||||
)
|
||||
|
||||
// Image defines Open Graph Image type
|
||||
type Image struct {
|
||||
URL string `json:"url"`
|
||||
SecureURL string `json:"secure_url"`
|
||||
Type string `json:"type"`
|
||||
Width uint64 `json:"width"`
|
||||
Height uint64 `json:"height"`
|
||||
draft bool `json:"-"`
|
||||
}
|
||||
|
||||
// Video defines Open Graph Video type
|
||||
type Video struct {
|
||||
URL string `json:"url"`
|
||||
SecureURL string `json:"secure_url"`
|
||||
Type string `json:"type"`
|
||||
Width uint64 `json:"width"`
|
||||
Height uint64 `json:"height"`
|
||||
draft bool `json:"-"`
|
||||
}
|
||||
|
||||
// Audio defines Open Graph Audio Type
|
||||
type Audio struct {
|
||||
URL string `json:"url"`
|
||||
SecureURL string `json:"secure_url"`
|
||||
Type string `json:"type"`
|
||||
draft bool `json:"-"`
|
||||
}
|
||||
|
||||
// Article contain Open Graph Article structure
|
||||
type Article struct {
|
||||
PublishedTime *time.Time `json:"published_time"`
|
||||
ModifiedTime *time.Time `json:"modified_time"`
|
||||
ExpirationTime *time.Time `json:"expiration_time"`
|
||||
Section string `json:"section"`
|
||||
Tags []string `json:"tags"`
|
||||
Authors []*Profile `json:"authors"`
|
||||
}
|
||||
|
||||
// Profile contains Open Graph Profile structure
|
||||
type Profile struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Username string `json:"username"`
|
||||
Gender string `json:"gender"`
|
||||
}
|
||||
|
||||
// Book contains Open Graph Book structure
|
||||
type Book struct {
|
||||
ISBN string `json:"isbn"`
|
||||
ReleaseDate *time.Time `json:"release_date"`
|
||||
Tags []string `json:"tags"`
|
||||
Authors []*Profile `json:"authors"`
|
||||
}
|
||||
|
||||
// OpenGraph contains facebook og data
|
||||
type OpenGraph struct {
|
||||
isArticle bool
|
||||
isBook bool
|
||||
isProfile bool
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Determiner string `json:"determiner"`
|
||||
SiteName string `json:"site_name"`
|
||||
Locale string `json:"locale"`
|
||||
LocalesAlternate []string `json:"locales_alternate"`
|
||||
Images []*Image `json:"images"`
|
||||
Audios []*Audio `json:"audios"`
|
||||
Videos []*Video `json:"videos"`
|
||||
Article *Article `json:"article,omitempty"`
|
||||
Book *Book `json:"book,omitempty"`
|
||||
Profile *Profile `json:"profile,omitempty"`
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Determiner string `json:"determiner"`
|
||||
SiteName string `json:"site_name"`
|
||||
Locale string `json:"locale"`
|
||||
LocalesAlternate []string `json:"locales_alternate"`
|
||||
Images []*image.Image `json:"images"`
|
||||
Audios []*audio.Audio `json:"audios"`
|
||||
Videos []*video.Video `json:"videos"`
|
||||
Article *article.Article `json:"article,omitempty"`
|
||||
Book *book.Book `json:"book,omitempty"`
|
||||
Profile *profile.Profile `json:"profile,omitempty"`
|
||||
Music *music.Music `json:"music,omitempty"`
|
||||
}
|
||||
|
||||
// NewOpenGraph returns new instance of Open Graph structure
|
||||
@@ -137,21 +93,13 @@ func (og *OpenGraph) ensureHasVideo() {
|
||||
if len(og.Videos) > 0 {
|
||||
return
|
||||
}
|
||||
og.Videos = append(og.Videos, &Video{draft: true})
|
||||
og.Videos = append(og.Videos, video.NewVideo())
|
||||
}
|
||||
|
||||
func (og *OpenGraph) ensureHasImage() {
|
||||
if len(og.Images) > 0 {
|
||||
return
|
||||
func (og *OpenGraph) ensureHasMusic() {
|
||||
if og.Music == nil {
|
||||
og.Music = music.NewMusic()
|
||||
}
|
||||
og.Images = append(og.Images, &Image{draft: true})
|
||||
}
|
||||
|
||||
func (og *OpenGraph) ensureHasAudio() {
|
||||
if len(og.Audios) > 0 {
|
||||
return
|
||||
}
|
||||
og.Audios = append(og.Audios, &Audio{draft: true})
|
||||
}
|
||||
|
||||
// ProcessMeta processes meta attributes and adds them to Open Graph structure if they are suitable for that
|
||||
@@ -182,73 +130,110 @@ func (og *OpenGraph) ProcessMeta(metaAttrs map[string]string) {
|
||||
case "og:locale:alternate":
|
||||
og.LocalesAlternate = append(og.LocalesAlternate, metaAttrs["content"])
|
||||
case "og:audio":
|
||||
if len(og.Audios)>0 && og.Audios[len(og.Audios)-1].draft {
|
||||
og.Audios[len(og.Audios)-1].URL = metaAttrs["content"]
|
||||
og.Audios[len(og.Audios)-1].draft = false
|
||||
} else {
|
||||
og.Audios = append(og.Audios, &Audio{URL: metaAttrs["content"]})
|
||||
}
|
||||
og.Audios = audio.AddUrl(og.Audios, metaAttrs["content"])
|
||||
case "og:audio:secure_url":
|
||||
og.ensureHasAudio()
|
||||
og.Audios[len(og.Audios)-1].SecureURL = metaAttrs["content"]
|
||||
og.Audios = audio.AddSecureUrl(og.Audios, metaAttrs["content"])
|
||||
case "og:audio:type":
|
||||
og.ensureHasAudio()
|
||||
og.Audios[len(og.Audios)-1].Type = metaAttrs["content"]
|
||||
og.Audios = audio.AddType(og.Audios, metaAttrs["content"])
|
||||
case "og:image":
|
||||
if len(og.Images)>0 && og.Images[len(og.Images)-1].draft {
|
||||
og.Images[len(og.Images)-1].URL = metaAttrs["content"]
|
||||
og.Images[len(og.Images)-1].draft = false
|
||||
} else {
|
||||
og.Images = append(og.Images, &Image{URL: metaAttrs["content"]})
|
||||
}
|
||||
og.Images = image.AddURL(og.Images, metaAttrs["content"])
|
||||
case "og:image:url":
|
||||
og.ensureHasImage()
|
||||
og.Images[len(og.Images)-1].URL = metaAttrs["content"]
|
||||
og.Images = image.AddURL(og.Images, metaAttrs["content"])
|
||||
case "og:image:secure_url":
|
||||
og.ensureHasImage()
|
||||
og.Images[len(og.Images)-1].SecureURL = metaAttrs["content"]
|
||||
og.Images = image.AddSecureURL(og.Images, metaAttrs["content"])
|
||||
case "og:image:type":
|
||||
og.ensureHasImage()
|
||||
og.Images[len(og.Images)-1].Type = metaAttrs["content"]
|
||||
og.Images = image.AddType(og.Images, metaAttrs["content"])
|
||||
case "og:image:width":
|
||||
w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||
if err == nil {
|
||||
og.ensureHasImage()
|
||||
og.Images[len(og.Images)-1].Width = w
|
||||
og.Images = image.AddWidth(og.Images, w)
|
||||
}
|
||||
case "og:image:height":
|
||||
h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||
if err == nil {
|
||||
og.ensureHasImage()
|
||||
og.Images[len(og.Images)-1].Height = h
|
||||
og.Images = image.AddHeight(og.Images, h)
|
||||
}
|
||||
case "og:video":
|
||||
if len(og.Videos)>0 && og.Videos[len(og.Videos)-1].draft {
|
||||
og.Videos[len(og.Videos)-1].URL = metaAttrs["content"]
|
||||
og.Videos[len(og.Videos)-1].draft = false
|
||||
} else {
|
||||
og.Videos = append(og.Videos, &Video{URL: metaAttrs["content"]})
|
||||
og.Videos = video.AddURL(og.Videos, metaAttrs["content"])
|
||||
case "og:video:tag":
|
||||
og.Videos = video.AddTag(og.Videos, metaAttrs["content"])
|
||||
case "og:video:duration":
|
||||
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||
og.Videos = video.AddDuration(og.Videos, i)
|
||||
}
|
||||
case "og:video:release_date":
|
||||
if t, err := time.Parse(time.RFC3339, metaAttrs["content"]); err == nil {
|
||||
og.Videos = video.AddReleaseDate(og.Videos, &t)
|
||||
}
|
||||
case "og:video:url":
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].URL = metaAttrs["content"]
|
||||
og.Videos = video.AddURL(og.Videos, metaAttrs["content"])
|
||||
case "og:video:secure_url":
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].SecureURL = metaAttrs["content"]
|
||||
og.Videos = video.AddSecureURL(og.Videos, metaAttrs["content"])
|
||||
case "og:video:type":
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].Type = metaAttrs["content"]
|
||||
og.Videos = video.AddTag(og.Videos, metaAttrs["content"])
|
||||
case "og:video:width":
|
||||
w, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||
if err == nil {
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].Width = w
|
||||
og.Videos = video.AddWidth(og.Videos, w)
|
||||
}
|
||||
case "og:video:height":
|
||||
h, err := strconv.ParseUint(metaAttrs["content"], 10, 64)
|
||||
if err == nil {
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].Height = h
|
||||
og.Videos = video.AddHeight(og.Videos, h)
|
||||
}
|
||||
case "og:video:actor":
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].Actors = actor.AddProfile(og.Videos[len(og.Videos)-1].Actors, metaAttrs["content"])
|
||||
case "og:video:actor:role":
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].Actors = actor.AddRole(og.Videos[len(og.Videos)-1].Actors, metaAttrs["content"])
|
||||
case "og:video:director":
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].Directors = append(og.Videos[len(og.Videos)-1].Directors, metaAttrs["content"])
|
||||
case "og:video:writer":
|
||||
og.ensureHasVideo()
|
||||
og.Videos[len(og.Videos)-1].Writers = append(og.Videos[len(og.Videos)-1].Writers, metaAttrs["content"])
|
||||
case "og:music:duration":
|
||||
og.ensureHasMusic()
|
||||
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||
og.Music.Duration = i
|
||||
}
|
||||
case "og:music:release_date":
|
||||
og.ensureHasMusic()
|
||||
if t, err := time.Parse(time.RFC3339, metaAttrs["content"]); err == nil {
|
||||
og.Music.ReleaseDate = &t
|
||||
}
|
||||
case "og:music:album":
|
||||
og.ensureHasMusic()
|
||||
og.Music.Album.URL = metaAttrs["content"]
|
||||
case "og:music:album:disc":
|
||||
og.ensureHasMusic()
|
||||
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||
og.Music.Album.Disc = i
|
||||
}
|
||||
case "og:music:album:track":
|
||||
og.ensureHasMusic()
|
||||
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||
og.Music.Album.Track = i
|
||||
}
|
||||
case "og:music:musician":
|
||||
og.ensureHasMusic()
|
||||
og.Music.Musicians = append(og.Music.Musicians, metaAttrs["content"])
|
||||
case "og:music:creator":
|
||||
og.ensureHasMusic()
|
||||
og.Music.Creators = append(og.Music.Creators, metaAttrs["content"])
|
||||
case "og:music:song":
|
||||
og.ensureHasMusic()
|
||||
og.Music.AddSongUrl(metaAttrs["content"])
|
||||
case "og:music:disc":
|
||||
og.ensureHasMusic()
|
||||
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||
og.Music.AddSongDisc(i)
|
||||
}
|
||||
case "og:music:track":
|
||||
og.ensureHasMusic()
|
||||
if i, err := strconv.ParseUint(metaAttrs["content"], 10, 64); err == nil {
|
||||
og.Music.AddSongTrack(i)
|
||||
}
|
||||
default:
|
||||
if og.isArticle {
|
||||
@@ -263,100 +248,64 @@ func (og *OpenGraph) ProcessMeta(metaAttrs map[string]string) {
|
||||
|
||||
func (og *OpenGraph) processArticleMeta(metaAttrs map[string]string) {
|
||||
if og.Article == nil {
|
||||
og.Article = &Article{}
|
||||
og.Article = &article.Article{}
|
||||
}
|
||||
switch metaAttrs["property"] {
|
||||
case "article:published_time":
|
||||
case "og:article:published_time":
|
||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||
if err == nil {
|
||||
og.Article.PublishedTime = &t
|
||||
}
|
||||
case "article:modified_time":
|
||||
case "og:article:modified_time":
|
||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||
if err == nil {
|
||||
og.Article.ModifiedTime = &t
|
||||
}
|
||||
case "article:expiration_time":
|
||||
case "og:article:expiration_time":
|
||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||
if err == nil {
|
||||
og.Article.ExpirationTime = &t
|
||||
}
|
||||
case "article:section":
|
||||
case "og:article:section":
|
||||
og.Article.Section = metaAttrs["content"]
|
||||
case "article:tag":
|
||||
case "og:article:tag":
|
||||
og.Article.Tags = append(og.Article.Tags, metaAttrs["content"])
|
||||
case "article:author:first_name":
|
||||
if len(og.Article.Authors) == 0 {
|
||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
||||
}
|
||||
og.Article.Authors[len(og.Article.Authors)-1].FirstName = metaAttrs["content"]
|
||||
case "article:author:last_name":
|
||||
if len(og.Article.Authors) == 0 {
|
||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
||||
}
|
||||
og.Article.Authors[len(og.Article.Authors)-1].LastName = metaAttrs["content"]
|
||||
case "article:author:username":
|
||||
if len(og.Article.Authors) == 0 {
|
||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
||||
}
|
||||
og.Article.Authors[len(og.Article.Authors)-1].Username = metaAttrs["content"]
|
||||
case "article:author:gender":
|
||||
if len(og.Article.Authors) == 0 {
|
||||
og.Article.Authors = append(og.Article.Authors, &Profile{})
|
||||
}
|
||||
og.Article.Authors[len(og.Article.Authors)-1].Gender = metaAttrs["content"]
|
||||
case "og:article:author":
|
||||
og.Article.Authors = append(og.Article.Authors, metaAttrs["content"])
|
||||
}
|
||||
}
|
||||
|
||||
func (og *OpenGraph) processBookMeta(metaAttrs map[string]string) {
|
||||
if og.Book == nil {
|
||||
og.Book = &Book{}
|
||||
og.Book = &book.Book{}
|
||||
}
|
||||
switch metaAttrs["property"] {
|
||||
case "book:release_date":
|
||||
case "og:book:release_date":
|
||||
t, err := time.Parse(time.RFC3339, metaAttrs["content"])
|
||||
if err == nil {
|
||||
og.Book.ReleaseDate = &t
|
||||
}
|
||||
case "book:isbn":
|
||||
case "og:book:isbn":
|
||||
og.Book.ISBN = metaAttrs["content"]
|
||||
case "book:tag":
|
||||
case "og:book:tag":
|
||||
og.Book.Tags = append(og.Book.Tags, metaAttrs["content"])
|
||||
case "book:author:first_name":
|
||||
if len(og.Book.Authors) == 0 {
|
||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
||||
}
|
||||
og.Book.Authors[len(og.Book.Authors)-1].FirstName = metaAttrs["content"]
|
||||
case "book:author:last_name":
|
||||
if len(og.Book.Authors) == 0 {
|
||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
||||
}
|
||||
og.Book.Authors[len(og.Book.Authors)-1].LastName = metaAttrs["content"]
|
||||
case "book:author:username":
|
||||
if len(og.Book.Authors) == 0 {
|
||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
||||
}
|
||||
og.Book.Authors[len(og.Book.Authors)-1].Username = metaAttrs["content"]
|
||||
case "book:author:gender":
|
||||
if len(og.Book.Authors) == 0 {
|
||||
og.Book.Authors = append(og.Book.Authors, &Profile{})
|
||||
}
|
||||
og.Book.Authors[len(og.Book.Authors)-1].Gender = metaAttrs["content"]
|
||||
case "og:book:author":
|
||||
og.Book.Authors = append(og.Book.Authors, metaAttrs["content"])
|
||||
}
|
||||
}
|
||||
|
||||
func (og *OpenGraph) processProfileMeta(metaAttrs map[string]string) {
|
||||
if og.Profile == nil {
|
||||
og.Profile = &Profile{}
|
||||
og.Profile = &profile.Profile{}
|
||||
}
|
||||
switch metaAttrs["property"] {
|
||||
case "profile:first_name":
|
||||
case "og:profile:first_name":
|
||||
og.Profile.FirstName = metaAttrs["content"]
|
||||
case "profile:last_name":
|
||||
case "og:profile:last_name":
|
||||
og.Profile.LastName = metaAttrs["content"]
|
||||
case "profile:username":
|
||||
case "og:profile:username":
|
||||
og.Profile.Username = metaAttrs["content"]
|
||||
case "profile:gender":
|
||||
case "og:profile:gender":
|
||||
og.Profile.Gender = metaAttrs["content"]
|
||||
}
|
||||
}
|
||||
|
||||
27
vendor/github.com/dyatlov/go-opengraph/opengraph/types/actor/actor.go
generated
vendored
Normal file
27
vendor/github.com/dyatlov/go-opengraph/opengraph/types/actor/actor.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package actor
|
||||
|
||||
// Actor contain Open Graph Actor structure
|
||||
type Actor struct {
|
||||
Profile string `json:"profile"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
func NewActor() *Actor {
|
||||
return &Actor{}
|
||||
}
|
||||
|
||||
func AddProfile(actors []*Actor, v string) []*Actor {
|
||||
if len(actors) == 0 || actors[len(actors)-1].Profile != "" {
|
||||
actors = append(actors, &Actor{})
|
||||
}
|
||||
actors[len(actors)-1].Profile = v
|
||||
return actors
|
||||
}
|
||||
|
||||
func AddRole(actors []*Actor, v string) []*Actor {
|
||||
if len(actors) == 0 || actors[len(actors)-1].Role != "" {
|
||||
actors = append(actors, &Actor{})
|
||||
}
|
||||
actors[len(actors)-1].Role = v
|
||||
return actors
|
||||
}
|
||||
15
vendor/github.com/dyatlov/go-opengraph/opengraph/types/article/article.go
generated
vendored
Normal file
15
vendor/github.com/dyatlov/go-opengraph/opengraph/types/article/article.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Article contain Open Graph Article structure
|
||||
type Article struct {
|
||||
PublishedTime *time.Time `json:"published_time"`
|
||||
ModifiedTime *time.Time `json:"modified_time"`
|
||||
ExpirationTime *time.Time `json:"expiration_time"`
|
||||
Section string `json:"section"`
|
||||
Tags []string `json:"tags"`
|
||||
Authors []string `json:"authors"`
|
||||
}
|
||||
36
vendor/github.com/dyatlov/go-opengraph/opengraph/types/audio/audio.go
generated
vendored
Normal file
36
vendor/github.com/dyatlov/go-opengraph/opengraph/types/audio/audio.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package audio
|
||||
|
||||
// Audio defines Open Graph Audio Type
|
||||
type Audio struct {
|
||||
URL string `json:"url"`
|
||||
SecureURL string `json:"secure_url"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func NewAudio() *Audio {
|
||||
return &Audio{}
|
||||
}
|
||||
|
||||
func AddUrl(audios []*Audio, v string) []*Audio {
|
||||
if len(audios) == 0 || audios[len(audios)-1].URL != "" {
|
||||
audios = append(audios, &Audio{})
|
||||
}
|
||||
audios[len(audios)-1].URL = v
|
||||
return audios
|
||||
}
|
||||
|
||||
func AddSecureUrl(audios []*Audio, v string) []*Audio {
|
||||
if len(audios) == 0 || audios[len(audios)-1].SecureURL != "" {
|
||||
audios = append(audios, &Audio{})
|
||||
}
|
||||
audios[len(audios)-1].SecureURL = v
|
||||
return audios
|
||||
}
|
||||
|
||||
func AddType(audios []*Audio, v string) []*Audio {
|
||||
if len(audios) == 0 || audios[len(audios)-1].Type != "" {
|
||||
audios = append(audios, &Audio{})
|
||||
}
|
||||
audios[len(audios)-1].Type = v
|
||||
return audios
|
||||
}
|
||||
13
vendor/github.com/dyatlov/go-opengraph/opengraph/types/book/book.go
generated
vendored
Normal file
13
vendor/github.com/dyatlov/go-opengraph/opengraph/types/book/book.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package book
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Book contains Open Graph Book structure
|
||||
type Book struct {
|
||||
ISBN string `json:"isbn"`
|
||||
ReleaseDate *time.Time `json:"release_date"`
|
||||
Tags []string `json:"tags"`
|
||||
Authors []string `json:"authors"`
|
||||
}
|
||||
53
vendor/github.com/dyatlov/go-opengraph/opengraph/types/image/image.go
generated
vendored
Normal file
53
vendor/github.com/dyatlov/go-opengraph/opengraph/types/image/image.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package image
|
||||
|
||||
// Image defines Open Graph Image type
|
||||
type Image struct {
|
||||
URL string `json:"url"`
|
||||
SecureURL string `json:"secure_url"`
|
||||
Type string `json:"type"`
|
||||
Width uint64 `json:"width"`
|
||||
Height uint64 `json:"height"`
|
||||
}
|
||||
|
||||
func NewImage() *Image {
|
||||
return &Image{}
|
||||
}
|
||||
|
||||
func ensureHasImage(images []*Image) []*Image {
|
||||
if len(images) == 0 {
|
||||
images = append(images, NewImage())
|
||||
}
|
||||
return images
|
||||
}
|
||||
|
||||
func AddURL(images []*Image, v string) []*Image {
|
||||
if len(images) == 0 || (images[len(images)-1].URL != "" && images[len(images)-1].URL != v) {
|
||||
images = append(images, NewImage())
|
||||
}
|
||||
images[len(images)-1].URL = v
|
||||
return images
|
||||
}
|
||||
|
||||
func AddSecureURL(images []*Image, v string) []*Image {
|
||||
images = ensureHasImage(images)
|
||||
images[len(images)-1].SecureURL = v
|
||||
return images
|
||||
}
|
||||
|
||||
func AddType(images []*Image, v string) []*Image {
|
||||
images = ensureHasImage(images)
|
||||
images[len(images)-1].Type = v
|
||||
return images
|
||||
}
|
||||
|
||||
func AddWidth(images []*Image, v uint64) []*Image {
|
||||
images = ensureHasImage(images)
|
||||
images[len(images)-1].Width = v
|
||||
return images
|
||||
}
|
||||
|
||||
func AddHeight(images []*Image, v uint64) []*Image {
|
||||
images = ensureHasImage(images)
|
||||
images[len(images)-1].Height = v
|
||||
return images
|
||||
}
|
||||
52
vendor/github.com/dyatlov/go-opengraph/opengraph/types/music/music.go
generated
vendored
Normal file
52
vendor/github.com/dyatlov/go-opengraph/opengraph/types/music/music.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package music
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Music defines Open Graph Music type
|
||||
type Music struct {
|
||||
Musicians []string `json:"musicians,omitempty"`
|
||||
Creators []string `json:"creators,omitempty"`
|
||||
Duration uint64 `json:"duration,omitempty"`
|
||||
ReleaseDate *time.Time `json:"release_date,omitempty"`
|
||||
Album *Album `json:"album"`
|
||||
Songs []*Song `json:"songs"`
|
||||
}
|
||||
|
||||
type Album struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Disc uint64 `json:"disc,omitempty"`
|
||||
Track uint64 `json:"track,omitempty"`
|
||||
}
|
||||
|
||||
type Song struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Disc uint64 `json:"disc,omitempty"`
|
||||
Track uint64 `json:"track,omitempty"`
|
||||
}
|
||||
|
||||
func NewMusic() *Music {
|
||||
return &Music{Album: &Album{}}
|
||||
}
|
||||
|
||||
func (m *Music) AddSongUrl(v string) {
|
||||
if len(m.Songs) == 0 || m.Songs[len(m.Songs)-1].URL != "" {
|
||||
m.Songs = append(m.Songs, &Song{})
|
||||
}
|
||||
m.Songs[len(m.Songs)-1].URL = v
|
||||
}
|
||||
|
||||
func (m *Music) AddSongDisc(v uint64) {
|
||||
if len(m.Songs) == 0 {
|
||||
m.Songs = append(m.Songs, &Song{})
|
||||
}
|
||||
m.Songs[len(m.Songs)-1].Disc = v
|
||||
}
|
||||
|
||||
func (m *Music) AddSongTrack(v uint64) {
|
||||
if len(m.Songs) == 0 {
|
||||
m.Songs = append(m.Songs, &Song{})
|
||||
}
|
||||
m.Songs[len(m.Songs)-1].Track = v
|
||||
}
|
||||
59
vendor/github.com/dyatlov/go-opengraph/opengraph/types/profile/profile.go
generated
vendored
Normal file
59
vendor/github.com/dyatlov/go-opengraph/opengraph/types/profile/profile.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package profile
|
||||
|
||||
import "strings"
|
||||
|
||||
// Profile contain Open Graph Profile structure
|
||||
type Profile struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Username string `json:"username"`
|
||||
Gender string `json:"gender"`
|
||||
}
|
||||
|
||||
func NewProfile() *Profile {
|
||||
return &Profile{}
|
||||
}
|
||||
|
||||
func AddBasicProfile(profiles []*Profile, v string) []*Profile {
|
||||
parts := strings.SplitN(v, " ", 2)
|
||||
if len(profiles) == 0 || profiles[len(profiles)-1].FirstName != "" {
|
||||
profiles = append(profiles, &Profile{})
|
||||
}
|
||||
profiles[len(profiles)-1].FirstName = parts[0]
|
||||
if len(parts) > 1 {
|
||||
profiles[len(profiles)-1].LastName = parts[1]
|
||||
}
|
||||
return profiles
|
||||
}
|
||||
|
||||
func AddFirstName(profiles []*Profile, v string) []*Profile {
|
||||
if len(profiles) == 0 || profiles[len(profiles)-1].FirstName != "" {
|
||||
profiles = append(profiles, &Profile{})
|
||||
}
|
||||
profiles[len(profiles)-1].FirstName = v
|
||||
return profiles
|
||||
}
|
||||
|
||||
func AddLastName(profiles []*Profile, v string) []*Profile {
|
||||
if len(profiles) == 0 || profiles[len(profiles)-1].LastName != "" {
|
||||
profiles = append(profiles, &Profile{})
|
||||
}
|
||||
profiles[len(profiles)-1].LastName = v
|
||||
return profiles
|
||||
}
|
||||
|
||||
func AddUsername(profiles []*Profile, v string) []*Profile {
|
||||
if len(profiles) == 0 || profiles[len(profiles)-1].Username != "" {
|
||||
profiles = append(profiles, &Profile{})
|
||||
}
|
||||
profiles[len(profiles)-1].Username = v
|
||||
return profiles
|
||||
}
|
||||
|
||||
func AddGender(profiles []*Profile, v string) []*Profile {
|
||||
if len(profiles) == 0 || profiles[len(profiles)-1].Gender != "" {
|
||||
profiles = append(profiles, &Profile{})
|
||||
}
|
||||
profiles[len(profiles)-1].Gender = v
|
||||
return profiles
|
||||
}
|
||||
83
vendor/github.com/dyatlov/go-opengraph/opengraph/types/video/video.go
generated
vendored
Normal file
83
vendor/github.com/dyatlov/go-opengraph/opengraph/types/video/video.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package video
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dyatlov/go-opengraph/opengraph/types/actor"
|
||||
)
|
||||
|
||||
// Video defines Open Graph Video type
|
||||
type Video struct {
|
||||
URL string `json:"url"`
|
||||
SecureURL string `json:"secure_url"`
|
||||
Type string `json:"type"`
|
||||
Width uint64 `json:"width"`
|
||||
Height uint64 `json:"height"`
|
||||
Actors []*actor.Actor `json:"actors,omitempty"`
|
||||
Directors []string `json:"directors,omitempty"`
|
||||
Writers []string `json:"writers,omitempty"`
|
||||
Duration uint64 `json:"duration,omitempty"`
|
||||
ReleaseDate *time.Time `json:"release_date,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
func NewVideo() *Video {
|
||||
return &Video{}
|
||||
}
|
||||
|
||||
func ensureHasVideo(videos []*Video) []*Video {
|
||||
if len(videos) == 0 {
|
||||
videos = append(videos, NewVideo())
|
||||
}
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddURL(videos []*Video, v string) []*Video {
|
||||
if len(videos) == 0 || (videos[len(videos)-1].URL != "" && videos[len(videos)-1].URL != v) {
|
||||
videos = append(videos, NewVideo())
|
||||
}
|
||||
videos[len(videos)-1].URL = v
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddTag(videos []*Video, v string) []*Video {
|
||||
videos = ensureHasVideo(videos)
|
||||
videos[len(videos)-1].Tags = append(videos[len(videos)-1].Tags, v)
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddDuration(videos []*Video, v uint64) []*Video {
|
||||
videos = ensureHasVideo(videos)
|
||||
videos[len(videos)-1].Duration = v
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddReleaseDate(videos []*Video, v *time.Time) []*Video {
|
||||
videos = ensureHasVideo(videos)
|
||||
videos[len(videos)-1].ReleaseDate = v
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddSecureURL(videos []*Video, v string) []*Video {
|
||||
videos = ensureHasVideo(videos)
|
||||
videos[len(videos)-1].SecureURL = v
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddType(videos []*Video, v string) []*Video {
|
||||
videos = ensureHasVideo(videos)
|
||||
videos[len(videos)-1].Type = v
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddWidth(videos []*Video, v uint64) []*Video {
|
||||
videos = ensureHasVideo(videos)
|
||||
videos[len(videos)-1].Width = v
|
||||
return videos
|
||||
}
|
||||
|
||||
func AddHeight(videos []*Video, v uint64) []*Video {
|
||||
videos = ensureHasVideo(videos)
|
||||
videos[len(videos)-1].Height = v
|
||||
return videos
|
||||
}
|
||||
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Fatih Arslan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
176
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
176
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
# color [](https://github.com/fatih/color/actions) [](https://pkg.go.dev/github.com/fatih/color)
|
||||
|
||||
Color lets you use colorized outputs in terms of [ANSI Escape
|
||||
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
||||
has support for Windows too! The API can be used in several ways, pick one that
|
||||
suits you.
|
||||
|
||||

|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/color
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Standard colors
|
||||
|
||||
```go
|
||||
// Print with default helper functions
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// A newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// These are using the default foreground colors
|
||||
color.Red("We have red")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
```
|
||||
|
||||
### Mix and reuse colors
|
||||
|
||||
```go
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with white background.")
|
||||
```
|
||||
|
||||
### Use your own output (io.Writer)
|
||||
|
||||
```go
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(writer, "This will print text in blue.")
|
||||
```
|
||||
|
||||
### Custom print functions (PrintFunc)
|
||||
|
||||
```go
|
||||
// Create a custom print function for convenience
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("Warning")
|
||||
red("Error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("Don't forget this...")
|
||||
```
|
||||
|
||||
### Custom fprint functions (FprintFunc)
|
||||
|
||||
```go
|
||||
blue := color.New(color.FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, "Don't forget this...")
|
||||
```
|
||||
|
||||
### Insert into noncolor strings (SprintFunc)
|
||||
|
||||
```go
|
||||
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
red := color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||
fmt.Printf("This %s rocks!\n", info("package"))
|
||||
|
||||
// Use helper functions
|
||||
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||
|
||||
// Windows supported too! Just don't forget to change the output to color.Output
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
```
|
||||
|
||||
### Plug into existing code
|
||||
|
||||
```go
|
||||
// Use handy standard colors
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will now be in yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // Don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // Use it in your function
|
||||
|
||||
fmt.Println("All text will now be bold magenta.")
|
||||
```
|
||||
|
||||
### Disable/Enable color
|
||||
|
||||
There might be a case where you want to explicitly disable/enable color output. the
|
||||
`go-isatty` package will automatically disable color output for non-tty output streams
|
||||
(for example if the output were piped directly to `less`).
|
||||
|
||||
The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment
|
||||
variable is set to a non-empty string.
|
||||
|
||||
`Color` has support to disable/enable colors programmatically both globally and
|
||||
for single color definitions. For example suppose you have a CLI app and a
|
||||
`-no-color` bool flag. You can easily disable the color output with:
|
||||
|
||||
```go
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
```
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
```go
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
```
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams.
|
||||
|
||||
## Todo
|
||||
|
||||
* Save/Return previous values
|
||||
* Evaluate fmt.Formatter interface
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||
650
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
650
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
@@ -0,0 +1,650 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
var (
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. It's also set to true if the NO_COLOR environment variable is
|
||||
// set (regardless of its value). This is a global option and affects all
|
||||
// colors. For more control over each color block use the methods
|
||||
// DisableColor() individually.
|
||||
NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" ||
|
||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||
|
||||
// Output defines the standard output of the print functions. By default,
|
||||
// os.Stdout is used.
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
// Error defines a color supporting writer for os.Stderr.
|
||||
Error = colorable.NewColorableStderr()
|
||||
|
||||
// colorsCache is used to reduce the count of created Color objects and
|
||||
// allows to reuse already created objects with required Attribute.
|
||||
colorsCache = make(map[Attribute]*Color)
|
||||
colorsCacheMu sync.Mutex // protects colorsCache
|
||||
)
|
||||
|
||||
// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
|
||||
func noColorIsSet() bool {
|
||||
return os.Getenv("NO_COLOR") != ""
|
||||
}
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
params []Attribute
|
||||
noColor *bool
|
||||
}
|
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
// Base attributes
|
||||
const (
|
||||
Reset Attribute = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
const (
|
||||
ResetBold Attribute = iota + 22
|
||||
ResetItalic
|
||||
ResetUnderline
|
||||
ResetBlinking
|
||||
_
|
||||
ResetReversed
|
||||
ResetConcealed
|
||||
ResetCrossedOut
|
||||
)
|
||||
|
||||
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
|
||||
Bold: ResetBold,
|
||||
Faint: ResetBold,
|
||||
Italic: ResetItalic,
|
||||
Underline: ResetUnderline,
|
||||
BlinkSlow: ResetBlinking,
|
||||
BlinkRapid: ResetBlinking,
|
||||
ReverseVideo: ResetReversed,
|
||||
Concealed: ResetConcealed,
|
||||
CrossedOut: ResetCrossedOut,
|
||||
}
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attribute = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack Attribute = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack Attribute = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
// New returns a newly created color object.
|
||||
func New(value ...Attribute) *Color {
|
||||
c := &Color{
|
||||
params: make([]Attribute, 0),
|
||||
}
|
||||
|
||||
if noColorIsSet() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
c.Add(value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attribute) *Color {
|
||||
c := New(p...)
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() {
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprint(Output, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unset() {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
Unset()
|
||||
}
|
||||
|
||||
// SetWriter is used to set the SGR sequence with the given io.Writer. This is
|
||||
// a low-level function, and users should use the higher-level functions, such
|
||||
// as color.Fprint, color.Print, etc.
|
||||
func (c *Color) SetWriter(w io.Writer) *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprint(w, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
// UnsetWriter resets all escape attributes and clears the output with the give
|
||||
// io.Writer. Usually should be called after SetWriter().
|
||||
func (c *Color) UnsetWriter(w io.Writer) {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attribute) *Color {
|
||||
c.params = append(c.params, value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Fprint formats using the default formats for its operands and writes to w.
|
||||
// Spaces are added between operands when neither is a string.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.SetWriter(w)
|
||||
defer c.UnsetWriter(w)
|
||||
|
||||
return fmt.Fprint(w, a...)
|
||||
}
|
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprint(Output, a...)
|
||||
}
|
||||
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
c.SetWriter(w)
|
||||
defer c.UnsetWriter(w)
|
||||
|
||||
return fmt.Fprintf(w, format, a...)
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintf(Output, format, a...)
|
||||
}
|
||||
|
||||
// Fprintln formats using the default formats for its operands and writes to w.
|
||||
// Spaces are always added between operands and a newline is appended.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, c.wrap(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(Output, c.wrap(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Sprint is just like Print, but returns a string instead of printing it.
|
||||
func (c *Color) Sprint(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(c.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// FprintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprint().
|
||||
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprint(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Print(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintf().
|
||||
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||
return func(w io.Writer, format string, a ...interface{}) {
|
||||
c.Fprintf(w, format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||
return func(format string, a ...interface{}) {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintln().
|
||||
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprintln(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Println(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
return func(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return fmt.Sprintln(c.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string {
|
||||
if c.isNoColorSet() {
|
||||
return s
|
||||
}
|
||||
|
||||
return c.format() + s + c.unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
//return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
//for each element in sequence let's use the speficic reset escape, ou the generic one if not found
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(Reset))
|
||||
ra, ok := mapResetAttributes[v]
|
||||
if ok {
|
||||
format[i] = strconv.Itoa(int(ra))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
// EnableColor enables the color output. Use it in conjunction with
|
||||
// DisableColor(). Otherwise, this method has no side effects.
|
||||
func (c *Color) EnableColor() {
|
||||
c.noColor = boolPtr(false)
|
||||
}
|
||||
|
||||
func (c *Color) isNoColorSet() bool {
|
||||
// check first if we have user set action
|
||||
if c.noColor != nil {
|
||||
return *c.noColor
|
||||
}
|
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor
|
||||
}
|
||||
|
||||
// Equals returns a boolean value indicating whether two colors are equal.
|
||||
func (c *Color) Equals(c2 *Color) bool {
|
||||
if c == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if c == nil || c2 == nil {
|
||||
return false
|
||||
}
|
||||
if len(c.params) != len(c2.params) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, attr := range c.params {
|
||||
if !c2.attrExists(attr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Color) attrExists(a Attribute) bool {
|
||||
for _, attr := range c.params {
|
||||
if attr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func getCachedColor(p Attribute) *Color {
|
||||
colorsCacheMu.Lock()
|
||||
defer colorsCacheMu.Unlock()
|
||||
|
||||
c, ok := colorsCache[p]
|
||||
if !ok {
|
||||
c = New(p)
|
||||
colorsCache[p] = c
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
if len(a) == 0 {
|
||||
c.Print(format)
|
||||
} else {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if len(a) == 0 {
|
||||
return c.SprintFunc()(format)
|
||||
}
|
||||
|
||||
return c.SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// Black is a convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||
|
||||
// Red is a convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||
|
||||
// Green is a convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||
|
||||
// Yellow is a convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||
|
||||
// Blue is a convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||
|
||||
// Magenta is a convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||
|
||||
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||
|
||||
// White is a convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||
|
||||
// BlackString is a convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||
|
||||
// RedString is a convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||
|
||||
// GreenString is a convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||
|
||||
// YellowString is a convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||
|
||||
// BlueString is a convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||
|
||||
// MagentaString is a convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgMagenta, a...)
|
||||
}
|
||||
|
||||
// CyanString is a convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||
|
||||
// WhiteString is a convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||
|
||||
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||
|
||||
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||
|
||||
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||
|
||||
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||
|
||||
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||
|
||||
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||
// foreground.
|
||||
func HiBlackString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiBlack, a...)
|
||||
}
|
||||
|
||||
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||
// foreground.
|
||||
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||
// foreground.
|
||||
func HiGreenString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiGreen, a...)
|
||||
}
|
||||
|
||||
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||
// foreground.
|
||||
func HiYellowString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiYellow, a...)
|
||||
}
|
||||
|
||||
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||
// foreground.
|
||||
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||
// foreground.
|
||||
func HiMagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiMagenta, a...)
|
||||
}
|
||||
|
||||
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||
// foreground.
|
||||
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||
// foreground.
|
||||
func HiWhiteString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiWhite, a...)
|
||||
}
|
||||
19
vendor/github.com/fatih/color/color_windows.go
generated
vendored
Normal file
19
vendor/github.com/fatih/color/color_windows.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Opt-in for ansi color support for current process.
|
||||
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences
|
||||
var outMode uint32
|
||||
out := windows.Handle(os.Stdout.Fd())
|
||||
if err := windows.GetConsoleMode(out, &outMode); err != nil {
|
||||
return
|
||||
}
|
||||
outMode |= windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
_ = windows.SetConsoleMode(out, outMode)
|
||||
}
|
||||
134
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
134
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
Package color is an ANSI color package to output colorized or SGR defined
|
||||
output to the standard output. The API can be used in several way, pick one
|
||||
that suits you.
|
||||
|
||||
Use simple and default helper functions with predefined foreground colors:
|
||||
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// a newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// More default foreground colors..
|
||||
color.Red("We have red")
|
||||
color.Yellow("Yellow color too!")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
// Hi-intensity colors
|
||||
color.HiGreen("Bright green color.")
|
||||
color.HiBlack("Bright black means gray..")
|
||||
color.HiWhite("Shiny white color!")
|
||||
|
||||
However, there are times when custom color mixes are required. Below are some
|
||||
examples to create custom color objects and use the print functions of each
|
||||
separate color object.
|
||||
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with White background.")
|
||||
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(myWriter, "This will print text in blue.")
|
||||
|
||||
You can create PrintXxx functions to simplify even more:
|
||||
|
||||
// Create a custom print function for convenient
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("warning")
|
||||
red("error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("don't forget this...")
|
||||
|
||||
You can also FprintXxx functions to pass your own io.Writer:
|
||||
|
||||
blue := color.New(FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, don't forget this...")
|
||||
|
||||
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
|
||||
yellow := New(FgYellow).SprintFunc()
|
||||
red := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Printf("this %s rocks!\n", info("package"))
|
||||
|
||||
Windows support is enabled by default. All Print functions work as intended.
|
||||
However, only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||
set the output to color.Output:
|
||||
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
Using with existing code is possible. Just use the Set() method to set the
|
||||
standard output to the given parameters. That way a rewrite of an existing
|
||||
code is not required.
|
||||
|
||||
// Use handy standard colors.
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will be now in Yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // use it in your function
|
||||
|
||||
fmt.Println("All text will be now bold magenta.")
|
||||
|
||||
There might be a case where you want to disable color output (for example to
|
||||
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||
disable colors both globally and for single color definition. For example
|
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||
the color output with:
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
|
||||
You can also disable the color by setting the NO_COLOR environment variable to any value.
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
*/
|
||||
package color
|
||||
13
vendor/github.com/fsnotify/fsnotify/.cirrus.yml
generated
vendored
Normal file
13
vendor/github.com/fsnotify/fsnotify/.cirrus.yml
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
freebsd_task:
|
||||
name: 'FreeBSD'
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-2
|
||||
install_script:
|
||||
- pkg update -f
|
||||
- pkg install -y go
|
||||
test_script:
|
||||
# run tests as user "cirrus" instead of root
|
||||
- pw useradd cirrus -m
|
||||
- chown -R cirrus:cirrus .
|
||||
- FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
|
||||
- sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
|
||||
1
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
1
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
@@ -4,3 +4,4 @@
|
||||
|
||||
# Output of go build ./cmd/fsnotify
|
||||
/fsnotify
|
||||
/fsnotify.exe
|
||||
|
||||
87
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
87
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
@@ -1,16 +1,87 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
Unreleased
|
||||
----------
|
||||
Nothing yet.
|
||||
|
||||
## [1.6.0] - 2022-10-13
|
||||
1.7.0 - 2023-10-22
|
||||
------------------
|
||||
This version of fsnotify needs Go 1.17.
|
||||
|
||||
### Additions
|
||||
|
||||
- illumos: add FEN backend to support illumos and Solaris. ([#371])
|
||||
|
||||
- all: add `NewBufferedWatcher()` to use a buffered channel, which can be useful
|
||||
in cases where you can't control the kernel buffer and receive a large number
|
||||
of events in bursts. ([#550], [#572])
|
||||
|
||||
- all: add `AddWith()`, which is identical to `Add()` but allows passing
|
||||
options. ([#521])
|
||||
|
||||
- windows: allow setting the ReadDirectoryChangesW() buffer size with
|
||||
`fsnotify.WithBufferSize()`; the default of 64K is the highest value that
|
||||
works on all platforms and is enough for most purposes, but in some cases a
|
||||
highest buffer is needed. ([#521])
|
||||
|
||||
### Changes and fixes
|
||||
|
||||
- inotify: remove watcher if a watched path is renamed ([#518])
|
||||
|
||||
After a rename the reported name wasn't updated, or even an empty string.
|
||||
Inotify doesn't provide any good facilities to update it, so just remove the
|
||||
watcher. This is already how it worked on kqueue and FEN.
|
||||
|
||||
On Windows this does work, and remains working.
|
||||
|
||||
- windows: don't listen for file attribute changes ([#520])
|
||||
|
||||
File attribute changes are sent as `FILE_ACTION_MODIFIED` by the Windows API,
|
||||
with no way to see if they're a file write or attribute change, so would show
|
||||
up as a fsnotify.Write event. This is never useful, and could result in many
|
||||
spurious Write events.
|
||||
|
||||
- windows: return `ErrEventOverflow` if the buffer is full ([#525])
|
||||
|
||||
Before it would merely return "short read", making it hard to detect this
|
||||
error.
|
||||
|
||||
- kqueue: make sure events for all files are delivered properly when removing a
|
||||
watched directory ([#526])
|
||||
|
||||
Previously they would get sent with `""` (empty string) or `"."` as the path
|
||||
name.
|
||||
|
||||
- kqueue: don't emit spurious Create events for symbolic links ([#524])
|
||||
|
||||
The link would get resolved but kqueue would "forget" it already saw the link
|
||||
itself, resulting on a Create for every Write event for the directory.
|
||||
|
||||
- all: return `ErrClosed` on `Add()` when the watcher is closed ([#516])
|
||||
|
||||
- other: add `Watcher.Errors` and `Watcher.Events` to the no-op `Watcher` in
|
||||
`backend_other.go`, making it easier to use on unsupported platforms such as
|
||||
WASM, AIX, etc. ([#528])
|
||||
|
||||
- other: use the `backend_other.go` no-op if the `appengine` build tag is set;
|
||||
Google AppEngine forbids usage of the unsafe package so the inotify backend
|
||||
won't compile there.
|
||||
|
||||
[#371]: https://github.com/fsnotify/fsnotify/pull/371
|
||||
[#516]: https://github.com/fsnotify/fsnotify/pull/516
|
||||
[#518]: https://github.com/fsnotify/fsnotify/pull/518
|
||||
[#520]: https://github.com/fsnotify/fsnotify/pull/520
|
||||
[#521]: https://github.com/fsnotify/fsnotify/pull/521
|
||||
[#524]: https://github.com/fsnotify/fsnotify/pull/524
|
||||
[#525]: https://github.com/fsnotify/fsnotify/pull/525
|
||||
[#526]: https://github.com/fsnotify/fsnotify/pull/526
|
||||
[#528]: https://github.com/fsnotify/fsnotify/pull/528
|
||||
[#537]: https://github.com/fsnotify/fsnotify/pull/537
|
||||
[#550]: https://github.com/fsnotify/fsnotify/pull/550
|
||||
[#572]: https://github.com/fsnotify/fsnotify/pull/572
|
||||
|
||||
1.6.0 - 2022-10-13
|
||||
------------------
|
||||
This version of fsnotify needs Go 1.16 (this was already the case since 1.5.1,
|
||||
but not documented). It also increases the minimum Linux version to 2.6.32.
|
||||
|
||||
|
||||
79
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
79
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
@@ -1,29 +1,31 @@
|
||||
fsnotify is a Go library to provide cross-platform filesystem notifications on
|
||||
Windows, Linux, macOS, and BSD systems.
|
||||
Windows, Linux, macOS, BSD, and illumos.
|
||||
|
||||
Go 1.16 or newer is required; the full documentation is at
|
||||
Go 1.17 or newer is required; the full documentation is at
|
||||
https://pkg.go.dev/github.com/fsnotify/fsnotify
|
||||
|
||||
**It's best to read the documentation at pkg.go.dev, as it's pinned to the last
|
||||
released version, whereas this README is for the last development version which
|
||||
may include additions/changes.**
|
||||
|
||||
---
|
||||
|
||||
Platform support:
|
||||
|
||||
| Adapter | OS | Status |
|
||||
| --------------------- | ---------------| -------------------------------------------------------------|
|
||||
| inotify | Linux 2.6.32+ | Supported |
|
||||
| kqueue | BSD, macOS | Supported |
|
||||
| ReadDirectoryChangesW | Windows | Supported |
|
||||
| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
|
||||
| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) |
|
||||
| fanotify | Linux 5.9+ | [Maybe](https://github.com/fsnotify/fsnotify/issues/114) |
|
||||
| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
|
||||
| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
|
||||
| Backend | OS | Status |
|
||||
| :-------------------- | :--------- | :------------------------------------------------------------------------ |
|
||||
| inotify | Linux | Supported |
|
||||
| kqueue | BSD, macOS | Supported |
|
||||
| ReadDirectoryChangesW | Windows | Supported |
|
||||
| FEN | illumos | Supported |
|
||||
| fanotify | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114) |
|
||||
| AHAFS | AIX | [aix branch]; experimental due to lack of maintainer and test environment |
|
||||
| FSEvents | macOS | [Needs support in x/sys/unix][fsevents] |
|
||||
| USN Journals | Windows | [Needs support in x/sys/windows][usn] |
|
||||
| Polling | *All* | [Not yet](https://github.com/fsnotify/fsnotify/issues/9) |
|
||||
|
||||
Linux and macOS should include Android and iOS, but these are currently untested.
|
||||
Linux and illumos should include Android and Solaris, but these are currently
|
||||
untested.
|
||||
|
||||
[fsevents]: https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120
|
||||
[usn]: https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847
|
||||
[aix branch]: https://github.com/fsnotify/fsnotify/issues/353#issuecomment-1284590129
|
||||
|
||||
Usage
|
||||
-----
|
||||
@@ -83,20 +85,23 @@ run with:
|
||||
|
||||
% go run ./cmd/fsnotify
|
||||
|
||||
Further detailed documentation can be found in godoc:
|
||||
https://pkg.go.dev/github.com/fsnotify/fsnotify
|
||||
|
||||
FAQ
|
||||
---
|
||||
### Will a file still be watched when it's moved to another directory?
|
||||
No, not unless you are watching the location it was moved to.
|
||||
|
||||
### Are subdirectories watched too?
|
||||
### Are subdirectories watched?
|
||||
No, you must add watches for any directory you want to watch (a recursive
|
||||
watcher is on the roadmap: [#18]).
|
||||
|
||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
|
||||
|
||||
### Do I have to watch the Error and Event channels in a goroutine?
|
||||
As of now, yes (you can read both channels in the same goroutine using `select`,
|
||||
you don't need a separate goroutine for both channels; see the example).
|
||||
Yes. You can read both channels in the same goroutine using `select` (you don't
|
||||
need a separate goroutine for both channels; see the example).
|
||||
|
||||
### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys?
|
||||
fsnotify requires support from underlying OS to work. The current NFS and SMB
|
||||
@@ -107,6 +112,32 @@ This could be fixed with a polling watcher ([#9]), but it's not yet implemented.
|
||||
|
||||
[#9]: https://github.com/fsnotify/fsnotify/issues/9
|
||||
|
||||
### Why do I get many Chmod events?
|
||||
Some programs may generate a lot of attribute changes; for example Spotlight on
|
||||
macOS, anti-virus programs, backup applications, and some others are known to do
|
||||
this. As a rule, it's typically best to ignore Chmod events. They're often not
|
||||
useful, and tend to cause problems.
|
||||
|
||||
Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||
temporary workaround is to add your folder(s) to the *Spotlight Privacy
|
||||
settings* until we have a native FSEvents implementation (see [#11]).
|
||||
|
||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
[#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||
|
||||
### Watching a file doesn't work well
|
||||
Watching individual files (rather than directories) is generally not recommended
|
||||
as many programs (especially editors) update files atomically: it will write to
|
||||
a temporary file which is then moved to to destination, overwriting the original
|
||||
(or some variant thereof). The watcher on the original file is now lost, as that
|
||||
no longer exists.
|
||||
|
||||
The upshot of this is that a power failure or crash won't leave a half-written
|
||||
file.
|
||||
|
||||
Watch the parent directory and use `Event.Name` to filter out files you're not
|
||||
interested in. There is an example of this in `cmd/fsnotify/file.go`.
|
||||
|
||||
Platform-specific notes
|
||||
-----------------------
|
||||
### Linux
|
||||
@@ -151,11 +182,3 @@ these platforms.
|
||||
|
||||
The sysctl variables `kern.maxfiles` and `kern.maxfilesperproc` can be used to
|
||||
control the maximum number of open files.
|
||||
|
||||
### macOS
|
||||
Spotlight indexing on macOS can result in multiple events (see [#15]). A temporary
|
||||
workaround is to add your folder(s) to the *Spotlight Privacy settings* until we
|
||||
have a native FSEvents implementation (see [#11]).
|
||||
|
||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
[#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||
|
||||
550
vendor/github.com/fsnotify/fsnotify/backend_fen.go
generated
vendored
550
vendor/github.com/fsnotify/fsnotify/backend_fen.go
generated
vendored
@@ -1,10 +1,19 @@
|
||||
//go:build solaris
|
||||
// +build solaris
|
||||
|
||||
// Note: the documentation on the Watcher type and methods is generated from
|
||||
// mkdoc.zsh
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Watcher watches a set of paths, delivering events on a channel.
|
||||
@@ -17,9 +26,9 @@ import (
|
||||
// When a file is removed a Remove event won't be emitted until all file
|
||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||
//
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
//
|
||||
// This is the event that inotify sends, so not much can be changed about this.
|
||||
//
|
||||
@@ -33,16 +42,16 @@ import (
|
||||
//
|
||||
// To increase them you can use sysctl or write the value to the /proc file:
|
||||
//
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
//
|
||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||
// your distro's documentation):
|
||||
//
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
//
|
||||
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||
// files" error.
|
||||
@@ -58,14 +67,20 @@ import (
|
||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||
// systems.
|
||||
//
|
||||
// # macOS notes
|
||||
// # Windows notes
|
||||
//
|
||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||
// Paths can be added as "C:\path\to\dir", but forward slashes
|
||||
// ("C:/path/to/dir") will also work.
|
||||
//
|
||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||
// When a watched directory is removed it will always send an event for the
|
||||
// directory itself, but may not send events for all files in that directory.
|
||||
// Sometimes it will send events for all times, sometimes it will send no
|
||||
// events, and often only for some files.
|
||||
//
|
||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
|
||||
// value that is guaranteed to work with SMB filesystems. If you have many
|
||||
// events in quick succession this may not be enough, and you will have to use
|
||||
// [WithBufferSize] to increase the value.
|
||||
type Watcher struct {
|
||||
// Events sends the filesystem change events.
|
||||
//
|
||||
@@ -92,44 +107,129 @@ type Watcher struct {
|
||||
// initiated by the user may show up as one or multiple
|
||||
// writes, depending on when the system syncs things to
|
||||
// disk. For example when compiling a large Go program
|
||||
// you may get hundreds of Write events, so you
|
||||
// probably want to wait until you've stopped receiving
|
||||
// them (see the dedup example in cmd/fsnotify).
|
||||
// you may get hundreds of Write events, and you may
|
||||
// want to wait until you've stopped receiving them
|
||||
// (see the dedup example in cmd/fsnotify).
|
||||
//
|
||||
// Some systems may send Write event for directories
|
||||
// when the directory content changes.
|
||||
//
|
||||
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||
// when a file is removed (or more accurately, when a
|
||||
// link to an inode is removed). On kqueue it's sent
|
||||
// and on kqueue when a file is truncated. On Windows
|
||||
// it's never sent.
|
||||
// when a file is truncated. On Windows it's never
|
||||
// sent.
|
||||
Events chan Event
|
||||
|
||||
// Errors sends any errors.
|
||||
//
|
||||
// ErrEventOverflow is used to indicate there are too many events:
|
||||
//
|
||||
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
|
||||
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
|
||||
// - kqueue, fen: Not used.
|
||||
Errors chan error
|
||||
|
||||
mu sync.Mutex
|
||||
port *unix.EventPort
|
||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
||||
dirs map[string]struct{} // Explicitly watched directories
|
||||
watches map[string]struct{} // Explicitly watched non-directories
|
||||
}
|
||||
|
||||
// NewWatcher creates a new Watcher.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
|
||||
return NewBufferedWatcher(0)
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
|
||||
// channel.
|
||||
//
|
||||
// The main use case for this is situations with a very large number of events
|
||||
// where the kernel buffer size can't be increased (e.g. due to lack of
|
||||
// permissions). An unbuffered Watcher will perform better for almost all use
|
||||
// cases, and whenever possible you will be better off increasing the kernel
|
||||
// buffers instead of adding a large userspace buffer.
|
||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
|
||||
w := &Watcher{
|
||||
Events: make(chan Event, sz),
|
||||
Errors: make(chan error),
|
||||
dirs: make(map[string]struct{}),
|
||||
watches: make(map[string]struct{}),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
var err error
|
||||
w.port, err = unix.NewEventPort()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fsnotify.NewWatcher: %w", err)
|
||||
}
|
||||
|
||||
go w.readEvents()
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// sendEvent attempts to send an event to the user, returning true if the event
|
||||
// was put in the channel successfully and false if the watcher has been closed.
|
||||
func (w *Watcher) sendEvent(name string, op Op) (sent bool) {
|
||||
select {
|
||||
case w.Events <- Event{Name: name, Op: op}:
|
||||
return true
|
||||
case <-w.done:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// sendError attempts to send an error to the user, returning true if the error
|
||||
// was put in the channel successfully and false if the watcher has been closed.
|
||||
func (w *Watcher) sendError(err error) (sent bool) {
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
return true
|
||||
case <-w.done:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Watcher) isClosed() bool {
|
||||
select {
|
||||
case <-w.done:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the Events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
return nil
|
||||
// Take the lock used by associateFile to prevent lingering events from
|
||||
// being processed after the close
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
close(w.done)
|
||||
return w.port.Close()
|
||||
}
|
||||
|
||||
// Add starts monitoring the path for changes.
|
||||
//
|
||||
// A path can only be watched once; attempting to watch it more than once will
|
||||
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// added. A watch will be automatically removed if the path is deleted.
|
||||
// A path can only be watched once; watching it more than once is a no-op and will
|
||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// watched.
|
||||
//
|
||||
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||
// re-created, or if it's moved to a different filesystem.
|
||||
// A watch will be automatically removed if the watched path is deleted or
|
||||
// renamed. The exception is the Windows backend, which doesn't remove the
|
||||
// watcher on renames.
|
||||
//
|
||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||
//
|
||||
// Returns [ErrClosed] if [Watcher.Close] was called.
|
||||
//
|
||||
// See [Watcher.AddWith] for a version that allows adding options.
|
||||
//
|
||||
// # Watching directories
|
||||
//
|
||||
// All files in a directory are monitored, including new files that are created
|
||||
@@ -139,15 +239,63 @@ func (w *Watcher) Close() error {
|
||||
// # Watching files
|
||||
//
|
||||
// Watching individual files (rather than directories) is generally not
|
||||
// recommended as many tools update files atomically. Instead of "just" writing
|
||||
// to the file a temporary file will be written to first, and if successful the
|
||||
// temporary file is moved to to destination removing the original, or some
|
||||
// variant thereof. The watcher on the original file is now lost, as it no
|
||||
// longer exists.
|
||||
// recommended as many programs (especially editors) update files atomically: it
|
||||
// will write to a temporary file which is then moved to to destination,
|
||||
// overwriting the original (or some variant thereof). The watcher on the
|
||||
// original file is now lost, as that no longer exists.
|
||||
//
|
||||
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||
func (w *Watcher) Add(name string) error {
|
||||
// The upshot of this is that a power failure or crash won't leave a
|
||||
// half-written file.
|
||||
//
|
||||
// Watch the parent directory and use Event.Name to filter out files you're not
|
||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
|
||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
|
||||
|
||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
|
||||
// the defaults described below are used.
|
||||
//
|
||||
// Possible options are:
|
||||
//
|
||||
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
|
||||
// other platforms. The default is 64K (65536 bytes).
|
||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
|
||||
if w.isClosed() {
|
||||
return ErrClosed
|
||||
}
|
||||
if w.port.PathIsWatched(name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = getOptions(opts...)
|
||||
|
||||
// Currently we resolve symlinks that were explicitly requested to be
|
||||
// watched. Otherwise we would use LStat here.
|
||||
stat, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Associate all files in the directory.
|
||||
if stat.IsDir() {
|
||||
err := w.handleDirectory(name, stat, true, w.associateFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
w.dirs[name] = struct{}{}
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
err = w.associateFile(name, stat, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
w.watches[name] = struct{}{}
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -157,6 +305,336 @@ func (w *Watcher) Add(name string) error {
|
||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||
//
|
||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
if !w.port.PathIsWatched(name) {
|
||||
return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
|
||||
}
|
||||
|
||||
// The user has expressed an intent. Immediately remove this name from
|
||||
// whichever watch list it might be in. If it's not in there the delete
|
||||
// doesn't cause harm.
|
||||
w.mu.Lock()
|
||||
delete(w.watches, name)
|
||||
delete(w.dirs, name)
|
||||
w.mu.Unlock()
|
||||
|
||||
stat, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove associations for every file in the directory.
|
||||
if stat.IsDir() {
|
||||
err := w.handleDirectory(name, stat, false, w.dissociateFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err = w.port.DissociatePath(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readEvents contains the main loop that runs in a goroutine watching for events.
|
||||
func (w *Watcher) readEvents() {
|
||||
// If this function returns, the watcher has been closed and we can close
|
||||
// these channels
|
||||
defer func() {
|
||||
close(w.Errors)
|
||||
close(w.Events)
|
||||
}()
|
||||
|
||||
pevents := make([]unix.PortEvent, 8)
|
||||
for {
|
||||
count, err := w.port.Get(pevents, 1, nil)
|
||||
if err != nil && err != unix.ETIME {
|
||||
// Interrupted system call (count should be 0) ignore and continue
|
||||
if errors.Is(err, unix.EINTR) && count == 0 {
|
||||
continue
|
||||
}
|
||||
// Get failed because we called w.Close()
|
||||
if errors.Is(err, unix.EBADF) && w.isClosed() {
|
||||
return
|
||||
}
|
||||
// There was an error not caused by calling w.Close()
|
||||
if !w.sendError(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
p := pevents[:count]
|
||||
for _, pevent := range p {
|
||||
if pevent.Source != unix.PORT_SOURCE_FILE {
|
||||
// Event from unexpected source received; should never happen.
|
||||
if !w.sendError(errors.New("Event from unexpected source received")) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = w.handleEvent(&pevent)
|
||||
if err != nil {
|
||||
if !w.sendError(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Watcher) handleDirectory(path string, stat os.FileInfo, follow bool, handler func(string, os.FileInfo, bool) error) error {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle all children of the directory.
|
||||
for _, entry := range files {
|
||||
finfo, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = handler(filepath.Join(path, finfo.Name()), finfo, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// And finally handle the directory itself.
|
||||
return handler(path, stat, follow)
|
||||
}
|
||||
|
||||
// handleEvent might need to emit more than one fsnotify event if the events
|
||||
// bitmap matches more than one event type (e.g. the file was both modified and
|
||||
// had the attributes changed between when the association was created and the
|
||||
// when event was returned)
|
||||
func (w *Watcher) handleEvent(event *unix.PortEvent) error {
|
||||
var (
|
||||
events = event.Events
|
||||
path = event.Path
|
||||
fmode = event.Cookie.(os.FileMode)
|
||||
reRegister = true
|
||||
)
|
||||
|
||||
w.mu.Lock()
|
||||
_, watchedDir := w.dirs[path]
|
||||
_, watchedPath := w.watches[path]
|
||||
w.mu.Unlock()
|
||||
isWatched := watchedDir || watchedPath
|
||||
|
||||
if events&unix.FILE_DELETE != 0 {
|
||||
if !w.sendEvent(path, Remove) {
|
||||
return nil
|
||||
}
|
||||
reRegister = false
|
||||
}
|
||||
if events&unix.FILE_RENAME_FROM != 0 {
|
||||
if !w.sendEvent(path, Rename) {
|
||||
return nil
|
||||
}
|
||||
// Don't keep watching the new file name
|
||||
reRegister = false
|
||||
}
|
||||
if events&unix.FILE_RENAME_TO != 0 {
|
||||
// We don't report a Rename event for this case, because Rename events
|
||||
// are interpreted as referring to the _old_ name of the file, and in
|
||||
// this case the event would refer to the new name of the file. This
|
||||
// type of rename event is not supported by fsnotify.
|
||||
|
||||
// inotify reports a Remove event in this case, so we simulate this
|
||||
// here.
|
||||
if !w.sendEvent(path, Remove) {
|
||||
return nil
|
||||
}
|
||||
// Don't keep watching the file that was removed
|
||||
reRegister = false
|
||||
}
|
||||
|
||||
// The file is gone, nothing left to do.
|
||||
if !reRegister {
|
||||
if watchedDir {
|
||||
w.mu.Lock()
|
||||
delete(w.dirs, path)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
if watchedPath {
|
||||
w.mu.Lock()
|
||||
delete(w.watches, path)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we didn't get a deletion the file still exists and we're going to have
|
||||
// to watch it again. Let's Stat it now so that we can compare permissions
|
||||
// and have what we need to continue watching the file
|
||||
|
||||
stat, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
// This is unexpected, but we should still emit an event. This happens
|
||||
// most often on "rm -r" of a subdirectory inside a watched directory We
|
||||
// get a modify event of something happening inside, but by the time we
|
||||
// get here, the sudirectory is already gone. Clearly we were watching
|
||||
// this path but now it is gone. Let's tell the user that it was
|
||||
// removed.
|
||||
if !w.sendEvent(path, Remove) {
|
||||
return nil
|
||||
}
|
||||
// Suppress extra write events on removed directories; they are not
|
||||
// informative and can be confusing.
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolve symlinks that were explicitly watched as we would have at Add()
|
||||
// time. this helps suppress spurious Chmod events on watched symlinks
|
||||
if isWatched {
|
||||
stat, err = os.Stat(path)
|
||||
if err != nil {
|
||||
// The symlink still exists, but the target is gone. Report the
|
||||
// Remove similar to above.
|
||||
if !w.sendEvent(path, Remove) {
|
||||
return nil
|
||||
}
|
||||
// Don't return the error
|
||||
}
|
||||
}
|
||||
|
||||
if events&unix.FILE_MODIFIED != 0 {
|
||||
if fmode.IsDir() {
|
||||
if watchedDir {
|
||||
if err := w.updateDirectory(path); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if !w.sendEvent(path, Write) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !w.sendEvent(path, Write) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if events&unix.FILE_ATTRIB != 0 && stat != nil {
|
||||
// Only send Chmod if perms changed
|
||||
if stat.Mode().Perm() != fmode.Perm() {
|
||||
if !w.sendEvent(path, Chmod) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if stat != nil {
|
||||
// If we get here, it means we've hit an event above that requires us to
|
||||
// continue watching the file or directory
|
||||
return w.associateFile(path, stat, isWatched)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Watcher) updateDirectory(path string) error {
|
||||
// The directory was modified, so we must find unwatched entities and watch
|
||||
// them. If something was removed from the directory, nothing will happen,
|
||||
// as everything else should still be watched.
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range files {
|
||||
path := filepath.Join(path, entry.Name())
|
||||
if w.port.PathIsWatched(path) {
|
||||
continue
|
||||
}
|
||||
|
||||
finfo, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.associateFile(path, finfo, false)
|
||||
if err != nil {
|
||||
if !w.sendError(err) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if !w.sendEvent(path, Create) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Watcher) associateFile(path string, stat os.FileInfo, follow bool) error {
|
||||
if w.isClosed() {
|
||||
return ErrClosed
|
||||
}
|
||||
// This is primarily protecting the call to AssociatePath but it is
|
||||
// important and intentional that the call to PathIsWatched is also
|
||||
// protected by this mutex. Without this mutex, AssociatePath has been seen
|
||||
// to error out that the path is already associated.
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
if w.port.PathIsWatched(path) {
|
||||
// Remove the old association in favor of this one If we get ENOENT,
|
||||
// then while the x/sys/unix wrapper still thought that this path was
|
||||
// associated, the underlying event port did not. This call will have
|
||||
// cleared up that discrepancy. The most likely cause is that the event
|
||||
// has fired but we haven't processed it yet.
|
||||
err := w.port.DissociatePath(path)
|
||||
if err != nil && err != unix.ENOENT {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// FILE_NOFOLLOW means we watch symlinks themselves rather than their
|
||||
// targets.
|
||||
events := unix.FILE_MODIFIED | unix.FILE_ATTRIB | unix.FILE_NOFOLLOW
|
||||
if follow {
|
||||
// We *DO* follow symlinks for explicitly watched entries.
|
||||
events = unix.FILE_MODIFIED | unix.FILE_ATTRIB
|
||||
}
|
||||
return w.port.AssociatePath(path, stat,
|
||||
events,
|
||||
stat.Mode())
|
||||
}
|
||||
|
||||
func (w *Watcher) dissociateFile(path string, stat os.FileInfo, unused bool) error {
|
||||
if !w.port.PathIsWatched(path) {
|
||||
return nil
|
||||
}
|
||||
return w.port.DissociatePath(path)
|
||||
}
|
||||
|
||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
|
||||
// yet removed).
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) WatchList() []string {
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
entries := make([]string, 0, len(w.watches)+len(w.dirs))
|
||||
for pathname := range w.dirs {
|
||||
entries = append(entries, pathname)
|
||||
}
|
||||
for pathname := range w.watches {
|
||||
entries = append(entries, pathname)
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
381
vendor/github.com/fsnotify/fsnotify/backend_inotify.go
generated
vendored
381
vendor/github.com/fsnotify/fsnotify/backend_inotify.go
generated
vendored
@@ -1,5 +1,8 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
//go:build linux && !appengine
|
||||
// +build linux,!appengine
|
||||
|
||||
// Note: the documentation on the Watcher type and methods is generated from
|
||||
// mkdoc.zsh
|
||||
|
||||
package fsnotify
|
||||
|
||||
@@ -26,9 +29,9 @@ import (
|
||||
// When a file is removed a Remove event won't be emitted until all file
|
||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||
//
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
//
|
||||
// This is the event that inotify sends, so not much can be changed about this.
|
||||
//
|
||||
@@ -42,16 +45,16 @@ import (
|
||||
//
|
||||
// To increase them you can use sysctl or write the value to the /proc file:
|
||||
//
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
//
|
||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||
// your distro's documentation):
|
||||
//
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
//
|
||||
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||
// files" error.
|
||||
@@ -67,14 +70,20 @@ import (
|
||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||
// systems.
|
||||
//
|
||||
// # macOS notes
|
||||
// # Windows notes
|
||||
//
|
||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||
// Paths can be added as "C:\path\to\dir", but forward slashes
|
||||
// ("C:/path/to/dir") will also work.
|
||||
//
|
||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||
// When a watched directory is removed it will always send an event for the
|
||||
// directory itself, but may not send events for all files in that directory.
|
||||
// Sometimes it will send events for all times, sometimes it will send no
|
||||
// events, and often only for some files.
|
||||
//
|
||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
|
||||
// value that is guaranteed to work with SMB filesystems. If you have many
|
||||
// events in quick succession this may not be enough, and you will have to use
|
||||
// [WithBufferSize] to increase the value.
|
||||
type Watcher struct {
|
||||
// Events sends the filesystem change events.
|
||||
//
|
||||
@@ -101,36 +110,148 @@ type Watcher struct {
|
||||
// initiated by the user may show up as one or multiple
|
||||
// writes, depending on when the system syncs things to
|
||||
// disk. For example when compiling a large Go program
|
||||
// you may get hundreds of Write events, so you
|
||||
// probably want to wait until you've stopped receiving
|
||||
// them (see the dedup example in cmd/fsnotify).
|
||||
// you may get hundreds of Write events, and you may
|
||||
// want to wait until you've stopped receiving them
|
||||
// (see the dedup example in cmd/fsnotify).
|
||||
//
|
||||
// Some systems may send Write event for directories
|
||||
// when the directory content changes.
|
||||
//
|
||||
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||
// when a file is removed (or more accurately, when a
|
||||
// link to an inode is removed). On kqueue it's sent
|
||||
// and on kqueue when a file is truncated. On Windows
|
||||
// it's never sent.
|
||||
// when a file is truncated. On Windows it's never
|
||||
// sent.
|
||||
Events chan Event
|
||||
|
||||
// Errors sends any errors.
|
||||
//
|
||||
// ErrEventOverflow is used to indicate there are too many events:
|
||||
//
|
||||
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
|
||||
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
|
||||
// - kqueue, fen: Not used.
|
||||
Errors chan error
|
||||
|
||||
// Store fd here as os.File.Read() will no longer return on close after
|
||||
// calling Fd(). See: https://github.com/golang/go/issues/26439
|
||||
fd int
|
||||
mu sync.Mutex // Map access
|
||||
inotifyFile *os.File
|
||||
watches map[string]*watch // Map of inotify watches (key: path)
|
||||
paths map[int]string // Map of watched paths (key: watch descriptor)
|
||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
||||
doneResp chan struct{} // Channel to respond to Close
|
||||
watches *watches
|
||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
||||
closeMu sync.Mutex
|
||||
doneResp chan struct{} // Channel to respond to Close
|
||||
}
|
||||
|
||||
type (
|
||||
watches struct {
|
||||
mu sync.RWMutex
|
||||
wd map[uint32]*watch // wd → watch
|
||||
path map[string]uint32 // pathname → wd
|
||||
}
|
||||
watch struct {
|
||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
||||
path string // Watch path.
|
||||
}
|
||||
)
|
||||
|
||||
func newWatches() *watches {
|
||||
return &watches{
|
||||
wd: make(map[uint32]*watch),
|
||||
path: make(map[string]uint32),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watches) len() int {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
return len(w.wd)
|
||||
}
|
||||
|
||||
func (w *watches) add(ww *watch) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.wd[ww.wd] = ww
|
||||
w.path[ww.path] = ww.wd
|
||||
}
|
||||
|
||||
func (w *watches) remove(wd uint32) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
delete(w.path, w.wd[wd].path)
|
||||
delete(w.wd, wd)
|
||||
}
|
||||
|
||||
func (w *watches) removePath(path string) (uint32, bool) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
wd, ok := w.path[path]
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
delete(w.path, path)
|
||||
delete(w.wd, wd)
|
||||
|
||||
return wd, true
|
||||
}
|
||||
|
||||
func (w *watches) byPath(path string) *watch {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
return w.wd[w.path[path]]
|
||||
}
|
||||
|
||||
func (w *watches) byWd(wd uint32) *watch {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
return w.wd[wd]
|
||||
}
|
||||
|
||||
func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
var existing *watch
|
||||
wd, ok := w.path[path]
|
||||
if ok {
|
||||
existing = w.wd[wd]
|
||||
}
|
||||
|
||||
upd, err := f(existing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if upd != nil {
|
||||
w.wd[upd.wd] = upd
|
||||
w.path[upd.path] = upd.wd
|
||||
|
||||
if upd.wd != wd {
|
||||
delete(w.wd, wd)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewWatcher creates a new Watcher.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
// Create inotify fd
|
||||
// Need to set the FD to nonblocking mode in order for SetDeadline methods to work
|
||||
// Otherwise, blocking i/o operations won't terminate on close
|
||||
return NewBufferedWatcher(0)
|
||||
}
|
||||
|
||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
|
||||
// channel.
|
||||
//
|
||||
// The main use case for this is situations with a very large number of events
|
||||
// where the kernel buffer size can't be increased (e.g. due to lack of
|
||||
// permissions). An unbuffered Watcher will perform better for almost all use
|
||||
// cases, and whenever possible you will be better off increasing the kernel
|
||||
// buffers instead of adding a large userspace buffer.
|
||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
|
||||
// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
|
||||
// I/O operations won't terminate on close.
|
||||
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
|
||||
if fd == -1 {
|
||||
return nil, errno
|
||||
@@ -139,9 +260,8 @@ func NewWatcher() (*Watcher, error) {
|
||||
w := &Watcher{
|
||||
fd: fd,
|
||||
inotifyFile: os.NewFile(uintptr(fd), ""),
|
||||
watches: make(map[string]*watch),
|
||||
paths: make(map[int]string),
|
||||
Events: make(chan Event),
|
||||
watches: newWatches(),
|
||||
Events: make(chan Event, sz),
|
||||
Errors: make(chan error),
|
||||
done: make(chan struct{}),
|
||||
doneResp: make(chan struct{}),
|
||||
@@ -157,8 +277,8 @@ func (w *Watcher) sendEvent(e Event) bool {
|
||||
case w.Events <- e:
|
||||
return true
|
||||
case <-w.done:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true if the error was sent, or false if watcher is closed.
|
||||
@@ -180,17 +300,15 @@ func (w *Watcher) isClosed() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
// Close removes all watches and closes the Events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
w.mu.Lock()
|
||||
w.closeMu.Lock()
|
||||
if w.isClosed() {
|
||||
w.mu.Unlock()
|
||||
w.closeMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send 'close' signal to goroutine, and set the Watcher to closed.
|
||||
close(w.done)
|
||||
w.mu.Unlock()
|
||||
w.closeMu.Unlock()
|
||||
|
||||
// Causes any blocking reads to return with an error, provided the file
|
||||
// still supports deadline operations.
|
||||
@@ -207,17 +325,21 @@ func (w *Watcher) Close() error {
|
||||
|
||||
// Add starts monitoring the path for changes.
|
||||
//
|
||||
// A path can only be watched once; attempting to watch it more than once will
|
||||
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// added. A watch will be automatically removed if the path is deleted.
|
||||
// A path can only be watched once; watching it more than once is a no-op and will
|
||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// watched.
|
||||
//
|
||||
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||
// re-created, or if it's moved to a different filesystem.
|
||||
// A watch will be automatically removed if the watched path is deleted or
|
||||
// renamed. The exception is the Windows backend, which doesn't remove the
|
||||
// watcher on renames.
|
||||
//
|
||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||
//
|
||||
// Returns [ErrClosed] if [Watcher.Close] was called.
|
||||
//
|
||||
// See [Watcher.AddWith] for a version that allows adding options.
|
||||
//
|
||||
// # Watching directories
|
||||
//
|
||||
// All files in a directory are monitored, including new files that are created
|
||||
@@ -227,44 +349,59 @@ func (w *Watcher) Close() error {
|
||||
// # Watching files
|
||||
//
|
||||
// Watching individual files (rather than directories) is generally not
|
||||
// recommended as many tools update files atomically. Instead of "just" writing
|
||||
// to the file a temporary file will be written to first, and if successful the
|
||||
// temporary file is moved to to destination removing the original, or some
|
||||
// variant thereof. The watcher on the original file is now lost, as it no
|
||||
// longer exists.
|
||||
// recommended as many programs (especially editors) update files atomically: it
|
||||
// will write to a temporary file which is then moved to to destination,
|
||||
// overwriting the original (or some variant thereof). The watcher on the
|
||||
// original file is now lost, as that no longer exists.
|
||||
//
|
||||
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||
func (w *Watcher) Add(name string) error {
|
||||
name = filepath.Clean(name)
|
||||
// The upshot of this is that a power failure or crash won't leave a
|
||||
// half-written file.
|
||||
//
|
||||
// Watch the parent directory and use Event.Name to filter out files you're not
|
||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
|
||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
|
||||
|
||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
|
||||
// the defaults described below are used.
|
||||
//
|
||||
// Possible options are:
|
||||
//
|
||||
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
|
||||
// other platforms. The default is 64K (65536 bytes).
|
||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
|
||||
if w.isClosed() {
|
||||
return errors.New("inotify instance already closed")
|
||||
return ErrClosed
|
||||
}
|
||||
|
||||
name = filepath.Clean(name)
|
||||
_ = getOptions(opts...)
|
||||
|
||||
var flags uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
|
||||
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
|
||||
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
watchEntry := w.watches[name]
|
||||
if watchEntry != nil {
|
||||
flags |= watchEntry.flags | unix.IN_MASK_ADD
|
||||
}
|
||||
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
|
||||
if wd == -1 {
|
||||
return errno
|
||||
}
|
||||
return w.watches.updatePath(name, func(existing *watch) (*watch, error) {
|
||||
if existing != nil {
|
||||
flags |= existing.flags | unix.IN_MASK_ADD
|
||||
}
|
||||
|
||||
if watchEntry == nil {
|
||||
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
|
||||
w.paths[wd] = name
|
||||
} else {
|
||||
watchEntry.wd = uint32(wd)
|
||||
watchEntry.flags = flags
|
||||
}
|
||||
wd, err := unix.InotifyAddWatch(w.fd, name, flags)
|
||||
if wd == -1 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
if existing == nil {
|
||||
return &watch{
|
||||
wd: uint32(wd),
|
||||
path: name,
|
||||
flags: flags,
|
||||
}, nil
|
||||
}
|
||||
|
||||
existing.wd = uint32(wd)
|
||||
existing.flags = flags
|
||||
return existing, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Remove stops monitoring the path for changes.
|
||||
@@ -273,32 +410,22 @@ func (w *Watcher) Add(name string) error {
|
||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||
//
|
||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
name = filepath.Clean(name)
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
return w.remove(filepath.Clean(name))
|
||||
}
|
||||
|
||||
// Fetch the watch.
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
watch, ok := w.watches[name]
|
||||
|
||||
// Remove it from inotify.
|
||||
func (w *Watcher) remove(name string) error {
|
||||
wd, ok := w.watches.removePath(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
|
||||
}
|
||||
|
||||
// We successfully removed the watch if InotifyRmWatch doesn't return an
|
||||
// error, we need to clean up our internal state to ensure it matches
|
||||
// inotify's kernel state.
|
||||
delete(w.paths, int(watch.wd))
|
||||
delete(w.watches, name)
|
||||
|
||||
// inotify_rm_watch will return EINVAL if the file has been deleted;
|
||||
// the inotify will already have been removed.
|
||||
// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
|
||||
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
|
||||
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
|
||||
// by another thread and we have not received IN_IGNORE event.
|
||||
success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
|
||||
success, errno := unix.InotifyRmWatch(w.fd, wd)
|
||||
if success == -1 {
|
||||
// TODO: Perhaps it's not helpful to return an error here in every case;
|
||||
// The only two possible errors are:
|
||||
@@ -312,26 +439,26 @@ func (w *Watcher) Remove(name string) error {
|
||||
// are watching is deleted.
|
||||
return errno
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
|
||||
// yet removed).
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) WatchList() []string {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
entries := make([]string, 0, len(w.watches))
|
||||
for pathname := range w.watches {
|
||||
entries = append(entries, pathname)
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
entries := make([]string, 0, w.watches.len())
|
||||
w.watches.mu.RLock()
|
||||
for pathname := range w.watches.path {
|
||||
entries = append(entries, pathname)
|
||||
}
|
||||
w.watches.mu.RUnlock()
|
||||
|
||||
type watch struct {
|
||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
||||
return entries
|
||||
}
|
||||
|
||||
// readEvents reads from the inotify file descriptor, converts the
|
||||
@@ -367,14 +494,11 @@ func (w *Watcher) readEvents() {
|
||||
if n < unix.SizeofInotifyEvent {
|
||||
var err error
|
||||
if n == 0 {
|
||||
// If EOF is received. This should really never happen.
|
||||
err = io.EOF
|
||||
err = io.EOF // If EOF is received. This should really never happen.
|
||||
} else if n < 0 {
|
||||
// If an error occurred while reading.
|
||||
err = errno
|
||||
err = errno // If an error occurred while reading.
|
||||
} else {
|
||||
// Read was too short.
|
||||
err = errors.New("notify: short read in readEvents()")
|
||||
err = errors.New("notify: short read in readEvents()") // Read was too short.
|
||||
}
|
||||
if !w.sendError(err) {
|
||||
return
|
||||
@@ -403,18 +527,29 @@ func (w *Watcher) readEvents() {
|
||||
// doesn't append the filename to the event, but we would like to always fill the
|
||||
// the "Name" field with a valid filename. We retrieve the path of the watch from
|
||||
// the "paths" map.
|
||||
w.mu.Lock()
|
||||
name, ok := w.paths[int(raw.Wd)]
|
||||
// IN_DELETE_SELF occurs when the file/directory being watched is removed.
|
||||
// This is a sign to clean up the maps, otherwise we are no longer in sync
|
||||
// with the inotify kernel state which has already deleted the watch
|
||||
// automatically.
|
||||
if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
|
||||
delete(w.paths, int(raw.Wd))
|
||||
delete(w.watches, name)
|
||||
}
|
||||
w.mu.Unlock()
|
||||
watch := w.watches.byWd(uint32(raw.Wd))
|
||||
|
||||
// inotify will automatically remove the watch on deletes; just need
|
||||
// to clean our state here.
|
||||
if watch != nil && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
|
||||
w.watches.remove(watch.wd)
|
||||
}
|
||||
// We can't really update the state when a watched path is moved;
|
||||
// only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
|
||||
// the watch.
|
||||
if watch != nil && mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
|
||||
err := w.remove(watch.path)
|
||||
if err != nil && !errors.Is(err, ErrNonExistentWatch) {
|
||||
if !w.sendError(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var name string
|
||||
if watch != nil {
|
||||
name = watch.path
|
||||
}
|
||||
if nameLen > 0 {
|
||||
// Point "bytes" at the first byte of the filename
|
||||
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
|
||||
|
||||
293
vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
generated
vendored
293
vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
generated
vendored
@@ -1,12 +1,14 @@
|
||||
//go:build freebsd || openbsd || netbsd || dragonfly || darwin
|
||||
// +build freebsd openbsd netbsd dragonfly darwin
|
||||
|
||||
// Note: the documentation on the Watcher type and methods is generated from
|
||||
// mkdoc.zsh
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
@@ -24,9 +26,9 @@ import (
|
||||
// When a file is removed a Remove event won't be emitted until all file
|
||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||
//
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
//
|
||||
// This is the event that inotify sends, so not much can be changed about this.
|
||||
//
|
||||
@@ -40,16 +42,16 @@ import (
|
||||
//
|
||||
// To increase them you can use sysctl or write the value to the /proc file:
|
||||
//
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
//
|
||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||
// your distro's documentation):
|
||||
//
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
//
|
||||
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||
// files" error.
|
||||
@@ -65,14 +67,20 @@ import (
|
||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||
// systems.
|
||||
//
|
||||
// # macOS notes
|
||||
// # Windows notes
|
||||
//
|
||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||
// Paths can be added as "C:\path\to\dir", but forward slashes
|
||||
// ("C:/path/to/dir") will also work.
|
||||
//
|
||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||
// When a watched directory is removed it will always send an event for the
|
||||
// directory itself, but may not send events for all files in that directory.
|
||||
// Sometimes it will send events for all times, sometimes it will send no
|
||||
// events, and often only for some files.
|
||||
//
|
||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
|
||||
// value that is guaranteed to work with SMB filesystems. If you have many
|
||||
// events in quick succession this may not be enough, and you will have to use
|
||||
// [WithBufferSize] to increase the value.
|
||||
type Watcher struct {
|
||||
// Events sends the filesystem change events.
|
||||
//
|
||||
@@ -99,18 +107,27 @@ type Watcher struct {
|
||||
// initiated by the user may show up as one or multiple
|
||||
// writes, depending on when the system syncs things to
|
||||
// disk. For example when compiling a large Go program
|
||||
// you may get hundreds of Write events, so you
|
||||
// probably want to wait until you've stopped receiving
|
||||
// them (see the dedup example in cmd/fsnotify).
|
||||
// you may get hundreds of Write events, and you may
|
||||
// want to wait until you've stopped receiving them
|
||||
// (see the dedup example in cmd/fsnotify).
|
||||
//
|
||||
// Some systems may send Write event for directories
|
||||
// when the directory content changes.
|
||||
//
|
||||
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||
// when a file is removed (or more accurately, when a
|
||||
// link to an inode is removed). On kqueue it's sent
|
||||
// and on kqueue when a file is truncated. On Windows
|
||||
// it's never sent.
|
||||
// when a file is truncated. On Windows it's never
|
||||
// sent.
|
||||
Events chan Event
|
||||
|
||||
// Errors sends any errors.
|
||||
//
|
||||
// ErrEventOverflow is used to indicate there are too many events:
|
||||
//
|
||||
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
|
||||
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
|
||||
// - kqueue, fen: Not used.
|
||||
Errors chan error
|
||||
|
||||
done chan struct{}
|
||||
@@ -133,6 +150,18 @@ type pathInfo struct {
|
||||
|
||||
// NewWatcher creates a new Watcher.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
return NewBufferedWatcher(0)
|
||||
}
|
||||
|
||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
|
||||
// channel.
|
||||
//
|
||||
// The main use case for this is situations with a very large number of events
|
||||
// where the kernel buffer size can't be increased (e.g. due to lack of
|
||||
// permissions). An unbuffered Watcher will perform better for almost all use
|
||||
// cases, and whenever possible you will be better off increasing the kernel
|
||||
// buffers instead of adding a large userspace buffer.
|
||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
|
||||
kq, closepipe, err := newKqueue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -147,7 +176,7 @@ func NewWatcher() (*Watcher, error) {
|
||||
paths: make(map[int]pathInfo),
|
||||
fileExists: make(map[string]struct{}),
|
||||
userWatches: make(map[string]struct{}),
|
||||
Events: make(chan Event),
|
||||
Events: make(chan Event, sz),
|
||||
Errors: make(chan error),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
@@ -197,8 +226,8 @@ func (w *Watcher) sendEvent(e Event) bool {
|
||||
case w.Events <- e:
|
||||
return true
|
||||
case <-w.done:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true if the error was sent, or false if watcher is closed.
|
||||
@@ -207,11 +236,11 @@ func (w *Watcher) sendError(err error) bool {
|
||||
case w.Errors <- err:
|
||||
return true
|
||||
case <-w.done:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
// Close removes all watches and closes the Events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
w.mu.Lock()
|
||||
if w.isClosed {
|
||||
@@ -239,17 +268,21 @@ func (w *Watcher) Close() error {
|
||||
|
||||
// Add starts monitoring the path for changes.
|
||||
//
|
||||
// A path can only be watched once; attempting to watch it more than once will
|
||||
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// added. A watch will be automatically removed if the path is deleted.
|
||||
// A path can only be watched once; watching it more than once is a no-op and will
|
||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// watched.
|
||||
//
|
||||
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||
// re-created, or if it's moved to a different filesystem.
|
||||
// A watch will be automatically removed if the watched path is deleted or
|
||||
// renamed. The exception is the Windows backend, which doesn't remove the
|
||||
// watcher on renames.
|
||||
//
|
||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||
//
|
||||
// Returns [ErrClosed] if [Watcher.Close] was called.
|
||||
//
|
||||
// See [Watcher.AddWith] for a version that allows adding options.
|
||||
//
|
||||
// # Watching directories
|
||||
//
|
||||
// All files in a directory are monitored, including new files that are created
|
||||
@@ -259,15 +292,28 @@ func (w *Watcher) Close() error {
|
||||
// # Watching files
|
||||
//
|
||||
// Watching individual files (rather than directories) is generally not
|
||||
// recommended as many tools update files atomically. Instead of "just" writing
|
||||
// to the file a temporary file will be written to first, and if successful the
|
||||
// temporary file is moved to to destination removing the original, or some
|
||||
// variant thereof. The watcher on the original file is now lost, as it no
|
||||
// longer exists.
|
||||
// recommended as many programs (especially editors) update files atomically: it
|
||||
// will write to a temporary file which is then moved to to destination,
|
||||
// overwriting the original (or some variant thereof). The watcher on the
|
||||
// original file is now lost, as that no longer exists.
|
||||
//
|
||||
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||
func (w *Watcher) Add(name string) error {
|
||||
// The upshot of this is that a power failure or crash won't leave a
|
||||
// half-written file.
|
||||
//
|
||||
// Watch the parent directory and use Event.Name to filter out files you're not
|
||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
|
||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
|
||||
|
||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
|
||||
// the defaults described below are used.
|
||||
//
|
||||
// Possible options are:
|
||||
//
|
||||
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
|
||||
// other platforms. The default is 64K (65536 bytes).
|
||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
|
||||
_ = getOptions(opts...)
|
||||
|
||||
w.mu.Lock()
|
||||
w.userWatches[name] = struct{}{}
|
||||
w.mu.Unlock()
|
||||
@@ -281,9 +327,19 @@ func (w *Watcher) Add(name string) error {
|
||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||
//
|
||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
return w.remove(name, true)
|
||||
}
|
||||
|
||||
func (w *Watcher) remove(name string, unwatchFiles bool) error {
|
||||
name = filepath.Clean(name)
|
||||
w.mu.Lock()
|
||||
if w.isClosed {
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
watchfd, ok := w.watches[name]
|
||||
w.mu.Unlock()
|
||||
if !ok {
|
||||
@@ -315,7 +371,7 @@ func (w *Watcher) Remove(name string) error {
|
||||
w.mu.Unlock()
|
||||
|
||||
// Find all watched paths that are in this directory that are not external.
|
||||
if isDir {
|
||||
if unwatchFiles && isDir {
|
||||
var pathsToRemove []string
|
||||
w.mu.Lock()
|
||||
for fd := range w.watchesByDir[name] {
|
||||
@@ -326,20 +382,25 @@ func (w *Watcher) Remove(name string) error {
|
||||
}
|
||||
w.mu.Unlock()
|
||||
for _, name := range pathsToRemove {
|
||||
// Since these are internal, not much sense in propagating error
|
||||
// to the user, as that will just confuse them with an error about
|
||||
// a path they did not explicitly watch themselves.
|
||||
// Since these are internal, not much sense in propagating error to
|
||||
// the user, as that will just confuse them with an error about a
|
||||
// path they did not explicitly watch themselves.
|
||||
w.Remove(name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
|
||||
// yet removed).
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) WatchList() []string {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if w.isClosed {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := make([]string, 0, len(w.userWatches))
|
||||
for pathname := range w.userWatches {
|
||||
@@ -352,18 +413,18 @@ func (w *Watcher) WatchList() []string {
|
||||
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
|
||||
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
|
||||
|
||||
// addWatch adds name to the watched file set.
|
||||
// The flags are interpreted as described in kevent(2).
|
||||
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
|
||||
// addWatch adds name to the watched file set; the flags are interpreted as
|
||||
// described in kevent(2).
|
||||
//
|
||||
// Returns the real path to the file which was added, with symlinks resolved.
|
||||
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||
var isDir bool
|
||||
// Make ./name and name equivalent
|
||||
name = filepath.Clean(name)
|
||||
|
||||
w.mu.Lock()
|
||||
if w.isClosed {
|
||||
w.mu.Unlock()
|
||||
return "", errors.New("kevent instance already closed")
|
||||
return "", ErrClosed
|
||||
}
|
||||
watchfd, alreadyWatching := w.watches[name]
|
||||
// We already have a watch, but we can still override flags.
|
||||
@@ -383,27 +444,30 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Follow Symlinks
|
||||
//
|
||||
// Linux can add unresolvable symlinks to the watch list without issue,
|
||||
// and Windows can't do symlinks period. To maintain consistency, we
|
||||
// will act like everything is fine if the link can't be resolved.
|
||||
// There will simply be no file events for broken symlinks. Hence the
|
||||
// returns of nil on errors.
|
||||
// Follow Symlinks.
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
name, err = filepath.EvalSymlinks(name)
|
||||
link, err := os.Readlink(name)
|
||||
if err != nil {
|
||||
// Return nil because Linux can add unresolvable symlinks to the
|
||||
// watch list without problems, so maintain consistency with
|
||||
// that. There will be no file events for broken symlinks.
|
||||
// TODO: more specific check; returns os.PathError; ENOENT?
|
||||
return "", nil
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
_, alreadyWatching = w.watches[name]
|
||||
_, alreadyWatching = w.watches[link]
|
||||
w.mu.Unlock()
|
||||
|
||||
if alreadyWatching {
|
||||
return name, nil
|
||||
// Add to watches so we don't get spurious Create events later
|
||||
// on when we diff the directories.
|
||||
w.watches[name] = 0
|
||||
w.fileExists[name] = struct{}{}
|
||||
return link, nil
|
||||
}
|
||||
|
||||
name = link
|
||||
fi, err = os.Lstat(name)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
@@ -411,7 +475,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||
}
|
||||
|
||||
// Retry on EINTR; open() can return EINTR in practice on macOS.
|
||||
// See #354, and go issues 11180 and 39237.
|
||||
// See #354, and Go issues 11180 and 39237.
|
||||
for {
|
||||
watchfd, err = unix.Open(name, openMode, 0)
|
||||
if err == nil {
|
||||
@@ -444,14 +508,13 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||
w.watchesByDir[parentName] = watchesByDir
|
||||
}
|
||||
watchesByDir[watchfd] = struct{}{}
|
||||
|
||||
w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
if isDir {
|
||||
// Watch the directory if it has not been watched before,
|
||||
// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
|
||||
// Watch the directory if it has not been watched before, or if it was
|
||||
// watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
|
||||
w.mu.Lock()
|
||||
|
||||
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
|
||||
@@ -473,13 +536,10 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||
// Event values that it sends down the Events channel.
|
||||
func (w *Watcher) readEvents() {
|
||||
defer func() {
|
||||
err := unix.Close(w.kq)
|
||||
if err != nil {
|
||||
w.Errors <- err
|
||||
}
|
||||
unix.Close(w.closepipe[0])
|
||||
close(w.Events)
|
||||
close(w.Errors)
|
||||
_ = unix.Close(w.kq)
|
||||
unix.Close(w.closepipe[0])
|
||||
}()
|
||||
|
||||
eventBuffer := make([]unix.Kevent_t, 10)
|
||||
@@ -513,18 +573,8 @@ func (w *Watcher) readEvents() {
|
||||
|
||||
event := w.newEvent(path.name, mask)
|
||||
|
||||
if path.isDir && !event.Has(Remove) {
|
||||
// Double check to make sure the directory exists. This can
|
||||
// happen when we do a rm -fr on a recursively watched folders
|
||||
// and we receive a modification event first but the folder has
|
||||
// been deleted and later receive the delete event.
|
||||
if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
|
||||
event.Op |= Remove
|
||||
}
|
||||
}
|
||||
|
||||
if event.Has(Rename) || event.Has(Remove) {
|
||||
w.Remove(event.Name)
|
||||
w.remove(event.Name, false)
|
||||
w.mu.Lock()
|
||||
delete(w.fileExists, event.Name)
|
||||
w.mu.Unlock()
|
||||
@@ -540,26 +590,30 @@ func (w *Watcher) readEvents() {
|
||||
}
|
||||
|
||||
if event.Has(Remove) {
|
||||
// Look for a file that may have overwritten this.
|
||||
// For example, mv f1 f2 will delete f2, then create f2.
|
||||
// Look for a file that may have overwritten this; for example,
|
||||
// mv f1 f2 will delete f2, then create f2.
|
||||
if path.isDir {
|
||||
fileDir := filepath.Clean(event.Name)
|
||||
w.mu.Lock()
|
||||
_, found := w.watches[fileDir]
|
||||
w.mu.Unlock()
|
||||
if found {
|
||||
// make sure the directory exists before we watch for changes. When we
|
||||
// do a recursive watch and perform rm -fr, the parent directory might
|
||||
// have gone missing, ignore the missing directory and let the
|
||||
// upcoming delete event remove the watch from the parent directory.
|
||||
if _, err := os.Lstat(fileDir); err == nil {
|
||||
w.sendDirectoryChangeEvents(fileDir)
|
||||
err := w.sendDirectoryChangeEvents(fileDir)
|
||||
if err != nil {
|
||||
if !w.sendError(err) {
|
||||
closed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filePath := filepath.Clean(event.Name)
|
||||
if fileInfo, err := os.Lstat(filePath); err == nil {
|
||||
w.sendFileCreatedEventIfNew(filePath, fileInfo)
|
||||
if fi, err := os.Lstat(filePath); err == nil {
|
||||
err := w.sendFileCreatedEventIfNew(filePath, fi)
|
||||
if err != nil {
|
||||
if !w.sendError(err) {
|
||||
closed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -582,21 +636,31 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
|
||||
if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
|
||||
e.Op |= Chmod
|
||||
}
|
||||
// No point sending a write and delete event at the same time: if it's gone,
|
||||
// then it's gone.
|
||||
if e.Op.Has(Write) && e.Op.Has(Remove) {
|
||||
e.Op &^= Write
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
|
||||
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
|
||||
// Get all files
|
||||
files, err := ioutil.ReadDir(dirPath)
|
||||
files, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fileInfo := range files {
|
||||
path := filepath.Join(dirPath, fileInfo.Name())
|
||||
for _, f := range files {
|
||||
path := filepath.Join(dirPath, f.Name())
|
||||
|
||||
cleanPath, err := w.internalWatch(path, fileInfo)
|
||||
fi, err := f.Info()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q: %w", path, err)
|
||||
}
|
||||
|
||||
cleanPath, err := w.internalWatch(path, fi)
|
||||
if err != nil {
|
||||
// No permission to read the file; that's not a problem: just skip.
|
||||
// But do add it to w.fileExists to prevent it from being picked up
|
||||
@@ -606,7 +670,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
|
||||
case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
|
||||
cleanPath = filepath.Clean(path)
|
||||
default:
|
||||
return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err)
|
||||
return fmt.Errorf("%q: %w", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,26 +686,37 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
|
||||
//
|
||||
// This functionality is to have the BSD watcher match the inotify, which sends
|
||||
// a create event for files created in a watched directory.
|
||||
func (w *Watcher) sendDirectoryChangeEvents(dir string) {
|
||||
// Get all files
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
func (w *Watcher) sendDirectoryChangeEvents(dir string) error {
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) {
|
||||
return
|
||||
// Directory no longer exists: we can ignore this safely. kqueue will
|
||||
// still give us the correct events.
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
|
||||
}
|
||||
|
||||
// Search for new files
|
||||
for _, fi := range files {
|
||||
err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
|
||||
for _, f := range files {
|
||||
fi, err := f.Info()
|
||||
if err != nil {
|
||||
return
|
||||
return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
|
||||
}
|
||||
|
||||
err = w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
|
||||
if err != nil {
|
||||
// Don't need to send an error if this file isn't readable.
|
||||
if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
|
||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
|
||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fi os.FileInfo) (err error) {
|
||||
w.mu.Lock()
|
||||
_, doesExist := w.fileExists[filePath]
|
||||
w.mu.Unlock()
|
||||
@@ -652,7 +727,7 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
|
||||
}
|
||||
|
||||
// like watchDirectoryFiles (but without doing another ReadDir)
|
||||
filePath, err = w.internalWatch(filePath, fileInfo)
|
||||
filePath, err = w.internalWatch(filePath, fi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -664,10 +739,10 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
|
||||
if fileInfo.IsDir() {
|
||||
// mimic Linux providing delete events for subdirectories
|
||||
// but preserve the flags used if currently watching subdirectory
|
||||
func (w *Watcher) internalWatch(name string, fi os.FileInfo) (string, error) {
|
||||
if fi.IsDir() {
|
||||
// mimic Linux providing delete events for subdirectories, but preserve
|
||||
// the flags used if currently watching subdirectory
|
||||
w.mu.Lock()
|
||||
flags := w.dirFlags[name]
|
||||
w.mu.Unlock()
|
||||
|
||||
203
vendor/github.com/fsnotify/fsnotify/backend_other.go
generated
vendored
203
vendor/github.com/fsnotify/fsnotify/backend_other.go
generated
vendored
@@ -1,39 +1,169 @@
|
||||
//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows
|
||||
// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
|
||||
//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
|
||||
// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
|
||||
|
||||
// Note: the documentation on the Watcher type and methods is generated from
|
||||
// mkdoc.zsh
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
import "errors"
|
||||
|
||||
// Watcher watches a set of files, delivering events to a channel.
|
||||
type Watcher struct{}
|
||||
// Watcher watches a set of paths, delivering events on a channel.
|
||||
//
|
||||
// A watcher should not be copied (e.g. pass it by pointer, rather than by
|
||||
// value).
|
||||
//
|
||||
// # Linux notes
|
||||
//
|
||||
// When a file is removed a Remove event won't be emitted until all file
|
||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||
//
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
//
|
||||
// This is the event that inotify sends, so not much can be changed about this.
|
||||
//
|
||||
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
|
||||
// for the number of watches per user, and fs.inotify.max_user_instances
|
||||
// specifies the maximum number of inotify instances per user. Every Watcher you
|
||||
// create is an "instance", and every path you add is a "watch".
|
||||
//
|
||||
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
|
||||
// /proc/sys/fs/inotify/max_user_instances
|
||||
//
|
||||
// To increase them you can use sysctl or write the value to the /proc file:
|
||||
//
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
//
|
||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||
// your distro's documentation):
|
||||
//
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
//
|
||||
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||
// files" error.
|
||||
//
|
||||
// # kqueue notes (macOS, BSD)
|
||||
//
|
||||
// kqueue requires opening a file descriptor for every file that's being watched;
|
||||
// so if you're watching a directory with five files then that's six file
|
||||
// descriptors. You will run in to your system's "max open files" limit faster on
|
||||
// these platforms.
|
||||
//
|
||||
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
|
||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||
// systems.
|
||||
//
|
||||
// # Windows notes
|
||||
//
|
||||
// Paths can be added as "C:\path\to\dir", but forward slashes
|
||||
// ("C:/path/to/dir") will also work.
|
||||
//
|
||||
// When a watched directory is removed it will always send an event for the
|
||||
// directory itself, but may not send events for all files in that directory.
|
||||
// Sometimes it will send events for all times, sometimes it will send no
|
||||
// events, and often only for some files.
|
||||
//
|
||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
|
||||
// value that is guaranteed to work with SMB filesystems. If you have many
|
||||
// events in quick succession this may not be enough, and you will have to use
|
||||
// [WithBufferSize] to increase the value.
|
||||
type Watcher struct {
|
||||
// Events sends the filesystem change events.
|
||||
//
|
||||
// fsnotify can send the following events; a "path" here can refer to a
|
||||
// file, directory, symbolic link, or special file like a FIFO.
|
||||
//
|
||||
// fsnotify.Create A new path was created; this may be followed by one
|
||||
// or more Write events if data also gets written to a
|
||||
// file.
|
||||
//
|
||||
// fsnotify.Remove A path was removed.
|
||||
//
|
||||
// fsnotify.Rename A path was renamed. A rename is always sent with the
|
||||
// old path as Event.Name, and a Create event will be
|
||||
// sent with the new name. Renames are only sent for
|
||||
// paths that are currently watched; e.g. moving an
|
||||
// unmonitored file into a monitored directory will
|
||||
// show up as just a Create. Similarly, renaming a file
|
||||
// to outside a monitored directory will show up as
|
||||
// only a Rename.
|
||||
//
|
||||
// fsnotify.Write A file or named pipe was written to. A Truncate will
|
||||
// also trigger a Write. A single "write action"
|
||||
// initiated by the user may show up as one or multiple
|
||||
// writes, depending on when the system syncs things to
|
||||
// disk. For example when compiling a large Go program
|
||||
// you may get hundreds of Write events, and you may
|
||||
// want to wait until you've stopped receiving them
|
||||
// (see the dedup example in cmd/fsnotify).
|
||||
//
|
||||
// Some systems may send Write event for directories
|
||||
// when the directory content changes.
|
||||
//
|
||||
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||
// when a file is removed (or more accurately, when a
|
||||
// link to an inode is removed). On kqueue it's sent
|
||||
// when a file is truncated. On Windows it's never
|
||||
// sent.
|
||||
Events chan Event
|
||||
|
||||
// Errors sends any errors.
|
||||
//
|
||||
// ErrEventOverflow is used to indicate there are too many events:
|
||||
//
|
||||
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
|
||||
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
|
||||
// - kqueue, fen: Not used.
|
||||
Errors chan error
|
||||
}
|
||||
|
||||
// NewWatcher creates a new Watcher.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
|
||||
return nil, errors.New("fsnotify not supported on the current platform")
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
return nil
|
||||
}
|
||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
|
||||
// channel.
|
||||
//
|
||||
// The main use case for this is situations with a very large number of events
|
||||
// where the kernel buffer size can't be increased (e.g. due to lack of
|
||||
// permissions). An unbuffered Watcher will perform better for almost all use
|
||||
// cases, and whenever possible you will be better off increasing the kernel
|
||||
// buffers instead of adding a large userspace buffer.
|
||||
func NewBufferedWatcher(sz uint) (*Watcher, error) { return NewWatcher() }
|
||||
|
||||
// Close removes all watches and closes the Events channel.
|
||||
func (w *Watcher) Close() error { return nil }
|
||||
|
||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
|
||||
// yet removed).
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) WatchList() []string { return nil }
|
||||
|
||||
// Add starts monitoring the path for changes.
|
||||
//
|
||||
// A path can only be watched once; attempting to watch it more than once will
|
||||
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// added. A watch will be automatically removed if the path is deleted.
|
||||
// A path can only be watched once; watching it more than once is a no-op and will
|
||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// watched.
|
||||
//
|
||||
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||
// re-created, or if it's moved to a different filesystem.
|
||||
// A watch will be automatically removed if the watched path is deleted or
|
||||
// renamed. The exception is the Windows backend, which doesn't remove the
|
||||
// watcher on renames.
|
||||
//
|
||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||
//
|
||||
// Returns [ErrClosed] if [Watcher.Close] was called.
|
||||
//
|
||||
// See [Watcher.AddWith] for a version that allows adding options.
|
||||
//
|
||||
// # Watching directories
|
||||
//
|
||||
// All files in a directory are monitored, including new files that are created
|
||||
@@ -43,17 +173,26 @@ func (w *Watcher) Close() error {
|
||||
// # Watching files
|
||||
//
|
||||
// Watching individual files (rather than directories) is generally not
|
||||
// recommended as many tools update files atomically. Instead of "just" writing
|
||||
// to the file a temporary file will be written to first, and if successful the
|
||||
// temporary file is moved to to destination removing the original, or some
|
||||
// variant thereof. The watcher on the original file is now lost, as it no
|
||||
// longer exists.
|
||||
// recommended as many programs (especially editors) update files atomically: it
|
||||
// will write to a temporary file which is then moved to to destination,
|
||||
// overwriting the original (or some variant thereof). The watcher on the
|
||||
// original file is now lost, as that no longer exists.
|
||||
//
|
||||
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||
func (w *Watcher) Add(name string) error {
|
||||
return nil
|
||||
}
|
||||
// The upshot of this is that a power failure or crash won't leave a
|
||||
// half-written file.
|
||||
//
|
||||
// Watch the parent directory and use Event.Name to filter out files you're not
|
||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
|
||||
func (w *Watcher) Add(name string) error { return nil }
|
||||
|
||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
|
||||
// the defaults described below are used.
|
||||
//
|
||||
// Possible options are:
|
||||
//
|
||||
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
|
||||
// other platforms. The default is 64K (65536 bytes).
|
||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error { return nil }
|
||||
|
||||
// Remove stops monitoring the path for changes.
|
||||
//
|
||||
@@ -61,6 +200,6 @@ func (w *Watcher) Add(name string) error {
|
||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||
//
|
||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
return nil
|
||||
}
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) Remove(name string) error { return nil }
|
||||
|
||||
245
vendor/github.com/fsnotify/fsnotify/backend_windows.go
generated
vendored
245
vendor/github.com/fsnotify/fsnotify/backend_windows.go
generated
vendored
@@ -1,6 +1,13 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
// Windows backend based on ReadDirectoryChangesW()
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
|
||||
//
|
||||
// Note: the documentation on the Watcher type and methods is generated from
|
||||
// mkdoc.zsh
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
@@ -27,9 +34,9 @@ import (
|
||||
// When a file is removed a Remove event won't be emitted until all file
|
||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||
//
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
//
|
||||
// This is the event that inotify sends, so not much can be changed about this.
|
||||
//
|
||||
@@ -43,16 +50,16 @@ import (
|
||||
//
|
||||
// To increase them you can use sysctl or write the value to the /proc file:
|
||||
//
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
//
|
||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||
// your distro's documentation):
|
||||
//
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
//
|
||||
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||
// files" error.
|
||||
@@ -68,14 +75,20 @@ import (
|
||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||
// systems.
|
||||
//
|
||||
// # macOS notes
|
||||
// # Windows notes
|
||||
//
|
||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||
// Paths can be added as "C:\path\to\dir", but forward slashes
|
||||
// ("C:/path/to/dir") will also work.
|
||||
//
|
||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||
// When a watched directory is removed it will always send an event for the
|
||||
// directory itself, but may not send events for all files in that directory.
|
||||
// Sometimes it will send events for all times, sometimes it will send no
|
||||
// events, and often only for some files.
|
||||
//
|
||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
|
||||
// value that is guaranteed to work with SMB filesystems. If you have many
|
||||
// events in quick succession this may not be enough, and you will have to use
|
||||
// [WithBufferSize] to increase the value.
|
||||
type Watcher struct {
|
||||
// Events sends the filesystem change events.
|
||||
//
|
||||
@@ -102,31 +115,52 @@ type Watcher struct {
|
||||
// initiated by the user may show up as one or multiple
|
||||
// writes, depending on when the system syncs things to
|
||||
// disk. For example when compiling a large Go program
|
||||
// you may get hundreds of Write events, so you
|
||||
// probably want to wait until you've stopped receiving
|
||||
// them (see the dedup example in cmd/fsnotify).
|
||||
// you may get hundreds of Write events, and you may
|
||||
// want to wait until you've stopped receiving them
|
||||
// (see the dedup example in cmd/fsnotify).
|
||||
//
|
||||
// Some systems may send Write event for directories
|
||||
// when the directory content changes.
|
||||
//
|
||||
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||
// when a file is removed (or more accurately, when a
|
||||
// link to an inode is removed). On kqueue it's sent
|
||||
// and on kqueue when a file is truncated. On Windows
|
||||
// it's never sent.
|
||||
// when a file is truncated. On Windows it's never
|
||||
// sent.
|
||||
Events chan Event
|
||||
|
||||
// Errors sends any errors.
|
||||
//
|
||||
// ErrEventOverflow is used to indicate there are too many events:
|
||||
//
|
||||
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
|
||||
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
|
||||
// - kqueue, fen: Not used.
|
||||
Errors chan error
|
||||
|
||||
port windows.Handle // Handle to completion port
|
||||
input chan *input // Inputs to the reader are sent on this channel
|
||||
quit chan chan<- error
|
||||
|
||||
mu sync.Mutex // Protects access to watches, isClosed
|
||||
watches watchMap // Map of watches (key: i-number)
|
||||
isClosed bool // Set to true when Close() is first called
|
||||
mu sync.Mutex // Protects access to watches, closed
|
||||
watches watchMap // Map of watches (key: i-number)
|
||||
closed bool // Set to true when Close() is first called
|
||||
}
|
||||
|
||||
// NewWatcher creates a new Watcher.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
return NewBufferedWatcher(50)
|
||||
}
|
||||
|
||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
|
||||
// channel.
|
||||
//
|
||||
// The main use case for this is situations with a very large number of events
|
||||
// where the kernel buffer size can't be increased (e.g. due to lack of
|
||||
// permissions). An unbuffered Watcher will perform better for almost all use
|
||||
// cases, and whenever possible you will be better off increasing the kernel
|
||||
// buffers instead of adding a large userspace buffer.
|
||||
func NewBufferedWatcher(sz uint) (*Watcher, error) {
|
||||
port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("CreateIoCompletionPort", err)
|
||||
@@ -135,7 +169,7 @@ func NewWatcher() (*Watcher, error) {
|
||||
port: port,
|
||||
watches: make(watchMap),
|
||||
input: make(chan *input, 1),
|
||||
Events: make(chan Event, 50),
|
||||
Events: make(chan Event, sz),
|
||||
Errors: make(chan error),
|
||||
quit: make(chan chan<- error, 1),
|
||||
}
|
||||
@@ -143,6 +177,12 @@ func NewWatcher() (*Watcher, error) {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *Watcher) isClosed() bool {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.closed
|
||||
}
|
||||
|
||||
func (w *Watcher) sendEvent(name string, mask uint64) bool {
|
||||
if mask == 0 {
|
||||
return false
|
||||
@@ -167,14 +207,14 @@ func (w *Watcher) sendError(err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
// Close removes all watches and closes the Events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
w.mu.Lock()
|
||||
if w.isClosed {
|
||||
w.mu.Unlock()
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
w.isClosed = true
|
||||
|
||||
w.mu.Lock()
|
||||
w.closed = true
|
||||
w.mu.Unlock()
|
||||
|
||||
// Send "quit" message to the reader goroutine
|
||||
@@ -188,17 +228,21 @@ func (w *Watcher) Close() error {
|
||||
|
||||
// Add starts monitoring the path for changes.
|
||||
//
|
||||
// A path can only be watched once; attempting to watch it more than once will
|
||||
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// added. A watch will be automatically removed if the path is deleted.
|
||||
// A path can only be watched once; watching it more than once is a no-op and will
|
||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// watched.
|
||||
//
|
||||
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||
// re-created, or if it's moved to a different filesystem.
|
||||
// A watch will be automatically removed if the watched path is deleted or
|
||||
// renamed. The exception is the Windows backend, which doesn't remove the
|
||||
// watcher on renames.
|
||||
//
|
||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||
//
|
||||
// Returns [ErrClosed] if [Watcher.Close] was called.
|
||||
//
|
||||
// See [Watcher.AddWith] for a version that allows adding options.
|
||||
//
|
||||
// # Watching directories
|
||||
//
|
||||
// All files in a directory are monitored, including new files that are created
|
||||
@@ -208,27 +252,41 @@ func (w *Watcher) Close() error {
|
||||
// # Watching files
|
||||
//
|
||||
// Watching individual files (rather than directories) is generally not
|
||||
// recommended as many tools update files atomically. Instead of "just" writing
|
||||
// to the file a temporary file will be written to first, and if successful the
|
||||
// temporary file is moved to to destination removing the original, or some
|
||||
// variant thereof. The watcher on the original file is now lost, as it no
|
||||
// longer exists.
|
||||
// recommended as many programs (especially editors) update files atomically: it
|
||||
// will write to a temporary file which is then moved to to destination,
|
||||
// overwriting the original (or some variant thereof). The watcher on the
|
||||
// original file is now lost, as that no longer exists.
|
||||
//
|
||||
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||
func (w *Watcher) Add(name string) error {
|
||||
w.mu.Lock()
|
||||
if w.isClosed {
|
||||
w.mu.Unlock()
|
||||
return errors.New("watcher already closed")
|
||||
// The upshot of this is that a power failure or crash won't leave a
|
||||
// half-written file.
|
||||
//
|
||||
// Watch the parent directory and use Event.Name to filter out files you're not
|
||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
|
||||
func (w *Watcher) Add(name string) error { return w.AddWith(name) }
|
||||
|
||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
|
||||
// the defaults described below are used.
|
||||
//
|
||||
// Possible options are:
|
||||
//
|
||||
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
|
||||
// other platforms. The default is 64K (65536 bytes).
|
||||
func (w *Watcher) AddWith(name string, opts ...addOpt) error {
|
||||
if w.isClosed() {
|
||||
return ErrClosed
|
||||
}
|
||||
|
||||
with := getOptions(opts...)
|
||||
if with.bufsize < 4096 {
|
||||
return fmt.Errorf("fsnotify.WithBufferSize: buffer size cannot be smaller than 4096 bytes")
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
in := &input{
|
||||
op: opAddWatch,
|
||||
path: filepath.Clean(name),
|
||||
flags: sysFSALLEVENTS,
|
||||
reply: make(chan error),
|
||||
op: opAddWatch,
|
||||
path: filepath.Clean(name),
|
||||
flags: sysFSALLEVENTS,
|
||||
reply: make(chan error),
|
||||
bufsize: with.bufsize,
|
||||
}
|
||||
w.input <- in
|
||||
if err := w.wakeupReader(); err != nil {
|
||||
@@ -243,7 +301,13 @@ func (w *Watcher) Add(name string) error {
|
||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||
//
|
||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
in := &input{
|
||||
op: opRemoveWatch,
|
||||
path: filepath.Clean(name),
|
||||
@@ -256,8 +320,15 @@ func (w *Watcher) Remove(name string) error {
|
||||
return <-in.reply
|
||||
}
|
||||
|
||||
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
|
||||
// yet removed).
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
func (w *Watcher) WatchList() []string {
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
@@ -279,7 +350,6 @@ func (w *Watcher) WatchList() []string {
|
||||
// This should all be removed at some point, and just use windows.FILE_NOTIFY_*
|
||||
const (
|
||||
sysFSALLEVENTS = 0xfff
|
||||
sysFSATTRIB = 0x4
|
||||
sysFSCREATE = 0x100
|
||||
sysFSDELETE = 0x200
|
||||
sysFSDELETESELF = 0x400
|
||||
@@ -305,9 +375,6 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
|
||||
if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
|
||||
e.Op |= Rename
|
||||
}
|
||||
if mask&sysFSATTRIB == sysFSATTRIB {
|
||||
e.Op |= Chmod
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
@@ -321,10 +388,11 @@ const (
|
||||
)
|
||||
|
||||
type input struct {
|
||||
op int
|
||||
path string
|
||||
flags uint32
|
||||
reply chan error
|
||||
op int
|
||||
path string
|
||||
flags uint32
|
||||
bufsize int
|
||||
reply chan error
|
||||
}
|
||||
|
||||
type inode struct {
|
||||
@@ -334,13 +402,14 @@ type inode struct {
|
||||
}
|
||||
|
||||
type watch struct {
|
||||
ov windows.Overlapped
|
||||
ino *inode // i-number
|
||||
path string // Directory path
|
||||
mask uint64 // Directory itself is being watched with these notify flags
|
||||
names map[string]uint64 // Map of names being watched and their notify flags
|
||||
rename string // Remembers the old name while renaming a file
|
||||
buf [65536]byte // 64K buffer
|
||||
ov windows.Overlapped
|
||||
ino *inode // i-number
|
||||
recurse bool // Recursive watch?
|
||||
path string // Directory path
|
||||
mask uint64 // Directory itself is being watched with these notify flags
|
||||
names map[string]uint64 // Map of names being watched and their notify flags
|
||||
rename string // Remembers the old name while renaming a file
|
||||
buf []byte // buffer, allocated later
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -413,7 +482,10 @@ func (m watchMap) set(ino *inode, watch *watch) {
|
||||
}
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
||||
func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
|
||||
//pathname, recurse := recursivePath(pathname)
|
||||
recurse := false
|
||||
|
||||
dir, err := w.getDir(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -433,9 +505,11 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
||||
return os.NewSyscallError("CreateIoCompletionPort", err)
|
||||
}
|
||||
watchEntry = &watch{
|
||||
ino: ino,
|
||||
path: dir,
|
||||
names: make(map[string]uint64),
|
||||
ino: ino,
|
||||
path: dir,
|
||||
names: make(map[string]uint64),
|
||||
recurse: recurse,
|
||||
buf: make([]byte, bufsize),
|
||||
}
|
||||
w.mu.Lock()
|
||||
w.watches.set(ino, watchEntry)
|
||||
@@ -465,6 +539,8 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (w *Watcher) remWatch(pathname string) error {
|
||||
pathname, recurse := recursivePath(pathname)
|
||||
|
||||
dir, err := w.getDir(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -478,6 +554,10 @@ func (w *Watcher) remWatch(pathname string) error {
|
||||
watch := w.watches.get(ino)
|
||||
w.mu.Unlock()
|
||||
|
||||
if recurse && !watch.recurse {
|
||||
return fmt.Errorf("can't use \\... with non-recursive watch %q", pathname)
|
||||
}
|
||||
|
||||
err = windows.CloseHandle(ino.handle)
|
||||
if err != nil {
|
||||
w.sendError(os.NewSyscallError("CloseHandle", err))
|
||||
@@ -535,8 +615,11 @@ func (w *Watcher) startRead(watch *watch) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
|
||||
uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
|
||||
// We need to pass the array, rather than the slice.
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf))
|
||||
rdErr := windows.ReadDirectoryChanges(watch.ino.handle,
|
||||
(*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len),
|
||||
watch.recurse, mask, nil, &watch.ov, 0)
|
||||
if rdErr != nil {
|
||||
err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
|
||||
if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
|
||||
@@ -563,9 +646,8 @@ func (w *Watcher) readEvents() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
for {
|
||||
// This error is handled after the watch == nil check below.
|
||||
qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE)
|
||||
// This error is handled after the watch == nil check below. NOTE: this
|
||||
// seems odd, note sure if it's correct.
|
||||
|
||||
watch := (*watch)(unsafe.Pointer(ov))
|
||||
if watch == nil {
|
||||
@@ -595,7 +677,7 @@ func (w *Watcher) readEvents() {
|
||||
case in := <-w.input:
|
||||
switch in.op {
|
||||
case opAddWatch:
|
||||
in.reply <- w.addWatch(in.path, uint64(in.flags))
|
||||
in.reply <- w.addWatch(in.path, uint64(in.flags), in.bufsize)
|
||||
case opRemoveWatch:
|
||||
in.reply <- w.remWatch(in.path)
|
||||
}
|
||||
@@ -605,6 +687,8 @@ func (w *Watcher) readEvents() {
|
||||
}
|
||||
|
||||
switch qErr {
|
||||
case nil:
|
||||
// No error
|
||||
case windows.ERROR_MORE_DATA:
|
||||
if watch == nil {
|
||||
w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer"))
|
||||
@@ -626,13 +710,12 @@ func (w *Watcher) readEvents() {
|
||||
default:
|
||||
w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr))
|
||||
continue
|
||||
case nil:
|
||||
}
|
||||
|
||||
var offset uint32
|
||||
for {
|
||||
if n == 0 {
|
||||
w.sendError(errors.New("short read in readEvents()"))
|
||||
w.sendError(ErrEventOverflow)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -703,8 +786,9 @@ func (w *Watcher) readEvents() {
|
||||
|
||||
// Error!
|
||||
if offset >= n {
|
||||
//lint:ignore ST1005 Windows should be capitalized
|
||||
w.sendError(errors.New(
|
||||
"Windows system assumed buffer larger than it is, events have likely been missed."))
|
||||
"Windows system assumed buffer larger than it is, events have likely been missed"))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -720,9 +804,6 @@ func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
|
||||
if mask&sysFSMODIFY != 0 {
|
||||
m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
|
||||
}
|
||||
if mask&sysFSATTRIB != 0 {
|
||||
m |= windows.FILE_NOTIFY_CHANGE_ATTRIBUTES
|
||||
}
|
||||
if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
|
||||
m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME
|
||||
}
|
||||
|
||||
91
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
91
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
@@ -1,13 +1,18 @@
|
||||
//go:build !plan9
|
||||
// +build !plan9
|
||||
|
||||
// Package fsnotify provides a cross-platform interface for file system
|
||||
// notifications.
|
||||
//
|
||||
// Currently supported systems:
|
||||
//
|
||||
// Linux 2.6.32+ via inotify
|
||||
// BSD, macOS via kqueue
|
||||
// Windows via ReadDirectoryChangesW
|
||||
// illumos via FEN
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -33,34 +38,52 @@ type Op uint32
|
||||
// The operations fsnotify can trigger; see the documentation on [Watcher] for a
|
||||
// full description, and check them with [Event.Has].
|
||||
const (
|
||||
// A new pathname was created.
|
||||
Create Op = 1 << iota
|
||||
|
||||
// The pathname was written to; this does *not* mean the write has finished,
|
||||
// and a write can be followed by more writes.
|
||||
Write
|
||||
|
||||
// The path was removed; any watches on it will be removed. Some "remove"
|
||||
// operations may trigger a Rename if the file is actually moved (for
|
||||
// example "remove to trash" is often a rename).
|
||||
Remove
|
||||
|
||||
// The path was renamed to something else; any watched on it will be
|
||||
// removed.
|
||||
Rename
|
||||
|
||||
// File attributes were changed.
|
||||
//
|
||||
// It's generally not recommended to take action on this event, as it may
|
||||
// get triggered very frequently by some software. For example, Spotlight
|
||||
// indexing on macOS, anti-virus software, backup software, etc.
|
||||
Chmod
|
||||
)
|
||||
|
||||
// Common errors that can be reported by a watcher
|
||||
// Common errors that can be reported.
|
||||
var (
|
||||
ErrNonExistentWatch = errors.New("can't remove non-existent watcher")
|
||||
ErrEventOverflow = errors.New("fsnotify queue overflow")
|
||||
ErrNonExistentWatch = errors.New("fsnotify: can't remove non-existent watch")
|
||||
ErrEventOverflow = errors.New("fsnotify: queue or buffer overflow")
|
||||
ErrClosed = errors.New("fsnotify: watcher already closed")
|
||||
)
|
||||
|
||||
func (op Op) String() string {
|
||||
func (o Op) String() string {
|
||||
var b strings.Builder
|
||||
if op.Has(Create) {
|
||||
if o.Has(Create) {
|
||||
b.WriteString("|CREATE")
|
||||
}
|
||||
if op.Has(Remove) {
|
||||
if o.Has(Remove) {
|
||||
b.WriteString("|REMOVE")
|
||||
}
|
||||
if op.Has(Write) {
|
||||
if o.Has(Write) {
|
||||
b.WriteString("|WRITE")
|
||||
}
|
||||
if op.Has(Rename) {
|
||||
if o.Has(Rename) {
|
||||
b.WriteString("|RENAME")
|
||||
}
|
||||
if op.Has(Chmod) {
|
||||
if o.Has(Chmod) {
|
||||
b.WriteString("|CHMOD")
|
||||
}
|
||||
if b.Len() == 0 {
|
||||
@@ -70,7 +93,7 @@ func (op Op) String() string {
|
||||
}
|
||||
|
||||
// Has reports if this operation has the given operation.
|
||||
func (o Op) Has(h Op) bool { return o&h == h }
|
||||
func (o Op) Has(h Op) bool { return o&h != 0 }
|
||||
|
||||
// Has reports if this event has the given operation.
|
||||
func (e Event) Has(op Op) bool { return e.Op.Has(op) }
|
||||
@@ -79,3 +102,45 @@ func (e Event) Has(op Op) bool { return e.Op.Has(op) }
|
||||
func (e Event) String() string {
|
||||
return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)
|
||||
}
|
||||
|
||||
type (
|
||||
addOpt func(opt *withOpts)
|
||||
withOpts struct {
|
||||
bufsize int
|
||||
}
|
||||
)
|
||||
|
||||
var defaultOpts = withOpts{
|
||||
bufsize: 65536, // 64K
|
||||
}
|
||||
|
||||
func getOptions(opts ...addOpt) withOpts {
|
||||
with := defaultOpts
|
||||
for _, o := range opts {
|
||||
o(&with)
|
||||
}
|
||||
return with
|
||||
}
|
||||
|
||||
// WithBufferSize sets the [ReadDirectoryChangesW] buffer size.
|
||||
//
|
||||
// This only has effect on Windows systems, and is a no-op for other backends.
|
||||
//
|
||||
// The default value is 64K (65536 bytes) which is the highest value that works
|
||||
// on all filesystems and should be enough for most applications, but if you
|
||||
// have a large burst of events it may not be enough. You can increase it if
|
||||
// you're hitting "queue or buffer overflow" errors ([ErrEventOverflow]).
|
||||
//
|
||||
// [ReadDirectoryChangesW]: https://learn.microsoft.com/en-gb/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
|
||||
func WithBufferSize(bytes int) addOpt {
|
||||
return func(opt *withOpts) { opt.bufsize = bytes }
|
||||
}
|
||||
|
||||
// Check if this path is recursive (ends with "/..." or "\..."), and return the
|
||||
// path with the /... stripped.
|
||||
func recursivePath(path string) (string, bool) {
|
||||
if filepath.Base(path) == "..." {
|
||||
return filepath.Dir(path), true
|
||||
}
|
||||
return path, false
|
||||
}
|
||||
|
||||
123
vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
generated
vendored
123
vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
generated
vendored
@@ -2,8 +2,8 @@
|
||||
[ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1
|
||||
setopt err_exit no_unset pipefail extended_glob
|
||||
|
||||
# Simple script to update the godoc comments on all watchers. Probably took me
|
||||
# more time to write this than doing it manually, but ah well 🙃
|
||||
# Simple script to update the godoc comments on all watchers so you don't need
|
||||
# to update the same comment 5 times.
|
||||
|
||||
watcher=$(<<EOF
|
||||
// Watcher watches a set of paths, delivering events on a channel.
|
||||
@@ -16,9 +16,9 @@ watcher=$(<<EOF
|
||||
// When a file is removed a Remove event won't be emitted until all file
|
||||
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||
//
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
// fp := os.Open("file")
|
||||
// os.Remove("file") // Triggers Chmod
|
||||
// fp.Close() // Triggers Remove
|
||||
//
|
||||
// This is the event that inotify sends, so not much can be changed about this.
|
||||
//
|
||||
@@ -32,16 +32,16 @@ watcher=$(<<EOF
|
||||
//
|
||||
// To increase them you can use sysctl or write the value to the /proc file:
|
||||
//
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
//
|
||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||
// your distro's documentation):
|
||||
//
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
//
|
||||
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||
// files" error.
|
||||
@@ -57,14 +57,20 @@ watcher=$(<<EOF
|
||||
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||
// systems.
|
||||
//
|
||||
// # macOS notes
|
||||
// # Windows notes
|
||||
//
|
||||
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||
// Paths can be added as "C:\\path\\to\\dir", but forward slashes
|
||||
// ("C:/path/to/dir") will also work.
|
||||
//
|
||||
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||
// When a watched directory is removed it will always send an event for the
|
||||
// directory itself, but may not send events for all files in that directory.
|
||||
// Sometimes it will send events for all times, sometimes it will send no
|
||||
// events, and often only for some files.
|
||||
//
|
||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
|
||||
// value that is guaranteed to work with SMB filesystems. If you have many
|
||||
// events in quick succession this may not be enough, and you will have to use
|
||||
// [WithBufferSize] to increase the value.
|
||||
EOF
|
||||
)
|
||||
|
||||
@@ -73,20 +79,36 @@ new=$(<<EOF
|
||||
EOF
|
||||
)
|
||||
|
||||
newbuffered=$(<<EOF
|
||||
// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
|
||||
// channel.
|
||||
//
|
||||
// The main use case for this is situations with a very large number of events
|
||||
// where the kernel buffer size can't be increased (e.g. due to lack of
|
||||
// permissions). An unbuffered Watcher will perform better for almost all use
|
||||
// cases, and whenever possible you will be better off increasing the kernel
|
||||
// buffers instead of adding a large userspace buffer.
|
||||
EOF
|
||||
)
|
||||
|
||||
add=$(<<EOF
|
||||
// Add starts monitoring the path for changes.
|
||||
//
|
||||
// A path can only be watched once; attempting to watch it more than once will
|
||||
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// added. A watch will be automatically removed if the path is deleted.
|
||||
// A path can only be watched once; watching it more than once is a no-op and will
|
||||
// not return an error. Paths that do not yet exist on the filesystem cannot be
|
||||
// watched.
|
||||
//
|
||||
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||
// re-created, or if it's moved to a different filesystem.
|
||||
// A watch will be automatically removed if the watched path is deleted or
|
||||
// renamed. The exception is the Windows backend, which doesn't remove the
|
||||
// watcher on renames.
|
||||
//
|
||||
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||
//
|
||||
// Returns [ErrClosed] if [Watcher.Close] was called.
|
||||
//
|
||||
// See [Watcher.AddWith] for a version that allows adding options.
|
||||
//
|
||||
// # Watching directories
|
||||
//
|
||||
// All files in a directory are monitored, including new files that are created
|
||||
@@ -96,14 +118,27 @@ add=$(<<EOF
|
||||
// # Watching files
|
||||
//
|
||||
// Watching individual files (rather than directories) is generally not
|
||||
// recommended as many tools update files atomically. Instead of "just" writing
|
||||
// to the file a temporary file will be written to first, and if successful the
|
||||
// temporary file is moved to to destination removing the original, or some
|
||||
// variant thereof. The watcher on the original file is now lost, as it no
|
||||
// longer exists.
|
||||
// recommended as many programs (especially editors) update files atomically: it
|
||||
// will write to a temporary file which is then moved to to destination,
|
||||
// overwriting the original (or some variant thereof). The watcher on the
|
||||
// original file is now lost, as that no longer exists.
|
||||
//
|
||||
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||
// The upshot of this is that a power failure or crash won't leave a
|
||||
// half-written file.
|
||||
//
|
||||
// Watch the parent directory and use Event.Name to filter out files you're not
|
||||
// interested in. There is an example of this in cmd/fsnotify/file.go.
|
||||
EOF
|
||||
)
|
||||
|
||||
addwith=$(<<EOF
|
||||
// AddWith is like [Watcher.Add], but allows adding options. When using Add()
|
||||
// the defaults described below are used.
|
||||
//
|
||||
// Possible options are:
|
||||
//
|
||||
// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
|
||||
// other platforms. The default is 64K (65536 bytes).
|
||||
EOF
|
||||
)
|
||||
|
||||
@@ -114,16 +149,21 @@ remove=$(<<EOF
|
||||
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||
//
|
||||
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
EOF
|
||||
)
|
||||
|
||||
close=$(<<EOF
|
||||
// Close removes all watches and closes the events channel.
|
||||
// Close removes all watches and closes the Events channel.
|
||||
EOF
|
||||
)
|
||||
|
||||
watchlist=$(<<EOF
|
||||
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
|
||||
// yet removed).
|
||||
//
|
||||
// Returns nil if [Watcher.Close] was called.
|
||||
EOF
|
||||
)
|
||||
|
||||
@@ -153,20 +193,29 @@ events=$(<<EOF
|
||||
// initiated by the user may show up as one or multiple
|
||||
// writes, depending on when the system syncs things to
|
||||
// disk. For example when compiling a large Go program
|
||||
// you may get hundreds of Write events, so you
|
||||
// probably want to wait until you've stopped receiving
|
||||
// them (see the dedup example in cmd/fsnotify).
|
||||
// you may get hundreds of Write events, and you may
|
||||
// want to wait until you've stopped receiving them
|
||||
// (see the dedup example in cmd/fsnotify).
|
||||
//
|
||||
// Some systems may send Write event for directories
|
||||
// when the directory content changes.
|
||||
//
|
||||
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||
// when a file is removed (or more accurately, when a
|
||||
// link to an inode is removed). On kqueue it's sent
|
||||
// and on kqueue when a file is truncated. On Windows
|
||||
// it's never sent.
|
||||
// when a file is truncated. On Windows it's never
|
||||
// sent.
|
||||
EOF
|
||||
)
|
||||
|
||||
errors=$(<<EOF
|
||||
// Errors sends any errors.
|
||||
//
|
||||
// ErrEventOverflow is used to indicate there are too many events:
|
||||
//
|
||||
// - inotify: There are too many queued events (fs.inotify.max_queued_events sysctl)
|
||||
// - windows: The buffer size is too small; WithBufferSize() can be used to increase it.
|
||||
// - kqueue, fen: Not used.
|
||||
EOF
|
||||
)
|
||||
|
||||
@@ -200,7 +249,9 @@ set-cmt() {
|
||||
|
||||
set-cmt '^type Watcher struct ' $watcher
|
||||
set-cmt '^func NewWatcher(' $new
|
||||
set-cmt '^func NewBufferedWatcher(' $newbuffered
|
||||
set-cmt '^func (w \*Watcher) Add(' $add
|
||||
set-cmt '^func (w \*Watcher) AddWith(' $addwith
|
||||
set-cmt '^func (w \*Watcher) Remove(' $remove
|
||||
set-cmt '^func (w \*Watcher) Close(' $close
|
||||
set-cmt '^func (w \*Watcher) WatchList(' $watchlist
|
||||
|
||||
43
vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml
generated
vendored
43
vendor/github.com/go-asn1-ber/asn1-ber/.travis.yml
generated
vendored
@@ -1,43 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.2.x
|
||||
- 1.6.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.14.x
|
||||
- tip
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
||||
arch:
|
||||
- amd64
|
||||
- ppc64le
|
||||
|
||||
dist: xenial
|
||||
|
||||
env:
|
||||
- GOARCH=amd64
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- os: windows
|
||||
go: 1.14.x
|
||||
- os: osx
|
||||
go: 1.14.x
|
||||
- os: linux
|
||||
go: 1.14.x
|
||||
arch: arm64
|
||||
- os: linux
|
||||
go: 1.14.x
|
||||
env:
|
||||
- GOARCH=386
|
||||
|
||||
script:
|
||||
- go test -v -cover ./... || go test -v ./...
|
||||
matrix:
|
||||
allowfailures:
|
||||
go: 1.2.x
|
||||
29
vendor/github.com/go-asn1-ber/asn1-ber/ber.go
generated
vendored
29
vendor/github.com/go-asn1-ber/asn1-ber/ber.go
generated
vendored
@@ -170,12 +170,10 @@ func PrintPacket(p *Packet) {
|
||||
printPacket(os.Stdout, p, 0, false)
|
||||
}
|
||||
|
||||
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
|
||||
indentStr := ""
|
||||
|
||||
for len(indentStr) != indent {
|
||||
indentStr += " "
|
||||
}
|
||||
// Return a string describing packet content. This is not recursive,
|
||||
// If the packet is a sequence, use `printPacket()`, or browse
|
||||
// sequence yourself.
|
||||
func DescribePacket(p *Packet) string {
|
||||
|
||||
classStr := ClassMap[p.ClassType]
|
||||
|
||||
@@ -194,7 +192,17 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
|
||||
description = p.Description + ": "
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(out, "%s%s(%s, %s, %s) Len=%d %q\n", indentStr, description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
|
||||
return fmt.Sprintf("%s(%s, %s, %s) Len=%d %q", description, classStr, tagTypeStr, tagStr, p.Data.Len(), value)
|
||||
}
|
||||
|
||||
func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) {
|
||||
indentStr := ""
|
||||
|
||||
for len(indentStr) != indent {
|
||||
indentStr += " "
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(out, "%s%s\n", indentStr, DescribePacket(p))
|
||||
|
||||
if printBytes {
|
||||
PrintBytes(out, p.Bytes(), indentStr)
|
||||
@@ -317,7 +325,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
|
||||
// Read the next packet
|
||||
child, r, err := readPacket(reader)
|
||||
if err != nil {
|
||||
return nil, read, err
|
||||
return nil, read, unexpectedEOF(err)
|
||||
}
|
||||
contentRead += r
|
||||
read += r
|
||||
@@ -348,10 +356,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) {
|
||||
if length > 0 {
|
||||
_, err := io.ReadFull(reader, content)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil, read, io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, read, err
|
||||
return nil, read, unexpectedEOF(err)
|
||||
}
|
||||
read += length
|
||||
}
|
||||
|
||||
2
vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
generated
vendored
2
vendor/github.com/go-asn1-ber/asn1-ber/identifier.go
generated
vendored
@@ -37,7 +37,7 @@ func readIdentifier(reader io.Reader) (Identifier, int, error) {
|
||||
if Debug {
|
||||
fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
|
||||
}
|
||||
return Identifier{}, read, err
|
||||
return Identifier{}, read, unexpectedEOF(err)
|
||||
}
|
||||
tagBytes++
|
||||
read++
|
||||
|
||||
4
vendor/github.com/go-asn1-ber/asn1-ber/length.go
generated
vendored
4
vendor/github.com/go-asn1-ber/asn1-ber/length.go
generated
vendored
@@ -13,7 +13,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
|
||||
if Debug {
|
||||
fmt.Printf("error reading length byte: %v\n", err)
|
||||
}
|
||||
return 0, 0, err
|
||||
return 0, 0, unexpectedEOF(err)
|
||||
}
|
||||
read++
|
||||
|
||||
@@ -47,7 +47,7 @@ func readLength(reader io.Reader) (length int, read int, err error) {
|
||||
if Debug {
|
||||
fmt.Printf("error reading long-form length byte %d: %v\n", i, err)
|
||||
}
|
||||
return 0, read, err
|
||||
return 0, read, unexpectedEOF(err)
|
||||
}
|
||||
read++
|
||||
|
||||
|
||||
6
vendor/github.com/go-asn1-ber/asn1-ber/real.go
generated
vendored
6
vendor/github.com/go-asn1-ber/asn1-ber/real.go
generated
vendored
@@ -89,12 +89,18 @@ func parseBinaryFloat(v []byte) (float64, error) {
|
||||
case 0x02:
|
||||
expLen = 3
|
||||
case 0x03:
|
||||
if len(v) < 2 {
|
||||
return 0.0, errors.New("invalid data")
|
||||
}
|
||||
expLen = int(v[0])
|
||||
if expLen > 8 {
|
||||
return 0.0, errors.New("too big value of exponent")
|
||||
}
|
||||
v = v[1:]
|
||||
}
|
||||
if expLen > len(v) {
|
||||
return 0.0, errors.New("too big value of exponent")
|
||||
}
|
||||
buf, v = v[:expLen], v[expLen:]
|
||||
exponent, err := ParseInt64(buf)
|
||||
if err != nil {
|
||||
|
||||
10
vendor/github.com/go-asn1-ber/asn1-ber/util.go
generated
vendored
10
vendor/github.com/go-asn1-ber/asn1-ber/util.go
generated
vendored
@@ -6,14 +6,18 @@ func readByte(reader io.Reader) (byte, error) {
|
||||
bytes := make([]byte, 1)
|
||||
_, err := io.ReadFull(reader, bytes)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return bytes[0], nil
|
||||
}
|
||||
|
||||
func unexpectedEOF(err error) error {
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func isEOCPacket(p *Packet) bool {
|
||||
return p != nil &&
|
||||
p.Tag == TagEOC &&
|
||||
|
||||
128
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
128
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
@@ -12,6 +12,31 @@ import (
|
||||
|
||||
// Symbols defined in public import of google/protobuf/descriptor.proto.
|
||||
|
||||
type Edition = descriptorpb.Edition
|
||||
|
||||
const Edition_EDITION_UNKNOWN = descriptorpb.Edition_EDITION_UNKNOWN
|
||||
const Edition_EDITION_PROTO2 = descriptorpb.Edition_EDITION_PROTO2
|
||||
const Edition_EDITION_PROTO3 = descriptorpb.Edition_EDITION_PROTO3
|
||||
const Edition_EDITION_2023 = descriptorpb.Edition_EDITION_2023
|
||||
const Edition_EDITION_2024 = descriptorpb.Edition_EDITION_2024
|
||||
const Edition_EDITION_1_TEST_ONLY = descriptorpb.Edition_EDITION_1_TEST_ONLY
|
||||
const Edition_EDITION_2_TEST_ONLY = descriptorpb.Edition_EDITION_2_TEST_ONLY
|
||||
const Edition_EDITION_99997_TEST_ONLY = descriptorpb.Edition_EDITION_99997_TEST_ONLY
|
||||
const Edition_EDITION_99998_TEST_ONLY = descriptorpb.Edition_EDITION_99998_TEST_ONLY
|
||||
const Edition_EDITION_99999_TEST_ONLY = descriptorpb.Edition_EDITION_99999_TEST_ONLY
|
||||
const Edition_EDITION_MAX = descriptorpb.Edition_EDITION_MAX
|
||||
|
||||
var Edition_name = descriptorpb.Edition_name
|
||||
var Edition_value = descriptorpb.Edition_value
|
||||
|
||||
type ExtensionRangeOptions_VerificationState = descriptorpb.ExtensionRangeOptions_VerificationState
|
||||
|
||||
const ExtensionRangeOptions_DECLARATION = descriptorpb.ExtensionRangeOptions_DECLARATION
|
||||
const ExtensionRangeOptions_UNVERIFIED = descriptorpb.ExtensionRangeOptions_UNVERIFIED
|
||||
|
||||
var ExtensionRangeOptions_VerificationState_name = descriptorpb.ExtensionRangeOptions_VerificationState_name
|
||||
var ExtensionRangeOptions_VerificationState_value = descriptorpb.ExtensionRangeOptions_VerificationState_value
|
||||
|
||||
type FieldDescriptorProto_Type = descriptorpb.FieldDescriptorProto_Type
|
||||
|
||||
const FieldDescriptorProto_TYPE_DOUBLE = descriptorpb.FieldDescriptorProto_TYPE_DOUBLE
|
||||
@@ -39,8 +64,8 @@ var FieldDescriptorProto_Type_value = descriptorpb.FieldDescriptorProto_Type_val
|
||||
type FieldDescriptorProto_Label = descriptorpb.FieldDescriptorProto_Label
|
||||
|
||||
const FieldDescriptorProto_LABEL_OPTIONAL = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL
|
||||
const FieldDescriptorProto_LABEL_REQUIRED = descriptorpb.FieldDescriptorProto_LABEL_REQUIRED
|
||||
const FieldDescriptorProto_LABEL_REPEATED = descriptorpb.FieldDescriptorProto_LABEL_REPEATED
|
||||
const FieldDescriptorProto_LABEL_REQUIRED = descriptorpb.FieldDescriptorProto_LABEL_REQUIRED
|
||||
|
||||
var FieldDescriptorProto_Label_name = descriptorpb.FieldDescriptorProto_Label_name
|
||||
var FieldDescriptorProto_Label_value = descriptorpb.FieldDescriptorProto_Label_value
|
||||
@@ -72,6 +97,31 @@ const FieldOptions_JS_NUMBER = descriptorpb.FieldOptions_JS_NUMBER
|
||||
var FieldOptions_JSType_name = descriptorpb.FieldOptions_JSType_name
|
||||
var FieldOptions_JSType_value = descriptorpb.FieldOptions_JSType_value
|
||||
|
||||
type FieldOptions_OptionRetention = descriptorpb.FieldOptions_OptionRetention
|
||||
|
||||
const FieldOptions_RETENTION_UNKNOWN = descriptorpb.FieldOptions_RETENTION_UNKNOWN
|
||||
const FieldOptions_RETENTION_RUNTIME = descriptorpb.FieldOptions_RETENTION_RUNTIME
|
||||
const FieldOptions_RETENTION_SOURCE = descriptorpb.FieldOptions_RETENTION_SOURCE
|
||||
|
||||
var FieldOptions_OptionRetention_name = descriptorpb.FieldOptions_OptionRetention_name
|
||||
var FieldOptions_OptionRetention_value = descriptorpb.FieldOptions_OptionRetention_value
|
||||
|
||||
type FieldOptions_OptionTargetType = descriptorpb.FieldOptions_OptionTargetType
|
||||
|
||||
const FieldOptions_TARGET_TYPE_UNKNOWN = descriptorpb.FieldOptions_TARGET_TYPE_UNKNOWN
|
||||
const FieldOptions_TARGET_TYPE_FILE = descriptorpb.FieldOptions_TARGET_TYPE_FILE
|
||||
const FieldOptions_TARGET_TYPE_EXTENSION_RANGE = descriptorpb.FieldOptions_TARGET_TYPE_EXTENSION_RANGE
|
||||
const FieldOptions_TARGET_TYPE_MESSAGE = descriptorpb.FieldOptions_TARGET_TYPE_MESSAGE
|
||||
const FieldOptions_TARGET_TYPE_FIELD = descriptorpb.FieldOptions_TARGET_TYPE_FIELD
|
||||
const FieldOptions_TARGET_TYPE_ONEOF = descriptorpb.FieldOptions_TARGET_TYPE_ONEOF
|
||||
const FieldOptions_TARGET_TYPE_ENUM = descriptorpb.FieldOptions_TARGET_TYPE_ENUM
|
||||
const FieldOptions_TARGET_TYPE_ENUM_ENTRY = descriptorpb.FieldOptions_TARGET_TYPE_ENUM_ENTRY
|
||||
const FieldOptions_TARGET_TYPE_SERVICE = descriptorpb.FieldOptions_TARGET_TYPE_SERVICE
|
||||
const FieldOptions_TARGET_TYPE_METHOD = descriptorpb.FieldOptions_TARGET_TYPE_METHOD
|
||||
|
||||
var FieldOptions_OptionTargetType_name = descriptorpb.FieldOptions_OptionTargetType_name
|
||||
var FieldOptions_OptionTargetType_value = descriptorpb.FieldOptions_OptionTargetType_value
|
||||
|
||||
type MethodOptions_IdempotencyLevel = descriptorpb.MethodOptions_IdempotencyLevel
|
||||
|
||||
const MethodOptions_IDEMPOTENCY_UNKNOWN = descriptorpb.MethodOptions_IDEMPOTENCY_UNKNOWN
|
||||
@@ -81,10 +131,77 @@ const MethodOptions_IDEMPOTENT = descriptorpb.MethodOptions_IDEMPOTENT
|
||||
var MethodOptions_IdempotencyLevel_name = descriptorpb.MethodOptions_IdempotencyLevel_name
|
||||
var MethodOptions_IdempotencyLevel_value = descriptorpb.MethodOptions_IdempotencyLevel_value
|
||||
|
||||
type FeatureSet_FieldPresence = descriptorpb.FeatureSet_FieldPresence
|
||||
|
||||
const FeatureSet_FIELD_PRESENCE_UNKNOWN = descriptorpb.FeatureSet_FIELD_PRESENCE_UNKNOWN
|
||||
const FeatureSet_EXPLICIT = descriptorpb.FeatureSet_EXPLICIT
|
||||
const FeatureSet_IMPLICIT = descriptorpb.FeatureSet_IMPLICIT
|
||||
const FeatureSet_LEGACY_REQUIRED = descriptorpb.FeatureSet_LEGACY_REQUIRED
|
||||
|
||||
var FeatureSet_FieldPresence_name = descriptorpb.FeatureSet_FieldPresence_name
|
||||
var FeatureSet_FieldPresence_value = descriptorpb.FeatureSet_FieldPresence_value
|
||||
|
||||
type FeatureSet_EnumType = descriptorpb.FeatureSet_EnumType
|
||||
|
||||
const FeatureSet_ENUM_TYPE_UNKNOWN = descriptorpb.FeatureSet_ENUM_TYPE_UNKNOWN
|
||||
const FeatureSet_OPEN = descriptorpb.FeatureSet_OPEN
|
||||
const FeatureSet_CLOSED = descriptorpb.FeatureSet_CLOSED
|
||||
|
||||
var FeatureSet_EnumType_name = descriptorpb.FeatureSet_EnumType_name
|
||||
var FeatureSet_EnumType_value = descriptorpb.FeatureSet_EnumType_value
|
||||
|
||||
type FeatureSet_RepeatedFieldEncoding = descriptorpb.FeatureSet_RepeatedFieldEncoding
|
||||
|
||||
const FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN = descriptorpb.FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN
|
||||
const FeatureSet_PACKED = descriptorpb.FeatureSet_PACKED
|
||||
const FeatureSet_EXPANDED = descriptorpb.FeatureSet_EXPANDED
|
||||
|
||||
var FeatureSet_RepeatedFieldEncoding_name = descriptorpb.FeatureSet_RepeatedFieldEncoding_name
|
||||
var FeatureSet_RepeatedFieldEncoding_value = descriptorpb.FeatureSet_RepeatedFieldEncoding_value
|
||||
|
||||
type FeatureSet_Utf8Validation = descriptorpb.FeatureSet_Utf8Validation
|
||||
|
||||
const FeatureSet_UTF8_VALIDATION_UNKNOWN = descriptorpb.FeatureSet_UTF8_VALIDATION_UNKNOWN
|
||||
const FeatureSet_VERIFY = descriptorpb.FeatureSet_VERIFY
|
||||
const FeatureSet_NONE = descriptorpb.FeatureSet_NONE
|
||||
|
||||
var FeatureSet_Utf8Validation_name = descriptorpb.FeatureSet_Utf8Validation_name
|
||||
var FeatureSet_Utf8Validation_value = descriptorpb.FeatureSet_Utf8Validation_value
|
||||
|
||||
type FeatureSet_MessageEncoding = descriptorpb.FeatureSet_MessageEncoding
|
||||
|
||||
const FeatureSet_MESSAGE_ENCODING_UNKNOWN = descriptorpb.FeatureSet_MESSAGE_ENCODING_UNKNOWN
|
||||
const FeatureSet_LENGTH_PREFIXED = descriptorpb.FeatureSet_LENGTH_PREFIXED
|
||||
const FeatureSet_DELIMITED = descriptorpb.FeatureSet_DELIMITED
|
||||
|
||||
var FeatureSet_MessageEncoding_name = descriptorpb.FeatureSet_MessageEncoding_name
|
||||
var FeatureSet_MessageEncoding_value = descriptorpb.FeatureSet_MessageEncoding_value
|
||||
|
||||
type FeatureSet_JsonFormat = descriptorpb.FeatureSet_JsonFormat
|
||||
|
||||
const FeatureSet_JSON_FORMAT_UNKNOWN = descriptorpb.FeatureSet_JSON_FORMAT_UNKNOWN
|
||||
const FeatureSet_ALLOW = descriptorpb.FeatureSet_ALLOW
|
||||
const FeatureSet_LEGACY_BEST_EFFORT = descriptorpb.FeatureSet_LEGACY_BEST_EFFORT
|
||||
|
||||
var FeatureSet_JsonFormat_name = descriptorpb.FeatureSet_JsonFormat_name
|
||||
var FeatureSet_JsonFormat_value = descriptorpb.FeatureSet_JsonFormat_value
|
||||
|
||||
type GeneratedCodeInfo_Annotation_Semantic = descriptorpb.GeneratedCodeInfo_Annotation_Semantic
|
||||
|
||||
const GeneratedCodeInfo_Annotation_NONE = descriptorpb.GeneratedCodeInfo_Annotation_NONE
|
||||
const GeneratedCodeInfo_Annotation_SET = descriptorpb.GeneratedCodeInfo_Annotation_SET
|
||||
const GeneratedCodeInfo_Annotation_ALIAS = descriptorpb.GeneratedCodeInfo_Annotation_ALIAS
|
||||
|
||||
var GeneratedCodeInfo_Annotation_Semantic_name = descriptorpb.GeneratedCodeInfo_Annotation_Semantic_name
|
||||
var GeneratedCodeInfo_Annotation_Semantic_value = descriptorpb.GeneratedCodeInfo_Annotation_Semantic_value
|
||||
|
||||
type FileDescriptorSet = descriptorpb.FileDescriptorSet
|
||||
type FileDescriptorProto = descriptorpb.FileDescriptorProto
|
||||
type DescriptorProto = descriptorpb.DescriptorProto
|
||||
type ExtensionRangeOptions = descriptorpb.ExtensionRangeOptions
|
||||
|
||||
const Default_ExtensionRangeOptions_Verification = descriptorpb.Default_ExtensionRangeOptions_Verification
|
||||
|
||||
type FieldDescriptorProto = descriptorpb.FieldDescriptorProto
|
||||
type OneofDescriptorProto = descriptorpb.OneofDescriptorProto
|
||||
type EnumDescriptorProto = descriptorpb.EnumDescriptorProto
|
||||
@@ -103,7 +220,6 @@ const Default_FileOptions_OptimizeFor = descriptorpb.Default_FileOptions_Optimiz
|
||||
const Default_FileOptions_CcGenericServices = descriptorpb.Default_FileOptions_CcGenericServices
|
||||
const Default_FileOptions_JavaGenericServices = descriptorpb.Default_FileOptions_JavaGenericServices
|
||||
const Default_FileOptions_PyGenericServices = descriptorpb.Default_FileOptions_PyGenericServices
|
||||
const Default_FileOptions_PhpGenericServices = descriptorpb.Default_FileOptions_PhpGenericServices
|
||||
const Default_FileOptions_Deprecated = descriptorpb.Default_FileOptions_Deprecated
|
||||
const Default_FileOptions_CcEnableArenas = descriptorpb.Default_FileOptions_CcEnableArenas
|
||||
|
||||
@@ -118,8 +234,10 @@ type FieldOptions = descriptorpb.FieldOptions
|
||||
const Default_FieldOptions_Ctype = descriptorpb.Default_FieldOptions_Ctype
|
||||
const Default_FieldOptions_Jstype = descriptorpb.Default_FieldOptions_Jstype
|
||||
const Default_FieldOptions_Lazy = descriptorpb.Default_FieldOptions_Lazy
|
||||
const Default_FieldOptions_UnverifiedLazy = descriptorpb.Default_FieldOptions_UnverifiedLazy
|
||||
const Default_FieldOptions_Deprecated = descriptorpb.Default_FieldOptions_Deprecated
|
||||
const Default_FieldOptions_Weak = descriptorpb.Default_FieldOptions_Weak
|
||||
const Default_FieldOptions_DebugRedact = descriptorpb.Default_FieldOptions_DebugRedact
|
||||
|
||||
type OneofOptions = descriptorpb.OneofOptions
|
||||
type EnumOptions = descriptorpb.EnumOptions
|
||||
@@ -129,6 +247,7 @@ const Default_EnumOptions_Deprecated = descriptorpb.Default_EnumOptions_Deprecat
|
||||
type EnumValueOptions = descriptorpb.EnumValueOptions
|
||||
|
||||
const Default_EnumValueOptions_Deprecated = descriptorpb.Default_EnumValueOptions_Deprecated
|
||||
const Default_EnumValueOptions_DebugRedact = descriptorpb.Default_EnumValueOptions_DebugRedact
|
||||
|
||||
type ServiceOptions = descriptorpb.ServiceOptions
|
||||
|
||||
@@ -140,12 +259,17 @@ const Default_MethodOptions_Deprecated = descriptorpb.Default_MethodOptions_Depr
|
||||
const Default_MethodOptions_IdempotencyLevel = descriptorpb.Default_MethodOptions_IdempotencyLevel
|
||||
|
||||
type UninterpretedOption = descriptorpb.UninterpretedOption
|
||||
type FeatureSet = descriptorpb.FeatureSet
|
||||
type FeatureSetDefaults = descriptorpb.FeatureSetDefaults
|
||||
type SourceCodeInfo = descriptorpb.SourceCodeInfo
|
||||
type GeneratedCodeInfo = descriptorpb.GeneratedCodeInfo
|
||||
type DescriptorProto_ExtensionRange = descriptorpb.DescriptorProto_ExtensionRange
|
||||
type DescriptorProto_ReservedRange = descriptorpb.DescriptorProto_ReservedRange
|
||||
type ExtensionRangeOptions_Declaration = descriptorpb.ExtensionRangeOptions_Declaration
|
||||
type EnumDescriptorProto_EnumReservedRange = descriptorpb.EnumDescriptorProto_EnumReservedRange
|
||||
type FieldOptions_EditionDefault = descriptorpb.FieldOptions_EditionDefault
|
||||
type UninterpretedOption_NamePart = descriptorpb.UninterpretedOption_NamePart
|
||||
type FeatureSetDefaults_FeatureSetEditionDefault = descriptorpb.FeatureSetDefaults_FeatureSetEditionDefault
|
||||
type SourceCodeInfo_Location = descriptorpb.SourceCodeInfo_Location
|
||||
type GeneratedCodeInfo_Annotation = descriptorpb.GeneratedCodeInfo_Annotation
|
||||
|
||||
|
||||
62
vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go
generated
vendored
Normal file
62
vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/empty/empty.proto
|
||||
|
||||
package empty
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/empty.proto.
|
||||
|
||||
type Empty = emptypb.Empty
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_empty_empty_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc = []byte{
|
||||
0x0a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x65, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_empty_empty_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_empty_empty_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_empty_empty_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_empty_empty_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs = nil
|
||||
}
|
||||
12
vendor/github.com/gomarkdown/markdown/README.md
generated
vendored
12
vendor/github.com/gomarkdown/markdown/README.md
generated
vendored
@@ -9,11 +9,11 @@ It's very fast and supports common extensions.
|
||||
Tutorial: https://blog.kowalczyk.info/article/cxn3/advanced-markdown-processing-in-go.html
|
||||
|
||||
Code examples:
|
||||
* https://onlinetool.io/goplayground/#txO7hJ-ibeU : basic markdown => HTML
|
||||
* https://onlinetool.io/goplayground/#yFRIWRiu-KL : customize HTML renderer
|
||||
* https://onlinetool.io/goplayground/#2yV5-HDKBUV : modify AST
|
||||
* https://onlinetool.io/goplayground/#9fqKwRbuJ04 : customize parser
|
||||
* https://onlinetool.io/goplayground/#Bk0zTvrzUDR : syntax highlight
|
||||
* https://tools.arslexis.io/goplayground/#txO7hJ-ibeU : basic markdown => HTML
|
||||
* https://tools.arslexis.io/goplayground/#yFRIWRiu-KL : customize HTML renderer
|
||||
* https://tools.arslexis.io/goplayground/#2yV5-HDKBUV : modify AST
|
||||
* https://tools.arslexis.io/goplayground/#9fqKwRbuJ04 : customize parser
|
||||
* https://tools.arslexis.io/goplayground/#Bk0zTvrzUDR : syntax highlight
|
||||
|
||||
Those examples are also in [examples](./examples) directory.
|
||||
|
||||
@@ -226,7 +226,7 @@ implements the following extensions:
|
||||
- **Hard line breaks**. With this extension enabled newlines in the input
|
||||
translates into line breaks in the output. This extension is off by default.
|
||||
|
||||
- **Non blocking space**. With this extension enabled spaces preceeded by a backslash
|
||||
- **Non blocking space**. With this extension enabled spaces preceded by a backslash
|
||||
in the input translates non-blocking spaces in the output. This extension is off by default.
|
||||
|
||||
- **Smart quotes**. Smartypants-style punctuation substitution is
|
||||
|
||||
7
vendor/github.com/gomarkdown/markdown/parser/block.go
generated
vendored
7
vendor/github.com/gomarkdown/markdown/parser/block.go
generated
vendored
@@ -191,6 +191,11 @@ func (p *Parser) Block(data []byte) {
|
||||
// <div>
|
||||
// ...
|
||||
// </div>
|
||||
|
||||
if len(data) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[0] == '<' {
|
||||
if i := p.html(data, true); i > 0 {
|
||||
data = data[i:]
|
||||
@@ -393,7 +398,7 @@ func (p *Parser) AddBlock(n ast.Node) ast.Node {
|
||||
}
|
||||
|
||||
func (p *Parser) isPrefixHeading(data []byte) bool {
|
||||
if data[0] != '#' {
|
||||
if len(data) > 0 && data[0] != '#' {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
5
vendor/github.com/gomarkdown/markdown/parser/citation.go
generated
vendored
5
vendor/github.com/gomarkdown/markdown/parser/citation.go
generated
vendored
@@ -65,6 +65,11 @@ func citation(p *Parser, data []byte, offset int) (int, ast.Node) {
|
||||
}
|
||||
|
||||
citeType := ast.CitationTypeInformative
|
||||
|
||||
if len(citation) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
j = 1
|
||||
switch citation[j] {
|
||||
case '!':
|
||||
|
||||
2
vendor/github.com/gomarkdown/markdown/parser/inline.go
generated
vendored
2
vendor/github.com/gomarkdown/markdown/parser/inline.go
generated
vendored
@@ -736,7 +736,7 @@ func leftAngle(p *Parser, data []byte, offset int) (int, ast.Node) {
|
||||
}
|
||||
|
||||
// '\\' backslash escape
|
||||
var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~^")
|
||||
var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~^$")
|
||||
|
||||
func escape(p *Parser, data []byte, offset int) (int, ast.Node) {
|
||||
data = data[offset:]
|
||||
|
||||
13
vendor/github.com/gomarkdown/markdown/parser/parser.go
generated
vendored
13
vendor/github.com/gomarkdown/markdown/parser/parser.go
generated
vendored
@@ -56,7 +56,7 @@ const (
|
||||
)
|
||||
|
||||
// for each character that triggers a response when parsing inline data.
|
||||
type inlineParser func(p *Parser, data []byte, offset int) (int, ast.Node)
|
||||
type InlineParser func(p *Parser, data []byte, offset int) (int, ast.Node)
|
||||
|
||||
// ReferenceOverrideFunc is expected to be called with a reference string and
|
||||
// return either a valid Reference type that the reference string maps to or
|
||||
@@ -98,7 +98,7 @@ type Parser struct {
|
||||
|
||||
refs map[string]*reference
|
||||
refsRecord map[string]struct{}
|
||||
inlineCallback [256]inlineParser
|
||||
inlineCallback [256]InlineParser
|
||||
nesting int
|
||||
maxNesting int
|
||||
insideLink bool
|
||||
@@ -181,6 +181,12 @@ func NewWithExtensions(extension Extensions) *Parser {
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *Parser) RegisterInline(n byte, fn InlineParser) InlineParser {
|
||||
prev := p.inlineCallback[n]
|
||||
p.inlineCallback[n] = fn
|
||||
return prev
|
||||
}
|
||||
|
||||
func (p *Parser) getRef(refid string) (ref *reference, found bool) {
|
||||
if p.ReferenceOverride != nil {
|
||||
r, overridden := p.ReferenceOverride(refid)
|
||||
@@ -901,6 +907,9 @@ func isListItem(d ast.Node) bool {
|
||||
}
|
||||
|
||||
func NormalizeNewlines(d []byte) []byte {
|
||||
res := make([]byte, len(d))
|
||||
copy(res, d)
|
||||
d = res
|
||||
wi := 0
|
||||
n := len(d)
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
9
vendor/github.com/google/uuid/.travis.yml
generated
vendored
9
vendor/github.com/google/uuid/.travis.yml
generated
vendored
@@ -1,9 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.3
|
||||
- 1.5.3
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
41
vendor/github.com/google/uuid/CHANGELOG.md
generated
vendored
Normal file
41
vendor/github.com/google/uuid/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# Changelog
|
||||
|
||||
## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06))
|
||||
* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6))
|
||||
|
||||
## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29))
|
||||
|
||||
## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4))
|
||||
|
||||
### Fixes
|
||||
|
||||
* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior)
|
||||
|
||||
## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0))
|
||||
|
||||
## Changelog
|
||||
16
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
16
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
@@ -2,6 +2,22 @@
|
||||
|
||||
We definitely welcome patches and contribution to this project!
|
||||
|
||||
### Tips
|
||||
|
||||
Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org).
|
||||
|
||||
Always try to include a test case! If it is not possible or not necessary,
|
||||
please explain why in the pull request description.
|
||||
|
||||
### Releasing
|
||||
|
||||
Commits that would precipitate a SemVer change, as described in the Conventional
|
||||
Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action)
|
||||
to create a release candidate pull request. Once submitted, `release-please`
|
||||
will create a release.
|
||||
|
||||
For tips on how to work with `release-please`, see its documentation.
|
||||
|
||||
### Legal requirements
|
||||
|
||||
In order to protect both you and ourselves, you will need to sign the
|
||||
|
||||
10
vendor/github.com/google/uuid/README.md
generated
vendored
10
vendor/github.com/google/uuid/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
# uuid 
|
||||
# uuid
|
||||
The uuid package generates and inspects UUIDs based on
|
||||
[RFC 4122](http://tools.ietf.org/html/rfc4122)
|
||||
[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)
|
||||
and DCE 1.1: Authentication and Security Services.
|
||||
|
||||
This package is based on the github.com/pborman/uuid package (previously named
|
||||
@@ -9,10 +9,12 @@ a UUID is a 16 byte array rather than a byte slice. One loss due to this
|
||||
change is the ability to represent an invalid UUID (vs a NIL UUID).
|
||||
|
||||
###### Install
|
||||
`go get github.com/google/uuid`
|
||||
```sh
|
||||
go get github.com/google/uuid
|
||||
```
|
||||
|
||||
###### Documentation
|
||||
[](http://godoc.org/github.com/google/uuid)
|
||||
[](https://pkg.go.dev/github.com/google/uuid)
|
||||
|
||||
Full `go doc` style documentation for the package can be viewed online without
|
||||
installing this package by using the GoDoc site here:
|
||||
|
||||
6
vendor/github.com/google/uuid/hash.go
generated
vendored
6
vendor/github.com/google/uuid/hash.go
generated
vendored
@@ -17,6 +17,12 @@ var (
|
||||
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
|
||||
Nil UUID // empty UUID, all zeros
|
||||
|
||||
// The Max UUID is special form of UUID that is specified to have all 128 bits set to 1.
|
||||
Max = UUID{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
}
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID derived from the hash of space concatenated with
|
||||
|
||||
2
vendor/github.com/google/uuid/node_js.go
generated
vendored
2
vendor/github.com/google/uuid/node_js.go
generated
vendored
@@ -7,6 +7,6 @@
|
||||
package uuid
|
||||
|
||||
// getHardwareInterface returns nil values for the JS version of the code.
|
||||
// This remvoves the "net" dependency, because it is not used in the browser.
|
||||
// This removes the "net" dependency, because it is not used in the browser.
|
||||
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
|
||||
func getHardwareInterface(name string) (string, []byte) { return "", nil }
|
||||
|
||||
21
vendor/github.com/google/uuid/time.go
generated
vendored
21
vendor/github.com/google/uuid/time.go
generated
vendored
@@ -108,12 +108,23 @@ func setClockSequence(seq int) {
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. The time is only defined for version 1 and 2 UUIDs.
|
||||
// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs.
|
||||
func (uuid UUID) Time() Time {
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time)
|
||||
var t Time
|
||||
switch uuid.Version() {
|
||||
case 6:
|
||||
time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110
|
||||
t = Time(time)
|
||||
case 7:
|
||||
time := binary.BigEndian.Uint64(uuid[:8])
|
||||
t = Time((time>>16)*10000 + g1582ns100)
|
||||
default: // forward compatible
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
t = Time(time)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid.
|
||||
|
||||
89
vendor/github.com/google/uuid/uuid.go
generated
vendored
89
vendor/github.com/google/uuid/uuid.go
generated
vendored
@@ -56,11 +56,15 @@ func IsInvalidLengthError(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// Parse decodes s into a UUID or returns an error. Both the standard UUID
|
||||
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
|
||||
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
|
||||
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
|
||||
// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both
|
||||
// the standard UUID forms defined in RFC 4122
|
||||
// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition,
|
||||
// Parse accepts non-standard strings such as the raw hex encoding
|
||||
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings,
|
||||
// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are
|
||||
// examined in the latter case. Parse should not be used to validate strings as
|
||||
// it parses non-standard encodings as indicated above.
|
||||
func Parse(s string) (UUID, error) {
|
||||
var uuid UUID
|
||||
switch len(s) {
|
||||
@@ -69,7 +73,7 @@ func Parse(s string) (UUID, error) {
|
||||
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
case 36 + 9:
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
if !strings.EqualFold(s[:9], "urn:uuid:") {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
|
||||
}
|
||||
s = s[9:]
|
||||
@@ -101,7 +105,8 @@ func Parse(s string) (UUID, error) {
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
24, 26, 28, 30, 32, 34,
|
||||
} {
|
||||
v, ok := xtob(s[x], s[x+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
@@ -117,7 +122,7 @@ func ParseBytes(b []byte) (UUID, error) {
|
||||
switch len(b) {
|
||||
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
|
||||
if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
|
||||
}
|
||||
b = b[9:]
|
||||
@@ -145,7 +150,8 @@ func ParseBytes(b []byte) (UUID, error) {
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
24, 26, 28, 30, 32, 34,
|
||||
} {
|
||||
v, ok := xtob(b[x], b[x+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
@@ -180,6 +186,59 @@ func Must(uuid UUID, err error) UUID {
|
||||
return uuid
|
||||
}
|
||||
|
||||
// Validate returns an error if s is not a properly formatted UUID in one of the following formats:
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
||||
// It returns an error if the format is invalid, otherwise nil.
|
||||
func Validate(s string) error {
|
||||
switch len(s) {
|
||||
// Standard UUID format
|
||||
case 36:
|
||||
|
||||
// UUID with "urn:uuid:" prefix
|
||||
case 36 + 9:
|
||||
if !strings.EqualFold(s[:9], "urn:uuid:") {
|
||||
return fmt.Errorf("invalid urn prefix: %q", s[:9])
|
||||
}
|
||||
s = s[9:]
|
||||
|
||||
// UUID enclosed in braces
|
||||
case 36 + 2:
|
||||
if s[0] != '{' || s[len(s)-1] != '}' {
|
||||
return fmt.Errorf("invalid bracketed UUID format")
|
||||
}
|
||||
s = s[1 : len(s)-1]
|
||||
|
||||
// UUID without hyphens
|
||||
case 32:
|
||||
for i := 0; i < len(s); i += 2 {
|
||||
_, ok := xtob(s[i], s[i+1])
|
||||
if !ok {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return invalidLengthError{len(s)}
|
||||
}
|
||||
|
||||
// Check for standard UUID format
|
||||
if len(s) == 36 {
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} {
|
||||
if _, ok := xtob(s[x], s[x+1]); !ok {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
@@ -292,3 +351,15 @@ func DisableRandPool() {
|
||||
poolMu.Lock()
|
||||
poolPos = randPoolSize
|
||||
}
|
||||
|
||||
// UUIDs is a slice of UUID types.
|
||||
type UUIDs []UUID
|
||||
|
||||
// Strings returns a string slice containing the string form of each UUID in uuids.
|
||||
func (uuids UUIDs) Strings() []string {
|
||||
var uuidStrs = make([]string, len(uuids))
|
||||
for i, uuid := range uuids {
|
||||
uuidStrs[i] = uuid.String()
|
||||
}
|
||||
return uuidStrs
|
||||
}
|
||||
|
||||
56
vendor/github.com/google/uuid/version6.go
generated
vendored
Normal file
56
vendor/github.com/google/uuid/version6.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2023 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// UUID version 6 is a field-compatible version of UUIDv1, reordered for improved DB locality.
|
||||
// It is expected that UUIDv6 will primarily be used in contexts where there are existing v1 UUIDs.
|
||||
// Systems that do not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead.
|
||||
//
|
||||
// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#uuidv6
|
||||
//
|
||||
// NewV6 returns a Version 6 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewV6 set NodeID is random bits automatically . If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewV6 returns Nil and an error.
|
||||
func NewV6() (UUID, error) {
|
||||
var uuid UUID
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
/*
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| time_high |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| time_mid | time_low_and_version |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|clk_seq_hi_res | clk_seq_low | node (0-1) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| node (2-5) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
binary.BigEndian.PutUint64(uuid[0:], uint64(now))
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
|
||||
uuid[6] = 0x60 | (uuid[6] & 0x0F)
|
||||
uuid[8] = 0x80 | (uuid[8] & 0x3F)
|
||||
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
copy(uuid[10:], nodeID[:])
|
||||
nodeMu.Unlock()
|
||||
|
||||
return uuid, nil
|
||||
}
|
||||
104
vendor/github.com/google/uuid/version7.go
generated
vendored
Normal file
104
vendor/github.com/google/uuid/version7.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2023 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// UUID version 7 features a time-ordered value field derived from the widely
|
||||
// implemented and well known Unix Epoch timestamp source,
|
||||
// the number of milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded.
|
||||
// As well as improved entropy characteristics over versions 1 or 6.
|
||||
//
|
||||
// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7
|
||||
//
|
||||
// Implementations SHOULD utilize UUID version 7 over UUID version 1 and 6 if possible.
|
||||
//
|
||||
// NewV7 returns a Version 7 UUID based on the current time(Unix Epoch).
|
||||
// Uses the randomness pool if it was enabled with EnableRandPool.
|
||||
// On error, NewV7 returns Nil and an error
|
||||
func NewV7() (UUID, error) {
|
||||
uuid, err := NewRandom()
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
makeV7(uuid[:])
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// NewV7FromReader returns a Version 7 UUID based on the current time(Unix Epoch).
|
||||
// it use NewRandomFromReader fill random bits.
|
||||
// On error, NewV7FromReader returns Nil and an error.
|
||||
func NewV7FromReader(r io.Reader) (UUID, error) {
|
||||
uuid, err := NewRandomFromReader(r)
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
makeV7(uuid[:])
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6])
|
||||
// uuid[8] already has the right version number (Variant is 10)
|
||||
// see function NewV7 and NewV7FromReader
|
||||
func makeV7(uuid []byte) {
|
||||
/*
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| unix_ts_ms |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| unix_ts_ms | ver | rand_a (12 bit seq) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|var| rand_b |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| rand_b |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
_ = uuid[15] // bounds check
|
||||
|
||||
t, s := getV7Time()
|
||||
|
||||
uuid[0] = byte(t >> 40)
|
||||
uuid[1] = byte(t >> 32)
|
||||
uuid[2] = byte(t >> 24)
|
||||
uuid[3] = byte(t >> 16)
|
||||
uuid[4] = byte(t >> 8)
|
||||
uuid[5] = byte(t)
|
||||
|
||||
uuid[6] = 0x70 | (0x0F & byte(s>>8))
|
||||
uuid[7] = byte(s)
|
||||
}
|
||||
|
||||
// lastV7time is the last time we returned stored as:
|
||||
//
|
||||
// 52 bits of time in milliseconds since epoch
|
||||
// 12 bits of (fractional nanoseconds) >> 8
|
||||
var lastV7time int64
|
||||
|
||||
const nanoPerMilli = 1000000
|
||||
|
||||
// getV7Time returns the time in milliseconds and nanoseconds / 256.
|
||||
// The returned (milli << 12 + seq) is guarenteed to be greater than
|
||||
// (milli << 12 + seq) returned by any previous call to getV7Time.
|
||||
func getV7Time() (milli, seq int64) {
|
||||
timeMu.Lock()
|
||||
defer timeMu.Unlock()
|
||||
|
||||
nano := timeNow().UnixNano()
|
||||
milli = nano / nanoPerMilli
|
||||
// Sequence number is between 0 and 3906 (nanoPerMilli>>8)
|
||||
seq = (nano - milli*nanoPerMilli) >> 8
|
||||
now := milli<<12 + seq
|
||||
if now <= lastV7time {
|
||||
now = lastV7time + 1
|
||||
milli = now >> 12
|
||||
seq = now & 0xfff
|
||||
}
|
||||
lastV7time = now
|
||||
return milli, seq
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user