From e50548bd63e3f48f980400c030e12e935fbbc521 Mon Sep 17 00:00:00 2001 From: Cyrus Ingraham Date: Fri, 13 Mar 2020 10:27:50 -0500 Subject: [PATCH] Initial release (#1) * Setup project structure and added initial implementations * Implemented unit tests * Implemented remaining unit and system tests * Removed example project * Fixed SPM build/test errors and updated podspec * Fixed podspec syntax * Removed Cocoapods from github workflow * Added contents of README * Added documentation for the Subprocess class * Added missing equals in documentation * Added table of contents to README * Fixed README table formatting issue * More table of contents formatting in README * Final format for table of contents * Added badges to README * Fixed build badge * Another fix for the build badge * Made recommended changes from PR * Fixed SwiftPM building and testing * Fixed SwiftPM build and test * Updated deployment target to 10.12 * Removed Linux support files and content * Added Swiftlint, fixed all warnings and moved Subprocess.Input into a non-nested type Input * Added Jazzy documentation * Added LICENSE to Input.swift * Updated documentation copyright * Move commented out line * Cleaned up Package.swift, added description and removed whitespaces * Fixed non-zero spelling * Updated fixed build error from SubprocessError name change * Forgot a couple more places using SubprocessError :) * Changed factory methods to conform to Swift API guidelines * Fixed typo in comment of Input.swift * Updated repo description * Updated deployment target at the project level * Fixed grammer in comments * Removed copyrights in Info.plists Co-authored-by: Cyrus Ingraham --- .../{swift.yml => build-and-test.yml} | 12 +- Package.swift | 18 + README.md | 151 +++- Sources/Subprocess/Errors.swift | 47 + Sources/Subprocess/Info.plist | 22 + Sources/Subprocess/Input.swift | 89 ++ Sources/Subprocess/Shell.swift | 193 ++++ Sources/Subprocess/Subprocess.h | 34 + Sources/Subprocess/Subprocess.swift | 162 ++++ .../SubprocessDependencyBuilder.swift | 87 ++ Sources/SubprocessMocks/Info.plist | 22 + Sources/SubprocessMocks/MockProcess.swift | 203 +++++ Sources/SubprocessMocks/MockShell.swift | 339 +++++++ Sources/SubprocessMocks/MockSubprocess.swift | 90 ++ .../MockSubprocessDependencyBuilder.swift | 241 +++++ Sources/SubprocessMocks/SubprocessMocks.h | 34 + Subprocess.podspec | 36 + Subprocess.xcodeproj/project.pbxproj | 816 +++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/Subprocess.xcscheme | 98 +++ .../xcschemes/SubprocessMocks.xcscheme | 67 ++ Tests/SystemTests/Info.plist | 22 + Tests/SystemTests/ShellTests.swift | 139 +++ Tests/UnitTests/Info.plist | 22 + Tests/UnitTests/ShellTests.swift | 345 ++++++++ Tests/UnitTests/SubprocessTests.swift | 227 +++++ docs/Classes.html | 154 ++++ docs/Classes/Shell.html | 825 ++++++++++++++++++ docs/Classes/Shell/OutputOptions.html | 237 +++++ docs/Classes/Subprocess.html | 537 ++++++++++++ docs/Enums.html | 126 +++ docs/Enums/SubprocessError.html | 239 +++++ docs/Protocols.html | 126 +++ .../SubprocessDependencyFactory.html | 259 ++++++ docs/Structs.html | 154 ++++ docs/Structs/Input.html | 359 ++++++++ docs/Structs/Input/Value.html | 185 ++++ docs/Structs/SubprocessDependencyBuilder.html | 209 +++++ docs/badge.svg | 28 + docs/css/highlight.css | 200 +++++ docs/css/jazzy.css | 372 ++++++++ .../Subprocess.docset/Contents/Info.plist | 20 + .../Contents/Resources/Documents/Classes.html | 154 ++++ .../Resources/Documents/Classes/Shell.html | 825 ++++++++++++++++++ .../Classes/Shell/OutputOptions.html | 237 +++++ .../Documents/Classes/Subprocess.html | 537 ++++++++++++ .../Contents/Resources/Documents/Enums.html | 126 +++ .../Documents/Enums/SubprocessError.html | 239 +++++ .../Resources/Documents/Protocols.html | 126 +++ .../SubprocessDependencyFactory.html | 259 ++++++ .../Contents/Resources/Documents/Structs.html | 154 ++++ .../Resources/Documents/Structs/Input.html | 359 ++++++++ .../Documents/Structs/Input/Value.html | 185 ++++ .../Structs/SubprocessDependencyBuilder.html | 209 +++++ .../Contents/Resources/Documents/badge.svg | 28 + .../Resources/Documents/css/highlight.css | 200 +++++ .../Resources/Documents/css/jazzy.css | 372 ++++++++ .../Resources/Documents/img/carat.png | Bin 0 -> 274 bytes .../Contents/Resources/Documents/img/dash.png | Bin 0 -> 1338 bytes .../Contents/Resources/Documents/img/gh.png | Bin 0 -> 1571 bytes .../Contents/Resources/Documents/index.html | 225 +++++ .../Contents/Resources/Documents/js/jazzy.js | 59 ++ .../Resources/Documents/js/jquery.min.js | 2 + .../Contents/Resources/Documents/search.json | 1 + .../Contents/Resources/docSet.dsidx | Bin 0 -> 28672 bytes docs/docsets/Subprocess.tgz | Bin 0 -> 56011 bytes docs/img/carat.png | Bin 0 -> 274 bytes docs/img/dash.png | Bin 0 -> 1338 bytes docs/img/gh.png | Bin 0 -> 1571 bytes docs/index.html | 225 +++++ docs/js/jazzy.js | 59 ++ docs/js/jquery.min.js | 2 + docs/search.json | 1 + 74 files changed, 12135 insertions(+), 10 deletions(-) rename .github/workflows/{swift.yml => build-and-test.yml} (73%) create mode 100644 Package.swift create mode 100644 Sources/Subprocess/Errors.swift create mode 100644 Sources/Subprocess/Info.plist create mode 100644 Sources/Subprocess/Input.swift create mode 100644 Sources/Subprocess/Shell.swift create mode 100644 Sources/Subprocess/Subprocess.h create mode 100644 Sources/Subprocess/Subprocess.swift create mode 100644 Sources/Subprocess/SubprocessDependencyBuilder.swift create mode 100644 Sources/SubprocessMocks/Info.plist create mode 100644 Sources/SubprocessMocks/MockProcess.swift create mode 100644 Sources/SubprocessMocks/MockShell.swift create mode 100644 Sources/SubprocessMocks/MockSubprocess.swift create mode 100644 Sources/SubprocessMocks/MockSubprocessDependencyBuilder.swift create mode 100644 Sources/SubprocessMocks/SubprocessMocks.h create mode 100644 Subprocess.podspec create mode 100644 Subprocess.xcodeproj/project.pbxproj create mode 100644 Subprocess.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Subprocess.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Subprocess.xcodeproj/xcshareddata/xcschemes/Subprocess.xcscheme create mode 100644 Subprocess.xcodeproj/xcshareddata/xcschemes/SubprocessMocks.xcscheme create mode 100644 Tests/SystemTests/Info.plist create mode 100644 Tests/SystemTests/ShellTests.swift create mode 100644 Tests/UnitTests/Info.plist create mode 100644 Tests/UnitTests/ShellTests.swift create mode 100644 Tests/UnitTests/SubprocessTests.swift create mode 100644 docs/Classes.html create mode 100644 docs/Classes/Shell.html create mode 100644 docs/Classes/Shell/OutputOptions.html create mode 100644 docs/Classes/Subprocess.html create mode 100644 docs/Enums.html create mode 100644 docs/Enums/SubprocessError.html create mode 100644 docs/Protocols.html create mode 100644 docs/Protocols/SubprocessDependencyFactory.html create mode 100644 docs/Structs.html create mode 100644 docs/Structs/Input.html create mode 100644 docs/Structs/Input/Value.html create mode 100644 docs/Structs/SubprocessDependencyBuilder.html create mode 100644 docs/badge.svg create mode 100644 docs/css/highlight.css create mode 100644 docs/css/jazzy.css create mode 100644 docs/docsets/Subprocess.docset/Contents/Info.plist create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell/OutputOptions.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Subprocess.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums/SubprocessError.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols/SubprocessDependencyFactory.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input/Value.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/SubprocessDependencyBuilder.html create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/badge.svg create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/highlight.css create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/jazzy.css create mode 100755 docs/docsets/Subprocess.docset/Contents/Resources/Documents/img/carat.png create mode 100755 docs/docsets/Subprocess.docset/Contents/Resources/Documents/img/dash.png create mode 100755 docs/docsets/Subprocess.docset/Contents/Resources/Documents/img/gh.png create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/index.html create mode 100755 docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jazzy.js create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jquery.min.js create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/Documents/search.json create mode 100644 docs/docsets/Subprocess.docset/Contents/Resources/docSet.dsidx create mode 100644 docs/docsets/Subprocess.tgz create mode 100755 docs/img/carat.png create mode 100755 docs/img/dash.png create mode 100755 docs/img/gh.png create mode 100644 docs/index.html create mode 100755 docs/js/jazzy.js create mode 100644 docs/js/jquery.min.js create mode 100644 docs/search.json diff --git a/.github/workflows/swift.yml b/.github/workflows/build-and-test.yml similarity index 73% rename from .github/workflows/swift.yml rename to .github/workflows/build-and-test.yml index 27768cd..ca939de 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/build-and-test.yml @@ -1,6 +1,6 @@ -name: Swift +name: Build & Test -on: [ push, pull_request ] +on: [push] jobs: spm: @@ -21,10 +21,4 @@ jobs: run: xcodebuild build -scheme 'SubprocessMocks' -derivedDataPath .build - name: Run tests run: xcodebuild test -scheme 'Subprocess' -derivedDataPath .build - cocoapods: - name: Cocoapod - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - name: Spec lint - run: pod spec lint --verbose + diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..c5f123f --- /dev/null +++ b/Package.swift @@ -0,0 +1,18 @@ +// swift-tools-version:5.1 + +import PackageDescription + +let package = Package( + name: "Subprocess", + platforms: [ .macOS(.v10_12) ], + products: [ + .library(name: "Subprocess", targets: [ "Subprocess" ]), + .library(name: "SubprocessMocks", targets: [ "SubprocessMocks" ]) + ], + targets: [ + .target( name: "Subprocess", dependencies: []), + .target( name: "SubprocessMocks", dependencies: [ "Subprocess" ]), + .testTarget(name: "UnitTests", dependencies: [ "Subprocess", "SubprocessMocks" ]), + .testTarget(name: "SystemTests", dependencies: [ "Subprocess" ]) + ] +) diff --git a/README.md b/README.md index 04733c4..c160c0b 100644 --- a/README.md +++ b/README.md @@ -1 +1,150 @@ -# Subprocess \ No newline at end of file +# Subprocess +![Build](https://github.com/jamf/Subprocess/workflows/Build%20&%20Test/badge.svg) +[![License](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](http://mit-license.org) +[![Platform](https://img.shields.io/badge/platform-macOS-lightgrey.svg?style=flat)](https://developer.apple.com/macos) +[![Language](http://img.shields.io/badge/language-Swift-lightgrey.svg?style=flat)](https://developer.apple.com/swift) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![SwiftPM compatible](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) +[![Documentation](docs/badge.svg)](./docs/index.html) + +Subprocess is a Swift library for macOS providing interfaces for both synchronous and asynchronous process execution. +SubprocessMocks can be used in unit tests for quick and highly customizable mocking and verification of Subprocess usage. + +- [Usage](#usage) + - [Shell](#shell-class) + - [Input](#command-input) - [Data](#input-for-data), [Text](#input-for-text), [File](#input-for-file-url) + - [Output](#command-output) - [Data](#output-as-data), [Text](#output-as-string), [JSON](#output-as-json), [Decodable JSON object](#output-as-decodable-object-from-json), [Property list](#output-as-property-list), [Decodable property list object](#output-as-decodable-object-from-property-list) + - [Subprocess](#subprocess-class) +- [Installation](#installation) + - [SwiftPM](#swiftpm) + - [Cocoapods](#cocoapods) + - [Carthage](#carthage) + +[Full Documentation](./docs/index.html) + +# Usage +### Shell Class +The Shell class can be used for synchronous command execution. + +#### Command Input + +###### Input for data +```swift +let inputData: Data = ... +let data = try Shell(["/usr/bin/grep", "Hello"]).exec(input: .data(inputData)) +``` +###### Input for text +```swift +let data = try Shell(["/usr/bin/grep", "Hello"]).exec(input: .text("Hello world")) +``` +###### Input for file URL +```swift +let url = URL(fileURLWithPath: "/path/to/input/file") +let data = try Shell(["/usr/bin/grep", "foo"]).exec(input: .file(url: url)) +``` +###### Input for file path +```swift +let data = try Shell(["/usr/bin/grep", "foo"]).exec(input: .file(path: "/path/to/input/file")) +``` + +#### Command Output + +###### Output as Data +```swift +let data = try Shell(["/usr/bin/sw_vers"]).exec() +``` +###### Output as String +```swift +let text = try Shell(["/usr/bin/sw_vers"]).exec(encoding: .utf8) +``` +###### Output as JSON (Array or Dictionary) +```swift +let command = ["/usr/bin/log", "show", "--style", "json", "--last", "5m"] +let logs: [[String: Any]] = try Shell(command).execJSON()) +``` +###### Output as decodable object from JSON +```swift +struct LogMessage: Codable { + var subsystem: String + var category: String + var machTimestamp: UInt64 +} +let command = ["/usr/bin/log", "show", "--style", "json", "--last", "5m"] +let logs: [LogMessage] = try Shell(command).exec(decoder: JSONDecoder()) +``` +###### Output as Property List (Array or Dictionary) +```swift +let command = ["/bin/cat", "/System/Library/CoreServices/SystemVersion.plist"] +let dictionary: [String: Any] = try Shell(command).execPropertyList()) +``` +###### Output as decodable object from Property List +```swift +struct SystemVersion: Codable { + enum CodingKeys: String, CodingKey { + case version = "ProductVersion" + } + var version: String +} +let command = ["/bin/cat", "/System/Library/CoreServices/SystemVersion.plist"] +let result: SystemVersion = try Shell(command).exec(decoder: PropertyListDecoder()) +``` +###### Output mapped to other type +```swift +let enabled = try Shell(["csrutil", "status"]).exec(encoding: .utf8) { _, txt in txt.contains("enabled") } +``` + +###### Output options +```swift +let command: [String] = ... +let errorText = try Shell(command).exec(options: .stderr, encoding: .utf8) +let outputText = try Shell(command).exec(options: .stdout, encoding: .utf8) +let combinedData = try Shell(command).exec(options: .combined) +``` +### Subprocess Class +The Subprocess class can be used for asynchronous command execution. + +###### Handling output as it is read +```swift +let command: [String] = ... +let process = Subprocess(command) + +// The outputHandler and errorHandler are invoked serially +try process.launch(outputHandler: { data in + // Handle new data read from stdout +}, errorHandler: { data in + // Handle new data read from stderr +}, terminationHandler: { process in + // Handle process termination, all scheduled calls to + // the outputHandler and errorHandler are guaranteed to + // have completed. +}) +``` +###### Handling output on termination +```swift +let command: [String] = ... +let process = Subprocess(command) + +try process.launch { (process, outputData, errorData) in + if process.exitCode == 0 { + // Do something with output data + } else { + // Handle failure + } +} +``` + +## Installation +### SwiftPM +```swift +dependencies: [ + .package(url: "https://github.com/jamf/Subprocess.git", from: "1.0.0") +] +``` +### Cocoapods +```ruby +pod 'Subprocess' +``` +### Carthage +```ruby +github 'jamf/Subprocess' +``` \ No newline at end of file diff --git a/Sources/Subprocess/Errors.swift b/Sources/Subprocess/Errors.swift new file mode 100644 index 0000000..187e99d --- /dev/null +++ b/Sources/Subprocess/Errors.swift @@ -0,0 +1,47 @@ +// +// Errors.swift +// Subprocess +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation + +/// Type representing possible errors +public enum SubprocessError: Error { + + /// The process completed with a non-zero exit code + case exitedWithNonZeroStatus(Int32) + + /// The property list object could not be cast to expected type + case unexpectedPropertyListObject(String) + + /// The JSON object could not be cast to expected type + case unexpectedJSONObject(String) + + /// Input string could not be encoded + case inputStringEncodingError + + /// Output string could not be encoded + case outputStringEncodingError +} diff --git a/Sources/Subprocess/Info.plist b/Sources/Subprocess/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Sources/Subprocess/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Sources/Subprocess/Input.swift b/Sources/Subprocess/Input.swift new file mode 100644 index 0000000..4c9f4b6 --- /dev/null +++ b/Sources/Subprocess/Input.swift @@ -0,0 +1,89 @@ +// +// Input.swift +// Subprocess +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +import Foundation + +/// Interface representing input to the process +public struct Input { + + /// Reference to the input value + public enum Value { + + /// Data to be written to stdin of the child process + case data(Data) + + /// Text to be written to stdin of the child process + case text(String, String.Encoding) + + /// File to be written to stdin of the child process + case file(URL) + } + + /// Reference to the input value + public let value: Value + + /// Creates input for writing data to stdin of the child process + /// - Parameter data: Data written to stdin of the child process + /// - Returns: New Input instance + public static func data(_ data: Data) -> Input { + return Input(value: .data(data)) + } + + /// Creates input for writing text to stdin of the child process + /// - Parameter text: Text written to stdin of the child process + /// - Returns: New Input instance + public static func text(_ text: String, encoding: String.Encoding = .utf8) -> Input { + return Input(value: .text(text, encoding)) + } + + /// Creates input for writing contents of file at path to stdin of the child process + /// - Parameter path: Path to file written to stdin of the child process + /// - Returns: New Input instance + public static func file(path: String) -> Input { + return Input(value: .file(URL(fileURLWithPath: path))) + } + + /// Creates input for writing contents of file URL to stdin of the child process + /// - Parameter url: URL for file written to stdin of the child process + /// - Returns: New Input instance + public static func file(url: URL) -> Input { + return Input(value: .file(url)) + } + + /// Creates file handle or pipe for given input + /// - Returns: New FileHandle or Pipe + func createPipeOrFileHandle() throws -> Any { + switch value { + case .data(let data): + return SubprocessDependencyBuilder.shared.makeInputPipe(data: data) + case .text(let text, let encoding): + guard let data = text.data(using: encoding) else { throw SubprocessError.inputStringEncodingError } + return SubprocessDependencyBuilder.shared.makeInputPipe(data: data) + case .file(let url): + return try SubprocessDependencyBuilder.shared.makeInputFileHandle(url: url) + } + } +} diff --git a/Sources/Subprocess/Shell.swift b/Sources/Subprocess/Shell.swift new file mode 100644 index 0000000..625a114 --- /dev/null +++ b/Sources/Subprocess/Shell.swift @@ -0,0 +1,193 @@ +// +// Shell.swift +// Subprocess +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation + +/// Class used for synchronous process execution +public class Shell { + + /// OptionSet representing output handling + public struct OutputOptions: OptionSet { + public let rawValue: Int + + /// Processes data written to stdout + public static let stdout = OutputOptions(rawValue: 1 << 0) + + /// Processes data written to stderr + public static let stderr = OutputOptions(rawValue: 1 << 1) + + /// Processes data written to both stdout and stderr + public static let combined: OutputOptions = [ .stdout, .stderr ] + public init(rawValue: Int) { self.rawValue = rawValue } + } + + /// Reference to subprocess + public let process: Subprocess + + /// Creates new Shell instance + /// + /// - Parameter command: Command represented as an array of strings + public init(_ command: [String]) { + process = Subprocess(command) + } + + /// Executes shell command using a supplied block to tranform the process output into whatever type you would like + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - transformBlock: Block executed given a reference to the completed process and the output + /// - Returns: Process output as output type of the `transformBlock` + /// - Throws: Error from process launch,`transformBlock` or failing create a string from the process output + public func exec(input: Input? = nil, + options: OutputOptions = .stdout, + transformBlock: (_ process: Subprocess, _ data: Data) throws -> T) throws -> T { + var buffer = Data() + try process.launch(input: input, + outputHandler: options.contains(.stdout) ? { buffer.append($0) } : nil, + errorHandler: options.contains(.stderr) ? { buffer.append($0) } : nil) + process.waitForTermination() + return try transformBlock(process, buffer) + } + + /// Executes shell command expecting exit code of zero and returning the output data + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - Returns: Process output data + /// - Throws: Error from process launch or if termination code is none-zero + public func exec(input: Input? = nil, options: OutputOptions = .stdout) throws -> Data { + return try exec(input: input, options: options) { process, data in + let exitCode = process.exitCode + guard exitCode == 0 else { throw SubprocessError.exitedWithNonZeroStatus(exitCode) } + return data + } + } + + /// Executes shell command using a supplied block to tranform the process output as a String + /// into whatever type you would like + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - encoding: Encoding to use for the output + /// - transformBlock: Block executed given a reference to the completed process and the output as a string + /// - Returns: Process output as output type of the `transformBlock` + /// - Throws: Error from process launch,`transformBlock` or failing create a string from the process output + public func exec(input: Input? = nil, + options: OutputOptions = .stdout, + encoding: String.Encoding, + transformBlock: (_ process: Subprocess, _ string: String) throws -> T) throws -> T { + return try exec(input: input, options: options) { process, data in + guard let text = String(data: data, encoding: encoding) else { + throw SubprocessError.outputStringEncodingError + } + return try transformBlock(process, text) + } + } + + /// Executes shell command expecting exit code of zero and returning the output as a string + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - encoding: Encoding to use for the output + /// - Returns: Process output as a String + /// - Throws: Error from process launch, if termination code is none-zero or failing create a string from the output + public func exec(input: Input? = nil, + options: OutputOptions = .stdout, + encoding: String.Encoding) throws -> String { + return try exec(input: input, options: options, encoding: encoding) { process, text in + let exitCode = process.exitCode + guard exitCode == 0 else { throw SubprocessError.exitedWithNonZeroStatus(exitCode) } + return text + } + } + + /// Executes shell command expecting JSON + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - Returns: Process output as an Array or Dictionary + /// - Throws: Error from process launch, JSONSerialization or failing to cast to expected type + public func execJSON(input: Input? = nil, options: OutputOptions = .stdout) throws -> T { + return try exec(input: input, options: options) { _, data in + let object = try JSONSerialization.jsonObject(with: data, options: []) + guard let value = object as? T else { + throw SubprocessError.unexpectedJSONObject(String(describing: type(of: object))) + } + return value + } + } + + /// Executes shell command expecting a property list + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - Returns: Process output as an Array or Dictionary + /// - Throws: Error from process launch, PropertyListSerialization or failing to cast to expected type + public func execPropertyList(input: Input? = nil, options: OutputOptions = .stdout) throws -> T { + return try exec(input: input, options: options) { _, data in + let object = try PropertyListSerialization.propertyList(from: data, options: [], format: .none) + guard let value = object as? T else { + throw SubprocessError.unexpectedPropertyListObject(String(describing: type(of: object))) + } + return value + } + } + + /// Executes shell command expecting JSON and decodes object conforming to Decodable + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - decoder: JSONDecoder instance used for decoding the output object + /// - Returns: Process output as the decodable object type + /// - Throws: Error from process launch or JSONDecoder + public func exec(input: Input? = nil, + options: OutputOptions = .stdout, + decoder: JSONDecoder) throws -> T { + return try exec(input: input, options: options) { _, data in try decoder.decode(T.self, from: data) } + } + + /// Executes shell command expecting property list and decodes object conforming to Decodable + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process (Default: nil) + /// - options: Output options defining the output to process (Default: .stdout) + /// - decoder: PropertyListDecoder instance used for decoding the output object + /// - Returns: Process output as the decodable object type + /// - Throws: Error from process launch or PropertyListDecoder + public func exec(input: Input? = nil, + options: OutputOptions = .stdout, + decoder: PropertyListDecoder) throws -> T { + return try exec(input: input, options: options) { _, data in try decoder.decode(T.self, from: data) } + } +} diff --git a/Sources/Subprocess/Subprocess.h b/Sources/Subprocess/Subprocess.h new file mode 100644 index 0000000..ce53257 --- /dev/null +++ b/Sources/Subprocess/Subprocess.h @@ -0,0 +1,34 @@ +// +// Subprocess.h +// Subprocess +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#import + +//! Project version number for Subprocess. +FOUNDATION_EXPORT double SubprocessVersionNumber; + +//! Project version string for Subprocess. +FOUNDATION_EXPORT const unsigned char SubprocessVersionString[]; diff --git a/Sources/Subprocess/Subprocess.swift b/Sources/Subprocess/Subprocess.swift new file mode 100644 index 0000000..fcbd7fc --- /dev/null +++ b/Sources/Subprocess/Subprocess.swift @@ -0,0 +1,162 @@ +// +// Subprocess.swift +// Subprocess +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation + +/// Class used for asynchronous process execution +open class Subprocess { + + /// Process reference + let reference: Process + + /// Process identifier + public var pid: Int32 { reference.processIdentifier } + + /// Exit code of the process + public var exitCode: Int32 { reference.terminationStatus } + + /// Returns whether the process is still running. + public var isRunning: Bool { reference.isRunning } + + /// Reason for process termination + public var terminationReason: Process.TerminationReason { reference.terminationReason } + + /// Creates new Subprocess + /// + /// - Parameter command: Command represented as an array of strings + public init(_ command: [String], qos: DispatchQoS = .default) { + reference = SubprocessDependencyBuilder.shared.makeProcess(command: command) + queue = DispatchQueue(label: "SubprocessQueue", + qos: qos, + attributes: [], + autoreleaseFrequency: .workItem, + target: nil) + + } + + /// Launches command with read handlers and termination handler + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process + /// - outputHandler: Block called whenever new data is read from standard output of the process + /// - errorHandler: Block called whenever new data is read from standard error of the process + /// - terminationHandler: Block called when process has terminated and all output handlers have returned + public func launch(input: Input? = nil, + outputHandler: ((Data) -> Void)? = nil, + errorHandler: ((Data) -> Void)? = nil, + terminationHandler: ((Subprocess) -> Void)? = nil) throws { + + reference.standardInput = try input?.createPipeOrFileHandle() + + if let handler = outputHandler { + reference.standardOutput = createPipeWithReadabilityHandler(handler) + } else { + reference.standardOutput = FileHandle.nullDevice + } + + if let handler = errorHandler { + reference.standardError = createPipeWithReadabilityHandler(handler) + } else { + reference.standardError = FileHandle.nullDevice + } + + group.enter() + reference.terminationHandler = { [weak self] _ in + self?.group.leave() + self?.reference.terminationHandler = nil + } + + group.notify(queue: queue) { + terminationHandler?(self) + } + + if #available(OSX 10.13, *) { + try reference.run() + } else { + reference.launch() + } + } + + /// Block type called for executing process returning data from standard out and standard error + public typealias DataTerminationHandler = (_ process: Subprocess, _ stdout: Data, _ stderr: Data) -> Void + + /// Launches command calling a block when process terminates + /// + /// - Parameters: + /// - input: File or data to write to standard input of the process + /// - terminationHandler: Block called with Subprocess, stdout Data, stderr Data + public func launch(input: Input? = nil, + terminationHandler: @escaping DataTerminationHandler) throws { + var stdoutBuffer = Data() + var stderrBuffer = Data() + try launch(input: input, outputHandler: { data in + stdoutBuffer.append(data) + }, errorHandler: { data in + stderrBuffer.append(data) + }, terminationHandler: { selfRef in + terminationHandler(selfRef, stdoutBuffer, stderrBuffer) + }) + } + + /// Suspends the command + public func suspend() -> Bool { + return reference.suspend() + } + + /// Resumes the command which was suspended + public func resume() -> Bool { + return reference.resume() + } + + /// Sends the command the term signal + public func kill() { + reference.terminate() + } + + /// Waits for process to complete and all handlers to be called + public func waitForTermination() { + group.wait() + } + + private let group = DispatchGroup() + private let queue: DispatchQueue + + private func createPipeWithReadabilityHandler(_ handler: @escaping (Data) -> Void) -> Pipe { + let pipe = Pipe() + group.enter() + pipe.fileHandleForReading.readabilityHandler = { handle in + let data = handle.availableData + if data.isEmpty { + self.queue.async { self.group.leave() } + handle.readabilityHandler = nil + } else { + self.queue.async { handler(data) } + } + } + return pipe + } +} diff --git a/Sources/Subprocess/SubprocessDependencyBuilder.swift b/Sources/Subprocess/SubprocessDependencyBuilder.swift new file mode 100644 index 0000000..fe40dae --- /dev/null +++ b/Sources/Subprocess/SubprocessDependencyBuilder.swift @@ -0,0 +1,87 @@ +// +// SubprocessDependencyBuilder.swift +// Subprocess +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation + +/// Protocol call used for dependency injection +public protocol SubprocessDependencyFactory { + /// Creates new Subprocess + /// + /// - Parameter command: Command represented as an array of strings + /// - Returns: New Subprocess instance + func makeProcess(command: [String]) -> Process + + /// Creates a FileHandle for reading + /// + /// - Parameter url: File URL + /// - Returns: New FileHandle for reading + /// - Throws: When unable to open file for reading + func makeInputFileHandle(url: URL) throws -> FileHandle + + /// Creates a Pipe and writes given data + /// + /// - Parameter data: Data to write to the Pipe + /// - Returns: New Pipe instance + func makeInputPipe(data: Data) -> Pipe +} + +/// Default implementation of SubprocessDependencyFactory +public struct SubprocessDependencyBuilder: SubprocessDependencyFactory { + + /// Shared instance used for dependency creatation + public static var shared: SubprocessDependencyFactory = SubprocessDependencyBuilder() + + public func makeProcess(command: [String]) -> Process { + var tmp = command + let process = Process() + if #available(OSX 10.13, *) { + process.executableURL = URL(fileURLWithPath: tmp.removeFirst()) + } else { + process.launchPath = tmp.removeFirst() + } + process.arguments = tmp + return process + } + + public func makeInputFileHandle(url: URL) throws -> FileHandle { + return try FileHandle(forReadingFrom: url) + } + + public func makeInputPipe(data: Data) -> Pipe { + let pipe = Pipe() + pipe.fileHandleForWriting.writeabilityHandler = { handle in + handle.write(data) + handle.writeabilityHandler = nil + if #available(OSX 10.15, *) { + try? handle.close() + } else { + handle.closeFile() + } + } + return pipe + } +} diff --git a/Sources/SubprocessMocks/Info.plist b/Sources/SubprocessMocks/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Sources/SubprocessMocks/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Sources/SubprocessMocks/MockProcess.swift b/Sources/SubprocessMocks/MockProcess.swift new file mode 100644 index 0000000..77138ff --- /dev/null +++ b/Sources/SubprocessMocks/MockProcess.swift @@ -0,0 +1,203 @@ +// +// MockProcess.swift +// SubprocessMocks +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import Subprocess + +/// Interface used for mocking a process +public struct MockProcess { + + /// The underlying `MockProcessReference` + public var reference: MockProcessReference + + /// Writes given data to standard out of the mock child process + /// - Parameter data: Data to write to standard out of the mock child process + public func writeTo(stdout data: Data) { + reference.standardOutputPipe?.fileHandleForWriting.write(data) + } + + /// Writes given text to standard out of the mock child process + /// - Parameter text: Text to write to standard out of the mock child process + public func writeTo(stdout text: String, encoding: String.Encoding = .utf8) { + guard let data = text.data(using: encoding) else { return } + reference.standardOutputPipe?.fileHandleForWriting.write(data) + } + + /// Writes given data to standard error of the mock child process + /// - Parameter data: Data to write to standard error of the mock child process + public func writeTo(stderr data: Data) { + reference.standardErrorPipe?.fileHandleForWriting.write(data) + } + + /// Writes given text to standard error of the mock child process + /// - Parameter text: Text to write to standard error of the mock child process + public func writeTo(stderr text: String, encoding: String.Encoding = .utf8) { + guard let data = text.data(using: encoding) else { return } + reference.standardErrorPipe?.fileHandleForWriting.write(data) + } + + /// Completes the mock process execution + /// - Parameters: + /// - statusCode: Exit code of the process (Default: 0) + /// - reason: Termination reason of the process (Default: .exit) + public func exit(withStatus statusCode: Int32 = 0, reason: Process.TerminationReason = .exit) { + reference.exit(withStatus: statusCode, reason: reason) + } +} + +/// Subclass of `Process` used for mocking +open class MockProcessReference: Process { + // swiftlint:disable nesting + + /// Context information and values used for overriden properties + public struct Context { + + /// State of the mock process + public enum State { + case initialized + case running + case uncaughtSignal + case exited + } + public var terminationStatus: Int32 = 0 + public var processIdentifier: Int32 = -1 + public var state: State = .initialized + + /// Block called to stub the call to launch + public var runStub: (MockProcess) throws -> Void + + var standardInput: Any? + var standardOutput: Any? + var standardError: Any? + var terminationHandler: ((Process) -> Void)? + } + // swiftlint:enable nesting + + public var context: Context + + /// Creates a new `MockProcessReference` which throws an error on launch + /// - Parameter error: Error thrown when `Process.run` is called + public init(withRunError error: Error) { + context = Context(runStub: { _ in throw error }) + } + + /// Creates a new `MockProcessReference` calling run stub block + /// - Parameter block: Block used to stub `Process.run` + public init(withRunBlock block: @escaping (MockProcess) -> Void) { + context = Context(runStub: { mock in + DispatchQueue.global(qos: .userInitiated).async { + block(mock) + } + }) + } + + /// Block called when `Process.terminate` is called + public var stubTerminate: ((MockProcessReference) -> Void)? + + /// Block called when `Process.resume` is called + public var stubResume: (() -> Bool)? + + /// Block called when `Process.suspend` is called + public var stubSuspend: (() -> Bool)? + + /// standardOutput object as a Pipe + public var standardOutputPipe: Pipe? { standardOutput as? Pipe } + + /// standardError object as a Pipe + public var standardErrorPipe: Pipe? { standardError as? Pipe } + + /// Completes the mock process execution + /// - Parameters: + /// - statusCode: Exit code of the process (Default: 0) + /// - reason: Termination reason of the process (Default: .exit) + public func exit(withStatus statusCode: Int32 = 0, reason: Process.TerminationReason = .exit) { + guard context.state == .running else { return } + context.state = (reason == .exit) ? .exited : .uncaughtSignal + context.terminationStatus = statusCode + if #available(OSX 10.15, *) { + try? standardOutputPipe?.fileHandleForWriting.close() + try? standardErrorPipe?.fileHandleForWriting.close() + } else { + standardOutputPipe?.fileHandleForWriting.closeFile() + standardErrorPipe?.fileHandleForWriting.closeFile() + } + + guard let handler = terminationHandler else { return } + terminationHandler = nil + handler(self) + } + + open override var terminationReason: TerminationReason { + context.state == .uncaughtSignal ? .uncaughtSignal : .exit + } + + open override var terminationStatus: Int32 { context.terminationStatus } + + open override var processIdentifier: Int32 { context.processIdentifier } + + open override var isRunning: Bool { context.state == .running } + + open override func run() throws { + guard context.state == .initialized else { return } + context.state = .running + context.processIdentifier = .random(in: 0...Int32.max) + let mock = MockProcess(reference: self) + try context.runStub(mock) + } + + open override func terminate() { + if let stub = stubTerminate { + stub(self) + } else { + context.state = .uncaughtSignal + } + } + + open override var standardInput: Any? { + get { context.standardInput } + set { context.standardInput = newValue } + } + + open override var standardOutput: Any? { + get { context.standardOutput } + set { context.standardOutput = newValue } + } + + open override var standardError: Any? { + get { context.standardError } + set { context.standardError = newValue } + } + + open override var terminationHandler: ((Process) -> Void)? { + get { context.terminationHandler } + set { context.terminationHandler = newValue } + } + + open override func resume() -> Bool { stubResume?() ?? false } + + open override func suspend() -> Bool { stubSuspend?() ?? false } +} diff --git a/Sources/SubprocessMocks/MockShell.swift b/Sources/SubprocessMocks/MockShell.swift new file mode 100644 index 0000000..fa24b89 --- /dev/null +++ b/Sources/SubprocessMocks/MockShell.swift @@ -0,0 +1,339 @@ +// +// MockShell.swift +// SubprocessMocks +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import Subprocess + +extension Shell: SubprocessMockObject {} + +public extension Shell { + + /// Adds a mock for a command which throws an error when `Process.run` is called + /// + /// - Parameters: + /// - command: The command to mock + /// - error: Error thrown when `Process.run` is called + static func stub(_ command: [String], error: Error) { + Subprocess.stub(command, error: error) + } + + /// Adds a mock for a command which writes the given data to the outputs and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - standardOutput: Data written to stdout of the process + /// - standardError: Data written to stderr of the process + /// - exitCode: Exit code of the process (Default: 0) + static func stub(_ command: [String], + standardOutput: Data? = nil, + standardError: Data? = nil, + exitCode: Int32 = 0) { + Subprocess.stub(command) { process in + if let data = standardOutput { + process.writeTo(stdout: data) + } + if let data = standardError { + process.writeTo(stderr: data) + } + process.exit(withStatus: exitCode) + } + } + + /// Adds a mock for a command which writes the given text to the outputs and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - stdout: String written to stdout of the process + /// - stderr: String written to stderr of the process (Default: nil) + /// - exitCode: Exit code of the process (Default: 0) + static func stub(_ command: [String], + stdout: String, + stderr: String? = nil, + exitCode: Int32 = 0) { + Subprocess.stub(command) { process in + process.writeTo(stdout: stdout) + if let text = stderr { + process.writeTo(stderr: text) + } + process.exit(withStatus: exitCode) + } + } + + /// Adds a mock for a command which writes the given text to stderr and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - stdout: String written to stdout of the process + /// - stderr: String written to stderr of the process + /// - exitCode: Exit code of the process (Default: 0) + static func stub(_ command: [String], + stderr: String, + exitCode: Int32 = 0) { + Subprocess.stub(command) { process in + process.writeTo(stderr: stderr) + process.exit(withStatus: exitCode) + } + } + + /// Adds a mock for a command which writes the given property list to stdout and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - plist: Property list object serialized and written to stdout + /// - exitCode: Exit code of the process (Default: 0) + /// - Throws: Error when serializing property list object + static func stub(_ command: [String], + plist: Any, + exitCode: Int32 = 0) throws { + let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) + Shell.stub(command, standardOutput: data, exitCode: exitCode) + } + + /// Adds a mock for a command which writes the given JSON object to stdout and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - plist: JSON object serialized and written to stdout + /// - exitCode: Exit code of the process (Default: 0) + /// - Throws: Error when serializing JSON object + static func stub(_ command: [String], + json: Any, + exitCode: Int32 = 0) throws { + let data = try JSONSerialization.data(withJSONObject: json, options: []) + Shell.stub(command, standardOutput: data, exitCode: exitCode) + } + + /// Adds a mock for a command which writes the given encodable object as a property list to stdout + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - plistObject: Encodable object written to stdout as a property list + /// - exitCode: Exit code of the process (Default: 0) + /// - Throws: Error when encoding the provided object + static func stub(_ command: [String], + plistObject: T, + exitCode: Int32 = 0) throws { + let data = try PropertyListEncoder().encode(plistObject) + Shell.stub(command, standardOutput: data, exitCode: exitCode) + } + + /// Adds a mock for a command which writes the given encodable object as JSON to stdout + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - jsonObject: Encodable object written to stdout as JSON + /// - exitCode: Exit code of the process (Default: 0) + /// - Throws: Error when encoding the provided object + static func stub(_ command: [String], + jsonObject: T, + exitCode: Int32 = 0) throws { + let data = try JSONEncoder().encode(jsonObject) + Shell.stub(command, standardOutput: data, exitCode: exitCode) + } + + // MARK: - + + /// Adds an expected mock for a command which throws an error when `Process.run` is called + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - error: Error thrown when `Process.run` is called + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + static func expect(_ command: [String], + input: Input? = nil, + error: Error, + file: StaticString = #file, + line: UInt = #line) { + Subprocess.expect(command, input: input, error: error, file: file, line: line) + } + + /// Adds an expected mock for a command which writes the given data to the outputs + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - standardOutput: Data written to stdout of the process + /// - standardError: Data written to stderr of the process + /// - exitCode: Exit code of the process (Default: 0) + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + static func expect(_ command: [String], + input: Input? = nil, + standardOutput: Data? = nil, + standardError: Data? = nil, + exitCode: Int32 = 0, + file: StaticString = #file, + line: UInt = #line) { + Subprocess.expect(command, input: input, file: file, line: line) { process in + if let data = standardOutput { + process.writeTo(stdout: data) + } + if let data = standardError { + process.writeTo(stderr: data) + } + process.exit(withStatus: exitCode) + } + } + + /// Adds an expected mock for a command which writes the given text to the outputs + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - stdout: String written to stdout of the process + /// - stderr: String written to stderr of the process (Default: nil) + /// - exitCode: Exit code of the process (Default: 0) + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + static func expect(_ command: [String], + input: Input? = nil, + stdout: String, + stderr: String? = nil, + exitCode: Int32 = 0, + file: StaticString = #file, + line: UInt = #line) { + Subprocess.expect(command, input: input, file: file, line: line) { process in + process.writeTo(stdout: stdout) + if let text = stderr { + process.writeTo(stderr: text) + } + process.exit(withStatus: exitCode) + } + } + + /// Adds an expected mock for a command which writes the given text to stderr and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - stdout: String written to stdout of the process + /// - stderr: String written to stderr of the process + /// - exitCode: Exit code of the process (Default: 0) + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + static func expect(_ command: [String], + input: Input? = nil, + stderr: String, + exitCode: Int32 = 0, + file: StaticString = #file, + line: UInt = #line) { + Subprocess.expect(command, input: input, file: file, line: line) { process in + process.writeTo(stderr: stderr) + process.exit(withStatus: exitCode) + } + } + + /// Adds an expected mock for a command which writes the given property list to stdout + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - plist: Property list object serialized and written to stdout + /// - exitCode: Exit code of the process (Default: 0) + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + /// - Throws: Error when serializing property list object + static func expect(_ command: [String], + input: Input? = nil, + plist: Any, + exitCode: Int32 = 0, + file: StaticString = #file, + line: UInt = #line) throws { + let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) + Shell.expect(command, input: input, standardOutput: data, exitCode: exitCode, file: file, line: line) + } + + /// Adds an expected mock for a command which writes the given JSON object to stdout + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - plist: JSON object serialized and written to stdout + /// - exitCode: Exit code of the process (Default: 0) + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + /// - Throws: Error when serializing JSON object + static func expect(_ command: [String], + input: Input? = nil, + json: Any, + exitCode: Int32 = 0, + file: StaticString = #file, + line: UInt = #line) throws { + let data = try JSONSerialization.data(withJSONObject: json, options: []) + Shell.expect(command, input: input, standardOutput: data, exitCode: exitCode, file: file, line: line) + } + + /// Adds an expected mock for a command which writes the given encodable object as a property list to stdout + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - plistObject: Encodable object written to stdout as a property list + /// - exitCode: Exit code of the process (Default: 0) + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + /// - Throws: Error when encoding the provided object + static func expect(_ command: [String], + input: Input? = nil, + plistObject: T, + exitCode: Int32 = 0, + file: StaticString = #file, + line: UInt = #line) throws { + let data = try PropertyListEncoder().encode(plistObject) + Shell.expect(command, input: input, standardOutput: data, exitCode: exitCode, file: file, line: line) + } + + /// Adds an expected mock for a command which writes the given encodable object as JSON to stdout + /// and exits with the provided exit code + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - jsonObject: Encodable object written to stdout as JSON + /// - exitCode: Exit code of the process (Default: 0) + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + /// - Throws: Error when encoding the provided object + static func expect(_ command: [String], + input: Input? = nil, + jsonObject: T, + exitCode: Int32 = 0, + file: StaticString = #file, + line: UInt = #line) throws { + let data = try JSONEncoder().encode(jsonObject) + Shell.expect(command, input: input, standardOutput: data, exitCode: exitCode, file: file, line: line) + } +} diff --git a/Sources/SubprocessMocks/MockSubprocess.swift b/Sources/SubprocessMocks/MockSubprocess.swift new file mode 100644 index 0000000..d77312c --- /dev/null +++ b/Sources/SubprocessMocks/MockSubprocess.swift @@ -0,0 +1,90 @@ +// +// MockSubprocess.swift +// SubprocessMocks +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import Subprocess + +extension Subprocess: SubprocessMockObject {} + +public extension Subprocess { + + /// Adds a mock for a given command which throws an error when `Process.run` is called + /// + /// - Parameters: + /// - command: The command to mock + /// - error: Error thrown when `Process.run` is called + static func stub(_ command: [String], error: Error) { + let mock = MockProcessReference(withRunError: error) + MockSubprocessDependencyBuilder.shared.stub(command, process: mock) + } + + /// Adds a mock for a given command which calls the run block to mock process execution + /// + /// - Important: You must call`MockProcess.exit` for the process to complete + /// - Parameters: + /// - command: The command to mock + /// - runBlock: Block called with a `MockProcess` to mock process execution. + static func stub(_ command: [String], runBlock: ((MockProcess) -> Void)? = nil) { + let mock = MockProcessReference(withRunBlock: runBlock ?? { $0.exit() }) + MockSubprocessDependencyBuilder.shared.stub(command, process: mock) + } + + /// Adds an expected mock for a given command which throws an error when `Process.run` is called + /// + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - error: Error thrown when `Process.run` is called + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + static func expect(_ command: [String], + input: Input? = nil, + error: Error, + file: StaticString = #file, + line: UInt = #line) { + let mock = MockProcessReference(withRunError: error) + MockSubprocessDependencyBuilder.shared.expect(command, input: input, process: mock, file: file, line: line) + } + + /// Adds an expected mock for a given command which calls the run block to mock process execution + /// + /// - Important: You must call`MockProcess.exit` for the process to complete + /// - Parameters: + /// - command: The command to mock + /// - input: The expected input of the process + /// - file: Source file where expect was called (Default: #file) + /// - line: Line number of source file where expect was called (Default: #line) + /// - runBlock: Block called with a `MockProcess` to mock process execution + static func expect(_ command: [String], + input: Input? = nil, + file: StaticString = #file, + line: UInt = #line, + runBlock: ((MockProcess) -> Void)? = nil) { + let mock = MockProcessReference(withRunBlock: runBlock ?? { $0.exit() }) + MockSubprocessDependencyBuilder.shared.expect(command, input: input, process: mock, file: file, line: line) + } +} diff --git a/Sources/SubprocessMocks/MockSubprocessDependencyBuilder.swift b/Sources/SubprocessMocks/MockSubprocessDependencyBuilder.swift new file mode 100644 index 0000000..90c295b --- /dev/null +++ b/Sources/SubprocessMocks/MockSubprocessDependencyBuilder.swift @@ -0,0 +1,241 @@ +// +// MockSubprocessDependencyBuilder.swift +// SubprocessMocks +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import Subprocess + +/// Error representing a failed call to Subprocess.expect or Shell.expect +public struct ExpectationError: Error { + /// Source file where expect was called + public var file: StaticString + /// Line number where expect was called + public var line: UInt + /// Error message + public var message: String +} + +/// Type representing possible errors thrown +public enum MockSubprocessError: Error { + /// Error containing command thrown when a process is launched that was not stubbed + case missingMock([String]) + /// List of expectations which failed + case missedExpectations([ExpectationError]) +} + +public protocol SubprocessMockObject {} + +public extension SubprocessMockObject { + + /// Verifies expected stubs and resets mocking + /// - Throws: A `MockSubprocessError.missedExpectations` containing failed expectations + static func verify() throws { try MockSubprocessDependencyBuilder.shared.verify() } + + /// Verifies expected stubs and resets mocking + /// - Parameter missedBlock: Block called for each failed expectation + static func verify(missedBlock: (ExpectationError) -> Void) { + MockSubprocessDependencyBuilder.shared.verify(missedBlock: missedBlock) + } + + /// Resets mocking + static func reset() { MockSubprocessDependencyBuilder.shared.reset() } +} + +public class MockFileHandle: FileHandle { + public var url: URL? +} + +public class MockPipe: Pipe { + public var data: Data? +} + +class MockSubprocessDependencyBuilder { + + class MockItem { + var used = false + var command: [String] + var input: Input? + var process: MockProcessReference + var file: StaticString? + var line: UInt? + init(command: [String], + input: Input?, + process: MockProcessReference, + file: StaticString?, + line: UInt?) { + self.command = command + self.input = input + self.process = process + self.file = file + self.line = line + } + } + + var mocks: [MockItem] = [] + + static let shared = MockSubprocessDependencyBuilder() + + init() { SubprocessDependencyBuilder.shared = self } + + func stub(_ command: [String], process: MockProcessReference) { + let mock = MockItem(command: command, input: nil, process: process, file: nil, line: nil) + mocks.append(mock) + } + + func expect(_ command: [String], + input: Input?, + process: MockProcessReference, + file: StaticString, + line: UInt) { + let mock = MockItem(command: command, input: input, process: process, file: file, line: line) + mocks.append(mock) + } + + func verify(missedBlock block: (ExpectationError) -> Void) { + defer { reset() } + // All of the mocks for errors + mocks.forEach { + // Check if the file and line properties were set, this indicates it was an expected mock + guard let file = $0.file, let line = $0.line else { return } + + // Check if the mock was used + guard $0.used else { return block(ExpectationError(file: file, line: line, message: "Command not called")) } + + // Check the expected input + let expectedData: Data? + let expectedFile: URL? + switch $0.input?.value { + case .data(let data): + expectedData = data + expectedFile = nil + case .text(let string, let encoding): + expectedData = string.data(using: encoding) + expectedFile = nil + case .file(let url): + expectedData = nil + expectedFile = url + default: + expectedData = nil + expectedFile = nil + } + + let inputFile: URL? = ($0.process.standardInput as? MockFileHandle)?.url + let inputData: Data? = ($0.process.standardInput as? MockPipe)?.data + + let fileError = verifyInputFile(inputURL: inputFile, expectedURL: expectedFile, file: file, line: line) + if let error = fileError { return block(error) } + + let dataError = verifyInputData(inputData: inputData, expectedData: expectedData, file: file, line: line) + guard let error = dataError else { return } + block(error) + } + } + + private func verifyInputFile(inputURL: URL?, + expectedURL: URL?, + file: StaticString, + line: UInt) -> ExpectationError? { + if let expected = expectedURL { + if let url = inputURL { + if expected != url { + return ExpectationError(file: file, + line: line, + message: "Input file URLs do not match \(expected) != \(url)") + } + } else { + return ExpectationError(file: file, line: line, message: "Missing file input") + } + } else if let url = inputURL { + return ExpectationError(file: file, line: line, message: "Unexpected input file \(url)") + } + return nil + } + + private func verifyInputData(inputData: Data?, + expectedData: Data?, + file: StaticString, + line: UInt) -> ExpectationError? { + if let expectedData = expectedData { + if let inputData = inputData { + if inputData != expectedData { + if let input = String(data: inputData, encoding: .utf8), + let expected = String(data: inputData, encoding: .utf8) { + let errorText = "Input text does not match expected input text \(input) != \(expected)" + return ExpectationError(file: file, + line: line, + message: errorText) + } else { + return ExpectationError(file: file, + line: line, + message: "Input data does not match expected input data") + } + } + } else { + return ExpectationError(file: file, line: line, message: "Missing data input") + } + } else if let unexpectedData = inputData { + if let input = String(data: unexpectedData, encoding: .utf8) { + return ExpectationError(file: file, line: line, message: "Unexpected input text: \(input)") + } else { + return ExpectationError(file: file, line: line, message: "Unexpected input data") + } + } + return nil + } + + func verify() throws { + var errors: [ExpectationError] = [] + verify { errors.append($0) } + if errors.isEmpty { return } + throw MockSubprocessError.missedExpectations(errors) + } + + func reset() { + mocks = [] + } +} + +extension MockSubprocessDependencyBuilder: SubprocessDependencyFactory { + func makeProcess(command: [String]) -> Process { + if let item = mocks.first(where: { !$0.used && $0.command == command }) { + item.used = true + return item.process + } + return MockProcessReference(withRunError: MockSubprocessError.missingMock(command)) + } + + func makeInputFileHandle(url: URL) throws -> FileHandle { + let handle = MockFileHandle() + handle.url = url + return handle + } + + func makeInputPipe(data: Data) -> Pipe { + let pipe = MockPipe() + pipe.data = data + return pipe + } +} diff --git a/Sources/SubprocessMocks/SubprocessMocks.h b/Sources/SubprocessMocks/SubprocessMocks.h new file mode 100644 index 0000000..2b9cc16 --- /dev/null +++ b/Sources/SubprocessMocks/SubprocessMocks.h @@ -0,0 +1,34 @@ +// +// SubprocessMocks.h +// SubprocessMocks +// +// MIT License +// +// Copyright (c) 2018 Jamf Software +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#import + +//! Project version number for SubprocessMocks. +FOUNDATION_EXPORT double SubprocessMocksVersionNumber; + +//! Project version string for SubprocessMocks. +FOUNDATION_EXPORT const unsigned char SubprocessMocksVersionString[]; diff --git a/Subprocess.podspec b/Subprocess.podspec new file mode 100644 index 0000000..0ed76b4 --- /dev/null +++ b/Subprocess.podspec @@ -0,0 +1,36 @@ +Pod::Spec.new do |s| + s.name = 'Subprocess' + s.version = '1.0.0' + s.summary = 'Wrapper for NSTask used for running processes and shell commands on macOS.' + s.license = { :type => 'MIT', :text => "" } + s.description = <<-DESC + Everything related to creating processes and running shell commands on macOS. + DESC + s.homepage = 'https://github.com/jamf/Subprocess' + s.authors = { 'Cyrus Ingraham' => 'cyrus.ingraham@jamf.com' } + s.source = { :git => "https://github.com/jamf/Subprocess.git", :tag => s.version.to_s } + s.platform = :osx, '10.12' + s.osx.deployment_target = '10.12' + s.swift_version = '5.1' + s.default_subspec = 'Core' + + s.subspec 'Core' do |ss| + ss.source_files = 'Sources/Subprocess/*.swift' + end + + s.subspec 'Mocks' do |ss| + ss.source_files = 'Sources/SubprocessMocks/*.swift' + ss.dependency 'Subprocess/Core' + end + + s.test_spec 'UnitTests' do |test_spec| + test_spec.source_files = [ 'Tests/UnitTests/*.swift' ] + test_spec.dependency 'Subprocess/Core' + test_spec.dependency 'Subprocess/Mocks' + end + + s.test_spec 'SystemTests' do |test_spec| + test_spec.source_files = [ 'Tests/SystemTests/*.swift' ] + test_spec.dependency 'Subprocess/Core' + end +end diff --git a/Subprocess.xcodeproj/project.pbxproj b/Subprocess.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5b36e2e --- /dev/null +++ b/Subprocess.xcodeproj/project.pbxproj @@ -0,0 +1,816 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 6E1195E42416630500534F74 /* Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E1195E32416630500534F74 /* Input.swift */; }; + 6EDDC6C12410379F00E171C6 /* Subprocess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6A52410378100E171C6 /* Subprocess.swift */; }; + 6EDDC6C22410379F00E171C6 /* SubprocessDependencyBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6A62410378100E171C6 /* SubprocessDependencyBuilder.swift */; }; + 6EDDC6C32410379F00E171C6 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6A72410378100E171C6 /* Shell.swift */; }; + 6EDDC6C42410379F00E171C6 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6A82410378100E171C6 /* Errors.swift */; }; + 6EDDC6C5241037A300E171C6 /* Subprocess.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EDDC6A92410378100E171C6 /* Subprocess.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6EDDC6D6241037DA00E171C6 /* Subprocess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EDDC6382410369400E171C6 /* Subprocess.framework */; }; + 6EDDC6D7241037EE00E171C6 /* SubprocessMocks.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EDDC6AC2410378100E171C6 /* SubprocessMocks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6EDDC6D8241037F500E171C6 /* MockSubprocess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6AD2410378100E171C6 /* MockSubprocess.swift */; }; + 6EDDC6D9241037F500E171C6 /* MockShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6AE2410378100E171C6 /* MockShell.swift */; }; + 6EDDC6DA241037F500E171C6 /* MockSubprocessDependencyBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6AF2410378100E171C6 /* MockSubprocessDependencyBuilder.swift */; }; + 6EDDC6DB241037F500E171C6 /* MockProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6B12410378100E171C6 /* MockProcess.swift */; }; + 6EDDC6E52410391500E171C6 /* Subprocess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EDDC6382410369400E171C6 /* Subprocess.framework */; }; + 6EDDC6F92410395800E171C6 /* ShellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6B82410378100E171C6 /* ShellTests.swift */; }; + 6EDDC6FB2410396000E171C6 /* SubprocessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6BC2410378100E171C6 /* SubprocessTests.swift */; }; + 6EDDC6FC2410396000E171C6 /* ShellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EDDC6BD2410378100E171C6 /* ShellTests.swift */; }; + 6EDDC6FD24105F8300E171C6 /* SubprocessMocks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EDDC6CB241037CE00E171C6 /* SubprocessMocks.framework */; }; + 6EDDC6FE24105F8700E171C6 /* Subprocess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EDDC6382410369400E171C6 /* Subprocess.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 6EDDC6D3241037D500E171C6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6EDDC62F2410369400E171C6 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6EDDC6372410369400E171C6; + remoteInfo = Subprocess; + }; + 6EDDC6E62410391500E171C6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6EDDC62F2410369400E171C6 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6EDDC6372410369400E171C6; + remoteInfo = Subprocess; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 6E1195E32416630500534F74 /* Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Input.swift; sourceTree = ""; }; + 6EDDC6382410369400E171C6 /* Subprocess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Subprocess.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6EDDC6A22410378100E171C6 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; + 6EDDC6A52410378100E171C6 /* Subprocess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subprocess.swift; sourceTree = ""; }; + 6EDDC6A62410378100E171C6 /* SubprocessDependencyBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubprocessDependencyBuilder.swift; sourceTree = ""; }; + 6EDDC6A72410378100E171C6 /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; + 6EDDC6A82410378100E171C6 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; + 6EDDC6A92410378100E171C6 /* Subprocess.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Subprocess.h; sourceTree = ""; }; + 6EDDC6AA2410378100E171C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6EDDC6AC2410378100E171C6 /* SubprocessMocks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SubprocessMocks.h; sourceTree = ""; }; + 6EDDC6AD2410378100E171C6 /* MockSubprocess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSubprocess.swift; sourceTree = ""; }; + 6EDDC6AE2410378100E171C6 /* MockShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockShell.swift; sourceTree = ""; }; + 6EDDC6AF2410378100E171C6 /* MockSubprocessDependencyBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSubprocessDependencyBuilder.swift; sourceTree = ""; }; + 6EDDC6B02410378100E171C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6EDDC6B12410378100E171C6 /* MockProcess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockProcess.swift; sourceTree = ""; }; + 6EDDC6B22410378100E171C6 /* Subprocess.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Subprocess.podspec; sourceTree = ""; }; + 6EDDC6B82410378100E171C6 /* ShellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellTests.swift; sourceTree = ""; }; + 6EDDC6B92410378100E171C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6EDDC6BC2410378100E171C6 /* SubprocessTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubprocessTests.swift; sourceTree = ""; }; + 6EDDC6BD2410378100E171C6 /* ShellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellTests.swift; sourceTree = ""; }; + 6EDDC6BE2410378100E171C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6EDDC6BF2410378100E171C6 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 6EDDC6C02410378100E171C6 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + 6EDDC6CB241037CE00E171C6 /* SubprocessMocks.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SubprocessMocks.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6EDDC6E02410391500E171C6 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6EDDC6EF2410392400E171C6 /* SystemTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SystemTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6EDDC6352410369400E171C6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6C8241037CE00E171C6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6D6241037DA00E171C6 /* Subprocess.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6DD2410391500E171C6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6FD24105F8300E171C6 /* SubprocessMocks.framework in Frameworks */, + 6EDDC6E52410391500E171C6 /* Subprocess.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6EC2410392400E171C6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6FE24105F8700E171C6 /* Subprocess.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6EDDC62E2410369400E171C6 = { + isa = PBXGroup; + children = ( + 6EDDC6C02410378100E171C6 /* LICENSE */, + 6EDDC6A22410378100E171C6 /* Package.swift */, + 6EDDC6BF2410378100E171C6 /* README.md */, + 6EDDC6B22410378100E171C6 /* Subprocess.podspec */, + 6EDDC6A32410378100E171C6 /* Sources */, + 6EDDC6B32410378100E171C6 /* Tests */, + 6EDDC6392410369400E171C6 /* Products */, + 6EDDC6D5241037DA00E171C6 /* Frameworks */, + ); + sourceTree = ""; + }; + 6EDDC6392410369400E171C6 /* Products */ = { + isa = PBXGroup; + children = ( + 6EDDC6382410369400E171C6 /* Subprocess.framework */, + 6EDDC6CB241037CE00E171C6 /* SubprocessMocks.framework */, + 6EDDC6E02410391500E171C6 /* UnitTests.xctest */, + 6EDDC6EF2410392400E171C6 /* SystemTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 6EDDC6A32410378100E171C6 /* Sources */ = { + isa = PBXGroup; + children = ( + 6EDDC6A42410378100E171C6 /* Subprocess */, + 6EDDC6AB2410378100E171C6 /* SubprocessMocks */, + ); + path = Sources; + sourceTree = ""; + }; + 6EDDC6A42410378100E171C6 /* Subprocess */ = { + isa = PBXGroup; + children = ( + 6EDDC6A52410378100E171C6 /* Subprocess.swift */, + 6EDDC6A62410378100E171C6 /* SubprocessDependencyBuilder.swift */, + 6EDDC6A72410378100E171C6 /* Shell.swift */, + 6EDDC6A82410378100E171C6 /* Errors.swift */, + 6EDDC6A92410378100E171C6 /* Subprocess.h */, + 6EDDC6AA2410378100E171C6 /* Info.plist */, + 6E1195E32416630500534F74 /* Input.swift */, + ); + path = Subprocess; + sourceTree = ""; + }; + 6EDDC6AB2410378100E171C6 /* SubprocessMocks */ = { + isa = PBXGroup; + children = ( + 6EDDC6AC2410378100E171C6 /* SubprocessMocks.h */, + 6EDDC6AD2410378100E171C6 /* MockSubprocess.swift */, + 6EDDC6AE2410378100E171C6 /* MockShell.swift */, + 6EDDC6AF2410378100E171C6 /* MockSubprocessDependencyBuilder.swift */, + 6EDDC6B02410378100E171C6 /* Info.plist */, + 6EDDC6B12410378100E171C6 /* MockProcess.swift */, + ); + path = SubprocessMocks; + sourceTree = ""; + }; + 6EDDC6B32410378100E171C6 /* Tests */ = { + isa = PBXGroup; + children = ( + 6EDDC6BA2410378100E171C6 /* UnitTests */, + 6EDDC6B52410378100E171C6 /* SystemTests */, + ); + path = Tests; + sourceTree = ""; + }; + 6EDDC6B52410378100E171C6 /* SystemTests */ = { + isa = PBXGroup; + children = ( + 6EDDC6B82410378100E171C6 /* ShellTests.swift */, + 6EDDC6B92410378100E171C6 /* Info.plist */, + ); + path = SystemTests; + sourceTree = ""; + }; + 6EDDC6BA2410378100E171C6 /* UnitTests */ = { + isa = PBXGroup; + children = ( + 6EDDC6BC2410378100E171C6 /* SubprocessTests.swift */, + 6EDDC6BD2410378100E171C6 /* ShellTests.swift */, + 6EDDC6BE2410378100E171C6 /* Info.plist */, + ); + path = UnitTests; + sourceTree = ""; + }; + 6EDDC6D5241037DA00E171C6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 6EDDC6332410369400E171C6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6C5241037A300E171C6 /* Subprocess.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6C6241037CE00E171C6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6D7241037EE00E171C6 /* SubprocessMocks.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 6EDDC6372410369400E171C6 /* Subprocess */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6EDDC6402410369400E171C6 /* Build configuration list for PBXNativeTarget "Subprocess" */; + buildPhases = ( + 6EDDC6332410369400E171C6 /* Headers */, + 6EDDC6342410369400E171C6 /* Sources */, + 6EDDC6352410369400E171C6 /* Frameworks */, + 6EDDC6362410369400E171C6 /* Resources */, + 6E1195E124165F1500534F74 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Subprocess; + productName = Subprocess; + productReference = 6EDDC6382410369400E171C6 /* Subprocess.framework */; + productType = "com.apple.product-type.framework"; + }; + 6EDDC6CA241037CE00E171C6 /* SubprocessMocks */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6EDDC6D0241037CE00E171C6 /* Build configuration list for PBXNativeTarget "SubprocessMocks" */; + buildPhases = ( + 6EDDC6C6241037CE00E171C6 /* Headers */, + 6EDDC6C7241037CE00E171C6 /* Sources */, + 6EDDC6C8241037CE00E171C6 /* Frameworks */, + 6EDDC6C9241037CE00E171C6 /* Resources */, + 6E1195E224165F2000534F74 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 6EDDC6D4241037D500E171C6 /* PBXTargetDependency */, + ); + name = SubprocessMocks; + productName = SubprocessMocks; + productReference = 6EDDC6CB241037CE00E171C6 /* SubprocessMocks.framework */; + productType = "com.apple.product-type.framework"; + }; + 6EDDC6DF2410391500E171C6 /* UnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6EDDC6E82410391500E171C6 /* Build configuration list for PBXNativeTarget "UnitTests" */; + buildPhases = ( + 6EDDC6DC2410391500E171C6 /* Sources */, + 6EDDC6DD2410391500E171C6 /* Frameworks */, + 6EDDC6DE2410391500E171C6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6EDDC6E72410391500E171C6 /* PBXTargetDependency */, + ); + name = UnitTests; + productName = UnitTests; + productReference = 6EDDC6E02410391500E171C6 /* UnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 6EDDC6EE2410392400E171C6 /* SystemTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6EDDC6F42410392400E171C6 /* Build configuration list for PBXNativeTarget "SystemTests" */; + buildPhases = ( + 6EDDC6EB2410392400E171C6 /* Sources */, + 6EDDC6EC2410392400E171C6 /* Frameworks */, + 6EDDC6ED2410392400E171C6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SystemTests; + productName = SystemTests; + productReference = 6EDDC6EF2410392400E171C6 /* SystemTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6EDDC62F2410369400E171C6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1130; + ORGANIZATIONNAME = Jamf; + TargetAttributes = { + 6EDDC6372410369400E171C6 = { + CreatedOnToolsVersion = 11.3.1; + LastSwiftMigration = 1130; + }; + 6EDDC6CA241037CE00E171C6 = { + CreatedOnToolsVersion = 11.3.1; + }; + 6EDDC6DF2410391500E171C6 = { + CreatedOnToolsVersion = 11.3.1; + }; + 6EDDC6EE2410392400E171C6 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 6EDDC6322410369400E171C6 /* Build configuration list for PBXProject "Subprocess" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6EDDC62E2410369400E171C6; + productRefGroup = 6EDDC6392410369400E171C6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6EDDC6372410369400E171C6 /* Subprocess */, + 6EDDC6CA241037CE00E171C6 /* SubprocessMocks */, + 6EDDC6DF2410391500E171C6 /* UnitTests */, + 6EDDC6EE2410392400E171C6 /* SystemTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6EDDC6362410369400E171C6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6C9241037CE00E171C6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6DE2410391500E171C6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6ED2410392400E171C6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6E1195E124165F1500534F74 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; + 6E1195E224165F2000534F74 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi \n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6EDDC6342410369400E171C6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6E1195E42416630500534F74 /* Input.swift in Sources */, + 6EDDC6C42410379F00E171C6 /* Errors.swift in Sources */, + 6EDDC6C32410379F00E171C6 /* Shell.swift in Sources */, + 6EDDC6C22410379F00E171C6 /* SubprocessDependencyBuilder.swift in Sources */, + 6EDDC6C12410379F00E171C6 /* Subprocess.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6C7241037CE00E171C6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6D9241037F500E171C6 /* MockShell.swift in Sources */, + 6EDDC6DB241037F500E171C6 /* MockProcess.swift in Sources */, + 6EDDC6D8241037F500E171C6 /* MockSubprocess.swift in Sources */, + 6EDDC6DA241037F500E171C6 /* MockSubprocessDependencyBuilder.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6DC2410391500E171C6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6FB2410396000E171C6 /* SubprocessTests.swift in Sources */, + 6EDDC6FC2410396000E171C6 /* ShellTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6EDDC6EB2410392400E171C6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EDDC6F92410395800E171C6 /* ShellTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 6EDDC6D4241037D500E171C6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6EDDC6372410369400E171C6 /* Subprocess */; + targetProxy = 6EDDC6D3241037D500E171C6 /* PBXContainerItemProxy */; + }; + 6EDDC6E72410391500E171C6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6EDDC6372410369400E171C6 /* Subprocess */; + targetProxy = 6EDDC6E62410391500E171C6 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 6EDDC63E2410369400E171C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6EDDC63F2410369400E171C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 6EDDC6412410369400E171C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Sources/Subprocess/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.Subprocess; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6EDDC6422410369400E171C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Sources/Subprocess/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.Subprocess; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 6EDDC6D1241037CE00E171C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Sources/SubprocessMocks/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.SubprocessMocks; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6EDDC6D2241037CE00E171C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Sources/SubprocessMocks/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.SubprocessMocks; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 6EDDC6E92410391500E171C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Tests/UnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6EDDC6EA2410391500E171C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Tests/UnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.UnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 6EDDC6F52410392400E171C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Tests/SystemTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.SystemTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6EDDC6F62410392400E171C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Tests/SystemTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.jamf.opensource.subprocess.SystemTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6EDDC6322410369400E171C6 /* Build configuration list for PBXProject "Subprocess" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6EDDC63E2410369400E171C6 /* Debug */, + 6EDDC63F2410369400E171C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6EDDC6402410369400E171C6 /* Build configuration list for PBXNativeTarget "Subprocess" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6EDDC6412410369400E171C6 /* Debug */, + 6EDDC6422410369400E171C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6EDDC6D0241037CE00E171C6 /* Build configuration list for PBXNativeTarget "SubprocessMocks" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6EDDC6D1241037CE00E171C6 /* Debug */, + 6EDDC6D2241037CE00E171C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6EDDC6E82410391500E171C6 /* Build configuration list for PBXNativeTarget "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6EDDC6E92410391500E171C6 /* Debug */, + 6EDDC6EA2410391500E171C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6EDDC6F42410392400E171C6 /* Build configuration list for PBXNativeTarget "SystemTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6EDDC6F52410392400E171C6 /* Debug */, + 6EDDC6F62410392400E171C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6EDDC62F2410369400E171C6 /* Project object */; +} diff --git a/Subprocess.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Subprocess.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..e49d243 --- /dev/null +++ b/Subprocess.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Subprocess.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Subprocess.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Subprocess.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Subprocess.xcodeproj/xcshareddata/xcschemes/Subprocess.xcscheme b/Subprocess.xcodeproj/xcshareddata/xcschemes/Subprocess.xcscheme new file mode 100644 index 0000000..e1edfca --- /dev/null +++ b/Subprocess.xcodeproj/xcshareddata/xcschemes/Subprocess.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Subprocess.xcodeproj/xcshareddata/xcschemes/SubprocessMocks.xcscheme b/Subprocess.xcodeproj/xcshareddata/xcschemes/SubprocessMocks.xcscheme new file mode 100644 index 0000000..bf19843 --- /dev/null +++ b/Subprocess.xcodeproj/xcshareddata/xcschemes/SubprocessMocks.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/SystemTests/Info.plist b/Tests/SystemTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/Tests/SystemTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Tests/SystemTests/ShellTests.swift b/Tests/SystemTests/ShellTests.swift new file mode 100644 index 0000000..f37f2a8 --- /dev/null +++ b/Tests/SystemTests/ShellTests.swift @@ -0,0 +1,139 @@ +import XCTest +@testable import Subprocess + +final class ShellTests: XCTestCase { + + override func setUp() { + SubprocessDependencyBuilder.shared = SubprocessDependencyBuilder() + } + + let softwareVersionFilePath = "/System/Library/CoreServices/SystemVersion.plist" + + // MARK: Input values + + func testExecWithDataInput() { + // Given + let inputText = "This is a text\nabc123\nHello World" + var result: String? + + // When + XCTAssertNoThrow(result = try Shell(["/usr/bin/grep", "abc123"]).exec(input: .text(inputText), encoding: .utf8)) + + // Then + XCTAssertFalse(result?.isEmpty ?? true) + } + + func testExecWithFileInput() { + // Given + let cmd = ["/usr/bin/grep", "ProductVersion"] + var result: String? + + // When + XCTAssertNoThrow(result = try Shell(cmd).exec(input: .file(path: softwareVersionFilePath), encoding: .utf8)) + + // Then + XCTAssertFalse(result?.isEmpty ?? true) + } + + // MARK: String transform + + func testExecReturningBoolFromString() { + // Given + var result = false + let username = NSUserName() + + // When + XCTAssertNoThrow(result = try Shell(["/usr/bin/dscl", ".", "list", "/Users"]).exec(encoding: .utf8, + transformBlock: { _, txt in + return txt.contains(username) + })) + + // Then + XCTAssertTrue(result) + } + + // MARK: String + + func testExecReturningString() { + // Given + var result: String? + let username = NSUserName() + + // When + XCTAssertNoThrow(result = try Shell(["/usr/bin/dscl", ".", "list", "/Users"]).exec(encoding: .utf8)) + + // Then + XCTAssertTrue(result?.contains(username) ?? false) + } + + // MARK: JSON object + + func testExecReturningJSONObject() { + // Given + var result: [[String: Any]]? + + // When + XCTAssertNoThrow(result = try Shell(["/usr/bin/log", "show", "--style", "json", "--last", "5m"]).execJSON()) + + // Then + XCTAssertFalse(result?.isEmpty ?? true) + } + + // MARK: Property list object + + func testExecReturningPropertyList() { + // Given + let fullVersionString = ProcessInfo.processInfo.operatingSystemVersionString + var result: [String: Any]? + + // When + XCTAssertNoThrow(result = try Shell(["/bin/cat", softwareVersionFilePath]).execPropertyList()) + + // Then + guard let versionNumber = result?["ProductVersion"] as? String else { return XCTFail("Unable to find version") } + XCTAssertTrue(fullVersionString.contains(versionNumber)) + } + + // MARK: Decodable object from JSON + + // swiftlint:disable nesting + + func testExecReturningDecodableObjectFromJSON() { + // Given + struct LogMessage: Codable { + var subsystem: String + var category: String + var machTimestamp: UInt64 + } + var result: [LogMessage]? + let cmd = ["/usr/bin/log", "show", "--style", "json", "--last", "5m"] + // When + XCTAssertNoThrow(result = try Shell(cmd).exec(decoder: JSONDecoder())) + + // Then + XCTAssertFalse(result?.isEmpty ?? true) + } + + // MARK: Decodable object from property list + + func testExecReturningDecodableObjectFromPropertyList() { + struct SystemVersion: Codable { + enum CodingKeys: String, CodingKey { + case version = "ProductVersion" + } + var version: String + } + + // Given + let fullVersionString = ProcessInfo.processInfo.operatingSystemVersionString + var result: SystemVersion? + + // When + XCTAssertNoThrow(result = try Shell(["/bin/cat", softwareVersionFilePath]).exec(decoder: PropertyListDecoder())) + + // Then + guard let versionNumber = result?.version else { return XCTFail("Result is nil") } + XCTAssertTrue(fullVersionString.contains(versionNumber)) + } + // swiftlint:enable nesting +} diff --git a/Tests/UnitTests/Info.plist b/Tests/UnitTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/Tests/UnitTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Tests/UnitTests/ShellTests.swift b/Tests/UnitTests/ShellTests.swift new file mode 100644 index 0000000..06c8ed0 --- /dev/null +++ b/Tests/UnitTests/ShellTests.swift @@ -0,0 +1,345 @@ +import XCTest +@testable import Subprocess +@testable import SubprocessMocks + +struct TestCodableObject: Codable, Equatable { + let uuid: UUID + init() { uuid = UUID() } +} + +// swiftlint:disable control_statement +final class ShellTests: XCTestCase { + + let command = [ "/usr/local/bin/somefakeCommand", "foo", "bar" ] + + override func setUp() { + // This is only needed for SwiftPM since it runs all of the test suites as a single test run + SubprocessDependencyBuilder.shared = MockSubprocessDependencyBuilder.shared + } + + override func tearDown() { + Shell.reset() + } + + // MARK: Data + + func testExecReturningDataWhenExitCodeIsNoneZero() { + // Given + let exitCode = Int32.random(in: 1...Int32.max) + Shell.expect(command, input: nil, standardError: Data(), exitCode: exitCode) + + // When + XCTAssertThrowsError(_ = try Shell(command).exec()) { + switch ($0 as? SubprocessError) { + case .exitedWithNonZeroStatus(let status): + XCTAssertEqual(status, exitCode) + default: XCTFail("Unexpected error type: \($0)") + } + } + + // Then + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningDataFromStandardOutput() { + // Given + var result: Data? + let expected = Data([ UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max) ]) + let errorData = Data([ UInt8.random(in: 0...UInt8.max) ]) + Shell.expect(command, input: nil, standardOutput: expected, standardError: errorData) + + // When + XCTAssertNoThrow(result = try Shell(command).exec()) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningDataFromStandardError() { + // Given + var result: Data? + let expected = Data([ UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max) ]) + let stdOutData = Data([ UInt8.random(in: 0...UInt8.max) ]) + Shell.expect(command, input: nil, standardOutput: stdOutData, standardError: expected) + + // When + XCTAssertNoThrow(result = try Shell(command).exec(options: .stderr)) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningDataFromBothOutputs() { + // Given + var result = Data() + let expectedStdout = UUID().uuidString + let expectedStderr = UUID().uuidString + Shell.expect(command, + input: nil, + standardOutput: expectedStdout.data(using: .utf8) ?? Data(), + standardError: expectedStderr.data(using: .utf8) ?? Data()) + + // When + XCTAssertNoThrow(result = try Shell(command).exec(options: .combined)) + + // Then + let text = String(data: result, encoding: .utf8) + XCTAssertTrue(text?.contains(expectedStdout) ?? false) + XCTAssertTrue(text?.contains(expectedStderr) ?? false) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: String + + func testExecReturningStringWhenExitCodeIsNoneZero() { + // Given + let exitCode = Int32.random(in: 1...Int32.max) + Shell.expect(command, input: nil, stderr: "Some error occured", exitCode: exitCode) + + // When + XCTAssertThrowsError(_ = try Shell(command).exec(encoding: .utf8)) { + switch ($0 as? SubprocessError) { + case .exitedWithNonZeroStatus(let status): + XCTAssertEqual(status, exitCode) + default: XCTFail("Unexpected error type: \($0)") + } + } + + // Then + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningStringWhenOutputEncodingErrorOccurs() { + // Given + let invalidData = Data([ 0xFF, 0xFF, 0xFF, 0xFF ]) + Shell.expect(command, input: nil, standardOutput: invalidData) + + // When + XCTAssertThrowsError(_ = try Shell(command).exec(encoding: .utf8)) { + switch ($0 as? SubprocessError) { + case .outputStringEncodingError: break + default: XCTFail("Unexpected error type: \($0)") + } + } + + // Then + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningStringFromStandardOutput() { + // Given + var result: String? + let expected = UUID().uuidString + Shell.expect(command, input: nil, stdout: expected, stderr: UUID().uuidString) + + // When + XCTAssertNoThrow(result = try Shell(command).exec(encoding: .utf8)) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningStringFromStandardError() { + // Given + var result: String? + let expected = UUID().uuidString + Shell.expect(command, input: nil, stdout: UUID().uuidString, stderr: expected) + + // When + XCTAssertNoThrow(result = try Shell(command).exec(options: .stderr, encoding: .utf8)) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningStringFromBothOutputs() { + // Given + var result: String? + let expectedStdout = UUID().uuidString + let expectedStderr = UUID().uuidString + Shell.expect(command, input: nil, stdout: expectedStdout, stderr: expectedStderr) + + // When + XCTAssertNoThrow(result = try Shell(command).exec(options: .combined, encoding: .utf8)) + + // Then + XCTAssertTrue(result?.contains(expectedStdout) ?? false) + XCTAssertTrue(result?.contains(expectedStderr) ?? false) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: JSON object + + func testExecReturningJSONArray() { + // Given + var result: [String]? + let expected: [String] = [ + UUID().uuidString, + UUID().uuidString + ] + XCTAssertNoThrow(try Shell.expect(command, input: nil, json: expected)) + + // When + XCTAssertNoThrow(result = try Shell(command).execJSON()) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningJSONDictionary() { + // Given + var result: [String: [String: String]]? + let expected: [String: [String: String]] = [ + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ], + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ] + ] + XCTAssertNoThrow(try Shell.expect(command, input: nil, json: expected)) + + // When + XCTAssertNoThrow(result = try Shell(command).execJSON()) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningJSONWithInvalidCast() { + // Given + var result: [String]? + let expected: [String: [String: String]] = [ + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ], + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ] + ] + XCTAssertNoThrow(try Shell.expect(command, input: nil, json: expected)) + + // When + XCTAssertThrowsError(result = try Shell(command).execJSON()) { + switch ($0 as? SubprocessError) { + case .unexpectedJSONObject(let type): + XCTAssertEqual(type, "__NSDictionaryI") + default: XCTFail("Unexpected error type: \($0)") + } + } + + // Then + XCTAssertNil(result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: Property list object + + func testExecReturningPropertyListArray() { + // Given + var result: [String]? + let expected: [String] = [ + UUID().uuidString, + UUID().uuidString + ] + XCTAssertNoThrow(try Shell.expect(command, input: nil, plist: expected)) + + // When + XCTAssertNoThrow(result = try Shell(command).execPropertyList()) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningPropertyListDictionary() { + // Given + var result: [String: [String: String]]? + let expected: [String: [String: String]] = [ + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ], + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ] + ] + XCTAssertNoThrow(try Shell.expect(command, input: nil, plist: expected)) + + // When + XCTAssertNoThrow(result = try Shell(command).execPropertyList()) + + // Then + XCTAssertEqual(expected, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + func testExecReturningPropertyListWithInvalidCast() { + // Given + var result: [String]? + let expected: [String: [String: String]] = [ + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ], + UUID().uuidString: [ + UUID().uuidString: UUID().uuidString + ] + ] + XCTAssertNoThrow(try Shell.expect(command, input: nil, plist: expected)) + + // When + XCTAssertThrowsError(result = try Shell(command).execPropertyList()) { + switch ($0 as? SubprocessError) { + case .unexpectedPropertyListObject(let type): + XCTAssertEqual(type, "__NSDictionaryM") + default: XCTFail("Unexpected error type: \($0)") + } + } + + // Then + XCTAssertNil(result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: Decodable object from JSON + + func testExecReturningDecodableObjectFromJSON() { + // Given + var result: TestCodableObject? + let expectObject: TestCodableObject = TestCodableObject() + XCTAssertNoThrow(try Shell.expect(command, input: nil, jsonObject: expectObject)) + + // When + XCTAssertNoThrow(result = try Shell(command).exec(decoder: JSONDecoder())) + + // Then + XCTAssertEqual(expectObject, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: Decodable object from property list + + func testExecReturningDecodableObjectFromPropertyList() { + // Given + var result: TestCodableObject? + let expectObject: TestCodableObject = TestCodableObject() + XCTAssertNoThrow(try Shell.expect(command, input: nil, plistObject: expectObject)) + + // When + XCTAssertNoThrow(result = try Shell(command).exec(decoder: PropertyListDecoder())) + + // Then + XCTAssertEqual(expectObject, result) + Shell.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } +} +// swiftlint:enable control_statement diff --git a/Tests/UnitTests/SubprocessTests.swift b/Tests/UnitTests/SubprocessTests.swift new file mode 100644 index 0000000..62294b9 --- /dev/null +++ b/Tests/UnitTests/SubprocessTests.swift @@ -0,0 +1,227 @@ +import XCTest +@testable import Subprocess +@testable import SubprocessMocks + +final class SubprocessTests: XCTestCase { + + let command = [ "/usr/local/bin/somefakeCommand", "foo", "bar" ] + + override func setUp() { + // This is only needed for SwiftPM since it runs all of the test suites as a single test run + SubprocessDependencyBuilder.shared = MockSubprocessDependencyBuilder.shared + } + + override func tearDown() { + Subprocess.reset() + } + + // MARK: Input + + func testInputData() { + // Given + var pipeOrFileHandle: Any? + let expected = Data([ UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max) ]) + + // When + let input = Input.data(expected) + XCTAssertNoThrow(pipeOrFileHandle = try input.createPipeOrFileHandle()) + + // Then + switch input.value { + case .data(let data): XCTAssertEqual(data, expected) + default: XCTFail("Unexpected type") + } + guard let pipe = pipeOrFileHandle as? MockPipe else { return XCTFail("Unable to cast MockPipe") } + XCTAssertEqual(pipe.data, expected) + } + + func testInputText() { + // Given + var pipeOrFileHandle: Any? + let expected = UUID().uuidString + + // When + let input = Input.text(expected) + XCTAssertNoThrow(pipeOrFileHandle = try input.createPipeOrFileHandle()) + + // Then + switch input.value { + case .text(let text, let encoding): + XCTAssertEqual(text, expected) + XCTAssertEqual(encoding, .utf8) + default: XCTFail("Unexpected type") + } + guard let pipe = pipeOrFileHandle as? MockPipe else { return XCTFail("Unable to cast MockPipe") } + guard let data = pipe.data, let text = String(data: data, encoding: .utf8) else { + return XCTFail("Failed to convert pipe data to string") + } + XCTAssertEqual(text, expected) + } + + func testInputFilePath() { + // Given + var pipeOrFileHandle: Any? + let expected = "/some/fake/path/\(UUID().uuidString)" + + // When + let input = Input.file(path: expected) + XCTAssertNoThrow(pipeOrFileHandle = try input.createPipeOrFileHandle()) + + // Then + switch input.value { + case .file(let url): XCTAssertEqual(url.path, expected) + default: XCTFail("Unexpected type") + } + guard let pipe = pipeOrFileHandle as? MockFileHandle else { return XCTFail("Unable to cast MockFileHandle") } + XCTAssertEqual(pipe.url?.path, expected) + } + + func testInputFileURL() { + // Given + var pipeOrFileHandle: Any? + let expected = URL(fileURLWithPath: "/some/fake/path/\(UUID().uuidString)") + + // When + let input = Input.file(url: expected) + XCTAssertNoThrow(pipeOrFileHandle = try input.createPipeOrFileHandle()) + + // Then + switch input.value { + case .file(let url): XCTAssertEqual(url, expected) + default: XCTFail("Unexpected type") + } + guard let pipe = pipeOrFileHandle as? MockFileHandle else { return XCTFail("Unable to cast MockFileHandle") } + XCTAssertEqual(pipe.url, expected) + } + + // MARK: PID + + func testGetPID() { + // Given + let mockCalled = expectation(description: "Mock setup called") + var expectedPID: Int32? + Subprocess.expect(command) { mock in + expectedPID = mock.reference.processIdentifier + mockCalled.fulfill() + } + + // When + let subprocess = Subprocess(command) + XCTAssertNoThrow(try subprocess.launch(terminationHandler: { (_, _, _) in })) + + // Then + waitForExpectations(timeout: 5.0) { _ in + XCTAssertEqual(subprocess.pid, expectedPID) + } + } + + // MARK: launch with termination handler + + func testLaunchWithTerminationHandler() { + // Given + let terminationExpectation = expectation(description: "Termination block called") + let expectedExitCode = Int32.random(in: Int32.min...Int32.max) + let expectedStdout = Data([ UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max) ]) + let expectedStderr = Data([ UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max), + UInt8.random(in: 0...UInt8.max) ]) + Subprocess.expect(command) { mock in + mock.writeTo(stdout: expectedStdout) + mock.writeTo(stderr: expectedStderr) + mock.exit(withStatus: expectedExitCode, reason: .uncaughtSignal) + } + + // When + let subprocess = Subprocess(command) + XCTAssertNoThrow(try subprocess.launch(terminationHandler: { (process, standardOutput, _) in + XCTAssertEqual(standardOutput, expectedStdout) + XCTAssertEqual(standardOutput, expectedStdout) + XCTAssertEqual(process.terminationReason, .uncaughtSignal) + XCTAssertEqual(process.exitCode, expectedExitCode) + terminationExpectation.fulfill() + })) + + // Then + waitForExpectations(timeout: 5.0) + Subprocess.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: suspend + + func testSuspend() { + // Given + let semaphore = DispatchSemaphore(value: 0) + let suspendCalled = expectation(description: "Suspend called") + Subprocess.expect(command) { mock in + mock.reference.stubSuspend = { + suspendCalled.fulfill() + return true + } + semaphore.signal() + } + let subprocess = Subprocess(command) + XCTAssertNoThrow(try subprocess.launch(terminationHandler: { (_, _, _) in })) + semaphore.wait() + + // When + XCTAssertTrue(subprocess.suspend()) + + // Then + waitForExpectations(timeout: 5.0) + Subprocess.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: resume + + func testResume() { + // Given + let semaphore = DispatchSemaphore(value: 0) + let resumeCalled = expectation(description: "Resume called") + Subprocess.expect(command) { mock in + mock.reference.stubResume = { + resumeCalled.fulfill() + return true + } + semaphore.signal() + } + let subprocess = Subprocess(command) + XCTAssertNoThrow(try subprocess.launch(terminationHandler: { (_, _, _) in })) + semaphore.wait() + + // When + XCTAssertTrue(subprocess.resume()) + + // Then + waitForExpectations(timeout: 5.0) + Subprocess.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } + + // MARK: kill + + func testKill() { + // Given + let semaphore = DispatchSemaphore(value: 0) + let terminateCalled = expectation(description: "Terminate called") + Subprocess.expect(command) { mock in + mock.reference.stubTerminate = { _ in + terminateCalled.fulfill() + } + semaphore.signal() + } + let subprocess = Subprocess(command) + XCTAssertNoThrow(try subprocess.launch(terminationHandler: { (_, _, _) in })) + semaphore.wait() + + // When + XCTAssertTrue(subprocess.isRunning) + subprocess.kill() + + // Then + waitForExpectations(timeout: 5.0) + Subprocess.verify { XCTFail($0.message, file: $0.file, line: $0.line) } + } +} diff --git a/docs/Classes.html b/docs/Classes.html new file mode 100644 index 0000000..652fe26 --- /dev/null +++ b/docs/Classes.html @@ -0,0 +1,154 @@ + + + + Classes Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Classes

+

The following classes are available globally.

+ +
+
+
+
    +
  • +
    + + + + Shell + +
    +
    +
    +
    +
    +
    +

    Class used for synchronous process execution

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class Shell
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + Subprocess + +
    +
    +
    +
    +
    +
    +

    Class used for asynchronous process execution

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    open class Subprocess
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Classes/Shell.html b/docs/Classes/Shell.html new file mode 100644 index 0000000..0bf8132 --- /dev/null +++ b/docs/Classes/Shell.html @@ -0,0 +1,825 @@ + + + + Shell Class Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Shell

+
+
+
public class Shell
+ +
+
+

Class used for synchronous process execution

+ +
+
+
+
    +
  • +
    + + + + OutputOptions + +
    +
    +
    +
    +
    +
    +

    OptionSet representing output handling

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct OutputOptions : OptionSet
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + process + +
    +
    +
    +
    +
    +
    +

    Reference to subprocess

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let process: Subprocess
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + init(_:) + +
    +
    +
    +
    +
    +
    +

    Creates new Shell instance

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(_ command: [String])
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + command + + +
    +

    Command represented as an array of strings

    +
    +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command using a supplied block to tranform the process output into whatever type you would like

    +
    +

    Throws

    + Error from process launch,transformBlock or failing create a string from the process output + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T>(input: Input? = nil,
    +                    options: OutputOptions = .stdout,
    +                    transformBlock: (_ process: Subprocess, _ data: Data) throws -> T) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + transformBlock + + +
    +

    Block executed given a reference to the completed process and the output

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as output type of the transformBlock

    +
    +
    +
    +
  • +
  • +
    + + + + exec(input:options:) + +
    +
    +
    +
    +
    +
    +

    Executes shell command expecting exit code of zero and returning the output data

    +
    +

    Throws

    + Error from process launch or if termination code is none-zero + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec(input: Input? = nil, options: OutputOptions = .stdout) throws -> Data
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    +
    +
    +

    Return Value

    +

    Process output data

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command using a supplied block to tranform the process output as a String +into whatever type you would like

    +
    +

    Throws

    + Error from process launch,transformBlock or failing create a string from the process output + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T>(input: Input? = nil,
    +                    options: OutputOptions = .stdout,
    +                    encoding: String.Encoding,
    +                    transformBlock: (_ process: Subprocess, _ string: String) throws -> T) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + encoding + + +
    +

    Encoding to use for the output

    +
    +
    + + transformBlock + + +
    +

    Block executed given a reference to the completed process and the output as a string

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as output type of the transformBlock

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting exit code of zero and returning the output as a string

    +
    +

    Throws

    + Error from process launch, if termination code is none-zero or failing create a string from the output + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec(input: Input? = nil,
    +                 options: OutputOptions = .stdout,
    +                 encoding: String.Encoding) throws -> String
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + encoding + + +
    +

    Encoding to use for the output

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as a String

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting JSON

    +
    +

    Throws

    + Error from process launch, JSONSerialization or failing to cast to expected type + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execJSON<T>(input: Input? = nil, options: OutputOptions = .stdout) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as an Array or Dictionary

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting a property list

    +
    +

    Throws

    + Error from process launch, PropertyListSerialization or failing to cast to expected type + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execPropertyList<T>(input: Input? = nil, options: OutputOptions = .stdout) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as an Array or Dictionary

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting JSON and decodes object conforming to Decodable

    +
    +

    Throws

    + Error from process launch or JSONDecoder + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T: Decodable>(input: Input? = nil,
    +                               options: OutputOptions = .stdout,
    +                               decoder: JSONDecoder) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + decoder + + +
    +

    JSONDecoder instance used for decoding the output object

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as the decodable object type

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting property list and decodes object conforming to Decodable

    +
    +

    Throws

    + Error from process launch or PropertyListDecoder + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T: Decodable>(input: Input? = nil,
    +                               options: OutputOptions = .stdout,
    +                               decoder: PropertyListDecoder) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + decoder + + +
    +

    PropertyListDecoder instance used for decoding the output object

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as the decodable object type

    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Classes/Shell/OutputOptions.html b/docs/Classes/Shell/OutputOptions.html new file mode 100644 index 0000000..911e60e --- /dev/null +++ b/docs/Classes/Shell/OutputOptions.html @@ -0,0 +1,237 @@ + + + + OutputOptions Structure Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

OutputOptions

+
+
+
public struct OutputOptions : OptionSet
+ +
+
+

OptionSet representing output handling

+ +
+
+
+
    +
  • +
    + + + + rawValue + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let rawValue: Int
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + stdout + +
    +
    +
    +
    +
    +
    +

    Processes data written to stdout

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let stdout: Shell.OutputOptions
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + stderr + +
    +
    +
    +
    +
    +
    +

    Processes data written to stderr

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let stderr: Shell.OutputOptions
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + combined + +
    +
    +
    +
    +
    +
    +

    Processes data written to both stdout and stderr

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let combined: Shell.OutputOptions
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + init(rawValue:) + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(rawValue: Int)
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Classes/Subprocess.html b/docs/Classes/Subprocess.html new file mode 100644 index 0000000..5f2b054 --- /dev/null +++ b/docs/Classes/Subprocess.html @@ -0,0 +1,537 @@ + + + + Subprocess Class Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Subprocess

+
+
+
open class Subprocess
+ +
+
+

Class used for asynchronous process execution

+ +
+
+
+
    +
  • +
    + + + + pid + +
    +
    +
    +
    +
    +
    +

    Process identifier

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var pid: Int32 { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + exitCode + +
    +
    +
    +
    +
    +
    +

    Exit code of the process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var exitCode: Int32 { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + isRunning + +
    +
    +
    +
    +
    +
    +

    Returns whether the process is still running.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var isRunning: Bool { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + terminationReason + +
    +
    +
    +
    +
    +
    +

    Reason for process termination

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var terminationReason: Process.TerminationReason { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + init(_:qos:) + +
    +
    +
    +
    +
    +
    +

    Creates new Subprocess

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(_ command: [String], qos: DispatchQoS = .default)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + command + + +
    +

    Command represented as an array of strings

    +
    +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Launches command with read handlers and termination handler

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func launch(input: Input? = nil,
    +                   outputHandler: ((Data) -> Void)? = nil,
    +                   errorHandler: ((Data) -> Void)? = nil,
    +                   terminationHandler: ((Subprocess) -> Void)? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process

    +
    +
    + + outputHandler + + +
    +

    Block called whenever new data is read from standard output of the process

    +
    +
    + + errorHandler + + +
    +

    Block called whenever new data is read from standard error of the process

    +
    +
    + + terminationHandler + + +
    +

    Block called when process has terminated and all output handlers have returned

    +
    +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Block type called for executing process returning data from standard out and standard error

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias DataTerminationHandler = (_ process: Subprocess, _ stdout: Data, _ stderr: Data) -> Void
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Launches command calling a block when process terminates

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func launch(input: Input? = nil,
    +                   terminationHandler: @escaping DataTerminationHandler) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process

    +
    +
    + + terminationHandler + + +
    +

    Block called with Subprocess, stdout Data, stderr Data

    +
    +
    +
    +
    +
    +
  • +
  • +
    + + + + suspend() + +
    +
    +
    +
    +
    +
    +

    Suspends the command

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func suspend() -> Bool
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + resume() + +
    +
    +
    +
    +
    +
    +

    Resumes the command which was suspended

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func resume() -> Bool
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + kill() + +
    +
    +
    +
    +
    +
    +

    Sends the command the term signal

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func kill()
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + waitForTermination() + +
    +
    +
    +
    +
    +
    +

    Waits for process to complete and all handlers to be called

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func waitForTermination()
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Enums.html b/docs/Enums.html new file mode 100644 index 0000000..619b7f6 --- /dev/null +++ b/docs/Enums.html @@ -0,0 +1,126 @@ + + + + Enumerations Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Enumerations

+

The following enumerations are available globally.

+ +
+
+
+
    +
  • +
    + + + + SubprocessError + +
    +
    +
    +
    +
    +
    +

    Type representing possible errors

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum SubprocessError : Error
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Enums/SubprocessError.html b/docs/Enums/SubprocessError.html new file mode 100644 index 0000000..c6ffdcd --- /dev/null +++ b/docs/Enums/SubprocessError.html @@ -0,0 +1,239 @@ + + + + SubprocessError Enumeration Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

SubprocessError

+
+
+
public enum SubprocessError : Error
+ +
+
+

Type representing possible errors

+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    The process completed with a none-zero exit code

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case exitedWithNoneZeroStatus(Int32)
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The property list object could not be casted into expected type

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case unexpectedPropertyListObject(String)
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The JSON object could not be casted into expected type

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case unexpectedJSONObject(String)
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Input string could not be encoded

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case inputStringEncodingError
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Output string could not be encoded

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case outputStringEncodingError
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Protocols.html b/docs/Protocols.html new file mode 100644 index 0000000..e5de965 --- /dev/null +++ b/docs/Protocols.html @@ -0,0 +1,126 @@ + + + + Protocols Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Protocol call used for dependency injection

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol SubprocessDependencyFactory
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Protocols/SubprocessDependencyFactory.html b/docs/Protocols/SubprocessDependencyFactory.html new file mode 100644 index 0000000..250c8dc --- /dev/null +++ b/docs/Protocols/SubprocessDependencyFactory.html @@ -0,0 +1,259 @@ + + + + SubprocessDependencyFactory Protocol Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

SubprocessDependencyFactory

+
+
+
public protocol SubprocessDependencyFactory
+ +
+
+

Protocol call used for dependency injection

+ +
+
+
+
    +
  • +
    + + + + createProcess(for:) + +
    +
    +
    +
    +
    +
    +

    Creates new Subprocess

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func createProcess(for command: [String]) -> Process
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + command + + +
    +

    Command represented as an array of strings

    +
    +
    +
    +
    +

    Return Value

    +

    New Subprocess instance

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Creates a FileHandle for reading

    +
    +

    Throws

    + When unable to open file for reading + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func createInputFileHandle(for url: URL) throws -> FileHandle
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + url + + +
    +

    File URL

    +
    +
    +
    +
    +

    Return Value

    +

    New FileHandle for reading

    +
    +
    +
    +
  • +
  • +
    + + + + createInputPipe(for:) + +
    +
    +
    +
    +
    +
    +

    Creates a Pipe and writes given data

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func createInputPipe(for data: Data) -> Pipe
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Data to write to the Pipe

    +
    +
    +
    +
    +

    Return Value

    +

    New Pipe instance

    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Structs.html b/docs/Structs.html new file mode 100644 index 0000000..a629342 --- /dev/null +++ b/docs/Structs.html @@ -0,0 +1,154 @@ + + + + Structures Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Structures

+

The following structures are available globally.

+ +
+
+
+
    +
  • +
    + + + + Input + +
    +
    +
    +
    +
    +
    +

    Interface representing into to the process

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Input
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Default implementation of SubprocessDependencyFactory

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct SubprocessDependencyBuilder : SubprocessDependencyFactory
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Structs/Input.html b/docs/Structs/Input.html new file mode 100644 index 0000000..ecc2acb --- /dev/null +++ b/docs/Structs/Input.html @@ -0,0 +1,359 @@ + + + + Input Structure Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Input

+
+
+
public struct Input
+ +
+
+

Interface representing into to the process

+ +
+
+
+
    +
  • +
    + + + + Value + +
    +
    +
    +
    +
    +
    +

    Reference to the input value

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Value
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    Reference to the input value

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let value: Value
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing data to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Data written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
  • +
    + + + + text(_:encoding:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing text to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func text(_ text: String, encoding: String.Encoding = .utf8) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + text + + +
    +

    Text written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
  • +
    + + + + file(path:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing contents of file at path to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func file(path: String) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + path + + +
    +

    Path to file written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
  • +
    + + + + file(url:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing contents of file URL to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func file(url: URL) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + url + + +
    +

    URL for file written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Structs/Input/Value.html b/docs/Structs/Input/Value.html new file mode 100644 index 0000000..019d05d --- /dev/null +++ b/docs/Structs/Input/Value.html @@ -0,0 +1,185 @@ + + + + Value Enumeration Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Value

+
+
+
public enum Value
+ +
+
+

Reference to the input value

+ +
+
+
+
    +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Data to be written to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case data(Data)
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + text(_:_:) + +
    +
    +
    +
    +
    +
    +

    Text to be written to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case text(String, String.Encoding)
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + file(_:) + +
    +
    +
    +
    +
    +
    +

    File to be written to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case file(URL)
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/Structs/SubprocessDependencyBuilder.html b/docs/Structs/SubprocessDependencyBuilder.html new file mode 100644 index 0000000..5de488a --- /dev/null +++ b/docs/Structs/SubprocessDependencyBuilder.html @@ -0,0 +1,209 @@ + + + + SubprocessDependencyBuilder Structure Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

SubprocessDependencyBuilder

+
+
+
public struct SubprocessDependencyBuilder : SubprocessDependencyFactory
+ +
+
+

Default implementation of SubprocessDependencyFactory

+ +
+
+
+
    +
  • +
    + + + + shared + +
    +
    +
    +
    +
    +
    +

    Shared instance used for dependency creatation

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static var shared: SubprocessDependencyFactory
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + createProcess(for:) + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func createProcess(for command: [String]) -> Process
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func createInputFileHandle(for url: URL) throws -> FileHandle
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + createInputPipe(for:) + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func createInputPipe(for data: Data) -> Pipe
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/badge.svg b/docs/badge.svg new file mode 100644 index 0000000..a096fec --- /dev/null +++ b/docs/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 100% + + + 100% + + + diff --git a/docs/css/highlight.css b/docs/css/highlight.css new file mode 100644 index 0000000..d0db0e1 --- /dev/null +++ b/docs/css/highlight.css @@ -0,0 +1,200 @@ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight { + /* Comment */ + /* Error */ + /* Keyword */ + /* Operator */ + /* Comment.Multiline */ + /* Comment.Preproc */ + /* Comment.Single */ + /* Comment.Special */ + /* Generic.Deleted */ + /* Generic.Deleted.Specific */ + /* Generic.Emph */ + /* Generic.Error */ + /* Generic.Heading */ + /* Generic.Inserted */ + /* Generic.Inserted.Specific */ + /* Generic.Output */ + /* Generic.Prompt */ + /* Generic.Strong */ + /* Generic.Subheading */ + /* Generic.Traceback */ + /* Keyword.Constant */ + /* Keyword.Declaration */ + /* Keyword.Pseudo */ + /* Keyword.Reserved */ + /* Keyword.Type */ + /* Literal.Number */ + /* Literal.String */ + /* Name.Attribute */ + /* Name.Builtin */ + /* Name.Class */ + /* Name.Constant */ + /* Name.Entity */ + /* Name.Exception */ + /* Name.Function */ + /* Name.Namespace */ + /* Name.Tag */ + /* Name.Variable */ + /* Operator.Word */ + /* Text.Whitespace */ + /* Literal.Number.Float */ + /* Literal.Number.Hex */ + /* Literal.Number.Integer */ + /* Literal.Number.Oct */ + /* Literal.String.Backtick */ + /* Literal.String.Char */ + /* Literal.String.Doc */ + /* Literal.String.Double */ + /* Literal.String.Escape */ + /* Literal.String.Heredoc */ + /* Literal.String.Interpol */ + /* Literal.String.Other */ + /* Literal.String.Regex */ + /* Literal.String.Single */ + /* Literal.String.Symbol */ + /* Name.Builtin.Pseudo */ + /* Name.Variable.Class */ + /* Name.Variable.Global */ + /* Name.Variable.Instance */ + /* Literal.Number.Integer.Long */ } + .highlight .c { + color: #999988; + font-style: italic; } + .highlight .err { + color: #a61717; + background-color: #e3d2d2; } + .highlight .k { + color: #000000; + font-weight: bold; } + .highlight .o { + color: #000000; + font-weight: bold; } + .highlight .cm { + color: #999988; + font-style: italic; } + .highlight .cp { + color: #999999; + font-weight: bold; } + .highlight .c1 { + color: #999988; + font-style: italic; } + .highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + .highlight .gd { + color: #000000; + background-color: #ffdddd; } + .highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + .highlight .ge { + color: #000000; + font-style: italic; } + .highlight .gr { + color: #aa0000; } + .highlight .gh { + color: #999999; } + .highlight .gi { + color: #000000; + background-color: #ddffdd; } + .highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + .highlight .go { + color: #888888; } + .highlight .gp { + color: #555555; } + .highlight .gs { + font-weight: bold; } + .highlight .gu { + color: #aaaaaa; } + .highlight .gt { + color: #aa0000; } + .highlight .kc { + color: #000000; + font-weight: bold; } + .highlight .kd { + color: #000000; + font-weight: bold; } + .highlight .kp { + color: #000000; + font-weight: bold; } + .highlight .kr { + color: #000000; + font-weight: bold; } + .highlight .kt { + color: #445588; } + .highlight .m { + color: #009999; } + .highlight .s { + color: #d14; } + .highlight .na { + color: #008080; } + .highlight .nb { + color: #0086B3; } + .highlight .nc { + color: #445588; + font-weight: bold; } + .highlight .no { + color: #008080; } + .highlight .ni { + color: #800080; } + .highlight .ne { + color: #990000; + font-weight: bold; } + .highlight .nf { + color: #990000; } + .highlight .nn { + color: #555555; } + .highlight .nt { + color: #000080; } + .highlight .nv { + color: #008080; } + .highlight .ow { + color: #000000; + font-weight: bold; } + .highlight .w { + color: #bbbbbb; } + .highlight .mf { + color: #009999; } + .highlight .mh { + color: #009999; } + .highlight .mi { + color: #009999; } + .highlight .mo { + color: #009999; } + .highlight .sb { + color: #d14; } + .highlight .sc { + color: #d14; } + .highlight .sd { + color: #d14; } + .highlight .s2 { + color: #d14; } + .highlight .se { + color: #d14; } + .highlight .sh { + color: #d14; } + .highlight .si { + color: #d14; } + .highlight .sx { + color: #d14; } + .highlight .sr { + color: #009926; } + .highlight .s1 { + color: #d14; } + .highlight .ss { + color: #990073; } + .highlight .bp { + color: #999999; } + .highlight .vc { + color: #008080; } + .highlight .vg { + color: #008080; } + .highlight .vi { + color: #008080; } + .highlight .il { + color: #009999; } diff --git a/docs/css/jazzy.css b/docs/css/jazzy.css new file mode 100644 index 0000000..2d5656d --- /dev/null +++ b/docs/css/jazzy.css @@ -0,0 +1,372 @@ +html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { + background: transparent; + border: 0; + margin: 0; + outline: 0; + padding: 0; + vertical-align: baseline; } + +body { + background-color: #f2f2f2; + font-family: Helvetica, freesans, Arial, sans-serif; + font-size: 14px; + -webkit-font-smoothing: subpixel-antialiased; + word-wrap: break-word; } + +h1, h2, h3 { + margin-top: 0.8em; + margin-bottom: 0.3em; + font-weight: 100; + color: black; } + +h1 { + font-size: 2.5em; } + +h2 { + font-size: 2em; + border-bottom: 1px solid #e2e2e2; } + +h4 { + font-size: 13px; + line-height: 1.5; + margin-top: 21px; } + +h5 { + font-size: 1.1em; } + +h6 { + font-size: 1.1em; + color: #777; } + +.section-name { + color: gray; + display: block; + font-family: Helvetica; + font-size: 22px; + font-weight: 100; + margin-bottom: 15px; } + +pre, code { + font: 0.95em Menlo, monospace; + color: #777; + word-wrap: normal; } + +p code, li code { + background-color: #eee; + padding: 2px 4px; + border-radius: 4px; } + +a { + color: #0088cc; + text-decoration: none; } + +ul { + padding-left: 15px; } + +li { + line-height: 1.8em; } + +img { + max-width: 100%; } + +blockquote { + margin-left: 0; + padding: 0 10px; + border-left: 4px solid #ccc; } + +.content-wrapper { + margin: 0 auto; + width: 980px; } + +header { + font-size: 0.85em; + line-height: 26px; + background-color: #414141; + position: fixed; + width: 100%; + z-index: 2; } + header img { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + header a { + color: #fff; } + header p { + float: left; + color: #999; } + header .header-right { + float: right; + margin-left: 16px; } + +#breadcrumbs { + background-color: #f2f2f2; + height: 27px; + padding-top: 17px; + position: fixed; + width: 100%; + z-index: 2; + margin-top: 26px; } + #breadcrumbs #carat { + height: 10px; + margin: 0 5px; } + +.sidebar { + background-color: #f9f9f9; + border: 1px solid #e2e2e2; + overflow-y: auto; + overflow-x: hidden; + position: fixed; + top: 70px; + bottom: 0; + width: 230px; + word-wrap: normal; } + +.nav-groups { + list-style-type: none; + background: #fff; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #e2e2e2; + font-size: 1.1em; + font-weight: 100; + padding: 15px 0 15px 20px; } + .nav-group-name > a { + color: #333; } + +.nav-group-tasks { + margin-top: 5px; } + +.nav-group-task { + font-size: 0.9em; + list-style-type: none; + white-space: nowrap; } + .nav-group-task a { + color: #888; } + +.main-content { + background-color: #fff; + border: 1px solid #e2e2e2; + margin-left: 246px; + position: absolute; + overflow: hidden; + padding-bottom: 20px; + top: 70px; + width: 734px; } + .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { + margin-bottom: 1em; } + .main-content p { + line-height: 1.8em; } + .main-content section .section:first-child { + margin-top: 0; + padding-top: 0; } + .main-content section .task-group-section .task-group:first-of-type { + padding-top: 10px; } + .main-content section .task-group-section .task-group:first-of-type .section-name { + padding-top: 15px; } + .main-content section .heading:before { + content: ""; + display: block; + padding-top: 70px; + margin: -70px 0 0; } + .main-content .section-name p { + margin-bottom: inherit; + line-height: inherit; } + .main-content .section-name code { + background-color: inherit; + padding: inherit; + color: inherit; } + +.section { + padding: 0 25px; } + +.highlight { + background-color: #eee; + padding: 10px 12px; + border: 1px solid #e2e2e2; + border-radius: 4px; + overflow-x: auto; } + +.declaration .highlight { + overflow-x: initial; + padding: 0 40px 40px 0; + margin-bottom: -25px; + background-color: transparent; + border: none; } + +.section-name { + margin: 0; + margin-left: 18px; } + +.task-group-section { + padding-left: 6px; + border-top: 1px solid #e2e2e2; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; + padding-top: 70px; + margin: -70px 0 0; } + +.section-name-container { + position: relative; + display: inline-block; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; + padding-top: 70px; + margin: -70px 0 0; } + .item code { + background-color: transparent; + padding: 0; } + .item .token, .item .direct-link { + padding-left: 3px; + margin-left: 15px; + font-size: 11.9px; + transition: all 300ms; } + .item .token-open { + margin-left: 0px; } + .item .discouraged { + text-decoration: line-through; } + .item .declaration-note { + font-size: .85em; + color: gray; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #e2e2e2; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + background: #f9f9f9; + border-left: 1px solid #e2e2e2; + border-top: 1px solid #e2e2e2; + height: 12px; + left: 21px; + top: -7px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + position: absolute; + width: 12px; } + +.height-container { + display: none; + left: -25px; + padding: 0 25px; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #f9f9f9; + border-bottom: 1px solid #e2e2e2; + left: -25px; + position: relative; + width: 100%; + padding-top: 10px; + padding-bottom: 5px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4b8afb; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #e2e2e2; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +#footer { + position: relative; + top: 10px; + bottom: 0px; + margin-left: 25px; } + #footer p { + margin: 0; + color: #aaa; + font-size: 0.8em; } + +html.dash header, html.dash #breadcrumbs, html.dash .sidebar { + display: none; } + +html.dash .main-content { + width: 980px; + margin-left: 0; + border: none; + width: 100%; + top: 0; + padding-bottom: 0; } + +html.dash .height-container { + display: block; } + +html.dash .item .token { + margin-left: 0; } + +html.dash .content-wrapper { + width: auto; } + +html.dash #footer { + position: static; } diff --git a/docs/docsets/Subprocess.docset/Contents/Info.plist b/docs/docsets/Subprocess.docset/Contents/Info.plist new file mode 100644 index 0000000..a0f5d16 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleIdentifier + com.jazzy.subprocess + CFBundleName + Subprocess + DocSetPlatformFamily + subprocess + isDashDocset + + dashIndexFilePath + index.html + isJavaScriptEnabled + + DashDocSetFamily + dashtoc + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes.html new file mode 100644 index 0000000..652fe26 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes.html @@ -0,0 +1,154 @@ + + + + Classes Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Classes

+

The following classes are available globally.

+ +
+
+
+
    +
  • +
    + + + + Shell + +
    +
    +
    +
    +
    +
    +

    Class used for synchronous process execution

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class Shell
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + Subprocess + +
    +
    +
    +
    +
    +
    +

    Class used for asynchronous process execution

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    open class Subprocess
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell.html new file mode 100644 index 0000000..0bf8132 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell.html @@ -0,0 +1,825 @@ + + + + Shell Class Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Shell

+
+
+
public class Shell
+ +
+
+

Class used for synchronous process execution

+ +
+
+
+
    +
  • +
    + + + + OutputOptions + +
    +
    +
    +
    +
    +
    +

    OptionSet representing output handling

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct OutputOptions : OptionSet
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + process + +
    +
    +
    +
    +
    +
    +

    Reference to subprocess

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let process: Subprocess
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + init(_:) + +
    +
    +
    +
    +
    +
    +

    Creates new Shell instance

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(_ command: [String])
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + command + + +
    +

    Command represented as an array of strings

    +
    +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command using a supplied block to tranform the process output into whatever type you would like

    +
    +

    Throws

    + Error from process launch,transformBlock or failing create a string from the process output + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T>(input: Input? = nil,
    +                    options: OutputOptions = .stdout,
    +                    transformBlock: (_ process: Subprocess, _ data: Data) throws -> T) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + transformBlock + + +
    +

    Block executed given a reference to the completed process and the output

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as output type of the transformBlock

    +
    +
    +
    +
  • +
  • +
    + + + + exec(input:options:) + +
    +
    +
    +
    +
    +
    +

    Executes shell command expecting exit code of zero and returning the output data

    +
    +

    Throws

    + Error from process launch or if termination code is none-zero + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec(input: Input? = nil, options: OutputOptions = .stdout) throws -> Data
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    +
    +
    +

    Return Value

    +

    Process output data

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command using a supplied block to tranform the process output as a String +into whatever type you would like

    +
    +

    Throws

    + Error from process launch,transformBlock or failing create a string from the process output + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T>(input: Input? = nil,
    +                    options: OutputOptions = .stdout,
    +                    encoding: String.Encoding,
    +                    transformBlock: (_ process: Subprocess, _ string: String) throws -> T) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + encoding + + +
    +

    Encoding to use for the output

    +
    +
    + + transformBlock + + +
    +

    Block executed given a reference to the completed process and the output as a string

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as output type of the transformBlock

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting exit code of zero and returning the output as a string

    +
    +

    Throws

    + Error from process launch, if termination code is none-zero or failing create a string from the output + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec(input: Input? = nil,
    +                 options: OutputOptions = .stdout,
    +                 encoding: String.Encoding) throws -> String
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + encoding + + +
    +

    Encoding to use for the output

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as a String

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting JSON

    +
    +

    Throws

    + Error from process launch, JSONSerialization or failing to cast to expected type + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execJSON<T>(input: Input? = nil, options: OutputOptions = .stdout) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as an Array or Dictionary

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting a property list

    +
    +

    Throws

    + Error from process launch, PropertyListSerialization or failing to cast to expected type + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execPropertyList<T>(input: Input? = nil, options: OutputOptions = .stdout) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as an Array or Dictionary

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting JSON and decodes object conforming to Decodable

    +
    +

    Throws

    + Error from process launch or JSONDecoder + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T: Decodable>(input: Input? = nil,
    +                               options: OutputOptions = .stdout,
    +                               decoder: JSONDecoder) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + decoder + + +
    +

    JSONDecoder instance used for decoding the output object

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as the decodable object type

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Executes shell command expecting property list and decodes object conforming to Decodable

    +
    +

    Throws

    + Error from process launch or PropertyListDecoder + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func exec<T: Decodable>(input: Input? = nil,
    +                               options: OutputOptions = .stdout,
    +                               decoder: PropertyListDecoder) throws -> T
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process (Default: nil)

    +
    +
    + + options + + +
    +

    Output options defining the output to process (Default: .stdout)

    +
    +
    + + decoder + + +
    +

    PropertyListDecoder instance used for decoding the output object

    +
    +
    +
    +
    +

    Return Value

    +

    Process output as the decodable object type

    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell/OutputOptions.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell/OutputOptions.html new file mode 100644 index 0000000..911e60e --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Shell/OutputOptions.html @@ -0,0 +1,237 @@ + + + + OutputOptions Structure Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

OutputOptions

+
+
+
public struct OutputOptions : OptionSet
+ +
+
+

OptionSet representing output handling

+ +
+
+
+
    +
  • +
    + + + + rawValue + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let rawValue: Int
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + stdout + +
    +
    +
    +
    +
    +
    +

    Processes data written to stdout

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let stdout: Shell.OutputOptions
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + stderr + +
    +
    +
    +
    +
    +
    +

    Processes data written to stderr

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let stderr: Shell.OutputOptions
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + combined + +
    +
    +
    +
    +
    +
    +

    Processes data written to both stdout and stderr

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let combined: Shell.OutputOptions
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + init(rawValue:) + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(rawValue: Int)
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Subprocess.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Subprocess.html new file mode 100644 index 0000000..5f2b054 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Classes/Subprocess.html @@ -0,0 +1,537 @@ + + + + Subprocess Class Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Subprocess

+
+
+
open class Subprocess
+ +
+
+

Class used for asynchronous process execution

+ +
+
+
+
    +
  • +
    + + + + pid + +
    +
    +
    +
    +
    +
    +

    Process identifier

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var pid: Int32 { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + exitCode + +
    +
    +
    +
    +
    +
    +

    Exit code of the process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var exitCode: Int32 { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + isRunning + +
    +
    +
    +
    +
    +
    +

    Returns whether the process is still running.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var isRunning: Bool { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + terminationReason + +
    +
    +
    +
    +
    +
    +

    Reason for process termination

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var terminationReason: Process.TerminationReason { get }
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + init(_:qos:) + +
    +
    +
    +
    +
    +
    +

    Creates new Subprocess

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(_ command: [String], qos: DispatchQoS = .default)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + command + + +
    +

    Command represented as an array of strings

    +
    +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Launches command with read handlers and termination handler

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func launch(input: Input? = nil,
    +                   outputHandler: ((Data) -> Void)? = nil,
    +                   errorHandler: ((Data) -> Void)? = nil,
    +                   terminationHandler: ((Subprocess) -> Void)? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process

    +
    +
    + + outputHandler + + +
    +

    Block called whenever new data is read from standard output of the process

    +
    +
    + + errorHandler + + +
    +

    Block called whenever new data is read from standard error of the process

    +
    +
    + + terminationHandler + + +
    +

    Block called when process has terminated and all output handlers have returned

    +
    +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Block type called for executing process returning data from standard out and standard error

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias DataTerminationHandler = (_ process: Subprocess, _ stdout: Data, _ stderr: Data) -> Void
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Launches command calling a block when process terminates

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func launch(input: Input? = nil,
    +                   terminationHandler: @escaping DataTerminationHandler) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + input + + +
    +

    File or data to write to standard input of the process

    +
    +
    + + terminationHandler + + +
    +

    Block called with Subprocess, stdout Data, stderr Data

    +
    +
    +
    +
    +
    +
  • +
  • +
    + + + + suspend() + +
    +
    +
    +
    +
    +
    +

    Suspends the command

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func suspend() -> Bool
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + resume() + +
    +
    +
    +
    +
    +
    +

    Resumes the command which was suspended

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func resume() -> Bool
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + kill() + +
    +
    +
    +
    +
    +
    +

    Sends the command the term signal

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func kill()
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + waitForTermination() + +
    +
    +
    +
    +
    +
    +

    Waits for process to complete and all handlers to be called

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func waitForTermination()
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums.html new file mode 100644 index 0000000..619b7f6 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums.html @@ -0,0 +1,126 @@ + + + + Enumerations Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Enumerations

+

The following enumerations are available globally.

+ +
+
+
+
    +
  • +
    + + + + SubprocessError + +
    +
    +
    +
    +
    +
    +

    Type representing possible errors

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum SubprocessError : Error
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums/SubprocessError.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums/SubprocessError.html new file mode 100644 index 0000000..c6ffdcd --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Enums/SubprocessError.html @@ -0,0 +1,239 @@ + + + + SubprocessError Enumeration Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

SubprocessError

+
+
+
public enum SubprocessError : Error
+ +
+
+

Type representing possible errors

+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    The process completed with a none-zero exit code

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case exitedWithNoneZeroStatus(Int32)
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The property list object could not be casted into expected type

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case unexpectedPropertyListObject(String)
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The JSON object could not be casted into expected type

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case unexpectedJSONObject(String)
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Input string could not be encoded

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case inputStringEncodingError
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Output string could not be encoded

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case outputStringEncodingError
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols.html new file mode 100644 index 0000000..e5de965 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols.html @@ -0,0 +1,126 @@ + + + + Protocols Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Protocol call used for dependency injection

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol SubprocessDependencyFactory
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols/SubprocessDependencyFactory.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols/SubprocessDependencyFactory.html new file mode 100644 index 0000000..250c8dc --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Protocols/SubprocessDependencyFactory.html @@ -0,0 +1,259 @@ + + + + SubprocessDependencyFactory Protocol Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

SubprocessDependencyFactory

+
+
+
public protocol SubprocessDependencyFactory
+ +
+
+

Protocol call used for dependency injection

+ +
+
+
+
    +
  • +
    + + + + createProcess(for:) + +
    +
    +
    +
    +
    +
    +

    Creates new Subprocess

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func createProcess(for command: [String]) -> Process
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + command + + +
    +

    Command represented as an array of strings

    +
    +
    +
    +
    +

    Return Value

    +

    New Subprocess instance

    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Creates a FileHandle for reading

    +
    +

    Throws

    + When unable to open file for reading + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func createInputFileHandle(for url: URL) throws -> FileHandle
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + url + + +
    +

    File URL

    +
    +
    +
    +
    +

    Return Value

    +

    New FileHandle for reading

    +
    +
    +
    +
  • +
  • +
    + + + + createInputPipe(for:) + +
    +
    +
    +
    +
    +
    +

    Creates a Pipe and writes given data

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func createInputPipe(for data: Data) -> Pipe
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Data to write to the Pipe

    +
    +
    +
    +
    +

    Return Value

    +

    New Pipe instance

    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs.html new file mode 100644 index 0000000..a629342 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs.html @@ -0,0 +1,154 @@ + + + + Structures Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Structures

+

The following structures are available globally.

+ +
+
+
+
    +
  • +
    + + + + Input + +
    +
    +
    +
    +
    +
    +

    Interface representing into to the process

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Input
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Default implementation of SubprocessDependencyFactory

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct SubprocessDependencyBuilder : SubprocessDependencyFactory
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input.html new file mode 100644 index 0000000..ecc2acb --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input.html @@ -0,0 +1,359 @@ + + + + Input Structure Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Input

+
+
+
public struct Input
+ +
+
+

Interface representing into to the process

+ +
+
+
+
    +
  • +
    + + + + Value + +
    +
    +
    +
    +
    +
    +

    Reference to the input value

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Value
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    Reference to the input value

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let value: Value
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing data to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Data written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
  • +
    + + + + text(_:encoding:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing text to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func text(_ text: String, encoding: String.Encoding = .utf8) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + text + + +
    +

    Text written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
  • +
    + + + + file(path:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing contents of file at path to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func file(path: String) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + path + + +
    +

    Path to file written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
  • +
    + + + + file(url:) + +
    +
    +
    +
    +
    +
    +

    Creates input for writing contents of file URL to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func file(url: URL) -> Input
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + url + + +
    +

    URL for file written to stdin of the child process

    +
    +
    +
    +
    +

    Return Value

    +

    New Input instance

    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input/Value.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input/Value.html new file mode 100644 index 0000000..019d05d --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/Input/Value.html @@ -0,0 +1,185 @@ + + + + Value Enumeration Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

Value

+
+
+
public enum Value
+ +
+
+

Reference to the input value

+ +
+
+
+
    +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Data to be written to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case data(Data)
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + text(_:_:) + +
    +
    +
    +
    +
    +
    +

    Text to be written to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case text(String, String.Encoding)
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + file(_:) + +
    +
    +
    +
    +
    +
    +

    File to be written to stdin of the child process

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case file(URL)
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/SubprocessDependencyBuilder.html b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/SubprocessDependencyBuilder.html new file mode 100644 index 0000000..5de488a --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/Structs/SubprocessDependencyBuilder.html @@ -0,0 +1,209 @@ + + + + SubprocessDependencyBuilder Structure Reference + + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+

SubprocessDependencyBuilder

+
+
+
public struct SubprocessDependencyBuilder : SubprocessDependencyFactory
+ +
+
+

Default implementation of SubprocessDependencyFactory

+ +
+
+
+
    +
  • +
    + + + + shared + +
    +
    +
    +
    +
    +
    +

    Shared instance used for dependency creatation

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static var shared: SubprocessDependencyFactory
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + createProcess(for:) + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func createProcess(for command: [String]) -> Process
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func createInputFileHandle(for url: URL) throws -> FileHandle
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + createInputPipe(for:) + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func createInputPipe(for data: Data) -> Pipe
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/badge.svg b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/badge.svg new file mode 100644 index 0000000..a096fec --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 100% + + + 100% + + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/highlight.css b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/highlight.css new file mode 100644 index 0000000..d0db0e1 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/highlight.css @@ -0,0 +1,200 @@ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight { + /* Comment */ + /* Error */ + /* Keyword */ + /* Operator */ + /* Comment.Multiline */ + /* Comment.Preproc */ + /* Comment.Single */ + /* Comment.Special */ + /* Generic.Deleted */ + /* Generic.Deleted.Specific */ + /* Generic.Emph */ + /* Generic.Error */ + /* Generic.Heading */ + /* Generic.Inserted */ + /* Generic.Inserted.Specific */ + /* Generic.Output */ + /* Generic.Prompt */ + /* Generic.Strong */ + /* Generic.Subheading */ + /* Generic.Traceback */ + /* Keyword.Constant */ + /* Keyword.Declaration */ + /* Keyword.Pseudo */ + /* Keyword.Reserved */ + /* Keyword.Type */ + /* Literal.Number */ + /* Literal.String */ + /* Name.Attribute */ + /* Name.Builtin */ + /* Name.Class */ + /* Name.Constant */ + /* Name.Entity */ + /* Name.Exception */ + /* Name.Function */ + /* Name.Namespace */ + /* Name.Tag */ + /* Name.Variable */ + /* Operator.Word */ + /* Text.Whitespace */ + /* Literal.Number.Float */ + /* Literal.Number.Hex */ + /* Literal.Number.Integer */ + /* Literal.Number.Oct */ + /* Literal.String.Backtick */ + /* Literal.String.Char */ + /* Literal.String.Doc */ + /* Literal.String.Double */ + /* Literal.String.Escape */ + /* Literal.String.Heredoc */ + /* Literal.String.Interpol */ + /* Literal.String.Other */ + /* Literal.String.Regex */ + /* Literal.String.Single */ + /* Literal.String.Symbol */ + /* Name.Builtin.Pseudo */ + /* Name.Variable.Class */ + /* Name.Variable.Global */ + /* Name.Variable.Instance */ + /* Literal.Number.Integer.Long */ } + .highlight .c { + color: #999988; + font-style: italic; } + .highlight .err { + color: #a61717; + background-color: #e3d2d2; } + .highlight .k { + color: #000000; + font-weight: bold; } + .highlight .o { + color: #000000; + font-weight: bold; } + .highlight .cm { + color: #999988; + font-style: italic; } + .highlight .cp { + color: #999999; + font-weight: bold; } + .highlight .c1 { + color: #999988; + font-style: italic; } + .highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + .highlight .gd { + color: #000000; + background-color: #ffdddd; } + .highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + .highlight .ge { + color: #000000; + font-style: italic; } + .highlight .gr { + color: #aa0000; } + .highlight .gh { + color: #999999; } + .highlight .gi { + color: #000000; + background-color: #ddffdd; } + .highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + .highlight .go { + color: #888888; } + .highlight .gp { + color: #555555; } + .highlight .gs { + font-weight: bold; } + .highlight .gu { + color: #aaaaaa; } + .highlight .gt { + color: #aa0000; } + .highlight .kc { + color: #000000; + font-weight: bold; } + .highlight .kd { + color: #000000; + font-weight: bold; } + .highlight .kp { + color: #000000; + font-weight: bold; } + .highlight .kr { + color: #000000; + font-weight: bold; } + .highlight .kt { + color: #445588; } + .highlight .m { + color: #009999; } + .highlight .s { + color: #d14; } + .highlight .na { + color: #008080; } + .highlight .nb { + color: #0086B3; } + .highlight .nc { + color: #445588; + font-weight: bold; } + .highlight .no { + color: #008080; } + .highlight .ni { + color: #800080; } + .highlight .ne { + color: #990000; + font-weight: bold; } + .highlight .nf { + color: #990000; } + .highlight .nn { + color: #555555; } + .highlight .nt { + color: #000080; } + .highlight .nv { + color: #008080; } + .highlight .ow { + color: #000000; + font-weight: bold; } + .highlight .w { + color: #bbbbbb; } + .highlight .mf { + color: #009999; } + .highlight .mh { + color: #009999; } + .highlight .mi { + color: #009999; } + .highlight .mo { + color: #009999; } + .highlight .sb { + color: #d14; } + .highlight .sc { + color: #d14; } + .highlight .sd { + color: #d14; } + .highlight .s2 { + color: #d14; } + .highlight .se { + color: #d14; } + .highlight .sh { + color: #d14; } + .highlight .si { + color: #d14; } + .highlight .sx { + color: #d14; } + .highlight .sr { + color: #009926; } + .highlight .s1 { + color: #d14; } + .highlight .ss { + color: #990073; } + .highlight .bp { + color: #999999; } + .highlight .vc { + color: #008080; } + .highlight .vg { + color: #008080; } + .highlight .vi { + color: #008080; } + .highlight .il { + color: #009999; } diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/jazzy.css b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/jazzy.css new file mode 100644 index 0000000..2d5656d --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/css/jazzy.css @@ -0,0 +1,372 @@ +html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { + background: transparent; + border: 0; + margin: 0; + outline: 0; + padding: 0; + vertical-align: baseline; } + +body { + background-color: #f2f2f2; + font-family: Helvetica, freesans, Arial, sans-serif; + font-size: 14px; + -webkit-font-smoothing: subpixel-antialiased; + word-wrap: break-word; } + +h1, h2, h3 { + margin-top: 0.8em; + margin-bottom: 0.3em; + font-weight: 100; + color: black; } + +h1 { + font-size: 2.5em; } + +h2 { + font-size: 2em; + border-bottom: 1px solid #e2e2e2; } + +h4 { + font-size: 13px; + line-height: 1.5; + margin-top: 21px; } + +h5 { + font-size: 1.1em; } + +h6 { + font-size: 1.1em; + color: #777; } + +.section-name { + color: gray; + display: block; + font-family: Helvetica; + font-size: 22px; + font-weight: 100; + margin-bottom: 15px; } + +pre, code { + font: 0.95em Menlo, monospace; + color: #777; + word-wrap: normal; } + +p code, li code { + background-color: #eee; + padding: 2px 4px; + border-radius: 4px; } + +a { + color: #0088cc; + text-decoration: none; } + +ul { + padding-left: 15px; } + +li { + line-height: 1.8em; } + +img { + max-width: 100%; } + +blockquote { + margin-left: 0; + padding: 0 10px; + border-left: 4px solid #ccc; } + +.content-wrapper { + margin: 0 auto; + width: 980px; } + +header { + font-size: 0.85em; + line-height: 26px; + background-color: #414141; + position: fixed; + width: 100%; + z-index: 2; } + header img { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + header a { + color: #fff; } + header p { + float: left; + color: #999; } + header .header-right { + float: right; + margin-left: 16px; } + +#breadcrumbs { + background-color: #f2f2f2; + height: 27px; + padding-top: 17px; + position: fixed; + width: 100%; + z-index: 2; + margin-top: 26px; } + #breadcrumbs #carat { + height: 10px; + margin: 0 5px; } + +.sidebar { + background-color: #f9f9f9; + border: 1px solid #e2e2e2; + overflow-y: auto; + overflow-x: hidden; + position: fixed; + top: 70px; + bottom: 0; + width: 230px; + word-wrap: normal; } + +.nav-groups { + list-style-type: none; + background: #fff; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #e2e2e2; + font-size: 1.1em; + font-weight: 100; + padding: 15px 0 15px 20px; } + .nav-group-name > a { + color: #333; } + +.nav-group-tasks { + margin-top: 5px; } + +.nav-group-task { + font-size: 0.9em; + list-style-type: none; + white-space: nowrap; } + .nav-group-task a { + color: #888; } + +.main-content { + background-color: #fff; + border: 1px solid #e2e2e2; + margin-left: 246px; + position: absolute; + overflow: hidden; + padding-bottom: 20px; + top: 70px; + width: 734px; } + .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { + margin-bottom: 1em; } + .main-content p { + line-height: 1.8em; } + .main-content section .section:first-child { + margin-top: 0; + padding-top: 0; } + .main-content section .task-group-section .task-group:first-of-type { + padding-top: 10px; } + .main-content section .task-group-section .task-group:first-of-type .section-name { + padding-top: 15px; } + .main-content section .heading:before { + content: ""; + display: block; + padding-top: 70px; + margin: -70px 0 0; } + .main-content .section-name p { + margin-bottom: inherit; + line-height: inherit; } + .main-content .section-name code { + background-color: inherit; + padding: inherit; + color: inherit; } + +.section { + padding: 0 25px; } + +.highlight { + background-color: #eee; + padding: 10px 12px; + border: 1px solid #e2e2e2; + border-radius: 4px; + overflow-x: auto; } + +.declaration .highlight { + overflow-x: initial; + padding: 0 40px 40px 0; + margin-bottom: -25px; + background-color: transparent; + border: none; } + +.section-name { + margin: 0; + margin-left: 18px; } + +.task-group-section { + padding-left: 6px; + border-top: 1px solid #e2e2e2; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; + padding-top: 70px; + margin: -70px 0 0; } + +.section-name-container { + position: relative; + display: inline-block; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; + padding-top: 70px; + margin: -70px 0 0; } + .item code { + background-color: transparent; + padding: 0; } + .item .token, .item .direct-link { + padding-left: 3px; + margin-left: 15px; + font-size: 11.9px; + transition: all 300ms; } + .item .token-open { + margin-left: 0px; } + .item .discouraged { + text-decoration: line-through; } + .item .declaration-note { + font-size: .85em; + color: gray; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #e2e2e2; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + background: #f9f9f9; + border-left: 1px solid #e2e2e2; + border-top: 1px solid #e2e2e2; + height: 12px; + left: 21px; + top: -7px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + position: absolute; + width: 12px; } + +.height-container { + display: none; + left: -25px; + padding: 0 25px; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #f9f9f9; + border-bottom: 1px solid #e2e2e2; + left: -25px; + position: relative; + width: 100%; + padding-top: 10px; + padding-bottom: 5px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4b8afb; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #e2e2e2; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +#footer { + position: relative; + top: 10px; + bottom: 0px; + margin-left: 25px; } + #footer p { + margin: 0; + color: #aaa; + font-size: 0.8em; } + +html.dash header, html.dash #breadcrumbs, html.dash .sidebar { + display: none; } + +html.dash .main-content { + width: 980px; + margin-left: 0; + border: none; + width: 100%; + top: 0; + padding-bottom: 0; } + +html.dash .height-container { + display: block; } + +html.dash .item .token { + margin-left: 0; } + +html.dash .content-wrapper { + width: auto; } + +html.dash #footer { + position: static; } diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/img/carat.png b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/img/carat.png new file mode 100755 index 0000000000000000000000000000000000000000..29d2f7fd4955fca6bc6fb740e0373a2c358c398e GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRo!3HEV4DF?Wlw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$y6JlqAi{-jv*Ddl5#RKJQ5NTUZgiPI4RUKGIKU?u8L&ndhX1t za+0CMVUnT(Gnb}ei=c~x==tMH^F1_tBocXwcoSWoO-SZY-o>!8%^=Bms)(~h;m_U( zXNixk28L}0LS5-jKyq@#2gyS|J&f#pGCLkTc<@2s1dqeyqJ*Rc0tSIETAgmODY;(s z2y|Mcp&2}7rpBprBBB~1qM1`N+}4SoxYVPqsXi&l`rxZp{(w0iSy$Nv5*Vy!RapG^ S^0y4=eg;ohKbLh*2~7a!Pg}VF literal 0 HcmV?d00001 diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/img/dash.png b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/img/dash.png new file mode 100755 index 0000000000000000000000000000000000000000..6f694c7a012b417908da3687a0a39aa182e91c74 GIT binary patch literal 1338 zcmaJ>U2NM_6t){^r>#wcfL0VSTvuX@)$vd4#5N6WVkc|1rR}naMb)(7I5(};#!el# zbtCASsp?W-qE8zSJoFVdA%-T$WL8RI_B? zd+t5o`T5Q{p6=<|U$?VqCxRe#u}(PwSIl{LRKstfSbPYV7pzFiI$~t4QN;vEC}X4n z7RxDpAOV!j*w8ni4MAK3S~6v&;)g`l$axh<$7|>E5RD*h?RH*K2Y`j8L7%1v@%vZi za7@bt@uOUvisvQJuXPqpaHQCkREqd6M>0WG?6AwXR*T65ziuw$&~q$MS$o zfPyh>s<0l}mI@eh_hd(oB8*1tHZ@ojWl%QM;T+Jdm>k66jW?rZ#Atx!qns4-g&E4v z(=;FQ%W^avW?3J{L@2IeV>_(Ca)Lk1vm70uX*$9Rewm8!AxRF0BcZTNSFka?U@5u^ zDtpMY2lVtCmQm<8@|YxHuf`Qs(;a!QQ=g4=WngL}AQLr> z9JWrdsBIHKHXF!fSydodRsaOc@jgNkSU^x9kY&;UP<}3pZ{joC5f_Tevd>4eG~;)Y z=eZ~qp=5#aaUn*E3OES^BApKTU&mCAU>iEyt^S9?)&v0^j*SWDqjRZr20>6rTPSJ& zlzz0f);`}+^~w}lP1PK7Ew3f7ot#*uJ@>1Yo3J0TdsRKpA+*n9JnDXDrM~YvF`;uS|vAh|-QdmRf4AqG=`U z#v1n_Lxg8;&z#YCU2K`_W{-A zUf_|V)B9U(WZ~PP>)O(JZ|Vc-*qP&Q{c~BE~6izDPQq)#Nu*KOf(n^(VHY9;fiINM65``pc+9*v(mL$bwfCjbc%v9V{8r9iX|O%>Nr%pLD2qT{mty}c=LVleeamv znz3SOSm@kP8jThvOOq(56Yzh*fz(booe!uZij=BJC6+_lbvQ~B8nA2>kXdv_RDtRY z`5QXWWEySCe6vbTs^#f?J!WC*{1~RgVx!nJTJjQyO{dRANgx|FnymtGbD9%JmCh9^y)##j7{Dcqfn*1ta$rG89pJF6w-S7Z037$rr|y0;1Onp_ zGFJdT6Q!1C0AdVB0WOmpuV=AgAQ550Tn+-mivTtYPJmz*#75#_n9oV%!#rSOfmAfy zki%C~=fTp1{O#BLpJ|0jj#m6#|LRWit-vq3PE1z9ZqyvET4sX$-Icqy7t z<=aq5ff86AuBZBu6EjJsYWM0uejufWFTwPA7Su}0Bm$7KFb!q{Um_8~A{LUG#1l(l zSehUda@kU8LIRg9fkk2tZ;~ss5~R+mM<==F7hLHpxqLB>>PQS%Vc7b~?q!%T5+h8Q z4G=4Nzyi5WZ?^gkasJ{?Xhm`JC#WG6$1K2jb@=9&D3EgD#3UhGh#*21rJjulVXjCF zvp76q62jt0zzMG5C7DlfMgPl%C^3+~wf|}Lq=}jz|MmIcQjh1Ok6NjD$Em^Iv26D> z8tt_TnM9~^Tt8mflRGPOrrX|HtT3gG4LEuuk{g2Rn}QgJIa?gZo))!!=o_l9bvD%A zZ`aHajl8#~u?!4f7F#*b*->A=R2L)6!>saz?h>#wTXT-I(XmQ zx{84skS>k=i~i`(6k4C7;Zpfx%dCPVjPayMf8pugtGM=~s=Id1l#8MZJ1-73wV#Q3 zR3>v3%}jbQs1f_Z0xo;%=LILlA+nTpKI4ha%xWW}uqHrNao~&T4AY6m`P$_n-6h*g zhoX+e4n%~gl_lhe#s+AMb7d{5WzvYTa%6Q~si@@4{;s(0zU|H&P3fE+t{7X`S#Cj@ zC#vd}^4pcBD*77Ny5=j$h8EL2_t$O38$SQiJ6fPjJMimypr~MB2(&P0aI|h}$64<0 z>_~duqNjaT=DM^6+N{&B_lED;F2wrl?!4Lk*2((x!fmrcsw+=cI^qttuZ9C}-m~5E z-ryYVpL%^xR#&(0YI5hz<(}F7-p)?FPcyJO-zVO>%9ZDXJH8pnY;GJYFDQ>vd#j_* zRrd}L(r=!g+1#nQwsO?kpS`Qq8`NxE+Zy{gf7*_7J*U2V_|NpLo{iasj7VCg_V9&| ShohtYzipXxh2)4xTk + + + Subprocess Reference + + + + + + + + + +
+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+ +

Subprocess

+ +

Build +License +Platform +Language +Carthage compatible +SwiftPM compatible

+ + + +

Full Documentation

+

Usage

+

Shell Class

+ +

The Shell class can be used for synchronous command execution.

+

Command Input

+
Input for data
+
let inputData: Data = ...
+let data = try Shell(["/usr/bin/grep", "Hello"]).exec(input: .data(inputData))
+
+
Input for text
+
let data = try Shell(["/usr/bin/grep", "Hello"]).exec(input: .text("Hello world"))
+
+
Input for file URL
+
let url = URL(fileURLWithPath: "/path/to/input/file")
+let data = try Shell(["/usr/bin/grep", "foo"]).exec(input: .file(url: url))
+
+
Input for file path
+
let data = try Shell(["/usr/bin/grep", "foo"]).exec(input: .file(path: "/path/to/input/file"))
+
+

Command Output

+
Output as Data
+
let data = try Shell(["/usr/bin/sw_vers"]).exec()
+
+
Output as String
+
let text = try Shell(["/usr/bin/sw_vers"]).exec(encoding: .utf8)
+
+
Output as JSON (Array or Dictionary)
+
let command = ["/usr/bin/log", "show", "--style", "json", "--last", "5m"]
+let logs: [[String: Any]] = try Shell(command).execJSON())
+
+
Output as decodable object from JSON
+
struct LogMessage: Codable {
+    var subsystem: String
+    var category: String
+    var machTimestamp: UInt64
+}
+let command = ["/usr/bin/log", "show", "--style", "json", "--last", "5m"]
+let logs: [LogMessage] = try Shell(command).exec(decoder: JSONDecoder())
+
+
Output as Property List (Array or Dictionary)
+
let command = ["/bin/cat", "/System/Library/CoreServices/SystemVersion.plist"]
+let dictionary: [String: Any] = try Shell(command).execPropertyList())
+
+
Output as decodable object from Property List
+
struct SystemVersion: Codable {
+    enum CodingKeys: String, CodingKey {
+        case version = "ProductVersion"
+    }
+    var version: String
+}
+let command = ["/bin/cat", "/System/Library/CoreServices/SystemVersion.plist"]
+let result: SystemVersion = try Shell(command).exec(decoder: PropertyListDecoder())
+
+
Output mapped to other type
+
let enabled = try Shell(["csrutil", "status"]).exec(encoding: .utf8) { _, txt in txt.contains("enabled") }
+
+
Output options
+
let command: [String] = ...
+let errorText = try Shell(command).exec(options: .stderr, encoding: .utf8)
+let outputText = try Shell(command).exec(options: .stdout, encoding: .utf8)
+let combinedData = try Shell(command).exec(options: .combined)
+
+

Subprocess Class

+ +

The Subprocess class can be used for asynchronous command execution.

+
Handling output as it is read
+
let command: [String] = ...
+let process = Subprocess(command)
+
+// The outputHandler and errorHandler are invoked serially
+try process.launch(outputHandler: { data in
+    // Handle new data read from stdout
+}, errorHandler: { data in
+    // Handle new data read from stderr
+}, terminationHandler: { process in
+    // Handle process termination, all scheduled calls to
+    // the outputHandler and errorHandler are guaranteed to
+    // have completed.
+})
+
+
Handling output on termination
+
let command: [String] = ...
+let process = Subprocess(command)
+
+try process.launch { (process, outputData, errorData) in
+    if process.exitCode == 0 {
+        // Do something with output data
+    } else {
+        // Handle failure
+    }
+}
+
+

Installation

+

SwiftPM

+
dependencies: [
+    .package(url: "https://github.com/jamf/Subprocess.git", from: "1.0.0")
+]
+
+

Cocoapods

+
pod 'Subprocess'
+
+

Carthage

+
github 'jamf/Subprocess'
+
+ +
+
+ +
+
+ + + diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jazzy.js b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jazzy.js new file mode 100755 index 0000000..c31dc05 --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jazzy.js @@ -0,0 +1,59 @@ +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`.token[href="${location.hash}"]`); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jquery.min.js b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jquery.min.js new file mode 100644 index 0000000..a1c07fd --- /dev/null +++ b/docs/docsets/Subprocess.docset/Contents/Resources/Documents/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0Shared instance used for dependency creatation

","parent_name":"SubprocessDependencyBuilder"},"Structs/SubprocessDependencyBuilder.html#/s:10Subprocess0A17DependencyFactoryP13createProcess3forSo6NSTaskCSaySSG_tF":{"name":"createProcess(for:)","parent_name":"SubprocessDependencyBuilder"},"Structs/SubprocessDependencyBuilder.html#/s:10Subprocess0A17DependencyFactoryP21createInputFileHandle3forSo06NSFileG0C10Foundation3URLV_tKF":{"name":"createInputFileHandle(for:)","parent_name":"SubprocessDependencyBuilder"},"Structs/SubprocessDependencyBuilder.html#/s:10Subprocess0A17DependencyFactoryP15createInputPipe3forSo6NSPipeC10Foundation4DataV_tF":{"name":"createInputPipe(for:)","parent_name":"SubprocessDependencyBuilder"},"Structs/Input/Value.html#/s:10Subprocess5InputV5ValueO4datayAE10Foundation4DataVcAEmF":{"name":"data(_:)","abstract":"

Data to be written to stdin of the child process

","parent_name":"Value"},"Structs/Input/Value.html#/s:10Subprocess5InputV5ValueO4textyAESS_SS10FoundationE8EncodingVtcAEmF":{"name":"text(_:_:)","abstract":"

Text to be written to stdin of the child process

","parent_name":"Value"},"Structs/Input/Value.html#/s:10Subprocess5InputV5ValueO4fileyAE10Foundation3URLVcAEmF":{"name":"file(_:)","abstract":"

File to be written to stdin of the child process

","parent_name":"Value"},"Structs/Input/Value.html":{"name":"Value","abstract":"

Reference to the input value

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV5valueAC5ValueOvp":{"name":"value","abstract":"

Reference to the input value

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4datayAC10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Creates input for writing data to stdin of the child process

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4text_8encodingACSS_SS10FoundationE8EncodingVtFZ":{"name":"text(_:encoding:)","abstract":"

Creates input for writing text to stdin of the child process

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4file4pathACSS_tFZ":{"name":"file(path:)","abstract":"

Creates input for writing contents of file at path to stdin of the child process

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4file3urlAC10Foundation3URLV_tFZ":{"name":"file(url:)","abstract":"

Creates input for writing contents of file URL to stdin of the child process

","parent_name":"Input"},"Structs/Input.html":{"name":"Input","abstract":"

Interface representing into to the process

"},"Structs/SubprocessDependencyBuilder.html":{"name":"SubprocessDependencyBuilder","abstract":"

Default implementation of SubprocessDependencyFactory

"},"Protocols/SubprocessDependencyFactory.html#/s:10Subprocess0A17DependencyFactoryP13createProcess3forSo6NSTaskCSaySSG_tF":{"name":"createProcess(for:)","abstract":"

Creates new Subprocess

","parent_name":"SubprocessDependencyFactory"},"Protocols/SubprocessDependencyFactory.html#/s:10Subprocess0A17DependencyFactoryP21createInputFileHandle3forSo06NSFileG0C10Foundation3URLV_tKF":{"name":"createInputFileHandle(for:)","abstract":"

Creates a FileHandle for reading

","parent_name":"SubprocessDependencyFactory"},"Protocols/SubprocessDependencyFactory.html#/s:10Subprocess0A17DependencyFactoryP15createInputPipe3forSo6NSPipeC10Foundation4DataV_tF":{"name":"createInputPipe(for:)","abstract":"

Creates a Pipe and writes given data

","parent_name":"SubprocessDependencyFactory"},"Protocols/SubprocessDependencyFactory.html":{"name":"SubprocessDependencyFactory","abstract":"

Protocol call used for dependency injection

"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO24exitedWithNoneZeroStatusyACs5Int32VcACmF":{"name":"exitedWithNoneZeroStatus(_:)","abstract":"

The process completed with a none-zero exit code

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO28unexpectedPropertyListObjectyACSScACmF":{"name":"unexpectedPropertyListObject(_:)","abstract":"

The property list object could not be casted into expected type

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO20unexpectedJSONObjectyACSScACmF":{"name":"unexpectedJSONObject(_:)","abstract":"

The JSON object could not be casted into expected type

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO019inputStringEncodingB0yA2CmF":{"name":"inputStringEncodingError","abstract":"

Input string could not be encoded

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO020outputStringEncodingB0yA2CmF":{"name":"outputStringEncodingError","abstract":"

Output string could not be encoded

","parent_name":"SubprocessError"},"Enums/SubprocessError.html":{"name":"SubprocessError","abstract":"

Type representing possible errors

"},"Classes/Subprocess.html#/s:10SubprocessAAC3pids5Int32Vvp":{"name":"pid","abstract":"

Process identifier

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC8exitCodes5Int32Vvp":{"name":"exitCode","abstract":"

Exit code of the process

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC9isRunningSbvp":{"name":"isRunning","abstract":"

Returns whether the process is still running.

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC17terminationReasonSo017NSTaskTerminationC0Vvp":{"name":"terminationReason","abstract":"

Reason for process termination

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC_3qosABSaySSG_8Dispatch0C3QoSVtcfc":{"name":"init(_:qos:)","abstract":"

Creates new Subprocess

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC6launch5input13outputHandler05errorE0011terminationE0yAA5InputVSg_y10Foundation4DataVcSgANyABcSgtKF":{"name":"launch(input:outputHandler:errorHandler:terminationHandler:)","abstract":"

Launches command with read handlers and termination handler

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC22DataTerminationHandlera":{"name":"DataTerminationHandler","abstract":"

Block type called for executing process returning data from standard out and standard error

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC6launch5input18terminationHandleryAA5InputVSg_yAB_10Foundation4DataVAKtctKF":{"name":"launch(input:terminationHandler:)","abstract":"

Launches command calling a block when process terminates

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC7suspendSbyF":{"name":"suspend()","abstract":"

Suspends the command

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC6resumeSbyF":{"name":"resume()","abstract":"

Resumes the command which was suspended

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC4killyyF":{"name":"kill()","abstract":"

Sends the command the term signal

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC18waitForTerminationyyF":{"name":"waitForTermination()","abstract":"

Waits for process to complete and all handlers to be called

","parent_name":"Subprocess"},"Classes/Shell/OutputOptions.html#/s:SY8rawValue03RawB0Qzvp":{"name":"rawValue","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:10Subprocess5ShellC13OutputOptionsV6stdoutAEvpZ":{"name":"stdout","abstract":"

Processes data written to stdout

","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:10Subprocess5ShellC13OutputOptionsV6stderrAEvpZ":{"name":"stderr","abstract":"

Processes data written to stderr

","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:10Subprocess5ShellC13OutputOptionsV8combinedAEvpZ":{"name":"combined","abstract":"

Processes data written to both stdout and stderr

","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:s9OptionSetP8rawValuex03RawD0Qz_tcfc":{"name":"init(rawValue:)","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html":{"name":"OutputOptions","abstract":"

OptionSet representing output handling

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC7processA2ACvp":{"name":"process","abstract":"

Reference to subprocess

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellCyACSaySSGcfc":{"name":"init(_:)","abstract":"

Creates new Shell instance

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options14transformBlockxAA5InputVSg_AC13OutputOptionsVxA2AC_10Foundation4DataVtKXEtKlF":{"name":"exec(input:options:transformBlock:)","abstract":"

Executes shell command using a supplied block to tranform the process output into whatever type you would like

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options10Foundation4DataVAA5InputVSg_AC13OutputOptionsVtKF":{"name":"exec(input:options:)","abstract":"

Executes shell command expecting exit code of zero and returning the output data

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options8encoding14transformBlockxAA5InputVSg_AC13OutputOptionsVSS10FoundationE8EncodingVxA2AC_SStKXEtKlF":{"name":"exec(input:options:encoding:transformBlock:)","abstract":"

Executes shell command using a supplied block to tranform the process output as a String","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options8encodingSSAA5InputVSg_AC13OutputOptionsVSS10FoundationE8EncodingVtKF":{"name":"exec(input:options:encoding:)","abstract":"

Executes shell command expecting exit code of zero and returning the output as a string

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC8execJSON5input7optionsxAA5InputVSg_AC13OutputOptionsVtKlF":{"name":"execJSON(input:options:)","abstract":"

Executes shell command expecting JSON

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC16execPropertyList5input7optionsxAA5InputVSg_AC13OutputOptionsVtKlF":{"name":"execPropertyList(input:options:)","abstract":"

Executes shell command expecting a property list

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options7decoderxAA5InputVSg_AC13OutputOptionsV10Foundation11JSONDecoderCtKSeRzlF":{"name":"exec(input:options:decoder:)","abstract":"

Executes shell command expecting JSON and decodes object conforming to Decodable

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options7decoderxAA5InputVSg_AC13OutputOptionsV10Foundation19PropertyListDecoderCtKSeRzlF":{"name":"exec(input:options:decoder:)","abstract":"

Executes shell command expecting property list and decodes object conforming to Decodable

","parent_name":"Shell"},"Classes/Shell.html":{"name":"Shell","abstract":"

Class used for synchronous process execution

"},"Classes/Subprocess.html":{"name":"Subprocess","abstract":"

Class used for asynchronous process execution

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/docsets/Subprocess.docset/Contents/Resources/docSet.dsidx b/docs/docsets/Subprocess.docset/Contents/Resources/docSet.dsidx new file mode 100644 index 0000000000000000000000000000000000000000..ba5bc0100b307a7d9932a5f35ae8467a1171c914 GIT binary patch literal 28672 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 literal 0 HcmV?d00001 diff --git a/docs/docsets/Subprocess.tgz b/docs/docsets/Subprocess.tgz new file mode 100644 index 0000000000000000000000000000000000000000..38a6183d08655c5bc319e4016f76c63c26a8201d GIT binary patch literal 56011 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 literal 0 HcmV?d00001 diff --git a/docs/img/carat.png b/docs/img/carat.png new file mode 100755 index 0000000000000000000000000000000000000000..29d2f7fd4955fca6bc6fb740e0373a2c358c398e GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRo!3HEV4DF?Wlw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$y6JlqAi{-jv*Ddl5#RKJQ5NTUZgiPI4RUKGIKU?u8L&ndhX1t za+0CMVUnT(Gnb}ei=c~x==tMH^F1_tBocXwcoSWoO-SZY-o>!8%^=Bms)(~h;m_U( zXNixk28L}0LS5-jKyq@#2gyS|J&f#pGCLkTc<@2s1dqeyqJ*Rc0tSIETAgmODY;(s z2y|Mcp&2}7rpBprBBB~1qM1`N+}4SoxYVPqsXi&l`rxZp{(w0iSy$Nv5*Vy!RapG^ S^0y4=eg;ohKbLh*2~7a!Pg}VF literal 0 HcmV?d00001 diff --git a/docs/img/dash.png b/docs/img/dash.png new file mode 100755 index 0000000000000000000000000000000000000000..6f694c7a012b417908da3687a0a39aa182e91c74 GIT binary patch literal 1338 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 ShohtYzipXxh2)4xTk + + + Subprocess Reference + + + + + + + + + +

+
+

Subprocess Docs (100% documented)

+
+
+
+ +
+
+ +
+
+
+ +

Subprocess

+ +

Build +License +Platform +Language +Carthage compatible +SwiftPM compatible

+ + + +

Full Documentation

+

Usage

+

Shell Class

+ +

The Shell class can be used for synchronous command execution.

+

Command Input

+
Input for data
+
let inputData: Data = ...
+let data = try Shell(["/usr/bin/grep", "Hello"]).exec(input: .data(inputData))
+
+
Input for text
+
let data = try Shell(["/usr/bin/grep", "Hello"]).exec(input: .text("Hello world"))
+
+
Input for file URL
+
let url = URL(fileURLWithPath: "/path/to/input/file")
+let data = try Shell(["/usr/bin/grep", "foo"]).exec(input: .file(url: url))
+
+
Input for file path
+
let data = try Shell(["/usr/bin/grep", "foo"]).exec(input: .file(path: "/path/to/input/file"))
+
+

Command Output

+
Output as Data
+
let data = try Shell(["/usr/bin/sw_vers"]).exec()
+
+
Output as String
+
let text = try Shell(["/usr/bin/sw_vers"]).exec(encoding: .utf8)
+
+
Output as JSON (Array or Dictionary)
+
let command = ["/usr/bin/log", "show", "--style", "json", "--last", "5m"]
+let logs: [[String: Any]] = try Shell(command).execJSON())
+
+
Output as decodable object from JSON
+
struct LogMessage: Codable {
+    var subsystem: String
+    var category: String
+    var machTimestamp: UInt64
+}
+let command = ["/usr/bin/log", "show", "--style", "json", "--last", "5m"]
+let logs: [LogMessage] = try Shell(command).exec(decoder: JSONDecoder())
+
+
Output as Property List (Array or Dictionary)
+
let command = ["/bin/cat", "/System/Library/CoreServices/SystemVersion.plist"]
+let dictionary: [String: Any] = try Shell(command).execPropertyList())
+
+
Output as decodable object from Property List
+
struct SystemVersion: Codable {
+    enum CodingKeys: String, CodingKey {
+        case version = "ProductVersion"
+    }
+    var version: String
+}
+let command = ["/bin/cat", "/System/Library/CoreServices/SystemVersion.plist"]
+let result: SystemVersion = try Shell(command).exec(decoder: PropertyListDecoder())
+
+
Output mapped to other type
+
let enabled = try Shell(["csrutil", "status"]).exec(encoding: .utf8) { _, txt in txt.contains("enabled") }
+
+
Output options
+
let command: [String] = ...
+let errorText = try Shell(command).exec(options: .stderr, encoding: .utf8)
+let outputText = try Shell(command).exec(options: .stdout, encoding: .utf8)
+let combinedData = try Shell(command).exec(options: .combined)
+
+

Subprocess Class

+ +

The Subprocess class can be used for asynchronous command execution.

+
Handling output as it is read
+
let command: [String] = ...
+let process = Subprocess(command)
+
+// The outputHandler and errorHandler are invoked serially
+try process.launch(outputHandler: { data in
+    // Handle new data read from stdout
+}, errorHandler: { data in
+    // Handle new data read from stderr
+}, terminationHandler: { process in
+    // Handle process termination, all scheduled calls to
+    // the outputHandler and errorHandler are guaranteed to
+    // have completed.
+})
+
+
Handling output on termination
+
let command: [String] = ...
+let process = Subprocess(command)
+
+try process.launch { (process, outputData, errorData) in
+    if process.exitCode == 0 {
+        // Do something with output data
+    } else {
+        // Handle failure
+    }
+}
+
+

Installation

+

SwiftPM

+
dependencies: [
+    .package(url: "https://github.com/jamf/Subprocess.git", from: "1.0.0")
+]
+
+

Cocoapods

+
pod 'Subprocess'
+
+

Carthage

+
github 'jamf/Subprocess'
+
+ +
+
+ +
+
+ + + diff --git a/docs/js/jazzy.js b/docs/js/jazzy.js new file mode 100755 index 0000000..c31dc05 --- /dev/null +++ b/docs/js/jazzy.js @@ -0,0 +1,59 @@ +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`.token[href="${location.hash}"]`); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); diff --git a/docs/js/jquery.min.js b/docs/js/jquery.min.js new file mode 100644 index 0000000..a1c07fd --- /dev/null +++ b/docs/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0Shared instance used for dependency creatation

","parent_name":"SubprocessDependencyBuilder"},"Structs/SubprocessDependencyBuilder.html#/s:10Subprocess0A17DependencyFactoryP13createProcess3forSo6NSTaskCSaySSG_tF":{"name":"createProcess(for:)","parent_name":"SubprocessDependencyBuilder"},"Structs/SubprocessDependencyBuilder.html#/s:10Subprocess0A17DependencyFactoryP21createInputFileHandle3forSo06NSFileG0C10Foundation3URLV_tKF":{"name":"createInputFileHandle(for:)","parent_name":"SubprocessDependencyBuilder"},"Structs/SubprocessDependencyBuilder.html#/s:10Subprocess0A17DependencyFactoryP15createInputPipe3forSo6NSPipeC10Foundation4DataV_tF":{"name":"createInputPipe(for:)","parent_name":"SubprocessDependencyBuilder"},"Structs/Input/Value.html#/s:10Subprocess5InputV5ValueO4datayAE10Foundation4DataVcAEmF":{"name":"data(_:)","abstract":"

Data to be written to stdin of the child process

","parent_name":"Value"},"Structs/Input/Value.html#/s:10Subprocess5InputV5ValueO4textyAESS_SS10FoundationE8EncodingVtcAEmF":{"name":"text(_:_:)","abstract":"

Text to be written to stdin of the child process

","parent_name":"Value"},"Structs/Input/Value.html#/s:10Subprocess5InputV5ValueO4fileyAE10Foundation3URLVcAEmF":{"name":"file(_:)","abstract":"

File to be written to stdin of the child process

","parent_name":"Value"},"Structs/Input/Value.html":{"name":"Value","abstract":"

Reference to the input value

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV5valueAC5ValueOvp":{"name":"value","abstract":"

Reference to the input value

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4datayAC10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Creates input for writing data to stdin of the child process

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4text_8encodingACSS_SS10FoundationE8EncodingVtFZ":{"name":"text(_:encoding:)","abstract":"

Creates input for writing text to stdin of the child process

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4file4pathACSS_tFZ":{"name":"file(path:)","abstract":"

Creates input for writing contents of file at path to stdin of the child process

","parent_name":"Input"},"Structs/Input.html#/s:10Subprocess5InputV4file3urlAC10Foundation3URLV_tFZ":{"name":"file(url:)","abstract":"

Creates input for writing contents of file URL to stdin of the child process

","parent_name":"Input"},"Structs/Input.html":{"name":"Input","abstract":"

Interface representing into to the process

"},"Structs/SubprocessDependencyBuilder.html":{"name":"SubprocessDependencyBuilder","abstract":"

Default implementation of SubprocessDependencyFactory

"},"Protocols/SubprocessDependencyFactory.html#/s:10Subprocess0A17DependencyFactoryP13createProcess3forSo6NSTaskCSaySSG_tF":{"name":"createProcess(for:)","abstract":"

Creates new Subprocess

","parent_name":"SubprocessDependencyFactory"},"Protocols/SubprocessDependencyFactory.html#/s:10Subprocess0A17DependencyFactoryP21createInputFileHandle3forSo06NSFileG0C10Foundation3URLV_tKF":{"name":"createInputFileHandle(for:)","abstract":"

Creates a FileHandle for reading

","parent_name":"SubprocessDependencyFactory"},"Protocols/SubprocessDependencyFactory.html#/s:10Subprocess0A17DependencyFactoryP15createInputPipe3forSo6NSPipeC10Foundation4DataV_tF":{"name":"createInputPipe(for:)","abstract":"

Creates a Pipe and writes given data

","parent_name":"SubprocessDependencyFactory"},"Protocols/SubprocessDependencyFactory.html":{"name":"SubprocessDependencyFactory","abstract":"

Protocol call used for dependency injection

"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO24exitedWithNoneZeroStatusyACs5Int32VcACmF":{"name":"exitedWithNoneZeroStatus(_:)","abstract":"

The process completed with a none-zero exit code

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO28unexpectedPropertyListObjectyACSScACmF":{"name":"unexpectedPropertyListObject(_:)","abstract":"

The property list object could not be casted into expected type

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO20unexpectedJSONObjectyACSScACmF":{"name":"unexpectedJSONObject(_:)","abstract":"

The JSON object could not be casted into expected type

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO019inputStringEncodingB0yA2CmF":{"name":"inputStringEncodingError","abstract":"

Input string could not be encoded

","parent_name":"SubprocessError"},"Enums/SubprocessError.html#/s:10Subprocess0A5ErrorO020outputStringEncodingB0yA2CmF":{"name":"outputStringEncodingError","abstract":"

Output string could not be encoded

","parent_name":"SubprocessError"},"Enums/SubprocessError.html":{"name":"SubprocessError","abstract":"

Type representing possible errors

"},"Classes/Subprocess.html#/s:10SubprocessAAC3pids5Int32Vvp":{"name":"pid","abstract":"

Process identifier

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC8exitCodes5Int32Vvp":{"name":"exitCode","abstract":"

Exit code of the process

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC9isRunningSbvp":{"name":"isRunning","abstract":"

Returns whether the process is still running.

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC17terminationReasonSo017NSTaskTerminationC0Vvp":{"name":"terminationReason","abstract":"

Reason for process termination

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC_3qosABSaySSG_8Dispatch0C3QoSVtcfc":{"name":"init(_:qos:)","abstract":"

Creates new Subprocess

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC6launch5input13outputHandler05errorE0011terminationE0yAA5InputVSg_y10Foundation4DataVcSgANyABcSgtKF":{"name":"launch(input:outputHandler:errorHandler:terminationHandler:)","abstract":"

Launches command with read handlers and termination handler

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC22DataTerminationHandlera":{"name":"DataTerminationHandler","abstract":"

Block type called for executing process returning data from standard out and standard error

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC6launch5input18terminationHandleryAA5InputVSg_yAB_10Foundation4DataVAKtctKF":{"name":"launch(input:terminationHandler:)","abstract":"

Launches command calling a block when process terminates

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC7suspendSbyF":{"name":"suspend()","abstract":"

Suspends the command

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC6resumeSbyF":{"name":"resume()","abstract":"

Resumes the command which was suspended

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC4killyyF":{"name":"kill()","abstract":"

Sends the command the term signal

","parent_name":"Subprocess"},"Classes/Subprocess.html#/s:10SubprocessAAC18waitForTerminationyyF":{"name":"waitForTermination()","abstract":"

Waits for process to complete and all handlers to be called

","parent_name":"Subprocess"},"Classes/Shell/OutputOptions.html#/s:SY8rawValue03RawB0Qzvp":{"name":"rawValue","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:10Subprocess5ShellC13OutputOptionsV6stdoutAEvpZ":{"name":"stdout","abstract":"

Processes data written to stdout

","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:10Subprocess5ShellC13OutputOptionsV6stderrAEvpZ":{"name":"stderr","abstract":"

Processes data written to stderr

","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:10Subprocess5ShellC13OutputOptionsV8combinedAEvpZ":{"name":"combined","abstract":"

Processes data written to both stdout and stderr

","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html#/s:s9OptionSetP8rawValuex03RawD0Qz_tcfc":{"name":"init(rawValue:)","parent_name":"OutputOptions"},"Classes/Shell/OutputOptions.html":{"name":"OutputOptions","abstract":"

OptionSet representing output handling

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC7processA2ACvp":{"name":"process","abstract":"

Reference to subprocess

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellCyACSaySSGcfc":{"name":"init(_:)","abstract":"

Creates new Shell instance

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options14transformBlockxAA5InputVSg_AC13OutputOptionsVxA2AC_10Foundation4DataVtKXEtKlF":{"name":"exec(input:options:transformBlock:)","abstract":"

Executes shell command using a supplied block to tranform the process output into whatever type you would like

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options10Foundation4DataVAA5InputVSg_AC13OutputOptionsVtKF":{"name":"exec(input:options:)","abstract":"

Executes shell command expecting exit code of zero and returning the output data

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options8encoding14transformBlockxAA5InputVSg_AC13OutputOptionsVSS10FoundationE8EncodingVxA2AC_SStKXEtKlF":{"name":"exec(input:options:encoding:transformBlock:)","abstract":"

Executes shell command using a supplied block to tranform the process output as a String","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options8encodingSSAA5InputVSg_AC13OutputOptionsVSS10FoundationE8EncodingVtKF":{"name":"exec(input:options:encoding:)","abstract":"

Executes shell command expecting exit code of zero and returning the output as a string

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC8execJSON5input7optionsxAA5InputVSg_AC13OutputOptionsVtKlF":{"name":"execJSON(input:options:)","abstract":"

Executes shell command expecting JSON

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC16execPropertyList5input7optionsxAA5InputVSg_AC13OutputOptionsVtKlF":{"name":"execPropertyList(input:options:)","abstract":"

Executes shell command expecting a property list

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options7decoderxAA5InputVSg_AC13OutputOptionsV10Foundation11JSONDecoderCtKSeRzlF":{"name":"exec(input:options:decoder:)","abstract":"

Executes shell command expecting JSON and decodes object conforming to Decodable

","parent_name":"Shell"},"Classes/Shell.html#/s:10Subprocess5ShellC4exec5input7options7decoderxAA5InputVSg_AC13OutputOptionsV10Foundation19PropertyListDecoderCtKSeRzlF":{"name":"exec(input:options:decoder:)","abstract":"

Executes shell command expecting property list and decodes object conforming to Decodable

","parent_name":"Shell"},"Classes/Shell.html":{"name":"Shell","abstract":"

Class used for synchronous process execution

"},"Classes/Subprocess.html":{"name":"Subprocess","abstract":"

Class used for asynchronous process execution

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file