Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add file.stat() #81

Merged
merged 1 commit into from
Feb 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions compiler/stdlib/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,40 @@ var osModule = map[string]objects.Object{
"start_process": &objects.UserFunction{Value: osStartProcess}, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error
"exec_look_path": FuncASRSE(exec.LookPath), // exec_look_path(file) => string/error
"exec": &objects.UserFunction{Value: osExec}, // exec(name, args...) => command
"stat": &objects.UserFunction{Value: osStat}, // stat(name) => imap(fileinfo)/error
}

func osStat(args ...objects.Object) (ret objects.Object, err error) {
if len(args) != 1 {
return nil, objects.ErrWrongNumArguments
}

fname, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}

stat, err := os.Stat(fname)
if err != nil {
return wrapError(err), nil
}

fstat := &objects.ImmutableMap{
Value: map[string]objects.Object{
"name": &objects.String{Value: stat.Name()},
"mtime": &objects.Time{Value: stat.ModTime()},
"size": &objects.Int{Value: stat.Size()},
"mode": &objects.Int{Value: int64(stat.Mode())},
},
}

if stat.IsDir() {
fstat.Value["directory"] = objects.TrueValue
} else {
fstat.Value["directory"] = objects.FalseValue
}

return fstat, nil
}

func osCreate(args ...objects.Object) (objects.Object, error) {
Expand Down
10 changes: 10 additions & 0 deletions compiler/stdlib/os_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ func makeOSFile(file *os.File) *objects.ImmutableMap {
return &objects.Int{Value: res}, nil
},
},
// stat() => imap(fileinfo)/error
"stat": &objects.UserFunction{
Value: func(args ...objects.Object) (ret objects.Object, err error) {
if len(args) != 0 {
return nil, objects.ErrWrongNumArguments
}

return osStat(&objects.String{Value: file.Name()})
},
},
},
}
}
72 changes: 72 additions & 0 deletions compiler/stdlib/os_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package stdlib_test

import (
"io/ioutil"
"os"
"testing"

"github.com/d5/tengo/objects"
)

func TestFileStatArgs(t *testing.T) {
module(t, "os").call("stat").expectError()
}

func TestFileStatFile(t *testing.T) {
content := []byte("the quick brown fox jumps over the lazy dog")
tf, err := ioutil.TempFile("", "test")
if err != nil {
t.Logf("could not open tempfile: %s", err)
return
}
defer os.Remove(tf.Name())

_, err = tf.Write(content)
if err != nil {
t.Logf("could not write temp content: %s", err)
return
}

tf.Close()

stat, err := os.Stat(tf.Name())
if err != nil {
t.Logf("could not get tmp file stat: %s", err)
return
}

module(t, "os").call("stat", tf.Name()).expect(&objects.ImmutableMap{
Value: map[string]objects.Object{
"name": &objects.String{Value: stat.Name()},
"mtime": &objects.Time{Value: stat.ModTime()},
"size": &objects.Int{Value: stat.Size()},
"mode": &objects.Int{Value: int64(stat.Mode())},
"directory": objects.FalseValue,
},
})
}

func TestFileStatDir(t *testing.T) {
td, err := ioutil.TempDir("", "test")
if err != nil {
t.Logf("could not open tempdir: %s", err)
return
}
defer os.RemoveAll(td)

stat, err := os.Stat(td)
if err != nil {
t.Logf("could not get tmp dir stat: %s", err)
return
}

module(t, "os").call("stat", td).expect(&objects.ImmutableMap{
Value: map[string]objects.Object{
"name": &objects.String{Value: stat.Name()},
"mtime": &objects.Time{Value: stat.ModTime()},
"size": &objects.Int{Value: stat.Size()},
"mode": &objects.Int{Value: int64(stat.Mode())},
"directory": objects.TrueValue,
},
})
}
10 changes: 10 additions & 0 deletions docs/stdlib-os.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ os := import("os")
- `remove_all(name string) => error `: removes path and any children it contains.
- `rename(oldpath string, newpath string) => error `: renames (moves) oldpath to newpath.
- `setenv(key string, value string) => error `: sets the value of the environment variable named by the key.
- `stat(filename string) => FileInfo/error`: returns a file info structure describing the file
- `symlink(oldname string newname string) => error `: creates newname as a symbolic link to oldname.
- `temp_dir() => string `: returns the default directory to use for temporary files.
- `truncate(name string, size int) => error `: changes the size of the named file.
Expand Down Expand Up @@ -98,6 +99,7 @@ file.close()
- `write(bytes) => int/error`: writes len(b) bytes to the File.
- `write_string(string) => int/error`: is like 'write', but writes the contents of string s rather than a slice of bytes.
- `read(bytes) => int/error`: reads up to len(b) bytes from the File.
- `stat() => FileInfo/error`: returns a file info structure describing the file
- `chmod(mode int) => error`: changes the mode of the file to mode.
- `seek(offset int, whence int) => int/error`: sets the offset for the next Read or Write on file to offset, interpreted according to whence: 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end.

Expand Down Expand Up @@ -131,6 +133,14 @@ cmd := exec.command("echo", ["foo", "bar"])
output := cmd.output()
```

## FileInfo

- `name`: name of the file the info describes
- `mtime`: time the file was last modified
- `size`: file size in bytes
- `mode`: file permissions as in int, comparable to octal permissions
- `directory`: boolean indicating if the file is a directory
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasnt sure how you want these as the other examples only show methods so just tried this to see


## Command

- `combined_output() => bytes/error`: runs the command and returns its combined standard output and standard error.
Expand Down