Compare commits

..

No commits in common. "master" and "swift-3.0" have entirely different histories.

270 changed files with 5746 additions and 16538 deletions

View File

@ -1,108 +0,0 @@
fastlane_version '2.54.1'
default_platform :mac
fastlane_require 'json'
platform :mac do
before_all do
ensure_git_status_clean
ensure_git_branch
end
desc 'Releases build specified by user.'
lane :release do
release_type = UI.select('Select release type: ', %w[major minor patch])
upload_release release_type
end
desc 'Releases major build. (+1).0.0'
lane :major do # fastlane major
upload_release 'major'
end
desc 'Releases minor build. X.(+1).0'
lane :minor do # fastlane minor
upload_release 'minor'
end
desc 'Releases patch build. X.X.(+1)'
lane :patch do # fastlane patch
upload_release 'patch'
end
def upload_release release_type
# Building Cuckoo Generator
sh('../build_generator')
# Settings
binary_name = 'cuckoo_generator'
cuckoo_gen_path = "../Generator/bin/#{binary_name}"
# GitHub username
username_var_name = 'GITHUB_USERNAME'
username = ENV[username_var_name]
# Personal Access Token
token_var_name = 'RELEASE_ACCESS_TOKEN'
access_token = ENV[token_var_name]
# Error Raisins
unless access_token
access_token = UI.input "Please type in your GitHub access token or cancel the release and define enviroment variable \"#{token_var_name}\" with the personal access token to use."
end
unless username
username = UI.input "Please type in your GitHub username or cancel the release and define enviroment variable \"#{username_var_name}\" with your GitHub username."
end
# URL variables
repo_path = 'Brightify/Cuckoo'
base_url = "github.com/repos/#{repo_path}/releases"
api_url = "https://api.#{base_url}"
auth_string = "#{username}:#{access_token}"
changelog = create_changelog.gsub(/`/, '``').gsub(/'/, ''').gsub(/"/, '"').gsub(/\n/, '\\n')
version = version_bump_podspec(path: 'Cuckoo.podspec', bump_type: release_type)
# Make sure the changed podspec is valid.
pod_lib_lint(allow_warnings: true)
git_commit(path: './Cuckoo.podspec', message: "Bump version.")
add_git_tag(tag: version)
push_to_git_remote
# https://developer.github.com/v3/repos/releases/#create-a-release
release_title = "#{version}"
release_body = "#{changelog}"
creation_body = "'{\"tag_name\":\"#{version}\",\"target_commitish\":\"master\", \"name\":\"#{release_title}\", \"body\":\"#{release_body}\", \"draft\":false, \"prerelease\":false}'"
creation_response = JSON.parse(`curl -X POST -d #{creation_body} -u #{auth_string} #{api_url} -v`)
UI.crash! 'Release draft creation failed!' unless creation_response
upload_url = (creation_response['upload_url']).sub(/{.*name.*}/, '')
# https://developer.github.com/v3/repos/releases/#upload-a-release-asset
upload_response = JSON.parse(`curl -X POST --data-binary "@#{cuckoo_gen_path}" -u "#{auth_string}" "#{upload_url}?name=#{binary_name}" -H "Content-Type:application/octet-stream"`)
UI.crash! 'Release draft upload failed!' unless upload_response
pod_push(path: 'Cuckoo.podspec', allow_warnings: true)
UI.success "All done!\nYou can now visit #{creation_response['url']} (command+click) and release the thing."
end
def create_changelog
changelog = changelog_from_git_commits(pretty: "- %s", merge_commit_filtering: "exclude_merges")
if changelog
changelog.gsub(/.(?<=[^|\n]).*[B|b]ump.*\n?/, '')
else
'No new changes, sorry!'
end
end
after_all do
reset_git_repo(disregard_gitignore: false)
end
error do |_, exception|
reset_git_repo(disregard_gitignore: false)
UI.error "Release failed. This might help: #{exception}"
end
end

37
.gitignore vendored
View File

@ -1,4 +1,5 @@
# Xcode # Xcode
#
.build/ .build/
build/ build/
*.pbxuser *.pbxuser
@ -18,33 +19,19 @@ DerivedData
*.xcuserstate *.xcuserstate
# CocoaPods # CocoaPods
Pods #
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Pods/
# Carthage # Carthage
Carthage/Build #
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
.DS_Store .DS_Store
default.profraw default.profraw
Tests/**/Generated/*.swift Tests/Generated/*.swift
Generator/*.app
# AppCode
.idea/
cuckoo_generator
.fastlane
Generator/CuckooGenerator.xcodeproj
Cuckoo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Cuckoo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
Cuckoo.xcworkspace
Cuckoo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Tuist/Dependencies/graph.json
Tuist/Dependencies/Carthage
Tuist/Dependencies/SwiftPackageManager
Tuist/Dependencies/Cocoapods
Generator/Generator.xcodeproj
Generator/GeneratedMocks.swift

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "Generator/Dependencies/FileKit"]
path = Generator/Dependencies/FileKit
url = https://github.com/nvzqz/FileKit
[submodule "Generator/Dependencies/SourceKitten"]
path = Generator/Dependencies/SourceKitten
url = https://github.com/jpsim/SourceKitten

View File

@ -1,104 +0,0 @@
{
"pins" : [
{
"identity" : "commandant",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Carthage/Commandant.git",
"state" : {
"revision" : "07cad52573bad19d95844035bf0b25acddf6b0f6",
"version" : "0.15.0"
}
},
{
"identity" : "filekit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/nvzqz/FileKit.git",
"state" : {
"branch" : "develop",
"revision" : "6937ec38b0c383b0505caeea6603e62086bf5431"
}
},
{
"identity" : "nimble",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Quick/Nimble.git",
"state" : {
"revision" : "e9d769113660769a4d9dd3afb855562c0b7ae7b0",
"version" : "7.3.4"
}
},
{
"identity" : "pathkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/PathKit.git",
"state" : {
"revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
"version" : "1.0.1"
}
},
{
"identity" : "quick",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Quick/Quick.git",
"state" : {
"revision" : "f2b5a06440ea87eba1a167cab37bf6496646c52e",
"version" : "1.3.4"
}
},
{
"identity" : "result",
"kind" : "remoteSourceControl",
"location" : "https://github.com/antitypical/Result.git",
"state" : {
"revision" : "2ca499ba456795616fbc471561ff1d963e6ae160",
"version" : "4.1.0"
}
},
{
"identity" : "sourcekitten",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/SourceKitten.git",
"state" : {
"revision" : "79ca340f609adee48defa966e6a3dd0e0acbeb08",
"version" : "0.21.3"
}
},
{
"identity" : "spectre",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/Spectre.git",
"state" : {
"revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
"version" : "0.10.1"
}
},
{
"identity" : "stencil",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/Stencil.git",
"state" : {
"revision" : "ccd9402682f4c07dac9561befd207c8156e80e20",
"version" : "0.14.2"
}
},
{
"identity" : "swxmlhash",
"kind" : "remoteSourceControl",
"location" : "https://github.com/drmohundro/SWXMLHash.git",
"state" : {
"revision" : "f43166a8e18fdd0857f29e303b1bb79a5428bca0",
"version" : "4.9.0"
}
},
{
"identity" : "yams",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams.git",
"state" : {
"revision" : "b08dba4bcea978bf1ad37703a384097d3efce5af",
"version" : "1.0.2"
}
}
],
"version" : 2
}

1
.swift-version Normal file
View File

@ -0,0 +1 @@
3.0

View File

@ -1,15 +1,18 @@
language: objective-c language: objective-c
osx_image: xcode10.2 osx_image: xcode8
xcode_project: Cuckoo.xcodeproj xcode_project: Cuckoo.xcodeproj
xcode_scheme: Cuckoo xcode_scheme: Cuckoo
xcode_sdk: macosx xcode_sdk: macosx
before_script: before_script:
- pod install --repo-update - gem install cucumber --no-rdoc --no-ri
- gem install aruba --no-rdoc --no-ri
script: script:
- xcodebuild -workspace 'Cuckoo.xcworkspace' -scheme 'Cuckoo+OCMock-macOS' clean test | xcpretty - cd Generator
- pod lib lint --allow-warnings - bash run_tests.sh
- cd ..
- xcodebuild -workspace 'Cuckoo.xcworkspace' -scheme 'Cuckoo' clean test
after_script: after_script:
- sleep 5 - sleep 5

View File

@ -1 +0,0 @@
3.11.0

View File

@ -1,172 +1,30 @@
# Changelog # Changelog
## 1.4.1 ##
- Sidestep `SourceKit`'s off-by-one bug when parsing generic parameter inheritance.
- Fix incorrect `where` clause parsing.
## 1.4.0 * Support for Swift 3
- Add tvOS targets and schemes. * Added --no-class-mocking parameter to generator
- Integrate `tuist`, fixing Carthage issues. * Added Stub objects
- Fix type equality check to rule out whitespace inconsistencies.
## 1.3.2
- Fix `image not found` error for iOS 13 and beyond.
## 1.3.1
- **Swift Package Manager support**
- Added tvOS target (thanks @rodrigoff).
- Fixed accessibility to match enclosing container.
- Restructured README.
## 1.3.0
- Fix closure generation where explicit return type is required in `withoutActuallyEscaping` since swift 5.1 (XCode 11)
- Switch swift_version to 5.0 for Cocoapods
## 1.2.0
#### Features
- Objective-C mocking! Mock system classes/protocols as well as dynamic Swift classes. This is an optional subspec `Cuckoo/OCMock`.
- Convenience matchers for sequences and dictionaries. No need to use `equal(to:)` anymore, passing the `Array`/`Set`/`Dictionary` itself is enough from now on!
## 1.1.1
- Fix property, initializer, and function accessibility in public protocols.
## 1.1.0
#### Features:
- Add a simple type guesser based on assigned value.
#### Fixes:
- Fix accessibility problems.
## 1.0.6
- Fix a bug where adding a private name to a function made it generate twice and fail the compilation.
## 1.0.5
- Fix generic protocol generation and type erasure with multiple methods of same name.
- Fix a bug concerning empty public name methods with no private ones.
- Exit `run` script with error if it fails to get generator download URL.
## 1.0.4
- The `run` script doesn't use `realpath` command anymore because it's not available by default on Mac OS.
- Remove redundant stubbing of optional classes.
## 1.0.3
- Add support for optional read-only properties.
## 1.0.2
- Fix `where` clause that doesn't work in Swift 4.
- Fix some `run` script bugs.
## 1.0.1
- Fix `any()` not working anymore by itself with optional parameters in functions.
## 1.0.0
#### Features:
- **Generics** is now fully supported! This includes generic classes, protocols and methods.
- `Dictionary` matching out of the box.
- Better closure matching. Now allowing up to 7 parameter closures.
- `rethrows` functions now work properly.
- Allow non-optional values to be passed as matchers for `Optional`s just like in normal Swift code.
- Add support for inout method parameters.
#### Fixes:
- Update the `build_generator` script to work with Swift 5.
- Fix not being able to put `Optional` into functions accepting `Optional`s.
- Accessibility of variables and functions in `public` classes are now `public` as well.
## 0.13.0
- Updated for **Xcode 10.2** and **Swift 5**.
## 0.12.1
- Add class accessibility support.
- Add support for attributes (e.g. `@available`).
- Add support for subimport (e.g. `import struct UICat.Food`).
- Add `--clean` option to the run script to always build or download the generator (promptly forget to add its documentation to `README.md`).
- Ignore `final` classes (because we mock by inheritance).
- Smaller fixes and improvements in the whole project.
## 0.12.0
- Add first draft of a new Mock initialization DSL.
- Add `enableDefaultImplementation` to protocol `Mock`.
- Reintroduce support for pre-0.11.0 Cuckoo spies.
- Add **regular expression** `class` and `protocol` matching.
- Add **glob** switch that parses input paths as globs enabling for easier project scaling.
- Build generator by default. Download using `--download [VERSION]` option.
- Modify the `run` bash script to allow the user to build rather than download the `cuckoo_generator`.
- Add a debug flag that generates general info above methods when used.
- Fix escaping closure (crashing in Xcode 10).
## 0.11.0
- Added contribution guide.
- **BREAKING CHANGE**: Spies were reworked. They now use superclasses as their victims if enabled. Please see the Readme for more information.
- **BREAKING CHANGE**: Verification of properties' `get` is now a method you have to call, instead of a property. This change was made to remove the "unused result" warning. [bug #141](https://github.com/Brightify/Cuckoo/issues/141)
## 0.10.2
- Double the maximum parameters in `call` and `callThrows` methods. [bug #145](https://github.com/Brightify/Cuckoo/issues/145)
- Make the generator deterministic by sorting input files.
- [bug #157](https://github.com/Brightify/Cuckoo/issues/157)
- [PR #158 - kudos to IanKeen](https://github.com/Brightify/Cuckoo/pull/158)
- Add `equalTo` for `Array` and `Set` where `Element` is `Equatable`.
## 0.10.1
- Fixed some errors with getters [bug #151](https://github.com/Brightify/Cuckoo/issues/151)
## 0.10.0
- Updated for **Swift 4** (Xcode 9 GM)
## 0.9.2
- Fixed crash when source files were using non-ASCII characters - [bug #126](https://github.com/Brightify/Cuckoo/issues/126)
- Added `--exclude` parameter to explicitly exclude some types from mocking - [PR #112](https://github.com/Brightify/Cuckoo/pull/112) - (thanks to nxtstep for the feature)
- Fixed compile errors when generating stubs where inner types were returned - [bug #118](https://github.com/Brightify/Cuckoo/issues/118)
- Added possibility to reset multiple mocks with different types at once - [but #103](https://github.com/Brightify/Cuckoo/issues/103)
## 0.9.1
- Fixed "too complex to resolve in reasonable time" error in generator
- Fixed directory names for case sensitive file systems - [PR #114](https://github.com/Brightify/Cuckoo/pull/115) - (thanks to sundance2000 for the fix)
- Moved repository from `SwiftKit` to `Brightify` organization on GitHub.
## 0.9.0
- Rewritten Generator to use Stencil
- Use Swift PM for generator binary (results in faster builds)
- This release works with Swift 3.1
## 0.8.4
- Added support for inheritance mocking.
## 0.8.3
- Added support for `fileprivate` (thanks to lvdstam for implementation).
- Added support for default values (thanks to lvdstam for implementation).
- Fixed wrongly generated code where public class had internal members.
## 0.8.2
- Show error in generator in build log.
- Fixed crash of generator when instance variable type is not explicitly set.
- Fixed support of closures and unwrapped optionals.
## 0.8.1
- Set "Reflection Metadata" to "None" to fix #72
## 0.8.0
- Support for **Swift 3**
- Added --no-class-mocking parameter to generator
- Added Stub objects
## 0.7.0 ## 0.7.0
- Updated documentation
- Added more automated tests * Updated documentation
- Added --file-prefix parameter to generator * Added more automated tests
- `and` and `or` methods can now be used with `Matchable` (literals) * Added --file-prefix parameter to generator
- Using of custom `Matchable`, `ParameterMatcher` and `CallMatcher` is now easier * `and` and `or` methods can now be used with `Matchable` (literals)
- Improved fail messages * Using of custom `Matchable`, `ParameterMatcher` and `CallMatcher` is now easier
- Merged generator and runtime repositories, making updating easier. * Improved fail messages
- Added support for named arguments in methods. * Merged generator and runtime repositories, making updating easier.
- Added support for classes with custom initializers. * Added support for named arguments in methods.
- Changed usage of spies. Instead of `init(spyOn:)` use `init().spy(on:)`. * Added support for classes with custom initializers.
* Changed usage of spies. Instead of `init(spyOn:)` use `init().spy(on:)`.
## 0.6.0 ## 0.6.0
- Added release notes
- Added stub resetting * Added release notes
- Added `thenCallRealImplementation` * Added stub resetting
- Added argument capturing * Added `thenCallRealImplementation`
- Added `verifyNoMoreInteractions` * Added argument capturing
- Added on going stubbing * Added `verifyNoMoreInteractions`
- Added `thenDoNothing` * Added on going stubbing
* Added `thenDoNothing`

View File

@ -1,54 +1,31 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Cuckoo" s.name = "Cuckoo"
s.version = "1.10.3" s.version = "0.7.0"
s.summary = "Cuckoo - first boilerplate-free Swift mocking framework." s.summary = "Cuckoo - first boilerplate-free Swift mocking framework."
s.description = <<-DESC s.description = <<-DESC
Cuckoo is a mocking framework with an easy to use API (inspired by Mockito). Cuckoo is a mocking framework with an easy to use API (inspired by Mockito).
It generates mocks and some helper structures automatically to enable this functionality. It generates mocks and some helper structures automatically to enable this functionality.
DESC DESC
s.homepage = "https://github.com/Brightify/Cuckoo" s.homepage = "https://github.com/SwiftKit/Cuckoo"
s.license = 'MIT' s.license = 'MIT'
s.author = { "Tadeas Kriz" => "tadeas@brightify.org", "Filip Dolnik" => "filip@brightify.org", "Adriaan (Arjan) Duijzer" => "arjan@nxtstep.nl" } s.author = { "Tadeas Kriz" => "tadeas@brightify.org", "Filip Dolnik" => "filip@brightify.org" }
s.source = { s.source = {
:git => "https://github.com/Brightify/Cuckoo.git", :git => "https://github.com/SwiftKit/Cuckoo.git",
:tag => s.version.to_s :tag => s.version.to_s
} }
s.ios.deployment_target = '9.0' s.ios.deployment_target = '8.0'
s.osx.deployment_target = '11.0' s.osx.deployment_target = '10.9'
#s.watchos.deployment_target = '2.0' # watchos does not include XCTest framework :( #s.watchos.deployment_target = '2.0' # watchos does not include XCTest framework :(
s.tvos.deployment_target = '9.0' s.tvos.deployment_target = '9.0'
generator_name = 'cuckoo_generator' s.source_files = ['Source/**/*.swift']
s.swift_version = '5.0' s.preserve_paths = ['Generator/**/*', 'run', 'build_generator']
s.preserve_paths = ['Generator/**/*', 'run', 'build_generator', generator_name]
s.prepare_command = <<-CMD s.prepare_command = <<-CMD
curl -Lo #{generator_name} https://github.com/Brightify/Cuckoo/releases/download/#{s.version}/#{generator_name} git submodule update --init --recursive
chmod +x #{generator_name} ./build_generator
CMD CMD
s.frameworks = 'XCTest', 'Foundation' s.frameworks = 'XCTest', 'Foundation'
s.requires_arc = true s.requires_arc = true
s.pod_target_xcconfig = { s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO' }
'ENABLE_BITCODE' => 'NO',
'SWIFT_REFLECTION_METADATA_LEVEL' => 'none',
'OTHER_LDFLAGS' => '$(inherited) -weak-lXCTestSwiftSupport',
}
s.default_subspec = 'Swift'
# When building for iOS versions 12 and lower in Xcode 11, a wild `image not found` appears.
# This is a workaround for that annoying error.
s.xcconfig = (8..12).map { |major| (0..5).map { |minor| [major, minor] } }.flatten(1).inject(Hash.new) do |hash, (major, minor)|
hash["LIBRARY_SEARCH_PATHS[sdk=iphoneos#{major}.#{minor}]"] = '$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift-$(SWIFT_VERSION)/$(PLATFORM_NAME)'
hash
end
s.subspec 'Swift' do |sub|
sub.source_files = 'Source/**/*.swift'
end
s.subspec 'OCMock' do |sub|
sub.source_files = 'OCMock/**/*.{h,m,swift}'
sub.dependency 'Cuckoo/Swift'
sub.dependency 'OCMock', '3.8.1'
end
end end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:/Users/TadeasKriz/Development/Sources/Mockery/Cuckoo.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,72 @@
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "509FD5E949BB67AB92770169324D45AB1917F796",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : 0,
"956D2B21DD155C49504BB67697A67F7C5351A353" : 0,
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : 0,
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : 0,
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : 0,
"EB2210CFD48672E403BED699D5D7F01B844069CF" : 0,
"E084C86B03F81D63323C9E7510697EA528A758C7" : 0,
"509FD5E949BB67AB92770169324D45AB1917F796" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "4F02CBB0-D238-438F-87C7-CFA61AC3F0B4",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/YamlSwift\/",
"956D2B21DD155C49504BB67697A67F7C5351A353" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/Result\/",
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/",
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : "Cuckoo\/Generator\/Dependencies\/FileKit\/",
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/",
"EB2210CFD48672E403BED699D5D7F01B844069CF" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/SWXMLHash\/",
"E084C86B03F81D63323C9E7510697EA528A758C7" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/xcconfigs\/",
"509FD5E949BB67AB92770169324D45AB1917F796" : "Cuckoo\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "Cuckoo",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Cuckoo.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/SwiftKit\/Cuckoo.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "509FD5E949BB67AB92770169324D45AB1917F796"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/nvzqz\/FileKit",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "64ACE19A884E8C30BC53E2E0CE86010ECED70B5A"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/antitypical\/Result.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "956D2B21DD155C49504BB67697A67F7C5351A353"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Carthage\/Commandant.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D23C0CEAADB77074FDA4459000068A7940EB7AD0"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/jspahrsummers\/xcconfigs.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E084C86B03F81D63323C9E7510697EA528A758C7"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/behrang\/YamlSwift.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E111B14063EC98ABE518A93E1C4498F4167C2DEE"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/drmohundro\/SWXMLHash.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EB2210CFD48672E403BED699D5D7F01B844069CF"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/jpsim\/SourceKitten",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "FD7DA18210A2C280E9107E37D7344F243FEE5F75"
}
]
}

