Skip to content

Commit 96b04d6

Browse files
committed
long_about for CLI commands containing the return data JSON schema
1 parent e7b9a6c commit 96b04d6

File tree

8 files changed

+131
-33
lines changed

8 files changed

+131
-33
lines changed

tmc-langs-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ quit = "1"
2222
dirs = "3"
2323
tempfile = "3"
2424
base64 = "0.12"
25+
schemars = "0.7"
2526

2627
[dev-dependencies]
2728
tempfile = "3"

tmc-langs-cli/src/app.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
//! Create clap app
22
33
use clap::{App, Arg, SubCommand};
4+
use schemars::JsonSchema;
5+
use std::path::PathBuf;
6+
use tmc_langs_core::*;
7+
use tmc_langs_framework::domain::*;
48

59
pub fn create_app() -> App<'static, 'static> {
610
App::new(env!("CARGO_PKG_NAME"))
@@ -10,6 +14,7 @@ pub fn create_app() -> App<'static, 'static> {
1014

1115
.subcommand(SubCommand::with_name("checkstyle")
1216
.about("Check code style for the exercise.")
17+
.long_about(SCHEMA_NULL)
1318
.arg(Arg::with_name("exercise-path")
1419
.help("Path to the directory where the project resides.")
1520
.long("exercise-path")
@@ -27,6 +32,7 @@ pub fn create_app() -> App<'static, 'static> {
2732

2833
.subcommand(SubCommand::with_name("compress-project")
2934
.about("Compress target exercise into a ZIP.")
35+
.long_about(SCHEMA_NULL)
3036
.arg(Arg::with_name("exercise-path")
3137
.help("Path to the directory where the exercise resides.")
3238
.long("exercise-path")
@@ -40,6 +46,7 @@ pub fn create_app() -> App<'static, 'static> {
4046

4147
.subcommand(SubCommand::with_name("extract-project")
4248
.about("Extract an exercise archive.")
49+
.long_about(SCHEMA_NULL)
4350
.arg(Arg::with_name("archive-path")
4451
.help("Path to the archive.")
4552
.long("archive-path")
@@ -53,6 +60,7 @@ pub fn create_app() -> App<'static, 'static> {
5360

5461
.subcommand(SubCommand::with_name("prepare-solutions")
5562
.about("Prepare a presentable solution from the original.")
63+
.long_about(SCHEMA_NULL)
5664
.arg(Arg::with_name("exercise-path")
5765
.help("Path to the directory where the exercise resides.")
5866
.long("exercise-path")
@@ -66,6 +74,7 @@ pub fn create_app() -> App<'static, 'static> {
6674

6775
.subcommand(SubCommand::with_name("prepare-stubs")
6876
.about("Prepare a stub exercise from the original.")
77+
.long_about(SCHEMA_NULL)
6978
.arg(Arg::with_name("exercise-path")
7079
.help("Path to the directory where the exercise resides.")
7180
.long("exercise-path")
@@ -79,6 +88,7 @@ pub fn create_app() -> App<'static, 'static> {
7988

8089
.subcommand(SubCommand::with_name("prepare-submission")
8190
.about("Prepares from submission and solution project for which the tests can be run in sandbox.")
91+
.long_about(SCHEMA_NULL)
8292
.arg(Arg::with_name("submission-path")
8393
.help("Path to the zip archive to be submitted.")
8494
.long("submission-path")
@@ -113,6 +123,7 @@ pub fn create_app() -> App<'static, 'static> {
113123

114124
.subcommand(SubCommand::with_name("run-tests")
115125
.about("Run the tests for the exercise.")
126+
.long_about(schema_leaked::<RunResult>())
116127
.arg(Arg::with_name("exercise-path")
117128
.help("Path to the directory where the exercise resides.")
118129
.long("exercise-path")
@@ -134,6 +145,7 @@ pub fn create_app() -> App<'static, 'static> {
134145

135146
.subcommand(SubCommand::with_name("scan-exercise")
136147
.about("Produce an exercise description of an exercise directory.")
148+
.long_about(schema_leaked::<ExerciseDesc>())
137149
.arg(Arg::with_name("exercise-path")
138150
.help("Path to the directory where the project resides.")
139151
.long("exercise-path")
@@ -146,6 +158,7 @@ pub fn create_app() -> App<'static, 'static> {
146158

147159
.subcommand(SubCommand::with_name("find-exercises")
148160
.about("Produce list of found exercises.")
161+
.long_about(schema_leaked::<Vec<PathBuf>>())
149162
.arg(Arg::with_name("exercise-path")
150163
.help("Path to the directory where the project resides.")
151164
.long("exercise-path")
@@ -158,6 +171,7 @@ pub fn create_app() -> App<'static, 'static> {
158171

159172
.subcommand(SubCommand::with_name("get-exercise-packaging-configuration")
160173
.about("Returns configuration of under which folders student and nonstudent files are located.")
174+
.long_about(schema_leaked::<ExercisePackagingConfiguration>())
161175
.arg(Arg::with_name("exercise-path")
162176
.help("Path to the directory where the exercise resides.")
163177
.long("exercise-path")
@@ -170,6 +184,7 @@ pub fn create_app() -> App<'static, 'static> {
170184

171185
.subcommand(SubCommand::with_name("clean")
172186
.about("Clean target directory.")
187+
.long_about(SCHEMA_NULL)
173188
.arg(Arg::with_name("exercise-path")
174189
.help("Path to the directory where the exercise resides.")
175190
.long("exercise-path")
@@ -191,6 +206,7 @@ pub fn create_app() -> App<'static, 'static> {
191206

192207
.subcommand(SubCommand::with_name("login")
193208
.about("Login and store OAuth2 token in config. You can login either by email and password or an access token.")
209+
.long_about(SCHEMA_NULL)
194210
.arg(Arg::with_name("email")
195211
.help("The email address of your TMC account.")
196212
.long("email")
@@ -206,16 +222,20 @@ pub fn create_app() -> App<'static, 'static> {
206222
.long("base64")))
207223

208224
.subcommand(SubCommand::with_name("logout")
209-
.about("Logout and remove OAuth2 token from config."))
225+
.about("Logout and remove OAuth2 token from config.")
226+
.long_about(SCHEMA_NULL))
210227

211228
.subcommand(SubCommand::with_name("logged-in")
212-
.about("Check if the CLI is logged in. Prints the access token if so."))
229+
.about("Check if the CLI is logged in. Prints the access token if so.")
230+
.long_about(SCHEMA_TOKEN))
213231

214232
.subcommand(SubCommand::with_name("get-organizations")
215-
.about("Get organizations."))
233+
.about("Get organizations.")
234+
.long_about(schema_leaked::<Vec<Organization>>()))
216235

217236
.subcommand(SubCommand::with_name("get-organization")
218237
.about("Get organization.")
238+
.long_about(schema_leaked::<Organization>())
219239
.arg(Arg::with_name("organization")
220240
.help("Organization slug (e.g. mooc, hy).")
221241
.long("organization")
@@ -224,6 +244,7 @@ pub fn create_app() -> App<'static, 'static> {
224244

225245
.subcommand(SubCommand::with_name("download-or-update-exercises")
226246
.about("Download exercise.")
247+
.long_about(SCHEMA_NULL)
227248
.arg(Arg::with_name("exercise")
228249
.help("An exercise. Takes two values, an exercise id and an exercise path. Multiple exercises can be given.")
229250
.long("exercise")
@@ -235,6 +256,7 @@ pub fn create_app() -> App<'static, 'static> {
235256

236257
.subcommand(SubCommand::with_name("get-course-details")
237258
.about("Get course details.")
259+
.long_about(schema_leaked::<CourseDetails>())
238260
.arg(Arg::with_name("course-id")
239261
.help("The ID of the course.")
240262
.long("course-id")
@@ -243,6 +265,7 @@ pub fn create_app() -> App<'static, 'static> {
243265

244266
.subcommand(SubCommand::with_name("get-courses")
245267
.about("List courses.")
268+
.long_about(schema_leaked::<Vec<CourseData>>())
246269
.arg(Arg::with_name("organization")
247270
.help("Organization slug (e.g. mooc, hy).")
248271
.long("organization")
@@ -251,6 +274,7 @@ pub fn create_app() -> App<'static, 'static> {
251274

252275
.subcommand(SubCommand::with_name("get-course-settings")
253276
.about("Get course settings.")
277+
.long_about(schema_leaked::<CourseData>())
254278
.arg(Arg::with_name("course-id")
255279
.help("The ID of the course.")
256280
.long("course-id")
@@ -259,6 +283,7 @@ pub fn create_app() -> App<'static, 'static> {
259283

260284
.subcommand(SubCommand::with_name("get-course-exercises")
261285
.about("Get course exercises.")
286+
.long_about(schema_leaked::<Vec<CourseExercise>>())
262287
.arg(Arg::with_name("course-id")
263288
.help("The ID of the course.")
264289
.long("course-id")
@@ -267,6 +292,7 @@ pub fn create_app() -> App<'static, 'static> {
267292

268293
.subcommand(SubCommand::with_name("get-exercise-details")
269294
.about("Get exercise details.")
295+
.long_about(schema_leaked::<ExerciseDetails>())
270296
.arg(Arg::with_name("exercise-id")
271297
.help("The ID of the exercise.")
272298
.long("exercise-id")
@@ -275,6 +301,7 @@ pub fn create_app() -> App<'static, 'static> {
275301

276302
.subcommand(SubCommand::with_name("paste")
277303
.about("Send exercise to pastebin with comment.")
304+
.long_about(schema_leaked::<NewSubmission>())
278305
.arg(Arg::with_name("submission-url")
279306
.help("The URL where the submission should be posted.")
280307
.long("submission-url")
@@ -297,6 +324,7 @@ pub fn create_app() -> App<'static, 'static> {
297324

298325
.subcommand(SubCommand::with_name("run-checkstyle")
299326
.about("Run checkstyle.")
327+
.long_about(schema_leaked::<Option<ValidationResult>>())
300328
.arg(Arg::with_name("exercise-path")
301329
.help("Path to the directory where the exercise resides.")
302330
.long("exercise-path")
@@ -310,6 +338,7 @@ pub fn create_app() -> App<'static, 'static> {
310338

311339
.subcommand(SubCommand::with_name("run-tests")
312340
.about("Run tests.")
341+
.long_about(schema_leaked::<RunResult>())
313342
.arg(Arg::with_name("exercise-path")
314343
.help("Path to the directory where the exercise resides.")
315344
.long("exercise-path")
@@ -318,6 +347,7 @@ pub fn create_app() -> App<'static, 'static> {
318347

319348
.subcommand(SubCommand::with_name("send-feedback")
320349
.about("Send feedback.")
350+
.long_about(schema_leaked::<SubmissionFeedbackResponse>())
321351
.arg(Arg::with_name("feedback-url")
322352
.help("URL where the feedback should be posted.")
323353
.long("feedback-url")
@@ -334,6 +364,7 @@ pub fn create_app() -> App<'static, 'static> {
334364

335365
.subcommand(SubCommand::with_name("submit")
336366
.about("Submit exercise. Will block until the submission results are returned.")
367+
.long_about(schema_leaked::<SubmissionFinished>())
337368
.arg(Arg::with_name("submission-url")
338369
.help("URL where the submission should be posted.")
339370
.long("submission-url")
@@ -354,6 +385,7 @@ pub fn create_app() -> App<'static, 'static> {
354385

355386
.subcommand(SubCommand::with_name("get-exercise-submissions")
356387
.about("Fetch the current user's old submissions for an exercise.")
388+
.long_about(schema_leaked::<Vec<Submission>>())
357389
.arg(Arg::with_name("exercise-id")
358390
.help("The ID of the exercise.")
359391
.long("exercise-id")
@@ -362,6 +394,7 @@ pub fn create_app() -> App<'static, 'static> {
362394

363395
.subcommand(SubCommand::with_name("wait-for-submission")
364396
.about("Wait for a submission to finish.")
397+
.long_about(schema_leaked::<SubmissionFinished>())
365398
.arg(Arg::with_name("submission-url")
366399
.help("URL to the submission's status.")
367400
.long("submission-url")
@@ -370,6 +403,7 @@ pub fn create_app() -> App<'static, 'static> {
370403

371404
.subcommand(SubCommand::with_name("get-exercise-updates")
372405
.about("Get exercise updates.")
406+
.long_about(schema_leaked::<UpdateResult>())
373407
.arg(Arg::with_name("course-id")
374408
.help("The ID of the course")
375409
.long("course-id")
@@ -386,6 +420,7 @@ pub fn create_app() -> App<'static, 'static> {
386420

387421
.subcommand(SubCommand::with_name("mark-review-as-read")
388422
.about("Mark review as read.")
423+
.long_about(SCHEMA_NULL)
389424
.arg(Arg::with_name("review-update-url")
390425
.help("URL to the review update API.")
391426
.long("review-update-url")
@@ -394,6 +429,7 @@ pub fn create_app() -> App<'static, 'static> {
394429

395430
.subcommand(SubCommand::with_name("get-unread-reviews")
396431
.about("Get unread reviews.")
432+
.long_about(schema_leaked::<Vec<Review>>())
397433
.arg(Arg::with_name("reviews-url")
398434
.help("URL to the reviews API.")
399435
.long("reviews-url")
@@ -402,6 +438,7 @@ pub fn create_app() -> App<'static, 'static> {
402438

403439
.subcommand(SubCommand::with_name("request-code-review")
404440
.about("Request code review.")
441+
.long_about(schema_leaked::<NewSubmission>())
405442
.arg(Arg::with_name("submission-url")
406443
.help("URL where the submission should be posted.")
407444
.long("submission-url")
@@ -425,6 +462,7 @@ pub fn create_app() -> App<'static, 'static> {
425462

426463
.subcommand(SubCommand::with_name("download-model-solution")
427464
.about("Download model solutions.")
465+
.long_about(SCHEMA_NULL)
428466
.arg(Arg::with_name("solution-download-url")
429467
.help("URL to the solution download.")
430468
.long("solution-download-url")
@@ -438,6 +476,7 @@ pub fn create_app() -> App<'static, 'static> {
438476

439477
.subcommand(SubCommand::with_name("reset-exercise")
440478
.about("Reset exercise, optionally submitting it before doing so.")
479+
.long_about(SCHEMA_NULL)
441480
.arg(Arg::with_name("exercise-path")
442481
.help("Path to the directory where the project resides.")
443482
.long("exercise-path")
@@ -454,6 +493,7 @@ pub fn create_app() -> App<'static, 'static> {
454493

455494
.subcommand(SubCommand::with_name("download-old-submission")
456495
.about("Downloads an old submission.")
496+
.long_about(SCHEMA_NULL)
457497
.arg(Arg::with_name("exercise-id")
458498
.help("The ID of the exercise.")
459499
.long("exercise-id")
@@ -478,3 +518,20 @@ pub fn create_app() -> App<'static, 'static> {
478518
.long("submission-url")
479519
.takes_value(true))))
480520
}
521+
522+
const SCHEMA_NULL: &str = "Result data JSON format: null";
523+
const SCHEMA_TOKEN: &str = r#"Result data JSON format:
524+
{
525+
"access_token": String,
526+
"token_type": String,
527+
"scope": String,
528+
}"#;
529+
530+
fn schema_leaked<T: JsonSchema>() -> &'static str {
531+
let schema = schemars::schema_for!(T);
532+
let json = format!(
533+
"Result data JSON format:\n{}",
534+
serde_json::to_string_pretty(&schema).unwrap()
535+
);
536+
Box::leak(Box::new(json))
537+
}

tmc-langs-cli/src/main.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use tempfile::NamedTempFile;
1818
use tmc_langs_core::oauth2::{
1919
basic::BasicTokenType, AccessToken, EmptyExtraTokenFields, Scope, StandardTokenResponse,
2020
};
21-
use tmc_langs_core::{FeedbackAnswer, StatusType, TmcCore, Token};
21+
use tmc_langs_core::{FeedbackAnswer, TmcCore, Token};
2222
use tmc_langs_framework::{domain::ValidationResult, io::submission_processing};
2323
use tmc_langs_util::{
2424
task_executor::{self, TmcParams},
@@ -908,6 +908,15 @@ fn run() -> Result<()> {
908908
let review_update_url = matches.value_of("review-update-url").unwrap();
909909
core.mark_review_as_read(review_update_url.to_string())
910910
.context("Failed to mark review as read")?;
911+
912+
let output = Output::<()> {
913+
status: Status::Successful,
914+
message: None,
915+
result: OutputResult::SentData,
916+
percent_done: 1.0,
917+
data: None,
918+
};
919+
print_output(&output)?;
911920
} else if let Some(matches) = matches.subcommand_matches("get-unread-reviews") {
912921
let reviews_url = matches.value_of("reviews-url").unwrap();
913922
let reviews_url = into_url(reviews_url)?;
@@ -966,6 +975,15 @@ fn run() -> Result<()> {
966975

967976
core.download_model_solution(solution_download_url, target)
968977
.context("Failed to download model solution")?;
978+
979+
let output = Output::<()> {
980+
status: Status::Successful,
981+
message: None,
982+
result: OutputResult::RetrievedData,
983+
percent_done: 1.0,
984+
data: None,
985+
};
986+
print_output(&output)?;
969987
} else if let Some(matches) = matches.subcommand_matches("reset-exercise") {
970988
let exercise_path = matches.value_of("exercise-path").unwrap();
971989
let exercise_path = Path::new(exercise_path);
@@ -981,6 +999,15 @@ fn run() -> Result<()> {
981999
core.submit(submission_url, exercise_path, None)?;
9821000
}
9831001
core.reset(exercise_id, exercise_path)?;
1002+
1003+
let output = Output::<()> {
1004+
status: Status::Successful,
1005+
message: None,
1006+
result: OutputResult::ExecutedCommand,
1007+
percent_done: 1.0,
1008+
data: None,
1009+
};
1010+
print_output(&output)?;
9841011
} else if let Some(matches) = matches.subcommand_matches("download-old-submission") {
9851012
let exercise_id = matches.value_of("exercise-id").unwrap();
9861013
let exercise_id = into_usize(exercise_id)?;
@@ -1003,6 +1030,15 @@ fn run() -> Result<()> {
10031030
let temp_zip = NamedTempFile::new().context("Failed to create a temporary archive")?;
10041031
core.download_old_submission(submission_id, temp_zip.path())?;
10051032
task_executor::extract_project(temp_zip.path(), output_path)?;
1033+
1034+
let output = Output::<()> {
1035+
status: Status::Successful,
1036+
message: None,
1037+
result: OutputResult::RetrievedData,
1038+
percent_done: 1.0,
1039+
data: None,
1040+
};
1041+
print_output(&output)?;
10061042
}
10071043
}
10081044
Ok(())

0 commit comments

Comments
 (0)