Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CLIツールにSwift Concurrencyを適用させようとしている話

CLIツールにSwift Concurrencyを適用させようとしている話

2022/04/25@Swift愛好会 vol.67

417.72KI

April 25, 2022
Tweet

More Decks by 417.72KI

Other Decks in Programming

Transcript

  1. 1SFTFOUFS struct Me { let name = "Takuhiro Muta" let

    aka = "417.72KI" let company = "***" let twitter = "417_72ki" let qiita = "417_72ki" let gitHub = "417-72KI" let products = [ "MockUserDefaults", "MultipartFormDataParser", "BuildConfig.swift", "SSGH" ] let contributing = [ "Danger-Swift", "octokit.swift", "etc..." ] }
  2. .PEVMFT targets: [ .executableTarget( name: "SSGH", dependencies: [ .product(name: "ArgumentParser",

    package: "swift-argument-parser"), "SSGHCore" ] ), .target( name: "SSGHCore", dependencies: ["GitHubAPI"] ), .target( name: "GitHubAPI", dependencies: ["OctoKit"] ),
  3. (JU)VC$MJFOU #FGPSF extension GitHubClientImpl { public func getUser(by userId: String)

    -> Result<User, GitHubAPIError> { var result: Result<OctoKit.User, Swift.Error>! let semaphore = DispatchSemaphore(value: 0) octoKit.user(session, name: userId) { result = $0 semaphore.signal() } semaphore.wait() return result.map(User.init) .mapError { if ($0 as NSError).code == 404 { return .userNotFound(userId) } return .other($0) } } }
  4. (JU)VC$MJFOU "GUFS extension GitHubClientImpl { public func getUser(by userId: String)

    async throws -> User { try await withCheckedThrowingContinuation { continuation in octoKit.user(session, name: userId) { switch $0 { case let .success(result): continuation.resume(returning: User(result)) case let .failure(error): if (error as NSError).code == 404 { continuation.resume(throwing: GitHubAPIError.userNotFound(userId)) } else { continuation.resume(throwing: GitHubAPIError.other(error)) } } } } } }
  5. $PSF #FGPSF func execute(mode: Mode) throws { switch mode {

    case let .specifiedTargets(targets): try targets.compactMap { switch gitHubClient.getUser(by: $0) { case let .success(user): return user case let .failure(error): dumpWarn(error) return nil } } .forEach(star(to:)) } }
  6. $PSF "GUFS func execute(mode: Mode) async throws { switch mode

    { case let .specifiedTargets(targets): for target in targets { do { let user = try await gitHubClient.getUser(by: target) try await star(to: user) } catch { dumpWarn(error) } } } }
  7. 1BSTBCMF$PNNBOE #FGPSF @main struct SSGH: ParsableCommand { @Argument(help: "GitHub user

    name to give stars.") var targets: [String] @Option(name: [.customLong("github-token"), .customShort("t")], help: "GitHub Token to give stars. If not set, use `SSGH_TOKEN` in environment.") var gitHubToken: String? @Flag(name: [.customLong("dry-run"), .short], help: "dry-run mode. Only fetch lists to give stars.") var dryRunMode = false }
  8. 1BSTBCMF$PNNBOE "GUFS d @main struct SSGH: AsyncParsableCommand { @Argument(help: "GitHub

    user name to give stars.") var targets: [String] @Option(name: [.customLong("github-token"), .customShort("t")], help: "GitHub Token to give stars. If not set, use `SSGH_TOKEN` in environment.") var gitHubToken: String? @Flag(name: [.customLong("dry-run"), .short], help: "dry-run mode. Only fetch lists to give stars.") var dryRunMode = false }
  9. 1BSTBCMF$PNNBOE "GUFS d @main enum Main: AsyncMainProtocol { typealias Command

    = SSGH } struct SSGH: AsyncParsableCommand { @Argument(help: "GitHub user name to give stars.") var targets: [String] @Option(name: [.customLong("github-token"), .customShort("t")], help: "GitHub Token to give stars. If not set, use `SSGH_TOKEN` in environment.") var gitHubToken: String? @Flag(name: [.customLong("dry-run"), .short], help: "dry-run mode. Only fetch lists to give stars.") var dryRunMode = false } IUUQTHJUIVCDPNBQQMFTXJGUBSHVNFOUQBSTFSQVMM
  10. 1BSTBCMF$PNNBOESVO #FGPSF extension SSGH { func run() throws { let

    gitHubToken = try gitHubToken ?? (try Environment.getValue(forKey: .gitHubToken)) let core = SSGHCore( gitHubToken: gitHubToken, dryRunMode: dryRunMode ) do { try core.execute(mode: . specifiedTargets(targets)) } catch { dumpError(error) Self.exit(withError: error) } } }
  11. 1BSTBCMF$PNNBOESVO "GUFS d extension SSGH { func run() async throws

    { let gitHubToken = try gitHubToken ?? (try Environment.getValue(forKey: .gitHubToken)) let core = SSGHCore( gitHubToken: gitHubToken, dryRunMode: dryRunMode ) do { try await core.execute(mode: .specifiedTargets(targets)) } catch { dumpError(error) Self.exit(withError: error) } } }