View File

@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC1E5664C17BDCFB45CF1DF4"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-tvOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6AD4A9670FA783A1EC213000"
BuildableName = "Cuckoo_tvOSTests.xctest"
BlueprintName = "Cuckoo-tvOSTests"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC1E5664C17BDCFB45CF1DF4"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-tvOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC1E5664C17BDCFB45CF1DF4"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-tvOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,11 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1010" LastUpgradeVersion = "0800"
version = "1.3"> version = "1.7">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "NO"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1808B15F1C4575AA0036E924"
BuildableName = "cuckoo_generator.app"
BlueprintName = "CuckooGenerator"
ReferencedContainer = "container:Generator/CuckooGenerator.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"
@ -14,9 +28,9 @@
buildForAnalyzing = "YES"> buildForAnalyzing = "YES">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1" BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
BuildableName = "Cuckoo.framework" BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS" BlueprintName = "Cuckoo"
ReferencedContainer = "container:Cuckoo.xcodeproj"> ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
@ -26,19 +40,35 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
skipped = "NO"> skipped = "NO">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "DBDB2DD4E7BE487EEB257CC8" BlueprintIdentifier = "183D04041C4691C600EBAEF3"
BuildableName = "Cuckoo_macOSTests.xctest" BuildableName = "CuckooTests.xctest"
BlueprintName = "Cuckoo-macOSTests" BlueprintName = "CuckooTests"
ReferencedContainer = "container:Cuckoo.xcodeproj"> ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference> </BuildableReference>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</TestableReference> </TestableReference>
</Testables> </Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
@ -53,12 +83,14 @@
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1" BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
BuildableName = "Cuckoo.framework" BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS" BlueprintName = "Cuckoo"
ReferencedContainer = "container:Cuckoo.xcodeproj"> ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"
@ -69,9 +101,9 @@
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1" BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
BuildableName = "Cuckoo.framework" BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS" BlueprintName = "Cuckoo"
ReferencedContainer = "container:Cuckoo.xcodeproj"> ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Cuckoo.xcodeproj">
</FileRef>
<FileRef
location = "group:Generator/CuckooGenerator.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,72 @@
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "509FD5E949BB67AB92770169324D45AB1917F796",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : 0,
"956D2B21DD155C49504BB67697A67F7C5351A353" : 0,
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : 0,
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : 0,
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : 0,
"EB2210CFD48672E403BED699D5D7F01B844069CF" : 0,
"E084C86B03F81D63323C9E7510697EA528A758C7" : 0,
"509FD5E949BB67AB92770169324D45AB1917F796" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "AA22B945-78BB-4BFD-8BCA-01658017BF31",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/YamlSwift\/",
"956D2B21DD155C49504BB67697A67F7C5351A353" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/Result\/",
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/",
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : "Cuckoo\/Generator\/Dependencies\/FileKit\/",
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/",
"EB2210CFD48672E403BED699D5D7F01B844069CF" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/SWXMLHash\/",
"E084C86B03F81D63323C9E7510697EA528A758C7" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/xcconfigs\/",
"509FD5E949BB67AB92770169324D45AB1917F796" : "Cuckoo\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "Cuckoo",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Cuckoo.xcworkspace",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/SwiftKit\/Cuckoo.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "509FD5E949BB67AB92770169324D45AB1917F796"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/nvzqz\/FileKit",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "64ACE19A884E8C30BC53E2E0CE86010ECED70B5A"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/antitypical\/Result.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "956D2B21DD155C49504BB67697A67F7C5351A353"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Carthage\/Commandant.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D23C0CEAADB77074FDA4459000068A7940EB7AD0"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/jspahrsummers\/xcconfigs.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E084C86B03F81D63323C9E7510697EA528A758C7"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/behrang\/YamlSwift.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E111B14063EC98ABE518A93E1C4498F4167C2DEE"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/drmohundro\/SWXMLHash.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EB2210CFD48672E403BED699D5D7F01B844069CF"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/jpsim\/SourceKitten",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "FD7DA18210A2C280E9107E37D7344F243FEE5F75"
}
]
}

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
</dict>
</plist>

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,6 +0,0 @@
# frozen_string_literal: true
source "https://rubygems.org"
gem 'json'
gem 'fastlane'
gem 'cocoapods'

View File

@ -1,285 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.5)
rexml
activesupport (6.1.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.600.0)
aws-sdk-core (3.131.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.57.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
cocoapods (1.11.2)
addressable (~> 2.8)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.11.2)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.8.0)
nap (~> 1.0)
ruby-macho (>= 1.0, < 3.0)
xcodeproj (>= 1.21.0, < 2.0)
cocoapods-core (1.11.2)
activesupport (>= 5.0, < 7)
addressable (~> 2.8)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (1.5.1)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.1)
cocoapods-trunk (1.6.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
concurrent-ruby (1.1.9)
declarative (0.0.20)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.3)
escape (0.0.4)
ethon (0.15.0)
ffi (>= 1.15.0)
excon (0.92.3)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.206.2)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
ffi (1.15.4)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.22.0)
google-apis-core (>= 0.5, < 2.a)
google-apis-core (0.6.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.12.0)
google-apis-core (>= 0.6, < 2.a)
google-apis-playcustomapp_v1 (0.9.0)
google-apis-core (>= 0.6, < 2.a)
google-apis-storage_v1 (0.15.0)
google-apis-core (>= 0.5, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.36.2)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.1.3)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (1.8.11)
concurrent-ruby (~> 1.0)
jmespath (1.6.1)
json (2.6.2)
jwt (2.4.1)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.2)
minitest (5.14.4)
molinillo (0.8.0)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
optparse (0.1.1)
os (1.1.4)
plist (3.6.0)
public_suffix (4.0.7)
rake (13.0.6)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.16.1)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
CFPropertyList
naturally
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
zeitwerk (2.5.1)
PLATFORMS
arm64-darwin-21
DEPENDENCIES
cocoapods
fastlane
json
BUNDLED WITH
2.2.32

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,86 @@
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "8C7D0042C66E869BF59148B34297150D4FBCD754",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64" : 0,
"956D2B21DD155C49504BB67697A67F7C5351A353" : 0,
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : 0,
"8C7D0042C66E869BF59148B34297150D4FBCD754" : 0,
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : 0,
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : 0,
"95438028B10BBB846574013D29F154A00556A9D1" : 0,
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : 0,
"EB2210CFD48672E403BED699D5D7F01B844069CF" : 0,
"E084C86B03F81D63323C9E7510697EA528A758C7" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "89C40CF6-2BE4-4290-9B22-6C1C16D03073",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64" : "CuckooGenerator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/Quick\/",
"956D2B21DD155C49504BB67697A67F7C5351A353" : "CuckooGenerator\/Dependencies\/Commandant\/Carthage\/Checkouts\/Result\/",
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : "CuckooGenerator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/YamlSwift\/",
"8C7D0042C66E869BF59148B34297150D4FBCD754" : "CuckooGenerator\/",
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : "CuckooGenerator\/Dependencies\/Commandant\/",
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : "CuckooGenerator\/Dependencies\/FileKit\/",
"95438028B10BBB846574013D29F154A00556A9D1" : "CuckooGenerator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/Nimble\/",
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : "CuckooGenerator\/Dependencies\/SourceKitten\/",
"EB2210CFD48672E403BED699D5D7F01B844069CF" : "CuckooGenerator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/SWXMLHash\/",
"E084C86B03F81D63323C9E7510697EA528A758C7" : "CuckooGenerator\/Dependencies\/Commandant\/Carthage\/Checkouts\/xcconfigs\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "CuckooGenerator",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CuckooGenerator.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/nvzqz\/FileKit",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "64ACE19A884E8C30BC53E2E0CE86010ECED70B5A"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/SwiftKit\/CuckooGenerator.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8C7D0042C66E869BF59148B34297150D4FBCD754"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Quick\/Nimble.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "95438028B10BBB846574013D29F154A00556A9D1"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/antitypical\/Result.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "956D2B21DD155C49504BB67697A67F7C5351A353"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Quick\/Quick.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Carthage\/Commandant",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D23C0CEAADB77074FDA4459000068A7940EB7AD0"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/jspahrsummers\/xcconfigs.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E084C86B03F81D63323C9E7510697EA528A758C7"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/behrang\/YamlSwift.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E111B14063EC98ABE518A93E1C4498F4167C2DEE"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/drmohundro\/SWXMLHash.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EB2210CFD48672E403BED699D5D7F01B844069CF"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/jpsim\/SourceKitten",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "FD7DA18210A2C280E9107E37D7344F243FEE5F75"
}
]
}

View File

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5204B84A1B96B83800AA473F"
BuildableName = "FileKit.framework"
BlueprintName = "FileKit-OSX"
ReferencedContainer = "container:Dependencies/FileKit/FileKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CD9D052B1A757D8B003CCB21"
BuildableName = "SWXMLHash.framework"
BlueprintName = "SWXMLHash OSX"
ReferencedContainer = "container:Dependencies/SourceKitten/Carthage/Checkouts/SWXMLHash/SWXMLHash.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D45480561A9572F5009D7229"
BuildableName = "Result.framework"
BlueprintName = "Result-Mac"
ReferencedContainer = "container:Dependencies/SourceKitten/Carthage/Checkouts/Commandant/Carthage/Checkouts/Result/Result.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D00CCDD81A20717400109F8C"
BuildableName = "Commandant.framework"
BlueprintName = "Commandant"
ReferencedContainer = "container:Dependencies/SourceKitten/Carthage/Checkouts/Commandant/Commandant.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8E1D76201B258FEE0022C013"
BuildableName = "Yaml.framework"
BlueprintName = "Yaml OSX"
ReferencedContainer = "container:Dependencies/SourceKitten/Carthage/Checkouts/YamlSwift/Yaml.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0D1216C19E87B05005E4BAA"
BuildableName = "SourceKittenFramework.framework"
BlueprintName = "SourceKittenFramework"
ReferencedContainer = "container:Dependencies/SourceKitten/sourcekitten.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1808B15F1C4575AA0036E924"
BuildableName = "cuckoo_generator.app"
BlueprintName = "CuckooGenerator"
ReferencedContainer = "container:CuckooGenerator.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1808B15F1C4575AA0036E924"
BuildableName = "cuckoo_generator.app"
BlueprintName = "CuckooGenerator"
ReferencedContainer = "container:CuckooGenerator.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "NO"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1808B15F1C4575AA0036E924"
BuildableName = "cuckoo_generator.app"
BlueprintName = "CuckooGenerator"
ReferencedContainer = "container:CuckooGenerator.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1808B15F1C4575AA0036E924"
BuildableName = "cuckoo_generator.app"
BlueprintName = "CuckooGenerator"
ReferencedContainer = "container:CuckooGenerator.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1010" LastUpgradeVersion = "0800"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@ -14,10 +14,10 @@
buildForAnalyzing = "YES"> buildForAnalyzing = "YES">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "6A8F12C3A778933132EBA10D" BlueprintIdentifier = "18E16D651C45D8280084EF54"
BuildableName = "Cuckoo.framework" BuildableName = "CuckooGeneratorFramework.framework"
BlueprintName = "Cuckoo-iOS" BlueprintName = "CuckooGeneratorFramework"
ReferencedContainer = "container:Cuckoo.xcodeproj"> ReferencedContainer = "container:CuckooGenerator.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
</BuildActionEntries> </BuildActionEntries>
@ -28,17 +28,9 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "92C74015545203C1F4395DD4"
BuildableName = "Cuckoo_iOSTests.xctest"
BlueprintName = "Cuckoo-iOSTests"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables> </Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
@ -53,12 +45,14 @@
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "6A8F12C3A778933132EBA10D" BlueprintIdentifier = "18E16D651C45D8280084EF54"
BuildableName = "Cuckoo.framework" BuildableName = "CuckooGeneratorFramework.framework"
BlueprintName = "Cuckoo-iOS" BlueprintName = "CuckooGeneratorFramework"
ReferencedContainer = "container:Cuckoo.xcodeproj"> ReferencedContainer = "container:CuckooGenerator.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"
@ -69,10 +63,10 @@
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "6A8F12C3A778933132EBA10D" BlueprintIdentifier = "18E16D651C45D8280084EF54"
BuildableName = "Cuckoo.framework" BuildableName = "CuckooGeneratorFramework.framework"
BlueprintName = "Cuckoo-iOS" BlueprintName = "CuckooGeneratorFramework"
ReferencedContainer = "container:Cuckoo.xcodeproj"> ReferencedContainer = "container:CuckooGenerator.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
</ProfileAction> </ProfileAction>

@ -0,0 +1 @@
Subproject commit 4d928bc99cc075b9d6c0c4293ba472be4b7eed02

@ -0,0 +1 @@
Subproject commit 5bc7cc9361546a611a32fff32238fe25fed16f09

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
</dict>
</plist>

View File

