Cambio librerie

main
Uriel Fanelli 2025-05-04 04:01:53 +02:00
parent 458b945837
commit cedb7d95d0
63 changed files with 3288 additions and 2787 deletions

5
go.mod
View File

@ -4,6 +4,7 @@ go 1.21.0
require ( require (
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
github.com/google/uuid v1.6.0 github.com/eduncan911/podcast v1.4.2
github.com/gorilla/feeds v1.2.0
) )
require github.com/pkg/errors v0.9.1 // indirect

24
go.sum
View File

@ -1,12 +1,16 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 h1:OtSeLS5y0Uy01jaKK4mA/WVIYtpzVm63vLVAPzJXigg= github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 h1:OtSeLS5y0Uy01jaKK4mA/WVIYtpzVm63vLVAPzJXigg=
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8/go.mod h1:apkPC/CR3s48O2D7Y++n1XWEpgPNNCjXYga3PPbJe2E= github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8/go.mod h1:apkPC/CR3s48O2D7Y++n1XWEpgPNNCjXYga3PPbJe2E=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/eduncan911/podcast v1.4.2 h1:S+fsUlbR2ULFou2Mc52G/MZI8JVJHedbxLQnoA+MY/w=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/eduncan911/podcast v1.4.2/go.mod h1:mSxiK1z5KeNO0YFaQ3ElJlUZbbDV9dA7R9c1coeeXkc=
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

6
pod.go
View File

