Skip to content

Commit 979fcdf

Browse files
committed
Update docs for isStdin/isStdout
1 parent 07b8c64 commit 979fcdf

File tree

6 files changed

+101
-80
lines changed

6 files changed

+101
-80
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
### Added
6+
- `InputStream.isCliktParameterDefaultStdin` and `OutputStream.isCliktParameterDefaultStdout` to check if the streams returned from `inputStream`/`outputStream` options are proxying stdin/stdout ([#272](https://github.com/ajalt/clikt/issues/272))
7+
58
### Changed
69
- Make parameters of `mutuallyExclusiveOptions` covariant to allow validation without explicit type annotations. ([#265](https://github.com/ajalt/clikt/issues/265))
710
- Update kotlin to 1.4.30

clikt/src/jvmMain/kotlin/com/github/ajalt/clikt/parameters/types/inputStream.kt

+42-32
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,20 @@ import java.nio.file.FileSystem
1010
import java.nio.file.FileSystems
1111
import java.nio.file.Files
1212

13-
private fun convertToInputStream(s: String, fileSystem: FileSystem, context: Context, fail: (String) -> Unit): InputStream {
14-
return if (s == "-") {
15-
UnclosableInputStream(System.`in`)
16-
} else {
17-
val path = convertToPath(
18-
path = s,
19-
mustExist = true,
20-
canBeFile = true,
21-
canBeFolder = false,
22-
mustBeWritable = false,
23-
mustBeReadable = true,
24-
canBeSymlink = true,
25-
fileSystem = fileSystem,
26-
context = context,
27-
fail = fail
28-
)
29-
Files.newInputStream(path)
30-
}
31-
}
32-
33-
//<editor-fold desc="options">
13+
// region ========== Options ==========
3414

3515
/**
3616
* Convert the option to an [InputStream].
3717
*
3818
* The value given on the command line must be either a path to a readable file, or `-`. If `-` is
3919
* given, stdin will be used.
4020
*
41-
* If stdin is used, the resulting [InputStream] will be a proxy for [System.in] that will not close
21+
* If stdin is used, the resulting [InputStream] will be a proxy for [System. in] that will not close
4222
* the underlying stream. So you can always [close][InputStream.close] the resulting stream without
43-
* worrying about accidentally closing [System.in].
23+
* worrying about accidentally closing [System. in].
4424
*/
4525
fun RawOption.inputStream(
46-
fileSystem: FileSystem = FileSystems.getDefault()
26+
fileSystem: FileSystem = FileSystems.getDefault(),
4727
): NullableOption<InputStream, InputStream> {
4828
return convert({ localization.fileMetavar() }, CompletionCandidates.Path) { s ->
4929
convertToInputStream(s, fileSystem, context) { fail(it) }
@@ -57,21 +37,21 @@ fun NullableOption<InputStream, InputStream>.defaultStdin(): OptionWithValues<In
5737
return default(UnclosableInputStream(System.`in`), "-")
5838
}
5939

60-
//</editor-fold>
61-
//<editor-fold desc="arguments">
40+
// endregion
41+
// region ========== Arguments ==========
6242

6343
/**
6444
* Convert the argument to an [InputStream].
6545
*
6646
* The value given on the command line must be either a path to a readable file, or `-`. If `-` is
6747
* given, stdin will be used.
6848
*
69-
* If stdin is used, the resulting [InputStream] will be a proxy for [System.in] that will not close
49+
* If stdin is used, the resulting [InputStream] will be a proxy for [System. in] that will not close
7050
* the underlying stream. So you can always [close][InputStream.close] the resulting stream without
71-
* worrying about accidentally closing [System.in].
51+
* worrying about accidentally closing [System. in].
7252
*/
7353
fun RawArgument.inputStream(
74-
fileSystem: FileSystem = FileSystems.getDefault()
54+
fileSystem: FileSystem = FileSystems.getDefault(),
7555
): ProcessedArgument<InputStream, InputStream> {
7656
return convert(completionCandidates = CompletionCandidates.Path) { s ->
7757
convertToInputStream(s, fileSystem, context) { fail(it) }
@@ -85,12 +65,40 @@ fun ProcessedArgument<InputStream, InputStream>.defaultStdin(): ArgumentDelegate
8565
return default(UnclosableInputStream(System.`in`))
8666
}
8767

88-
//</editor-fold>
68+
// endregion
8969

9070
/**
91-
* Checks whether this stream is an unclosable [System.`in`] proxy.
71+
* Checks whether this stream was returned from an [inputStream] parameter, and that it is
72+
* reading from [System. in] (because `-` was given, or no value was given and the parameter uses
73+
* [defaultStdin]).
9274
*/
93-
fun InputStream.isCliktParameterDefaultStdin(): Boolean = this is UnclosableInputStream
75+
val InputStream.isCliktParameterDefaultStdin: Boolean
76+
get() = this is UnclosableInputStream
77+
78+
private fun convertToInputStream(
79+
s: String,
80+
fileSystem: FileSystem,
81+
context: Context,
82+
fail: (String) -> Unit,
83+
): InputStream {
84+
return if (s == "-") {
85+
UnclosableInputStream(System.`in`)
86+
} else {
87+
val path = convertToPath(
88+
path = s,
89+
mustExist = true,
90+
canBeFile = true,
91+
canBeFolder = false,
92+
mustBeWritable = false,
93+
mustBeReadable = true,
94+
canBeSymlink = true,
95+
fileSystem = fileSystem,
96+
context = context,
97+
fail = fail
98+
)
99+
Files.newInputStream(path)
100+
}
101+
}
94102

95103
private class UnclosableInputStream(private var delegate: InputStream?) : InputStream() {
96104
private val stream get() = delegate ?: throw IOException("Stream closed")
@@ -108,3 +116,5 @@ private class UnclosableInputStream(private var delegate: InputStream?) : InputS
108116
delegate = null
109117
}
110118
}
119+
120+
// endregion

clikt/src/jvmMain/kotlin/com/github/ajalt/clikt/parameters/types/outputStream.kt

+43-40
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,7 @@ import java.nio.file.FileSystems
1111
import java.nio.file.Files
1212
import java.nio.file.StandardOpenOption.*
1313

14-
private fun convertToOutputStream(
15-
s: String,
16-
createIfNotExist: Boolean,
17-
truncateExisting: Boolean,
18-
fileSystem: FileSystem,
19-
context: Context,
20-
fail: (String) -> Unit
21-
): OutputStream {
22-
return if (s == "-") {
23-
UnclosableOutputStream(System.out)
24-
} else {
25-
val path = convertToPath(
26-
s,
27-
mustExist = !createIfNotExist,
28-
canBeFile = true,
29-
canBeFolder = false,
30-
mustBeWritable = !createIfNotExist,
31-
mustBeReadable = false,
32-
canBeSymlink = true,
33-
fileSystem = fileSystem,
34-
context = context
35-
) { fail(it) }
36-
val openType = if (truncateExisting) TRUNCATE_EXISTING else APPEND
37-
val options = arrayOf(WRITE, CREATE, openType)
38-
Files.newOutputStream(path, *options)
39-
}
40-
}
41-
42-
//<editor-fold desc="options">
14+
// region ========== Options ==========
4315

4416
/**
4517
* Convert the option to an [OutputStream].
@@ -55,9 +27,9 @@ private fun convertToOutputStream(
5527
* @param truncateExisting If true, existing files will be truncated when opened. By default, the file will be appended to.
5628
*/
5729
fun RawOption.outputStream(
58-
createIfNotExist: Boolean = true,
59-
truncateExisting: Boolean = false,
60-
fileSystem: FileSystem = FileSystems.getDefault()
30+
createIfNotExist: Boolean = true,
31+
truncateExisting: Boolean = false,
32+
fileSystem: FileSystem = FileSystems.getDefault(),
6133
): NullableOption<OutputStream, OutputStream> {
6234
return convert({ localization.fileMetavar() }, CompletionCandidates.Path) { s ->
6335
convertToOutputStream(s, createIfNotExist, truncateExisting, fileSystem, context) { fail(it) }
@@ -71,8 +43,8 @@ fun NullableOption<OutputStream, OutputStream>.defaultStdout(): OptionWithValues
7143
return default(UnclosableOutputStream(System.out), "-")
7244
}
7345

74-
//</editor-fold>
75-
//<editor-fold desc="arguments">
46+
// endregion
47+
// region ========== Arguments ==========
7648

7749
/**
7850
* Convert the argument to an [OutputStream].
@@ -88,9 +60,9 @@ fun NullableOption<OutputStream, OutputStream>.defaultStdout(): OptionWithValues
8860
* @param truncateExisting If true, existing files will be truncated when opened. By default, the file will be appended to.
8961
*/
9062
fun RawArgument.outputStream(
91-
createIfNotExist: Boolean = true,
92-
truncateExisting: Boolean = false,
93-
fileSystem: FileSystem = FileSystems.getDefault()
63+
createIfNotExist: Boolean = true,
64+
truncateExisting: Boolean = false,
65+
fileSystem: FileSystem = FileSystems.getDefault(),
9466
): ProcessedArgument<OutputStream, OutputStream> {
9567
return convert(completionCandidates = CompletionCandidates.Path) { s ->
9668
convertToOutputStream(s, createIfNotExist, truncateExisting, fileSystem, context) { fail(it) }
@@ -104,12 +76,43 @@ fun ProcessedArgument<OutputStream, OutputStream>.defaultStdout(): ArgumentDeleg
10476
return default(UnclosableOutputStream(System.out))
10577
}
10678

107-
//</editor-fold>
79+
// endregion
10880

10981
/**
110-
* Checks whether this stream is an unclosable [System.out] proxy.
82+
* Checks whether this stream was returned from an [outputStream] parameter, and that it is
83+
* writing to [System.out] (because `-` was given, or no value was given and the parameter uses
84+
* [defaultStdout]).
11185
*/
112-
fun OutputStream.isCliktParameterDefaultStdout(): Boolean = this is UnclosableOutputStream
86+
val OutputStream.isCliktParameterDefaultStdout: Boolean
87+
get() = this is UnclosableOutputStream
88+
89+
private fun convertToOutputStream(
90+
s: String,
91+
createIfNotExist: Boolean,
92+
truncateExisting: Boolean,
93+
fileSystem: FileSystem,
94+
context: Context,
95+
fail: (String) -> Unit,
96+
): OutputStream {
97+
return if (s == "-") {
98+
UnclosableOutputStream(System.out)
99+
} else {
100+
val path = convertToPath(
101+
s,
102+
mustExist = !createIfNotExist,
103+
canBeFile = true,
104+
canBeFolder = false,
105+
mustBeWritable = !createIfNotExist,
106+
mustBeReadable = false,
107+
canBeSymlink = true,
108+
fileSystem = fileSystem,
109+
context = context
110+
) { fail(it) }
111+
val openType = if (truncateExisting) TRUNCATE_EXISTING else APPEND
112+
val options = arrayOf(WRITE, CREATE, openType)
113+
Files.newOutputStream(path, *options)
114+
}
115+
}
113116

114117
private class UnclosableOutputStream(private var delegate: OutputStream?) : OutputStream() {
115118
private val stream get() = delegate ?: throw IOException("Stream closed")

clikt/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/InputStreamTest.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class InputStreamTest {
7878
val option by option().inputStream(fs).defaultStdin()
7979

8080
override fun run_() {
81-
option.isCliktParameterDefaultStdin().shouldBeTrue()
81+
option.isCliktParameterDefaultStdin.shouldBeTrue()
8282
}
8383
}
8484

@@ -93,7 +93,7 @@ class InputStreamTest {
9393
val option by option().inputStream(fs)
9494

9595
override fun run_() {
96-
option?.isCliktParameterDefaultStdin()?.shouldBeFalse()
96+
option?.isCliktParameterDefaultStdin?.shouldBeFalse()
9797
}
9898
}
9999

@@ -106,7 +106,7 @@ class InputStreamTest {
106106
val stream by argument().inputStream(fs).defaultStdin()
107107

108108
override fun run_() {
109-
stream.isCliktParameterDefaultStdin().shouldBeTrue()
109+
stream.isCliktParameterDefaultStdin.shouldBeTrue()
110110
}
111111
}
112112

@@ -121,7 +121,7 @@ class InputStreamTest {
121121
val stream by argument().inputStream(fs)
122122

123123
override fun run_() {
124-
stream.isCliktParameterDefaultStdin().shouldBeFalse()
124+
stream.isCliktParameterDefaultStdin.shouldBeFalse()
125125
}
126126
}
127127

clikt/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/OutputStreamTest.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class OutputStreamTest {
8484
val option by option().outputStream(fileSystem = fs).defaultStdout()
8585

8686
override fun run_() {
87-
option.isCliktParameterDefaultStdout().shouldBeTrue()
87+
option.isCliktParameterDefaultStdout.shouldBeTrue()
8888
}
8989
}
9090

@@ -97,7 +97,7 @@ class OutputStreamTest {
9797
val option by option().outputStream(fileSystem = fs)
9898

9999
override fun run_() {
100-
option?.isCliktParameterDefaultStdout()?.shouldBeFalse()
100+
option?.isCliktParameterDefaultStdout?.shouldBeFalse()
101101
}
102102
}
103103

@@ -110,7 +110,7 @@ class OutputStreamTest {
110110
val stream by argument().outputStream(fileSystem = fs).defaultStdout()
111111

112112
override fun run_() {
113-
stream.isCliktParameterDefaultStdout().shouldBeTrue()
113+
stream.isCliktParameterDefaultStdout.shouldBeTrue()
114114
}
115115
}
116116

@@ -123,7 +123,7 @@ class OutputStreamTest {
123123
val stream by argument().outputStream(fileSystem = fs)
124124

125125
override fun run_() {
126-
stream.isCliktParameterDefaultStdout().shouldBeFalse()
126+
stream.isCliktParameterDefaultStdout.shouldBeFalse()
127127
}
128128
}
129129

docs/parameters.md

+5
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ reading or writing. They support the unix convention of passing `-` to specify s
145145
rather than a file on the filesystem. You'll need to close the streams yourself. You can also use
146146
[stdin][defaultStdin] or [stdout][defaultStdout] as their default values.
147147

148+
If you need to check if one of these streams is pointing to a file rather than stdin or stdout, you
149+
can use [`isCliktParameterDefaultStdin`][isStdin] or [`isCliktParameterDefaultStdout`][isStdout].
150+
148151
## Custom Types
149152

150153
You can convert parameter values to a custom type by using
@@ -338,6 +341,8 @@ been set, so (unlike in transforms) you can reference other parameters:
338341
[float]: api/clikt/com.github.ajalt.clikt.parameters.types/float.md
339342
[inputStream]: api/clikt/com.github.ajalt.clikt.parameters.types/input-stream.md
340343
[int]: api/clikt/com.github.ajalt.clikt.parameters.types/int.md
344+
[isStdin]: api/clikt/com.github.ajalt.clikt.parameters.types/java.io.-input-stream/is-clikt-parameter-default-stdin.md
345+
[isStdout]: api/clikt/com.github.ajalt.clikt.parameters.types/java.io.-output-stream/is-clikt-parameter-default-stdout.md
341346
[long]: api/clikt/com.github.ajalt.clikt.parameters.types/long.md
342347
[outputStream]: api/clikt/com.github.ajalt.clikt.parameters.types/output-stream.md
343348
[path]: api/clikt/com.github.ajalt.clikt.parameters.types/path.md

0 commit comments

Comments
 (0)