@@ -4,9 +4,12 @@ import akka.actor.typed.ActorSystem
4
4
import akka .actor .typed .scaladsl .Behaviors
5
5
import akka .event .Logging
6
6
import akka .stream .Attributes
7
+ import ch .qos .logback .classic .LoggerContext
7
8
import com .github .invis1ble .whatismyip .info .providers .MyipcomInfoProvider
8
9
import org .slf4j .LoggerFactory
10
+ import scopt .{DefaultOEffectSetup , DefaultOParserSetup , OParser }
9
11
12
+ import java .io .{File , PrintWriter }
10
13
import scala .concurrent .ExecutionContextExecutor
11
14
import scala .concurrent .duration .DurationInt
12
15
@@ -16,6 +19,11 @@ object App {
16
19
def main (args : Array [String ]): Unit = {
17
20
logger.info(" App started" )
18
21
22
+ val config = parseArgs(args) match {
23
+ case Some (config) => config
24
+ case _ => terminate(); sys.exit(1 )
25
+ }
26
+
19
27
implicit val system : ActorSystem [Nothing ] = ActorSystem (Behaviors .empty, " WhatIsMyIpAddress" )
20
28
implicit val ec : ExecutionContextExecutor = system.executionContext
21
29
@@ -25,11 +33,70 @@ object App {
25
33
onFinish = Logging .InfoLevel ,
26
34
onFailure = Logging .ErrorLevel ,
27
35
))
28
- .runForeach {
36
+ .map {
29
37
case Some (info) =>
30
38
val ccLabel = info.location.flatMap(_.countryCode).getOrElse(" n/a" )
31
- println( s " [ $ccLabel] ${info.address}" )
32
- case _ => println( " n/a" )
39
+ s " [ $ccLabel] ${info.address}"
40
+ case _ => " n/a"
33
41
}
42
+ .runForeach(writeln(_, config.file))
43
+ }
44
+
45
+ private def writeln (str : String , file : Option [File ] = None ): Unit = {
46
+ file match {
47
+ case Some (file) =>
48
+ val writer = new PrintWriter (file)
49
+ writer.println(str)
50
+ writer.close()
51
+ case None => println(str)
52
+ }
53
+ }
54
+
55
+ private def parseArgs (args : Array [String ]): Option [Config ] = {
56
+ OParser .runParser(createParser, args, Config (), new DefaultOParserSetup {
57
+ override def showUsageOnError : Some [Boolean ] = Some (true )
58
+ }) match {
59
+ case (result, effects) =>
60
+ OParser .runEffects(effects, new DefaultOEffectSetup {
61
+ override def terminate (exitState : Either [String , Unit ]): Unit = {
62
+ App .terminate()
63
+ super .terminate(exitState)
64
+ }
65
+ })
66
+ result
67
+ }
68
+ }
69
+
70
+ private def createParser : OParser [_, Config ] = {
71
+ val builder = OParser .builder[Config ]
72
+ import builder ._
73
+
74
+ OParser .sequence(
75
+ programName(" whatismyip" ),
76
+ head(" whatismyip" , " 1.2.0" ),
77
+ opt[Option [File ]]('f' , " file" )
78
+ .action((x, c) => c.copy(file = x))
79
+ .validate {
80
+ case Some (r) =>
81
+ if (r.exists) {
82
+ if (r.isFile) {
83
+ if (! r.canWrite) failure(s """ Path " ${r.getAbsolutePath}" must be writable by current user """ )
84
+ } else {
85
+ failure(s """ Path " ${r.getAbsolutePath}" is not a file """ )
86
+ }
87
+ }
88
+ success
89
+ case None => success
90
+ }
91
+ .text(" Output file (will be created if not exists)" ),
92
+ help(" help" ).text(" prints this usage text" ),
93
+ version(" version" )
94
+ )
95
+ }
96
+
97
+ private def terminate (): Unit = {
98
+ LoggerFactory .getILoggerFactory match {
99
+ case ctx : LoggerContext => ctx.stop()
100
+ }
34
101
}
35
102
}
0 commit comments