///|
pub(all) enum ShellDialect {
BashDialect
PowerShellDialect
} derive(Debug, Eq, ToJson)
///|
pub(all) struct RunnerScript {
dialect : ShellDialect
selection : SelectionMode
mutant_count : Int
content : String
} derive(Debug, Eq, ToJson)
///|
pub fn shell_dialect_label(dialect : ShellDialect) -> String {
match dialect {
BashDialect => "bash"
PowerShellDialect => "powershell"
}
}
///|
pub fn generate_runner_script(
plan : ExecutionPlan,
dialect : ShellDialect,
) -> RunnerScript {
render_runner_script(plan, plan.executions, AllMutants, dialect)
}
///|
pub fn generate_selected_runner_script(
plan : ExecutionPlan,
mode : SelectionMode,
dialect : ShellDialect,
) -> RunnerScript {
render_runner_script(plan, select_executions(plan, mode), mode, dialect)
}
///|
pub fn format_runner_script(script : RunnerScript) -> String {
script.content
}
///|
fn render_runner_script(
plan : ExecutionPlan,
executions : ArrayView[MutationExecution],
mode : SelectionMode,
dialect : ShellDialect,
) -> RunnerScript {
let content = match dialect {
BashDialect => render_bash_script(plan, executions, mode)
PowerShellDialect => render_powershell_script(plan, executions, mode)
}
{ dialect, selection: mode, mutant_count: executions.length(), content }
}
///|
fn render_bash_script(
plan : ExecutionPlan,
executions : ArrayView[MutationExecution],
mode : SelectionMode,
) -> String {
let lines : Array[String] = [
"#!/usr/bin/env bash",
"set -euo pipefail",
"",
"# Generated by moon-mutest.",
"# Selection: \{format_selection_mode(mode)}",
"# Mutants: \{executions.length()} of \{plan.executions.length()} planned executions",
"",
"echo " + bash_quote("moon-mutest baseline"),
]
for command in plan.config.commands {
append_bash_command(lines, command, fail_fast=plan.config.fail_fast)
}
for execution in executions {
append_bash_mutant(lines, execution, fail_fast=plan.config.fail_fast)
}
lines.join("\n")
}
///|
fn render_powershell_script(
plan : ExecutionPlan,
executions : ArrayView[MutationExecution],
mode : SelectionMode,
) -> String {
let lines : Array[String] = [
"$ErrorActionPreference = 'Stop'",
"",
"# Generated by moon-mutest.",
"# Selection: \{format_selection_mode(mode)}",
"# Mutants: \{executions.length()} of \{plan.executions.length()} planned executions",
"",
"Write-Host " + powershell_quote("moon-mutest baseline"),
]
for command in plan.config.commands {
append_powershell_command(lines, command, fail_fast=plan.config.fail_fast)
}
for execution in executions {
append_powershell_mutant(lines, execution, fail_fast=plan.config.fail_fast)
}
lines.join("\n")
}
///|
fn append_bash_mutant(
lines : Array[String],
execution : MutationExecution,
fail_fast~ : Bool,
) -> Unit {
let mutation = execution.mutation
lines.push("")
lines.push(
"# mutant #\{mutation.global_id} \{mutation.file}:" +
"\{execution.edit.line}:\{execution.edit.column} " +
"\{mutation.candidate.rule.label}",
)
lines.push("# apply: \{format_text_edit(execution.edit)}")
for command in execution.commands {
append_bash_command(lines, command, fail_fast~)
}
lines.push("# restore: \{format_text_edit(execution.restore_edit)}")
}
///|
fn append_powershell_mutant(
lines : Array[String],
execution : MutationExecution,
fail_fast~ : Bool,
) -> Unit {
let mutation = execution.mutation
lines.push("")
lines.push(
"# mutant #\{mutation.global_id} \{mutation.file}:" +
"\{execution.edit.line}:\{execution.edit.column} " +
"\{mutation.candidate.rule.label}",
)
lines.push("# apply: \{format_text_edit(execution.edit)}")
for command in execution.commands {
append_powershell_command(lines, command, fail_fast~)
}
lines.push("# restore: \{format_text_edit(execution.restore_edit)}")
}
///|
fn append_bash_command(
lines : Array[String],
command : RunnerCommand,
fail_fast~ : Bool,
) -> Unit {
lines.push("echo " + bash_quote("+ " + command.command))
if fail_fast {
lines.push(command.command)
} else {
lines.push(command.command + " || true")
}
}
///|
fn append_powershell_command(
lines : Array[String],
command : RunnerCommand,
fail_fast~ : Bool,
) -> Unit {
lines.push("Write-Host " + powershell_quote("+ " + command.command))
lines.push(command.command)
if fail_fast {
lines.push("if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }")
}
}
///|
fn format_text_edit(edit : TextEdit) -> String {
"\{edit.file}[\{edit.start}..\{edit.end}] " +
shell_comment_quote(edit.original) +
" -> " +
shell_comment_quote(edit.replacement)
}
///|
fn shell_comment_quote(text : String) -> String {
"`" + text.replace(old="\n", new="\\n") + "`"
}
///|
fn bash_quote(text : String) -> String {
"'" + text.replace(old="'", new="'\"'\"'") + "'"
}
///|
fn powershell_quote(text : String) -> String {
"'" + text.replace(old="'", new="''") + "'"
}