///|
fn uniq_command() -> @argparse.Command {
Command(
"uniq",
about="Filter adjacent repeated lines.",
flags=[
FlagArg("count", short='c', about="Prefix lines with occurrence counts."),
FlagArg("repeated", short='d', about="Only print lines that repeat."),
FlagArg("unique", short='u', about="Only print lines that do not repeat."),
FlagArg("ignore-case", short='i', about="Compare case-insensitively."),
],
positionals=[
PositionArg(
"input",
about="[file] ('-' or no file reads stdin)",
num_args=ValueRange(lower=0, upper=1),
),
],
disable_help_subcommand=true,
)
}
///|
fn split_lines(text : String) -> Array[String] {
if text is "" {
return []
}
let lines : Array[String] = text.split("\n").map(v => v.to_owned()).collect()
if text.has_suffix("\n") {
ignore(lines.pop())
}
lines
}
///|
fn pad_left(text : String, width : Int) -> String {
let sb = StringBuilder()
for _ in 0..<(width - text.length()) {
sb.write_char(' ')
}
sb.write_string(text)
sb.to_string()
}
///|
async fn read_source_text(path : String) -> String {
if path == "-" {
@stdio.stdin.read_all().text()
} else {
@fs.read_file_to_string(path)
}
}
///|
async fn main {
let args = @env.args()[1:]
let command = uniq_command()
let matches = command.parse(argv=args, env=Map([])) catch {
err => {
@stdio.stderr.write("\{err}\n")
@sys.exit(2)
return
}
}
let show_count = matches.flags.get_or_default("count", false)
let only_repeated = matches.flags.get_or_default("repeated", false)
let only_unique = matches.flags.get_or_default("unique", false)
let ignore_case = matches.flags.get_or_default("ignore-case", false)
let inputs = matches.values.get("input").unwrap_or([])
let path = if inputs.is_empty() { "-" } else { inputs[0] }
let text = read_source_text(path) catch {
err => {
@stdio.stderr.write("uniq: \{err}\n")
@sys.exit(1)
return
}
}
let lines = split_lines(text)
// Each group is the first line of an adjacent run plus its length.
let groups : Array[(String, Int)] = []
for line in lines {
let key = if ignore_case { line.to_lower() } else { line }
if groups.length() > 0 {
let (last, count) = groups[groups.length() - 1]
let last_key = if ignore_case { last.to_lower() } else { last }
if last_key == key {
groups[groups.length() - 1] = (last, count + 1)
continue
}
}
groups.push((line, 1))
}
for group in groups {
let (line, count) = group
let selected = if only_repeated && only_unique {
false
} else if only_repeated {
count > 1
} else if only_unique {
count == 1
} else {
true
}
if selected {
if show_count {
@stdio.stdout.write(pad_left(count.to_string(), 7) + " " + line + "\n")
} else {
@stdio.stdout.write(line + "\n")
}
}
}
}