@ -1,56 +0,0 @@
import ProjectDescription
import ProjectDescriptionHelpers
// MARK: project definition
let project = Project(
name: "Generator",
options: .options(automaticSchemesOptions: .disabled, disableSynthesizedResourceAccessors: true),
packages: [
.package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.21.2")),
.package(url: "https://github.com/nvzqz/FileKit.git", .branch("develop")),
.package(url: "https://github.com/kylef/Stencil.git", .exact("0.14.2")),
.package(url: "https://github.com/Carthage/Commandant.git", .exact("0.15.0")),
],
targets: [
Target(
name: "CuckooGenerator",
platform: .macOS,
product: .commandLineTool,
productName: "cuckoo_generator",
bundleId: "CuckooGenerator",
deploymentTarget: DeploymentTarget.macOS(targetVersion: "10.13"),
sources: "Source/**",
dependencies: ["FileKit", "SourceKittenFramework", "Stencil", "Commandant"].map(TargetDependency.package(product:))
),
],
schemes: [
Scheme(
name: "Generator",
buildAction: BuildAction.buildAction(
targets: ["CuckooGenerator"],
postActions: [
ExecutionAction(
title: "Copy executable",
scriptText: #"\cp "$BUILT_PRODUCTS_DIR/$EXECUTABLE_NAME" "$PROJECT_DIR/bin/cuckoo_generator""#,
target: "CuckooGenerator"
)
],
runPostActionsOnFailure: false
),
runAction: RunAction.runAction(
executable: "CuckooGenerator",
arguments: Arguments(
launchArguments: [
// Any changes here should be reflected in `../Project.swift` as well.
LaunchArgument(name: "generate", isEnabled: true),
LaunchArgument(name: "--testable Cuckoo", isEnabled: true),
LaunchArgument(name: "--exclude ExcludedTestClass,ExcludedProtocol", isEnabled: true),
LaunchArgument(name: #"--output "$PROJECT_DIR"/GeneratedMocks.swift"#, isEnabled: true),
// This is the input file(s) glob.
LaunchArgument(name: #"--glob "$PROJECT_DIR"/../Tests/Swift/Source/*.swift"#, isEnabled: true),
]
)
)
),
]
)

View File

@ -1,242 +0,0 @@
import Foundation
import Commandant
import Result
import SourceKittenFramework
import FileKit
private func curry<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R>
(_ f: @escaping (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R)
-> (P1) -> (P2) -> (P3) -> (P4) -> (P5) -> (P6) -> (P7) -> (P8) -> (P9) -> (P10) -> (P11) -> (P12) -> R {
return { p1 in { p2 in { p3 in { p4 in { p5 in { p6 in { p7 in { p8 in { p9 in { p10 in { p11 in { p12 in
f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12)
} } } } } } } } } } } }
}
public struct GenerateMocksCommand: CommandProtocol {
public let verb = "generate"
public let function = "Generates mock files"
public func run(_ options: Options) -> Result<Void, CuckooGeneratorError> {
let getFullPathSortedArray: ([String]) -> [String] = { stringArray in
Array(Set(stringArray.map { Path($0).standardRawValue })).sorted()
}
let inputPathValues: [String]
if options.globEnabled {
inputPathValues = getFullPathSortedArray(options.files.flatMap { Glob(pattern: $0).paths })
} else {
inputPathValues = getFullPathSortedArray(options.files)
}
let inputFiles = inputPathValues.map { File(path: $0) }.compactMap { $0 }
let tokens = inputFiles.map { Tokenizer(sourceFile: $0, debugMode: options.debugMode).tokenize() }
let tokensWithInheritance = options.noInheritance ? tokens : inheritNSObject(mergeInheritance(tokens))
// filter classes/protocols based on the settings passed to the generator
var typeFilters = [] as [(Token) -> Bool]
if options.noClassMocking {
typeFilters.append(ignoreClasses)
}
if !options.regex.isEmpty {
typeFilters.append(keepMatching(pattern: options.regex))
}
if !options.exclude.isEmpty {
typeFilters.append(ignoreIfExists(in: options.exclude))
}
let parsedFiles = removeTypes(from: tokensWithInheritance, using: typeFilters)
// generating headers and mocks
let headers = parsedFiles.map { options.noHeader ? "" : FileHeaderHandler.getHeader(of: $0, includeTimestamp: !options.noTimestamp) }
let imports = parsedFiles.map { FileHeaderHandler.getImports(of: $0, testableFrameworks: options.testableFrameworks) }
let mocks = parsedFiles.map { try! Generator(file: $0).generate(debug: options.debugMode) }
let mergedFiles = zip(zip(headers, imports), mocks).map { $0.0 + $0.1 + $1 }
let outputPath = Path(options.output)
do {
if outputPath.isDirectory {
let inputPaths = inputFiles.compactMap { $0.path }.map { Path($0) }
for (inputPath, outputText) in zip(inputPaths, mergedFiles) {
let fileName = options.filePrefix + inputPath.fileName
let outputFile = TextFile(path: outputPath + fileName)
try outputText |> outputFile
}
} else {
let outputFile = TextFile(path: outputPath)
try mergedFiles.joined(separator: "\n") |> outputFile
}
} catch let error as FileKitError {
return .failure(.ioError(error))
} catch let error {
return .failure(.unknownError(error))
}
return stderrUsed ? .failure(.stderrUsed) : .success(())
}
private func mergeInheritance(_ filesRepresentation: [FileRepresentation]) -> [FileRepresentation] {
return filesRepresentation.compactMap { $0.mergeInheritance(with: filesRepresentation) }
}
private func inheritNSObject(_ filesRepresentation: [FileRepresentation]) -> [FileRepresentation] {
func containsRecursively(name: String) -> Bool {
if let protocolDeclaration = protocolDeclarationDictionary[name] {
let collapsedInheritedTypesName = protocolDeclaration
.inheritedTypes
.map({ $0.name.components(separatedBy: "&") })
.joined()
.map({ $0.trimmingCharacters(in: .whitespaces) })
if collapsedInheritedTypesName.contains(where: { $0 == "NSObjectProtocol" }) {
return true
} else {
return protocolDeclaration.inheritedTypes.contains(where: { inheritanceType in
containsRecursively(name: inheritanceType.name)
})
}
} else {
return false
}
}
let protocolDeclarationDictionary: [String: ProtocolDeclaration] = Dictionary(
uniqueKeysWithValues: filesRepresentation.flatMap { file in
file.declarations.compactMap { token -> (name: String, protocolDeclaration: ProtocolDeclaration)? in
guard let protocolDeclaration = token as? ProtocolDeclaration else { return nil }
return (name: protocolDeclaration.name, protocolDeclaration: protocolDeclaration)
}
}
)
let nsObjectProtocols: [ProtocolDeclaration] = protocolDeclarationDictionary.values.reduce(into: []) { accumulator, protocolDeclaration in
guard containsRecursively(name: protocolDeclaration.name) else { return }
accumulator.append(protocolDeclaration)
}
return filesRepresentation.map { $0.inheritNSObject(subjects: nsObjectProtocols) }
}
private func removeTypes(from files: [FileRepresentation], using filters: [(Token) -> Bool]) -> [FileRepresentation] {
// Only keep those that pass all filters
let filter: (Token) -> Bool = { token in
!filters.contains { !$0(token) }
}
return files.compactMap { file in
let filteredDeclarations = file.declarations.filter(filter)
guard !filteredDeclarations.isEmpty else { return nil }
return FileRepresentation(sourceFile: file.sourceFile, declarations: filteredDeclarations)
}
}
// filter that keeps the protocols while removing all classes
private func ignoreClasses(token: Token) -> Bool {
return !(token is ClassDeclaration)
}
// filter that keeps the classes/protocols that match the passed regular expression
private func keepMatching(pattern: String) -> (Token) -> Bool {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [])
return { token in
guard let containerToken = token as? ContainerToken else { return true }
return regex.firstMatch(in: containerToken.name, options: [], range: NSMakeRange(0, containerToken.name.count)) != nil
}
} catch {
fatalError("Invalid regular expression: " + (error as NSError).description)
}
}
// filter that keeps only the classes/protocols that are not supposed to be excluded
private func ignoreIfExists(in excluded: [String]) -> (Token) -> Bool {
let excludedSet = Set(excluded)
return { token in
guard let containerToken = token as? ContainerToken else { return true }
return !excludedSet.contains(containerToken.name)
}
}
public struct Options: OptionsProtocol {
let files: [String]
let output: String
let noHeader: Bool
let noTimestamp: Bool
let noInheritance: Bool
let testableFrameworks: [String]
let exclude: [String]
let filePrefix: String
let noClassMocking: Bool
let debugMode: Bool
let globEnabled: Bool
let regex: String
public init(
output: String,
testableFrameworks: String,
exclude: String,
noHeader: Bool,
noTimestamp: Bool,
noInheritance: Bool,
filePrefix: String,
noClassMocking: Bool,
debugMode: Bool,
globEnabled: Bool,
regex: String,
files: [String]
) {
self.output = output
self.testableFrameworks = testableFrameworks.components(separatedBy: ",").filter { !$0.isEmpty }
self.exclude = exclude.components(separatedBy: ",").filter { !$0.isEmpty }.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
self.noHeader = noHeader
self.noTimestamp = noTimestamp
self.noInheritance = noInheritance
self.filePrefix = filePrefix
self.noClassMocking = noClassMocking
self.debugMode = debugMode
self.globEnabled = globEnabled
self.regex = regex
self.files = files
}
// all options are declared here and then parsed by Commandant
public static func evaluate(_ m: CommandMode) -> Result<Options, CommandantError<CuckooGeneratorError>> {
let output: Result<String, CommandantError<ClientError>> = m <| Option(key: "output", defaultValue: "GeneratedMocks.swift", usage: "Where to put the generated mocks.\nIf a path to a directory is supplied, each input file will have a respective output file with mocks.\nIf a path to a Swift file is supplied, all mocks will be in a single file.\nDefault value is `GeneratedMocks.swift`.")
let testable: Result<String, CommandantError<ClientError>> = m <| Option(key: "testable", defaultValue: "", usage: "A comma separated list of frameworks that should be imported as @testable in the mock files.")
let exclude: Result<String, CommandantError<ClientError>> = m <| Option(key: "exclude", defaultValue: "", usage: "A comma separated list of classes and protocols that should be skipped during mock generation.")
let noHeader: Result<Bool, CommandantError<ClientError>> = m <| Option(key: "no-header", defaultValue: false, usage: "Do not generate file headers.")
let noTimestamp: Result<Bool, CommandantError<ClientError>> = m <| Option(key: "no-timestamp", defaultValue: false, usage: "Do not generate timestamp.")
let noInheritance: Result<Bool, CommandantError<ClientError>> = m <| Option(key: "no-inheritance", defaultValue: false, usage: "Do not generate stubs/mock for super class/protocol even if available.")
let filePrefix: Result<String, CommandantError<ClientError>> = m <| Option(key: "file-prefix", defaultValue: "", usage: "Names of generated files in directory will start with this prefix. Only works when output path is directory.")
let noClassMocking: Result<Bool, CommandantError<ClientError>> = m <| Option(key: "no-class-mocking", defaultValue: false, usage: "Do not generate mocks for classes.")
let debugMode: Result<Bool, CommandantError<ClientError>> = m <| Switch(flag: "d", key: "debug", usage: "Run generator in debug mode.")
let globEnabled: Result<Bool, CommandantError<ClientError>> = m <| Switch(flag: "g", key: "glob", usage: "Use glob for specifying input paths.")
let regex: Result<String, CommandantError<ClientError>> = m <| Option(key: "regex", defaultValue: "", usage: "A regular expression pattern that is used to match Classes and Protocols.\nAll that do not match are excluded.\nCan be used alongside `--exclude`.")
let input: Result<[String], CommandantError<ClientError>> = m <| Argument(usage: "Files to parse and generate mocks for.")
return curry(Options.init)
<*> output
<*> testable
<*> exclude
<*> noHeader
<*> noTimestamp
<*> noInheritance
<*> filePrefix
<*> noClassMocking
<*> debugMode
<*> globEnabled
<*> regex
<*> input
}
}
}

View File

@ -1,178 +0,0 @@
import Foundation
// Adapted from https://gist.github.com/blakemerryman/76312e1cbf8aec248167
/**
Finds files on the file system using pattern matching.
*/
class Glob: Collection {
/**
* Different glob implementations have different behaviors, so the behavior of this
* implementation is customizable.
*/
struct Behavior {
// If true then a globstar ("**") causes matching to be done recursively in subdirectories.
// If false then "**" is treated the same as "*"
let supportsGlobstar: Bool
// If true the results from the directory where the globstar is declared will be included as well.
// For example, with the pattern "dir/**/*.ext" the fie "dir/file.ext" would be included if this
// property is true, and would be omitted if it's false.
let includesFilesFromRootOfGlobstar: Bool
// If false then the results will not include directory entries. This does not affect recursion depth.
let includesDirectoriesInResults: Bool
// If false and the last characters of the pattern are "**/" then only directories are returned in the results.
let includesFilesInResultsIfTrailingSlash: Bool
}
static var defaultBehavior = Glob.Behavior(
supportsGlobstar: true,
includesFilesFromRootOfGlobstar: true,
includesDirectoriesInResults: false,
includesFilesInResultsIfTrailingSlash: false
)
private var isDirectoryCache = [String: Bool]()
let behavior: Behavior
var paths = [String]()
var startIndex: Int { return paths.startIndex }
var endIndex: Int { return paths.endIndex }
init(pattern: String, behavior: Behavior = Glob.defaultBehavior) {
self.behavior = behavior
var adjustedPattern = pattern
let hasTrailingGlobstarSlash = pattern.hasSuffix("**/")
var includeFiles = !hasTrailingGlobstarSlash
if behavior.includesFilesInResultsIfTrailingSlash {
includeFiles = true
if hasTrailingGlobstarSlash {
// Grab the files too.
adjustedPattern += "*"
}
}
let patterns = behavior.supportsGlobstar ? expandGlobstar(pattern: adjustedPattern) : [adjustedPattern]
for pattern in patterns {
var gt = glob_t()
if executeGlob(pattern: pattern, gt: &gt) {
populateFiles(gt: gt, includeFiles: includeFiles)
}
globfree(&gt)
}
paths = Array(Set(paths)).sorted { lhs, rhs in
lhs.compare(rhs) != ComparisonResult.orderedDescending
}
clearCaches()
}
// MARK: Private
private var globalFlags = GLOB_TILDE | GLOB_BRACE | GLOB_MARK
private func executeGlob(pattern: UnsafePointer<CChar>, gt: UnsafeMutablePointer<glob_t>) -> Bool {
return 0 == glob(pattern, globalFlags, nil, gt)
}
private func expandGlobstar(pattern: String) -> [String] {
guard pattern.contains("**") else {
return [pattern]
}
var results = [String]()
var parts = pattern.components(separatedBy: "**")
let firstPart = parts.removeFirst()
var lastPart = parts.joined(separator: "**")
let fileManager = FileManager.default
var directories: [String]
do {
directories = try fileManager.subpathsOfDirectory(atPath: firstPart).compactMap { subpath in
let fullPath = NSString(string: firstPart).appendingPathComponent(subpath)
var isDirectory = ObjCBool(false)
if fileManager.fileExists(atPath: fullPath, isDirectory: &isDirectory) && isDirectory.boolValue {
return fullPath
} else {
return nil
}
}
} catch {
directories = []
print("Error parsing file system item: \(error)")
}
if behavior.includesFilesFromRootOfGlobstar {
// Check the base directory for the glob star as well.
directories.insert(firstPart, at: 0)
// Include the globstar root directory ("dir/") in a pattern like "dir/**" or "dir/**/"
if lastPart.isEmpty {
results.append(firstPart)
}
}
if lastPart.isEmpty {
lastPart = "*"
}
for directory in directories {
let partiallyResolvedPattern = NSString(string: directory).appendingPathComponent(lastPart)
results.append(contentsOf: expandGlobstar(pattern: partiallyResolvedPattern))
}
return results
}
private func isDirectory(path: String) -> Bool {
var isDirectory = isDirectoryCache[path]
if let isDirectory = isDirectory {
return isDirectory
}
var isDirectoryBool = ObjCBool(false)
isDirectory = FileManager.default.fileExists(atPath: path, isDirectory: &isDirectoryBool) && isDirectoryBool.boolValue
isDirectoryCache[path] = isDirectory!
return isDirectory!
}
private func clearCaches() {
isDirectoryCache.removeAll()
}
private func populateFiles(gt: glob_t, includeFiles: Bool) {
let includeDirectories = behavior.includesDirectoriesInResults
for i in 0..<Int(gt.gl_matchc) {
if let path = String(validatingUTF8: gt.gl_pathv[i]!) {
if !includeFiles || !includeDirectories {
let isDirectory = self.isDirectory(path: path)
if (!includeFiles && !isDirectory) || (!includeDirectories && isDirectory) {
continue
}
}
paths.append(path)
}
}
}
// MARK: Subscript Support
subscript(i: Int) -> String {
return paths[i]
}
func index(after i: Int) -> Int {
return i + 1
}
}

View File

@ -1,10 +1,17 @@
//
// CuckooGeneratorError.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 13/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation import Foundation
import FileKit import FileKit
public enum CuckooGeneratorError: Error { public enum CuckooGeneratorError: Error {
case ioError(FileKitError) case ioError(FileKitError)
case unknownError(Error) case unknownError(Error)
case stderrUsed
public var description: String { public var description: String {
switch self { switch self {
@ -12,8 +19,6 @@ public enum CuckooGeneratorError: Error {
return error.description return error.description
case .unknownError(let error): case .unknownError(let error):
return "\(error)" return "\(error)"
case .stderrUsed:
return ""
} }
} }
} }

View File

@ -0,0 +1,36 @@
//
// CodeBuilder.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 06.07.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public class CodeBuilder {
fileprivate static let Tab = " "
public fileprivate(set) var code = ""
fileprivate var nesting = 0
public func clear() {
code = ""
}
public func nest(_ closure: () -> ()) {
nesting += 1
closure()
nesting -= 1
}
public func nest(_ line: String) {
nest { self += line }
}
}
public func +=(left: CodeBuilder, right: String) {
(0..<left.nesting).forEach { _ in left.code += CodeBuilder.Tab }
left.code += right
left.code += "\n"
}

View File

