Author: Honza Pokorny <honza@pokorny.ca>
Add Atom feed
cmd/build.go | 11 ++-- cmd/cache.go | 11 ++-- cmd/root.go | 2 go.mod | 1 go.sum | 4 + pkg/bookends/bookends.go | 97 +++++++++++++++++++++++++++++++++++++----
diff --git a/cmd/build.go b/cmd/build.go index b58970656b732d438175c8d9918eb76003d5b64f..931d6c46199b95814c40a728c809990ba5ed64a3 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -29,11 +29,12 @@ Use: "build", Short: "", Run: func(cmd *cobra.Command, args []string) { config := bookends.Config{ - BookLogFilename: bookLogFilename, - CacheDir: cacheDir, - CoversDir: coversDir, - OutputFilename: outputFilename, - TemplateFilename: templateFilename, + BookLogFilename: bookLogFilename, + CacheDir: cacheDir, + CoversDir: coversDir, + OutputFilename: outputFilename, + AtomOutputFilename: atomOutputFilename, + TemplateFilename: templateFilename, } err := bookends.Build(config) if err != nil { diff --git a/cmd/cache.go b/cmd/cache.go index 86e63b3628cd0b82e982aa6f4eb2fa464c49d3a2..c7bc4a26879749879bbf0d0177034376dd38af80 100644 --- a/cmd/cache.go +++ b/cmd/cache.go @@ -29,11 +29,12 @@ Use: "cache", Short: "", Run: func(cmd *cobra.Command, args []string) { config := bookends.Config{ - BookLogFilename: bookLogFilename, - CacheDir: cacheDir, - CoversDir: coversDir, - OutputFilename: outputFilename, - TemplateFilename: templateFilename, + BookLogFilename: bookLogFilename, + CacheDir: cacheDir, + CoversDir: coversDir, + OutputFilename: outputFilename, + AtomOutputFilename: atomOutputFilename, + TemplateFilename: templateFilename, } err := bookends.CacheCovers(config) if err != nil { diff --git a/cmd/root.go b/cmd/root.go index 0934acc2151c550d43a2bf2434b19464983fb487..b95fd2b90191eca6a5a6afe09cb98c030951fae5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,6 +27,7 @@ var bookLogFilename string var cacheDir string var coversDir string var outputFilename string +var atomOutputFilename string var templateFilename string var rootCmd = &cobra.Command{ @@ -50,6 +51,7 @@ rootCmd.PersistentFlags().StringVar(&bookLogFilename, "book-log-filename", "book-log.org", "") rootCmd.PersistentFlags().StringVar(&cacheDir, "cache-dir", "cache", "") rootCmd.PersistentFlags().StringVar(&coversDir, "covers-dir", "covers", "") rootCmd.PersistentFlags().StringVar(&outputFilename, "output-filename", "output.html", "") + rootCmd.PersistentFlags().StringVar(&atomOutputFilename, "atom-output-filename", "atom.xml", "") rootCmd.PersistentFlags().StringVar(&templateFilename, "template-filename", "template.html", "") rootCmd.AddCommand(buildCmd) diff --git a/go.mod b/go.mod index d7bc339e491f484f85a36695cef09e24e02479ce..6ad56605d6708de5cb461b6e4df71dbf75c8e29c 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ go 1.16 require ( + github.com/gorilla/feeds v1.1.1 github.com/niklasfasching/go-org v1.5.0 github.com/spf13/cobra v1.1.3 ) diff --git a/go.sum b/go.sum index 1b548be0cd60a2de297fb748166b6e2568d0c3f1..aca65712bcbfc7159dba5ae23f7b871d5446b234 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY= +github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -106,8 +108,10 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= diff --git a/pkg/bookends/bookends.go b/pkg/bookends/bookends.go index bafec02a594a9aa639c07bce578074f1b184f497..cba1d3f05c7b4e2c3c3d8c68418a857d2b545933 100644 --- a/pkg/bookends/bookends.go +++ b/pkg/bookends/bookends.go @@ -31,15 +31,25 @@ "strconv" "strings" "time" + "github.com/gorilla/feeds" "github.com/niklasfasching/go-org/org" ) type Config struct { - BookLogFilename string - CacheDir string - CoversDir string - OutputFilename string - TemplateFilename string + BookLogFilename string + CacheDir string + CoversDir string + OutputFilename string + AtomOutputFilename string + TemplateFilename string +} + +type AtomConfig struct { + Name string + Author string + Email string + Link string + Description string } type Book struct { @@ -140,6 +150,13 @@ } return nil } +func (b Book) AtomContent() string { + if b.Review == "" { + return fmt.Sprintf("%s by %s, rated with %d stars", b.Title, b.Author, b.Rating) + } + return fmt.Sprintf("%s by %s, rated with %d stars\n\n%s", b.Title, b.Author, b.Rating, string(b.Review)) +} + func BuildHTML(config Config, books []Book) (string, error) { t, err := template.ParseFiles(config.TemplateFilename) @@ -176,6 +193,35 @@ return nil } +func BuildAtom(books []Book, config AtomConfig) (string, error) { + now := time.Now() + author := &feeds.Author{Name: config.Author, Email: config.Email} + feed := &feeds.Feed{ + Title: config.Name, + Link: &feeds.Link{Href: config.Link}, + Description: config.Description, + Author: author, + Created: now, + } + + var items []*feeds.Item + + for _, book := range books { + item := &feeds.Item{ + Title: book.Title, + Link: &feeds.Link{}, + Content: book.AtomContent(), + Author: author, + Created: book.DateRead, + } + + items = append(items, item) + } + + feed.Items = items + return feed.ToAtom() +} + func Build(config Config) error { f, err := os.Open(config.BookLogFilename) @@ -183,7 +229,7 @@ if err != nil { return err } - books, err := ParseOrgFile(f) + books, atomConfig, err := ParseOrgFile(f) if err != nil { return err @@ -203,13 +249,42 @@ if err != nil { return err } + atom, err := BuildAtom(books, atomConfig) + + if err != nil { + return err + } + + err = WriteFile(config.AtomOutputFilename, atom) + + if err != nil { + return err + } + return nil } -func ParseOrgFile(input io.Reader) ([]Book, error) { +func ParseAtomConfig(drawer org.PropertyDrawer) AtomConfig { + name, _ := drawer.Get("ATOM_NAME") + author, _ := drawer.Get("ATOM_AUTHOR") + description, _ := drawer.Get("ATOM_DESCRIPTION") + link, _ := drawer.Get("ATOM_LINK") + + return AtomConfig{ + Name: name, + Author: author, + Description: description, + Link: link, + } +} + +func ParseOrgFile(input io.Reader) ([]Book, AtomConfig, error) { books := []Book{} conf := org.New() d := conf.Parse(input, "") + + documentProperties := d.Nodes[0].(org.PropertyDrawer) + atomConfig := ParseAtomConfig(documentProperties) for _, child := range d.Outline.Children { for _, sub := range child.Children { @@ -238,7 +313,7 @@ rating, ok := t.Get("RATING") if ok { ratingInt, err := strconv.ParseInt(rating, 10, 8) if err != nil { - return books, err + return books, atomConfig, err } book.Rating = int(ratingInt) } @@ -255,7 +330,7 @@ if strings.HasPrefix(s, "CLOSED") { s = strings.TrimSpace(s) dt, err := time.Parse("CLOSED: [2006-01-02 Mon 15:04]", s) if err != nil { - return books, err + return books, atomConfig, err } book.DateRead = dt continue @@ -332,7 +407,7 @@ } } - return books, nil + return books, atomConfig, nil } @@ -368,7 +443,7 @@ if err != nil { return err } - books, err := ParseOrgFile(f) + books, _, err := ParseOrgFile(f) if err != nil { return err