Skip to content
Permalink
Browse files
Carlize
  • Loading branch information
carlmjohnson committed Jun 1, 2020
1 parent 2cd947f commit d5d51722ef95a5ebdf49c453cd7f00ed4a9e3910
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 197 deletions.

This file was deleted.

@@ -0,0 +1,49 @@
package grabxkcd

import (
"fmt"
"path"
"time"
)

// LatestComic is a magic number that grabs whatever is the latest comic
const LatestComic = 0

// BuildURL from comicNumber (or LatestComic).
func BuildURL(comicNumber int) string {
if comicNumber == LatestComic {
return "https://xkcd.com/info.0.json"
}
return fmt.Sprintf("https://xkcd.com/%d/info.0.json", comicNumber)
}

// APIResponse returned by the XKCD API
type APIResponse struct {
Month string `json:"month"`
Number int `json:"num"`
Link string `json:"link"`
Year string `json:"year"`
News string `json:"news"`
SafeTitle string `json:"safe_title"`
Transcript string `json:"transcript"`
Description string `json:"alt"`
Image string `json:"img"`
Title string `json:"title"`
Day string `json:"day"`
}

// Date returns a *time.Time based on the API strings (or nil if the response is malformed).
func (ar APIResponse) Date() *time.Time {
t, err := time.Parse(
"2006-1-2",
fmt.Sprintf("%s-%s-%s", ar.Year, ar.Month, ar.Day),
)
if err != nil {
return nil
}
return &t
}

func (ar APIResponse) Filename() string {
return path.Base(ar.Image)
}
@@ -0,0 +1,145 @@
package grabxkcd

import (
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"time"
)

// CLI runs the go-grab-xkcd command line app and returns its exit status.
func CLI(args []string) int {
var app appEnv
err := app.fromArgs(args)
if err != nil {
return 2
}
if err = app.run(); err != nil {
fmt.Fprintf(os.Stderr, "Runtime error: %v\n", err)
return 1
}
return 0
}

type appEnv struct {
hc *http.Client
comicNo int
saveImage bool
outputJSON bool
}

func (app *appEnv) fromArgs(args []string) error {
// Shallow copy of default client
app.hc = &*http.DefaultClient
fl := flag.NewFlagSet("xkcd-grab", flag.ContinueOnError)
fl.IntVar(
&app.comicNo, "n", LatestComic, "Comic number to fetch (default latest)",
)
fl.DurationVar(&app.hc.Timeout, "t", 30*time.Second, "Client timeout")
fl.BoolVar(
&app.saveImage, "s", false, "Save image to current directory",
)
outputType := fl.String(
"o", "text", "Print output in format: text/json",
)
if err := fl.Parse(args); err != nil {
return err
}
if *outputType != "text" && *outputType != "json" {
fmt.Fprintf(os.Stderr, "got bad output type: %q\n", *outputType)
fl.Usage()
return flag.ErrHelp
}
app.outputJSON = *outputType == "json"
return nil
}

func (app *appEnv) run() error {
u := BuildURL(app.comicNo)

var resp APIResponse
if err := app.fetchJSON(u, &resp); err != nil {
return err
}
if resp.Date() == nil {
return fmt.Errorf("could not parse date of comic: %q/%q/%q",
resp.Year, resp.Month, resp.Day)
}
if app.saveImage {
if err := app.fetchAndSave(resp.Image, resp.Filename()); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "Saved: %q\n", resp.Filename())
}
if app.outputJSON {
return printJSON(resp)
}

return prettyPrint(resp)
}

func (app *appEnv) fetchJSON(url string, data interface{}) error {
resp, err := app.hc.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()

return json.NewDecoder(resp.Body).Decode(data)
}

func (app *appEnv) fetchAndSave(url, destPath string) error {
resp, err := app.hc.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()

f, err := os.Create(destPath)
if err != nil {
return err
}

_, err = io.Copy(f, resp.Body)
return err
}

// Output is the JSON output of this app
type Output struct {
Title string `json:"title"`
Number int `json:"number"`
Date string `json:"date"`
Description string `json:"description"`
Image string `json:"image"`
}

func printJSON(ar APIResponse) error {
o := Output{
Title: ar.Title,
Number: ar.Number,
Date: ar.Date().Format("2006-01-02"),
Description: ar.Description,
Image: ar.Image,
}
b, err := json.MarshalIndent(&o, "", " ")
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}

func prettyPrint(ar APIResponse) error {
_, err := fmt.Printf(
"Title: %s\nComic No: %d\nDate: %s\nDescription: %s\nImage: %s\n",
ar.Title,
ar.Number,
ar.Date().Format("02-01-2006"),
ar.Description,
ar.Image,
)
return err
}
35 main.go
@@ -1,40 +1,11 @@
package main

import (
"flag"
"fmt"
"log"
"time"
"os"

"github.com/erybz/go-grab-xkcd/client"
"github.com/erybz/go-grab-xkcd/grabxkcd"
)

func main() {
comicNo := flag.Int(
"n", int(client.LatestComic), "Comic number to fetch (default latest)",
)
clientTimeout := flag.Int64(
"t", int64(client.DefaultClientTimeout.Seconds()), "Client timeout in seconds",
)
saveImage := flag.Bool(
"s", false, "Save image to current directory",
)
outputType := flag.String(
"o", "text", "Print output in format: text/json",
)
flag.Parse()

xkcdClient := client.NewXKCDClient()
xkcdClient.SetTimeout(time.Duration(*clientTimeout) * time.Second)

comic, err := xkcdClient.Fetch(client.ComicNumber(*comicNo), *saveImage)
if err != nil {
log.Println(err)
}

if *outputType == "json" {
fmt.Println(comic.JSON())
} else {
fmt.Println(comic.PrettyString())
}
os.Exit(grabxkcd.CLI(os.Args[1:]))
}

0 comments on commit d5d5172

Please sign in to comment.