@ -1,8 +1,15 @@
import Foundation //
// FileHeaderHandler.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import FileKit import FileKit
public struct FileHeaderHandler { public struct FileHeaderHandler {
public static func getHeader(of file: FileRepresentation, includeTimestamp: Bool) -> String { public static func getHeader(of file: FileRepresentation, includeTimestamp: Bool) -> String {
let path: String let path: String
if let absolutePath = file.sourceFile.path { if let absolutePath = file.sourceFile.path {
@ -14,32 +21,32 @@ public struct FileHeaderHandler {
let header = getHeader(of: file) let header = getHeader(of: file)
return generationInfo + "\n" + header + "\n" return generationInfo + "\n" + header + "\n"
} }
public static func getImports(of file: FileRepresentation, testableFrameworks: [String]) -> String { public static func getImports(of file: FileRepresentation, testableFrameworks: [String]) -> String {
var imports = Array(Set(file.declarations.only(Import.self).map { "import \($0.importee)\n" })).sorted().joined(separator: "") var imports = Array(Set(file.declarations.only(Import.self).map { "import " + $0.library + "\n" })).sorted().joined(separator: "")
if imports.isEmpty == false { if imports.isEmpty == false {
imports = "\n\(imports)" imports = "\n" + imports
} }
return "import Cuckoo\n" + getTestableImports(testableFrameworks: testableFrameworks) + imports return "import Cuckoo\n" + getTestableImports(testableFrameworks: testableFrameworks) + imports
} }
private static func getRelativePath(from absolutePath: String) -> String { private static func getRelativePath(from absolutePath: String) -> String {
let path = Path(absolutePath) let path = Path(absolutePath)
let base = path.commonAncestor(Path.current) let base = path.commonAncestor(Path.Current)
let components = path.components.suffix(from: base.components.endIndex) let components = path.components.suffix(from: base.components.endIndex)
let result = components.map { $0.rawValue }.joined(separator: Path.separator) let result = components.map { $0.rawValue }.joined(separator: Path.separator)
let difference = Path.current.components.endIndex - base.components.endIndex let difference = Path.Current.components.endIndex - base.components.endIndex
return (0..<difference).reduce(result) { acc, _ in ".." + Path.separator + acc } return (0..<difference).reduce(result) { acc, _ in ".." + Path.separator + acc }
} }
private static func getHeader(of file: FileRepresentation) -> String { private static func getHeader(of file: FileRepresentation) -> String {
let possibleHeaderEnd = getPossibleHeaderEnd(current: file.sourceFile.contents.unicodeScalars.count, declarations: file.declarations) let possibleHeaderEnd = getPossibleHeaderEnd(current: file.sourceFile.contents.unicodeScalars.count, declarations: file.declarations)
let possibleHeader = String(file.sourceFile.contents.utf8.prefix(possibleHeaderEnd)) ?? "" let possibleHeader = String(file.sourceFile.contents.utf8.prefix(possibleHeaderEnd)) ?? ""
let singleLine = getPrefixToLastSingleLineComment(text: possibleHeader) let singleLine = getPrefixToLastSingleLineComment(text: possibleHeader)
let multiLine = getPrefixToLastMultiLineComment(text: possibleHeader) let multiLine = getPrefixToLastMultiLineComment(text: possibleHeader)
return singleLine.count > multiLine.count ? singleLine : multiLine return singleLine.characters.count > multiLine.characters.count ? singleLine : multiLine
} }
private static func getPossibleHeaderEnd(current: Int, declarations: [Token]) -> Int { private static func getPossibleHeaderEnd(current: Int, declarations: [Token]) -> Int {
return declarations.reduce(current) { minimum, declaration in return declarations.reduce(current) { minimum, declaration in
let declarationMinimum: Int let declarationMinimum: Int
@ -56,24 +63,24 @@ public struct FileHeaderHandler {
return min(declarationMinimum, minimum) return min(declarationMinimum, minimum)
} }
} }
private static func getPrefixToLastSingleLineComment(text: String) -> String { private static func getPrefixToLastSingleLineComment(text: String) -> String {
if let range = text.range(of: "//", options: .backwards) { if let range = text.range(of: "//", options: .backwards) {
let lastLine = text.lineRange(for: range) let lastLine = text.lineRange(for: range)
return String(text[..<lastLine.upperBound]) return text.substring(to: lastLine.upperBound)
} else { } else {
return "" return ""
} }
} }
private static func getPrefixToLastMultiLineComment(text: String) -> String { private static func getPrefixToLastMultiLineComment(text: String) -> String {
if let range = text.range(of: "*/", options: .backwards) { if let range = text.range(of: "*/", options: .backwards) {
return String(text[..<range.upperBound]) + "\n" return text.substring(to: range.upperBound) + "\n"
} else { } else {
return "" return ""
} }
} }
private static func getTestableImports(testableFrameworks: [String]) -> String { private static func getTestableImports(testableFrameworks: [String]) -> String {
func replaceIllegalCharacters(_ char: UnicodeScalar) -> Character { func replaceIllegalCharacters(_ char: UnicodeScalar) -> Character {
if CharacterSet.letters.contains(UnicodeScalar(char.value)!) || CharacterSet.decimalDigits.contains(UnicodeScalar(char.value)!) { if CharacterSet.letters.contains(UnicodeScalar(char.value)!) || CharacterSet.decimalDigits.contains(UnicodeScalar(char.value)!) {

View File

@ -0,0 +1,366 @@
//
// Generator.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 13/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct Generator {
private let declarations: [Token]
private let code = CodeBuilder()
public init(file: FileRepresentation) {
declarations = file.declarations
}
public func generate() -> String {
code.clear()
declarations.forEach { generate(for: $0) }
return code.code
}
private func generate(for token: Token) {
switch token {
case let containerToken as ContainerToken:
generateClass(for: containerToken)
generateNoImplStubClass(for: containerToken)
case let property as InstanceVariable:
generateProperty(for: property)
case let method as Method:
generateMethod(for: method)
default:
break
}
}
private func generateClass(for token: ContainerToken) {
guard token.accessibility != .Private else { return }
code += ""
code += "\(token.accessibility.sourceName)class \(mockClassName(of: token.name)): \(token.name), Cuckoo.Mock {"
code.nest {
code += "\(token.accessibility.sourceName)typealias MocksType = \(token.name)"
code += "\(token.accessibility.sourceName)typealias Stubbing = \(stubbingProxyName(of: token.name))"
code += "\(token.accessibility.sourceName)typealias Verification = \(verificationProxyName(of: token.name))"
code += "\(token.accessibility.sourceName)let manager = Cuckoo.MockManager()"
code += ""
code += "private var observed: \(token.name)?"
code += ""
code += "\(token.accessibility.sourceName)func spy(on victim: \(token.name)) -> Self {"
code.nest {
code += "observed = victim"
code += "return self"
}
code += "}"
token.children.forEach { generate(for: $0) }
code += ""
generateStubbing(for: token)
code += ""
generateVerification(for: token)
}
code += "}"
}
private func generateProperty(for token: InstanceVariable) {
guard token.accessibility != .Private else { return }
code += ""
code += "\(token.accessibility.sourceName)\(token.overriding ? "override " : "")var \(token.name): \(token.type) {"
code.nest {
code += "get {"
code.nest("return manager.getter(\"\(token.name)\", original: observed.map { o in return { () -> \(token.type) in o.\(token.name) } })")
code += "}"
if token.readOnly == false {
code += "set {"
code.nest("manager.setter(\"\(token.name)\", value: newValue, original: observed != nil ? { self.observed?.\(token.name) = $0 } : nil)")
code += "}"
}
}
code += "}"
}
private func generateMethod(for token: Method) {
guard token.accessibility != .Private else { return }
guard !token.isInit else { return }
let override = token is ClassMethod ? "override " : ""
let parametersSignature = token.parameters.enumerated().map { "\($1.labelAndName): \($1.type)" }.joined(separator: ", ")
let parametersSignatureWithoutNames = token.parameters.map { "\($0.name): \($0.type)" }.joined(separator: ", ")
var managerCall: String
let tryIfThrowing: String
if token.isThrowing {
managerCall = "try manager.callThrows(\"\(token.fullyQualifiedName)\""
tryIfThrowing = "try "
} else {
managerCall = "manager.call(\"\(token.fullyQualifiedName)\""
tryIfThrowing = ""
}
managerCall += ", parameters: (\(token.parameters.map { $0.name }.joined(separator: ", ")))"
let methodCall = token.parameters.enumerated().map {
if let label = $1.label {
return "\(label): \($1.name)"
} else {
return $1.name
}
}.joined(separator: ", ")
managerCall += ", original: observed.map { o in return { (\(parametersSignatureWithoutNames))\(token.returnSignature) in \(tryIfThrowing)o.\(token.rawName)(\(methodCall)) } })"
code += ""
code += "\(token.accessibility.sourceName)\(override)\(token.isInit ? "" : "func " )\(token.rawName)(\(parametersSignature))\(token.returnSignature) {"
code.nest("return \(managerCall)")
code += "}"
}
private func generateStubbing(for token: Token) {
switch token {
case let containerToken as ContainerToken:
generateStubbingClass(for: containerToken)
case let property as InstanceVariable:
generateStubbingProperty(for: property)
case let method as Method:
generateStubbingMethod(for: method)
default:
break
}
}
private func generateStubbingClass(for token: ContainerToken) {
guard token.accessibility != .Private else { return }
code += "\(token.accessibility.sourceName)struct \(stubbingProxyName(of: token.name)): Cuckoo.StubbingProxy {"
code.nest {
code += "private let manager: Cuckoo.MockManager"
code += ""
code += "\(token.accessibility.sourceName)init(manager: Cuckoo.MockManager) {"
code.nest("self.manager = manager")
code += "}"
token.children.forEach { generateStubbing(for: $0) }
}
code += "}"
}
private func generateStubbingProperty(for token: InstanceVariable) {
guard token.accessibility != .Private else { return }
let propertyType = token.readOnly ? "Cuckoo.ToBeStubbedReadOnlyProperty" : "Cuckoo.ToBeStubbedProperty"
code += ""
code += "var \(token.name): \(propertyType)<\(token.type)> {"
code.nest("return \(propertyType)(manager: manager, name: \"\(token.name)\")")
code += "}"
}
private func generateStubbingMethod(for token: Method) {
guard token.accessibility != .Private else { return }
guard !token.isInit else { return }
let stubFunction: String
if token.isThrowing {
if token.returnType == "Void" {
stubFunction = "Cuckoo.StubNoReturnThrowingFunction"
} else {
stubFunction = "Cuckoo.StubThrowingFunction"
}
} else {
if token.returnType == "Void" {
stubFunction = "Cuckoo.StubNoReturnFunction"
} else {
stubFunction = "Cuckoo.StubFunction"
}
}
let inputTypes = token.parameters.map { $0.typeWithoutAttributes }.joined(separator: ", ")
var returnType = "\(stubFunction)<(\(inputTypes))"
if token.returnType != "Void" {
returnType += ", "
returnType += token.returnType
}
returnType += ">"
code += ""
code += ("\(token.accessibility.sourceName)func \(token.rawName)\(matchableGenerics(with: token.parameters))" +
"(\(matchableParameterSignature(with: token.parameters))) -> \(returnType)\(matchableGenerics(where: token.parameters)) {")
let matchers: String
if token.parameters.isEmpty {
matchers = "[]"
} else {
code.nest(parameterMatchers(for: token.parameters))
matchers = "matchers"
}
code.nest("return \(stubFunction)(stub: manager.createStub(\"\(token.fullyQualifiedName)\", parameterMatchers: \(matchers)))")
code += "}"
}
private func generateVerification(for token: Token) {
switch token {
case let containerToken as ContainerToken:
generateVerificationClass(for: containerToken)
case let property as InstanceVariable:
generateVerificationProperty(for: property)
case let method as Method:
generateVerificationMethod(for: method)
default:
break
}
}
private func generateVerificationClass(for token: ContainerToken) {
guard token.accessibility != .Private else { return }
code += "\(token.accessibility.sourceName)struct \(verificationProxyName(of: token.name)): Cuckoo.VerificationProxy {"
code.nest {
code += "private let manager: Cuckoo.MockManager"
code += "private let callMatcher: Cuckoo.CallMatcher"
code += "private let sourceLocation: Cuckoo.SourceLocation"
code += ""
code += "\(token.accessibility.sourceName)init(manager: Cuckoo.MockManager, callMatcher: Cuckoo.CallMatcher, sourceLocation: Cuckoo.SourceLocation) {"
code.nest {
code += "self.manager = manager"
code += "self.callMatcher = callMatcher"
code += "self.sourceLocation = sourceLocation"
}
code += "}"
token.children.forEach { generateVerification(for: $0) }
}
code += "}"
}
private func generateVerificationProperty(for token: InstanceVariable) {
guard token.accessibility != .Private else { return }
let propertyType = token.readOnly ? "Cuckoo.VerifyReadOnlyProperty" : "Cuckoo.VerifyProperty"
code += ""
code += "var \(token.name): \(propertyType)<\(token.type)> {"
code.nest("return \(propertyType)(manager: manager, name: \"\(token.name)\", callMatcher: callMatcher, sourceLocation: sourceLocation)")
code += "}"
}
private func generateVerificationMethod(for token: Method) {
guard token.accessibility != .Private else { return }
guard !token.isInit else { return }
code += ""
code += "@discardableResult"
code += ("\(token.accessibility.sourceName)func \(token.rawName)\(matchableGenerics(with: token.parameters))" +
"(\(matchableParameterSignature(with: token.parameters))) -> Cuckoo.__DoNotUse<\(token.returnType)>\(matchableGenerics(where: token.parameters)) {")
let matchers: String
if token.parameters.isEmpty {
matchers = "[] as [Cuckoo.ParameterMatcher<Void>]"
} else {
code.nest(parameterMatchers(for: token.parameters))
matchers = "matchers"
}
code.nest("return manager.verify(\"\(token.fullyQualifiedName)\", callMatcher: callMatcher, parameterMatchers: \(matchers), sourceLocation: sourceLocation)")
code += "}"
}
private func generateNoImplStubClass(for token: ContainerToken) {
guard token.accessibility != .Private else { return }
code += ""
code += "\(token.accessibility.sourceName)class \(stubClassName(of: token.name)): \(token.name) {"
code.nest {
token.children.forEach { generateNoImplStub(for: $0) }
}
code += "}"
}
private func generateNoImplStub(for token: Token) {
switch token {
case let property as InstanceVariable:
generateNoImplStubProperty(for: property)
case let method as Method:
generateNoImplStubMethod(for: method)
default:
break
}
}
private func generateNoImplStubProperty(for token: InstanceVariable) {
guard token.accessibility != .Private else { return }
code += ""
code += "\(token.accessibility.sourceName)\(token.overriding ? "override " : "")var \(token.name): \(token.type) {"
code.nest {
code += "get {"
code.nest("return DefaultValueRegistry.defaultValue(for: \(getTypeWithSelfSuffix(for: token.type)))")
code += "}"
if token.readOnly == false {
code += "set {"
code += "}"
}
}
code += "}"
}
private func generateNoImplStubMethod(for token: Method) {
guard token.accessibility != .Private else { return }
guard !token.isInit else { return }
let override = token is ClassMethod ? "override " : ""
let parametersSignature = token.parameters.enumerated().map { "\($1.labelAndName): \($1.type)" }.joined(separator: ", ")
code += ""
code += "\(token.accessibility.sourceName)\(override)func \(token.rawName)(\(parametersSignature))\(token.returnSignature) {"
code.nest("return DefaultValueRegistry.defaultValue(for: \(getTypeWithSelfSuffix(for: token.returnType)))")
code += "}"
}
private func mockClassName(of originalName: String) -> String {
return "Mock" + originalName
}
private func stubClassName(of originalName: String) -> String {
return originalName + "Stub"
}
private func stubbingProxyName(of originalName: String) -> String {
return "__StubbingProxy_" + originalName
}
private func verificationProxyName(of originalName: String) -> String {
return "__VerificationProxy_" + originalName
}
private func matchableGenerics(with parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
let genericParameters = (1...parameters.count).map { "M\($0): Cuckoo.Matchable" }.joined(separator: ", ")
return "<\(genericParameters)>"
}
private func matchableGenerics(where parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
let whereClause = parameters.enumerated().map { "M\($0 + 1).MatchedType == \($1.typeWithoutAttributes)" }.joined(separator: ", ")
return " where \(whereClause)"
}
private func matchableParameterSignature(with parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
return parameters.enumerated().map { "\($1.labelAndName): M\($0 + 1)" }.joined(separator: ", ")
}
private func parameterMatchers(for parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
let tupleType = parameters.map { $0.typeWithoutAttributes }.joined(separator: ", ")
let matchers = parameters.enumerated().map { "wrap(matchable: \($1.name)) { $0\(parameters.count > 1 ? ".\($0)" : "") }" }.joined(separator: ", ")
return "let matchers: [Cuckoo.ParameterMatcher<(\(tupleType))>] = [\(matchers)]"
}
private func getTypeWithSelfSuffix(for typeName: String) -> String {
if (typeName.hasSuffix("?")) {
return "Optional<\(typeName.substring(to: typeName.index(before: typeName.endIndex)))>.self"
} else {
return typeName + ".self"
}
}
}

View File

@ -0,0 +1,17 @@
//
// CuckooGeneratorFramework.h
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 13/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
#import <Foundation/Foundation.h>
//! Project version number for CuckooGeneratorFramework.
FOUNDATION_EXPORT double CuckooGeneratorFrameworkVersionNumber;
//! Project version string for CuckooGeneratorFramework.
FOUNDATION_EXPORT const unsigned char CuckooGeneratorFrameworkVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <CuckooGeneratorFramework/PublicHeader.h>

View File

@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@ -15,10 +15,14 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>0.6.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string> <string>Copyright © 2016 Brightify. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict> </dict>
</plist> </plist>

View File

@ -0,0 +1,210 @@
//
// Tokenizer.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import SourceKittenFramework
public struct Tokenizer {
private let file: File
private let source: String
public init(sourceFile: File) {
self.file = sourceFile
source = sourceFile.contents
}
public func tokenize() -> FileRepresentation {
let structure = Structure(file: file)
let declarations = tokenize(structure.dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let imports = tokenize(imports: declarations)
return FileRepresentation(sourceFile: file, declarations: declarations + imports)
}
private func tokenize(_ representables: [SourceKitRepresentable]) -> [Token] {
return representables.flatMap(tokenize)
}
private func tokenize(_ representable: SourceKitRepresentable) -> Token? {
guard let dictionary = representable as? [String: SourceKitRepresentable] else { return nil }
// Common fields
let name = dictionary[Key.Name.rawValue] as? String ?? "name not set"
let kind = dictionary[Key.Kind.rawValue] as? String ?? "unknown type"
// Optional fields
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let bodyRange = extractRange(from: dictionary, offset: .BodyOffset, length: .BodyLength)
let accessibility = (dictionary[Key.Accessibility.rawValue] as? String).flatMap { Accessibility(rawValue: $0) }
let type = dictionary[Key.TypeName.rawValue] as? String
switch kind {
case Kinds.ProtocolDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self)
return ProtocolDeclaration(
name: name,
accessibility: accessibility!,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children)
case Kinds.ClassDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self).map { child -> Token in
if var property = child as? InstanceVariable {
property.overriding = true
return property
} else {
return child
}
}
return ClassDeclaration(
name: name,
accessibility: accessibility!,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children)
case Kinds.ExtensionDeclaration.rawValue:
return ExtensionDeclaration(range: range!)
case Kinds.InstanceVariable.rawValue:
let setterAccessibility = (dictionary[Key.SetterAccessibility.rawValue] as? String).flatMap(Accessibility.init)
if String(describing: source.utf8.dropFirst(range!.startIndex)).takeUntil(occurence: name)?.trimmed.hasPrefix("let") == true {
return nil
}
return InstanceVariable(
name: name,
type: type!,
accessibility: accessibility!,
setterAccessibility: setterAccessibility,
range: range!,
nameRange: nameRange!,
overriding: false)
case Kinds.InstanceMethod.rawValue:
let parameters = tokenize(methodName: name, parameters: dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
var returnSignature: String
if let bodyRange = bodyRange {
returnSignature = source[nameRange!.endIndex..<bodyRange.startIndex].takeUntil(occurence: "{")?.trimmed ?? ""
} else {
returnSignature = source[nameRange!.endIndex..<range!.endIndex].trimmed
if returnSignature.isEmpty {
let untilThrows = String(source.utf8.dropFirst(nameRange!.endIndex))?
.takeUntil(occurence: "throws").map { $0 + "throws" }?
.trimmed
if let untilThrows = untilThrows, untilThrows == "throws" || untilThrows == "rethrows" {
returnSignature = "\(untilThrows)"
}
}
}
if returnSignature.isEmpty == false {
returnSignature = " " + returnSignature
}
// When bodyRange != nil, we need to create .ClassMethod instead of .ProtocolMethod
if let bodyRange = bodyRange {
return ClassMethod(
name: name,
accessibility: accessibility!,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: parameters,
bodyRange: bodyRange)
} else {
return ProtocolMethod(
name: name,
accessibility: accessibility!,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: parameters)
}
case Kinds.Mark.rawValue:
// Do not log warning
return nil
default:
fputs("Unknown kind: \(kind)", stderr)
return nil
}
}
private func tokenize(methodName: String, parameters: [SourceKitRepresentable]) -> [MethodParameter] {
// Takes the string between `(` and `)`
let parameterNames = methodName.components(separatedBy: "(").last?.characters.dropLast(1).map { "\($0)" }.joined(separator: "")
let parameterLabels: [String?] = parameterNames?.components(separatedBy: ":").map { $0 != "_" ? $0 : nil } ?? []
return zip(parameterLabels, parameters).flatMap(tokenize)
}
private func tokenize(parameterLabel: String?, parameter: SourceKitRepresentable) -> MethodParameter? {
guard let dictionary = parameter as? [String: SourceKitRepresentable] else { return nil }
let name = dictionary[Key.Name.rawValue] as? String ?? "name not set"
let kind = dictionary[Key.Kind.rawValue] as? String ?? "unknown type"
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let type = dictionary[Key.TypeName.rawValue] as? String
switch kind {
case Kinds.MethodParameter.rawValue:
return MethodParameter(label: parameterLabel, name: name, type: type!, range: range!, nameRange: nameRange!)
default:
fputs("Unknown method parameter kind: \(kind)", stderr)
return nil
}
}
private func tokenize(imports otherTokens: [Token]) -> [Token] {
let rangesToIgnore: [CountableRange<Int>] = otherTokens.flatMap { token in
switch token {
case let container as ContainerToken:
return container.range
case let extensionToken as ExtensionDeclaration:
return extensionToken.range
default:
return nil
}
}
do {
let regex = try NSRegularExpression(pattern: "(?:\\b|;)import(?:\\s|(?:\\/\\/.*\\n)|(?:\\/\\*.*\\*\\/))+([^\\s;\\/]+)", options: [])
let results = regex.matches(in: source, options: [], range: NSMakeRange(0, source.characters.count))
return results.filter { result in
rangesToIgnore.filter { $0 ~= result.range.location }.isEmpty
}.map {
let libraryRange = $0.rangeAt(1)
let fromIndex = source.index(source.startIndex, offsetBy: libraryRange.location)
let toIndex = source.index(fromIndex, offsetBy: libraryRange.length)
let library = source.substring(with: fromIndex..<toIndex)
let range = $0.range.location..<($0.range.location + $0.range.length)
return Import(range: range, library: library)
}
} catch let error as NSError {
fatalError("Invalid regex:" + error.description)
}
}
}

View File

@ -0,0 +1,24 @@
//
// Accessibility.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public enum Accessibility: String {
case Public = "source.lang.swift.accessibility.public"
case Internal = "source.lang.swift.accessibility.internal"
case Private = "source.lang.swift.accessibility.private"
public var sourceName: String {
switch self {
case .Public:
return "public "
case .Internal:
return ""
case .Private:
return "private "
}
}
}

View File

@ -0,0 +1,22 @@
//
// ClassDeclaration.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ClassDeclaration: ContainerToken {
public let name: String
public let accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public let initializers: [Initializer]
public let children: [Token]
public let implementation: Bool = true
public var hasNoArgInit: Bool {
return initializers.filter { $0.parameters.isEmpty }.isEmpty
}
}

View File

@ -0,0 +1,18 @@
//
// ClassMethod.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ClassMethod: Method {
public let name: String
public let accessibility: Accessibility
public let returnSignature: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let parameters: [MethodParameter]
public let bodyRange: CountableRange<Int>
}

View File

@ -0,0 +1,18 @@
//
// ContainerToken.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public protocol ContainerToken: Token {
var name: String { get }
var accessibility: Accessibility { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var bodyRange: CountableRange<Int> { get }
var initializers: [Initializer] { get }
var children: [Token] { get }
var implementation: Bool { get }
}

View File

@ -0,0 +1,12 @@
//
// ExtensionDeclaration.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 17.06.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ExtensionDeclaration: Token {
// TODO Implement support for extensions
public let range: CountableRange<Int>
}

View File

@ -0,0 +1,19 @@
//
// FileRepresentation.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
import SourceKittenFramework
public struct FileRepresentation {
public let sourceFile: File
public let declarations: [Token]
public init(sourceFile: File, declarations: [Token]) {
self.sourceFile = sourceFile
self.declarations = declarations
}
}

View File

@ -0,0 +1,12 @@
//
// Import.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 17.06.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct Import: Token {
public let range: CountableRange<Int>
public let library: String
}

View File

@ -0,0 +1,18 @@
//
// Initializer.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct Initializer: Method {
public let name: String
public let accessibility: Accessibility
public let returnSignature: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let parameters: [MethodParameter]
public let required: Bool
}

View File

@ -0,0 +1,21 @@
//
// InstanceVariable.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct InstanceVariable: Token {
public var name: String
public var type: String
public var accessibility: Accessibility
public var setterAccessibility: Accessibility?
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var overriding: Bool
public var readOnly: Bool {
return setterAccessibility == nil
}
}

View File

@ -1,3 +1,11 @@
//
// Key.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public enum Key: String { public enum Key: String {
case Substructure = "key.substructure" case Substructure = "key.substructure"
case Kind = "key.kind" case Kind = "key.kind"
@ -5,10 +13,7 @@ public enum Key: String {
case SetterAccessibility = "key.setter_accessibility" case SetterAccessibility = "key.setter_accessibility"
case Name = "key.name" case Name = "key.name"
case TypeName = "key.typename" case TypeName = "key.typename"
case InheritedTypes = "key.inheritedtypes"
case Attributes = "key.attributes"
case Attribute = "key.attribute"
case Length = "key.length" case Length = "key.length"
case Offset = "key.offset" case Offset = "key.offset"

View File

@ -1,12 +1,18 @@
//
// Kinds.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public enum Kinds: String { public enum Kinds: String {
case ProtocolDeclaration = "source.lang.swift.decl.protocol" case ProtocolDeclaration = "source.lang.swift.decl.protocol"
case InstanceMethod = "source.lang.swift.decl.function.method.instance" case InstanceMethod = "source.lang.swift.decl.function.method.instance"
case MethodParameter = "source.lang.swift.decl.var.parameter" case MethodParameter = "source.lang.swift.decl.var.parameter"
case ClassDeclaration = "source.lang.swift.decl.class" case ClassDeclaration = "source.lang.swift.decl.class"
case StructDeclaration = "source.lang.swift.decl.struct"
case ExtensionDeclaration = "source.lang.swift.decl.extension" case ExtensionDeclaration = "source.lang.swift.decl.extension"
case InstanceVariable = "source.lang.swift.decl.var.instance" case InstanceVariable = "source.lang.swift.decl.var.instance"
case GenericParameter = "source.lang.swift.decl.generic_type_param"
case AssociatedType = "source.lang.swift.decl.associatedtype" case Mark = "source.lang.swift.syntaxtype.comment.mark"
case Optional = "source.decl.attribute.optional"
} }

View File

@ -0,0 +1,48 @@
//
// .swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public protocol Method: Token {
var name: String { get }
var accessibility: Accessibility { get }
var returnSignature: String { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var parameters: [MethodParameter] { get }
}
public extension Method {
var rawName: String {
return name.takeUntil(occurence: "(") ?? ""
}
var isInit: Bool {
return rawName == "init"
}
var fullyQualifiedName: String {
let parameterTypes = parameters.map { $0.type }
let nameParts = name.components(separatedBy: ":")
let lastNamePart = nameParts.last ?? ""
return zip(nameParts.dropLast(), parameterTypes)
.map { $0 + ": " + $1 }
.joined(separator: ", ") + lastNamePart + returnSignature
}
var isThrowing: Bool {
return returnSignature.trimmed.hasPrefix("throws")
}
var returnType: String {
if let range = returnSignature.range(of: "->") {
return returnSignature.substring(from: range.upperBound).trimmed
} else {
return "Void"
}
}
}

View File

@ -0,0 +1,27 @@
//
// MethodParameter.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct MethodParameter: Token {
public let label: String?
public let name: String
public let type: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public var labelAndName: String {
if let label = label {
return label != name ? "\(label) \(name)" : name
} else {
return "_ \(name)"
}
}
public var typeWithoutAttributes: String {
return type.replacingOccurrences(of: "@escaping", with: "").replacingOccurrences(of: "@autoclosure", with: "").trimmed
}
}

View File

@ -0,0 +1,18 @@
//
// ProtocolDeclaration.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ProtocolDeclaration: ContainerToken {
public let name: String
public let accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public let initializers: [Initializer]
public let children: [Token]
public let implementation: Bool = false
}

View File

@ -0,0 +1,16 @@
//
// ProtocolMethod.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ProtocolMethod: Method {
public let name: String
public let accessibility: Accessibility
public let returnSignature: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let parameters: [MethodParameter]
}

