-
Notifications
You must be signed in to change notification settings - Fork 1k
/
MainAnnotation.scala
129 lines (116 loc) · 5.31 KB
/
MainAnnotation.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package scala.annotation
/** MainAnnotation provides the functionality for a compiler-generated main class.
* It links a compiler-generated main method (call it compiler-main) to a user
* written main method (user-main).
* The protocol of calls from compiler-main is as follows:
*
* - create a `command` with the command line arguments,
* - for each parameter of user-main, a call to `command.argGetter`,
* or `command.varargGetter` if is a final varargs parameter,
* - a call to `command.run` with the closure of user-main applied to all arguments.
*
* Example:
* ```scala sc:nocompile
* /** Sum all the numbers
* *
* * @param first Fist number to sum
* * @param rest The rest of the numbers to sum
* */
* @myMain def sum(first: Int, second: Int = 0, rest: Int*): Int = first + second + rest.sum
* ```
* generates
* ```scala sc:nocompile
* object foo {
* def main(args: Array[String]): Unit = {
* val mainAnnot = new myMain()
* val info = new Info(
* name = "foo.main",
* documentation = "Sum all the numbers",
* parameters = Seq(
* new Parameter("first", "scala.Int", hasDefault=false, isVarargs=false, "Fist number to sum"),
* new Parameter("rest", "scala.Int" , hasDefault=false, isVarargs=true, "The rest of the numbers to sum")
* )
* )
* val mainArgsOpt = mainAnnot.command(info, args)
* if mainArgsOpt.isDefined then
* val mainArgs = mainArgsOpt.get
* val args0 = mainAnnot.argGetter[Int](info.parameters(0), mainArgs(0), None) // using parser Int
* val args1 = mainAnnot.argGetter[Int](info.parameters(1), mainArgs(1), Some(() => sum$default$1())) // using parser Int
* val args2 = mainAnnot.varargGetter[Int](info.parameters(2), mainArgs.drop(2)) // using parser Int
* mainAnnot.run(() => sum(args0(), args1(), args2()*))
* }
* }
* ```
*
* @param Parser The class used for argument string parsing and arguments into a `T`
* @param Result The required result type of the main method.
* If this type is Any or Unit, any type will be accepted.
*/
@experimental
trait MainAnnotation[Parser[_], Result] extends StaticAnnotation:
import MainAnnotation.{Info, Parameter}
/** Process the command arguments before parsing them.
*
* Return `Some` of the sequence of arguments that will be parsed to be passed to the main method.
* This sequence needs to have the same length as the number of parameters of the main method (i.e. `info.parameters.size`).
* If there is a varags parameter, then the sequence must be at least of length `info.parameters.size - 1`.
*
* Returns `None` if the arguments are invalid and parsing and run should be stopped.
*
* @param info The information about the command (name, documentation and info about parameters)
* @param args The command line arguments
*/
def command(info: Info, args: Seq[String]): Option[Seq[String]]
/** The getter for the `idx`th argument of type `T`
*
* @param idx The index of the argument
* @param defaultArgument Optional lambda to instantiate the default argument
*/
def argGetter[T](param: Parameter, arg: String, defaultArgument: Option[() => T])(using Parser[T]): () => T
/** The getter for a final varargs argument of type `T*` */
def varargGetter[T](param: Parameter, args: Seq[String])(using Parser[T]): () => Seq[T]
/** Run `program` if all arguments are valid if all arguments are valid
*
* @param program A function containing the call to the main method and instantiation of its arguments
*/
def run(program: () => Result): Unit
end MainAnnotation
@experimental
object MainAnnotation:
/** Information about the main method
*
* @param name The name of the main method
* @param documentation The documentation of the main method without the `@param` documentation (see Parameter.documentaion)
* @param parameters Information about the parameters of the main method
*/
@experimental // MiMa does not check scope inherited @experimental
final class Info(
val name: String,
val documentation: String,
val parameters: Seq[Parameter],
):
/** If the method ends with a varargs parameter */
def hasVarargs: Boolean = parameters.nonEmpty && parameters.last.isVarargs
end Info
/** Information about a parameter of a main method
*
* @param name The name of the parameter
* @param typeName The name of the parameter's type
* @param hasDefault If the parameter has a default argument
* @param isVarargs If the parameter is a varargs parameter (can only be true for the last parameter)
* @param documentation The documentation of the parameter (from `@param` documentation in the main method)
* @param annotations The annotations of the parameter that extend `ParameterAnnotation`
*/
@experimental // MiMa does not check scope inherited @experimental
final class Parameter(
val name: String,
val typeName: String,
val hasDefault: Boolean,
val isVarargs: Boolean,
val documentation: String,
val annotations: Seq[ParameterAnnotation],
)
/** Marker trait for annotations that will be included in the Parameter annotations. */
@experimental // MiMa does not check scope inherited @experimental
trait ParameterAnnotation extends StaticAnnotation
end MainAnnotation