util

package
v0.29.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2021 License: MIT Imports: 17 Imported by: 9

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

Examples

Constants

This section is empty.

Variables

View Source
var ErrUnknownFileType = errors.New("unknown filetype")

ErrUnknownFileType is returned by Run for files it can't identify.

Functions

func ClearDirectory

func ClearDirectory(p string) error

ClearDirectory deletes all files within a directory, but not directory itself.

func MustExist added in v0.10.1

func MustExist(dirpath ...string) string

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

func Pad(str, pad string, n int) string

Pad pads str to length n by adding pad to both ends.

Example
fmt.Println(Pad("wow", "-", 10))
Output:

---wow----

func PadLeft

func PadLeft(str, pad string, n int) string

PadLeft pads str to length n by adding pad to its left.

Example
fmt.Println(PadLeft("wow", "-", 5))
Output:

--wow

func PadRight

func PadRight(str, pad string, n int) string

PadRight pads str to length n by adding pad to its right.

Example
fmt.Println(PadRight("wow", "-", 5))
Output:

wow--

func PathExists

func PathExists(path string) bool

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

func PrettyPath(path string) string

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

func QuoteAS(s string) string

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

func Run(filename string, args ...string) ([]byte, error)

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

func RunAS(script string, args ...string) (string, error)

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

func RunCmd(cmd *exec.Cmd) ([]byte, error)

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

func RunJS(script string, args ...string) (string, error)

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 Slugify added in v0.20.0

func Slugify(s string) string

Slugify makes a string filesystem- and URL-safe.

func Timed added in v0.15.0

func Timed(start time.Time, title string)

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 ...

func WriteFile added in v0.17.0

func WriteFile(filename string, data []byte, perm os.FileMode) error

WriteFile is an atomic version of ioutil.WriteFile. It first writes data to a temporary file and renames this to filename if the write is successful.

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.

func (ExecRunner) Cmd added in v0.15.0

func (r ExecRunner) Cmd(executable string, args ...string) *exec.Cmd

Cmd returns a Cmd to run executable with args.

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

func (rs Runners) CanRun(filename string) bool

CanRun returns true if one of the runners can run this file.

func (Runners) Cmd added in v0.15.0

func (rs Runners) Cmd(filename string, args ...string) *exec.Cmd

Cmd returns a command to run the (script) file.

func (Runners) Run added in v0.15.0

func (rs Runners) Run(filename string, args ...string) ([]byte, error)

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.

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.

func (ScriptRunner) Cmd added in v0.15.0

func (r ScriptRunner) Cmd(filename string, args ...string) *exec.Cmd

Cmd returns a Cmd to run filename with its interpreter.

Directories

Path Synopsis
Package build provides helpers for developing and building workflows.
Package build provides helpers for developing and building workflows.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL