From 5a42df65e882028d34bc335a46679fab4586040d Mon Sep 17 00:00:00 2001 From: Sinuhe Tellez Date: Thu, 11 May 2017 19:06:21 -0400 Subject: [PATCH] Using the a builder --- author_builder.go | 30 ++++++ entry_builder.go | 47 +++++++++ feed_builder.go | 39 +++++++ link_builder.go | 38 +++++++ main.go | 257 ++++++++++++++++++++-------------------------- text_builder.go | 22 ++++ 6 files changed, 286 insertions(+), 147 deletions(-) create mode 100644 author_builder.go create mode 100644 entry_builder.go create mode 100644 feed_builder.go create mode 100644 link_builder.go create mode 100644 text_builder.go diff --git a/author_builder.go b/author_builder.go new file mode 100644 index 0000000..b8a476e --- /dev/null +++ b/author_builder.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/lann/builder" + "golang.org/x/tools/blog/atom" +) + +type authorBuilder builder.Builder + +func (a authorBuilder) Name(name string) authorBuilder { + return builder.Set(a, "Name", name).(authorBuilder) +} + +func (a authorBuilder) URI(uri string) authorBuilder { + return builder.Set(a, "URI", uri).(authorBuilder) +} + +func (a authorBuilder) Email(email string) authorBuilder { + return builder.Set(a, "Email", email).(authorBuilder) +} + +func (a authorBuilder) InnerXml(inner string) authorBuilder { + return builder.Set(a, "InnerXml", inner).(authorBuilder) +} + +func (a authorBuilder) Build() atom.Person { + return builder.GetStruct(a).(atom.Person) +} + +var AuthorBuilder = builder.Register(authorBuilder{}, atom.Person{}).(authorBuilder) diff --git a/entry_builder.go b/entry_builder.go new file mode 100644 index 0000000..3363f6f --- /dev/null +++ b/entry_builder.go @@ -0,0 +1,47 @@ +package main + +import ( + "github.com/lann/builder" + "golang.org/x/tools/blog/atom" + "time" +) + +type entryBuilder builder.Builder + +func (e entryBuilder) Title(title string) entryBuilder { + return builder.Set(e, "Title", title).(entryBuilder) +} + +func (e entryBuilder) Id(id string) entryBuilder { + return builder.Set(e, "ID", id).(entryBuilder) +} + +func (e entryBuilder) AddLink(link atom.Link) entryBuilder { + return builder.Append(e, "Link", link).(entryBuilder) +} + +func (e entryBuilder) Published(published time.Time) entryBuilder { + return builder.Set(e, "Published", atom.Time(published)).(entryBuilder) +} + +func (e entryBuilder) Updated(updated time.Time) entryBuilder { + return builder.Set(e, "Updated", atom.Time(updated)).(entryBuilder) +} + +func (e entryBuilder) Author(author *atom.Person) entryBuilder { + return builder.Set(e, "Author", author).(entryBuilder) +} + +func (e entryBuilder) Summary(summary *atom.Text) entryBuilder { + return builder.Set(e, "Summary", summary).(entryBuilder) +} + +func (e entryBuilder) Content(content *atom.Text) entryBuilder { + return builder.Set(e, "Content", content).(entryBuilder) +} + +func (e entryBuilder) Build() atom.Entry { + return builder.GetStruct(e).(atom.Entry) +} + +var EntryBuilder = builder.Register(entryBuilder{}, atom.Entry{}).(entryBuilder) diff --git a/feed_builder.go b/feed_builder.go new file mode 100644 index 0000000..d161b8a --- /dev/null +++ b/feed_builder.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/lann/builder" + "golang.org/x/tools/blog/atom" + "time" +) + +type feedBuilder builder.Builder + +func (f feedBuilder) Title(title string) feedBuilder { + return builder.Set(f, "Title", title).(feedBuilder) +} + +func (f feedBuilder) Id(id string) feedBuilder { + return builder.Set(f, "ID", id).(feedBuilder) +} + +func (f feedBuilder) AddLink(link atom.Link) feedBuilder { + return builder.Append(f, "Link", link).(feedBuilder) +} + +func (f feedBuilder) Updated(updated time.Time) feedBuilder { + return builder.Set(f, "Updated", atom.Time(updated)).(feedBuilder) +} + +func (f feedBuilder) Author(author atom.Person) feedBuilder { + return builder.Set(f, "Author", &author).(feedBuilder) +} + +func (f feedBuilder) AddEntry(entry atom.Entry) feedBuilder { + return builder.Append(f, "Entry", &entry).(feedBuilder) +} + +func (f feedBuilder) Build() atom.Feed { + return builder.GetStruct(f).(atom.Feed) +} + +var FeedBuilder = builder.Register(feedBuilder{}, atom.Feed{}).(feedBuilder) diff --git a/link_builder.go b/link_builder.go new file mode 100644 index 0000000..7c83e87 --- /dev/null +++ b/link_builder.go @@ -0,0 +1,38 @@ +package main + +import ( + "github.com/lann/builder" + "golang.org/x/tools/blog/atom" +) + +type linkBuilder builder.Builder + +func (l linkBuilder) Rel(rel string) linkBuilder { + return builder.Set(l, "Rel", rel).(linkBuilder) +} + +func (l linkBuilder) Href(href string) linkBuilder { + return builder.Set(l, "Href", href).(linkBuilder) +} + +func (l linkBuilder) Type(typeName string) linkBuilder { + return builder.Set(l, "Type", typeName).(linkBuilder) +} + +func (l linkBuilder) HrefLang(lang string) linkBuilder { + return builder.Set(l, "HrefLang", lang).(linkBuilder) +} + +func (l linkBuilder) Title(title string) linkBuilder { + return builder.Set(l, "Title", title).(linkBuilder) +} + +func (l linkBuilder) Length(length uint) linkBuilder { + return builder.Set(l, "Length", length).(linkBuilder) +} + +func (l linkBuilder) Build() atom.Link { + return builder.GetStruct(l).(atom.Link) +} + +var LinkBuilder = builder.Register(linkBuilder{}, atom.Link{}).(linkBuilder) diff --git a/main.go b/main.go index 0b1f84e..a1f60a2 100644 --- a/main.go +++ b/main.go @@ -18,196 +18,159 @@ package main import ( - "bufio" + "bytes" "encoding/xml" "flag" - "io" + + "golang.org/x/tools/blog/atom" + "io/ioutil" "log" "mime" "net/http" "net/url" "os" + "path/filepath" "time" - - "golang.org/x/tools/blog/atom" ) -type AcquisitionFeed struct { - *atom.Feed - Dc string `xml:"xmlns:dc,attr"` - Opds string `xml:"xmlns:opds,attr"` -} - -type CatalogFeed atom.Feed - -const acquisitionType = "application/atom+xml;profile=opds-catalog;kind=acquisition" -const navegationType = "application/atom+xml;profile=opds-catalog;kind=navigation" - var ( port, dirRoot, author, authorUri, authorEmail string - updated atom.TimeStr ) +const acquisitionType = "application/atom+xml;profile=opds-catalog;kind=acquisition" +const navegationType = "application/atom+xml;profile=opds-catalog;kind=navigation" + +type AcquisitionFeed struct { + *atom.Feed + Dc string `xml:"xmlns:dc,attr"` + Opds string `xml:"xmlns:opds,attr"` +} + func init() { mime.AddExtensionType(".mobi", "application/x-mobipocket-ebook") mime.AddExtensionType(".epub", "application/epub+zip") mime.AddExtensionType(".fb2", "txt/xml") - - flag.StringVar(&port, "port", "8080", "The server will listen in this port") - flag.StringVar(&dirRoot, "dir", "./books", "A directory with books") - flag.StringVar(&author, "author", "", "The author of the feed") - flag.StringVar(&authorUri, "uri", "", "The author uri") - flag.StringVar(&authorEmail, "email", "", "The author email") - flag.Parse() - updated = atom.Time(time.Now()) } -func main() { - http.HandleFunc("/", errorHandler(func(w http.ResponseWriter, req *http.Request) error { - dirPath := filepath.Join(dirRoot, req.URL.Path) - fi, err := os.Stat(dirPath) +func handler(w http.ResponseWriter, req *http.Request) error { + fpath := filepath.Join(dirRoot, req.URL.Path) + + fi, err := os.Stat(fpath) + if err != nil { + return err + } + + if fi.IsDir() { + content, err := getContent(w, req, fpath) if err != nil { return err } + http.ServeContent(w, req, "feed.xml", time.Now(), bytes.NewReader(content)) + } else { + http.ServeFile(w, req, fpath) + } - if fi.IsDir() { - w.Write([]byte(xml.Header)) - return writeFeedTo(w, req.URL) - } + return nil +} - return writeFileTo(w, dirPath) - })) +func main() { + flag.StringVar(&port, "port", "8080", "The server will listen in this port") + flag.StringVar(&dirRoot, "dir", "./books", "A directory with books") + flag.StringVar(&author, "serveFeedauthor", "", "The feed's author") + flag.StringVar(&authorUri, "uri", "", "The feed's author uri") + flag.StringVar(&authorEmail, "email", "", "The feed's author email") + flag.Parse() + + http.HandleFunc("/", errorHandler(handler)) log.Fatal(http.ListenAndServe(":"+port, nil)) } -func writeFeedTo(w io.Writer, u *url.URL) error { - isAcquisition, err := isAcquisitionFeed(filepath.Join(dirRoot, u.Path)) - if err != nil { - return err +func getContent(w http.ResponseWriter, req *http.Request, dirpath string) (result []byte, err error) { + feed := makeFeed(dirpath, req) + if isAcquisition(dirpath) { + acFeed := &AcquisitionFeed{&feed, "http://purl.org/dc/terms/", "http://opds-spec.org/2010/catalog"} + result, err = xml.MarshalIndent(acFeed, " ", " ") + + } else { + result, err = xml.MarshalIndent(feed, " ", " ") } - if isAcquisition { - return writeAcquisitionFeed(w, u) - } - return writeCatalogFeed(w, u) + return } -func isAcquisitionFeed(p string) (bool, error) { - fis, err := ioutil.ReadDir(p) - if err != nil { - return false, err +func makeFeed(dirpath string, req *http.Request) atom.Feed { + feedBuilder := FeedBuilder. + Id(req.URL.Path). + Title("Catalog in " + req.URL.Path). + Author(AuthorBuilder.Name(author).Email(authorEmail).URI(authorUri).Build()). + Updated(time.Now()). + AddLink(LinkBuilder.Rel("start").Href("/").Type(navegationType).Build()) + + fis, _ := ioutil.ReadDir(dirpath) + for _, fi := range fis { + linkIsAcquisition := isAcquisition(filepath.Join(dirpath, fi.Name())) + feedBuilder = feedBuilder. + AddEntry(EntryBuilder. + Id(req.URL.Path + fi.Name()). + Title(fi.Name()). + Updated(time.Now()). + Published(time.Now()). + AddLink(LinkBuilder. + Rel(getRel(fi.Name(), linkIsAcquisition)). + Title(fi.Name()). + Href(getHref(req, fi.Name())). + Type(getType(fi.Name(), linkIsAcquisition)). + Build()). + Build()) } + return feedBuilder.Build() +} + +func getRel(name string, acquisition bool) (rel string) { + rel = "subsection" + if !acquisition { + return + } + ext := filepath.Ext(name) + if rel = "http://opds-spec.org/acquisition"; ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gift" { + rel = "http://opds-spec.org/image/thumbnail" + } + return +} + +func getType(name string, acquisition bool) (linkType string) { + linkType = acquisitionType + if !acquisition { + return + } + ext := filepath.Ext(name) + linkType = mime.TypeByExtension(ext) + return +} + +func getHref(req *http.Request, name string) string { + return filepath.Join(req.URL.EscapedPath(), url.PathEscape(name)) +} +func isAcquisition(dirpath string) bool { + fi, _ := os.Stat(dirpath) + if !fi.IsDir() { + return false + } + + fis, _ := ioutil.ReadDir(dirpath) + for _, fi := range fis { if !fi.IsDir() { - return true, nil + return true } } - - return false, nil -} - -func writeCatalogFeed(w io.Writer, u *url.URL) error { - feed := &CatalogFeed{ID: u.Path, Title: "Catalog feed in " + u.Path} - feed.Author = &atom.Person{Name: author, Email: authorEmail, URI: authorUri} - feed.Updated = updated - feed.Link = []atom.Link{{ - Rel: "start", - Href: "/", - Type: navegationType, - }} - - abs_path := filepath.Join(dirRoot, u.Path) - fis, err := ioutil.ReadDir(abs_path) - if err != nil { - return err - } - - for _, fi := range fis { - link := atom.Link{ - Rel: "subsection", - Title: fi.Name(), - Href: filepath.Join(u.EscapedPath(), url.PathEscape(fi.Name())), - Type: acquisitionType, - } - entry := &atom.Entry{ - ID: filepath.Join(u.Path, fi.Name()), - Title: fi.Name(), - Updated: updated, - Published: updated, - Link: []atom.Link{link}, - } - feed.Entry = append(feed.Entry, entry) - - } - - enc := xml.NewEncoder(w) - enc.Indent(" ", " ") - enc.Encode(feed) - return nil -} - -func writeAcquisitionFeed(w io.Writer, u *url.URL) error { - f := &atom.Feed{} - feed := &AcquisitionFeed{f, "http://purl.org/dc/terms/", "http://opds-spec.org/2010/catalog"} - feed.ID = u.Path - feed.Updated = updated - feed.Title = filepath.Base(u.Path) - feed.Author = &atom.Person{Name: author, Email: authorEmail, URI: authorUri} - feed.Link = []atom.Link{{ - Rel: "start", - Href: "/", - Type: navegationType, - }} - - abs_path := filepath.Join(dirRoot, u.Path) - fis, err := ioutil.ReadDir(abs_path) - if err != nil { - return err - } - entry := &atom.Entry{ - ID: u.Path, - Title: filepath.Base(u.Path), - Updated: updated, - Published: updated, - } - for _, fi := range fis { - ext := filepath.Ext(fi.Name()) - mime_type := mime.TypeByExtension(ext) - var rel string - if rel = "http://opds-spec.org/acquisition"; ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gift" { - rel = "http://opds-spec.org/image/thumbnail" - } - link := atom.Link{ - Title: fi.Name(), - Rel: rel, - Type: mime_type, - Href: filepath.Join(u.EscapedPath(), url.PathEscape(fi.Name())), - } - entry.Link = append(entry.Link, link) - } - feed.Entry = append(feed.Entry, entry) - - enc := xml.NewEncoder(w) - enc.Indent(" ", " ") - enc.Encode(feed) - return nil -} - -func writeFileTo(w io.Writer, p string) error { - f, err := os.Open(p) - if err != nil { - return err - } - defer f.Close() - r := bufio.NewReader(f) - r.WriteTo(w) - return nil + return false } func errorHandler(f func(http.ResponseWriter, *http.Request) error) http.HandlerFunc { diff --git a/text_builder.go b/text_builder.go new file mode 100644 index 0000000..6a1a0c8 --- /dev/null +++ b/text_builder.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/lann/builder" + "golang.org/x/tools/blog/atom" +) + +type textBuilder builder.Builder + +func (t textBuilder) Type(textType string) textBuilder { + return builder.Set(t, "Type", textType).(textBuilder) +} + +func (t textBuilder) Body(body string) textBuilder { + return builder.Set(t, "Body", body).(textBuilder) +} + +func (t textBuilder) Build() atom.Text { + return builder.GetStruct(t).(atom.Text) +} + +var TextBuilder = builder.Register(textBuilder{}, atom.Text{}).(textBuilder)