commit a9f96a604b510116b1178805feac471ee101a3d8 from: Oliver Lowe date: Sun Apr 13 05:34:58 2025 UTC import atom package Atom is easier feed format to parse as it's stricter than RSS 2.0. Both are common in the wild but I thought Atom would be a good start. And of course this a better place for the package rather than my random Go package repo. commit - 3f7e247f5e371934a15857d90f183e254bec9d2d commit + a9f96a604b510116b1178805feac471ee101a3d8 blob - /dev/null blob + d986c6ded9e2e83b290615711c1b70716666633d (mode 644) --- /dev/null +++ atom/atom.go @@ -0,0 +1,62 @@ +// Package atom implements decoding encoding of Atom feeds as +// specified in RFC 4287. +package atom + +import ( + "encoding/xml" + "time" +) + +const xmlns = "http://www.w3.org/2005/Atom" + +// MediaType is Atom's IANA media type. +const MediaType = "application/atom+xml" + +type Feed struct { + ID string `xml:"id"` + Title string `xml:"title"` + Updated time.Time `xml:"updated"` + Author *Author `xml:"author,omitempty"` + Link []Link `xml:"link,omitempty"` + Subtitle string `xml:"subtitle,omitempty"` + Entries []Entry `xml:"entry"` +} + +type feed struct { + XMLName struct{} `xml:"feed"` + Namespace string `xml:"xmlns,attr"` + *Feed +} + +type Author struct { + Name string `xml:"name"` + URI string `xml:"uri,omitempty"` + Email string `xml:"email,omitempty"` +} + +type Entry struct { + ID string `xml:"id"` + Title string `xml:"title"` + Updated time.Time `xml:"updated,omitempty"` + Author *Author `xml:"author,omitempty"` + Links []Link `xml:"link"` + Summary string `xml:"summary,omitempty"` + Content []byte `xml:"content,omitempty"` + Published *time.Time `xml:"published,omitempty"` +} + +type Link struct { + HRef string `xml:"href,attr,omitempty"` + Rel string `xml:"rel,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` +} + +func Marshal(f *Feed) ([]byte, error) { + f1 := &feed{ + Namespace: xmlns, + Feed: f, + } + return xml.MarshalIndent(f1, "", "\t") + // b = bytes.ReplaceAll(b, []byte(">"), []byte("/>")) + // return b, err +} blob - /dev/null blob + 5e54cfd439080b97125d2e85958079a61f1726f9 (mode 644) --- /dev/null +++ atom/atom_test.go @@ -0,0 +1,52 @@ +package atom + +import ( + "bytes" + "encoding/xml" + "fmt" + "os" + "testing" + "time" +) + +func TestMarshal(t *testing.T) { + f := &Feed{ + Title: "Example Feed", + Link: []Link{ + {HRef: "http://example.org/"}, + }, + Updated: time.Date(2003, time.Month(12), 13, 18, 30, 2, 0, time.UTC), + Author: &Author{Name: "John Doe"}, + ID: "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6", + Entries: []Entry{ + { + Title: "Atom-Powered Robots Run Amok", + ID: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a", + Links: []Link{ + {HRef: "http://example.org/2003/12/13/atom03"}, + }, + Updated: time.Date(2003, time.Month(12), 13, 18, 30, 2, 0, time.UTC), + Summary: "Some text.", + }, + }, + } + feed1 := &feed{ + Namespace: xmlns, + Feed: f, + } + got, err := xml.MarshalIndent(feed1, "", " ") + if err != nil { + t.Fatal(err) + } + got = bytes.ReplaceAll(got, []byte(">"), []byte("/>")) + + want, err := os.ReadFile("testdata/1.xml") + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(got, want) { + t.Errorf("oops") + fmt.Fprintln(os.Stderr, string(got)) + } +} blob - /dev/null blob + 2fd7e73c373369d952ba24c0458bd53a26e6f2e7 (mode 644) --- /dev/null +++ atom/testdata/1.xml @@ -0,0 +1,16 @@ + + urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 + Example Feed + 2003-12-13T18:30:02Z + + John Doe + + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + Atom-Powered Robots Run Amok + 2003-12-13T18:30:02Z + + Some text. + + \ No newline at end of file blob - /dev/null blob + 328e2caaa79eadf4fc62b1d93671afd90599d59a (mode 644) --- /dev/null +++ atom/testdata/2.xml @@ -0,0 +1,45 @@ + + + dive into mark + + A <em>lot</em> of effort + went into making this effortless + + 2005-07-31T12:29:29Z + tag:example.org,2003:3 + + + Copyright (c) 2003, Mark Pilgrim + + Example Toolkit + + + Atom draft-07 snapshot + + + tag:example.org,2003:3.2397 + 2005-07-31T12:29:29Z + 2003-12-13T08:29:29-04:00 + + Mark Pilgrim + http://example.org/ + f8dy@example.com + + + Sam Ruby + + + Joe Gregorio + + +
+

[Update: The Atom draft is finished.]

+
+
+
+