View File

@ -0,0 +1,9 @@
//
// Token.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public protocol Token { }

View File

@ -0,0 +1,44 @@
//
// String+Utility.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import SourceKittenFramework
extension String {
var trimmed: String {
return trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
func takeUntil(occurence: String) -> String? {
return components(separatedBy: occurence).first
}
subscript(range: Range<Int>) -> String {
let stringRange = characters.index(startIndex, offsetBy: range.lowerBound)..<characters.index(startIndex, offsetBy: range.upperBound)
return self[stringRange]
}
}
extension Sequence {
func only<T>(_ type: T.Type) -> [T] {
return flatMap { $0 as? T }
}
func noneOf<T>(_ type: T.Type) -> [Iterator.Element] {
return filter { !($0 is T) }
}
}
internal func extractRange(from dictionary: [String: SourceKitRepresentable], offset: Key, length: Key) -> CountableRange<Int>? {
guard let
offset = (dictionary[offset.rawValue] as? Int64).map({ Int($0) }),
let length = (dictionary[length.rawValue] as? Int64).map({ Int($0) })
else { return nil }
return offset..<offset.advanced(by: length)
}

View File

@ -0,0 +1,94 @@
//
// GenerateMocksCommand.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Commandant
import Result
import SourceKittenFramework
import FileKit
import CuckooGeneratorFramework
private func curry<P1, P2, P3, P4, P5, P6, P7, R>(_ f: @escaping (P1, P2, P3, P4, P5, P6, P7) -> R)
-> (P1) -> (P2) -> (P3) -> (P4) -> (P5) -> (P6) -> (P7) -> R {
return { p1 in { p2 in { p3 in { p4 in { p5 in { p6 in { p7 in f(p1, p2, p3, p4, p5, p6, p7) } } } } } } }
}
public struct GenerateMocksCommand: CommandProtocol {
public let verb = "generate"
public let function = "Generates mock files"
public func run(_ options: Options) -> Result<Void, CuckooGeneratorError> {
let inputFiles = Array(Set(options.files.map { Path($0).standardRawValue }))
let tokens = inputFiles.flatMap { File(path: $0) }.map { Tokenizer(sourceFile: $0).tokenize() }
let parsedFiles = options.noClassMocking ? removeClasses(tokens) : tokens
let headers = parsedFiles.map { options.noHeader ? "" : FileHeaderHandler.getHeader(of: $0, includeTimestamp: !options.noTimestamp) }
let imports = parsedFiles.map { FileHeaderHandler.getImports(of: $0, testableFrameworks: options.testableFrameworks) }
let mocks = parsedFiles.map { Generator(file: $0).generate() }
let mergedFiles = zip(zip(headers, imports), mocks).map { $0.0 + $0.1 + $1 }
let outputPath = Path(options.output)
do {
if outputPath.isDirectory {
let inputPaths = inputFiles.map { Path($0) }
for (inputPath, outputText) in zip(inputPaths, mergedFiles) {
let fileName = options.filePrefix + inputPath.fileName
let outputFile = TextFile(path: outputPath + fileName)
try outputText |> outputFile
}
} else {
let outputFile = TextFile(path: outputPath)
try mergedFiles.joined(separator: "\n") |> outputFile
}
} catch let error as FileKitError {
return .failure(.ioError(error))
} catch let error {
return .failure(.unknownError(error))
}
return .success()
}
private func removeClasses(_ filesRepresentation: [FileRepresentation]) -> [FileRepresentation] {
return filesRepresentation.map {
let declarations = $0.declarations.filter { !($0 is ClassDeclaration) }
return FileRepresentation(sourceFile: $0.sourceFile, declarations: declarations)
}.filter { !$0.declarations.isEmpty }
}
public struct Options: OptionsProtocol {
let files: [String]
let output: String
let noHeader: Bool
let noTimestamp: Bool
let testableFrameworks: [String]
let filePrefix: String
let noClassMocking: Bool
public init(output: String, testableFrameworks: String, noHeader: Bool, noTimestamp: Bool, filePrefix: String, noClassMocking: Bool, files: [String]) {
self.output = output
self.testableFrameworks = testableFrameworks.components(separatedBy: ",").filter { !$0.isEmpty }
self.noHeader = noHeader
self.noTimestamp = noTimestamp
self.filePrefix = filePrefix
self.files = files
self.noClassMocking = noClassMocking
}
public static func evaluate(_ m: CommandMode) -> Result<Options, CommandantError<CuckooGeneratorError>> {
return curry(Options.init)
<*> m <| Option(key: "output", defaultValue: "GeneratedMocks.swift", usage: "Where to put the generated mocks.\nIf a path to a directory is supplied, each input file will have a respective output file with mocks.\nIf a path to a Swift file is supplied, all mocks will be in a single file.\nDefault value is `GeneratedMocks.swift`.")
<*> m <| Option(key: "testable", defaultValue: "", usage: "A comma separated list of frameworks that should be imported as @testable in the mock files.")
<*> m <| Option(key: "no-header", defaultValue: false, usage: "Do not generate file headers.")
<*> m <| Option(key: "no-timestamp", defaultValue: false, usage: "Do not generate timestamp.")
<*> m <| Option(key: "file-prefix", defaultValue: "", usage: "Names of generated files in directory will start with this prefix. Only works when output path is directory.")
<*> m <| Option(key: "no-class-mocking", defaultValue: false, usage: "Do not generate mocks for classes.")
<*> m <| Argument(usage: "Files to parse and generate mocks for.")
}
}
}

View File

@ -1,156 +0,0 @@
import Foundation
import Stencil
public struct Generator {
private let declarations: [Token]
public init(file: FileRepresentation) {
declarations = file.declarations
}
public func generate(debug: Bool = false) throws -> String {
let ext = Extension()
ext.registerFilter("genericSafe") { (value: Any?) in
guard let string = value as? String else { return value }
return self.genericSafeType(from: string)
}
ext.registerFilter("matchableGenericNames") { (value: Any?) in
guard let method = value as? Method else { return value }
return self.matchableGenericTypes(from: method)
}
ext.registerFilter("matchableGenericWhereClause") { (value: Any?) in
guard let method = value as? Method else { return value }
return self.matchableGenericsWhereClause(from: method)
}
ext.registerFilter("matchableParameterSignature") { (value: Any?) in
guard let parameters = value as? [MethodParameter] else { return value }
return self.matchableParameterSignature(with: parameters)
}
ext.registerFilter("parameterMatchers") { (value: Any?) in
guard let parameters = value as? [MethodParameter] else { return value }
return self.parameterMatchers(for: parameters)
}
ext.registerFilter("openNestedClosure") { (value: Any?) in
guard let method = value as? Method else { return value }
return self.openNestedClosure(for: method)
}
ext.registerFilter("closeNestedClosure") { (value: Any?) in
guard let parameters = value as? [MethodParameter] else { return value }
return self.closeNestedClosure(for: parameters)
}
ext.registerFilter("escapeReservedKeywords") { (value: Any?) in
guard let name = value as? String else { return value }
return escapeReservedKeywords(for: name)
}
ext.registerFilter("removeClosureArgumentNames") { (value: Any?) in
guard let type = value as? String else { return value }
return self.removeClosureArgumentNames(for: type)
}
let environment = Environment(extensions: [ext])
let containers = declarations.compactMap { $0 as? ContainerToken }
.filter {
$0.parent?.areAllHierarchiesAccessible ?? true && $0.accessibility.isAccessible
}
.map { $0.serializeWithType() }
return try environment.renderTemplate(string: Templates.mock, context: ["containers": containers, "debug": debug])
}
private func matchableGenericTypes(from method: Method) -> String {
guard !method.parameters.isEmpty || !method.genericParameters.isEmpty else { return "" }
let matchableGenericParameters = method.parameters.enumerated().map { index, parameter -> String in
let type = parameter.isOptional ? "OptionalMatchable" : "Matchable"
return "M\(index + 1): Cuckoo.\(type)"
}
let methodGenericParameters = method.genericParameters.map { $0.description }
return "<\((matchableGenericParameters + methodGenericParameters).joined(separator: ", "))>"
}
private func matchableGenericsWhereClause(from method: Method) -> String {
guard method.parameters.isEmpty == false else { return "" }
let matchableWhereConstraints = method.parameters.enumerated().map { index, parameter -> String in
let type = parameter.isOptional ? "OptionalMatchedType" : "MatchedType"
return "M\(index + 1).\(type) == \(genericSafeType(from: parameter.type.withoutAttributes.unoptionaled.sugarized))"
}
let methodWhereConstraints = method.returnSignature.whereConstraints
return " where \((matchableWhereConstraints + methodWhereConstraints).joined(separator: ", "))"
}
private func matchableParameterSignature(with parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
return parameters.enumerated().map { "\($1.labelAndName): M\($0 + 1)" }.joined(separator: ", ")
}
private func parameterMatchers(for parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "let matchers: [Cuckoo.ParameterMatcher<Void>] = []" }
let tupleType = parameters.map { $0.typeWithoutAttributes }.joined(separator: ", ")
let matchers = parameters.enumerated().map { index, parameter in
let name = escapeReservedKeywords(for: parameter.name)
return "wrap(matchable: \(name)) { $0\(parameters.count > 1 ? ".\(index)" : "") }"
}
.joined(separator: ", ")
return "let matchers: [Cuckoo.ParameterMatcher<(\(genericSafeType(from: tupleType)))>] = [\(matchers)]"
}
private func genericSafeType(from type: String) -> String {
return type.replacingOccurrences(of: "!", with: "?")
}
private func openNestedClosure(for method: Method) -> String {
var fullString = ""
for (index, parameter) in method.parameters.enumerated() {
if parameter.isClosure && !parameter.isEscaping {
let indents = String(repeating: "\t", count: index)
let tries = method.isThrowing ? "try " : ""
let awaits = method.isAsync ? "await " : ""
let sugarizedReturnType = method.returnType.sugarized
let returnSignature: String
if sugarizedReturnType.isEmpty {
returnSignature = sugarizedReturnType
} else {
returnSignature = " -> \(sugarizedReturnType)"
}
fullString += "\(indents)return \(tries)\(awaits)withoutActuallyEscaping(\(parameter.name), do: { (\(parameter.name): @escaping \(parameter.type))\(returnSignature) in\n"
}
}
return fullString
}
private func closeNestedClosure(for parameters: [MethodParameter]) -> String {
var fullString = ""
for (index, parameter) in parameters.enumerated() {
if parameter.isClosure && !parameter.isEscaping {
let indents = String(repeating: "\t", count: index)
fullString += "\(indents)})\n"
}
}
return fullString
}
private func removeClosureArgumentNames(for type: String) -> String {
type.replacingOccurrences(
of: "_\\s+?[_a-zA-Z]\\w*?\\s*?:",
with: "",
options: .regularExpression
)
}
}

View File

@ -1,19 +0,0 @@
import Foundation
@dynamicMemberLookup
public final class Reference<Value> {
private(set) var value: Value
public init(value: Value) {
self.value = value
}
public subscript<T>(dynamicMember keyPath: KeyPath<Value, T>) -> T {
value[keyPath: keyPath]
}
public subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> T {
get { value[keyPath: keyPath] }
set { value[keyPath: keyPath] = newValue }
}
}

View File

@ -1,8 +0,0 @@
import Foundation
public private(set) var stderrUsed = false
func stderrPrint(_ item: Any) {
stderrUsed = true
fputs("error: \(item)\n", stderr)
}

View File

@ -1,153 +0,0 @@
import Foundation
extension Templates {
static let staticGenericParameter = "_CUCKOO$$GENERIC"
static let typeErasureClassName = "DefaultImplCaller"
static let mock = """
{% for container in containers %}
{% if debug %}
// {{ container }}
{% endif %}
{{ container.unavailablePlatformsCheck }}
{% for attribute in container.attributes %}
{{ attribute.text }}
{% endfor %}
{% if container.hasParent %}
extension {{ container.parentFullyQualifiedName }} {
{% endif %}
{{ container.accessibility }} class {{ container.mockName }}{{ container.genericParameters }}: {% if container.isNSObjectProtocol %}NSObject, {% endif %}{{ container.name }}{% if container.isImplementation %}{{ container.genericArguments }}{% endif %}, {% if container.isImplementation %}Cuckoo.ClassMock{% else %}Cuckoo.ProtocolMock{% endif %} {
{% if container.isGeneric and not container.isImplementation %}
{{ container.accessibility }} typealias MocksType = \(typeErasureClassName){{ container.genericArguments }}
{% else %}
{{ container.accessibility }} typealias MocksType = {{ container.name }}{{ container.genericArguments }}
{% endif %}
{{ container.accessibility }} typealias Stubbing = __StubbingProxy_{{ container.name }}
{{ container.accessibility }} typealias Verification = __VerificationProxy_{{ container.name }}
{{ container.accessibility }} let cuckoo_manager = Cuckoo.MockManager.preconfiguredManager ?? Cuckoo.MockManager(hasParent: {{ container.isImplementation }})
{% if container.isGeneric and not container.isImplementation %}
\(Templates.typeErasure.indented())
private var __defaultImplStub: \(typeErasureClassName){{ container.genericArguments }}?
{{ container.accessibility }} func enableDefaultImplementation<\(staticGenericParameter): {{ container.name }}>(_ stub: \(staticGenericParameter)) where {{ container.genericProtocolIdentity }} {
var mutableStub = stub
__defaultImplStub = \(typeErasureClassName)(from: &mutableStub, keeping: mutableStub)
cuckoo_manager.enableDefaultStubImplementation()
}
{{ container.accessibility }} func enableDefaultImplementation<\(staticGenericParameter): {{ container.name }}>(mutating stub: UnsafeMutablePointer<\(staticGenericParameter)>) where {{ container.genericProtocolIdentity }} {
__defaultImplStub = \(typeErasureClassName)(from: stub, keeping: nil)
cuckoo_manager.enableDefaultStubImplementation()
}
{% else %}
private var __defaultImplStub: {{ container.name }}{{ container.genericArguments }}?
{{ container.accessibility }} func enableDefaultImplementation(_ stub: {{ container.name }}{{ container.genericArguments }}) {
__defaultImplStub = stub
cuckoo_manager.enableDefaultStubImplementation()
}
{% endif %}
{% for property in container.properties %}
{{ property.unavailablePlatformsCheck }}
{% if debug %}
// {{property}}
{% endif %}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
{{ property.accessibility }}{% if container.isImplementation %} override{% endif %} var {{ property.name }}: {{ property.type }} {
get{% if property.isAsync %} async{% endif %}{% if property.isThrowing %} throws{% endif %} {
return {% if property.isThrowing %}try {% endif %}{% if property.isAsync %}await {% endif %}cuckoo_manager.getter{% if property.isThrowing %}Throws{% endif %}("{{ property.name }}",
superclassCall:
{% if container.isImplementation %}
{% if property.isThrowing %}try {% endif %}{% if property.isAsync %}await {% endif %}super.{{ property.name }}
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: {% if property.isThrowing %}try {% endif %}{% if property.isAsync %}await {% endif %} __defaultImplStub!.{{property.name}})
}
{% ifnot property.isReadOnly %}
set {
cuckoo_manager.setter("{{ property.name }}",
value: newValue,
superclassCall:
{% if container.isImplementation %}
super.{{ property.name }} = newValue
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: __defaultImplStub!.{{property.name}} = newValue)
}
{% endif %}
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for initializer in container.initializers %}
{{ initializer.unavailablePlatformsCheck }}
{% if debug %}
// {{initializer}}
{% endif %}
{{ initializer.accessibility }}{% if container.isImplementation %} override{% endif %}{% if initializer.@type == "ProtocolMethod" %} required{% endif %} init({{initializer.parameterSignature}}) {
{% if container.isImplementation %}
super.init({{initializer.call}})
{% endif %}
}
{% if initializer.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% if debug %}
// {{method}}
{% endif %}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
{{ method.accessibility }}{% if container.isImplementation and method.isOverriding %} override{% endif %} func {{ method.name|escapeReservedKeywords }}{{ method.genericParameters }}({{ method.parameterSignature }}) {{ method.returnSignature }} {
{{ method.self|openNestedClosure }}
return{% if method.isThrowing %} try{% endif %}{% if method.isAsync %} await{% endif %} cuckoo_manager.call{% if method.isThrowing %}{{ method.throwType|capitalize }}{% endif %}(
\"\"\"
{{method.fullyQualifiedName}}
\"\"\",
parameters: ({{method.parameterNames}}),
escapingParameters: ({{method.escapingParameterNames}}),
superclassCall:
{% if container.isImplementation %}
{% if method.isAsync %}await {% endif %}super.{{method.name}}({{method.call}})
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: {% if method.isAsync %}await {% endif %}__defaultImplStub!.{{method.name}}{%if method.isOptional %}!{%endif%}({{method.call}}))
{{ method.parameters|closeNestedClosure }}
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
\(Templates.stubbingProxy.indented())
\(Templates.verificationProxy.indented())
}
\(Templates.noImplStub)
{% if container.hasParent %}
}
{% endif %}
{% if container.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
"""
}

