Add 'environment' package
This commit adds a new environment package that manages all calls to the underlying operating system.
This commit is contained in:
parent
822c70cd69
commit
f19619ef9f
60 changed files with 539 additions and 308 deletions
58
pkg/environment/environment.go
Normal file
58
pkg/environment/environment.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package environment
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/tkw1536/goprogram/stream"
|
||||
)
|
||||
|
||||
// Environment represents an environment that a program can run it.
|
||||
// It mostly mimics the interfaces of the [os] package.
|
||||
type Environment interface {
|
||||
isEnv()
|
||||
|
||||
GetEnv(name string) string
|
||||
|
||||
Stat(path string) (fs.FileInfo, error)
|
||||
Lstat(path string) (fs.FileInfo, error)
|
||||
|
||||
Readlink(path string) (string, error)
|
||||
Symlink(oldname, newname string) error
|
||||
|
||||
ReadDir(name string) ([]fs.DirEntry, error)
|
||||
|
||||
Open(path string) (fs.File, error)
|
||||
Chtimes(name string, atime time.Time, mtime time.Time) error
|
||||
SameFile(f1, f2 fs.FileInfo) bool
|
||||
|
||||
Create(path string, mode fs.FileMode) (WritableFile, error)
|
||||
|
||||
Mkdir(path string, mode fs.FileMode) error
|
||||
MkdirAll(path string, mode fs.FileMode) error
|
||||
|
||||
Remove(path string) error
|
||||
RemoveAll(path string) error
|
||||
|
||||
WalkDir(root string, fn fs.WalkDirFunc) error
|
||||
|
||||
Abs(path string) (string, error)
|
||||
|
||||
Listen(network, address string) (net.Listener, error)
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
|
||||
Executable() (string, error)
|
||||
Exec(io stream.IOStream, workdir string, exe string, argv ...string) int
|
||||
LookPathAbs(name string) (string, error)
|
||||
}
|
||||
|
||||
type WritableFile interface {
|
||||
fs.File
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func init() {
|
||||
var _ Environment = Native{}
|
||||
}
|
||||
69
pkg/environment/globals.go
Normal file
69
pkg/environment/globals.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package environment
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/tkw1536/goprogram/stream"
|
||||
)
|
||||
|
||||
// ExecCommandError is returned by Exec when a command could not be executed.
|
||||
// This typically hints that the executable cannot be found, but may have other causes.
|
||||
const ExecCommandError = 127
|
||||
|
||||
// DefaultFilePerm is the default mode to use for files
|
||||
const DefaultFilePerm fs.FileMode = 0666
|
||||
|
||||
// DefaultDirPerm is the default mode to use for directories
|
||||
const DefaultDirPerm fs.FileMode = os.ModeDir & fs.ModePerm
|
||||
|
||||
// IsExist checks if the provided error represents a 'does not exist' errror
|
||||
func IsExist(err error) bool {
|
||||
return os.IsExist(err)
|
||||
}
|
||||
|
||||
// IsNotExist checks if the provided error represents a 'does exist' error
|
||||
func IsNotExist(err error) bool {
|
||||
return os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// WriteFile is like [os.WriteFile].
|
||||
func WriteFile(env Environment, path string, data []byte, mode fs.FileMode) error {
|
||||
handle, err := env.Create(path, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer handle.Close()
|
||||
|
||||
if _, err := handle.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadFile is like [os.ReadFile]
|
||||
func ReadFile(env Environment, path string) ([]byte, error) {
|
||||
// open the file!
|
||||
file, err := env.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// copy everything into a buffer!
|
||||
var buffer bytes.Buffer
|
||||
if _, err := io.Copy(&buffer, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// return the buffer contents!
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// MustExec is like Exec, except that it returns true if the command exited successfully, and else false.
|
||||
func MustExec(env Environment, io stream.IOStream, workdir string, exe string, argv ...string) bool {
|
||||
return env.Exec(io, workdir, exe, argv...) == 0
|
||||
}
|
||||
44
pkg/environment/native_exec.go
Normal file
44
pkg/environment/native_exec.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package environment
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/tkw1536/goprogram/stream"
|
||||
)
|
||||
|
||||
// Exec executes a system command with the specified input/output streams, working directory, and arguments.
|
||||
//
|
||||
// If the command executes, it's exit code will be returned.
|
||||
// If the command can not be executed, returns [ExecCommandError].
|
||||
func (Native) Exec(io stream.IOStream, workdir string, exe string, argv ...string) int {
|
||||
// setup the command
|
||||
cmd := exec.Command(exe, argv...)
|
||||
cmd.Dir = workdir
|
||||
cmd.Stdin = io.Stdin
|
||||
cmd.Stdout = io.Stdout
|
||||
cmd.Stderr = io.Stderr
|
||||
|
||||
// run it
|
||||
err := cmd.Run()
|
||||
|
||||
// non-zero exit
|
||||
if err, ok := err.(*exec.ExitError); ok {
|
||||
return err.ExitCode()
|
||||
}
|
||||
|
||||
// unknown error
|
||||
if err != nil {
|
||||
return ExecCommandError
|
||||
}
|
||||
|
||||
// everything is fine!
|
||||
return 0
|
||||
}
|
||||
|
||||
func (n Native) LookPathAbs(file string) (string, error) {
|
||||
path, err := exec.LookPath(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return n.Abs(path)
|
||||
}
|
||||
80
pkg/environment/native_fs.go
Normal file
80
pkg/environment/native_fs.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package environment
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Native struct{}
|
||||
|
||||
func (Native) isEnv() {}
|
||||
|
||||
func (Native) GetEnv(name string) string {
|
||||
return os.Getenv(name)
|
||||
}
|
||||
|
||||
func (Native) Stat(path string) (fs.FileInfo, error) {
|
||||
return os.Stat(path)
|
||||
}
|
||||
|
||||
func (Native) Lstat(path string) (fs.FileInfo, error) {
|
||||
return os.Lstat(path)
|
||||
}
|
||||
|
||||
func (Native) Readlink(path string) (string, error) {
|
||||
return os.Readlink(path)
|
||||
}
|
||||
|
||||
func (Native) Symlink(oldname, newname string) error {
|
||||
return os.Symlink(oldname, newname)
|
||||
}
|
||||
|
||||
func (Native) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
return os.ReadDir(name)
|
||||
}
|
||||
|
||||
func (Native) SameFile(f1, f2 fs.FileInfo) bool {
|
||||
return os.SameFile(f1, f2)
|
||||
}
|
||||
|
||||
func (Native) WalkDir(path string, f fs.WalkDirFunc) error {
|
||||
return filepath.WalkDir(path, f)
|
||||
}
|
||||
|
||||
func (Native) Executable() (string, error) {
|
||||
return os.Executable() // TODO: not sure this works with the remote concepts
|
||||
}
|
||||
|
||||
func (Native) Open(path string) (fs.File, error) {
|
||||
return os.Open(path)
|
||||
}
|
||||
|
||||
func (Native) Create(path string, mode fs.FileMode) (WritableFile, error) {
|
||||
return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
|
||||
}
|
||||
|
||||
func (Native) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return os.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
func (Native) Mkdir(path string, mode fs.FileMode) error {
|
||||
return os.Mkdir(path, mode)
|
||||
}
|
||||
|
||||
func (Native) MkdirAll(path string, mode fs.FileMode) error {
|
||||
return os.MkdirAll(path, mode)
|
||||
}
|
||||
|
||||
func (Native) Remove(path string) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
func (Native) RemoveAll(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
func (Native) Abs(path string) (string, error) {
|
||||
return filepath.Abs(path)
|
||||
}
|
||||
11
pkg/environment/native_net.go
Normal file
11
pkg/environment/native_net.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package environment
|
||||
|
||||
import "net"
|
||||
|
||||
func (Native) Listen(network, address string) (net.Listener, error) {
|
||||
return net.Listen(network, address)
|
||||
}
|
||||
|
||||
func (Native) Dial(network, address string) (net.Conn, error) {
|
||||
return net.Dial(network, address)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue