Commit Diff


commit - 3f7e247f5e371934a15857d90f183e254bec9d2d
commit + 33d9e23c99af8e8c059dd587cf9ee8d61734d300
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("></link>"), []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("></link>"), []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 @@
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
+  <title>Example Feed</title>
+  <updated>2003-12-13T18:30:02Z</updated>
+  <author>
+    <name>John Doe</name>
+  </author>
+  <link href="http://example.org/"/>
+  <entry>
+    <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
+    <title>Atom-Powered Robots Run Amok</title>
+    <updated>2003-12-13T18:30:02Z</updated>
+    <link href="http://example.org/2003/12/13/atom03"/>
+    <summary>Some text.</summary>
+  </entry>
+</feed>
\ No newline at end of file
blob - /dev/null
blob + 328e2caaa79eadf4fc62b1d93671afd90599d59a (mode 644)
--- /dev/null
+++ atom/testdata/2.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <title type="text">dive into mark</title>
+  <subtitle type="html">
+    A &lt;em&gt;lot&lt;/em&gt; of effort
+    went into making this effortless
+  </subtitle>
+  <updated>2005-07-31T12:29:29Z</updated>
+  <id>tag:example.org,2003:3</id>
+  <link rel="alternate" type="text/html"
+   hreflang="en" href="http://example.org/"/>
+  <link rel="self" type="application/atom+xml"
+   href="http://example.org/feed.atom"/>
+  <rights>Copyright (c) 2003, Mark Pilgrim</rights>
+  <generator uri="http://www.example.com/" version="1.0">
+    Example Toolkit
+  </generator>
+  <entry>
+    <title>Atom draft-07 snapshot</title>
+    <link rel="alternate" type="text/html"
+     href="http://example.org/2005/04/02/atom"/>
+    <link rel="enclosure" type="audio/mpeg" length="1337"
+     href="http://example.org/audio/ph34r_my_podcast.mp3"/>
+    <id>tag:example.org,2003:3.2397</id>
+    <updated>2005-07-31T12:29:29Z</updated>
+    <published>2003-12-13T08:29:29-04:00</published>
+    <author>
+      <name>Mark Pilgrim</name>
+      <uri>http://example.org/</uri>
+      <email>f8dy@example.com</email>
+    </author>
+    <contributor>
+      <name>Sam Ruby</name>
+    </contributor>
+    <contributor>
+      <name>Joe Gregorio</name>
+    </contributor>
+    <content type="xhtml" xml:lang="en"
+     xml:base="http://diveintomark.org/">
+      <div xmlns="http://www.w3.org/1999/xhtml">
+        <p><i>[Update: The Atom draft is finished.]</i></p>
+      </div>
+    </content>
+  </entry>
+</feed>