View File

@ -1,57 +0,0 @@
extension Templates {
static let noImplStub = """
{% for attribute in container.attributes %}
{{ attribute.text }}
{% endfor %}
{{container.accessibility}} class {{ container.name }}Stub{{ container.genericParameters }}: {% if container.isNSObjectProtocol %}NSObject, {% endif %}{{ container.name }}{% if container.isImplementation %}{{ container.genericArguments }}{% endif %} {
{% for property in container.properties %}
{{ property.unavailablePlatformsCheck }}
{% if debug %}
// {{property}}
{% endif %}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
{{ property.accessibility }}{% if container.@type == "ClassDeclaration" %} override{% endif %} var {{ property.name }}: {{ property.type }} {
get {
return DefaultValueRegistry.defaultValue(for: ({{property.type|genericSafe|removeClosureArgumentNames}}).self)
}
{% ifnot property.isReadOnly %}
set { }
{% endif %}
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for initializer in container.initializers %}
{{ initializer.unavailablePlatformsCheck }}
{{ initializer.accessibility }}{% if container.@type == "ClassDeclaration" %} override{% endif %}{% if initializer.@type == "ProtocolMethod" %} required{%endif%} init({{initializer.parameterSignature}}) {
{% if container.@type == "ClassDeclaration" %}
super.init({{initializer.call}})
{% endif %}
}
{% if initializer.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% if debug %}
// {{method}}
{% endif %}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
{{ method.accessibility }}{% if container.@type == "ClassDeclaration" and method.isOverriding %} override{% endif %} func {{ method.name|escapeReservedKeywords }}{{ method.genericParameters }}({{ method.parameterSignature }}) {{ method.returnSignature }} {{ method.whereClause }} {
return DefaultValueRegistry.defaultValue(for: ({{method.returnType|genericSafe}}).self)
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
}
"""
}

View File

@ -1,41 +0,0 @@
import Foundation
extension Templates {
static let stubbingProxy = """
{{ container.accessibility }} struct __StubbingProxy_{{ container.name }}: Cuckoo.StubbingProxy {
private let cuckoo_manager: Cuckoo.MockManager
{{ container.accessibility }} init(manager: Cuckoo.MockManager) {
self.cuckoo_manager = manager
}
{% for property in container.properties %}
{{ property.unavailablePlatformsCheck }}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
var {{property.name}}: Cuckoo.{{ property.stubType }}<{{ container.mockName }}, {% if property.isReadOnly %}{{property.type|genericSafe}}{% else %}{{property.nonOptionalType|genericSafe}}{% endif %}> {
return .init(manager: cuckoo_manager, name: "{{property.name}}")
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
func {{method.name|escapeReservedKeywords}}{{method.self|matchableGenericNames}}({{method.parameters|matchableParameterSignature}}) -> {{method.stubFunction}}<({{method.inputTypes|genericSafe}}){%if method.returnType != "Void" %}, {{method.returnType|genericSafe}}{%endif%}>{{method.self|matchableGenericWhereClause}} {
{{method.parameters|parameterMatchers}}
return .init(stub: cuckoo_manager.createStub(for: {{ container.mockName }}.self, method:
\"\"\"
{{method.fullyQualifiedName}}
\"\"\", parameterMatchers: matchers))
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
}
"""
}

View File

@ -1,13 +0,0 @@
import Foundation
struct Templates { }
extension String {
func indented(times: Int = 1) -> String {
let indentation = String(repeating: " ", count: times)
return self.components(separatedBy: CharacterSet.newlines).map {
indentation + $0
}.joined(separator: "\n")
}
}

View File

@ -1,42 +0,0 @@
import Foundation
extension Templates {
static let typeErasure = """
{{ container.accessibility }} class \(typeErasureClassName){{ container.genericParameters }}: {{ container.name }} {
private let reference: Any
{% for property in container.properties %}private let _getter_storage$${{ property.name }}: () -> {{ property.type }}{% if not property.isReadOnly %}
private let _setter_storage$${{ property.name }}: ({{ property.type }}) -> Void{% endif %}
{{ container.accessibility }} var {{ property.name }}: {{ property.type }} {
get { return _getter_storage$${{ property.name }}() }{% if not property.isReadOnly %}
set { _setter_storage$${{ property.name }}(newValue) }{% endif %}
}
{% endfor %}
{# For developers: The `keeping reference: Any?` is necessary because when called from the `enableDefaultImplementation(stub:)` method
instead of `enableDefaultImplementation(mutating:)`, we need to prevent the struct getting deallocated. #}
init<\(staticGenericParameter): {{ container.name }}>(from defaultImpl: UnsafeMutablePointer<\(staticGenericParameter)>, keeping reference: @escaping @autoclosure () -> Any?) where {{ container.genericProtocolIdentity }} {
self.reference = reference
{% for property in container.properties %}_getter_storage$${{ property.name }} = { defaultImpl.pointee.{{ property.name }} }
{% if not property.isReadOnly %}_setter_storage$${{ property.name }} = { defaultImpl.pointee.{{ property.name }} = $0 }{% endif %}
{% endfor %}
{% for method in container.methods %}_storage${{ forloop.counter }}${{ method.name }} = defaultImpl.pointee.{{ method.name }}
{% endfor %}
}
{% if container.initializers %}
/// MARK:- ignored required initializers{% endif %}
{% for initializer in container.initializers %}{{ container.accessibility }} required init({{ initializer.parameterSignature }}) {
fatalError("`DefaultImplCaller` class is only used for calling default implementation and can't be initialized on its own.")
}
{% endfor %}
{% for method in container.methods %}
private let _storage${{ forloop.counter }}${{ method.name }}: ({{ method.inputTypes }}) {% if method.isAsync %} async{% endif %} -> {{ method.returnType }}
{{ container.accessibility }} func {{ method.name|escapeReservedKeywords }}({{ method.parameterSignature }}) {{ method.returnSignature }} {
return {% if method.isAsync %} await{% endif %} _storage${{ forloop.counter }}${{ method.name }}({{ method.parameterNames }})
}
{% endfor %}
}
"""
}

View File

@ -1,48 +0,0 @@
import Foundation
extension Templates {
static let verificationProxy = """
{{ container.accessibility }} struct __VerificationProxy_{{ container.name }}: Cuckoo.VerificationProxy {
private let cuckoo_manager: Cuckoo.MockManager
private let callMatcher: Cuckoo.CallMatcher
private let sourceLocation: Cuckoo.SourceLocation
{{ container.accessibility }} init(manager: Cuckoo.MockManager, callMatcher: Cuckoo.CallMatcher, sourceLocation: Cuckoo.SourceLocation) {
self.cuckoo_manager = manager
self.callMatcher = callMatcher
self.sourceLocation = sourceLocation
}
{% for property in container.properties %}
{{ property.unavailablePlatformsCheck }}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
var {{property.name}}: Cuckoo.{{property.verifyType}}<{% if property.isReadOnly %}{{property.type|genericSafe}}{% else %}{{property.nonOptionalType|genericSafe}}{% endif %}> {
return .init(manager: cuckoo_manager, name: "{{property.name}}", callMatcher: callMatcher, sourceLocation: sourceLocation)
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
@discardableResult
func {{method.name|escapeReservedKeywords}}{{method.self|matchableGenericNames}}({{method.parameters|matchableParameterSignature}}) -> Cuckoo.__DoNotUse<({{method.inputTypes|genericSafe}}), {{method.returnType|genericSafe}}>{{method.self|matchableGenericWhereClause}} {
{{method.parameters|parameterMatchers}}
return cuckoo_manager.verify(
\"\"\"
{{method.fullyQualifiedName}}
\"\"\", callMatcher: callMatcher, parameterMatchers: matchers, sourceLocation: sourceLocation)
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
}
"""
}

View File

@ -1,603 +0,0 @@
import Foundation
import SourceKittenFramework
public struct Tokenizer {
private let file: File
private let source: String
private let debugMode: Bool
public init(sourceFile: File, debugMode: Bool) {
self.file = sourceFile
self.debugMode = debugMode
source = sourceFile.contents
}
public func tokenize() -> FileRepresentation {
do {
let structure = try Structure(file: file)
let declarations = tokenize(structure.dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
.flatMap { declaration -> [Token] in
guard let parent = declaration as? ParentToken else { return [declaration] }
return [parent] + parent.adoptAllYoungerGenerations()
}
let imports = tokenize(imports: declarations)
return FileRepresentation(sourceFile: file, declarations: declarations + imports)
} catch {
print("Error")
}
return FileRepresentation(sourceFile: file, declarations: [])
}
private func tokenize(_ representables: [SourceKitRepresentable]) -> [Token] {
return representables.compactMap(tokenize)
}
internal static let nameNotSet = "name not set"
internal static let unknownType = "unknown type"
private func tokenize(_ representable: SourceKitRepresentable) -> Token? {
guard let dictionary = representable as? [String: SourceKitRepresentable] else { return nil }
// Common fields
let name = dictionary[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
let kind = dictionary[Key.Kind.rawValue] as? String ?? Tokenizer.unknownType
// Inheritance
let inheritedTypes = dictionary[Key.InheritedTypes.rawValue] as? [SourceKitRepresentable] ?? []
let tokenizedInheritedTypes = inheritedTypes.compactMap { type -> InheritanceDeclaration in
guard let typeDict = type as? [String: SourceKitRepresentable] else {
return InheritanceDeclaration.empty
}
let name = typeDict[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
return InheritanceDeclaration(name: name)
}
// Optional fields
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let bodyRange = extractRange(from: dictionary, offset: .BodyOffset, length: .BodyLength)
// Attributes
let attributes = (NSOrderedSet(array: (dictionary[Key.Attributes.rawValue] as? [SourceKitRepresentable] ?? [])
.compactMap { attribute -> Attribute? in
guard let attribute = attribute as? [String: SourceKitRepresentable],
let stringKind = attribute[Key.Attribute.rawValue] as? String,
let kind = Attribute.Kind(rawValue: stringKind),
let attributeRange = extractRange(from: attribute, offset: .Offset, length: .Length) else { return nil }
let startIndex = source.utf8.index(source.utf8.startIndex, offsetBy: attributeRange.lowerBound)
let endIndex = source.utf8.index(source.utf8.startIndex, offsetBy: attributeRange.upperBound)
guard let text = String(source.utf8[startIndex..<endIndex]) else { return nil }
return Attribute(kind: kind, text: text)
}
).array as? [Attribute]) ?? []
guard !attributes.map({ $0.kind }).contains(.final) else {
if debugMode {
fputs("Cuckoo: Ignoring mocking of '\(name)' because it's marked `final`.\n", stdout)
}
return nil
}
let accessibility = (dictionary[Key.Accessibility.rawValue] as? String).flatMap { Accessibility(rawValue: $0) } ?? .Internal
let type: WrappableType?
if let stringType = dictionary[Key.TypeName.rawValue] as? String {
type = WrappableType(parsing: stringType)
} else {
type = nil
}
switch kind {
case Kinds.ProtocolDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self)
let genericParameters = subtokens.only(GenericParameter.self)
// FIXME: Remove when SourceKitten fixes the off-by-one error that includes the ending `>` in the last inherited type.
let fixedGenericParameters = fixSourceKittenLastGenericParameterBug(genericParameters)
return ProtocolDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children,
inheritedTypes: tokenizedInheritedTypes,
attributes: attributes,
genericParameters: fixedGenericParameters,
isNSObjectProtocol: false
)
case Kinds.ClassDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self).map { child -> Token in
if var property = child as? InstanceVariable {
property.overriding = true
return property
} else {
return child
}
}
let genericParameters = subtokens.only(GenericParameter.self)
// FIXME: Remove when SourceKitten fixes the off-by-one error that includes the ending `>` in the last inherited type.
let fixedGenericParameters = fixSourceKittenLastGenericParameterBug(genericParameters)
return ClassDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children,
inheritedTypes: tokenizedInheritedTypes,
attributes: attributes,
genericParameters: fixedGenericParameters
)
case Kinds.StructDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let children = subtokens.only(ContainerToken.self)
return StructDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
attributes: attributes,
children: children
)
case Kinds.ExtensionDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let children = subtokens.only(ContainerToken.self)
return ExtensionDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
attributes: attributes,
children: children
)
case Kinds.InstanceVariable.rawValue:
let setterAccessibility = (dictionary[Key.SetterAccessibility.rawValue] as? String).flatMap(Accessibility.init)
if String(source.utf8.dropFirst(range!.startIndex))?.takeUntil(occurence: name)?.trimmed.hasPrefix("let") == true {
return nil
}
let guessedType: WrappableType?
if type == nil {
guessedType = TypeGuesser.guessType(from: String(source.utf8[range!.startIndex..<range!.endIndex].drop(while: { $0 != "=" }).dropFirst()).trimmed).map { .type($0) }
} else {
guessedType = type
}
let effects: InstanceVariable.Effects
if let bodyRange = bodyRange {
effects = parseEffects(source: source.utf8[bodyRange])
} else {
effects = .init()
}
return InstanceVariable(
name: name,
type: guessedType ?? .type("__UnknownType"),
accessibility: accessibility,
setterAccessibility: setterAccessibility,
effects: effects,
range: range!,
nameRange: nameRange!,
overriding: false,
attributes: attributes
)
case Kinds.InstanceMethod.rawValue:
let genericParameters = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? []).only(GenericParameter.self)
let parameters = tokenize(methodName: name, parameters: dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let returnSignature: ReturnSignature
if let bodyRange = bodyRange {
returnSignature = parseReturnSignature(source: source.utf8[nameRange!.endIndex..<bodyRange.startIndex].takeUntil(occurence: "{")?.trimmed ?? "")
} else {
returnSignature = parseReturnSignature(source: source.utf8[nameRange!.endIndex..<range!.endIndex].trimmed)
}
// methods can specify an empty public name of a parameter without any private name - `fun(_: String)`
let namedParameters = parameters.enumerated().map { index, parameter -> MethodParameter in
if parameter.name == Tokenizer.nameNotSet {
var mutableParameter = parameter
mutableParameter.name = "parameter\(index)"
return mutableParameter
} else {
return parameter
}
}
// FIXME: Remove when SourceKit fixes the off-by-one error that includes the ending `>` in the last inherited type.
let fixedGenericParameters = fixSourceKittenLastGenericParameterBug(genericParameters)
// When bodyRange != nil, we need to create `ClassMethod` instead of `ProtocolMethod`
if let bodyRange = bodyRange {
return ClassMethod(
name: name,
accessibility: accessibility,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: namedParameters,
bodyRange: bodyRange,
attributes: attributes,
genericParameters: fixedGenericParameters
)
} else {
return ProtocolMethod(
name: name,
accessibility: accessibility,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: namedParameters,
attributes: attributes,
genericParameters: fixedGenericParameters
)
}
case Kinds.GenericParameter.rawValue:
return tokenize(parameterLabel: nil, parameter: representable)
case Kinds.AssociatedType.rawValue:
let regex = try! NSRegularExpression(pattern: "\\s*:\\s*([^\\s;\\/]+)")
guard let range = range else { return nil }
guard let inheritanceMatch = regex.firstMatch(
in: source,
range: NSMakeRange(range.startIndex, range.endIndex - range.startIndex)) else {
return GenericParameter(name: name, range: range, inheritedType: nil)
}
let inheritanceRange = inheritanceMatch.range(at: 1)
let fromIndex = source.index(source.startIndex, offsetBy: inheritanceRange.location)
let toIndex = source.index(fromIndex, offsetBy: inheritanceRange.length)
let inheritance = String(source[fromIndex..<toIndex])
let fullRange = range.lowerBound..<(range.upperBound + inheritanceMatch.range.length)
return GenericParameter(
name: name,
range: fullRange,
inheritedType: InheritanceDeclaration(name: inheritance)
)
default:
// Do not log anything, until the parser contains all known cases.
// stderrPrint("Unknown kind. Dictionary: \(dictionary) \(file.path ?? "")")
return nil
}
}
private func tokenize(methodName: String, parameters: [SourceKitRepresentable]) -> [MethodParameter] {
// Takes the string between `(` and `)`
let parameterNames = methodName.components(separatedBy: "(").last?.dropLast(1).map { "\($0)" }.joined(separator: "")
var parameterLabels: [String?] = parameterNames?.components(separatedBy: ":").map { $0 != "_" ? $0 : nil } ?? []
// Last element is not type.
parameterLabels = Array(parameterLabels.dropLast())
// Substructure can contain some other information after the parameters.
let filteredParameters = parameters.filter {
let dictionary = $0 as? [String: SourceKitRepresentable]
let kind = dictionary?[Key.Kind.rawValue] as? String ?? ""
return kind == Kinds.MethodParameter.rawValue
}
return zip(parameterLabels, filteredParameters).compactMap { tokenize(parameterLabel: $0, parameter: $1) as? MethodParameter }
}
private func tokenize(parameterLabel: String?, parameter: SourceKitRepresentable) -> Token? {
guard let dictionary = parameter as? [String: SourceKitRepresentable] else { return nil }
let name = dictionary[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
let kind = dictionary[Key.Kind.rawValue] as? String ?? Tokenizer.unknownType
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let type = dictionary[Key.TypeName.rawValue] as? String
switch kind {
case Kinds.MethodParameter.rawValue:
// separate `inout` from the type and remember that the parameter is inout
let type = type!
// we want to remove `inout` and remember it, but we don't want to affect a potential `inout` closure parameter
let inoutSeparatedType: String
let isInout: Bool
if let inoutRange = type.range(of: "inout ") {
if let closureParenIndex = type.firstIndex(of: "("), closureParenIndex < inoutRange.upperBound {
inoutSeparatedType = type
isInout = false
} else {
var mutableString = type
mutableString.removeSubrange(inoutRange)
inoutSeparatedType = mutableString
isInout = true
}
} else {
inoutSeparatedType = type
isInout = false
}
let wrappableType = WrappableType(parsing: inoutSeparatedType)
return MethodParameter(label: parameterLabel, name: name, type: wrappableType, range: range!, nameRange: nameRange, isInout: isInout)
case Kinds.GenericParameter.rawValue:
let inheritedTypeElement = (dictionary[Key.InheritedTypes.rawValue] as? [SourceKitRepresentable] ?? []).first
let inheritedType = (inheritedTypeElement as? [String: SourceKitRepresentable] ?? [:])[Key.Name.rawValue] as? String
let inheritanceDeclaration: InheritanceDeclaration?
if let inheritedType = inheritedType {
inheritanceDeclaration = .init(name: inheritedType)
} else {
inheritanceDeclaration = nil
}
return GenericParameter(name: name, range: range!, inheritedType: inheritanceDeclaration)
default:
stderrPrint("Unknown method parameter. Dictionary: \(dictionary) \(file.path ?? "")")
return nil
}
}
private func tokenize(imports otherTokens: [Token]) -> [Token] {
let rangesToIgnore: [CountableRange<Int>] = otherTokens.compactMap { token in
switch token {
case let container as ContainerToken:
return container.range
case let extensionToken as ExtensionDeclaration:
return extensionToken.range
default:
return nil
}
}
do {
let baseRegex = "(?:\\b|;)import(?:\\s|(?:\\/\\/.*\\n)|(?:\\/\\*.*\\*\\/))+"
let identifierRegex = "[^\\s;\\/]+"
let libraryImportRegex = baseRegex + "(\(identifierRegex))(?:\\n|(?:\\s)*;)"
let componentImportRegex = baseRegex + "(\(identifierRegex))\\s+(\(identifierRegex))\\.(\(identifierRegex))"
let libraryRegex = try NSRegularExpression(pattern: libraryImportRegex)
let componentRegex = try NSRegularExpression(pattern: componentImportRegex)
let librariesRange = NSRange(location: 0, length: source.count)
let libraries = libraryRegex.matches(in: source, range: librariesRange)
.filter { result in
rangesToIgnore.filter { $0 ~= result.range.location }.isEmpty
}
.map { result -> Import in
let range = result.range.location..<(result.range.location + result.range.length)
let library = source.stringMatch(from: result, at: 1)
return Import(range: range, importee: .library(name: library))
}
let components = componentRegex.matches(in: source, range: NSRange(location: 0, length: source.count))
.filter { result in
rangesToIgnore.filter { $0 ~= result.range.location }.isEmpty
}
.map { result -> Import in
let componentRange = result.range(at: 1)
let componentType = componentRange.location == NSNotFound ? nil : source[componentRange]
let library = source[result.range(at: 2)]
let component = source[result.range(at: 3)]
let range = result.range.location..<(result.range.location + result.range.length)
return Import(range: range, importee: .component(componentType: componentType, library: library, name: component))
}
return libraries + components
} catch let error as NSError {
fatalError("Invalid regex:" + error.description)
}
}
private func getReturnType(source: String, index: inout String.Index) -> String {
var returnType = ""
var afterArrow = true
var parenLevel = 0
while index != source.endIndex {
let character = source[index]
switch character {
case "(", "<", "[":
parenLevel += 1
returnType.append(character)
index = source.index(after: index)
case ")", ">", "]":
parenLevel -= 1
returnType.append(character)
index = source.index(after: index)
case "-":
index = source.index(after: index)
// just a little sanity check
guard source[index] == ">" else { fatalError("Uhh, what.") }
index = source.index(after: index)
returnType.append(" -> ")
afterArrow = true
case " ":
index = source.index(after: index)
returnType.append(character)
case "w":
let previousCharacter = source[source.index(before: index)]
guard parenLevel == 0 && !afterArrow && previousCharacter == " " else {
returnType.append(character)
index = source.index(after: index)
continue
}
// we reached the "where" clause
return returnType
default:
afterArrow = false
returnType.append(character)
index = source.index(after: index)
}
}
return returnType
}
/// - returns: the where constraints parsed from the where clause
private func parseWhereClause(source: String, index: inout String.Index) -> [String] {
var whereConstraints = [] as [String]
var currentConstraint = ""
var parenLevel = 0
while index != source.endIndex {
let character = source[index]
switch character {
case "(", "<", "[":
parenLevel += 1
currentConstraint.append(character)
case ")", ">", "]":
parenLevel -= 1
currentConstraint.append(character)
case "," where parenLevel == 0:
currentConstraint = currentConstraint.trimmed
whereConstraints.append(currentConstraint)
currentConstraint = ""
default:
currentConstraint.append(character)
}
index = source.index(after: index)
}
if !currentConstraint.isEmpty {
currentConstraint = currentConstraint.trimmed
whereConstraints.append(currentConstraint)
}
return whereConstraints
}
/// - parameter source: A trimmed string containing only the method return signature excluding the trailing brace
/// - returns: ReturnSignature structure containing the parsed throwString, return type, and where constraints
private func parseReturnSignature(source: String) -> ReturnSignature {
var isAsync = false
var throwString = nil as String?
var returnType: WrappableType?
var whereConstraints = [] as [String]
var index = source.startIndex
parseLoop: while index != source.endIndex {
let character = source[index]
switch character {
case "a":
isAsync = true
let asyncString = "async"
index = source.index(index, offsetBy: asyncString.count)
continue
case "r" where returnType == nil:
throwString = "rethrows"
index = source.index(index, offsetBy: throwString!.count)
continue
case "t" where returnType == nil:
throwString = "throws"
index = source.index(index, offsetBy: throwString!.count)
continue
case "w":
index = source.index(index, offsetBy: "where".count)
whereConstraints = parseWhereClause(source: source, index: &index)
// the where clause is the last thing in method signature, so we'll just stop the parsing
break parseLoop
case "-":
index = source.index(after: index)
guard source[index] == ">" else { fatalError("Uhh, what.") }
index = source.index(after: index)
returnType = WrappableType(parsing: getReturnType(source: source, index: &index).trimmed)
continue
default:
break
}
index = source.index(after: index)
}
return ReturnSignature(isAsync: isAsync, throwString: throwString, returnType: returnType ?? WrappableType.type("Void"), whereConstraints: whereConstraints)
}
private func parseEffects(source: String) -> InstanceVariable.Effects {
var effects = InstanceVariable.Effects()
let trimmed = source.drop(while: { $0.isWhitespace })
guard trimmed.hasPrefix("get") else { return effects }
let afterGet = trimmed.dropFirst("get".count).drop(while: { $0.isWhitespace })
var index = afterGet.startIndex
parseLoop: while index != afterGet.endIndex {
let character = afterGet[index]
switch character {
case "a":
effects.isAsync = true
index = source.index(index, offsetBy: "async".count)
case "t":
effects.isThrowing = true
index = source.index(index, offsetBy: "throws".count)
case let c where c.isWhitespace:
break
default:
break parseLoop
}
index = source.index(after: index)
}
return effects
}
// FIXME: Remove when SourceKitten fixes the off-by-one error that includes the ending `>` in the last inherited type.
private func fixSourceKittenLastGenericParameterBug(_ genericParameters: [GenericParameter]) -> [GenericParameter] {
let fixedGenericParameters: [GenericParameter]
if let lastGenericParameter = genericParameters.last,
let inheritedType = lastGenericParameter.inheritedType,
inheritedType.name.hasSuffix(">>") == true {
fixedGenericParameters = genericParameters.dropLast() + [
GenericParameter(
name: lastGenericParameter.name,
range: lastGenericParameter.range.lowerBound..<lastGenericParameter.range.upperBound - 1,
inheritedType: InheritanceDeclaration(name: String(inheritedType.name.dropLast()))
)
]
} else {
fixedGenericParameters = genericParameters
}
return fixedGenericParameters
}
}
extension String {
subscript(range: NSRange) -> String {
let fromIndex = self.index(self.startIndex, offsetBy: range.location)
let toIndex = self.index(fromIndex, offsetBy: range.length)
return String(self[fromIndex..<toIndex])
}
}
extension String {
func stringMatch(from match: NSTextCheckingResult, at range: Int = 0) -> String {
let matchRange = match.range(at: range)
let fromIndex = index(startIndex, offsetBy: matchRange.location)
let toIndex = index(fromIndex, offsetBy: matchRange.length)
return String(self[fromIndex..<toIndex])
}
func removing(match: NSTextCheckingResult, at range: Int = 0) -> String {
let matchRange = match.range(at: range)
let fromIndex = index(startIndex, offsetBy: matchRange.location)
let toIndex = index(fromIndex, offsetBy: matchRange.length)
var mutableString = self
mutableString.removeSubrange(fromIndex..<toIndex)
return mutableString
}
}

