Skip to content

Commit

Permalink
io/fs: add FormatFileInfo and FormatDirEntry functions
Browse files Browse the repository at this point in the history
For #54451

Change-Id: I3214066f77b1398ac1f2786ea035c83f32f0a826
Reviewed-on: https://go-review.googlesource.com/c/go/+/489555
Run-TryBot: Ian Lance Taylor <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
Run-TryBot: Ian Lance Taylor <[email protected]>
Reviewed-by: Joseph Tsai <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
Auto-Submit: Ian Lance Taylor <[email protected]>
  • Loading branch information
ianlancetaylor authored and gopherbot committed May 2, 2023
1 parent 630ef2e commit 72ba919
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 0 deletions.
2 changes: 2 additions & 0 deletions api/next/54451.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pkg io/fs, func FormatDirEntry(DirEntry) string #54451
pkg io/fs, func FormatFileInfo(FileInfo) string #54451
76 changes: 76 additions & 0 deletions src/io/fs/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fs

import (
"time"
)

// FormatFileInfo returns a formatted version of info for human readability.
// Implementations of FileInfo can call this from a String method.
// The output for a file named "hello.go", 100 bytes, mode 0o644, created
// January 1, 1970 at noon is
//
// -rw-r--r-- 100 1970-01-01 12:00:00 hello.go
func FormatFileInfo(info FileInfo) string {
name := info.Name()
b := make([]byte, 0, 40+len(name))
b = append(b, info.Mode().String()...)
b = append(b, ' ')

size := info.Size()
var usize uint64
if size >= 0 {
usize = uint64(size)
} else {
b = append(b, '-')
usize = uint64(-size)
}
var buf [20]byte
i := len(buf) - 1
for usize >= 10 {
q := usize / 10
buf[i] = byte('0' + usize - q*10)
i--
usize = q
}
buf[i] = byte('0' + usize)
b = append(b, buf[i:]...)
b = append(b, ' ')

b = append(b, info.ModTime().Format(time.DateTime)...)
b = append(b, ' ')

b = append(b, name...)
if info.IsDir() {
b = append(b, '/')
}

return string(b)
}

// FormatDirEntry returns a formatted version of dir for human readability.
// Implementations of DirEntry can call this from a String method.
// The outputs for a directory named subdir and a file named hello.go are:
//
// d subdir/
// - hello.go
func FormatDirEntry(dir DirEntry) string {
name := dir.Name()
b := make([]byte, 0, 5+len(name))

// The Type method does not return any permission bits,
// so strip them from the string.
mode := dir.Type().String()
mode = mode[:len(mode)-9]

b = append(b, mode...)
b = append(b, ' ')
b = append(b, name...)
if dir.IsDir() {
b = append(b, '/')
}
return string(b)
}
123 changes: 123 additions & 0 deletions src/io/fs/format_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fs_test

import (
. "io/fs"
"testing"
"time"
)

// formatTest implements FileInfo to test FormatFileInfo,
// and implements DirEntry to test FormatDirEntry.
type formatTest struct {
name string
size int64
mode FileMode
modTime time.Time
isDir bool
}

func (fs *formatTest) Name() string {
return fs.name
}

func (fs *formatTest) Size() int64 {
return fs.size
}

func (fs *formatTest) Mode() FileMode {
return fs.mode
}

func (fs *formatTest) ModTime() time.Time {
return fs.modTime
}

func (fs *formatTest) IsDir() bool {
return fs.isDir
}

func (fs *formatTest) Sys() any {
return nil
}

func (fs *formatTest) Type() FileMode {
return fs.mode.Type()
}

func (fs *formatTest) Info() (FileInfo, error) {
return fs, nil
}

var formatTests = []struct {
input formatTest
wantFileInfo string
wantDirEntry string
}{
{
formatTest{
name: "hello.go",
size: 100,
mode: 0o644,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: false,
},
"-rw-r--r-- 100 1970-01-01 12:00:00 hello.go",
"- hello.go",
},
{
formatTest{
name: "home/gopher",
size: 0,
mode: ModeDir | 0o755,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: true,
},
"drwxr-xr-x 0 1970-01-01 12:00:00 home/gopher/",
"d home/gopher/",
},
{
formatTest{
name: "big",
size: 0x7fffffffffffffff,
mode: ModeIrregular | 0o644,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: false,
},
"?rw-r--r-- 9223372036854775807 1970-01-01 12:00:00 big",
"? big",
},
{
formatTest{
name: "small",
size: -0x8000000000000000,
mode: ModeSocket | ModeSetuid | 0o644,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: false,
},
"Surw-r--r-- -9223372036854775808 1970-01-01 12:00:00 small",
"S small",
},
}

func TestFormatFileInfo(t *testing.T) {
for i, test := range formatTests {
got := FormatFileInfo(&test.input)
if got != test.wantFileInfo {
t.Errorf("%d: FormatFileInfo(%#v) = %q, want %q", i, test.input, got, test.wantFileInfo)
}
}
}

func TestFormatDirEntry(t *testing.T) {
for i, test := range formatTests {
got := FormatDirEntry(&test.input)
if got != test.wantDirEntry {
t.Errorf("%d: FormatDirEntry(%#v) = %q, want %q", i, test.input, got, test.wantDirEntry)
}
}

}

0 comments on commit 72ba919

Please sign in to comment.