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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
type ExecRunner struct{}
ExecRunner implements Runner for executable files.
func (ExecRunner) CanRun ¶
func (r ExecRunner) CanRun(filename string) bool
CanRun returns true if file exists and is executable.
type Runner ¶
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/python3"}, ".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 ¶
type Runners []Runner
Runners implements Runner over a sequence of Runner objects.
type ScriptRunner ¶
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 ¶
func NewScriptRunner(interpreters map[string][]string) *ScriptRunner
NewScriptRunner creates a new ScriptRunner for interpreters.
func (ScriptRunner) CanRun ¶
func (r ScriptRunner) CanRun(filename string) bool
CanRun returns true if file exists and its extension is in Interpreters.