View File

@ -1,48 +0,0 @@
public enum Accessibility: String {
case Open = "source.lang.swift.accessibility.open"
case Public = "source.lang.swift.accessibility.public"
case Internal = "source.lang.swift.accessibility.internal"
case Private = "source.lang.swift.accessibility.private"
case FilePrivate = "source.lang.swift.accessibility.fileprivate"
public var sourceName: String {
switch self {
case .Open:
fallthrough
case .Public:
return "public"
case .Internal:
return ""
case .Private:
return "private"
case .FilePrivate:
return "fileprivate"
}
}
public var isAccessible: Bool {
return self != .Private && self != .FilePrivate
}
}
extension Accessibility: Comparable {
/// How open is this accessibility. The higher number the more accessible.
private var openness: Int {
switch self {
case .Open:
return 4
case .Public:
return 3
case .Internal:
return 2
case .FilePrivate:
return 1
case .Private:
return 0
}
}
public static func < (lhs: Accessibility, rhs: Accessibility) -> Bool {
return lhs.openness < rhs.openness
}
}

View File

@ -1,60 +0,0 @@
import Foundation
public struct Attribute: Hashable {
public enum Kind: String, Hashable {
case objc = "source.decl.attribute.objc"
case optional = "source.decl.attribute.optional"
case lazy = "source.decl.attribute.lazy"
case required = "source.decl.attribute.required"
case override = "source.decl.attribute.override"
case convenience = "source.decl.attribute.convenience"
case weak = "source.decl.attribute.weak"
case ibAction = "source.decl.attribute.ibaction"
case ibOutlet = "source.decl.attribute.iboutlet"
case available = "source.decl.attribute.available"
case final = "source.decl.attribute.final"
}
public var kind: Kind
public var text: String
public var isSupported: Bool {
switch (kind) {
case .objc, .optional, .lazy, .required, .override, .convenience, .weak, .ibAction, .ibOutlet, .final:
return false
case .available:
return true
}
}
public var unavailablePlatform: String? {
guard kind == .available,
text.hasPrefix("@available(") else {
return nil
}
let parameters = text
.dropFirst("@available(".count)
.dropLast()
.split(separator: ",")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
guard parameters.count >= 2,
parameters[1] == "unavailable" else {
return nil
}
return String(parameters[0])
}
}
extension Attribute: Token {
public func isEqual(to other: Token) -> Bool {
guard let otherAttribute = other as? Attribute else { return false }
return self.kind == otherAttribute.kind && self.text == otherAttribute.text
}
public func serialize() -> [String : Any] {
return ["text": text]
}
}

View File

@ -1,5 +0,0 @@
import Foundation
public protocol ChildToken: Token {
var parent: Reference<ParentToken>? { get set }
}

View File

@ -1,37 +0,0 @@
public struct ClassDeclaration: ContainerToken, HasAccessibility {
public let implementation: Bool = true
public var name: String
public var parent: Reference<ParentToken>?
public var accessibility: Accessibility
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var bodyRange: CountableRange<Int>
public var initializers: [Initializer]
public var children: [Token]
public var inheritedTypes: [InheritanceDeclaration]
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var hasNoArgInit: Bool {
return initializers.filter { $0.parameters.isEmpty }.isEmpty
}
public func replace(children tokens: [Token]) -> ClassDeclaration {
return ClassDeclaration(
name: self.name,
parent: self.parent,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
bodyRange: self.bodyRange,
initializers: self.initializers,
children: tokens,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes,
genericParameters: self.genericParameters)
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? ClassDeclaration else { return false }
return self.name == other.name
}
}

View File

@ -1,17 +0,0 @@
public struct ClassMethod: Method {
public var name: String
public var accessibility: Accessibility
public var returnSignature: ReturnSignature
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var parameters: [MethodParameter]
public var bodyRange: CountableRange<Int>
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isOptional: Bool {
return false
}
public var isOverriding: Bool {
return true
}
}

View File

@ -1,63 +0,0 @@
public protocol ContainerToken: ParentToken, ChildToken {
var initializers: [Initializer] { get }
var implementation: Bool { get }
var inheritedTypes: [InheritanceDeclaration] { get }
var genericParameters: [GenericParameter] { get }
}
extension ContainerToken {
public func serialize() -> [String : Any] {
func withAdjustedAccessibility(token: Token & HasAccessibility) -> Token & HasAccessibility {
// We only want to adjust tokens that are accessible and lower than the enclosing type
guard token.accessibility.isAccessible && token.accessibility < accessibility else { return token }
var mutableToken = token
mutableToken.accessibility = accessibility
return mutableToken
}
let accessibilityAdjustedChildren = children.map { child -> Token in
guard let childWithAccessibility = child as? HasAccessibility & Token else { return child }
return withAdjustedAccessibility(token: childWithAccessibility)
}
let properties = accessibilityAdjustedChildren.compactMap { $0 as? InstanceVariable }
.filter { $0.accessibility.isAccessible }
.map { $0.serializeWithType() }
let methods = accessibilityAdjustedChildren.compactMap { $0 as? Method }
.filter { $0.accessibility.isAccessible && !$0.isInit && !$0.isDeinit }
.map { $0.serializeWithType() }
let initializers = accessibilityAdjustedChildren.compactMap { $0 as? Method }
.filter { $0.accessibility.isAccessible && $0.isInit && !$0.isDeinit }
.map { $0.serializeWithType() }
let genericParametersString = genericParameters.map { $0.description }.joined(separator: ", ")
let genericArgumentsString = genericParameters.map { $0.name }.joined(separator: ", ")
let genericProtocolIdentity = genericParameters.map { "\(Templates.staticGenericParameter).\($0.name) == \($0.name)" }.joined(separator: ", ")
let isGeneric = !genericParameters.isEmpty
return [
"name": name,
"accessibility": accessibility.sourceName,
"isAccessible": accessibility.isAccessible,
"hasParent": parent != nil,
"parentFullyQualifiedName": parent?.fullyQualifiedName ?? "",
"children": accessibilityAdjustedChildren.map { $0.serializeWithType() },
"properties": properties,
"methods": methods,
"initializers": implementation ? [] : initializers,
"isImplementation": implementation,
"mockName": "Mock\(name)",
"inheritedTypes": inheritedTypes,
"attributes": attributes.filter { $0.isSupported },
"hasUnavailablePlatforms": hasUnavailablePlatforms,
"unavailablePlatformsCheck": unavailablePlatformsCheck,
"isGeneric": isGeneric,
"genericParameters": isGeneric ? "<\(genericParametersString)>" : "",
"genericArguments": isGeneric ? "<\(genericArgumentsString)>" : "",
"genericProtocolIdentity": genericProtocolIdentity,
"isNSObjectProtocol": (self as? ProtocolDeclaration)?.isNSObjectProtocol ?? false
]
}
}

View File

@ -1,15 +0,0 @@
public struct ExtensionDeclaration: ParentToken {
// TODO Implement support for extensions
public let name: String
public var accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public var attributes: [Attribute]
public let children: [Token]
public func isEqual(to other: Token) -> Bool {
guard let other = other as? ExtensionDeclaration else { return false }
return self.range == other.range
}
}

View File

@ -1,71 +0,0 @@
import SourceKittenFramework
public struct FileRepresentation {
public let sourceFile: File
public let declarations: [Token]
public init(sourceFile: File, declarations: [Token]) {
self.sourceFile = sourceFile
self.declarations = declarations
}
}
public extension FileRepresentation {
func mergeInheritance(with files: [FileRepresentation]) -> FileRepresentation {
let tokens = self.declarations.reduce([Token]()) { list, token in
let mergeToken = token.mergeInheritance(with: files)
return list + [mergeToken]
}
return FileRepresentation(sourceFile: self.sourceFile, declarations: tokens)
}
func inheritNSObject(subjects: [ProtocolDeclaration]) -> FileRepresentation {
FileRepresentation(sourceFile: self.sourceFile, declarations: self.declarations.map { $0.inheritNSObject(subjects: subjects) })
}
}
extension Token {
func mergeInheritance(with files: [FileRepresentation]) -> Token {
guard let typeToken = self as? ContainerToken else {
return self
}
let inheritedRepresentations: [Token] = typeToken.inheritedTypes
.compactMap { Self.findToken(forClassOrProtocol: $0.name, in: files) }
.compactMap { $0.mergeInheritance(with: files) }
// Merge super declarations
let mergedTokens = inheritedRepresentations.filter { $0.isClassOrProtocolDeclaration }
.map { $0 as! ContainerToken }
.flatMap { $0.children }
.reduce(typeToken.children) { tokens, inheritedToken in
if tokens.contains(where: { $0 == inheritedToken }) {
return tokens
}
return tokens + [inheritedToken]
}
switch typeToken {
case let classToken as ClassDeclaration:
return classToken.replace(children: mergedTokens)
case let protocolToken as ProtocolDeclaration:
return protocolToken.replace(children: mergedTokens)
default:
return typeToken
}
}
func inheritNSObject(subjects: [ProtocolDeclaration]) -> Token {
guard let protocolToken = self as? ProtocolDeclaration else {
return self
}
return subjects.contains { $0.name == protocolToken.name } ? protocolToken.replace(isNSObjectProtocol: true) : self
}
static func findToken(forClassOrProtocol name: String, in files: [FileRepresentation]) -> Token? {
return files.flatMap { $0.declarations }
.filter { $0.isClassOrProtocolDeclaration }
.map { $0 as! ContainerToken }
.first { $0.name == name }
}
}

View File

@ -1,24 +0,0 @@
import Foundation
public struct GenericParameter: Token {
public let name: String
public let range: CountableRange<Int>
public let inheritedType: InheritanceDeclaration?
public var description: String {
let hasInheritedType = inheritedType != nil
return "\(name)\(hasInheritedType ? ": " : "")\(inheritedType?.name ?? "")"
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? GenericParameter else { return false }
return self.name == other.name && self.range == other.range && self.inheritedType?.name == other.inheritedType?.name
}
public func serialize() -> [String : Any] {
return [
"name": name,
"inheritedType": inheritedType,
]
}
}

View File

@ -1,5 +0,0 @@
import Foundation
public protocol HasAccessibility {
var accessibility: Accessibility { get set }
}

View File

@ -1,19 +0,0 @@
import Foundation
public protocol HasAttributes {
var attributes: [Attribute] { get }
}
extension HasAttributes {
private var unavailablePlatforms: [String] {
return attributes.lazy.compactMap { $0.unavailablePlatform }
}
var hasUnavailablePlatforms: Bool {
return !unavailablePlatforms.isEmpty
}
var unavailablePlatformsCheck: String {
return hasUnavailablePlatforms ? "#if !os(\(unavailablePlatforms.joined(separator: ") && !os(")))" : ""
}
}

View File

@ -1,30 +0,0 @@
public struct Import: Token {
public enum Importee: CustomStringConvertible {
case library(name: String)
case component(componentType: String?, library: String, name: String)
public var description: String {
switch self {
case .library(let name):
return name
case .component(let componentType, let library, let name):
return [componentType, "\(library).\(name)"].compactMap { $0 }.joined(separator: " ")
}
}
}
public let range: CountableRange<Int>
public let importee: Importee
public func isEqual(to other: Token) -> Bool {
guard let other = other as? Import, self.range == other.range else { return false }
switch (self.importee, other.importee) {
case (.library(let lhsName), .library(let rhsName)):
return lhsName == rhsName
case (.component(let lhsImportType, let lhsLibrary, let lhsName), .component(let rhsImportType, let rhsLibrary, let rhsName)):
return lhsImportType == rhsImportType && lhsLibrary == rhsLibrary && lhsName == rhsName
default:
return false
}
}
}

View File

@ -1,9 +0,0 @@
public struct InheritanceDeclaration: Token {
public static let empty = InheritanceDeclaration(name: Tokenizer.nameNotSet)
public let name: String
public func isEqual(to other: Token) -> Bool {
guard let other = other as? InheritanceDeclaration else { return false }
return self.name == other.name
}
}

View File

@ -1,17 +0,0 @@
public struct Initializer: Method, HasAccessibility {
public var name: String
public var accessibility: Accessibility
public var returnType: WrappableType
public var returnSignature: ReturnSignature
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var parameters: [MethodParameter]
public var isOverriding: Bool
public var required: Bool
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isOptional: Bool {
return false
}
}

View File

