jellyflood/fastlane/swift/RubyCommand.swift

158 lines
5.2 KiB
Swift

// RubyCommand.swift
// Copyright (c) 2025 FastlaneTools
//
// ** NOTE **
// This file is provided by fastlane and WILL be overwritten in future updates
// If you want to add extra functionality to this project, create a new file in a
// new group so that it won't be marked for upgrade
//
import Foundation
struct RubyCommand: RubyCommandable {
var type: CommandType { return .action }
struct Argument {
enum ArgType {
case stringClosure
var typeString: String {
switch self {
case .stringClosure:
return "string_closure" // this should match when is in ruby's SocketServerActionCommandExecutor
}
}
}
let name: String
let value: Any?
let type: ArgType?
init(name: String, value: Any?, type: ArgType? = nil) {
self.name = name
self.value = value
self.type = type
}
var hasValue: Bool {
return value != nil
}
var json: String {
if let someValue = value {
let typeJson: String
if let type = type {
typeJson = ", \"value_type\" : \"\(type.typeString)\""
} else {
typeJson = ""
}
if type == .stringClosure {
return "{\"name\" : \"\(name)\", \"value\" : \"ignored_for_closure\"\(typeJson)}"
} else if let array = someValue as? [String] {
return "{\"name\" : \"\(name)\", \"value\" : \(array)\(typeJson)}"
} else if let hash = someValue as? [String: Any] {
let jsonData = try! JSONSerialization.data(withJSONObject: hash, options: [])
let jsonString = String(data: jsonData, encoding: .utf8)!
return "{\"name\" : \"\(name)\", \"value\" : \(jsonString)\(typeJson)}"
} else {
let dictionary = [
"name": name,
"value": someValue,
]
let jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
let jsonString = String(data: jsonData, encoding: .utf8)!
return jsonString
}
} else {
// Just exclude this arg if it doesn't have a value
return ""
}
}
}
let commandID: String
let methodName: String
let className: String?
let args: [Argument]
let id: String = UUID().uuidString
var closure: ((String) -> Void)? {
let callbacks = args.filter { ($0.type != nil) && $0.type == .stringClosure }
guard let callback = callbacks.first else {
return nil
}
guard let callbackArgValue = callback.value else {
return nil
}
guard let callbackClosure = callbackArgValue as? ((String) -> Void) else {
return nil
}
return callbackClosure
}
func callbackClosure(_ callbackArg: String) -> ((String) -> Void)? {
// WARNING: This will perform the first callback it receives
let callbacks = args.filter { ($0.type != nil) && $0.type == .stringClosure }
guard let callback = callbacks.first else {
verbose(message: "received call to performCallback with \(callbackArg), but no callback available to perform")
return nil
}
guard let callbackArgValue = callback.value else {
verbose(message: "received call to performCallback with \(callbackArg), but callback is nil")
return nil
}
guard let callbackClosure = callbackArgValue as? ((String) -> Void) else {
verbose(message: "received call to performCallback with \(callbackArg), but callback type is unknown \(callbackArgValue.self)")
return nil
}
return callbackClosure
}
func performCallback(callbackArg: String, socket: SocketClient, completion: @escaping () -> Void) {
verbose(message: "Performing callback with: \(callbackArg)")
socket.leave()
callbackClosure(callbackArg)?(callbackArg)
completion()
}
var commandJson: String {
let argsArrayJson = args
.map { $0.json }
.filter { $0 != "" }
let argsJson: String?
if !argsArrayJson.isEmpty {
argsJson = "\"args\" : [\(argsArrayJson.joined(separator: ","))]"
} else {
argsJson = nil
}
let commandIDJson = "\"commandID\" : \"\(commandID)\""
let methodNameJson = "\"methodName\" : \"\(methodName)\""
var jsonParts = [commandIDJson, methodNameJson]
if let argsJson = argsJson {
jsonParts.append(argsJson)
}
if let className = className {
let classNameJson = "\"className\" : \"\(className)\""
jsonParts.append(classNameJson)
}
let commandJsonString = "{\(jsonParts.joined(separator: ","))}"
return commandJsonString
}
}
// Please don't remove the lines below
// They are used to detect outdated files
// FastlaneRunnerAPIVersion [0.9.2]