Documentation
¶
Overview ¶
Package util contains general helper functions for workflow (library) authors.
The functions can be divided into roughly three groups: paths, formatting and scripting.
Paths ¶
There are a couple of convenience path functions, MustExist and ClearDirectory.
Formatting ¶
PrettyPath for user-friendly paths, and the Pad* functions for padding strings.
Scripting ¶
QuoteAS quotes strings for insertion into AppleScript code and there are several Run* functions for executing script code and files.
Run() // run a script file or executable & return output RunAS() // run AppleScript code & return output RunJS() // run JXA code & return output RunCmd() // run *exec.Cmd & return output
Run takes the path to a script or executable. If file is executable, it runs the file directly. If it's a script file, it tries to guess the appropriate interpreter.
See Runner for more information.
Index ¶
- Variables
- func ClearDirectory(p string) error
- func MustExist(dirpath ...string) string
- func Pad(str, pad string, n int) string
- func PadLeft(str, pad string, n int) string
- func PadRight(str, pad string, n int) string
- func PathExists(path string) bool
- func PrettyPath(path string) string
- func QuoteAS(s string) string
- func QuoteJS(v interface{}) string
- func Run(filename string, args ...string) ([]byte, error)
- func RunAS(script string, args ...string) (string, error)
- func RunCmd(cmd *exec.Cmd) ([]byte, error)
- func RunJS(script string, args ...string) (string, error)
- func Slugify(s string) string
- func Timed(start time.Time, title string)
- func WriteFile(filename string, data []byte, perm os.FileMode) error
- type ExecRunner
- type Runner
- type Runners
- type ScriptRunner
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrUnknownFileType = errors.New("unknown filetype")
ErrUnknownFileType is returned by Run for files it can't identify.
Functions ¶
func ClearDirectory ¶
ClearDirectory deletes all files within a directory, but not directory itself.
func MustExist ¶ added in v0.10.1
MustExist creates all specified directories and returns the last one. Panics if any directory cannot be created. All created directories have permission set to 0700.
func Pad ¶
Pad pads str to length n by adding pad to both ends.
Example ¶
fmt.Println(Pad("wow", "-", 10))
Output: ---wow----
func PadLeft ¶
PadLeft pads str to length n by adding pad to its left.
Example ¶
fmt.Println(PadLeft("wow", "-", 5))
Output: --wow
func PadRight ¶
PadRight pads str to length n by adding pad to its right.
Example ¶
fmt.Println(PadRight("wow", "-", 5))
Output: wow--
func PathExists ¶
PathExists checks for the existence of path. Panics if an error is encountered.
Example ¶
name := "my-test-file.txt" // Non-existent file fmt.Println(PathExists(name)) // Create the file if err := ioutil.WriteFile(name, []byte("test"), 0600); err != nil { panic(err) } // Now it exists fmt.Println(PathExists(name))
Output: false true
func PrettyPath ¶ added in v0.10.1
PrettyPath replaces $HOME with ~ in path
Example ¶
Shorten paths by replacing user's home directory with ~
paths := []string{ "", "$HOME", "$HOME/", "$HOME/Documents", "/Applications", } for _, s := range paths { // Expand $HOME p := os.ExpandEnv(s) fmt.Println(PrettyPath(p)) }
Output: ~ ~/ ~/Documents /Applications
func QuoteAS ¶ added in v0.15.0
QuoteAS converts string to an AppleScript string literal for insertion into AppleScript code. It wraps the value in quotation marks, so don't insert additional ones.
Example ¶
QuoteAS wraps the string in quotes and escapes quotes within the string.
values := []string{ "", "simple", "with spaces", `has "quotes" within`, `"within quotes"`, `"`, } // Quote values for insertion into AppleScript for _, s := range values { fmt.Println(QuoteAS(s)) }
Output: "" "simple" "with spaces" "has " & quote & "quotes" & quote & " within" quote & "within quotes" & quote quote
func QuoteJS ¶ added in v0.15.0
func QuoteJS(v interface{}) string
QuoteJS converts a value into JavaScript source code. It calls json.Marshal(v), and returns an empty string if an error occurs.
func Run ¶ added in v0.15.0
Run runs the executable or script at path and returns the output. If it can't figure out how to run the file (see Runner), it returns ErrUnknownFileType.
Example ¶
Run calls any executable file. It does *not* use $PATH to find commands.
// Create a simple test script filename := "test-script" script := `#!/bin/bash echo -n Happy Hour ` // Make sure script is executable! if err := ioutil.WriteFile(filename, []byte(script), 0700); err != nil { panic(err) } // Note: we're running "test-script", but Run looks for "./test-script", // not a command "test-script" on your $PATH. out, err := Run(filename) if err != nil { panic(err) } fmt.Println(string(out))
Output: Happy Hour
Example (Arguments) ¶
You can pass arguments to the program/script you run.
// Run an executable with arguments out, err := Run("/bin/bash", "-c", "echo -n Stringfellow Hawke") if err != nil { panic(err) } fmt.Println(string(out))
Output: Stringfellow Hawke
Example (Scripts) ¶
Run recognises certain kinds of script files and knows which interpreter to run them with.
// Test scripts that output $1. // Run will run them based on their file extension. scripts := []struct { name, code string }{ {"test-file.py", "import sys; print(sys.argv[1])"}, {"test-file.txt", "ignored"}, // invalid {"test-file.sh", `echo "$1"`}, {"test-file.scpt", "on run(argv)\nreturn first item of argv\nend run"}, {"test-file.doc", "irrelevant"}, // invalid } // Create test scripts. Note: they aren't executable. for _, script := range scripts { if err := ioutil.WriteFile(script.name, []byte(script.code), 0600); err != nil { panic(err) } } // Run scripts for _, script := range scripts { // Run runs file based on file extension // Pass script's own name as $1 data, err := Run(script.name, script.name) if err != nil { // We're expecting 2 unknown types if err == ErrUnknownFileType { fmt.Printf("[err] %s: %s\n", err, script.name) continue } // Oops :( panic(err) } // Script's own name str := strings.TrimSpace(string(data)) fmt.Println(str) }
Output: test-file.py [err] unknown filetype: test-file.txt test-file.sh test-file.scpt [err] unknown filetype: test-file.doc
func RunAS ¶ added in v0.15.0
RunAS executes AppleScript and returns the output.
Example ¶
// Some test words data := []string{ "Hello, AppleScript!", `"Just Do It!"`, `He said, "I'm fine!" then died :(`, `"`, } for _, input := range data { // Simple script to return input // QuoteAS adds quotation marks, so don't add any more quoted := QuoteAS(input) script := "return " + quoted // Run script and collect result output, err := RunAS(script) if err != nil { // handle error } fmt.Printf("> %s\n", input) fmt.Printf("< %s\n", output) }
Output: > Hello, AppleScript! < Hello, AppleScript! > "Just Do It!" < "Just Do It!" > He said, "I'm fine!" then died :( < He said, "I'm fine!" then died :( > " < "
func RunCmd ¶ added in v0.15.0
RunCmd executes a command and returns its output.
The main difference to exec.Cmd.Output() is that RunCmd writes all STDERR output to the log if a command fails.
func RunJS ¶ added in v0.15.0
RunJS executes JavaScript (JXA) and returns the output.
Example (Arguments) ¶
You can pass additional arguments to your scripts.
// Some test values argv := []string{"angular", "react", "vue"} script := `function run(argv) { return argv.join('\n') }` output, err := RunJS(script, argv...) if err != nil { // handle error } fmt.Println(output)
Output: angular react vue
func Timed ¶ added in v0.15.0
Timed logs the duration since start & title. Use it with defer.
func doSomething() { defer Timed(time.Now(), "long running task") // do thing here // and another thing } // Output: ... long running task
Example ¶
Timed logs the execution duration of a function with a message. Call with defer and time.Now().
doThing := func() { // defer Timed(time.Now(), "long-running thing") fmt.Printf("doing long-running thing ...") // simulate work time.Sleep(time.Second) } // Call function. NOTE: the output from deferred functions is not // captured. doThing()
Output: doing long-running thing ...
Types ¶
type ExecRunner ¶ added in v0.15.0
type ExecRunner struct{}
ExecRunner implements Runner for executable files.
func (ExecRunner) CanRun ¶ added in v0.15.0
func (r ExecRunner) CanRun(filename string) bool
CanRun returns true if file exists and is executable.
type Runner ¶ added in v0.15.0
type Runner interface { // Can Runner execute this (type of) file? CanRun(filename string) bool // Cmd that executes file (via Runner's execution mechanism). Cmd(filename string, args ...string) *exec.Cmd }
Runner knows how to execute a file passed to it. It is used by Run to determine how to run a file.
When Run is passed a filepath, it asks each registered Runner in turn whether it can handle the file.
var ( Executable Runner // run executable files directly Script Runner // run script files with commands from Interpreters // DefaultInterpreters maps script file extensions to interpreters. // Used by the Script Runner (and by extension Run()) to determine // how to run files that aren't executable. DefaultInterpreters = map[string][]string{ ".py": {"/usr/bin/python"}, ".rb": {"/usr/bin/ruby"}, ".sh": {"/bin/bash"}, ".zsh": {"/bin/zsh"}, ".scpt": {"/usr/bin/osascript"}, ".scptd": {"/usr/bin/osascript"}, ".applescript": {"/usr/bin/osascript"}, ".js": {"/usr/bin/osascript", "-l", "JavaScript"}, } )
Default Runners used by Run to determine how to execute a file.
type Runners ¶ added in v0.15.0
type Runners []Runner
Runners implements Runner over a sequence of Runner objects.
func (Runners) CanRun ¶ added in v0.15.0
CanRun returns true if one of the runners can run this file.
type ScriptRunner ¶ added in v0.15.0
type ScriptRunner struct { // Interpreters is an "extension: command" mapping of file extensions // to commands to invoke interpreters that can run the files. // // Interpreters = map[string][]string{ // ".py": []string{"/usr/bin/python"}, // ".rb": []string{"/usr/bin/ruby"}, // } // Interpreters map[string][]string }
ScriptRunner implements Runner for the specified file extensions. It calls the given script with the interpreter command from Interpreters.
A ScriptRunner (combined with Runners, which implements Run) is a useful base for adding support for running scripts to your own program.
func NewScriptRunner ¶ added in v0.15.0
func NewScriptRunner(interpreters map[string][]string) *ScriptRunner
NewScriptRunner creates a new ScriptRunner for interpreters.
func (ScriptRunner) CanRun ¶ added in v0.15.0
func (r ScriptRunner) CanRun(filename string) bool
CanRun returns true if file exists and its extension is in Interpreters.