core/swift57Action/swiftbuild.py.launcher.swift (191 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Imports import Foundation import _Concurrency #if os(Linux) import Glibc #else import Darwin #endif public struct _WhiskRuntime { private enum WhiskRuntimeErrorMessage: String { case actionHandlerCallbackError = "Action handler callback returned an error:" case actionHandlerCallbackNullOrError = "Action handler callback did not return response or error." case failToEncodeDictionary = "Failed to encode Dictionary type to JSON string:" case failToEncodeCodableToJson = "JSONEncoder failed to encode Codable type to JSON string:" case errorSerializingJSON = "Error serializing JSON, data does not appear to be valid JSON" case failedToExecuteActionHandler = "Failed to execute action handler with error:" } public static func wiskRunLoop(actionMain: ((Data) async -> Void)) async throws { while let inputStr: String = readLine() { let json = inputStr.data(using: .utf8, allowLossyConversion: true)! let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any] for (key, value) in parsed { if key != "value" { setenv("__OW_\(key.uppercased())",value as! String,1) } } let jsonData = try JSONSerialization.data(withJSONObject: parsed["value"] as Any, options: []) await actionMain(jsonData) } } private static func whiskPrintJSONDecoderError(json: Data, error: Error?) { let jsonString = String( data: json, encoding: .utf8 ) ?? "" let fixedJSONString = jsonString.replacingOccurrences(of: "\"", with: "\\\"") let message = "JSONDecoder failed to decode JSON string \(fixedJSONString) to Codable type:" var errStr = "{\"error\":\"\(message)\"}\n" if let error = error { errStr = "{\"error\":\"\(message) \(error.localizedDescription)\"\n}" } whiskPrintBuffer(jsonString: errStr) } private static func whiskPrintError(message: WhiskRuntimeErrorMessage, error: Error?){ var errStr = "{\"error\":\"\(message.rawValue)\"}\n" if let error = error { errStr = "{\"error\":\"\(message.rawValue) \(error.localizedDescription)\"\n}" } whiskPrintBuffer(jsonString: errStr) } private static func whiskPrintResult(jsonData: Data){ let jsonString = String(data: jsonData, encoding: .utf8)! whiskPrintBuffer(jsonString: jsonString) } private static func whiskPrintBuffer(jsonString: String){ var buf : [UInt8] = Array(jsonString.utf8) buf.append(10) fflush(stdout) fflush(stderr) write(3, buf, buf.count) } /** Execute an async throwing Action with Any Input and Any Output Example: ``` func action(args: Any) async throws -> Any { //async code sleep for 1 sec try await Task.sleep(nanoseconds: 1_000_000_000) let newArgs = args as! [String:Any] if let name = newArgs["name"] as? String { return [ "greeting" : "Hello \(name)!" ] } else { return [ "greeting" : "Hello stranger!" ] } } ``` - Parameters: - mainFunction: action - json: action parameters - Returns: Void */ public static func runAsyncMain(mainFunction: (Any) async throws -> Any, json: Data) async -> Void { do { let parsed = try JSONSerialization.jsonObject(with: json, options: []) let result = try await mainFunction(parsed) if JSONSerialization.isValidJSONObject(result) { do { let jsonData = try JSONSerialization.data(withJSONObject: result, options: []) whiskPrintResult(jsonData: jsonData) } catch { whiskPrintError(message: .failToEncodeDictionary, error: error) } } else { whiskPrintError(message: .errorSerializingJSON, error: nil) } } catch let error as DecodingError { whiskPrintJSONDecoderError(json: json, error: error) return } catch { whiskPrintError(message: .failedToExecuteActionHandler, error: error) return } } /** Execute an Action with Codable Input and completion with Codable Output and Error Example: ``` struct Input: Codable { let name: String? } struct Output: Codable { let count: Int } func action(input: Input, completion: @escaping (Output?, Error?) -> Void) -> Void { if let name = input.name { let output = Output(count: name.count) completion(output, nil) } else { let output = Output(count: 0) completion(output, nil) } } ``` - Parameters: - mainFunction: action - json: action parameters - Returns: Void */ public static func runAsyncMain<In: Decodable, Out: Encodable>(mainFunction: (In, @escaping (Out?, Error?) -> Void) -> Void, json: Data) { do { let input = try Whisk.jsonDecoder.decode(In.self, from: json) let resultHandler = { (out: Out?, error: Error?) in if let error = error { whiskPrintError(message: .actionHandlerCallbackError, error: error) return } guard let out = out else { whiskPrintError(message: .actionHandlerCallbackNullOrError, error: nil) return } do { let jsonData = try Whisk.jsonEncoder.encode(out) whiskPrintResult(jsonData: jsonData) } catch let error as EncodingError { whiskPrintError(message: .failToEncodeCodableToJson, error: error) return } catch { whiskPrintError(message: .failedToExecuteActionHandler, error: error) return } } let _ = mainFunction(input, resultHandler) } catch let error as DecodingError { whiskPrintJSONDecoderError(json: json, error: error) return } catch { whiskPrintError(message: .failedToExecuteActionHandler, error: error) return } } /** Execute an async throwing Action with a Codable Input and a Codable Output Example: ``` struct Input: Codable { let name: String? } struct Output: Codable { let count: Int } func action(input: Input) async throws -> Output? { try await Task.sleep(nanoseconds: 1_000_000_000) if let name = input.name { return Output(count: name.count) } else { return Output(count: 0) } } ``` - Parameters: - mainFunction: action - json: action parameters - Returns: Void */ public static func runAsyncMain<In: Decodable, Out: Encodable>(mainFunction: (In) async throws -> Out?, json: Data) async { do { let input = try Whisk.jsonDecoder.decode(In.self, from: json) do { let out = try await mainFunction(input) guard let out = out else { whiskPrintError(message: .actionHandlerCallbackNullOrError, error: nil) return } do { let jsonData = try Whisk.jsonEncoder.encode(out) whiskPrintResult(jsonData: jsonData) } catch let error as EncodingError { whiskPrintError(message: .failToEncodeCodableToJson, error: error) return } catch { whiskPrintError(message: .failedToExecuteActionHandler, error: error) return } } catch { whiskPrintError(message: .actionHandlerCallbackError, error: error) return } } catch let error as DecodingError { whiskPrintJSONDecoderError(json: json, error: error) return } catch { whiskPrintError(message: .failedToExecuteActionHandler, error: error) return } } /** Execute an Action with Codable Input and completion with Codable Output and Error Example: ``` struct Input: Codable { let name: String? } struct Output: Codable { let count: Int } func action(completion: @escaping (Output?, Error?) -> Void) -> Void { let output = Output(count: 0) completion(output, nil) } ``` - Parameters: - mainFunction: action - json: action parameters - Returns: Void */ public static func runAsyncMain<Out: Encodable>(mainFunction: ( @escaping (Out?, Error?) -> Void) -> Void, json: Data) { let resultHandler = { (out: Out?, error: Error?) in if let error = error { whiskPrintError(message: .actionHandlerCallbackError, error: error) return } guard let out = out else { whiskPrintError(message: .actionHandlerCallbackNullOrError, error: nil) return } do { let jsonData = try Whisk.jsonEncoder.encode(out) whiskPrintResult(jsonData: jsonData) } catch let error as EncodingError { whiskPrintError(message: .failToEncodeCodableToJson, error: error) return } catch { whiskPrintError(message: .failedToExecuteActionHandler, error: error) return } } let _ = mainFunction(resultHandler) } /** Execute an async throwing Action with Codable Output Example: ``` struct Input: Codable { let name: String? } struct Output: Codable { let count: Int } func action() async throws -> Output? { try await Task.sleep(nanoseconds: 1_000_000_000) return Output(count: 0) } ``` - Parameters: - mainFunction: action - json: action parameters - Returns: Void */ public static func runAsyncMain<Out: Encodable>(mainFunction: () async throws -> Out?, json: Data) async { do { let out = try await mainFunction() guard let out = out else { whiskPrintError(message: .actionHandlerCallbackNullOrError, error: nil) return } do { let jsonData = try Whisk.jsonEncoder.encode(out) whiskPrintResult(jsonData: jsonData) } catch let error as EncodingError { whiskPrintError(message: .failToEncodeCodableToJson, error: error) return } catch { whiskPrintError(message: .failedToExecuteActionHandler, error: error) return } } catch { whiskPrintError(message: .actionHandlerCallbackError, error: error) return } } }