@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/dhowden/tag" "github.com/dhowden/tag"
"github.com/google/uuid"
) )
type Episode struct { type Episode struct {
@ -27,11 +26,6 @@ var (
rssLock sync.Mutex rssLock sync.Mutex
) )
func generateUUIDFromString(input string) uuid.UUID {
namespace := uuid.NameSpaceDNS // Namespace fisso per consistenza
return uuid.NewSHA1(namespace, []byte(input))
}
func scanEpisodes() ([]Episode, error) { func scanEpisodes() ([]Episode, error) {
var episodes []Episode var episodes []Episode

View File

@ -1,12 +1,13 @@
package main package main
import ( import (
"fmt" "log"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"time" "time"
"github.com/gorilla/feeds" "github.com/eduncan911/podcast"
) )
func generateRSS() error { func generateRSS() error {
@ -18,46 +19,59 @@ func generateRSS() error {
return err return err
} }
// rss := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?> timeNow := time.Now()
// <rss version="2.0"
// xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
// xmlns:podcast="https://podcastindex.org/namespace/1.0"
// xmlns:atom="http://www.w3.org/2005/Atom"
// xmlns:content="http://purl.org/rss/1.0/modules/content/">
// <channel>
// <description>%s</description>
// <title>%s</title>
// <link>%s</link>`, podTitle, podTitle, baseURL)
feed := &feeds.Feed{ p := podcast.New(
Title: podTitle, podTitle,
Link: &feeds.Link{Href: baseURL}, baseURL,
Description: podDesc, podDesc,
Author: &feeds.Author{Name: podAuthor}, &timeNow, &timeNow,
Created: time.Now(), )
Copyright: podRights,
} // 2. Aggiungi metadati globali
p.IAuthor = podAuthor
p.AddImage(podLogo)
for _, ep := range episodes { for _, ep := range episodes {
epBaseUrl := baseURL + "/audio/" + filepath.Base(ep.File) epBaseUrl := baseURL + "/audio/" + filepath.Base(ep.File)
feed.Add(&feeds.Item{ strip, found := strings.CutSuffix(epBaseUrl, ".mp3")
if !found {
log.Println("Seems the file is not mp3??????")
}
epBaseImg := baseURL + "/covers/" + strip + ".jpg"
item := podcast.Item{
Title: ep.Title, Title: ep.Title,
Link: &feeds.Link{Href: epBaseUrl},
Description: ep.Description, Description: ep.Description,
Enclosure: &feeds.Enclosure{ PubDate: &timeNow,
Url: epBaseUrl, Enclosure: &podcast.Enclosure{
Length: fmt.Sprintf("%d", ep.Size), URL: epBaseUrl,
Type: "audio/mpeg", Type: podcast.MP3,
Length: ep.Size,
}, },
Created: time.Now(), // Metadati specifici iTunes
}) IAuthor: ep.Artist,
IImage: &podcast.IImage{
HREF: epBaseImg, // Campo corretto
},
IExplicit: "no",
}
if _, err := p.AddItem(item); err != nil {
log.Fatal("Errore nell'aggiunta dell'episodio: ", err)
}
} }
// Genera RSS if err := p.Encode(os.Stdout); err != nil {
rss, _ := feed.ToRss() panic(err)
return os.WriteFile("feed.xml", []byte(rss), 0644) }
// Opzionale: salva su file
file, _ := os.Create("feed.xml")
defer file.Close()
return p.Encode(file)
} }

7
vendor/github.com/eduncan911/podcast/.gitignore generated vendored Normal file
View File

@ -0,0 +1,7 @@
profile.out
README.md.tmp
corpus
crashers
suppressions
workdir
podcast-fuzz.zip

21
vendor/github.com/eduncan911/podcast/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Eric Duncan
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.

17
vendor/github.com/eduncan911/podcast/Makefile generated vendored Normal file
View File

@ -0,0 +1,17 @@
SHELL = /bin/bash
GITHUB_REPO:=eduncan911/podcast
README:
godoc2ghmd -play -ex -verify_import_links=0 github.com/$(GITHUB_REPO) > README.md.tmp
echo "[![GoDoc](https://godoc.org/github.com/$(GITHUB_REPO)?status.svg)](https://godoc.org/github.com/$(GITHUB_REPO))" > README.md
echo "[![Build Status](https://github.com/$(GITHUB_REPO)/workflows/go-cicd/badge.svg)](https://github.com/$(GITHUB_REPO)/actions?workflow=go-cicd)" >> README.md
echo "[![Coverage Status](https://coveralls.io/repos/github/$(GITHUB_REPO)/badge.svg?branch=master)](https://coveralls.io/github/$(GITHUB_REPO)?branch=master)" >> README.md
echo "[![Go Report Card](https://goreportcard.com/badge/github.com/$(GITHUB_REPO))](https://goreportcard.com/report/github.com/$(GITHUB_REPO))" >> README.md
echo "[![MIT License](https://img.shields.io/npm/l/mediaelement.svg)](https://eduncan911.mit-license.org/)" >> README.md
echo >>README.md
cat README.md.tmp >> README.md
rm README.md.tmp
clean:
rm -rf corpus crashers suppressions workdir podcast-fuzz.zip

1274
vendor/github.com/eduncan911/podcast/README.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

11
vendor/github.com/eduncan911/podcast/atomlink.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
package podcast
import "encoding/xml"
// AtomLink represents the Atom reference link.
type AtomLink struct {
XMLName xml.Name `xml:"atom:link"`
HREF string `xml:"href,attr"`
Rel string `xml:"rel,attr"`
Type string `xml:"type,attr"`
}

12
vendor/github.com/eduncan911/podcast/author.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
package podcast
import "encoding/xml"
// Author represents a named author and email.
//
// For iTunes compliance, both Name and Email are required.
type Author struct {
XMLName xml.Name `xml:"itunes:owner"`
Name string `xml:"itunes:name"`
Email string `xml:"itunes:email"`
}

157
vendor/github.com/eduncan911/podcast/doc.go generated vendored Normal file
View File

@ -0,0 +1,157 @@
// Package podcast generates a fully compliant iTunes and RSS 2.0 podcast feed
// for GoLang using a simple API.
//
// Full documentation with detailed examples located at https://godoc.org/github.com/eduncan911/podcast
//
// Usage
//
// To use, `go get` and `import` the package like your typical GoLang library.
//
// $ go get -u github.com/eduncan911/podcast
//
// import "github.com/eduncan911/podcast"
//
// The API exposes a number of method receivers on structs that implements the
// logic required to comply with the specifications and ensure a compliant feed.
// A number of overrides occur to help with iTunes visibility of your episodes.
//
// Notably, the `Podcast.AddItem` function performs most
// of the heavy lifting by taking the `Item` input and performing
// validation, overrides and duplicate setters through the feed.
//
// Full detailed Examples of the API are at https://godoc.org/github.com/eduncan911/podcast.
//
// Go Modules
//
// This library is supported on GoLang 1.7 and higher.
//
// We have implemented Go Modules support and the CI pipeline shows it working with
// new installs, tested with Go 1.13. To keep 1.7 compatibility, we use
// `go mod vendor` to maintain the `vendor/` folder for older 1.7 and later runtimes.
//
// If either runtime has an issue, please create an Issue and I will address.
//
// Extensibility
//
// For version 1.x, you are not restricted in having full control over your feeds.
// You may choose to skip the API methods and instead use the structs directly. The
// fields have been grouped by RSS 2.0 and iTunes fields with iTunes specific fields
// all prefixed with the letter `I`.
//
// However, do note that the 2.x version currently in progress will break this
// extensibility and enforce API methods going forward. This is to ensure that the feed
// can both be marshalled, and unmarshalled back and forth (current 1.x branch can only
// be unmarshalled - hence the work for 2.x).
//
// Fuzzing Inputs
//
// `go-fuzz` has been added in 1.4.1, covering all exported API methods. They have been
// ran extensively and no issues have come out of them yet (most tests were ran overnight,
// over about 11 hours with zero crashes).
//
// If you wish to help fuzz the inputs, with Go 1.13 or later you can run `go-fuzz` on any
// of the inputs.
//
// go get -u github.com/dvyukov/go-fuzz/go-fuzz
// go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
// go get -u github.com/eduncan911/podcast
// cd $GOPATH/src/github.com/eduncan911/podcast
// go-fuzz-build
// go-fuzz -func FuzzPodcastAddItem
//
// To obtain a list of available funcs to pass, just run `go-fuzz` without any parameters:
//
// $ go-fuzz
// 2020/02/13 07:27:32 -func flag not provided, but multiple fuzz functions available:
// FuzzItemAddDuration, FuzzItemAddEnclosure, FuzzItemAddImage, FuzzItemAddPubDate,
// FuzzItemAddSummary, FuzzPodcastAddAtomLink, FuzzPodcastAddAuthor, FuzzPodcastAddCategory,
// FuzzPodcastAddImage, FuzzPodcastAddItem, FuzzPodcastAddLastBuildDate, FuzzPodcastAddPubDate,
// FuzzPodcastAddSubTitle, FuzzPodcastAddSummary, FuzzPodcastBytes, FuzzPodcastEncode,
// FuzzPodcastNew
//
// If you do find an issue, please raise an issue immediately and I will quickly address.
//
// Roadmap
//
// The 1.x branch is now mostly in maintenance mode, open to PRs. This means no
// more planned features on the 1.x feature branch is expected. With the success of 6
// iTunes-accepted podcasts I have published with this library, and with the feedback from
// the community, the 1.x releases are now considered stable.
//
// The 2.x branch's primary focus is to allow for bi-direction marshalling both ways.
// Currently, the 1.x branch only allows unmarshalling to a serial feed. An attempt to marshall
// a serialized feed back into a Podcast form will error or not work correctly. Note that while
// the 2.x branch is targeted to remain backwards compatible, it is true if using the public
// API funcs to set parameters only. Several of the underlying public fields are being removed
// in order to accommodate the marshalling of serialized data. Therefore, a version 2.x is denoted
// for this release.
//
// Versioning
//
// We use SemVer versioning schema. You can rest assured that pulling 1.x branches will
// remain backwards compatible now and into the future.
//
// However, the new 2.x branch, while keeping the same API, is expected break those that
// bypass the API methods and use the underlying public properties instead.
//
// Release Notes
//
// v1.4.2
// * Slim down Go Modules for consumers (#32)
//
// v1.4.1
// * Implement fuzz logic testing of exported funcs (#31)
// * Upgrade CICD Pipeline Tooling (#31)
// * Update documentation for 1.x and 2.3 (#31)
// * Allow godoc2ghmd to run without network (#31)
//
// v1.4.0
// * Add Go Modules, Update vendor folder (#26, #25)
// * Add C.I. GitHub Actions (#25)
// * Add additional error checks found by linters (#25)
// * Go Fmt enclosure_test.go (#25)
//
// v1.3.2
// * Correct count len of UTF8 strings (#9)
// * Implement duration parser (#8)
// * Fix Github and GoDocs Markdown (#14)
// * Move podcast.go Private Methods to Respected Files (#12)
// * Allow providing GUID on Podcast (#15)
//
// v1.3.1
// * increased itunes compliance after feedback from Apple:
// - specified what categories should be set with AddCategory().
// - enforced title and link as part of Image.
// * added Podcast.AddAtomLink() for more broad compliance to readers.
//
// v1.3.0
// * fixes Item.Duration being set incorrectly.
// * changed Item.AddEnclosure() parameter definition (Bytes not Seconds!).
// * added Item.AddDuration formatting and override.
// * added more documentation surrounding Item.Enclosure{}
//
// v1.2.1
// * added Podcast.AddSubTitle() and truncating to 64 chars.
// * added a number of Guards to protect against empty fields.
//
// v1.2.0
// * added Podcast.AddPubDate() and Podcast.AddLastBuildDate() overrides.
// * added Item.AddImage() to mask some cumbersome addition of IImage.
// * added Item.AddPubDate to simply datetime setters.
// * added more examples (mostly around Item struct).
// * tweaked some documentation.
//
// v1.1.0
// * Enabling CDATA in ISummary fields for Podcast and Channel.
//
// v1.0.0
// * Initial release.
// * Full documentation, full examples and complete code coverage.
//
// References
//
// RSS 2.0: https://cyber.harvard.edu/rss/rss.html
//
// Podcasts: https://help.apple.com/itc/podcasts_connect/#/itca5b22233
//
package podcast

65
vendor/github.com/eduncan911/podcast/enclosure.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
package podcast
import "encoding/xml"
// EnclosureType specifies the type of the enclosure.
const (
M4A EnclosureType = iota
M4V
MP4
MP3
MOV
PDF
EPUB
)
const (
enclosureDefault = "application/octet-stream"
)
// EnclosureType specifies the type of the enclosure.
type EnclosureType int
// String returns the MIME type encoding of the specified EnclosureType.
func (et EnclosureType) String() string {
// https://help.apple.com/itc/podcasts_connect/#/itcb54353390
switch et {
case M4A:
return "audio/x-m4a"
case M4V:
return "video/x-m4v"
case MP4:
return "video/mp4"
case MP3:
return "audio/mpeg"
case MOV:
return "video/quicktime"
case PDF:
return "application/pdf"
case EPUB:
return "document/x-epub"
}
return enclosureDefault
}
// Enclosure represents a download enclosure.
type Enclosure struct {
XMLName xml.Name `xml:"enclosure"`
// URL is the downloadable url for the content. (Required)
URL string `xml:"url,attr"`
// Length is the size in Bytes of the download. (Required)
Length int64 `xml:"-"`
// LengthFormatted is the size in Bytes of the download. (Required)
//
// This field gets overwritten with the API when setting Length.
LengthFormatted string `xml:"length,attr"`
// Type is MIME type encoding of the download. (Required)
Type EnclosureType `xml:"-"`
// TypeFormatted is MIME type encoding of the download. (Required)
//
// This field gets overwritten with the API when setting Type.
TypeFormatted string `xml:"type,attr"`
}

297
vendor/github.com/eduncan911/podcast/fuzz.go generated vendored Normal file
View File

@ -0,0 +1,297 @@
// +build gofuzz
package podcast
import (
"bytes"
"encoding/binary"
"time"
)
func FuzzItemAddDuration(data []byte) int {
input, read := binary.Varint(data)
if input <= 0 && read == 0 {
// error converting []byte into int64
return 0
}
i := newItem(data)
i.AddDuration(input)
p := newPodcast(data)
if _, err := p.AddItem(i); err != nil {
return 0
}
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzItemAddEnclosure(data []byte) int {
url := string(data)
length, read := binary.Varint(data)
if length <= 0 && read == 0 {
// error converting []byte into int64
return 0
}
i := newItem(data)
i.AddEnclosure(url, MP3, length)
p := newPodcast(data)
if _, err := p.AddItem(i); err != nil {
return 0
}
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzItemAddImage(data []byte) int {
i := newItem(data)
i.AddImage(string(data))
p := newPodcast(data)
if _, err := p.AddItem(i); err != nil {
return 0
}
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzItemAddPubDate(data []byte) int {
t := time.Time{}
if err := t.GobDecode(data); err != nil {
return 0
}
i := newItem(data)
i.AddPubDate(&t)
p := newPodcast(data)
if _, err := p.AddItem(i); err != nil {
return 0
}
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzItemAddSummary(data []byte) int {
i := newItem(data)
i.AddSummary(string(data))
p := newPodcast(data)
if _, err := p.AddItem(i); err != nil {
return 0
}
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastNew(data []byte) int {
p := newPodcast(data)
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddAtomLink(data []byte) int {
p := newPodcast(data)
p.AddAtomLink(string(data))
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddAuthor(data []byte) int {
p := newPodcast(data)
p.AddAuthor(string(data), string(data))
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddCategory(data []byte) int {
p := newPodcast(data)
subs := make([]string, 3)
subs[0] = string(data)
subs[1] = string(data)
subs[2] = string(data)
p.AddCategory(string(data), subs)
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddImage(data []byte) int {
p := newPodcast(data)
p.AddImage(string(data))
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddItem(data []byte) int {
p := newPodcast(data)
i := newItem(data)
if _, err := p.AddItem(i); err != nil {
return 0
}
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddLastBuildDate(data []byte) int {
p := newPodcast(data)
t := time.Time{}
if err := t.GobDecode(data); err != nil {
return 0
}
p.AddLastBuildDate(&t)
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddPubDate(data []byte) int {
p := newPodcast(data)
t := time.Time{}
if err := t.GobDecode(data); err != nil {
return 0
}
p.AddPubDate(&t)
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddSubTitle(data []byte) int {
p := newPodcast(data)
p.AddSubTitle(string(data))
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastAddSummary(data []byte) int {
p := newPodcast(data)
p.AddSummary(string(data))
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func FuzzPodcastBytes(data []byte) int {
p := newPodcast(data)
p.Bytes()
return 1
}
func FuzzPodcastEncode(data []byte) int {
p := newPodcast(data)
var buf bytes.Buffer
if err := p.Encode(&buf); err != nil {
return 0
}
return 1
}
func newPodcast(data []byte) Podcast {
return New(
string(data),
string(data),
string(data),
nil, nil)
}
func newItem(data []byte) Item {
// Article minimal requirements are:
// - Title
// - Description
// - Link
//
// Audio minimal requirements are:
// - Title
// - Description
// - Enclosure (HREF, Type and Length all required)
//
return Item{
Title: string(data),
Description: string(data),
Link: string(data),
}
}

21
vendor/github.com/eduncan911/podcast/image.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
package podcast
import "encoding/xml"
// Image represents an image.
//
// Podcast feeds contain artwork that is a minimum size of
// 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels,
// 72 dpi, in JPEG or PNG format with appropriate file
// extensions (.jpg, .png), and in the RGB colorspace. To optimize
// images for mobile devices, Apple recommends compressing your
// image files.
type Image struct {
XMLName xml.Name `xml:"image"`
URL string `xml:"url"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description,omitempty"`
Width int `xml:"width,omitempty"`
Height int `xml:"height,omitempty"`
}

144
vendor/github.com/eduncan911/podcast/item.go generated vendored Normal file
View File

@ -0,0 +1,144 @@
package podcast
import (
"encoding/xml"
"fmt"
"time"
"unicode/utf8"
)
// Item represents a single entry in a podcast.
//
// Article minimal requirements are:
// - Title
// - Description
// - Link
//
// Audio minimal requirements are:
// - Title
// - Description
// - Enclosure (HREF, Type and Length all required)
//
// Recommendations:
// - Setting the minimal fields sets most of other fields, including iTunes.
// - Use the Published time.Time setting instead of PubDate.
// - Always set an Enclosure.Length, to be nice to your downloaders.
// - Use Enclosure.Type instead of setting TypeFormatted for valid extensions.
type Item struct {
XMLName xml.Name `xml:"item"`
GUID string `xml:"guid"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Author *Author `xml:"-"`
AuthorFormatted string `xml:"author,omitempty"`
Category string `xml:"category,omitempty"`
Comments string `xml:"comments,omitempty"`
Source string `xml:"source,omitempty"`
PubDate *time.Time `xml:"-"`
PubDateFormatted string `xml:"pubDate,omitempty"`
Enclosure *Enclosure
// https://help.apple.com/itc/podcasts_connect/#/itcb54353390
IAuthor string `xml:"itunes:author,omitempty"`
ISubtitle string `xml:"itunes:subtitle,omitempty"`
ISummary *ISummary
IImage *IImage
IDuration string `xml:"itunes:duration,omitempty"`
IExplicit string `xml:"itunes:explicit,omitempty"`
IIsClosedCaptioned string `xml:"itunes:isClosedCaptioned,omitempty"`
IOrder string `xml:"itunes:order,omitempty"`
}
// AddEnclosure adds the downloadable asset to the podcast Item.
func (i *Item) AddEnclosure(
url string, enclosureType EnclosureType, lengthInBytes int64) {
i.Enclosure = &Enclosure{
URL: url,
Type: enclosureType,
Length: lengthInBytes,
}
}
// AddImage adds the image as an iTunes-only IImage. RSS 2.0 does not have
// the specification of Images at the Item level.
//
// Podcast feeds contain artwork that is a minimum size of
// 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels,
// 72 dpi, in JPEG or PNG format with appropriate file
// extensions (.jpg, .png), and in the RGB colorspace. To optimize
// images for mobile devices, Apple recommends compressing your
// image files.
func (i *Item) AddImage(url string) {
if len(url) > 0 {
i.IImage = &IImage{HREF: url}
}
}
// AddPubDate adds the datetime as a parsed PubDate.
//
// UTC time is used by default.
func (i *Item) AddPubDate(datetime *time.Time) {
i.PubDate = datetime
i.PubDateFormatted = parseDateRFC1123Z(i.PubDate)
}
// AddSummary adds the iTunes summary.
//
// Limit: 4000 characters
//
// Note that this field is a CDATA encoded field which allows for rich text
// such as html links: `<a href="http://www.apple.com">Apple</a>`.
func (i *Item) AddSummary(summary string) {
count := utf8.RuneCountInString(summary)
if count > 4000 {
s := []rune(summary)
summary = string(s[0:4000])
}
i.ISummary = &ISummary{
Text: summary,
}
}
// AddDuration adds the duration to the iTunes duration field.
func (i *Item) AddDuration(durationInSeconds int64) {
if durationInSeconds <= 0 {
return
}
i.IDuration = parseDuration(durationInSeconds)
}
var parseDuration = func(duration int64) string {
h := duration / 3600
duration = duration % 3600
m := duration / 60
duration = duration % 60
s := duration
// HH:MM:SS
if h > 9 {
return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
}
// H:MM:SS
if h > 0 {
return fmt.Sprintf("%d:%02d:%02d", h, m, s)
}
// MM:SS
if m > 9 {
return fmt.Sprintf("%02d:%02d", m, s)
}
// M:SS
return fmt.Sprintf("%d:%02d", m, s)
}
var parseDateRFC1123Z = func(t *time.Time) string {
if t != nil && !t.IsZero() {
return t.Format(time.RFC1123Z)
}
return time.Now().UTC().Format(time.RFC1123Z)
}

34
vendor/github.com/eduncan911/podcast/itunes.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package podcast
import "encoding/xml"
// Specifications: https://help.apple.com/itc/podcasts_connect/#/itcb54353390
//
// ICategory is a 2-tier classification system for iTunes.
type ICategory struct {
XMLName xml.Name `xml:"itunes:category"`
Text string `xml:"text,attr"`
ICategories []*ICategory
}
// IImage represents an iTunes image.
//
// Podcast feeds contain artwork that is a minimum size of
// 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels,
// 72 dpi, in JPEG or PNG format with appropriate file
// extensions (.jpg, .png), and in the RGB colorspace. To optimize
// images for mobile devices, Apple recommends compressing your
// image files.
type IImage struct {
XMLName xml.Name `xml:"itunes:image"`
HREF string `xml:"href,attr"`
}
// ISummary is a 4000 character rich-text field for the itunes:summary tag.
//
// This is rendered as CDATA which allows for HTML tags such as `<a href="">`.
type ISummary struct {
XMLName xml.Name `xml:"itunes:summary"`
Text string `xml:",cdata"`
}

454
vendor/github.com/eduncan911/podcast/podcast.go generated vendored Normal file
View File

@ -0,0 +1,454 @@
package podcast
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"strconv"
"time"
"unicode/utf8"
"github.com/pkg/errors"
)
const (
pVersion = "1.3.1"
)
// Podcast represents a podcast.
type Podcast struct {
XMLName xml.Name `xml:"channel"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Category string `xml:"category,omitempty"`
Cloud string `xml:"cloud,omitempty"`
Copyright string `xml:"copyright,omitempty"`
Docs string `xml:"docs,omitempty"`
Generator string `xml:"generator,omitempty"`
Language string `xml:"language,omitempty"`
LastBuildDate string `xml:"lastBuildDate,omitempty"`
ManagingEditor string `xml:"managingEditor,omitempty"`
PubDate string `xml:"pubDate,omitempty"`
Rating string `xml:"rating,omitempty"`
SkipHours string `xml:"skipHours,omitempty"`
SkipDays string `xml:"skipDays,omitempty"`
TTL int `xml:"ttl,omitempty"`
WebMaster string `xml:"webMaster,omitempty"`
Image *Image
TextInput *TextInput
AtomLink *AtomLink
// https://help.apple.com/itc/podcasts_connect/#/itcb54353390
IAuthor string `xml:"itunes:author,omitempty"`
ISubtitle string `xml:"itunes:subtitle,omitempty"`
ISummary *ISummary
IBlock string `xml:"itunes:block,omitempty"`
IImage *IImage
IDuration string `xml:"itunes:duration,omitempty"`
IExplicit string `xml:"itunes:explicit,omitempty"`
IComplete string `xml:"itunes:complete,omitempty"`
INewFeedURL string `xml:"itunes:new-feed-url,omitempty"`
IOwner *Author // Author is formatted for itunes as-is
ICategories []*ICategory
Items []*Item
encode func(w io.Writer, o interface{}) error
}
// New instantiates a Podcast with required parameters.
//
// Nil-able fields are optional but recommended as they are formatted
// to the expected proper formats.
func New(title, link, description string,
pubDate, lastBuildDate *time.Time) Podcast {
return Podcast{
Title: title,
Link: link,
Description: description,
Generator: fmt.Sprintf("go podcast v%s (github.com/eduncan911/podcast)", pVersion),
PubDate: parseDateRFC1123Z(pubDate),
LastBuildDate: parseDateRFC1123Z(lastBuildDate),
Language: "en-us",
// setup dependency (could inject later)
encode: encoder,
}
}
// AddAuthor adds the specified Author to the podcast.
func (p *Podcast) AddAuthor(name, email string) {
if len(email) == 0 {
return
}
p.ManagingEditor = parseAuthorNameEmail(&Author{
Name: name,
Email: email,
})
p.IAuthor = p.ManagingEditor
}
// AddAtomLink adds a FQDN reference to an atom feed.
func (p *Podcast) AddAtomLink(href string) {
if len(href) == 0 {
return
}
p.AtomLink = &AtomLink{
HREF: href,
Rel: "self",
Type: "application/rss+xml",
}
}
// AddCategory adds the category to the Podcast.
//
// ICategory can be listed multiple times.
//
// Calling this method multiple times will APPEND the category to the existing
// list, if any, including ICategory.
//
// Note that Apple iTunes has a specific list of categories that only can be
// used and will invalidate the feed if deviated from the list. That list is
// as follows.
//
// * Arts
// * Design
// * Fashion & Beauty
// * Food
// * Literature
// * Performing Arts
// * Visual Arts
// * Business
// * Business News
// * Careers
// * Investing
// * Management & Marketing
// * Shopping
// * Comedy
// * Education
// * Education Technology
// * Higher Education
// * K-12
// * Language Courses
// * Training
// * Games & Hobbies
// * Automotive
// * Aviation
// * Hobbies
// * Other Games
// * Video Games
// * Government & Organizations
// * Local
// * National
// * Non-Profit
// * Regional
// * Health
// * Alternative Health
// * Fitness & Nutrition
// * Self-Help
// * Sexuality
// * Kids & Family
// * Music
// * News & Politics
// * Religion & Spirituality
// * Buddhism
// * Christianity
// * Hinduism
// * Islam
// * Judaism
// * Other
// * Spirituality
// * Science & Medicine
// * Medicine
// * Natural Sciences
// * Social Sciences
// * Society & Culture
// * History
// * Personal Journals
// * Philosophy
// * Places & Travel
// * Sports & Recreation
// * Amateur
// * College & High School
// * Outdoor
// * Professional
// * Technology
// * Gadgets
// * Podcasting
// * Software How-To
// * Tech News
// * TV & Film
func (p *Podcast) AddCategory(category string, subCategories []string) {
if len(category) == 0 {
return
}
// RSS 2.0 Category only supports 1-tier
if len(p.Category) > 0 {
p.Category = p.Category + "," + category
} else {
p.Category = category
}
icat := ICategory{Text: category}
for _, c := range subCategories {
if len(c) == 0 {
continue
}
icat2 := ICategory{Text: c}
icat.ICategories = append(icat.ICategories, &icat2)
}
p.ICategories = append(p.ICategories, &icat)
}
// AddImage adds the specified Image to the Podcast.
//
// Podcast feeds contain artwork that is a minimum size of
// 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels,
// 72 dpi, in JPEG or PNG format with appropriate file
// extensions (.jpg, .png), and in the RGB colorspace. To optimize
// images for mobile devices, Apple recommends compressing your
// image files.
func (p *Podcast) AddImage(url string) {
if len(url) == 0 {
return
}
p.Image = &Image{
URL: url,
Title: p.Title,
Link: p.Link,
}
p.IImage = &IImage{HREF: url}
}
// AddItem adds the podcast episode. It returns a count of Items added or any
// errors in validation that may have occurred.
//
// This method takes the "itunes overrides" approach to populating
// itunes tags according to the overrides rules in the specification.
// This not only complies completely with iTunes parsing rules; but, it also
// displays what is possible to be set on an individual episode level if you
// wish to have more fine grain control over your content.
//
// This method imposes strict validation of the Item being added to confirm
// to Podcast and iTunes specifications.
//
// Article minimal requirements are:
//
// * Title
// * Description
// * Link
//
// Audio, Video and Downloads minimal requirements are:
//
// * Title
// * Description
// * Enclosure (HREF, Type and Length all required)
//
// The following fields are always overwritten (don't set them):
//
// * GUID
// * PubDateFormatted
// * AuthorFormatted
// * Enclosure.TypeFormatted
// * Enclosure.LengthFormatted
//
// Recommendations:
//
// * Just set the minimal fields: the rest get set for you.
// * Always set an Enclosure.Length, to be nice to your downloaders.
// * Follow Apple's best practices to enrich your podcasts:
// https://help.apple.com/itc/podcasts_connect/#/itc2b3780e76
// * For specifications of itunes tags, see:
// https://help.apple.com/itc/podcasts_connect/#/itcb54353390
//
func (p *Podcast) AddItem(i Item) (int, error) {
// initial guards for required fields
if len(i.Title) == 0 || len(i.Description) == 0 {
return len(p.Items), errors.New("Title and Description are required")
}
if i.Enclosure != nil {
if len(i.Enclosure.URL) == 0 {
return len(p.Items),
errors.New(i.Title + ": Enclosure.URL is required")
}
if i.Enclosure.Type.String() == enclosureDefault {
return len(p.Items),
errors.New(i.Title + ": Enclosure.Type is required")
}
} else if len(i.Link) == 0 {
return len(p.Items),
errors.New(i.Title + ": Link is required when not using Enclosure")
}
// corrective actions and overrides
//
i.PubDateFormatted = parseDateRFC1123Z(i.PubDate)
i.AuthorFormatted = parseAuthorNameEmail(i.Author)
if i.Enclosure != nil {
if len(i.GUID) == 0 {
i.GUID = i.Enclosure.URL // yep, GUID is the Permlink URL
}
if i.Enclosure.Length < 0 {
i.Enclosure.Length = 0
}
i.Enclosure.LengthFormatted = strconv.FormatInt(i.Enclosure.Length, 10)
i.Enclosure.TypeFormatted = i.Enclosure.Type.String()
// allow Link to be set for article references to Downloads,
// otherwise set it to the enclosurer's URL.
if len(i.Link) == 0 {
i.Link = i.Enclosure.URL
}
} else {
i.GUID = i.Link // yep, GUID is the Permlink URL
}
// iTunes it
//
if len(i.IAuthor) == 0 {
switch {
case i.Author != nil:
i.IAuthor = i.Author.Email
case len(p.IAuthor) != 0:
i.Author = &Author{Email: p.IAuthor}
i.IAuthor = p.IAuthor
case len(p.ManagingEditor) != 0:
i.Author = &Author{Email: p.ManagingEditor}
i.IAuthor = p.ManagingEditor
}
}
if i.IImage == nil {
if p.Image != nil {
i.IImage = &IImage{HREF: p.Image.URL}
}
}
p.Items = append(p.Items, &i)
return len(p.Items), nil
}
// AddPubDate adds the datetime as a parsed PubDate.
//
// UTC time is used by default.
func (p *Podcast) AddPubDate(datetime *time.Time) {
p.PubDate = parseDateRFC1123Z(datetime)
}
// AddLastBuildDate adds the datetime as a parsed PubDate.
//
// UTC time is used by default.
func (p *Podcast) AddLastBuildDate(datetime *time.Time) {
p.LastBuildDate = parseDateRFC1123Z(datetime)
}
// AddSubTitle adds the iTunes subtitle that is displayed with the title
// in iTunes.
//
// Note that this field should be just a few words long according to Apple.
// This method will truncate the string to 64 chars if too long with "..."
func (p *Podcast) AddSubTitle(subTitle string) {
count := utf8.RuneCountInString(subTitle)
if count == 0 {
return
}
if count > 64 {
s := []rune(subTitle)
subTitle = string(s[0:61]) + "..."
}
p.ISubtitle = subTitle
}
// AddSummary adds the iTunes summary.
//
// Limit: 4000 characters
//
// Note that this field is a CDATA encoded field which allows for rich text
// such as html links: `<a href="http://www.apple.com">Apple</a>`.
func (p *Podcast) AddSummary(summary string) {
count := utf8.RuneCountInString(summary)
if count == 0 {
return
}
if count > 4000 {
s := []rune(summary)
summary = string(s[0:4000])
}
p.ISummary = &ISummary{
Text: summary,
}
}
// Bytes returns an encoded []byte slice.
func (p *Podcast) Bytes() []byte {
return []byte(p.String())
}
// Encode writes the bytes to the io.Writer stream in RSS 2.0 specification.
func (p *Podcast) Encode(w io.Writer) error {
if _, err := w.Write([]byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")); err != nil {
return errors.Wrap(err, "podcast.Encode: w.Write return error")
}
atomLink := ""
if p.AtomLink != nil {
atomLink = "http://www.w3.org/2005/Atom"
}
wrapped := podcastWrapper{
ITUNESNS: "http://www.itunes.com/dtds/podcast-1.0.dtd",
ATOMNS: atomLink,
Version: "2.0",
Channel: p,
}
return p.encode(w, wrapped)
}
// String encodes the Podcast state to a string.
func (p *Podcast) String() string {
b := new(bytes.Buffer)
if err := p.Encode(b); err != nil {
return "String: podcast.write returned the error: " + err.Error()
}
return b.String()
}
// // Write implements the io.Writer interface to write an RSS 2.0 stream
// // that is compliant to the RSS 2.0 specification.
// func (p *Podcast) Write(b []byte) (n int, err error) {
// buf := bytes.NewBuffer(b)
// if err := p.Encode(buf); err != nil {
// return 0, errors.Wrap(err, "Write: podcast.encode returned error")
// }
// return buf.Len(), nil
// }
type podcastWrapper struct {
XMLName xml.Name `xml:"rss"`
Version string `xml:"version,attr"`
ATOMNS string `xml:"xmlns:atom,attr,omitempty"`
ITUNESNS string `xml:"xmlns:itunes,attr"`
Channel *Podcast
}
var encoder = func(w io.Writer, o interface{}) error {
e := xml.NewEncoder(w)
e.Indent("", " ")
if err := e.Encode(o); err != nil {
return errors.Wrap(err, "podcast.encoder: e.Encode returned error")
}
return nil
}
var parseAuthorNameEmail = func(a *Author) string {
var author string
if a != nil {
author = a.Email
if len(a.Name) > 0 {
author = fmt.Sprintf("%s (%s)", a.Email, a.Name)
}
}
return author
}

12
vendor/github.com/eduncan911/podcast/textinput.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
package podcast
import "encoding/xml"
// TextInput represents text inputs.
type TextInput struct {
XMLName xml.Name `xml:"textInput"`
Title string `xml:"title"`
Description string `xml:"description"`
Name string `xml:"name"`
Link string `xml:"link"`
}

View File

@ -1,41 +0,0 @@
# 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

View File

@ -1,26 +0,0 @@
# How to contribute
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
[Contributor License Agreement](https://cla.developers.google.com/clas).
You may have already signed it for other Google projects.

View File

@ -1,9 +0,0 @@
Paul Borman <borman@google.com>
bmatsuo
shawnps
theory
jboverfelt
dsymonds
cd1
wallclockbuilder
dansouza

View File

@ -1,27 +0,0 @@
Copyright (c) 2009,2014 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,21 +0,0 @@
# uuid
The uuid package generates and inspects UUIDs based on
[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
code.google.com/p/go-uuid). It differs from these earlier packages in that
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
```sh
go get github.com/google/uuid
```
###### Documentation
[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](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:
http://pkg.go.dev/github.com/google/uuid

80
vendor/github.com/google/uuid/dce.go generated vendored
View File

@ -1,80 +0,0 @@
// Copyright 2016 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"
"fmt"
"os"
)
// A Domain represents a Version 2 domain
type Domain byte
// Domain constants for DCE Security (Version 2) UUIDs.
const (
Person = Domain(0)
Group = Domain(1)
Org = Domain(2)
)
// NewDCESecurity returns a DCE Security (Version 2) UUID.
//
// The domain should be one of Person, Group or Org.
// On a POSIX system the id should be the users UID for the Person
// domain and the users GID for the Group. The meaning of id for
// the domain Org or on non-POSIX systems is site defined.
//
// For a given domain/id pair the same token may be returned for up to
// 7 minutes and 10 seconds.
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
uuid, err := NewUUID()
if err == nil {
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
uuid[9] = byte(domain)
binary.BigEndian.PutUint32(uuid[0:], id)
}
return uuid, err
}
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
// domain with the id returned by os.Getuid.
//
// NewDCESecurity(Person, uint32(os.Getuid()))
func NewDCEPerson() (UUID, error) {
return NewDCESecurity(Person, uint32(os.Getuid()))
}
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
// domain with the id returned by os.Getgid.
//
// NewDCESecurity(Group, uint32(os.Getgid()))
func NewDCEGroup() (UUID, error) {
return NewDCESecurity(Group, uint32(os.Getgid()))
}
// Domain returns the domain for a Version 2 UUID. Domains are only defined
// for Version 2 UUIDs.
func (uuid UUID) Domain() Domain {
return Domain(uuid[9])
}
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
// UUIDs.
func (uuid UUID) ID() uint32 {
return binary.BigEndian.Uint32(uuid[0:4])
}
func (d Domain) String() string {
switch d {
case Person:
return "Person"
case Group:
return "Group"
case Org:
return "Org"
}
return fmt.Sprintf("Domain%d", int(d))
}

12
vendor/github.com/google/uuid/doc.go generated vendored
View File

@ -1,12 +0,0 @@
// Copyright 2016 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 generates and inspects UUIDs.
//
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
// Services.
//
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
// maps or compared directly.
package uuid

View File

@ -1,59 +0,0 @@
// Copyright 2016 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 (
"crypto/md5"
"crypto/sha1"
"hash"
)
// Well known namespace IDs and UUIDs
var (
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
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
// data generated by h. The hash should be at least 16 byte in length. The
// first 16 bytes of the hash are used to form the UUID. The version of the
// UUID will be the lower 4 bits of version. NewHash is used to implement
// NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset()
h.Write(space[:]) //nolint:errcheck
h.Write(data) //nolint:errcheck
s := h.Sum(nil)
var uuid UUID
copy(uuid[:], s)
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
return uuid
}
// NewMD5 returns a new MD5 (Version 3) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(md5.New(), space, data, 3)
func NewMD5(space UUID, data []byte) UUID {
return NewHash(md5.New(), space, data, 3)
}
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(sha1.New(), space, data, 5)
func NewSHA1(space UUID, data []byte) UUID {
return NewHash(sha1.New(), space, data, 5)
}

View File

@ -1,38 +0,0 @@
// Copyright 2016 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 "fmt"
// MarshalText implements encoding.TextMarshaler.
func (uuid UUID) MarshalText() ([]byte, error) {
var js [36]byte
encodeHex(js[:], uuid)
return js[:], nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err != nil {
return err
}
*uuid = id
return nil
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (uuid UUID) MarshalBinary() ([]byte, error) {
return uuid[:], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (uuid *UUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(uuid[:], data)
return nil
}

View File

@ -1,90 +0,0 @@
// Copyright 2016 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 (
"sync"
)
var (
nodeMu sync.Mutex
ifname string // name of interface being used
nodeID [6]byte // hardware for version 1 UUIDs
zeroID [6]byte // nodeID with only 0's
)
// NodeInterface returns the name of the interface from which the NodeID was
// derived. The interface "user" is returned if the NodeID was set by
// SetNodeID.
func NodeInterface() string {
defer nodeMu.Unlock()
nodeMu.Lock()
return ifname
}
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
// If name is "" then the first usable interface found will be used or a random
// Node ID will be generated. If a named interface cannot be found then false
// is returned.
//
// SetNodeInterface never fails when name is "".
func SetNodeInterface(name string) bool {
defer nodeMu.Unlock()
nodeMu.Lock()
return setNodeInterface(name)
}
func setNodeInterface(name string) bool {
iname, addr := getHardwareInterface(name) // null implementation for js
if iname != "" && addr != nil {
ifname = iname
copy(nodeID[:], addr)
return true
}
// We found no interfaces with a valid hardware address. If name
// does not specify a specific interface generate a random Node ID
// (section 4.1.6)
if name == "" {
ifname = "random"
randomBits(nodeID[:])
return true
}
return false
}
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
// if not already set.
func NodeID() []byte {
defer nodeMu.Unlock()
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nid := nodeID
return nid[:]
}
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
// of id are used. If id is less than 6 bytes then false is returned and the
// Node ID is not set.
func SetNodeID(id []byte) bool {
if len(id) < 6 {
return false
}
defer nodeMu.Unlock()
nodeMu.Lock()
copy(nodeID[:], id)
ifname = "user"
return true
}
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) NodeID() []byte {
var node [6]byte
copy(node[:], uuid[10:])
return node[:]
}

View File

@ -1,12 +0,0 @@
// Copyright 2017 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.
// +build js
package uuid
// getHardwareInterface returns nil values for the JS version of the code.
// 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 }

View File

@ -1,33 +0,0 @@
// Copyright 2017 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.
// +build !js
package uuid
import "net"
var interfaces []net.Interface // cached list of interfaces
// getHardwareInterface returns the name and hardware address of interface name.
// If name is "" then the name and hardware address of one of the system's
// interfaces is returned. If no interfaces are found (name does not exist or
// there are no interfaces) then "", nil is returned.
//
// Only addresses of at least 6 bytes are returned.
func getHardwareInterface(name string) (string, []byte) {
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
if err != nil {
return "", nil
}
}
for _, ifs := range interfaces {
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
return ifs.Name, ifs.HardwareAddr
}
}
return "", nil
}

118
vendor/github.com/google/uuid/null.go generated vendored
View File

@ -1,118 +0,0 @@
// Copyright 2021 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 (
"bytes"
"database/sql/driver"
"encoding/json"
"fmt"
)
var jsonNull = []byte("null")
// NullUUID represents a UUID that may be null.
// NullUUID implements the SQL driver.Scanner interface so
// it can be used as a scan destination:
//
// var u uuid.NullUUID
// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
// ...
// if u.Valid {
// // use u.UUID
// } else {
// // NULL value
// }
//
type NullUUID struct {
UUID UUID
Valid bool // Valid is true if UUID is not NULL
}
// Scan implements the SQL driver.Scanner interface.
func (nu *NullUUID) Scan(value interface{}) error {
if value == nil {
nu.UUID, nu.Valid = Nil, false
return nil
}
err := nu.UUID.Scan(value)
if err != nil {
nu.Valid = false
return err
}
nu.Valid = true
return nil
}
// Value implements the driver Valuer interface.
func (nu NullUUID) Value() (driver.Value, error) {
if !nu.Valid {
return nil, nil
}
// Delegate to UUID Value function
return nu.UUID.Value()
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (nu NullUUID) MarshalBinary() ([]byte, error) {
if nu.Valid {
return nu.UUID[:], nil
}
return []byte(nil), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (nu *NullUUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(nu.UUID[:], data)
nu.Valid = true
return nil
}
// MarshalText implements encoding.TextMarshaler.
func (nu NullUUID) MarshalText() ([]byte, error) {
if nu.Valid {
return nu.UUID.MarshalText()
}
return jsonNull, nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (nu *NullUUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err != nil {
nu.Valid = false
return err
}
nu.UUID = id
nu.Valid = true
return nil
}
// MarshalJSON implements json.Marshaler.
func (nu NullUUID) MarshalJSON() ([]byte, error) {
if nu.Valid {
return json.Marshal(nu.UUID)
}
return jsonNull, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (nu *NullUUID) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, jsonNull) {
*nu = NullUUID{}
return nil // valid null UUID
}
err := json.Unmarshal(data, &nu.UUID)
nu.Valid = err == nil
return err
}

59
vendor/github.com/google/uuid/sql.go generated vendored
View File

@ -1,59 +0,0 @@
// Copyright 2016 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 (
"database/sql/driver"
"fmt"
)
// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
switch src := src.(type) {
case nil:
return nil
case string:
// if an empty UUID comes from a table, we return a null UUID
if src == "" {
return nil
}
// see Parse for required string format
u, err := Parse(src)
if err != nil {
return fmt.Errorf("Scan: %v", err)
}
*uuid = u
case []byte:
// if an empty UUID comes from a table, we return a null UUID
if len(src) == 0 {
return nil
}
// assumes a simple slice of bytes if 16 bytes
// otherwise attempts to parse
if len(src) != 16 {
return uuid.Scan(string(src))
}
copy((*uuid)[:], src)
default:
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
}
return nil
}
// Value implements sql.Valuer so that UUIDs can be written to databases
// transparently. Currently, UUIDs map to strings. Please consult
// database-specific driver documentation for matching types.
func (uuid UUID) Value() (driver.Value, error) {
return uuid.String(), nil
}

134
vendor/github.com/google/uuid/time.go generated vendored
View File

@ -1,134 +0,0 @@
// Copyright 2016 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"
"sync"
"time"
)
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
// 1582.
type Time int64
const (
lillian = 2299160 // Julian day of 15 Oct 1582
unix = 2440587 // Julian day of 1 Jan 1970
epoch = unix - lillian // Days between epochs
g1582 = epoch * 86400 // seconds between epochs
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
)
var (
timeMu sync.Mutex
lasttime uint64 // last time we returned
clockSeq uint16 // clock sequence for this run
timeNow = time.Now // for testing
)
// UnixTime converts t the number of seconds and nanoseconds using the Unix
// epoch of 1 Jan 1970.
func (t Time) UnixTime() (sec, nsec int64) {
sec = int64(t - g1582ns100)
nsec = (sec % 10000000) * 100
sec /= 10000000
return sec, nsec
}
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
// clock sequence as well as adjusting the clock sequence as needed. An error
// is returned if the current time cannot be determined.
func GetTime() (Time, uint16, error) {
defer timeMu.Unlock()
timeMu.Lock()
return getTime()
}
func getTime() (Time, uint16, error) {
t := timeNow()
// If we don't have a clock sequence already, set one.
if clockSeq == 0 {
setClockSequence(-1)
}
now := uint64(t.UnixNano()/100) + g1582ns100
// If time has gone backwards with this clock sequence then we
// increment the clock sequence
if now <= lasttime {
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
}
lasttime = now
return Time(now), clockSeq, nil
}
// ClockSequence returns the current clock sequence, generating one if not
// already set. The clock sequence is only used for Version 1 UUIDs.
//
// The uuid package does not use global static storage for the clock sequence or
// the last time a UUID was generated. Unless SetClockSequence is used, a new
// random clock sequence is generated the first time a clock sequence is
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
func ClockSequence() int {
defer timeMu.Unlock()
timeMu.Lock()
return clockSequence()
}
func clockSequence() int {
if clockSeq == 0 {
setClockSequence(-1)
}
return int(clockSeq & 0x3fff)
}
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
// -1 causes a new sequence to be generated.
func SetClockSequence(seq int) {
defer timeMu.Unlock()
timeMu.Lock()
setClockSequence(seq)
}
func setClockSequence(seq int) {
if seq == -1 {
var b [2]byte
randomBits(b[:]) // clock sequence
seq = int(b[0])<<8 | int(b[1])
}
oldSeq := clockSeq
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
if oldSeq != clockSeq {
lasttime = 0
}
}
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs.
func (uuid UUID) 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.
// The clock sequence is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) ClockSequence() int {
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
}

View File

@ -1,43 +0,0 @@
// Copyright 2016 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"
)
// randomBits completely fills slice b with random data.
func randomBits(b []byte) {
if _, err := io.ReadFull(rander, b); err != nil {
panic(err.Error()) // rand should never fail
}
}
// xvalues returns the value of a byte as a hexadecimal digit or 255.
var xvalues = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}
// xtob converts hex characters x1 and x2 into a byte.
func xtob(x1, x2 byte) (byte, bool) {
b1 := xvalues[x1]
b2 := xvalues[x2]
return (b1 << 4) | b2, b1 != 255 && b2 != 255
}

365
vendor/github.com/google/uuid/uuid.go generated vendored
View File

@ -1,365 +0,0 @@
// Copyright 2018 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 (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"sync"
)
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
// 4122.
type UUID [16]byte
// A Version represents a UUID's version.
type Version byte
// A Variant represents a UUID's variant.
type Variant byte
// Constants returned by Variant.
const (
Invalid = Variant(iota) // Invalid UUID
RFC4122 // The variant specified in RFC4122
Reserved // Reserved, NCS backward compatibility.
Microsoft // Reserved, Microsoft Corporation backward compatibility.
Future // Reserved for future definition.
)
const randPoolSize = 16 * 16
var (
rander = rand.Reader // random function
poolEnabled = false
poolMu sync.Mutex
poolPos = randPoolSize // protected with poolMu
pool [randPoolSize]byte // protected with poolMu
)
type invalidLengthError struct{ len int }
func (err invalidLengthError) Error() string {
return fmt.Sprintf("invalid UUID length: %d", err.len)
}
// IsInvalidLengthError is matcher function for custom error invalidLengthError
func IsInvalidLengthError(err error) bool {
_, ok := err.(invalidLengthError)
return ok
}
// 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) {
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36:
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9:
if !strings.EqualFold(s[:9], "urn:uuid:") {
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
}
s = s[9:]
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
case 36 + 2:
s = s[1:]
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
case 32:
var ok bool
for i := range uuid {
uuid[i], ok = xtob(s[i*2], s[i*2+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, invalidLengthError{len(s)}
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34,
} {
v, ok := xtob(s[x], s[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
func ParseBytes(b []byte) (UUID, error) {
var uuid UUID
switch len(b) {
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
}
b = b[9:]
case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
b = b[1:]
case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
var ok bool
for i := 0; i < 32; i += 2 {
uuid[i/2], ok = xtob(b[i], b[i+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, invalidLengthError{len(b)}
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34,
} {
v, ok := xtob(b[x], b[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// MustParse is like Parse but panics if the string cannot be parsed.
// It simplifies safe initialization of global variables holding compiled UUIDs.
func MustParse(s string) UUID {
uuid, err := Parse(s)
if err != nil {
panic(`uuid: Parse(` + s + `): ` + err.Error())
}
return uuid
}
// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
// does not have a length of 16. The bytes are copied from the slice.
func FromBytes(b []byte) (uuid UUID, err error) {
err = uuid.UnmarshalBinary(b)
return uuid, err
}
// Must returns uuid if err is nil and panics otherwise.
func Must(uuid UUID, err error) UUID {
if err != nil {
panic(err)
}
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 {
var buf [36]byte
encodeHex(buf[:], uuid)
return string(buf[:])
}
// URN returns the RFC 2141 URN form of uuid,
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
func (uuid UUID) URN() string {
var buf [36 + 9]byte
copy(buf[:], "urn:uuid:")
encodeHex(buf[9:], uuid)
return string(buf[:])
}
func encodeHex(dst []byte, uuid UUID) {
hex.Encode(dst, uuid[:4])
dst[8] = '-'
hex.Encode(dst[9:13], uuid[4:6])
dst[13] = '-'
hex.Encode(dst[14:18], uuid[6:8])
dst[18] = '-'
hex.Encode(dst[19:23], uuid[8:10])
dst[23] = '-'
hex.Encode(dst[24:], uuid[10:])
}
// Variant returns the variant encoded in uuid.
func (uuid UUID) Variant() Variant {
switch {
case (uuid[8] & 0xc0) == 0x80:
return RFC4122
case (uuid[8] & 0xe0) == 0xc0:
return Microsoft
case (uuid[8] & 0xe0) == 0xe0:
return Future
default:
return Reserved
}
}
// Version returns the version of uuid.
func (uuid UUID) Version() Version {
return Version(uuid[6] >> 4)
}
func (v Version) String() string {
if v > 15 {
return fmt.Sprintf("BAD_VERSION_%d", v)
}
return fmt.Sprintf("VERSION_%d", v)
}
func (v Variant) String() string {
switch v {
case RFC4122:
return "RFC4122"
case Reserved:
return "Reserved"
case Microsoft:
return "Microsoft"
case Future:
return "Future"
case Invalid:
return "Invalid"
}
return fmt.Sprintf("BadVariant%d", int(v))
}
// SetRand sets the random number generator to r, which implements io.Reader.
// If r.Read returns an error when the package requests random data then
// a panic will be issued.
//
// Calling SetRand with nil sets the random number generator to the default
// generator.
func SetRand(r io.Reader) {
if r == nil {
rander = rand.Reader
return
}
rander = r
}
// EnableRandPool enables internal randomness pool used for Random
// (Version 4) UUID generation. The pool contains random bytes read from
// the random number generator on demand in batches. Enabling the pool
// may improve the UUID generation throughput significantly.
//
// Since the pool is stored on the Go heap, this feature may be a bad fit
// for security sensitive applications.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func EnableRandPool() {
poolEnabled = true
}
// DisableRandPool disables the randomness pool if it was previously
// enabled with EnableRandPool.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func DisableRandPool() {
poolEnabled = false
defer poolMu.Unlock()
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
}

View File

@ -1,44 +0,0 @@
// Copyright 2016 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"
)
// NewUUID returns a Version 1 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 NewUUID returns nil. If clock sequence has not been set by
// SetClockSequence then it will be set automatically. If GetTime fails to
// return the current NewUUID returns nil and an error.
//
// In most cases, New should be used.
func NewUUID() (UUID, error) {
var uuid UUID
now, seq, err := GetTime()
if err != nil {
return uuid, err
}
timeLow := uint32(now & 0xffffffff)
timeMid := uint16((now >> 32) & 0xffff)
timeHi := uint16((now >> 48) & 0x0fff)
timeHi |= 0x1000 // Version 1
binary.BigEndian.PutUint32(uuid[0:], timeLow)
binary.BigEndian.PutUint16(uuid[4:], timeMid)
binary.BigEndian.PutUint16(uuid[6:], timeHi)
binary.BigEndian.PutUint16(uuid[8:], seq)
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
copy(uuid[10:], nodeID[:])
nodeMu.Unlock()
return uuid, nil
}

View File

@ -1,76 +0,0 @@
// Copyright 2016 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"
// New creates a new random UUID or panics. New is equivalent to
// the expression
//
// uuid.Must(uuid.NewRandom())
func New() UUID {
return Must(NewRandom())
}
// NewString creates a new random UUID and returns it as a string or panics.
// NewString is equivalent to the expression
//
// uuid.New().String()
func NewString() string {
return Must(NewRandom()).String()
}
// NewRandom returns a Random (Version 4) UUID.
//
// The strength of the UUIDs is based on the strength of the crypto/rand
// package.
//
// Uses the randomness pool if it was enabled with EnableRandPool.
//
// A note about uniqueness derived from the UUID Wikipedia entry:
//
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
// hit by a meteorite is estimated to be one chance in 17 billion, that
// means the probability is about 0.00000000006 (6 × 1011),
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate.
func NewRandom() (UUID, error) {
if !poolEnabled {
return NewRandomFromReader(rander)
}
return newRandomFromPool()
}
// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
func NewRandomFromReader(r io.Reader) (UUID, error) {
var uuid UUID
_, err := io.ReadFull(r, uuid[:])
if err != nil {
return Nil, err
}
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}
func newRandomFromPool() (UUID, error) {
var uuid UUID
poolMu.Lock()
if poolPos == randPoolSize {
_, err := io.ReadFull(rander, pool[:])
if err != nil {
poolMu.Unlock()
return Nil, err
}
poolPos = 0
}
copy(uuid[:], pool[poolPos:(poolPos+16)])
poolPos += 16
poolMu.Unlock()
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

View File

@ -1,56 +0,0 @@
// 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
}

View File

@ -1,104 +0,0 @@
// 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
}

View File

@ -1,20 +0,0 @@
; https://editorconfig.org/
root = true
[*]
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
indent_style = tab
indent_size = 4
[*.md]
indent_size = 4
trim_trailing_whitespace = false
eclint_indent_style = unset

View File

@ -1 +0,0 @@
coverage.coverprofile

View File

@ -1,28 +0,0 @@
Copyright (c) 2023 The Gorilla Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,34 +0,0 @@
GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '')
GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest
GO_SEC=$(shell which gosec 2> /dev/null || echo '')
GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest
GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '')
GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest
.PHONY: golangci-lint
golangci-lint:
$(if $(GO_LINT), ,go install $(GO_LINT_URI))
@echo "##### Running golangci-lint"
golangci-lint run -v
.PHONY: gosec
gosec:
$(if $(GO_SEC), ,go install $(GO_SEC_URI))
@echo "##### Running gosec"
gosec ./...
.PHONY: govulncheck
govulncheck:
$(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI))
@echo "##### Running govulncheck"
govulncheck ./...
.PHONY: verify
verify: golangci-lint gosec govulncheck
.PHONY: test
test:
@echo "##### Running tests"
go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./...

View File

@ -1,198 +0,0 @@
## gorilla/feeds
![testing](https://github.com/gorilla/feeds/actions/workflows/test.yml/badge.svg)
[![codecov](https://codecov.io/github/gorilla/feeds/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/feeds)
[![godoc](https://godoc.org/github.com/gorilla/feeds?status.svg)](https://godoc.org/github.com/gorilla/feeds)
[![sourcegraph](https://sourcegraph.com/github.com/gorilla/feeds/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/feeds?badge)
![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5)
feeds is a web feed generator library for generating RSS, Atom and JSON feeds from Go
applications.
### Goals
* Provide a simple interface to create both Atom & RSS 2.0 feeds
* Full support for [Atom][atom], [RSS 2.0][rss], and [JSON Feed Version 1][jsonfeed] spec elements
* Ability to modify particulars for each spec
[atom]: https://tools.ietf.org/html/rfc4287
[rss]: http://www.rssboard.org/rss-specification
[jsonfeed]: https://jsonfeed.org/version/1.1
### Usage
```go
package main
import (
"fmt"
"log"
"time"
"github.com/gorilla/feeds"
)
func main() {
now := time.Now()
feed := &feeds.Feed{
Title: "jmoiron.net blog",
Link: &feeds.Link{Href: "http://jmoiron.net/blog"},
Description: "discussion about tech, footie, photos",
Author: &feeds.Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
Created: now,
}
feed.Items = []*feeds.Item{
&feeds.Item{
Title: "Limiting Concurrency in Go",
Link: &feeds.Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"},
Description: "A discussion on controlled parallelism in golang",
Author: &feeds.Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
Created: now,
},
&feeds.Item{
Title: "Logic-less Template Redux",
Link: &feeds.Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"},
Description: "More thoughts on logicless templates",
Created: now,
},
&feeds.Item{
Title: "Idiomatic Code Reuse in Go",
Link: &feeds.Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"},
Description: "How to use interfaces <em>effectively</em>",
Created: now,
},
}
atom, err := feed.ToAtom()
if err != nil {
log.Fatal(err)
}
rss, err := feed.ToRss()
if err != nil {
log.Fatal(err)
}
json, err := feed.ToJSON()
if err != nil {
log.Fatal(err)
}
fmt.Println(atom, "\n", rss, "\n", json)
}
```
Outputs:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>jmoiron.net blog</title>
<link href="http://jmoiron.net/blog"></link>
<id>http://jmoiron.net/blog</id>
<updated>2013-01-16T03:26:01-05:00</updated>
<summary>discussion about tech, footie, photos</summary>
<entry>
<title>Limiting Concurrency in Go</title>
<link href="http://jmoiron.net/blog/limiting-concurrency-in-go/"></link>
<updated>2013-01-16T03:26:01-05:00</updated>
<id>tag:jmoiron.net,2013-01-16:/blog/limiting-concurrency-in-go/</id>
<summary type="html">A discussion on controlled parallelism in golang</summary>
<author>
<name>Jason Moiron</name>
<email>jmoiron@jmoiron.net</email>
</author>
</entry>
<entry>
<title>Logic-less Template Redux</title>
<link href="http://jmoiron.net/blog/logicless-template-redux/"></link>
<updated>2013-01-16T03:26:01-05:00</updated>
<id>tag:jmoiron.net,2013-01-16:/blog/logicless-template-redux/</id>
<summary type="html">More thoughts on logicless templates</summary>
<author></author>
</entry>
<entry>
<title>Idiomatic Code Reuse in Go</title>
<link href="http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"></link>
<updated>2013-01-16T03:26:01-05:00</updated>
<id>tag:jmoiron.net,2013-01-16:/blog/idiomatic-code-reuse-in-go/</id>
<summary type="html">How to use interfaces &lt;em&gt;effectively&lt;/em&gt;</summary>
<author></author>
</entry>
</feed>
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>jmoiron.net blog</title>
<link>http://jmoiron.net/blog</link>
<description>discussion about tech, footie, photos</description>
<managingEditor>jmoiron@jmoiron.net (Jason Moiron)</managingEditor>
<pubDate>2013-01-16T03:22:24-05:00</pubDate>
<item>
<title>Limiting Concurrency in Go</title>
<link>http://jmoiron.net/blog/limiting-concurrency-in-go/</link>
<description>A discussion on controlled parallelism in golang</description>
<pubDate>2013-01-16T03:22:24-05:00</pubDate>
</item>
<item>
<title>Logic-less Template Redux</title>
<link>http://jmoiron.net/blog/logicless-template-redux/</link>
<description>More thoughts on logicless templates</description>
<pubDate>2013-01-16T03:22:24-05:00</pubDate>
</item>
<item>
<title>Idiomatic Code Reuse in Go</title>
<link>http://jmoiron.net/blog/idiomatic-code-reuse-in-go/</link>
<description>How to use interfaces &lt;em&gt;effectively&lt;/em&gt;</description>
<pubDate>2013-01-16T03:22:24-05:00</pubDate>
</item>
</channel>
</rss>
{
"version": "https://jsonfeed.org/version/1.1",
"title": "jmoiron.net blog",
"home_page_url": "http://jmoiron.net/blog",
"description": "discussion about tech, footie, photos",
"author": {
"name": "Jason Moiron"
},
"authors": [
{
"name": "Jason Moiron"
}
],
"items": [
{
"id": "",
"url": "http://jmoiron.net/blog/limiting-concurrency-in-go/",
"title": "Limiting Concurrency in Go",
"summary": "A discussion on controlled parallelism in golang",
"date_published": "2013-01-16T03:22:24.530817846-05:00",
"author": {
"name": "Jason Moiron"
},
"authors": [
{
"name": "Jason Moiron"
}
]
},
{
"id": "",
"url": "http://jmoiron.net/blog/logicless-template-redux/",
"title": "Logic-less Template Redux",
"summary": "More thoughts on logicless templates",
"date_published": "2013-01-16T03:22:24.530817846-05:00"
},
{
"id": "",
"url": "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/",
"title": "Idiomatic Code Reuse in Go",
"summary": "How to use interfaces \u003cem\u003eeffectively\u003c/em\u003e",
"date_published": "2013-01-16T03:22:24.530817846-05:00"
}
]
}
```

View File

@ -1,178 +0,0 @@
package feeds
import (
"encoding/xml"
"fmt"
"net/url"
"time"
)
// Generates Atom feed as XML
const ns = "http://www.w3.org/2005/Atom"
type AtomPerson struct {
Name string `xml:"name,omitempty"`
Uri string `xml:"uri,omitempty"`
Email string `xml:"email,omitempty"`
}
type AtomSummary struct {
XMLName xml.Name `xml:"summary"`
Content string `xml:",chardata"`
Type string `xml:"type,attr"`
}
type AtomContent struct {
XMLName xml.Name `xml:"content"`
Content string `xml:",chardata"`
Type string `xml:"type,attr"`
}
type AtomAuthor struct {
XMLName xml.Name `xml:"author"`
AtomPerson
}
type AtomContributor struct {
XMLName xml.Name `xml:"contributor"`
AtomPerson
}
type AtomEntry struct {
XMLName xml.Name `xml:"entry"`
Xmlns string `xml:"xmlns,attr,omitempty"`
Title string `xml:"title"` // required
Updated string `xml:"updated"` // required
Id string `xml:"id"` // required
Category string `xml:"category,omitempty"`
Content *AtomContent
Rights string `xml:"rights,omitempty"`
Source string `xml:"source,omitempty"`
Published string `xml:"published,omitempty"`
Contributor *AtomContributor
Links []AtomLink // required if no child 'content' elements
Summary *AtomSummary // required if content has src or content is base64
Author *AtomAuthor // required if feed lacks an author
}
// Multiple links with different rel can coexist
type AtomLink struct {
//Atom 1.0 <link rel="enclosure" type="audio/mpeg" title="MP3" href="http://www.example.org/myaudiofile.mp3" length="1234" />
XMLName xml.Name `xml:"link"`
Href string `xml:"href,attr"`
Rel string `xml:"rel,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
Length string `xml:"length,attr,omitempty"`
}
type AtomFeed struct {
XMLName xml.Name `xml:"feed"`
Xmlns string `xml:"xmlns,attr"`
Title string `xml:"title"` // required
Id string `xml:"id"` // required
Updated string `xml:"updated"` // required
Category string `xml:"category,omitempty"`
Icon string `xml:"icon,omitempty"`
Logo string `xml:"logo,omitempty"`
Rights string `xml:"rights,omitempty"` // copyright used
Subtitle string `xml:"subtitle,omitempty"`
Link *AtomLink
Author *AtomAuthor `xml:"author,omitempty"`
Contributor *AtomContributor
Entries []*AtomEntry `xml:"entry"`
}
type Atom struct {
*Feed
}
func newAtomEntry(i *Item) *AtomEntry {
id := i.Id
link := i.Link
if link == nil {
link = &Link{}
}
if len(id) == 0 {
// if there's no id set, try to create one, either from data or just a uuid
if len(link.Href) > 0 && (!i.Created.IsZero() || !i.Updated.IsZero()) {
dateStr := anyTimeFormat("2006-01-02", i.Updated, i.Created)
host, path := link.Href, "/invalid.html"
if url, err := url.Parse(link.Href); err == nil {
host, path = url.Host, url.Path
}
id = fmt.Sprintf("tag:%s,%s:%s", host, dateStr, path)
} else {
id = "urn:uuid:" + NewUUID().String()
}
}
var name, email string
if i.Author != nil {
name, email = i.Author.Name, i.Author.Email
}
link_rel := link.Rel
if link_rel == "" {
link_rel = "alternate"
}
x := &AtomEntry{
Title: i.Title,
Links: []AtomLink{{Href: link.Href, Rel: link_rel, Type: link.Type}},
Id: id,
Updated: anyTimeFormat(time.RFC3339, i.Updated, i.Created),
}
// if there's a description, assume it's html
if len(i.Description) > 0 {
x.Summary = &AtomSummary{Content: i.Description, Type: "html"}
}
// if there's a content, assume it's html
if len(i.Content) > 0 {
x.Content = &AtomContent{Content: i.Content, Type: "html"}
}
if i.Enclosure != nil && link_rel != "enclosure" {
x.Links = append(x.Links, AtomLink{Href: i.Enclosure.Url, Rel: "enclosure", Type: i.Enclosure.Type, Length: i.Enclosure.Length})
}
if len(name) > 0 || len(email) > 0 {
x.Author = &AtomAuthor{AtomPerson: AtomPerson{Name: name, Email: email}}
}
return x
}
// create a new AtomFeed with a generic Feed struct's data
func (a *Atom) AtomFeed() *AtomFeed {
updated := anyTimeFormat(time.RFC3339, a.Updated, a.Created)
link := a.Link
if link == nil {
link = &Link{}
}
feed := &AtomFeed{
Xmlns: ns,
Title: a.Title,
Link: &AtomLink{Href: link.Href, Rel: link.Rel},
Subtitle: a.Description,
Id: link.Href,
Updated: updated,
Rights: a.Copyright,
}
if a.Author != nil {
feed.Author = &AtomAuthor{AtomPerson: AtomPerson{Name: a.Author.Name, Email: a.Author.Email}}
}
for _, e := range a.Items {
feed.Entries = append(feed.Entries, newAtomEntry(e))
}
return feed
}
// FeedXml returns an XML-Ready object for an Atom object
func (a *Atom) FeedXml() interface{} {
return a.AtomFeed()
}
// FeedXml returns an XML-ready object for an AtomFeed object
func (a *AtomFeed) FeedXml() interface{} {
return a
}

View File

@ -1,73 +0,0 @@
/*
Syndication (feed) generator library for golang.
Installing
go get github.com/gorilla/feeds
Feeds provides a simple, generic Feed interface with a generic Item object as well as RSS, Atom and JSON Feed specific RssFeed, AtomFeed and JSONFeed objects which allow access to all of each spec's defined elements.
Examples
Create a Feed and some Items in that feed using the generic interfaces:
import (
"time"
. "github.com/gorilla/feeds"
)
now = time.Now()
feed := &Feed{
Title: "jmoiron.net blog",
Link: &Link{Href: "http://jmoiron.net/blog"},
Description: "discussion about tech, footie, photos",
Author: &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
Created: now,
Copyright: "This work is copyright © Benjamin Button",
}
feed.Items = []*Item{
&Item{
Title: "Limiting Concurrency in Go",
Link: &Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"},
Description: "A discussion on controlled parallelism in golang",
Author: &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
Created: now,
},
&Item{
Title: "Logic-less Template Redux",
Link: &Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"},
Description: "More thoughts on logicless templates",
Created: now,
},
&Item{
Title: "Idiomatic Code Reuse in Go",
Link: &Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"},
Description: "How to use interfaces <em>effectively</em>",
Created: now,
},
}
From here, you can output Atom, RSS, or JSON Feed versions of this feed easily
atom, err := feed.ToAtom()
rss, err := feed.ToRss()
json, err := feed.ToJSON()
You can also get access to the underlying objects that feeds uses to export its XML
atomFeed := (&Atom{Feed: feed}).AtomFeed()
rssFeed := (&Rss{Feed: feed}).RssFeed()
jsonFeed := (&JSON{Feed: feed}).JSONFeed()
From here, you can modify or add each syndication's specific fields before outputting
atomFeed.Subtitle = "plays the blues"
atom, err := ToXML(atomFeed)
rssFeed.Generator = "gorilla/feeds v1.0 (github.com/gorilla/feeds)"
rss, err := ToXML(rssFeed)
jsonFeed.NextUrl = "https://www.example.com/feed.json?page=2"
json, err := jsonFeed.ToJSON()
*/
package feeds

View File

@ -1,146 +0,0 @@
package feeds
import (
"encoding/json"
"encoding/xml"
"io"
"sort"
"time"
)
type Link struct {
Href, Rel, Type, Length string
}
type Author struct {
Name, Email string
}
type Image struct {
Url, Title, Link string
Width, Height int
}
type Enclosure struct {
Url, Length, Type string
}
type Item struct {
Title string
Link *Link
Source *Link
Author *Author
Description string // used as description in rss, summary in atom
Id string // used as guid in rss, id in atom
IsPermaLink string // an optional parameter for guid in rss
Updated time.Time
Created time.Time
Enclosure *Enclosure
Content string
}
type Feed struct {
Title string
Link *Link
Description string
Author *Author
Updated time.Time
Created time.Time
Id string
Subtitle string
Items []*Item
Copyright string
Image *Image
}
// add a new Item to a Feed
func (f *Feed) Add(item *Item) {
f.Items = append(f.Items, item)
}
// returns the first non-zero time formatted as a string or ""
func anyTimeFormat(format string, times ...time.Time) string {
for _, t := range times {
if !t.IsZero() {
return t.Format(format)
}
}
return ""
}
// interface used by ToXML to get a object suitable for exporting XML.
type XmlFeed interface {
FeedXml() interface{}
}
// turn a feed object (either a Feed, AtomFeed, or RssFeed) into xml
// returns an error if xml marshaling fails
func ToXML(feed XmlFeed) (string, error) {
x := feed.FeedXml()
data, err := xml.MarshalIndent(x, "", " ")
if err != nil {
return "", err
}
// strip empty line from default xml header
s := xml.Header[:len(xml.Header)-1] + string(data)
return s, nil
}
// WriteXML writes a feed object (either a Feed, AtomFeed, or RssFeed) as XML into
// the writer. Returns an error if XML marshaling fails.
func WriteXML(feed XmlFeed, w io.Writer) error {
x := feed.FeedXml()
// write default xml header, without the newline
if _, err := w.Write([]byte(xml.Header[:len(xml.Header)-1])); err != nil {
return err
}
e := xml.NewEncoder(w)
e.Indent("", " ")
return e.Encode(x)
}
// creates an Atom representation of this feed
func (f *Feed) ToAtom() (string, error) {
a := &Atom{f}
return ToXML(a)
}
// WriteAtom writes an Atom representation of this feed to the writer.
func (f *Feed) WriteAtom(w io.Writer) error {
return WriteXML(&Atom{f}, w)
}
// creates an Rss representation of this feed
func (f *Feed) ToRss() (string, error) {
r := &Rss{f}
return ToXML(r)
}
// WriteRss writes an RSS representation of this feed to the writer.
func (f *Feed) WriteRss(w io.Writer) error {
return WriteXML(&Rss{f}, w)
}
// ToJSON creates a JSON Feed representation of this feed
func (f *Feed) ToJSON() (string, error) {
j := &JSON{f}
return j.ToJSON()
}
// WriteJSON writes an JSON representation of this feed to the writer.
func (f *Feed) WriteJSON(w io.Writer) error {
j := &JSON{f}
feed := j.JSONFeed()
e := json.NewEncoder(w)
e.SetIndent("", " ")
return e.Encode(feed)
}
// Sort sorts the Items in the feed with the given less function.
func (f *Feed) Sort(less func(a, b *Item) bool) {
lessFunc := func(i, j int) bool {
return less(f.Items[i], f.Items[j])
}
sort.SliceStable(f.Items, lessFunc)
}

View File

@ -1,190 +0,0 @@
package feeds
import (
"encoding/json"
"strings"
"time"
)
const jsonFeedVersion = "https://jsonfeed.org/version/1.1"
// JSONAuthor represents the author of the feed or of an individual item
// in the feed
type JSONAuthor struct {
Name string `json:"name,omitempty"`
Url string `json:"url,omitempty"`
Avatar string `json:"avatar,omitempty"`
}
// JSONAttachment represents a related resource. Podcasts, for instance, would
// include an attachment thats an audio or video file.
type JSONAttachment struct {
Url string `json:"url,omitempty"`
MIMEType string `json:"mime_type,omitempty"`
Title string `json:"title,omitempty"`
Size int32 `json:"size,omitempty"`
Duration time.Duration `json:"duration_in_seconds,omitempty"`
}
// MarshalJSON implements the json.Marshaler interface.
// The Duration field is marshaled in seconds, all other fields are marshaled
// based upon the definitions in struct tags.
func (a *JSONAttachment) MarshalJSON() ([]byte, error) {
type EmbeddedJSONAttachment JSONAttachment
return json.Marshal(&struct {
Duration float64 `json:"duration_in_seconds,omitempty"`
*EmbeddedJSONAttachment
}{
EmbeddedJSONAttachment: (*EmbeddedJSONAttachment)(a),
Duration: a.Duration.Seconds(),
})
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// The Duration field is expected to be in seconds, all other field types
// match the struct definition.
func (a *JSONAttachment) UnmarshalJSON(data []byte) error {
type EmbeddedJSONAttachment JSONAttachment
var raw struct {
Duration float64 `json:"duration_in_seconds,omitempty"`
*EmbeddedJSONAttachment
}
raw.EmbeddedJSONAttachment = (*EmbeddedJSONAttachment)(a)
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
if raw.Duration > 0 {
nsec := int64(raw.Duration * float64(time.Second))
raw.EmbeddedJSONAttachment.Duration = time.Duration(nsec)
}
return nil
}
// JSONItem represents a single entry/post for the feed.
type JSONItem struct {
Id string `json:"id"`
Url string `json:"url,omitempty"`
ExternalUrl string `json:"external_url,omitempty"`
Title string `json:"title,omitempty"`
ContentHTML string `json:"content_html,omitempty"`
ContentText string `json:"content_text,omitempty"`
Summary string `json:"summary,omitempty"`
Image string `json:"image,omitempty"`
BannerImage string `json:"banner_,omitempty"`
PublishedDate *time.Time `json:"date_published,omitempty"`
ModifiedDate *time.Time `json:"date_modified,omitempty"`
Author *JSONAuthor `json:"author,omitempty"` // deprecated in JSON Feed v1.1, keeping for backwards compatibility
Authors []*JSONAuthor `json:"authors,omitempty"`
Tags []string `json:"tags,omitempty"`
Attachments []JSONAttachment `json:"attachments,omitempty"`
}
// JSONHub describes an endpoint that can be used to subscribe to real-time
// notifications from the publisher of this feed.
type JSONHub struct {
Type string `json:"type"`
Url string `json:"url"`
}
// JSONFeed represents a syndication feed in the JSON Feed Version 1 format.
// Matching the specification found here: https://jsonfeed.org/version/1.
type JSONFeed struct {
Version string `json:"version"`
Title string `json:"title"`
Language string `json:"language,omitempty"`
HomePageUrl string `json:"home_page_url,omitempty"`
FeedUrl string `json:"feed_url,omitempty"`
Description string `json:"description,omitempty"`
UserComment string `json:"user_comment,omitempty"`
NextUrl string `json:"next_url,omitempty"`
Icon string `json:"icon,omitempty"`
Favicon string `json:"favicon,omitempty"`
Author *JSONAuthor `json:"author,omitempty"` // deprecated in JSON Feed v1.1, keeping for backwards compatibility
Authors []*JSONAuthor `json:"authors,omitempty"`
Expired *bool `json:"expired,omitempty"`
Hubs []*JSONHub `json:"hubs,omitempty"`
Items []*JSONItem `json:"items,omitempty"`
}
// JSON is used to convert a generic Feed to a JSONFeed.
type JSON struct {
*Feed
}
// ToJSON encodes f into a JSON string. Returns an error if marshalling fails.
func (f *JSON) ToJSON() (string, error) {
return f.JSONFeed().ToJSON()
}
// ToJSON encodes f into a JSON string. Returns an error if marshalling fails.
func (f *JSONFeed) ToJSON() (string, error) {
data, err := json.MarshalIndent(f, "", " ")
if err != nil {
return "", err
}
return string(data), nil
}
// JSONFeed creates a new JSONFeed with a generic Feed struct's data.
func (f *JSON) JSONFeed() *JSONFeed {
feed := &JSONFeed{
Version: jsonFeedVersion,
Title: f.Title,
Description: f.Description,
}
if f.Link != nil {
feed.HomePageUrl = f.Link.Href
}
if f.Author != nil {
author := &JSONAuthor{
Name: f.Author.Name,
}
feed.Author = author
feed.Authors = []*JSONAuthor{author}
}
for _, e := range f.Items {
feed.Items = append(feed.Items, newJSONItem(e))
}
return feed
}
func newJSONItem(i *Item) *JSONItem {
item := &JSONItem{
Id: i.Id,
Title: i.Title,
Summary: i.Description,
ContentHTML: i.Content,
}
if i.Link != nil {
item.Url = i.Link.Href
}
if i.Source != nil {
item.ExternalUrl = i.Source.Href
}
if i.Author != nil {
author := &JSONAuthor{
Name: i.Author.Name,
}
item.Author = author
item.Authors = []*JSONAuthor{author}
}
if !i.Created.IsZero() {
item.PublishedDate = &i.Created
}
if !i.Updated.IsZero() {
item.ModifiedDate = &i.Updated
}
if i.Enclosure != nil && strings.HasPrefix(i.Enclosure.Type, "image/") {
item.Image = i.Enclosure.Url
}
return item
}

View File

@ -1,183 +0,0 @@
package feeds
// rss support
// validation done according to spec here:
// http://cyber.law.harvard.edu/rss/rss.html
import (
"encoding/xml"
"fmt"
"time"
)
// private wrapper around the RssFeed which gives us the <rss>..</rss> xml
type RssFeedXml struct {
XMLName xml.Name `xml:"rss"`
Version string `xml:"version,attr"`
ContentNamespace string `xml:"xmlns:content,attr"`
Channel *RssFeed
}
type RssContent struct {
XMLName xml.Name `xml:"content:encoded"`
Content string `xml:",cdata"`
}
type RssImage struct {
XMLName xml.Name `xml:"image"`
Url string `xml:"url"`
Title string `xml:"title"`
Link string `xml:"link"`
Width int `xml:"width,omitempty"`
Height int `xml:"height,omitempty"`
}
type RssTextInput struct {
XMLName xml.Name `xml:"textInput"`
Title string `xml:"title"`
Description string `xml:"description"`
Name string `xml:"name"`
Link string `xml:"link"`
}
type RssFeed struct {
XMLName xml.Name `xml:"channel"`
Title string `xml:"title"` // required
Link string `xml:"link"` // required
Description string `xml:"description"` // required
Language string `xml:"language,omitempty"`
Copyright string `xml:"copyright,omitempty"`
ManagingEditor string `xml:"managingEditor,omitempty"` // Author used
WebMaster string `xml:"webMaster,omitempty"`
PubDate string `xml:"pubDate,omitempty"` // created or updated
LastBuildDate string `xml:"lastBuildDate,omitempty"` // updated used
Category string `xml:"category,omitempty"`
Generator string `xml:"generator,omitempty"`
Docs string `xml:"docs,omitempty"`
Cloud string `xml:"cloud,omitempty"`
Ttl int `xml:"ttl,omitempty"`
Rating string `xml:"rating,omitempty"`
SkipHours string `xml:"skipHours,omitempty"`
SkipDays string `xml:"skipDays,omitempty"`
Image *RssImage
TextInput *RssTextInput
Items []*RssItem `xml:"item"`
}
type RssItem struct {
XMLName xml.Name `xml:"item"`
Title string `xml:"title"` // required
Link string `xml:"link"` // required
Description string `xml:"description"` // required
Content *RssContent
Author string `xml:"author,omitempty"`
Category string `xml:"category,omitempty"`
Comments string `xml:"comments,omitempty"`
Enclosure *RssEnclosure
Guid *RssGuid // Id used
PubDate string `xml:"pubDate,omitempty"` // created or updated
Source string `xml:"source,omitempty"`
}
type RssEnclosure struct {
//RSS 2.0 <enclosure url="http://example.com/file.mp3" length="123456789" type="audio/mpeg" />
XMLName xml.Name `xml:"enclosure"`
Url string `xml:"url,attr"`
Length string `xml:"length,attr"`
Type string `xml:"type,attr"`
}
type RssGuid struct {
//RSS 2.0 <guid isPermaLink="true">http://inessential.com/2002/09/01.php#a2</guid>
XMLName xml.Name `xml:"guid"`
Id string `xml:",chardata"`
IsPermaLink string `xml:"isPermaLink,attr,omitempty"` // "true", "false", or an empty string
}
type Rss struct {
*Feed
}
// create a new RssItem with a generic Item struct's data
func newRssItem(i *Item) *RssItem {
item := &RssItem{
Title: i.Title,
Description: i.Description,
PubDate: anyTimeFormat(time.RFC1123Z, i.Created, i.Updated),
}
if i.Id != "" {
item.Guid = &RssGuid{Id: i.Id, IsPermaLink: i.IsPermaLink}
}
if i.Link != nil {
item.Link = i.Link.Href
}
if len(i.Content) > 0 {
item.Content = &RssContent{Content: i.Content}
}
if i.Source != nil {
item.Source = i.Source.Href
}
// Define a closure
if i.Enclosure != nil && i.Enclosure.Type != "" && i.Enclosure.Length != "" {
item.Enclosure = &RssEnclosure{Url: i.Enclosure.Url, Type: i.Enclosure.Type, Length: i.Enclosure.Length}
}
if i.Author != nil {
item.Author = i.Author.Name
}
return item
}
// create a new RssFeed with a generic Feed struct's data
func (r *Rss) RssFeed() *RssFeed {
pub := anyTimeFormat(time.RFC1123Z, r.Created, r.Updated)
build := anyTimeFormat(time.RFC1123Z, r.Updated)
author := ""
if r.Author != nil {
author = r.Author.Email
if len(r.Author.Name) > 0 {
author = fmt.Sprintf("%s (%s)", r.Author.Email, r.Author.Name)
}
}
var image *RssImage
if r.Image != nil {
image = &RssImage{Url: r.Image.Url, Title: r.Image.Title, Link: r.Image.Link, Width: r.Image.Width, Height: r.Image.Height}
}
var href string
if r.Link != nil {
href = r.Link.Href
}
channel := &RssFeed{
Title: r.Title,
Link: href,
Description: r.Description,
ManagingEditor: author,
PubDate: pub,
LastBuildDate: build,
Copyright: r.Copyright,
Image: image,
}
for _, i := range r.Items {
channel.Items = append(channel.Items, newRssItem(i))
}
return channel
}
// FeedXml returns an XML-Ready object for an Rss object
func (r *Rss) FeedXml() interface{} {
// only generate version 2.0 feeds for now
return r.RssFeed().FeedXml()
}
// FeedXml returns an XML-ready object for an RssFeed object
func (r *RssFeed) FeedXml() interface{} {
return &RssFeedXml{
Version: "2.0",
Channel: r,
ContentNamespace: "http://purl.org/rss/1.0/modules/content/",
}
}

View File

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:atom="http://www.w3.org/2005/Atom">
<title><![CDATA[Lorem ipsum feed for an interval of 1 minutes]]></title>
<description><![CDATA[This is a constantly updating lorem ipsum feed]]></description>
<link>http://example.com/</link>
<generator>RSS for Node</generator>
<lastBuildDate>Tue, 30 Oct 2018 23:22:37 GMT</lastBuildDate>
<author><![CDATA[John Smith]]></author>
<pubDate>Tue, 30 Oct 2018 23:22:00 GMT</pubDate>
<copyright><![CDATA[Michael Bertolacci, licensed under a Creative Commons Attribution 3.0 Unported License.]]></copyright>
<ttl>60</ttl>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:22:00+00:00]]></title>
<description><![CDATA[Exercitation ut Lorem sint proident.]]></description>
<link>http://example.com/test/1540941720</link>
<guid isPermaLink="true">http://example.com/test/1540941720</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:22:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:21:00+00:00]]></title>
<description><![CDATA[Ea est do quis fugiat exercitation.]]></description>
<link>http://example.com/test/1540941660</link>
<guid isPermaLink="true">http://example.com/test/1540941660</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:21:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:20:00+00:00]]></title>
<description><![CDATA[Ipsum velit cillum ad laborum sit nulla exercitation consequat sint veniam culpa veniam voluptate incididunt.]]></description>
<link>http://example.com/test/1540941600</link>
<guid isPermaLink="true">http://example.com/test/1540941600</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:20:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:19:00+00:00]]></title>
<description><![CDATA[Ullamco pariatur aliqua consequat ea veniam id qui incididunt laborum.]]></description>
<link>http://example.com/test/1540941540</link>
<guid isPermaLink="true">http://example.com/test/1540941540</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:19:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:18:00+00:00]]></title>
<description><![CDATA[Velit proident aliquip aliquip anim mollit voluptate laboris voluptate et occaecat occaecat laboris ea nulla.]]></description>
<link>http://example.com/test/1540941480</link>
<guid isPermaLink="true">http://example.com/test/1540941480</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:18:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:17:00+00:00]]></title>
<description><![CDATA[Do in quis mollit consequat id in minim laborum sint exercitation laborum elit officia.]]></description>
<link>http://example.com/test/1540941420</link>
<guid isPermaLink="true">http://example.com/test/1540941420</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:17:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:16:00+00:00]]></title>
<description><![CDATA[Irure id sint ullamco Lorem magna consectetur officia adipisicing duis incididunt.]]></description>
<link>http://example.com/test/1540941360</link>
<guid isPermaLink="true">http://example.com/test/1540941360</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:16:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:15:00+00:00]]></title>
<description><![CDATA[Sunt anim excepteur esse nisi commodo culpa laborum exercitation ad anim ex elit.]]></description>
<link>http://example.com/test/1540941300</link>
<guid isPermaLink="true">http://example.com/test/1540941300</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:15:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:14:00+00:00]]></title>
<description><![CDATA[Excepteur aliquip fugiat ex labore nisi.]]></description>
<link>http://example.com/test/1540941240</link>
<guid isPermaLink="true">http://example.com/test/1540941240</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:14:00 GMT</pubDate>
</entry>
<entry>
<title><![CDATA[Lorem ipsum 2018-10-30T23:13:00+00:00]]></title>
<description><![CDATA[Id proident adipisicing proident pariatur aute pariatur pariatur dolor dolor in voluptate dolor.]]></description>
<link>http://example.com/test/1540941180</link>
<guid isPermaLink="true">http://example.com/test/1540941180</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:13:00 GMT</pubDate>
</entry>
</feed>

View File

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title><![CDATA[Lorem ipsum feed for an interval of 1 minutes]]></title>
<description><![CDATA[This is a constantly updating lorem ipsum feed]]></description>
<link>http://example.com/</link>
<generator>RSS for Node</generator>
<lastBuildDate>Tue, 30 Oct 2018 23:22:37 GMT</lastBuildDate>
<author><![CDATA[John Smith]]></author>
<pubDate>Tue, 30 Oct 2018 23:22:00 GMT</pubDate>
<copyright><![CDATA[Michael Bertolacci, licensed under a Creative Commons Attribution 3.0 Unported License.]]></copyright>
<ttl>60</ttl>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:22:00+00:00]]></title>
<description><![CDATA[Exercitation ut Lorem sint proident.]]></description>
<link>http://example.com/test/1540941720</link>
<guid isPermaLink="true">http://example.com/test/1540941720</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:22:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:21:00+00:00]]></title>
<description><![CDATA[Ea est do quis fugiat exercitation.]]></description>
<link>http://example.com/test/1540941660</link>
<guid isPermaLink="true">http://example.com/test/1540941660</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:21:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:20:00+00:00]]></title>
<description><![CDATA[Ipsum velit cillum ad laborum sit nulla exercitation consequat sint veniam culpa veniam voluptate incididunt.]]></description>
<link>http://example.com/test/1540941600</link>
<guid isPermaLink="true">http://example.com/test/1540941600</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:20:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:19:00+00:00]]></title>
<description><![CDATA[Ullamco pariatur aliqua consequat ea veniam id qui incididunt laborum.]]></description>
<link>http://example.com/test/1540941540</link>
<guid isPermaLink="true">http://example.com/test/1540941540</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:19:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:18:00+00:00]]></title>
<description><![CDATA[Velit proident aliquip aliquip anim mollit voluptate laboris voluptate et occaecat occaecat laboris ea nulla.]]></description>
<link>http://example.com/test/1540941480</link>
<guid isPermaLink="true">http://example.com/test/1540941480</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:18:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:17:00+00:00]]></title>
<description><![CDATA[Do in quis mollit consequat id in minim laborum sint exercitation laborum elit officia.]]></description>
<link>http://example.com/test/1540941420</link>
<guid isPermaLink="true">http://example.com/test/1540941420</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:17:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:16:00+00:00]]></title>
<description><![CDATA[Irure id sint ullamco Lorem magna consectetur officia adipisicing duis incididunt.]]></description>
<link>http://example.com/test/1540941360</link>
<guid isPermaLink="true">http://example.com/test/1540941360</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:16:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:15:00+00:00]]></title>
<description><![CDATA[Sunt anim excepteur esse nisi commodo culpa laborum exercitation ad anim ex elit.]]></description>
<link>http://example.com/test/1540941300</link>
<guid isPermaLink="true">http://example.com/test/1540941300</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:15:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:14:00+00:00]]></title>
<description><![CDATA[Excepteur aliquip fugiat ex labore nisi.]]></description>
<link>http://example.com/test/1540941240</link>
<guid isPermaLink="true">http://example.com/test/1540941240</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:14:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[Lorem ipsum 2018-10-30T23:13:00+00:00]]></title>
<description><![CDATA[Id proident adipisicing proident pariatur aute pariatur pariatur dolor dolor in voluptate dolor.]]></description>
<link>http://example.com/test/1540941180</link>
<guid isPermaLink="true">http://example.com/test/1540941180</guid>
<dc:creator><![CDATA[John Smith]]></dc:creator>
<pubDate>Tue, 30 Oct 2018 23:13:00 GMT</pubDate>
</item>
</channel>
</rss>

View File

@ -1,20 +0,0 @@
[Full iTunes list](https://help.apple.com/itc/podcasts_connect/#/itcb54353390)
[Example of ideal iTunes RSS feed](https://help.apple.com/itc/podcasts_connect/#/itcbaf351599)
```
<itunes:author>
<itunes:block>
<itunes:catergory>
<itunes:image>
<itunes:duration>
<itunes:explicit>
<itunes:isClosedCaptioned>
<itunes:order>
<itunes:complete>
<itunes:new-feed-url>
<itunes:owner>
<itunes:subtitle>
<itunes:summary>
<language>
```

View File

@ -1,27 +0,0 @@
package feeds
// relevant bits from https://github.com/abneptis/GoUUID/blob/master/uuid.go
import (
"crypto/rand"
"fmt"
)
type UUID [16]byte
// create a new uuid v4
func NewUUID() *UUID {
u := &UUID{}
_, err := rand.Read(u[:16])
if err != nil {
panic(err)
}
u[8] = (u[8] | 0x80) & 0xBf
u[6] = (u[6] | 0x40) & 0x4f
return u
}
func (u *UUID) String() string {
return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:])
}

24
vendor/github.com/pkg/errors/.gitignore generated vendored Normal file
View File

@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

10
vendor/github.com/pkg/errors/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,10 @@
language: go
go_import_path: github.com/pkg/errors
go:
- 1.11.x
- 1.12.x
- 1.13.x
- tip
script:
- make check

23
vendor/github.com/pkg/errors/LICENSE generated vendored Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

44
vendor/github.com/pkg/errors/Makefile generated vendored Normal file
View File

@ -0,0 +1,44 @@
PKGS := github.com/pkg/errors
SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS))
GO := go
check: test vet gofmt misspell unconvert staticcheck ineffassign unparam
test:
$(GO) test $(PKGS)
vet: | test
$(GO) vet $(PKGS)
staticcheck:
$(GO) get honnef.co/go/tools/cmd/staticcheck
staticcheck -checks all $(PKGS)
misspell:
$(GO) get github.com/client9/misspell/cmd/misspell
misspell \
-locale GB \
-error \
*.md *.go
unconvert:
$(GO) get github.com/mdempsky/unconvert
unconvert -v $(PKGS)
ineffassign:
$(GO) get github.com/gordonklaus/ineffassign
find $(SRCDIRS) -name '*.go' | xargs ineffassign
pedantic: check errcheck
unparam:
$(GO) get mvdan.cc/unparam
unparam ./...
errcheck:
$(GO) get github.com/kisielk/errcheck
errcheck $(PKGS)
gofmt:
@echo Checking code is gofmted
@test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)"

59
vendor/github.com/pkg/errors/README.md generated vendored Normal file
View File

@ -0,0 +1,59 @@
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
Package errors provides simple error handling primitives.
`go get github.com/pkg/errors`
The traditional error handling idiom in Go is roughly akin to
```go
if err != nil {
return err
}
```
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
## Adding context to an error
The errors.Wrap function returns a new error that adds context to the original error. For example
```go
_, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "read failed")
}
```
## Retrieving the cause of an error
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
```go
type causer interface {
Cause() error
}
```
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
```go
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}
```
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
## Roadmap
With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
- 1.0. Final release.
## Contributing
Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
Before sending a PR, please discuss your change by raising an issue.
## License
BSD-2-Clause

32
vendor/github.com/pkg/errors/appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,32 @@
version: build-{build}.{branch}
clone_folder: C:\gopath\src\github.com\pkg\errors
shallow_clone: true # for startup speed
environment:
GOPATH: C:\gopath
platform:
- x64
# http://www.appveyor.com/docs/installed-software
install:
# some helpful output for debugging builds
- go version
- go env
# pre-installed MinGW at C:\MinGW is 32bit only
# but MSYS2 at C:\msys64 has mingw64
- set PATH=C:\msys64\mingw64\bin;%PATH%
- gcc --version
- g++ --version
build_script:
- go install -v ./...
test_script:
- set PATH=C:\gopath\bin;%PATH%
- go test -v ./...
#artifacts:
# - path: '%GOPATH%\bin\*.exe'
deploy: off

288
vendor/github.com/pkg/errors/errors.go generated vendored Normal file
View File

@ -0,0 +1,288 @@
// Package errors provides simple error handling primitives.
//
// The traditional error handling idiom in Go is roughly akin to
//
// if err != nil {
// return err
// }
//
// which when applied recursively up the call stack results in error reports
// without context or debugging information. The errors package allows
// programmers to add context to the failure path in their code in a way
// that does not destroy the original value of the error.
//
// Adding context to an error
//
// The errors.Wrap function returns a new error that adds context to the
// original error by recording a stack trace at the point Wrap is called,
// together with the supplied message. For example
//
// _, err := ioutil.ReadAll(r)
// if err != nil {
// return errors.Wrap(err, "read failed")
// }
//
// If additional control is required, the errors.WithStack and
// errors.WithMessage functions destructure errors.Wrap into its component
// operations: annotating an error with a stack trace and with a message,
// respectively.
//
// Retrieving the cause of an error
//
// Using errors.Wrap constructs a stack of errors, adding context to the
// preceding error. Depending on the nature of the error it may be necessary
// to reverse the operation of errors.Wrap to retrieve the original error
// for inspection. Any error value which implements this interface
//
// type causer interface {
// Cause() error
// }
//
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
// the topmost error that does not implement causer, which is assumed to be
// the original cause. For example:
//
// switch err := errors.Cause(err).(type) {
// case *MyError:
// // handle specifically
// default:
// // unknown error
// }
//
// Although the causer interface is not exported by this package, it is
// considered a part of its stable public interface.
//
// Formatted printing of errors
//
// All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported:
//
// %s print the error. If the error has a Cause it will be
// printed recursively.
// %v see %s
// %+v extended format. Each Frame of the error's StackTrace will
// be printed in detail.
//
// Retrieving the stack trace of an error or wrapper
//
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface:
//
// type stackTracer interface {
// StackTrace() errors.StackTrace
// }
//
// The returned errors.StackTrace type is defined as
//
// type StackTrace []Frame
//
// The Frame type represents a call site in the stack trace. Frame supports
// the fmt.Formatter interface that can be used for printing information about
// the stack trace of this error. For example:
//
// if err, ok := err.(stackTracer); ok {
// for _, f := range err.StackTrace() {
// fmt.Printf("%+s:%d\n", f, f)
// }
// }
//
// Although the stackTracer interface is not exported by this package, it is
// considered a part of its stable public interface.
//
// See the documentation for Frame.Format for more details.
package errors
import (
"fmt"
"io"
)
// New returns an error with the supplied message.
// New also records the stack trace at the point it was called.
func New(message string) error {
return &fundamental{
msg: message,
stack: callers(),
}
}
// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
// Errorf also records the stack trace at the point it was called.
func Errorf(format string, args ...interface{}) error {
return &fundamental{
msg: fmt.Sprintf(format, args...),
stack: callers(),
}
}
// fundamental is an error that has a message and a stack, but no caller.
type fundamental struct {
msg string
*stack
}
func (f *fundamental) Error() string { return f.msg }
func (f *fundamental) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
io.WriteString(s, f.msg)
f.stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, f.msg)
case 'q':
fmt.Fprintf(s, "%q", f.msg)
}
}
// WithStack annotates err with a stack trace at the point WithStack was called.
// If err is nil, WithStack returns nil.
func WithStack(err error) error {
if err == nil {
return nil
}
return &withStack{
err,
callers(),
}
}
type withStack struct {
error
*stack
}
func (w *withStack) Cause() error { return w.error }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withStack) Unwrap() error { return w.error }
func (w *withStack) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v", w.Cause())
w.stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, w.Error())
case 'q':
fmt.Fprintf(s, "%q", w.Error())
}
}
// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: message,
}
return &withStack{
err,
callers(),
}
}
// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is called, and the format specifier.
// If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
return &withStack{
err,
callers(),
}
}
// WithMessage annotates err with a new message.
// If err is nil, WithMessage returns nil.
func WithMessage(err error, message string) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: message,
}
}
// WithMessagef annotates err with the format specifier.
// If err is nil, WithMessagef returns nil.
func WithMessagef(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
}
type withMessage struct {
cause error
msg string
}
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }
// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withMessage) Unwrap() error { return w.cause }
func (w *withMessage) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v\n", w.Cause())
io.WriteString(s, w.msg)
return
}
fallthrough
case 's', 'q':
io.WriteString(s, w.Error())
}
}
// Cause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func Cause(err error) error {
type causer interface {
Cause() error
}
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return err
}

