2021-12-29 05:33:38 +02:00
package oscommands
import (
2022-01-03 06:15:26 +02:00
"fmt"
2021-12-29 05:33:38 +02:00
"os"
2023-07-30 11:33:20 +02:00
"os/exec"
2021-12-29 05:33:38 +02:00
"strings"
"github.com/mgutz/str"
)
type ICmdObjBuilder interface {
2023-05-21 09:00:29 +02:00
// NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object.
New ( args [ ] string ) ICmdObj
2021-12-29 05:33:38 +02:00
// NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
NewShell ( commandStr string ) ICmdObj
// Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects.
Quote ( str string ) string
}
type CmdObjBuilder struct {
2022-01-05 03:01:59 +02:00
runner ICmdObjRunner
platform * Platform
2021-12-29 05:33:38 +02:00
}
2021-12-30 04:11:58 +02:00
// poor man's version of explicitly saying that struct X implements interface Y
var _ ICmdObjBuilder = & CmdObjBuilder { }
2023-05-21 09:00:29 +02:00
func ( self * CmdObjBuilder ) New ( args [ ] string ) ICmdObj {
2023-07-30 11:33:20 +02:00
cmd := exec . Command ( args [ 0 ] , args [ 1 : ] ... )
2021-12-29 05:33:38 +02:00
cmd . Env = os . Environ ( )
return & CmdObj {
2022-01-05 02:57:32 +02:00
cmd : cmd ,
runner : self . runner ,
2021-12-29 05:33:38 +02:00
}
}
func ( self * CmdObjBuilder ) NewShell ( commandStr string ) ICmdObj {
2023-03-19 02:20:29 +02:00
var quotedCommand string
2022-01-03 06:15:26 +02:00
// Windows does not seem to like quotes around the command
if self . platform . OS == "windows" {
quotedCommand = strings . NewReplacer (
"^" , "^^" ,
"&" , "^&" ,
"|" , "^|" ,
"<" , "^<" ,
">" , "^>" ,
"%" , "^%" ,
) . Replace ( commandStr )
} else {
quotedCommand = self . Quote ( commandStr )
}
2023-05-21 09:00:29 +02:00
cmdArgs := str . ToArgv ( fmt . Sprintf ( "%s %s %s" , self . platform . Shell , self . platform . ShellArg , quotedCommand ) )
return self . New ( cmdArgs )
2021-12-29 05:33:38 +02:00
}
func ( self * CmdObjBuilder ) CloneWithNewRunner ( decorate func ( ICmdObjRunner ) ICmdObjRunner ) * CmdObjBuilder {
decoratedRunner := decorate ( self . runner )
return & CmdObjBuilder {
2022-01-05 03:01:59 +02:00
runner : decoratedRunner ,
platform : self . platform ,
2021-12-29 05:33:38 +02:00
}
}
2022-01-05 03:01:59 +02:00
2023-05-21 09:00:29 +02:00
const CHARS_REQUIRING_QUOTES = "\"\\$` "
// If you update this method, be sure to update CHARS_REQUIRING_QUOTES
2022-01-05 03:01:59 +02:00
func ( self * CmdObjBuilder ) Quote ( message string ) string {
var quote string
if self . platform . OS == "windows" {
quote = ` \" `
message = strings . NewReplacer (
` " ` , ` "'"'" ` ,
` \" ` , ` \\" ` ,
) . Replace ( message )
} else {
quote = ` " `
message = strings . NewReplacer (
` \ ` , ` \\ ` ,
` " ` , ` \" ` ,
` $ ` , ` \$ ` ,
"`" , "\\`" ,
) . Replace ( message )
}
return quote + message + quote
}