From f7f0b046f7d86fdda3c641410d4e66167b9130b7 Mon Sep 17 00:00:00 2001 From: subdiox Date: Sun, 1 Oct 2023 23:51:30 +0900 Subject: [PATCH] Add support for submission to the latest AtCoder contests --- Sources/AtCoderLibrary/API/OjApiCommand.swift | 54 +++++++++++++------ Sources/AtCoderLibrary/API/Response.swift | 9 ++++ Sources/AtCoderLibrary/Command/Submit.swift | 4 +- .../Command/Submit/RunTest.swift | 2 +- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/Sources/AtCoderLibrary/API/OjApiCommand.swift b/Sources/AtCoderLibrary/API/OjApiCommand.swift index db889ac..9ff89e8 100644 --- a/Sources/AtCoderLibrary/API/OjApiCommand.swift +++ b/Sources/AtCoderLibrary/API/OjApiCommand.swift @@ -13,23 +13,18 @@ enum OjApiCommand { return (contest, problems) } - static func submitCode(contestName: String, task: Character, ojApiPath: String) throws -> URL { + static func submitCode(contestName: String, task: String, ojApiPath: String) throws -> URL { try precheck(path: ojApiPath) - let result = run( - ojApiPath, - "submit-code", - "--file", - "Sources/\(task.uppercased())/main.swift", - "--language", - "4055", - "https://atcoder.jp/contests/\(contestName)/tasks/\(contestName)_\(task.lowercased())" - ) - guard result.succeeded else { - throw result.stderror + let contestURL = "https://atcoder.jp/contests/\(contestName)" + let contest = try getContest(url: contestURL, ojApiPath: ojApiPath) + guard let problem = contest.problems.first(where: { + $0.context.alphabet.uppercased() == task.uppercased() + }) else { + throw "The contest name or the task name is invalid." } - print(result.stdout) - let response = try JSONDecoder().decode(SubmitCodeResponse.self, from: result.stdout.data(using: .utf8)!) - return response.result.url + let filePath = "Sources/\(task.uppercased())/main.swift" + let language = try guessLanguage(url: problem.url, filePath: filePath, ojApiPath: ojApiPath) + return try submitCode(url: problem.url, filePath: filePath, language: language, ojApiPath: ojApiPath) } } @@ -63,4 +58,33 @@ private extension OjApiCommand { let response = try JSONDecoder().decode(GetProblemResponse.self, from: result.stdout.data(using: .utf8)!) return response.result } + + static func guessLanguage(url: URL, filePath: String, ojApiPath: String) throws -> Language { + try precheck(path: ojApiPath) + let result = run(ojApiPath, "guess-language-id", url.absoluteString, "--file=\(filePath)") + guard result.succeeded else { + throw result.stderror + } + print(result.stdout) + let response = try JSONDecoder().decode(GuessLanguageResponse.self, from: result.stdout.data(using: .utf8)!) + return response.result + } + + static func submitCode(url: URL, filePath: String, language: Language, ojApiPath: String) throws -> URL { + let result = run( + ojApiPath, + "submit-code", + "--file", + filePath, + "--language", + language.id, + url.absoluteString + ) + guard result.succeeded else { + throw result.stderror + } + print(result.stdout) + let response = try JSONDecoder().decode(SubmitCodeResponse.self, from: result.stdout.data(using: .utf8)!) + return response.result.url + } } diff --git a/Sources/AtCoderLibrary/API/Response.swift b/Sources/AtCoderLibrary/API/Response.swift index c1c1464..71c7fca 100644 --- a/Sources/AtCoderLibrary/API/Response.swift +++ b/Sources/AtCoderLibrary/API/Response.swift @@ -9,6 +9,10 @@ extension OjApiCommand { let result: Problem } + struct GuessLanguageResponse: Decodable { + let result: Language + } + struct SubmitCodeResponse: Decodable { let result: Result @@ -57,3 +61,8 @@ struct Context: Decodable { let url: URL } } + +struct Language: Decodable { + let id: String + let description: String +} diff --git a/Sources/AtCoderLibrary/Command/Submit.swift b/Sources/AtCoderLibrary/Command/Submit.swift index ba8adfd..0e43687 100644 --- a/Sources/AtCoderLibrary/Command/Submit.swift +++ b/Sources/AtCoderLibrary/Command/Submit.swift @@ -8,8 +8,8 @@ public struct Submit: ParsableCommand { abstract: "Submit a your code." ) - @Argument(help: "Alphabet of the problem to be submitted.", transform: Character.init) - var task: Character + @Argument(help: "Alphabet of the problem to be submitted.") + var task: String @Flag(name: .shortAndLong, help: "Run a UnitTest before submitting.") var runTest: Bool = false diff --git a/Sources/AtCoderLibrary/Command/Submit/RunTest.swift b/Sources/AtCoderLibrary/Command/Submit/RunTest.swift index 1fc6163..9d4f2ca 100644 --- a/Sources/AtCoderLibrary/Command/Submit/RunTest.swift +++ b/Sources/AtCoderLibrary/Command/Submit/RunTest.swift @@ -2,7 +2,7 @@ import Foundation import SwiftShell enum RunTest { - static func sampleCase(task: Character) throws { + static func sampleCase(task: String) throws { try runAndPrint("swift", "test", "--filter", "\(task.uppercased())Tests/testExample") } }