38
vendor/github.com/pkg/errors/go113.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
// +build go1.13
package errors
import (
stderrors "errors"
)
// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
func Is(err, target error) bool { return stderrors.Is(err, target) }
// As finds the first error in err's chain that matches target, and if so, sets
// target to that error value and returns true.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
//
// As will panic if target is not a non-nil pointer to either a type that implements
// error, or to any interface type. As returns false if err is nil.
func As(err error, target interface{}) bool { return stderrors.As(err, target) }
// Unwrap returns the result of calling the Unwrap method on err, if err's
// type contains an Unwrap method returning error.
// Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
return stderrors.Unwrap(err)
}

177
vendor/github.com/pkg/errors/stack.go generated vendored Normal file
View File

@ -0,0 +1,177 @@
package errors
import (
"fmt"
"io"
"path"
"runtime"
"strconv"
"strings"
)
// Frame represents a program counter inside a stack frame.
// For historical reasons if Frame is interpreted as a uintptr
// its value represents the program counter + 1.
type Frame uintptr
// pc returns the program counter for this frame;
// multiple frames may have the same PC value.
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
// file returns the full path to the file that contains the
// function for this Frame's pc.
func (f Frame) file() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
file, _ := fn.FileLine(f.pc())
return file
}
// line returns the line number of source code of the
// function for this Frame's pc.
func (f Frame) line() int {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return 0
}
_, line := fn.FileLine(f.pc())
return line
}
// name returns the name of this function, if known.
func (f Frame) name() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
return fn.Name()
}
// Format formats the frame according to the fmt.Formatter interface.
//
// %s source file
// %d source line
// %n function name
// %v equivalent to %s:%d
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+s function name and path of source file relative to the compile time
// GOPATH separated by \n\t (<funcname>\n\t<path>)
// %+v equivalent to %+s:%d
func (f Frame) Format(s fmt.State, verb rune) {
switch verb {
case 's':
switch {
case s.Flag('+'):
io.WriteString(s, f.name())
io.WriteString(s, "\n\t")
io.WriteString(s, f.file())
default:
io.WriteString(s, path.Base(f.file()))
}
case 'd':
io.WriteString(s, strconv.Itoa(f.line()))
case 'n':
io.WriteString(s, funcname(f.name()))
case 'v':
f.Format(s, 's')
io.WriteString(s, ":")
f.Format(s, 'd')
}
}
// MarshalText formats a stacktrace Frame as a text string. The output is the
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
func (f Frame) MarshalText() ([]byte, error) {
name := f.name()
if name == "unknown" {
return []byte(name), nil
}
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
}
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame
// Format formats the stack of Frames according to the fmt.Formatter interface.
//
// %s lists source files for each Frame in the stack
// %v lists the source file and line number for each Frame in the stack
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+v Prints filename, function, and line number for each Frame in the stack.
func (st StackTrace) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case s.Flag('+'):
for _, f := range st {
io.WriteString(s, "\n")
f.Format(s, verb)
}
case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st))
default:
st.formatSlice(s, verb)
}
case 's':
st.formatSlice(s, verb)
}
}
// formatSlice will format this StackTrace into the given buffer as a slice of
// Frame, only valid when called with '%s' or '%v'.
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
io.WriteString(s, "[")
for i, f := range st {
if i > 0 {
io.WriteString(s, " ")
}
f.Format(s, verb)
}
io.WriteString(s, "]")
}
// stack represents a stack of program counters.
type stack []uintptr
func (s *stack) Format(st fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case st.Flag('+'):
for _, pc := range *s {
f := Frame(pc)
fmt.Fprintf(st, "\n%+v", f)
}
}
}
}
func (s *stack) StackTrace() StackTrace {
f := make([]Frame, len(*s))
for i := 0; i < len(f); i++ {
f[i] = Frame((*s)[i])
}
return f
}
func callers() *stack {
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(3, pcs[:])
var st stack = pcs[0:n]
return &st
}
// funcname removes the path prefix component of a function's name reported by func.Name().
func funcname(name string) string {
i := strings.LastIndex(name, "/")
name = name[i+1:]
i = strings.Index(name, ".")
return name[i+1:]
}

10
vendor/modules.txt vendored
View File

@ -1,9 +1,9 @@
# github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 # github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
## explicit; go 1.20 ## explicit; go 1.20
github.com/dhowden/tag github.com/dhowden/tag
# github.com/google/uuid v1.6.0 # github.com/eduncan911/podcast v1.4.2
## explicit; go 1.13
github.com/eduncan911/podcast
# github.com/pkg/errors v0.9.1
## explicit ## explicit
github.com/google/uuid github.com/pkg/errors
# github.com/gorilla/feeds v1.2.0
## explicit; go 1.20
github.com/gorilla/feeds