-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathleet.scala
executable file
·274 lines (209 loc) · 5.25 KB
/
leet.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
import scala.collection.immutable.HashMap
import scala.annotation.tailrec
import java.io.{ File, PrintWriter }
object Main {
private final val README_PATH: String =
"../../README.md"
private final val DIFFICULTIES: Set[String] =
Set("E", "M", "H")
private final val EASY_HEADER: String = "## Easy"
private final val MEDIUM_HEADER: String = "## Medium"
private final val HARD_HEADER: String = "## Hard"
private final val DIFF_MAP: HashMap[String, String] =
HashMap(
"E" -> EASY_HEADER,
"M" -> MEDIUM_HEADER,
"H" -> HARD_HEADER
)
private def exitIf(cond: Boolean, msg: String): Unit = {
if (cond) {
sys.error(msg)
sys.exit(1)
}
}
private def extractProblemNumber(title: String): Option[Int] = {
f"^[0-9]+"
.r
.findFirstIn(title) match {
case Some(nr) => Some(nr.toInt)
case None => None
}
}
private def updateREADME(title: String, number: Int, difficulty: String): Unit = {
val source = scala.io.Source.fromFile(README_PATH)
val SOURCE_LINES: List[String] =
source
.getLines()
.toList
val HEADER: String = DIFF_MAP.getOrElse(difficulty, "")
def getLineNrForHeader(header: String): Int = {
SOURCE_LINES
.indexWhere(_ == header)
}
def getNextHeaderIndex(header: String): Int = {
if (header == EASY_HEADER)
getLineNrForHeader(MEDIUM_HEADER)
else if (header == MEDIUM_HEADER)
getLineNrForHeader(HARD_HEADER)
else
SOURCE_LINES.length
}
val START_INDEX: Int = getLineNrForHeader(HEADER) + 2
def END_INDEX: Int =
getNextHeaderIndex(HEADER) -
{
if (HEADER == HARD_HEADER) 0
else 1
}
val LINES_OF_INTEREST: List[String] =
SOURCE_LINES
.slice(START_INDEX, END_INDEX)
var isLastLine: Boolean = false
@tailrec
def searchForInsertIndex(
problemNr: Int = number,
lines: List[String] = LINES_OF_INTEREST,
lastIndex: Int = 0,
): Int = {
if (lines.isEmpty) {
isLastLine = true
lastIndex
}
else if (lines.head.isEmpty)
searchForInsertIndex(
problemNr,
lines.tail,
lastIndex + 1
)
else {
val LINE_NR: Option[Int] =
extractProblemNumber(lines.head.tail)
LINE_NR match {
case Some(nr) => {
exitIf(
nr == problemNr,
"[ERROR]: Problem is already present"
)
if (nr > problemNr) {
lastIndex
}
else
searchForInsertIndex(
problemNr,
lines.tail,
lastIndex + 1
)
}
case None =>
searchForInsertIndex(
problemNr,
lines.tail,
lastIndex + 1
)
}
}
}
def getLineMD: String = {
val ABSOLUTE_SOLUTION_PATH: String =
System.getProperty("user.dir")
def getRelativePathToReadMe(absPath: String = ABSOLUTE_SOLUTION_PATH): String = {
val SRC_DIR: String = "src"
val SRC_INDEX: Int =
absPath
.split("/")
.indexWhere(_ == SRC_DIR)
absPath
.split("/")
.zipWithIndex
.filter(
pr => {
pr._2 >= SRC_INDEX
}
)
.map(_._1)
.mkString("/")
}
val RELATIVE_SOLUTION_PATH: String =
getRelativePathToReadMe()
val FILE_NAMES: Array[String] =
(new File(ABSOLUTE_SOLUTION_PATH))
.listFiles
.map(_.getName)
exitIf(
FILE_NAMES.isEmpty,
"[ERROR]: No solution provided"
)
val SOLUTION_FILE_NAME: String =
FILE_NAMES.head
f"[$title]($RELATIVE_SOLUTION_PATH/$SOLUTION_FILE_NAME)" ++
{
if (isLastLine) ""
else " \\"
}
}
val RELATIVE_INSERT_INDEX: Int =
searchForInsertIndex()
val PROBLEM_LINE_MD: String =
getLineMD
val LINES_OF_INTEREST_UPDATED: List[String] =
if (isLastLine) {
LINES_OF_INTEREST
.reverse
.tail
.reverse :+
f"${LINES_OF_INTEREST.last} \\"
}
else LINES_OF_INTEREST
val UPDATED_SECTION: List[String] =
((LINES_OF_INTEREST_UPDATED.slice(0, RELATIVE_INSERT_INDEX) :+ PROBLEM_LINE_MD) ++
LINES_OF_INTEREST_UPDATED.slice(RELATIVE_INSERT_INDEX, LINES_OF_INTEREST_UPDATED.length))
val UPDATED_README: String =
(
SOURCE_LINES
.slice(0, START_INDEX) ++
UPDATED_SECTION ++
SOURCE_LINES
.slice(END_INDEX, SOURCE_LINES.length)
)
.mkString("\n")
new PrintWriter(README_PATH) {
try write(UPDATED_README)
finally close()
}
println("[UPDATE]: README locally updated")
source.close()
}
def pushToGit(number: Int): Unit = {
import sys.process._
f"git add ${README_PATH}".!!
f"git add ${System.getProperty("user.dir")}".!!
f"git commit -m \"added $number\"".!!
"git push -q -f origin main".!!
println("[UPDATE]: Solution pushed")
}
def main(args: Array[String]): Unit = {
exitIf(
args.length != 2,
"[USAGE]: scala-cli ../../leet.scala -- \"<PROBLEM_NUMBER>. <PROBLEM_TITLE>\" \"<DIFFICULTY>\""
)
val PROBLEM_TITLE: String = args(0)
val PROBLEM_NUMBER: Int =
extractProblemNumber(PROBLEM_TITLE) match {
case Some(nr) => nr
case None => {
exitIf(
true,
"[USAGE]: first argument should be in the following format: \"<PROBLEM_NR>. <PROBLEM_TITLE>\""
)
-1
}
}
val DIFFICULTY: String = args(1)
exitIf(
!DIFFICULTIES.contains(DIFFICULTY),
"[USAGE]: second argument should be E (EASY), M (MEDIUM) or H (HARD)"
)
updateREADME(PROBLEM_TITLE, PROBLEM_NUMBER, DIFFICULTY)
pushToGit(PROBLEM_NUMBER)
}
}