Skip to content

Commit

Permalink
feat: implement stdin option
Browse files Browse the repository at this point in the history
  • Loading branch information
vlmaier committed Jan 4, 2024
1 parent 0a64dfa commit 702edf7
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 46 deletions.
57 changes: 31 additions & 26 deletions src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,53 +1,58 @@
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.optional
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.help
import com.github.ajalt.clikt.parameters.options.option
import java.io.InputStreamReader
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.Path

class WordCount : CliktCommand(name = "wk") {
private val countBytes by option("-c").flag()
.help { "The number of bytes in the input file is written to the standard output." }
.help { "The number of bytes in the input file or stdin is written to the stdout." }
private val countLines by option("-l").flag()
.help { "The number of lines in the input file is written to the standard output." }
.help { "The number of lines in the input file or stdin is written to the stdout." }
private val countCharacters by option("-m").flag()
.help { "The number of characters in the input file is written to the standard output. " +
.help { "The number of characters in the input file or stdin is written to the stdout. " +
"If the current locale does not support multibyte characters, this is equivalent to the -c option. " +
"This will cancel out any prior usage of the -c option." }
private val countWords by option("-w").flag()
.help { "The number of words in the input file is written to the standard output." }
private val fileName by argument()
.help { "The number of words in the input file or stdin is written to the stdout." }
private val fileName by argument().optional()

override fun run() {
val filePath = Path.of(fileName)
val counter = when {
countBytes -> countBytes(filePath)
countLines -> countLines(filePath)
countWords -> countWords(filePath)
countCharacters -> countCharacters(filePath)
else -> defaultCount(filePath)
val bufferedReader = if (fileName.isNullOrBlank()) {
InputStreamReader(System.`in`).buffered()
} else {
Files.newBufferedReader(Path.of(fileName!!))
}
bufferedReader.use {
val content = it.readText()
val counter = when {
countBytes -> countBytes(content)
countLines -> countLines(content)
countWords -> countWords(content)
countCharacters -> countCharacters(content)
else -> defaultCount(content)
}
echo(" $counter ${fileName ?: ""}".trimEnd())
}
echo(" $counter $fileName")
}

private fun defaultCount(filePath: Path): String {
val lines = countLines(filePath)
val words = countWords(filePath)
val characters = countCharacters(filePath)
return "$lines $words $characters"
}
private fun defaultCount(content: String) =
"${countLines(content)} ${countWords(content)} ${countCharacters(content)}"

private fun countBytes(filePath: Path): Int = Files.readAllBytes(filePath).size
private fun countLines(filePath: Path): Int = Files.readAllLines(filePath).size
private fun countWords(filePath: Path): Int = Files.readString(filePath).trim().split("\\s+".toRegex()).size
private fun countBytes(content: String) = content.toByteArray(Charsets.UTF_8).size
private fun countLines(content: String) = content.split("\n").size - 1
private fun countWords(content: String) = content.trim().split("\\s+".toRegex()).size

private fun countCharacters(filePath: Path): Int {
return if (Charset.defaultCharset().displayName().contains("UTF-8", ignoreCase = true)) {
Files.readString(filePath).length
private fun countCharacters(content: String): Int {
return if (Charset.defaultCharset().displayName().contains(Charsets.UTF_8.name(), ignoreCase = true)) {
content.length
} else {
countBytes(filePath)
countBytes(content)
}
}
}
Expand Down
28 changes: 12 additions & 16 deletions src/test/kotlin/WordCountTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ class WordCountTests {
fun `test help output`() {
val result = wk.test("-h")
assertEquals("""
Usage: wk [<options>] <filename>
Usage: wk [<options>] [<filename>]
Options:
-c The number of bytes in the input file is written to the standard
output.
-l The number of lines in the input file is written to the standard
output.
-m The number of characters in the input file is written to the
standard output. If the current locale does not support multibyte
-c The number of bytes in the input file or stdin is written to the
stdout.
-l The number of lines in the input file or stdin is written to the
stdout.
-m The number of characters in the input file or stdin is written to
the stdout. If the current locale does not support multibyte
characters, this is equivalent to the -c option. This will cancel
out any prior usage of the -c option.
-w The number of words in the input file is written to the standard
output.
-w The number of words in the input file or stdin is written to the
stdout.
-h, --help Show this message and exit
""".trimStart(), result.stdout)
assertEquals("", result.stderr)
Expand All @@ -39,13 +39,9 @@ Options:
@Test
fun `test missing file name`() {
val result = wk.test("-c")
assertEquals("""
Usage: wk [<options>] <filename>
Error: missing argument <filename>
""".trimStart(), result.stderr)
assertEquals("", result.stdout)
assertEquals(1, result.statusCode)
assertEquals(" 0\n", result.stdout)
assertEquals("", result.stderr)
assertEquals(0, result.statusCode)
}

@Test
Expand Down
16 changes: 12 additions & 4 deletions wk.1
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
.\" Adapted from the example at https://www.systutorials.com/docs/linux/man/7-man-pages/
.TH WK 1 "January 2024" "1.0.0" "wk man page"
.SH NAME
wk \- Count bytes, lines, or words in a file (wc clone written in Kotlin)
wk \- Count bytes, lines, or words in a file or from standard input (wc clone written in Kotlin)
.SH SYNOPSIS
.B wk
.RI [ options ]
.I filename
.I [ filename ]
.SH DESCRIPTION
.B wk
counts the number of bytes, lines, or words in a file and writes the result to the standard output.
Expand All @@ -26,15 +26,23 @@ Count the number of words in the input file.
.SH ARGUMENTS
.TP
.I filename
The path to the input file.
The path to the input file. If not specified, read from standard input.
.SH EXAMPLES
Count the number of lines in a file:
.PP
.B wk -l myfile.txt
.PP
Count the number of bytes from standard input:
.PP
.B echo Hello, World! | wk -c
.SH SEE ALSO
.BR wc (1)
.SH AUTHOR
Vladas Maier <vladas.mr@gmail.com>.
.SH COPYRIGHT
Copyright © 2024 Vladas Maier. This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
Copyright © 2024 Vladas Maier.
.PP
This is free software; you are free to change and redistribute it.
.PP
There is NO WARRANTY, to the extent permitted by law.
.\" End of man page

0 comments on commit 702edf7

Please sign in to comment.