diff --git a/gopher.go b/gopher.go index 9205c3f..13e76c9 100644 --- a/gopher.go +++ b/gopher.go @@ -99,11 +99,6 @@ var ( // ItemType represents the type of an item type ItemType byte -// MarshalJSON returns a JSON mashaled byte array of an ItemType -func (it ItemType) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("%q", string(byte(it)))), nil -} - // Return a human friendly represation of an ItemType func (it ItemType) String() string { switch it { @@ -152,14 +147,33 @@ func (it ItemType) String() string { // Item describes an entry in a directory listing. type Item struct { - Type ItemType - Description string - Selector string - Host string - Port int + Type ItemType `json:"type"` + Description string `json:"description"` + Selector string `json:"selector"` + Host string `json:"host"` + Port int `json:"port"` // non-standard extensions (ignored by standard clients) - Extras []string + Extras []string `json:"extras"` +} + +// MarshalJSON serializes an Item into a JSON structure +func (i Item) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string `json:"type"` + Description string `json:"description"` + Selector string `json:"selector"` + Host string `json:"host"` + Port int `json:"port"` + Extras []string `json:"extras"` + }{ + Type: string(i.Type), + Description: i.Description, + Selector: i.Selector, + Host: i.Host, + Port: i.Port, + Extras: i.Extras, + }) } // MarshalText serializes an Item into an array of bytes @@ -241,7 +255,9 @@ func (i *Item) isDirectoryLike() bool { } // Directory representes a Gopher Menu of Items -type Directory []Item +type Directory struct { + Items []Item `json:"items"` +} // ToJSON returns the Directory as JSON bytes func (d *Directory) ToJSON() ([]byte, error) { @@ -252,7 +268,7 @@ func (d *Directory) ToJSON() ([]byte, error) { // ToText returns the Directory as UTF-8 encoded bytes func (d *Directory) ToText() ([]byte, error) { var buffer bytes.Buffer - for _, i := range *d { + for _, i := range d.Items { val, err := i.MarshalText() if err != nil { return nil, err @@ -368,25 +384,25 @@ func (i *Item) FetchFile() (io.Reader, error) { // Calling this on an Item whose type is not DIRECTORY will return an error. func (i *Item) FetchDirectory() (Directory, error) { if !i.isDirectoryLike() { - return nil, errors.New("cannot fetch a file as a directory") + return Directory{}, errors.New("cannot fetch a file as a directory") } conn, err := net.Dial("tcp", i.Host+":"+strconv.Itoa(i.Port)) if err != nil { - return nil, err + return Directory{}, err } _, err = conn.Write([]byte(i.Selector + CRLF)) if err != nil { - return nil, err + return Directory{}, err } - var d Directory - reader := bufio.NewReader(conn) scanner := bufio.NewScanner(reader) scanner.Split(bufio.ScanLines) + var items []Item + for scanner.Scan() { line := strings.Trim(scanner.Text(), "\r\n") @@ -398,16 +414,16 @@ func (i *Item) FetchDirectory() (Directory, error) { break } - var i Item - err := i.parse(line) + item := Item{} + err := item.parse(line) if err != nil { log.Printf("Error parsing %q: %q", line, err) continue } - d = append(d, i) + items = append(items, item) } - return d, nil + return Directory{items}, nil } // Request repsesnts an inbound request to a listening server. diff --git a/gopher_test.go b/gopher_test.go index e63acee..546b0c2 100644 --- a/gopher_test.go +++ b/gopher_test.go @@ -43,13 +43,27 @@ func TestGet(t *testing.T) { t.Logf("res: %s", string(b)) - assert.Len(res.Dir, 1) + assert.Len(res.Dir.Items, 1) - assert.Equal(res.Dir[0].Type, gopher.INFO) - assert.Equal(res.Dir[0].Description, "Hello World!") + assert.Equal(res.Dir.Items[0].Type, gopher.INFO) + assert.Equal(res.Dir.Items[0].Description, "Hello World!") +} + +func TestFileServer(t *testing.T) { + assert := assert.New(t) + + res, err := gopher.Get("gopher://localhost:7000/") + assert.Nil(err) + assert.Len(res.Dir.Items, 5) + + json, err := res.Dir.ToJSON() + assert.Nil(err) + + assert.JSONEq(string(json), `{"items":[{"type":"0","description":"LICENSE","selector":"LICENSE","host":"127.0.0.1","port":7000,"extras":null},{"type":"0","description":"README.md","selector":"README.md","host":"127.0.0.1","port":7000,"extras":null},{"type":"1","description":"examples","selector":"examples","host":"127.0.0.1","port":7000,"extras":null},{"type":"0","description":"gopher.go","selector":"gopher.go","host":"127.0.0.1","port":7000,"extras":null},{"type":"0","description":"gopher_test.go","selector":"gopher_test.go","host":"127.0.0.1","port":7000,"extras":null}]}`) } func TestMain(m *testing.M) { + gopher.Handle("/", gopher.FileServer(gopher.Dir("."))) gopher.HandleFunc("/hello", hello) go func() { log.Fatal(gopher.ListenAndServe("localhost:7000", nil))