From: Ian Lance Taylor Date: Thu, 27 Apr 2023 00:44:24 +0000 (-0700) Subject: io/fs: add FormatFileInfo and FormatDirEntry functions X-Git-Tag: go1.21rc1~718 X-Git-Url: http://www.git.cypherpunks.ru/?a=commitdiff_plain;h=72ba91902a39abb47ee9681319d517d4413e3b65;p=gostls13.git io/fs: add FormatFileInfo and FormatDirEntry functions For #54451 Change-Id: I3214066f77b1398ac1f2786ea035c83f32f0a826 Reviewed-on: https://go-review.googlesource.com/c/go/+/489555 Run-TryBot: Ian Lance Taylor TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Reviewed-by: Joseph Tsai Reviewed-by: Dmitri Shuralyov Auto-Submit: Ian Lance Taylor --- diff --git a/api/next/54451.txt b/api/next/54451.txt new file mode 100644 index 0000000000..a1d4fd5280 --- /dev/null +++ b/api/next/54451.txt @@ -0,0 +1,2 @@ +pkg io/fs, func FormatDirEntry(DirEntry) string #54451 +pkg io/fs, func FormatFileInfo(FileInfo) string #54451 diff --git a/src/io/fs/format.go b/src/io/fs/format.go new file mode 100644 index 0000000000..f490341f6c --- /dev/null +++ b/src/io/fs/format.go @@ -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) +} diff --git a/src/io/fs/format_test.go b/src/io/fs/format_test.go new file mode 100644 index 0000000000..a5f5066f36 --- /dev/null +++ b/src/io/fs/format_test.go @@ -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) + } + } + +}