@ -1,51 +0,0 @@
public struct InstanceVariable: Token, HasAccessibility, HasAttributes {
public struct Effects {
public var isThrowing = false
public var isAsync = false
}
public var name: String
public var type: WrappableType
public var accessibility: Accessibility
public var setterAccessibility: Accessibility?
public var effects: Effects
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var overriding: Bool
public var attributes: [Attribute]
public var readOnly: Bool {
if let setterAccessibility = setterAccessibility {
return !setterAccessibility.isAccessible
} else {
return true
}
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? InstanceVariable else { return false }
return self.name == other.name
}
public func serialize() -> [String : Any] {
let readOnlyVerifyString = readOnly ? "ReadOnly" : ""
let readOnlyStubString = effects.isThrowing ? "" : readOnlyVerifyString
let optionalString = type.isOptional && !readOnly ? "Optional" : ""
let throwingString = effects.isThrowing ? "Throwing" : ""
return [
"name": name,
"type": type.sugarized,
"nonOptionalType": type.unoptionaled.sugarized,
"accessibility": accessibility.sourceName,
"isReadOnly": readOnly,
"isAsync": effects.isAsync,
"isThrowing": effects.isThrowing,
"stubType": (overriding ? "Class" : "Protocol") + "ToBeStubbed\(readOnlyStubString)\(optionalString)\(throwingString)Property",
"verifyType": "Verify\(readOnlyVerifyString)\(optionalString)Property",
"attributes": attributes.filter { $0.isSupported },
"hasUnavailablePlatforms": hasUnavailablePlatforms,
"unavailablePlatformsCheck": unavailablePlatformsCheck,
]
}
}

View File

@ -1,158 +0,0 @@
import Foundation
public protocol Method: Token, HasAccessibility, HasAttributes {
var name: String { get }
var returnSignature: ReturnSignature { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var parameters: [MethodParameter] { get }
var isOptional: Bool { get }
var isOverriding: Bool { get }
var hasClosureParams: Bool { get }
var hasOptionalParams: Bool { get }
var genericParameters: [GenericParameter] { get }
}
public extension Method {
var rawName: String {
return name.takeUntil(occurence: "(") ?? ""
}
var isInit: Bool {
return rawName == "init"
}
var isDeinit: Bool {
return rawName == "deinit"
}
var fullyQualifiedName: String {
let parameterTypes = parameters.map { ($0.isInout ? "inout " : "") + $0.type.sugarized }
let nameParts = name.components(separatedBy: ":")
let lastNamePart = nameParts.last ?? ""
let returnSignatureDescription = returnSignature.description
let returnSignatureString = returnSignatureDescription.isEmpty ? "" : " \(returnSignatureDescription)"
return zip(nameParts.dropLast(), parameterTypes)
.map { $0 + ": " + $1 }
.joined(separator: ", ") + lastNamePart + returnSignatureString
}
var isAsync: Bool {
return returnSignature.isAsync
}
var isThrowing: Bool {
guard let throwType = returnSignature.throwType else { return false }
return throwType.isThrowing || throwType.isRethrowing
}
var returnType: WrappableType {
return returnSignature.returnType
}
var hasClosureParams: Bool {
return parameters.contains { $0.isClosure }
}
var hasOptionalParams: Bool {
return parameters.contains { $0.isOptional }
}
func isEqual(to other: Token) -> Bool {
guard let other = other as? Method else { return false }
return self.name == other.name && self.parameters == other.parameters && self.returnType == other.returnType
}
func serialize() -> [String : Any] {
let call = parameters.map {
let name = escapeReservedKeywords(for: $0.name)
let referencedName = "\($0.isInout ? "&" : "")\(name)\($0.isAutoClosure ? "()" : "")"
if let label = $0.label {
return "\(label): \(referencedName)"
} else {
return referencedName
}
}.joined(separator: ", ")
let stubFunctionPrefix = isOverriding ? "Class" : "Protocol"
let returnString = returnType.sugarized == "Void" ? "NoReturn" : ""
let throwingString = isThrowing ? "Throwing" : ""
let stubFunction = "Cuckoo.\(stubFunctionPrefix)Stub\(returnString)\(throwingString)Function"
let escapingParameterNames = parameters.map { parameter in
if parameter.isClosure && !parameter.isEscaping {
let parameterCount = parameter.closureParamCount
let parameterSignature = parameterCount > 0 ? (1...parameterCount).map { _ in "_" }.joined(separator: ", ") : "()"
// FIXME: Instead of parsing the closure return type here, Tokenizer should do it and pass the information in a data structure
let returnSignature: String
let closureReturnType = extractClosureReturnType(parameter: parameter.type.sugarized)
if let closureReturnType = closureReturnType, !closureReturnType.isEmpty && closureReturnType != "Void" {
returnSignature = " -> " + closureReturnType
} else {
returnSignature = ""
}
return "{ \(parameterSignature)\(returnSignature) in fatalError(\"This is a stub! It's not supposed to be called!\") }"
} else {
return escapeReservedKeywords(for: parameter.name)
}
}.joined(separator: ", ")
let genericParametersString = genericParameters.map { $0.description }.joined(separator: ", ")
let isGeneric = !genericParameters.isEmpty
return [
"self": self,
"name": rawName,
"accessibility": accessibility.sourceName,
"returnSignature": returnSignature.description,
"parameters": parameters,
"parameterNames": parameters.map { escapeReservedKeywords(for: $0.name) }.joined(separator: ", "),
"escapingParameterNames": escapingParameterNames,
"isInit": isInit,
"returnType": returnType.explicitOptionalOnly.sugarized,
"isAsync": isAsync,
"isThrowing": isThrowing,
"throwType": returnSignature.throwType?.description ?? "",
"fullyQualifiedName": fullyQualifiedName,
"call": call,
"isOverriding": isOverriding,
"parameterSignature": parameters.map { "\($0.labelAndName): \($0.isInout ? "inout " : "")\($0.type)" }.joined(separator: ", "),
"parameterSignatureWithoutNames": parameters.map { "\($0.name): \($0.type)" }.joined(separator: ", "),
"argumentSignature": parameters.map { $0.type.description }.joined(separator: ", "),
"stubFunction": stubFunction,
"inputTypes": parameters.map { $0.typeWithoutAttributes }.joined(separator: ", "),
"isOptional": isOptional,
"hasClosureParams": hasClosureParams,
"hasOptionalParams": hasOptionalParams,
"attributes": attributes.filter { $0.isSupported },
"hasUnavailablePlatforms": hasUnavailablePlatforms,
"unavailablePlatformsCheck": unavailablePlatformsCheck,
"genericParameters": isGeneric ? "<\(genericParametersString)>" : "",
]
}
private func extractClosureReturnType(parameter: String) -> String? {
var parenLevel = 0
for i in 0..<parameter.count {
let index = parameter.index(parameter.startIndex, offsetBy: i)
let character = parameter[index]
if character == "(" {
parenLevel += 1
} else if character == ")" {
parenLevel -= 1
if parenLevel == 0 {
let returnSignature = String(parameter[parameter.index(after: index)..<parameter.endIndex])
let regex = try! NSRegularExpression(pattern: "\\s*->\\s*(.*)\\s*")
guard let result = regex.matches(in: returnSignature, range: NSRange(location: 0, length: returnSignature.count)).first else { return nil }
return returnSignature[result.range(at: 1)]
}
}
}
return nil
}
}

View File

@ -1,83 +0,0 @@
public struct MethodParameter: Token, Equatable {
public var label: String?
public var name: String
public var type: WrappableType
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>?
public var isInout: Bool
public var labelAndName: String {
if let label = label {
return label != name ? "\(label) \(name)" : name
} else {
return "_ \(name)"
}
}
public var typeWithoutAttributes: String {
return type.withoutAttributes.sugarized.trimmed
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? MethodParameter else { return false }
return label == other.label && type == other.type
}
public var isClosure: Bool {
return typeWithoutAttributes.hasPrefix("(") && typeWithoutAttributes.range(of: "->") != nil
}
public var isAutoClosure: Bool {
type.containsAttribute(named: "@autoclosure")
}
public var isOptional: Bool {
return type.isOptional
}
public var closureParamCount: Int {
// make sure that the parameter is a closure and that it's not just an empty `() -> ...` closure
guard isClosure && !"^\\s*\\(\\s*\\)".regexMatches(typeWithoutAttributes) else { return 0 }
var parenLevel = 0
var parameterCount = 1
for character in typeWithoutAttributes {
switch character {
case "(", "<":
parenLevel += 1
case ")", ">":
parenLevel -= 1
case ",":
parameterCount += parenLevel == 1 ? 1 : 0
default:
break
}
if parenLevel == 0 {
break
}
}
return parameterCount
}
public var isEscaping: Bool {
return isClosure && (type.containsAttribute(named: "@escaping") || type.isOptional)
}
public func serialize() -> [String : Any] {
return [
"label": label ?? "",
"name": name,
"type": type,
"labelAndName": labelAndName,
"typeWithoutAttributes": typeWithoutAttributes,
"isClosure": isClosure,
"isOptional": isOptional,
"isEscaping": isEscaping
]
}
}
public func ==(lhs: MethodParameter, rhs: MethodParameter) -> Bool {
return lhs.isEqual(to: rhs)
}

View File

@ -1,43 +0,0 @@
import Foundation
public protocol ParentToken: Token, HasAccessibility, HasAttributes {
var name: String { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var bodyRange: CountableRange<Int> { get }
var children: [Token] { get }
}
extension ParentToken {
var fullyQualifiedName: String {
var names = [name]
var parent: ParentToken? = (self as? ChildToken)?.parent?.value
while let p = parent {
names.insert(p.name, at: 0)
parent = (p as? ChildToken)?.parent?.value
}
return names.joined(separator: ".")
}
var areAllHierarchiesAccessible: Bool {
guard accessibility.isAccessible else { return false }
var parent: ParentToken? = (self as? ChildToken)?.parent?.value
while let p = parent {
guard p.accessibility.isAccessible else { return false }
parent = (p as? ChildToken)?.parent?.value
}
return true
}
func adoptAllYoungerGenerations() -> [ParentToken] {
let parentReference: Reference<ParentToken> = Reference(value: self)
return children
.compactMap { child -> ParentToken? in
guard var c = child as? ContainerToken else { return nil }
c.parent = parentReference
return c
}
.flatMap { [$0] + $0.adoptAllYoungerGenerations() }
}
}

View File

@ -1,54 +0,0 @@
public struct ProtocolDeclaration: ContainerToken, HasAccessibility {
public let implementation: Bool = false
public var name: String
public var parent: Reference<ParentToken>?
public var accessibility: Accessibility
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var bodyRange: CountableRange<Int>
public var initializers: [Initializer]
public var children: [Token]
public var inheritedTypes: [InheritanceDeclaration]
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isNSObjectProtocol: Bool
public func replace(children tokens: [Token]) -> ProtocolDeclaration {
return ProtocolDeclaration(
name: self.name,
parent: self.parent,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
bodyRange: self.bodyRange,
initializers: self.initializers,
children: tokens,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes,
genericParameters: self.genericParameters,
isNSObjectProtocol: self.isNSObjectProtocol
)
}
public func replace(isNSObjectProtocol: Bool) -> ProtocolDeclaration {
ProtocolDeclaration(
name: self.name,
parent: self.parent,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
bodyRange: self.bodyRange,
initializers: self.initializers,
children: self.children,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes,
genericParameters: self.genericParameters,
isNSObjectProtocol: isNSObjectProtocol
)
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? ProtocolDeclaration else { return false }
return self.name == other.name
}
}

View File

@ -1,17 +0,0 @@
public struct ProtocolMethod: Method {
public var name: String
public var accessibility: Accessibility
public var returnSignature: ReturnSignature
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var parameters: [MethodParameter]
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isOptional: Bool {
return attributes.map { $0.kind }.contains(.optional)
}
public var isOverriding: Bool {
return false
}
}

View File

@ -1,29 +0,0 @@
import Foundation
public struct ReturnSignature {
public var isAsync: Bool
public var throwType: ThrowType?
public var returnType: WrappableType
public var whereConstraints: [String]
public init(isAsync: Bool, throwString: String?, returnType: WrappableType, whereConstraints: [String]) {
self.isAsync = isAsync
if let throwString = throwString {
throwType = ThrowType(string: throwString)
} else {
throwType = nil
}
self.returnType = returnType
self.whereConstraints = whereConstraints
}
}
extension ReturnSignature: CustomStringConvertible {
public var description: String {
let asyncString = isAsync ? "async" : nil
let trimmedReturnType = returnType.explicitOptionalOnly.sugarized.trimmed
let returnString = trimmedReturnType.isEmpty || trimmedReturnType == "Void" ? nil : "-> \(returnType)"
let whereString = whereConstraints.isEmpty ? nil : "where \(whereConstraints.joined(separator: ", "))"
return [asyncString, throwType?.description, returnString, whereString].compactMap { $0 }.joined(separator: " ")
}
}

View File

@ -1,17 +0,0 @@
import Foundation
public struct StructDeclaration: ParentToken {
// NOTE: Purely for supporting nested classes, could be any generic parent (like extensions)
public let name: String
public var accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public var attributes: [Attribute]
public let children: [Token]
public func isEqual(to other: Token) -> Bool {
guard let other = other as? StructDeclaration else { return false }
return self.name == other.name
}
}

View File

@ -1,31 +0,0 @@
public enum ThrowType: CustomStringConvertible {
case throwing
case rethrowing
public init?(string: String) {
if string.trimmed.hasPrefix("throws") {
self = .throwing
} else if string.trimmed.hasPrefix("rethrows") {
self = .rethrowing
} else {
return nil
}
}
public var isThrowing: Bool {
return self == .throwing
}
public var isRethrowing: Bool {
return self == .rethrowing
}
public var description: String {
switch self {
case .throwing:
return "throws"
case .rethrowing:
return "rethrows"
}
}
}

View File

@ -1,29 +0,0 @@
public protocol Token {
func isEqual(to other: Token) -> Bool
func serialize() -> [String: Any]
}
public func ==(rhs: Token, lhs: Token) -> Bool {
return rhs.isEqual(to: lhs)
}
public extension Token {
func serialize() -> [String: Any] {
return [:]
}
func serializeWithType() -> [String: Any] {
var serialized = serialize()
serialized["@type"] = "\(type(of: self))"
return serialized
}
var isClassOrProtocolDeclaration: Bool {
return self is ProtocolDeclaration || self is ClassDeclaration
}
var isInheritanceDeclaration: Bool {
return self is InheritanceDeclaration
}
}

View File

@ -1,148 +0,0 @@
public enum WrappableType {
indirect case optional(WrappableType)
indirect case implicitlyUnwrappedOptional(WrappableType)
indirect case attributed(WrappableType, attributes: [String])
case type(String)
public var sugarized: String {
switch self {
case .optional(let wrapped):
return "\(wrapped.sugarized)?"
case .implicitlyUnwrappedOptional(let wrapped):
return "\(wrapped.sugarized)!"
case .attributed(let wrapped, let attributes):
return "\(attributes.joined(separator: " ")) \(wrapped.sugarized)"
case .type(let type):
return type
}
}
public var desugarized: String {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return "Optional<\(wrapped.desugarized)>"
case .attributed(let wrapped, let attributes):
return "\(attributes.joined(separator: " ")) \(wrapped.desugarized)"
case .type(let type):
return type
}
}
public var explicitOptionalOnly: WrappableType {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return .optional(wrapped.explicitOptionalOnly)
case .attributed(let wrapped, let attributes):
return .attributed(wrapped.explicitOptionalOnly, attributes: attributes)
case .type:
return self
}
}
public var unoptionaled: WrappableType {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return wrapped.unoptionaled
case .attributed(let wrapped, let attributes):
return .attributed(wrapped.unoptionaled, attributes: attributes)
case .type:
return self
}
}
public var unwrapped: WrappableType {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return wrapped
case .attributed(let wrapped, let attributes):
return .attributed(wrapped.unwrapped, attributes: attributes)
case .type:
return self
}
}
public var withoutAttributes: WrappableType {
switch self {
case .optional(let wrapped):
return .optional(wrapped.withoutAttributes)
case .implicitlyUnwrappedOptional(let wrapped):
return .implicitlyUnwrappedOptional(wrapped.withoutAttributes)
case .attributed(let wrapped, _):
return wrapped
case .type:
return self
}
}
public var isOptional: Bool {
switch self {
case .optional, .implicitlyUnwrappedOptional:
return true
case .attributed(let wrapped, _):
return wrapped.isOptional
case .type:
return false
}
}
public init(parsing value: String) {
let trimmedValue = value.trimmed
let optionalPrefix = "Optional<"
if trimmedValue.hasPrefix("@") {
let (attributes, resultString) = ["@autoclosure", "@escaping", "@noescape"]
.reduce(([], trimmedValue)) { acc, next -> ([String], String) in
var (attributes, resultString) = acc
guard let range = resultString.range(of: next) else { return acc }
resultString.removeSubrange(range)
attributes.append(next)
return (attributes, resultString)
}
self = .attributed(WrappableType(parsing: resultString), attributes: attributes)
} else if trimmedValue.hasSuffix("?") {
if trimmedValue.contains("->") && !trimmedValue.hasSuffix(")?") {
self = .type(trimmedValue)
} else {
self = .optional(WrappableType(parsing: String(trimmedValue.dropLast())))
}
} else if trimmedValue.hasPrefix(optionalPrefix) {
self = .optional(WrappableType(parsing: String(trimmedValue.dropFirst(optionalPrefix.count).dropLast())))
} else if trimmedValue.hasSuffix("!") {
self = .implicitlyUnwrappedOptional(WrappableType(parsing: String(trimmedValue.dropLast())))
} else {
self = .type(trimmedValue)
}
}
public func containsAttribute(named attribute: String) -> Bool {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return wrapped.containsAttribute(named: attribute)
case .attributed(_, let attributes):
return attributes.contains(attribute.trimmed)
case .type:
return false
}
}
}
extension WrappableType: CustomStringConvertible {
public var description: String {
return sugarized
}
}
extension WrappableType: Equatable {
public static func ==(lhs: WrappableType, rhs: WrappableType) -> Bool {
switch (lhs, rhs) {
case (.optional(let lhsWrapped), .optional(let rhsWrapped)),
(.implicitlyUnwrappedOptional(let lhsWrapped), .implicitlyUnwrappedOptional(let rhsWrapped)):
return lhsWrapped == rhsWrapped
case (.attributed(let lhsWrapped, let lhsAttributes), .attributed(let rhsWrapped, let rhsAttributes)):
return lhsWrapped == rhsWrapped && lhsAttributes == rhsAttributes
case (.type(let lhsType), .type(let rhsType)):
return lhsType.components(separatedBy: .whitespacesAndNewlines).joined() == rhsType.components(separatedBy: .whitespacesAndNewlines).joined()
default:
return false
}
}
}

View File

@ -1,53 +0,0 @@
import Foundation
struct TypeGuesser {
static func guessType(from value: String) -> String? {
let value = value.trimmed
guard !value.isEmpty else { return nil }
let casting = checkCasting(from: value)
guard casting == nil else { return casting }
let character = value[value.startIndex]
switch character {
case "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
return guessNumberType(from: value)
case "_":
return guessIdentifier(from: value)
case "\"":
return "String"
default:
let identifier = guessIdentifier(from: value)
return identifier == "true" || identifier == "false" ? "Bool" : identifier
}
}
private static func guessNumberType(from value: String) -> String {
var iterator = value.makeIterator()
while let character = iterator.next() {
if character == "." || character == "e" {
return "Double"
}
}
return "Int"
}
private static func guessIdentifier(from value: String) -> String? {
var identifier = ""
var iterator = value.makeIterator()
while let character = iterator.next() {
guard character != "(" else { break }
identifier.append(character)
}
return identifier
}
private static func checkCasting(from value: String) -> String? {
let regex = try! NSRegularExpression(pattern: " as (.*)$")
let range = NSRange(location: 0, length: value.count)
guard let casting = regex.firstMatch(in: value, range: range) else { return nil }
let foundRange = casting.range(at: 1)
guard foundRange.location != NSNotFound else { return nil }
return value[foundRange]
}
}

Some files were not shown because too many files have changed in this diff Show More