Compare commits
342 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
088c021cbc | |
![]() |
475322e609 | |
![]() |
9676d7e374 | |
![]() |
75a209c3ba | |
![]() |
e29f10eac6 | |
![]() |
4148a6a7ff | |
![]() |
a71ba74df1 | |
![]() |
fa66b6a0a0 | |
![]() |
6f41152d83 | |
![]() |
a9469909b7 | |
![]() |
7d51beb752 | |
![]() |
41a70422fb | |
![]() |
f0f77397a5 | |
![]() |
835c61246a | |
![]() |
cbb5f5d416 | |
![]() |
102c9b3a10 | |
![]() |
8d7b893bc2 | |
![]() |
64b1681e37 | |
![]() |
5a9301b97c | |
![]() |
4b5a397f58 | |
![]() |
55149ddee9 | |
![]() |
a84978a0c1 | |
![]() |
0ff327d675 | |
![]() |
3df9608f5a | |
![]() |
1b0af7cc4a | |
![]() |
efa47f2afa | |
![]() |
d3de8c0bfd | |
![]() |
5d52a7f75b | |
![]() |
3f6e9a84e8 | |
![]() |
a3c4ad13f7 | |
![]() |
6af1b4d09f | |
![]() |
69c1c9aee6 | |
![]() |
de28aa5f3c | |
![]() |
084f5dbdc3 | |
![]() |
83b5c4e83b | |
![]() |
dd8db792ba | |
![]() |
e4a0153485 | |
![]() |
b1da946216 | |
![]() |
2775830d13 | |
![]() |
fe7b56381c | |
![]() |
21a4fc39df | |
![]() |
249aba0930 | |
![]() |
53b8ff4428 | |
![]() |
6c6c7fa83c | |
![]() |
6473adac0d | |
![]() |
79b13331e5 | |
![]() |
b7ff314d6b | |
![]() |
df8847df2d | |
![]() |
7da80b2321 | |
![]() |
eb1502c657 | |
![]() |
8f1d1f1d09 | |
![]() |
d266c2bc68 | |
![]() |
0259cd8730 | |
![]() |
10b480320c | |
![]() |
39e05cdf56 | |
![]() |
6c59599fc0 | |
![]() |
798ea3fcdc | |
![]() |
7ca53fb53c | |
![]() |
96ff0d11d0 | |
![]() |
71d7423add | |
![]() |
531f4d403a | |
![]() |
58ec81375b | |
![]() |
d85e2dd8e7 | |
![]() |
1d31d53b04 | |
![]() |
6373c81dd2 | |
![]() |
cacef807d2 | |
![]() |
bc085e23cf | |
![]() |
f6429bb757 | |
![]() |
60f2cc0006 | |
![]() |
403b225770 | |
![]() |
4d8e999f37 | |
![]() |
a508ab347f | |
![]() |
bf197c4ccb | |
![]() |
296c14b610 | |
![]() |
1d302ede70 | |
![]() |
0ef473893c | |
![]() |
a0a7c82526 | |
![]() |
e2bb73412f | |
![]() |
b3bec5f8ff | |
![]() |
8abe3f2091 | |
![]() |
07b4a701a4 | |
![]() |
a732857b79 | |
![]() |
bfc893657e | |
![]() |
3b533e15a0 | |
![]() |
c2ff12851f | |
![]() |
db48119479 | |
![]() |
5a1f5f35d9 | |
![]() |
5afae85bbe | |
![]() |
b1c7c77dbb | |
![]() |
ce2de127e6 | |
![]() |
24f743f950 | |
![]() |
826e5c4427 | |
![]() |
972f0594f7 | |
![]() |
f81c04b504 | |
![]() |
264d2ebb50 | |
![]() |
d16b4e6635 | |
![]() |
ee447e93c0 | |
![]() |
9cc13cc195 | |
![]() |
6cb87a423f | |
![]() |
bbdd3304b0 | |
![]() |
7fa3d75aee | |
![]() |
01fb627626 | |
![]() |
9f6f3e04e6 | |
![]() |
f3dcdb0edc | |
![]() |
5600ca3d96 | |
![]() |
9e8cdfde9a | |
![]() |
4a51ddd885 | |
![]() |
5d68a586de | |
![]() |
caf0db428f | |
![]() |
491ff8dff8 | |
![]() |
1ef420cad8 | |
![]() |
60525cc68f | |
![]() |
fb9e2ea421 | |
![]() |
619d8c73dd | |
![]() |
f49e47ee7b | |
![]() |
0a3b667e4f | |
![]() |
e763706be7 | |
![]() |
54a86c37da | |
![]() |
cd58a7f09e | |
![]() |
81e1cb59a5 | |
![]() |
6a2f9596fb | |
![]() |
ed1e947e1f | |
![]() |
674ecd9de6 | |
![]() |
cc5afef8f7 | |
![]() |
44dedd3859 | |
![]() |
808a19f920 | |
![]() |
ff4c627d42 | |
![]() |
fc91ef5e1c | |
![]() |
69facb06bb | |
![]() |
67f91fdad6 | |
![]() |
e1682c7888 | |
![]() |
b0e46c78e2 | |
![]() |
7eb5517b27 | |
![]() |
b49d23a6ce | |
![]() |
c3af210aff | |
![]() |
ba7f7049f4 | |
![]() |
60d8aa1597 | |
![]() |
308cb86548 | |
![]() |
2228a9bea7 | |
![]() |
f10f174357 | |
![]() |
30c664f744 | |
![]() |
d2f83928ba | |
![]() |
d42fc414f6 | |
![]() |
3a42272199 | |
![]() |
d48f2ab5cc | |
![]() |
c5f6d0cd25 | |
![]() |
0d786501bc | |
![]() |
b0c66e7afa | |
![]() |
4e8abcace6 | |
![]() |
9947868805 | |
![]() |
18ab4be1d8 | |
![]() |
989680cebe | |
![]() |
62befddc20 | |
![]() |
39257d9cc8 | |
![]() |
e405dbfe0a | |
![]() |
86432954a9 | |
![]() |
76c303797f | |
![]() |
b5c472b4a2 | |
![]() |
c6965b30ff | |
![]() |
b3fd0dacfd | |
![]() |
fe3af6709e | |
![]() |
0cb6417067 | |
![]() |
4493b12144 | |
![]() |
694bd9c8be | |
![]() |
d69bcd5bdd | |
![]() |
ec8c77bd17 | |
![]() |
efddb1848d | |
![]() |
bbe20871e7 | |
![]() |
8412e772fa | |
![]() |
922889ca32 | |
![]() |
32b2e3ebf1 | |
![]() |
e16fb3a383 | |
![]() |
f9e035475c | |
![]() |
dda4e2eab6 | |
![]() |
d0182512e0 | |
![]() |
0c3333e8d7 | |
![]() |
7a41a928fc | |
![]() |
ec10e6bf00 | |
![]() |
b4c2bbe5b7 | |
![]() |
449b71e3db | |
![]() |
f3d28b70a9 | |
![]() |
b039aa697f | |
![]() |
f4e9608375 | |
![]() |
59ac461c98 | |
![]() |
1cc26c2ee1 | |
![]() |
51353539bf | |
![]() |
dc0ec1242d | |
![]() |
935afbfd6a | |
![]() |
083a6b5c17 | |
![]() |
14afe0a125 | |
![]() |
1b71fcfd2e | |
![]() |
2305ec2221 | |
![]() |
ec41a4ff97 | |
![]() |
446590b81d | |
![]() |
0e894e2b7d | |
![]() |
732b4f3fed | |
![]() |
bd28aad894 | |
![]() |
bf8033643c | |
![]() |
6e869257d3 | |
![]() |
803bd38b58 | |
![]() |
ffc33ba6db | |
![]() |
7cd237c212 | |
![]() |
505da8310f | |
![]() |
58deea7542 | |
![]() |
ae4d42b8d5 | |
![]() |
7d07b486ac | |
![]() |
79c7961e68 | |
![]() |
ffa15c5a84 | |
![]() |
a4e6e5fae6 | |
![]() |
1118272598 | |
![]() |
31a5a40c0b | |
![]() |
14eaaf945f | |
![]() |
f4124a6802 | |
![]() |
f2b492e60e | |
![]() |
b739903983 | |
![]() |
93a42ccacc | |
![]() |
edb170175e | |
![]() |
6affe4029b | |
![]() |
170f1b4004 | |
![]() |
98559606f8 | |
![]() |
f70a59169f | |
![]() |
9bce92c6bf | |
![]() |
fa8177c81c | |
![]() |
5ce0538170 | |
![]() |
6c5501792b | |
![]() |
773bc177d4 | |
![]() |
18f399097d | |
![]() |
31798c0f76 | |
![]() |
4d617cd573 | |
![]() |
03bf8c1c73 | |
![]() |
87d92153d4 | |
![]() |
63b03cb2d1 | |
![]() |
b97e7c3dda | |
![]() |
b0b64cf503 | |
![]() |
6e760b70ce | |
![]() |
83e79fffd6 | |
![]() |
349fa1c154 | |
![]() |
6fc3283bfc | |
![]() |
944f2c7f8a | |
![]() |
515a91c5ca | |
![]() |
2bcce595b3 | |
![]() |
1cd91fb1f4 | |
![]() |
dd252b5b75 | |
![]() |
4f24586e9c | |
![]() |
312257727f | |
![]() |
e2bc56d7fc | |
![]() |
75e5ef54a8 | |
![]() |
233a04fda5 | |
![]() |
f9f947fc5b | |
![]() |
aa0b7ec77a | |
![]() |
04c9db0400 | |
![]() |
5867e24421 | |
![]() |
1b77516e83 | |
![]() |
2f6ab41bbc | |
![]() |
2961debaba | |
![]() |
7ad094cf78 | |
![]() |
9d6ecf889c | |
![]() |
d29ab5f839 | |
![]() |
acf338e4a1 | |
![]() |
5c3c305216 | |
![]() |
b22502a82b | |
![]() |
c558179b33 | |
![]() |
231216b588 | |
![]() |
7fb9d2d6e8 | |
![]() |
fa8231105a | |
![]() |
12f9bac435 | |
![]() |
0fc242dcec | |
![]() |
c6baeda34c | |
![]() |
ae465a4689 | |
![]() |
59f2240831 | |
![]() |
0a5c4f7f1d | |
![]() |
76472af91e | |
![]() |
3793af312a | |
![]() |
8cc707ff7c | |
![]() |
79e19c6fe3 | |
![]() |
682619c35f | |
![]() |
bee9b97bf0 | |
![]() |
5165e3fc52 | |
![]() |
7968fdae9d | |
![]() |
851a5f9755 | |
![]() |
e920e14acc | |
![]() |
c6e23cdfc8 | |
![]() |
68dae06d96 | |
![]() |
df7138b9f2 | |
![]() |
ba9b72b061 | |
![]() |
72c63071db | |
![]() |
e81835e64d | |
![]() |
81b5bd0e1c | |
![]() |
7aaab444a7 | |
![]() |
853faf631d | |
![]() |
1aab334a10 | |
![]() |
af699f2ace | |
![]() |
a92d246465 | |
![]() |
c9e272fccb | |
![]() |
9febf9783d | |
![]() |
92161a4563 | |
![]() |
1d36cdd55d | |
![]() |
645ba65c73 | |
![]() |
ace61bb081 | |
![]() |
cd216972e6 | |
![]() |
6fe686a79e | |
![]() |
3d6bbd31b3 | |
![]() |
611f9849a9 | |
![]() |
3f1d9c6468 | |
![]() |
25d9700f5e | |
![]() |
43a23b4b4f | |
![]() |
e0a1b1f6fe | |
![]() |
9287e60611 | |
![]() |
26ba750b2b | |
![]() |
f5093adcae | |
![]() |
b086f6e1f0 | |
![]() |
614dfaa515 | |
![]() |
eaab7db26d | |
![]() |
f0f31bd0e9 | |
![]() |
0f52ceef7e | |
![]() |
b95d48ecf7 | |
![]() |
8e49eefd4d | |
![]() |
f3190d8bcb | |
![]() |
d179601d9c | |
![]() |
0ecde3fdcd | |
![]() |
eb1992abdb | |
![]() |
3adcde2fc0 | |
![]() |
96ebbbf9ca | |
![]() |
2bf60e5dc1 | |
![]() |
52dcdbf17f | |
![]() |
01ee3fa05c | |
![]() |
75be1e38d7 | |
![]() |
85da0d9fb1 | |
![]() |
dec5e08c1e | |
![]() |
4ca15ff61d | |
![]() |
1bdc14698c | |
![]() |
725e30ef3f | |
![]() |
d9b2614d2b | |
![]() |
cfe0b0189d | |
![]() |
12e8cdcc8b | |
![]() |
9c09ea19d8 | |
![]() |
8d09211853 | |
![]() |
e2e7928018 | |
![]() |
dc5610dca9 | |
![]() |
25c061a489 | |
![]() |
0a9a8a3b9d | |
![]() |
129ba1b9d5 |
|
@ -0,0 +1,108 @@
|
||||||
|
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
|
|
@ -1,5 +1,4 @@
|
||||||
# Xcode
|
# Xcode
|
||||||
#
|
|
||||||
.build/
|
.build/
|
||||||
build/
|
build/
|
||||||
*.pbxuser
|
*.pbxuser
|
||||||
|
@ -19,25 +18,33 @@ 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
|
||||||
#
|
|
||||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
|
||||||
# Carthage/Checkouts
|
|
||||||
|
|
||||||
Carthage/Build
|
Carthage/Build
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
default.profraw
|
default.profraw
|
||||||
Tests/Generated/*.swift
|
Tests/**/Generated/*.swift
|
||||||
Generator/*.app
|
Generator/*.app
|
||||||
|
|
||||||
# AppCode
|
# AppCode
|
||||||
.idea/
|
.idea/
|
||||||
cuckoo_generator
|
cuckoo_generator
|
||||||
|
.fastlane
|
||||||
|
|
||||||
Generator/CuckooGenerator.xcodeproj
|
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
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"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 +0,0 @@
|
||||||
3.0
|
|
12
.travis.yml
12
.travis.yml
|
@ -1,19 +1,15 @@
|
||||||
language: objective-c
|
language: objective-c
|
||||||
osx_image: xcode8.2
|
osx_image: xcode10.2
|
||||||
xcode_project: Cuckoo.xcodeproj
|
xcode_project: Cuckoo.xcodeproj
|
||||||
xcode_scheme: Cuckoo
|
xcode_scheme: Cuckoo
|
||||||
xcode_sdk: macosx
|
xcode_sdk: macosx
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- gem install cucumber --no-rdoc --no-ri
|
- pod install --repo-update
|
||||||
- gem install aruba --no-rdoc --no-ri
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cd Generator
|
- xcodebuild -workspace 'Cuckoo.xcworkspace' -scheme 'Cuckoo+OCMock-macOS' clean test | xcpretty
|
||||||
- make test
|
- pod lib lint --allow-warnings
|
||||||
- cd ..
|
|
||||||
- xcodebuild -workspace 'Cuckoo.xcworkspace' -scheme 'Cuckoo+Run' clean test
|
|
||||||
- pod lib lint
|
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- sleep 5
|
- sleep 5
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
3.11.0
|
192
CHANGELOG.md
192
CHANGELOG.md
|
@ -1,50 +1,172 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.8.4
|
## 1.4.1
|
||||||
|
- Sidestep `SourceKit`'s off-by-one bug when parsing generic parameter inheritance.
|
||||||
|
- Fix incorrect `where` clause parsing.
|
||||||
|
|
||||||
* Added support for inheritance mocking.
|
## 1.4.0
|
||||||
|
- Add tvOS targets and schemes.
|
||||||
|
- Integrate `tuist`, fixing Carthage issues.
|
||||||
|
- 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
|
## 0.8.3
|
||||||
|
- Added support for `fileprivate` (thanks to lvdstam for implementation).
|
||||||
* Added support for `fileprivate` (thanks to lvdstam for implementation).
|
- Added support for default values (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.
|
||||||
* Fixed wrongly generated code where public class had internal members.
|
|
||||||
|
|
||||||
## 0.8.2
|
## 0.8.2
|
||||||
|
- Show error in generator in build log.
|
||||||
* Show error in generator in build log.
|
- Fixed crash of generator when instance variable type is not explicitly set.
|
||||||
* Fixed crash of generator when instance variable type is not explicitly set.
|
- Fixed support of closures and unwrapped optionals.
|
||||||
* Fixed support of closures and unwrapped optionals.
|
|
||||||
|
|
||||||
## 0.8.1
|
## 0.8.1
|
||||||
|
- Set "Reflection Metadata" to "None" to fix #72
|
||||||
* Set "Reflection Metadata" to "None" to fix #72
|
|
||||||
|
|
||||||
## 0.8.0
|
## 0.8.0
|
||||||
|
- Support for **Swift 3**
|
||||||
* Support for Swift 3
|
- Added --no-class-mocking parameter to generator
|
||||||
* Added --no-class-mocking parameter to generator
|
- Added Stub objects
|
||||||
* Added Stub objects
|
|
||||||
|
|
||||||
## 0.7.0
|
## 0.7.0
|
||||||
|
- Updated documentation
|
||||||
* Updated documentation
|
- Added more automated tests
|
||||||
* Added more automated tests
|
- Added --file-prefix parameter to generator
|
||||||
* Added --file-prefix parameter to generator
|
- `and` and `or` methods can now be used with `Matchable` (literals)
|
||||||
* `and` and `or` methods can now be used with `Matchable` (literals)
|
- Using of custom `Matchable`, `ParameterMatcher` and `CallMatcher` is now easier
|
||||||
* Using of custom `Matchable`, `ParameterMatcher` and `CallMatcher` is now easier
|
- Improved fail messages
|
||||||
* Improved fail messages
|
- Merged generator and runtime repositories, making updating easier.
|
||||||
* Merged generator and runtime repositories, making updating easier.
|
- Added support for named arguments in methods.
|
||||||
* Added support for named arguments in methods.
|
- Added support for classes with custom initializers.
|
||||||
* Added support for classes with custom initializers.
|
- Changed usage of spies. Instead of `init(spyOn:)` use `init().spy(on:)`.
|
||||||
* Changed usage of spies. Instead of `init(spyOn:)` use `init().spy(on:)`.
|
|
||||||
|
|
||||||
## 0.6.0
|
## 0.6.0
|
||||||
|
- Added release notes
|
||||||
* Added release notes
|
- Added stub resetting
|
||||||
* Added stub resetting
|
- Added `thenCallRealImplementation`
|
||||||
* Added `thenCallRealImplementation`
|
- Added argument capturing
|
||||||
* Added argument capturing
|
- Added `verifyNoMoreInteractions`
|
||||||
* Added `verifyNoMoreInteractions`
|
- Added on going stubbing
|
||||||
* Added on going stubbing
|
- Added `thenDoNothing`
|
||||||
* Added `thenDoNothing`
|
|
||||||
|
|
|
@ -1,31 +1,54 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "Cuckoo"
|
s.name = "Cuckoo"
|
||||||
s.version = "0.8.4"
|
s.version = "1.10.3"
|
||||||
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/SwiftKit/Cuckoo"
|
s.homepage = "https://github.com/Brightify/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", "Adriaan (Arjan) Duijzer" => "arjan@nxtstep.nl" }
|
||||||
s.source = {
|
s.source = {
|
||||||
:git => "https://github.com/SwiftKit/Cuckoo.git",
|
:git => "https://github.com/Brightify/Cuckoo.git",
|
||||||
:tag => s.version.to_s
|
:tag => s.version.to_s
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ios.deployment_target = '8.0'
|
s.ios.deployment_target = '9.0'
|
||||||
s.osx.deployment_target = '10.9'
|
s.osx.deployment_target = '11.0'
|
||||||
#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'
|
||||||
s.source_files = ['Source/**/*.swift']
|
generator_name = 'cuckoo_generator'
|
||||||
s.preserve_paths = ['Generator/**/*', 'run', 'build_generator']
|
s.swift_version = '5.0'
|
||||||
|
s.preserve_paths = ['Generator/**/*', 'run', 'build_generator', generator_name]
|
||||||
s.prepare_command = <<-CMD
|
s.prepare_command = <<-CMD
|
||||||
git submodule update --init --recursive
|
curl -Lo #{generator_name} https://github.com/Brightify/Cuckoo/releases/download/#{s.version}/#{generator_name}
|
||||||
./build_generator
|
chmod +x #{generator_name}
|
||||||
CMD
|
CMD
|
||||||
s.frameworks = 'XCTest', 'Foundation'
|
s.frameworks = 'XCTest', 'Foundation'
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO', 'SWIFT_REFLECTION_METADATA_LEVEL' => 'none' }
|
s.pod_target_xcconfig = {
|
||||||
|
'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
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "self:/Users/TadeasKriz/Development/Sources/Mockery/Cuckoo.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
|
@ -1,72 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,25 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0800"
|
LastUpgradeVersion = "1010"
|
||||||
version = "1.7">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "NO"
|
parallelizeBuildables = "YES"
|
||||||
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"
|
||||||
|
@ -28,47 +14,31 @@
|
||||||
buildForAnalyzing = "YES">
|
buildForAnalyzing = "YES">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
|
BlueprintIdentifier = "6A8F12C3A778933132EBA10D"
|
||||||
BuildableName = "Cuckoo.framework"
|
BuildableName = "Cuckoo.framework"
|
||||||
BlueprintName = "Cuckoo"
|
BlueprintName = "Cuckoo-iOS"
|
||||||
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
buildConfiguration = "TestWithRun"
|
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 = "183D04041C4691C600EBAEF3"
|
BlueprintIdentifier = "92C74015545203C1F4395DD4"
|
||||||
BuildableName = "CuckooTests.xctest"
|
BuildableName = "Cuckoo_iOSTests.xctest"
|
||||||
BlueprintName = "CuckooTests"
|
BlueprintName = "Cuckoo-iOSTests"
|
||||||
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"
|
||||||
|
@ -83,14 +53,12 @@
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
|
BlueprintIdentifier = "6A8F12C3A778933132EBA10D"
|
||||||
BuildableName = "Cuckoo.framework"
|
BuildableName = "Cuckoo.framework"
|
||||||
BlueprintName = "Cuckoo"
|
BlueprintName = "Cuckoo-iOS"
|
||||||
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
@ -101,9 +69,9 @@
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
|
BlueprintIdentifier = "6A8F12C3A778933132EBA10D"
|
||||||
BuildableName = "Cuckoo.framework"
|
BuildableName = "Cuckoo.framework"
|
||||||
BlueprintName = "Cuckoo"
|
BlueprintName = "Cuckoo-iOS"
|
||||||
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
|
@ -1,39 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0800"
|
LastUpgradeVersion = "1010"
|
||||||
version = "1.7">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "NO"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES">
|
buildImplicitDependencies = "YES">
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "NO"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "OBJ_459"
|
|
||||||
BuildableName = "TemplateEncoder"
|
|
||||||
BlueprintName = "TemplateEncoder"
|
|
||||||
ReferencedContainer = "container:Generator/CuckooGenerator.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "NO"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "OBJ_427"
|
|
||||||
BuildableName = "cuckoo_generator"
|
|
||||||
BlueprintName = "cuckoo_generator"
|
|
||||||
ReferencedContainer = "container:Generator/CuckooGenerator.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
buildForRunning = "YES"
|
buildForRunning = "YES"
|
||||||
|
@ -42,9 +14,9 @@
|
||||||
buildForAnalyzing = "YES">
|
buildForAnalyzing = "YES">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
|
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
|
||||||
BuildableName = "Cuckoo.framework"
|
BuildableName = "Cuckoo.framework"
|
||||||
BlueprintName = "Cuckoo"
|
BlueprintName = "Cuckoo-macOS"
|
||||||
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
|
@ -54,35 +26,19 @@
|
||||||
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 = "183D04041C4691C600EBAEF3"
|
BlueprintIdentifier = "DBDB2DD4E7BE487EEB257CC8"
|
||||||
BuildableName = "CuckooTests.xctest"
|
BuildableName = "Cuckoo_macOSTests.xctest"
|
||||||
BlueprintName = "CuckooTests"
|
BlueprintName = "Cuckoo-macOSTests"
|
||||||
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"
|
||||||
|
@ -97,14 +53,12 @@
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
|
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
|
||||||
BuildableName = "Cuckoo.framework"
|
BuildableName = "Cuckoo.framework"
|
||||||
BlueprintName = "Cuckoo"
|
BlueprintName = "Cuckoo-macOS"
|
||||||
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
@ -115,9 +69,9 @@
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "183D03FA1C4691C600EBAEF3"
|
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
|
||||||
BuildableName = "Cuckoo.framework"
|
BuildableName = "Cuckoo.framework"
|
||||||
BlueprintName = "Cuckoo"
|
BlueprintName = "Cuckoo-macOS"
|
||||||
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
ReferencedContainer = "container:Cuckoo.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?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>
|
|
@ -1,10 +0,0 @@
|
||||||
<?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>
|
|
|
@ -1,100 +0,0 @@
|
||||||
{
|
|
||||||
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "509FD5E949BB67AB92770169324D45AB1917F796",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
|
|
||||||
|
|
||||||
},
|
|
||||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
|
|
||||||
"1B9E61546D4F9511D800AE955CDC56CD3D276E5C" : 9223372036854775807,
|
|
||||||
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : 0,
|
|
||||||
"E084C86B03F81D63323C9E7510697EA528A758C7" : 0,
|
|
||||||
"E3472B155FC961329A2EA7BFAD3AFAE6EFBD63E5" : 9223372036854775807,
|
|
||||||
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : 0,
|
|
||||||
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : 0,
|
|
||||||
"95438028B10BBB846574013D29F154A00556A9D1" : 9223372036854775807,
|
|
||||||
"EB2210CFD48672E403BED699D5D7F01B844069CF" : 0,
|
|
||||||
"D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64" : 9223372036854775807,
|
|
||||||
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : 0,
|
|
||||||
"956D2B21DD155C49504BB67697A67F7C5351A353" : 0,
|
|
||||||
"509FD5E949BB67AB92770169324D45AB1917F796" : 0
|
|
||||||
},
|
|
||||||
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "AA22B945-78BB-4BFD-8BCA-01658017BF31",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
|
|
||||||
"1B9E61546D4F9511D800AE955CDC56CD3D276E5C" : "Cuckoo\/Generator\/Packages\/Yams-0.1.4\/",
|
|
||||||
"D23C0CEAADB77074FDA4459000068A7940EB7AD0" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/",
|
|
||||||
"E084C86B03F81D63323C9E7510697EA528A758C7" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/xcconfigs\/",
|
|
||||||
"E3472B155FC961329A2EA7BFAD3AFAE6EFBD63E5" : "Cuckoo\/Generator\/Packages\/PathKit-0.8.0\/",
|
|
||||||
"E111B14063EC98ABE518A93E1C4498F4167C2DEE" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/YamlSwift\/",
|
|
||||||
"64ACE19A884E8C30BC53E2E0CE86010ECED70B5A" : "Cuckoo\/Generator\/Dependencies\/FileKit\/",
|
|
||||||
"95438028B10BBB846574013D29F154A00556A9D1" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/Quick\/Externals\/Nimble\/",
|
|
||||||
"EB2210CFD48672E403BED699D5D7F01B844069CF" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/SWXMLHash\/",
|
|
||||||
"D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/Quick\/",
|
|
||||||
"FD7DA18210A2C280E9107E37D7344F243FEE5F75" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/",
|
|
||||||
"956D2B21DD155C49504BB67697A67F7C5351A353" : "Cuckoo\/Generator\/Dependencies\/SourceKitten\/Carthage\/Checkouts\/Commandant\/Carthage\/Checkouts\/Result\/",
|
|
||||||
"509FD5E949BB67AB92770169324D45AB1917F796" : "Cuckoo\/"
|
|
||||||
},
|
|
||||||
"DVTSourceControlWorkspaceBlueprintNameKey" : "Cuckoo",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
|
|
||||||
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Cuckoo.xcworkspace",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
|
|
||||||
{
|
|
||||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/jpsim\/Yams.git",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "1B9E61546D4F9511D800AE955CDC56CD3D276E5C"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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\/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.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\/kylef\/PathKit.git",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
|
||||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E3472B155FC961329A2EA7BFAD3AFAE6EFBD63E5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -3,7 +3,7 @@
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
|
@ -15,14 +15,10 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.8.4</string>
|
<string>1.0</string>
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>1</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2016 Brightify. All rights reserved.</string>
|
<string>Copyright ©. All rights reserved.</string>
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
|
@ -3,11 +3,9 @@
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
@ -15,18 +13,12 @@
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>$(PRODUCT_NAME)</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>BNDL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.8.4</string>
|
<string>1.0</string>
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2016 Brightify. All rights reserved.</string>
|
<string>Copyright ©. All rights reserved.</string>
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem 'json'
|
||||||
|
gem 'fastlane'
|
||||||
|
gem 'cocoapods'
|
|
@ -0,0 +1,285 @@
|
||||||
|
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
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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>
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"autoPin": true,
|
|
||||||
"pins": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"version": 1
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
import PackageDescription
|
|
||||||
|
|
||||||
let package = Package(
|
|
||||||
name: "CuckooGenerator",
|
|
||||||
targets: [
|
|
||||||
Target(name: "TemplateEncoder"),
|
|
||||||
|
|
||||||
Target(name: "CuckooGeneratorFramework", dependencies: [
|
|
||||||
.Target(name: "TemplateEncoder")]),
|
|
||||||
|
|
||||||
Target(name: "cuckoo_generator", dependencies: [
|
|
||||||
.Target(name: "CuckooGeneratorFramework")]),
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
.Package(url: "https://github.com/jpsim/SourceKitten.git", versions: Version(0, 15, 0)..<Version(0, 17, .max)),
|
|
||||||
.Package(url: "https://github.com/TadeasKriz/FileKit.git", Version(4, 0, 2)),
|
|
||||||
.Package(url: "https://github.com/kylef/Stencil.git", majorVersion: 0, minor: 8),
|
|
||||||
],
|
|
||||||
exclude: [
|
|
||||||
"Tests"
|
|
||||||
]
|
|
||||||
)
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
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),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// 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
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
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: >) {
|
||||||
|
populateFiles(gt: gt, includeFiles: includeFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
globfree(>)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// VersionCommand.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Tadeas Kriz on 17/01/16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Commandant
|
import Commandant
|
||||||
import Result
|
import Result
|
||||||
|
@ -21,7 +13,7 @@ public struct VersionCommand: CommandProtocol {
|
||||||
|
|
||||||
public func run(_ options: Options) -> Result<Void, CuckooGeneratorError> {
|
public func run(_ options: Options) -> Result<Void, CuckooGeneratorError> {
|
||||||
print(VersionCommand.appVersion)
|
print(VersionCommand.appVersion)
|
||||||
return .success()
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Options: OptionsProtocol {
|
public struct Options: OptionsProtocol {
|
|
@ -1,12 +1,3 @@
|
||||||
//#!/usr/bin/swift -F Carthage/Build/Mac
|
|
||||||
//
|
|
||||||
// main.swift
|
|
||||||
// SwiftMockGenerator
|
|
||||||
//
|
|
||||||
// Created by Tadeas Kriz on 12/01/16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Commandant
|
import Commandant
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
//
|
|
||||||
// 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"
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
//
|
|
||||||
// Generator.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Tadeas Kriz on 13/01/16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Stencil
|
|
||||||
|
|
||||||
public struct Generator {
|
|
||||||
|
|
||||||
private let declarations: [Token]
|
|
||||||
private let code = CodeBuilder()
|
|
||||||
|
|
||||||
public init(file: FileRepresentation) {
|
|
||||||
declarations = file.declarations
|
|
||||||
}
|
|
||||||
|
|
||||||
public func generate() -> String {
|
|
||||||
code.clear()
|
|
||||||
|
|
||||||
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 parameters = value as? [MethodParameter] else { return value }
|
|
||||||
return self.matchableGenerics(with: parameters)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext.registerFilter("matchableGenericWhere") { (value: Any?) in
|
|
||||||
guard let parameters = value as? [MethodParameter] else { return value }
|
|
||||||
return self.matchableGenerics(where: parameters)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
let environment = Environment(loader: InternalLoader(), extensions: [ext])
|
|
||||||
|
|
||||||
let containers = declarations.flatMap { $0 as? ContainerToken }
|
|
||||||
.filter { $0.accessibility.isAccessible }
|
|
||||||
.map { $0.serializeWithType() }
|
|
||||||
|
|
||||||
return try! environment.renderTemplate(name: "Mock.swift.stencil", context: ["containers": containers])
|
|
||||||
}
|
|
||||||
|
|
||||||
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 == \(genericSafeType(from: $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 matchers: [Cuckoo.ParameterMatcher<Void>] = []" }
|
|
||||||
|
|
||||||
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<(\(genericSafeType(from: tupleType)))>] = [\(matchers)]"
|
|
||||||
}
|
|
||||||
|
|
||||||
private func genericSafeType(from type: String) -> String {
|
|
||||||
return type.replacingOccurrences(of: "!", with: "?")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2017, Google Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above
|
|
||||||
* copyright notice, this list of conditions and the following disclaimer
|
|
||||||
* in the documentation and/or other materials provided with the
|
|
||||||
* distribution.
|
|
||||||
* * Neither the name of Google Inc. nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Stencil
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
// A class for loading Stencil templates from compiled-in representations
|
|
||||||
public class InternalLoader: Loader {
|
|
||||||
private var templates: [String:String]
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
self.templates = loadTemplates()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func loadTemplate(name: String, environment: Environment) throws -> Template {
|
|
||||||
if let encoding = templates[name],
|
|
||||||
let data = Data(base64Encoded: encoding, options:[]),
|
|
||||||
let template = String(data:data, encoding:.utf8) {
|
|
||||||
return environment.templateClass.init(templateString: template,
|
|
||||||
environment: environment,
|
|
||||||
name: name)
|
|
||||||
} else {
|
|
||||||
throw TemplateDoesNotExist(templateNames: [name], loader: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func loadTemplate(names: [String], environment: Environment) throws -> Template {
|
|
||||||
for name in names {
|
|
||||||
if let encoding = templates[name],
|
|
||||||
let data = Data(base64Encoded: encoding, options:[]),
|
|
||||||
let template = String(data:data, encoding:.utf8) {
|
|
||||||
return environment.templateClass.init(templateString: template,
|
|
||||||
environment: environment,
|
|
||||||
name: name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw TemplateDoesNotExist(templateNames: names, loader: self)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// 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>
|
|
|
@ -1,17 +0,0 @@
|
||||||
// GENERATED: DO NOT EDIT
|
|
||||||
//
|
|
||||||
// This file contains base64 encodings of templates used for Swift GRPC code generation.
|
|
||||||
//
|
|
||||||
func loadTemplates() -> [String:String] {
|
|
||||||
var templates : [String:String] = [:]
|
|
||||||
|
|
||||||
templates["Mock.swift.stencil"] = "eyUgZm9yIGNvbnRhaW5lciBpbiBjb250YWluZXJzICV9CmNsYXNzIE1vY2t7eyBjb250YWluZXIubmFtZSB9fToge3sgY29udGFpbmVyLm5hbWUgfX0sIEN1Y2tvby5Nb2NrIHsKICAgIHR5cGVhbGlhcyBNb2Nrc1R5cGUgPSB7eyBjb250YWluZXIubmFtZSB9fQogICAgdHlwZWFsaWFzIFN0dWJiaW5nID0gX19TdHViYmluZ1Byb3h5X3t7IGNvbnRhaW5lci5uYW1lIH19CiAgICB0eXBlYWxpYXMgVmVyaWZpY2F0aW9uID0gX19WZXJpZmljYXRpb25Qcm94eV97eyBjb250YWluZXIubmFtZSB9fQogICAgbGV0IGN1Y2tvb19tYW5hZ2VyID0gQ3Vja29vLk1vY2tNYW5hZ2VyKCkKCiAgICBwcml2YXRlIHZhciBvYnNlcnZlZDoge3sgY29udGFpbmVyLm5hbWUgfX0/CgogICAgZnVuYyBzcHkob24gdmljdGltOiB7eyBjb250YWluZXIubmFtZSB9fSkgLT4gU2VsZiB7CiAgICAgICAgb2JzZXJ2ZWQgPSB2aWN0aW0KICAgICAgICByZXR1cm4gc2VsZgogICAgfQoKICAgIHslIGZvciBwcm9wZXJ0eSBpbiBjb250YWluZXIucHJvcGVydGllcyAlfQogICAgLy8ge3twcm9wZXJ0eX19CiAgICB7eyBwcm9wZXJ0eS5hY2Nlc3NpYmlsaXR5IH19eyUgaWYgY29udGFpbmVyLkB0eXBlID09ICJDbGFzc0RlY2xhcmF0aW9uIiAlfSBvdmVycmlkZXslIGVuZGlmICV9IHZhciB7eyBwcm9wZXJ0eS5uYW1lIH19OiB7eyBwcm9wZXJ0eS50eXBlIH19IHsKICAgICAgICBnZXQgewogICAgICAgICAgICByZXR1cm4gY3Vja29vX21hbmFnZXIuZ2V0dGVyKCJ7eyBwcm9wZXJ0eS5uYW1lIH19Iiwgb3JpZ2luYWw6IG9ic2VydmVkLm1hcCB7IG8gaW4gcmV0dXJuIHsgKCkgLT4ge3sgcHJvcGVydHkudHlwZSB9fSBpbiBvLnt7IHByb3BlcnR5Lm5hbWUgfX0gfX0pCiAgICAgICAgfQogICAgICAgIHslIGlmbm90IHByb3BlcnR5LmlzUmVhZE9ubHkgJX0KICAgICAgICBzZXQgewogICAgICAgICAgICBjdWNrb29fbWFuYWdlci5zZXR0ZXIoInt7IHByb3BlcnR5Lm5hbWUgfX0iLCB2YWx1ZTogbmV3VmFsdWUsIG9yaWdpbmFsOiBvYnNlcnZlZCAhPSBuaWwgPyB7IHNlbGYub2JzZXJ2ZWQ/Lnt7IHByb3BlcnR5Lm5hbWUgfX0gPSAkMCB9IDogbmlsKQogICAgICAgIH0KICAgICAgICB7JSBlbmRpZiAlfQogICAgfQogICAgeyUgZW5kZm9yICV9CgogICAgeyUgZm9yIGluaXRpYWxpemVyIGluIGNvbnRhaW5lci5pbml0aWFsaXplcnMgJX0KICAgIHt7IGluaXRpYWxpemVyLmFjY2Vzc2liaWxpdHkgfX17JSBpZiBjb250YWluZXIuQHR5cGUgPT0gIkNsYXNzRGVjbGFyYXRpb24iICV9IG92ZXJyaWRleyUgZW5kaWYgJX17JSBpZiBpbml0aWFsaXplci5AdHlwZSA9PSAiUHJvdG9jb2xNZXRob2QiICV9IHJlcXVpcmVkeyVlbmRpZiV9IGluaXQoe3tpbml0aWFsaXplci5wYXJhbWV0ZXJTaWduYXR1cmV9fSkgewogICAgICAgIHslIGlmIGNvbnRhaW5lci5AdHlwZSA9PSAiQ2xhc3NEZWNsYXJhdGlvbiIgJX0KICAgICAgICBzdXBlci5pbml0KHt7aW5pdGlhbGl6ZXIuY2FsbH19KQogICAgICAgIHslIGVuZGlmICV9CiAgICB9CiAgICB7JSBlbmRmb3IgJX0KCiAgICB7JSBmb3IgbWV0aG9kIGluIGNvbnRhaW5lci5tZXRob2RzICV9CiAgICB7eyBtZXRob2QuYWNjZXNzaWJpbGl0eSB9fXslIGlmIGNvbnRhaW5lci5AdHlwZSA9PSAiQ2xhc3NEZWNsYXJhdGlvbiIgJX0gb3ZlcnJpZGV7JSBlbmRpZiAlfSBmdW5jIHt7IG1ldGhvZC5uYW1lIH19KHt7IG1ldGhvZC5wYXJhbWV0ZXJTaWduYXR1cmUgfX0pIHt7IG1ldGhvZC5yZXR1cm5TaWduYXR1cmUgfX0gewogICAgICAgIHslIGlmIG1ldGhvZC5pc1Rocm93aW5nICV9CiAgICAgICAgcmV0dXJuIHRyeSBjdWNrb29fbWFuYWdlci5jYWxsVGhyb3dzKCJ7e21ldGhvZC5mdWxseVF1YWxpZmllZE5hbWV9fSIsCiAgICAgICAgICAgIHBhcmFtZXRlcnM6ICh7e21ldGhvZC5wYXJhbWV0ZXJOYW1lc319KSwKICAgICAgICAgICAgb3JpZ2luYWw6IG9ic2VydmVkLm1hcCB7IG8gaW4gcmV0dXJuIHsgKHt7IG1ldGhvZC5wYXJhbWV0ZXJTaWduYXR1cmVXaXRob3V0TmFtZXMgfX0pe3ttZXRob2QucmV0dXJuU2lnbmF0dXJlfX0gaW4gdHJ5IG8ue3ttZXRob2QubmFtZX19eyVpZiBtZXRob2QuaXNPcHRpb25hbCAlfT97JWVuZGlmJX0oe3ttZXRob2QuY2FsbH19KSB9IH0pCiAgICAgICAgeyUgZWxzZSAlfQogICAgICAgIHJldHVybiBjdWNrb29fbWFuYWdlci5jYWxsKCJ7e21ldGhvZC5mdWxseVF1YWxpZmllZE5hbWV9fSIsCiAgICAgICAgICAgIHBhcmFtZXRlcnM6ICh7e21ldGhvZC5wYXJhbWV0ZXJOYW1lc319KSwKICAgICAgICAgICAgb3JpZ2luYWw6IG9ic2VydmVkLm1hcCB7IG8gaW4gcmV0dXJuIHsgKHt7IG1ldGhvZC5wYXJhbWV0ZXJTaWduYXR1cmVXaXRob3V0TmFtZXMgfX0pe3ttZXRob2QucmV0dXJuU2lnbmF0dXJlfX0gaW4gby57e21ldGhvZC5uYW1lfX17JWlmIG1ldGhvZC5pc09wdGlvbmFsICV9P3slZW5kaWYlfSh7e21ldGhvZC5jYWxsfX0pIH0gfSkKICAgICAgICB7JSBlbmRpZiAlfQogICAgfQogICAgeyUgZW5kZm9yICV9Cgp7JSBpbmNsdWRlICJTdHViYmluZ1Byb3h5LnN3aWZ0LnN0ZW5jaWwiICV9Cgp7JSBpbmNsdWRlICJWZXJpZmljYXRpb25Qcm94eS5zd2lmdC5zdGVuY2lsIiAlfQoKfQoKeyUgaW5jbHVkZSAiTm9wSW1wbFN0dWIuc3dpZnQuc3RlbmNpbCIgJX0KCnslIGVuZGZvciAlfQo="
|
|
||||||
|
|
||||||
templates["NopImplStub.swift.stencil"] = "e3tjb250YWluZXIuYWNjZXNzaWJpbGl0eX19IGNsYXNzIHt7IGNvbnRhaW5lci5uYW1lIH19U3R1Yjoge3sgY29udGFpbmVyLm5hbWUgfX0gewogICAgeyUgZm9yIHByb3BlcnR5IGluIGNvbnRhaW5lci5wcm9wZXJ0aWVzICV9CiAgICB7eyBwcm9wZXJ0eS5hY2Nlc3NpYmlsaXR5IH19eyUgaWYgY29udGFpbmVyLkB0eXBlID09ICJDbGFzc0RlY2xhcmF0aW9uIiAlfSBvdmVycmlkZXslIGVuZGlmICV9IHZhciB7eyBwcm9wZXJ0eS5uYW1lIH19OiB7eyBwcm9wZXJ0eS50eXBlIH19IHsKICAgICAgICBnZXQgewogICAgICAgICAgICByZXR1cm4gRGVmYXVsdFZhbHVlUmVnaXN0cnkuZGVmYXVsdFZhbHVlKGZvcjogKHt7cHJvcGVydHkudHlwZX19KS5zZWxmKQogICAgICAgIH0KICAgICAgICB7JSBpZm5vdCBwcm9wZXJ0eS5pc1JlYWRPbmx5ICV9CiAgICAgICAgc2V0IHsgfQogICAgICAgIHslIGVuZGlmICV9CiAgICB9CiAgICB7JSBlbmRmb3IgJX0KCiAgICB7JSBmb3IgaW5pdGlhbGl6ZXIgaW4gY29udGFpbmVyLmluaXRpYWxpemVycyAlfQogICAge3sgaW5pdGlhbGl6ZXIuYWNjZXNzaWJpbGl0eSB9fXslIGlmIGNvbnRhaW5lci5AdHlwZSA9PSAiQ2xhc3NEZWNsYXJhdGlvbiIgJX0gb3ZlcnJpZGV7JSBlbmRpZiAlfXslIGlmIGluaXRpYWxpemVyLkB0eXBlID09ICJQcm90b2NvbE1ldGhvZCIgJX0gcmVxdWlyZWR7JWVuZGlmJX0gaW5pdCh7e2luaXRpYWxpemVyLnBhcmFtZXRlclNpZ25hdHVyZX19KSB7CiAgICAgICAgeyUgaWYgY29udGFpbmVyLkB0eXBlID09ICJDbGFzc0RlY2xhcmF0aW9uIiAlfQogICAgICAgIHN1cGVyLmluaXQoe3tpbml0aWFsaXplci5jYWxsfX0pCiAgICAgICAgeyUgZW5kaWYgJX0KICAgIH0KICAgIHslIGVuZGZvciAlfQoKICAgIHslIGZvciBtZXRob2QgaW4gY29udGFpbmVyLm1ldGhvZHMgJX0KICAgIHt7IG1ldGhvZC5hY2Nlc3NpYmlsaXR5IH19eyUgaWYgY29udGFpbmVyLkB0eXBlID09ICJDbGFzc0RlY2xhcmF0aW9uIiAlfSBvdmVycmlkZXslIGVuZGlmICV9IGZ1bmMge3sgbWV0aG9kLm5hbWUgfX0oe3sgbWV0aG9kLnBhcmFtZXRlclNpZ25hdHVyZSB9fSkge3sgbWV0aG9kLnJldHVyblNpZ25hdHVyZSB9fSB7CiAgICAgICAgcmV0dXJuIERlZmF1bHRWYWx1ZVJlZ2lzdHJ5LmRlZmF1bHRWYWx1ZShmb3I6ICh7e21ldGhvZC5yZXR1cm5UeXBlfX0pLnNlbGYpCiAgICB9CiAgICB7JSBlbmRmb3IgJX0KfQo="
|
|
||||||
|
|
||||||
templates["StubbingProxy.swift.stencil"] = "ICAgIHN0cnVjdCBfX1N0dWJiaW5nUHJveHlfe3sgY29udGFpbmVyLm5hbWUgfX06IEN1Y2tvby5TdHViYmluZ1Byb3h5IHsKICAgICAgICBwcml2YXRlIGxldCBjdWNrb29fbWFuYWdlcjogQ3Vja29vLk1vY2tNYW5hZ2VyCgogICAgICAgIGluaXQobWFuYWdlcjogQ3Vja29vLk1vY2tNYW5hZ2VyKSB7CiAgICAgICAgICAgIHNlbGYuY3Vja29vX21hbmFnZXIgPSBtYW5hZ2VyCiAgICAgICAgfQogICAgICAgIHslIGZvciBwcm9wZXJ0eSBpbiBjb250YWluZXIucHJvcGVydGllcyAlfQogICAgICAgIHZhciB7e3Byb3BlcnR5Lm5hbWV9fTogQ3Vja29vLlRvQmVTdHViYmVkeyUgaWYgcHJvcGVydHkuaXNSZWFkT25seSAlfVJlYWRPbmx5eyVlbmRpZiV9UHJvcGVydHk8e3twcm9wZXJ0eS50eXBlfGdlbmVyaWNTYWZlfX0+IHsKICAgICAgICAgICAgcmV0dXJuIC5pbml0KG1hbmFnZXI6IGN1Y2tvb19tYW5hZ2VyLCBuYW1lOiAie3twcm9wZXJ0eS5uYW1lfX0iKQogICAgICAgIH0KICAgICAgICB7JSBlbmRmb3IgJX0KICAgICAgICB7JSBmb3IgbWV0aG9kIGluIGNvbnRhaW5lci5tZXRob2RzICV9CiAgICAgICAgZnVuYyB7e21ldGhvZC5uYW1lfX17e21ldGhvZC5wYXJhbWV0ZXJzfG1hdGNoYWJsZUdlbmVyaWNOYW1lc319KHt7bWV0aG9kLnBhcmFtZXRlcnN8bWF0Y2hhYmxlUGFyYW1ldGVyU2lnbmF0dXJlfX0pIC0+IHt7bWV0aG9kLnN0dWJGdW5jdGlvbn19PCh7e21ldGhvZC5pbnB1dFR5cGVzfGdlbmVyaWNTYWZlfX0peyVpZiBtZXRob2QucmV0dXJuVHlwZSAhPSAiVm9pZCIgJX0sIHt7bWV0aG9kLnJldHVyblR5cGV8Z2VuZXJpY1NhZmV9fXslZW5kaWYlfT57e21ldGhvZC5wYXJhbWV0ZXJzfG1hdGNoYWJsZUdlbmVyaWNXaGVyZX19IHsKICAgICAgICAgICAge3ttZXRob2QucGFyYW1ldGVyc3xwYXJhbWV0ZXJNYXRjaGVyc319CiAgICAgICAgICAgIHJldHVybiAuaW5pdChzdHViOiBjdWNrb29fbWFuYWdlci5jcmVhdGVTdHViKCJ7e21ldGhvZC5mdWxseVF1YWxpZmllZE5hbWV9fSIsIHBhcmFtZXRlck1hdGNoZXJzOiBtYXRjaGVycykpCiAgICAgICAgfQogICAgICAgIHslIGVuZGZvciAlfQogICAgfQo="
|
|
||||||
|
|
||||||
templates["VerificationProxy.swift.stencil"] = "ICAgIHN0cnVjdCBfX1ZlcmlmaWNhdGlvblByb3h5X3t7IGNvbnRhaW5lci5uYW1lIH19OiBDdWNrb28uVmVyaWZpY2F0aW9uUHJveHkgewogICAgICAgIHByaXZhdGUgbGV0IGN1Y2tvb19tYW5hZ2VyOiBDdWNrb28uTW9ja01hbmFnZXIKICAgICAgICBwcml2YXRlIGxldCBjYWxsTWF0Y2hlcjogQ3Vja29vLkNhbGxNYXRjaGVyCiAgICAgICAgcHJpdmF0ZSBsZXQgc291cmNlTG9jYXRpb246IEN1Y2tvby5Tb3VyY2VMb2NhdGlvbgoKICAgICAgICBpbml0KG1hbmFnZXI6IEN1Y2tvby5Nb2NrTWFuYWdlciwgY2FsbE1hdGNoZXI6IEN1Y2tvby5DYWxsTWF0Y2hlciwgc291cmNlTG9jYXRpb246IEN1Y2tvby5Tb3VyY2VMb2NhdGlvbikgewogICAgICAgICAgICBzZWxmLmN1Y2tvb19tYW5hZ2VyID0gbWFuYWdlcgogICAgICAgICAgICBzZWxmLmNhbGxNYXRjaGVyID0gY2FsbE1hdGNoZXIKICAgICAgICAgICAgc2VsZi5zb3VyY2VMb2NhdGlvbiA9IHNvdXJjZUxvY2F0aW9uCiAgICAgICAgfQoKICAgICAgICB7JSBmb3IgcHJvcGVydHkgaW4gY29udGFpbmVyLnByb3BlcnRpZXMgJX0KICAgICAgICB2YXIge3twcm9wZXJ0eS5uYW1lfX06IEN1Y2tvby5WZXJpZnl7JSBpZiBwcm9wZXJ0eS5pc1JlYWRPbmx5ICV9UmVhZE9ubHl7JWVuZGlmJX1Qcm9wZXJ0eTx7e3Byb3BlcnR5LnR5cGV8Z2VuZXJpY1NhZmV9fT4gewogICAgICAgICAgICByZXR1cm4gLmluaXQobWFuYWdlcjogY3Vja29vX21hbmFnZXIsIG5hbWU6ICJ7e3Byb3BlcnR5Lm5hbWV9fSIsIGNhbGxNYXRjaGVyOiBjYWxsTWF0Y2hlciwgc291cmNlTG9jYXRpb246IHNvdXJjZUxvY2F0aW9uKQogICAgICAgIH0KICAgICAgICB7JSBlbmRmb3IgJX0KCiAgICAgICAgeyUgZm9yIG1ldGhvZCBpbiBjb250YWluZXIubWV0aG9kcyAlfQogICAgICAgIEBkaXNjYXJkYWJsZVJlc3VsdAogICAgICAgIGZ1bmMge3ttZXRob2QubmFtZX19e3ttZXRob2QucGFyYW1ldGVyc3xtYXRjaGFibGVHZW5lcmljTmFtZXN9fSh7e21ldGhvZC5wYXJhbWV0ZXJzfG1hdGNoYWJsZVBhcmFtZXRlclNpZ25hdHVyZX19KSAtPiBDdWNrb28uX19Eb05vdFVzZTx7e21ldGhvZC5yZXR1cm5UeXBlfGdlbmVyaWNTYWZlfX0+e3ttZXRob2QucGFyYW1ldGVyc3xtYXRjaGFibGVHZW5lcmljV2hlcmV9fSB7CiAgICAgICAgICAgIHt7bWV0aG9kLnBhcmFtZXRlcnN8cGFyYW1ldGVyTWF0Y2hlcnN9fQogICAgICAgICAgICByZXR1cm4gY3Vja29vX21hbmFnZXIudmVyaWZ5KCJ7e21ldGhvZC5mdWxseVF1YWxpZmllZE5hbWV9fSIsIGNhbGxNYXRjaGVyOiBjYWxsTWF0Y2hlciwgcGFyYW1ldGVyTWF0Y2hlcnM6IG1hdGNoZXJzLCBzb3VyY2VMb2NhdGlvbjogc291cmNlTG9jYXRpb24pCiAgICAgICAgfQogICAgICAgIHslIGVuZGZvciAlfQogICAgfQo="
|
|
||||||
return templates
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,241 +0,0 @@
|
||||||
//
|
|
||||||
// Tokenizer.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Tadeas Kriz on 12/01/16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.flatMap { 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)
|
|
||||||
|
|
||||||
let attributes = dictionary[Key.Attributes.rawValue]
|
|
||||||
print(attributes)
|
|
||||||
let attributeOptional = (dictionary[Key.Attributes.rawValue] as? [Any])?.first(where: {($0 as? [String : String])?[Key.Attribute.rawValue] == Kinds.Optional.rawValue}) != nil
|
|
||||||
|
|
||||||
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,
|
|
||||||
inheritedTypes: tokenizedInheritedTypes)
|
|
||||||
|
|
||||||
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,
|
|
||||||
inheritedTypes: tokenizedInheritedTypes)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if type == nil {
|
|
||||||
stderrPrint("Type of instance variable \(name) could not be inferred. Please specify it explicitly. (\(file.path ?? ""))")
|
|
||||||
}
|
|
||||||
|
|
||||||
return InstanceVariable(
|
|
||||||
name: name,
|
|
||||||
type: type ?? "__UnknownType",
|
|
||||||
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,
|
|
||||||
isOptional: attributeOptional,
|
|
||||||
range: range!,
|
|
||||||
nameRange: nameRange!,
|
|
||||||
parameters: parameters)
|
|
||||||
}
|
|
||||||
|
|
||||||
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?.characters.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 informations 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).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:
|
|
||||||
stderrPrint("Unknown method parameter. Dictionary: \(dictionary) \(file.path ?? "")")
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
//
|
|
||||||
// Attribute.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Tadeas Kriz on 2/25/17.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
public enum Attribute: String {
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
//
|
|
||||||
// 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 let inheritedTypes: [InheritanceDeclaration]
|
|
||||||
public let attributes: [Attribute] = []
|
|
||||||
|
|
||||||
public var hasNoArgInit: Bool {
|
|
||||||
return initializers.filter { $0.parameters.isEmpty }.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
public func replace(children tokens: [Token]) -> ClassDeclaration {
|
|
||||||
return ClassDeclaration(name: self.name,
|
|
||||||
accessibility: self.accessibility,
|
|
||||||
range: self.range,
|
|
||||||
nameRange: self.nameRange,
|
|
||||||
bodyRange: self.bodyRange,
|
|
||||||
initializers: self.initializers,
|
|
||||||
children: tokens,
|
|
||||||
inheritedTypes: self.inheritedTypes)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to other: Token) -> Bool {
|
|
||||||
guard let other = other as? ClassDeclaration else { return false }
|
|
||||||
return self.name == other.name
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
//
|
|
||||||
// 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>
|
|
||||||
|
|
||||||
public var isOptional: Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
//
|
|
||||||
// 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 }
|
|
||||||
var inheritedTypes: [InheritanceDeclaration] { get }
|
|
||||||
var attributes: [Attribute] { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ContainerToken {
|
|
||||||
public func serialize() -> [String : Any] {
|
|
||||||
let properties = children.flatMap { $0 as? InstanceVariable }
|
|
||||||
.filter { $0.accessibility.isAccessible }
|
|
||||||
.map { $0.serializeWithType() }
|
|
||||||
|
|
||||||
let methods = children.flatMap { $0 as? Method }
|
|
||||||
.filter { $0.accessibility.isAccessible && !$0.isInit && !$0.isDeinit }
|
|
||||||
.map { $0.serializeWithType() }
|
|
||||||
|
|
||||||
let initializers = children.flatMap { $0 as? Method }
|
|
||||||
.filter { $0.accessibility.isAccessible && $0.isInit && !$0.isDeinit }
|
|
||||||
.map { $0.serializeWithType() }
|
|
||||||
|
|
||||||
return [
|
|
||||||
"name": name,
|
|
||||||
"accessibility": accessibility,
|
|
||||||
"isAccessible": accessibility.isAccessible,
|
|
||||||
"children": children.map { $0.serializeWithType() },
|
|
||||||
"properties": properties,
|
|
||||||
"methods": methods,
|
|
||||||
"initializers": implementation ? [] : initializers
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// 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>
|
|
||||||
|
|
||||||
public func isEqual(to other: Token) -> Bool {
|
|
||||||
guard let other = other as? ExtensionDeclaration else { return false }
|
|
||||||
return self.range == other.range
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension FileRepresentation {
|
|
||||||
public 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal extension Token {
|
|
||||||
internal func mergeInheritance(with files: [FileRepresentation]) -> Token {
|
|
||||||
guard let typeToken = self as? ContainerToken else {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
let inheritedRepresentations: [Token] = typeToken.inheritedTypes
|
|
||||||
.flatMap { Self.findToken(forClassOrProtocol: $0.name, in: files) }
|
|
||||||
.flatMap { $0.mergeInheritance(with: files) }
|
|
||||||
|
|
||||||
// Merge super declarations
|
|
||||||
let mergedTokens = inheritedRepresentations.filter { $0.isClassOrProtocolDefinition }
|
|
||||||
.map { $0 as! ContainerToken }
|
|
||||||
.flatMap { $0.children }
|
|
||||||
.reduce(typeToken.children) { tokens, inheritedToken in
|
|
||||||
if (tokens.contains { $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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static func findToken(forClassOrProtocol name: String, in files: [FileRepresentation]) -> Token? {
|
|
||||||
return files.flatMap { $0.declarations }
|
|
||||||
.filter { $0.isClassOrProtocolDefinition }
|
|
||||||
.map { $0 as! ContainerToken }
|
|
||||||
.first { $0.name == name }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
||||||
public func isEqual(to other: Token) -> Bool {
|
|
||||||
guard let other = other as? Import else { return false }
|
|
||||||
return self.range == other.range && self.library == other.library
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
||||||
public var isOptional: Bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
//
|
|
||||||
// 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 {
|
|
||||||
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] {
|
|
||||||
return [
|
|
||||||
"name": name,
|
|
||||||
"type": type,
|
|
||||||
"accesibility": accessibility.sourceName,
|
|
||||||
"isReadOnly": readOnly,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
//
|
|
||||||
// .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 }
|
|
||||||
var isOptional: Bool { 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.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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to other: Token) -> Bool {
|
|
||||||
guard let other = other as? Method else { return false }
|
|
||||||
return self.name == other.name
|
|
||||||
}
|
|
||||||
|
|
||||||
public func serialize() -> [String : Any] {
|
|
||||||
let call = parameters.map {
|
|
||||||
if let label = $0.label {
|
|
||||||
return "\(label): \($0.name)"
|
|
||||||
} else {
|
|
||||||
return $0.name
|
|
||||||
}
|
|
||||||
}.joined(separator: ", ")
|
|
||||||
|
|
||||||
let stubFunction: String
|
|
||||||
if isThrowing {
|
|
||||||
if returnType == "Void" {
|
|
||||||
stubFunction = "Cuckoo.StubNoReturnThrowingFunction"
|
|
||||||
} else {
|
|
||||||
stubFunction = "Cuckoo.StubThrowingFunction"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if returnType == "Void" {
|
|
||||||
stubFunction = "Cuckoo.StubNoReturnFunction"
|
|
||||||
} else {
|
|
||||||
stubFunction = "Cuckoo.StubFunction"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
"name": rawName,
|
|
||||||
"accessibility": accessibility.sourceName,
|
|
||||||
"returnSignature": returnSignature,
|
|
||||||
"parameters": parameters,
|
|
||||||
"parameterNames": parameters.map { $0.name }.joined(separator: ", "),
|
|
||||||
"isInit": isInit,
|
|
||||||
"returnType": returnType,
|
|
||||||
"isThrowing": isThrowing,
|
|
||||||
"fullyQualifiedName": fullyQualifiedName,
|
|
||||||
"call": call,
|
|
||||||
"parameterSignature": parameters.map { "\($0.labelAndName): \($0.type)" }.joined(separator: ", "),
|
|
||||||
"parameterSignatureWithoutNames": parameters.map { "\($0.name): \($0.type)" }.joined(separator: ", "),
|
|
||||||
"stubFunction": stubFunction,
|
|
||||||
"inputTypes": parameters.map { $0.typeWithoutAttributes }.joined(separator: ", "),
|
|
||||||
"isOptional": isOptional
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to other: Token) -> Bool {
|
|
||||||
guard let other = other as? MethodParameter else { return false }
|
|
||||||
return self.name == other.name
|
|
||||||
}
|
|
||||||
|
|
||||||
public func serialize() -> [String : Any] {
|
|
||||||
return [
|
|
||||||
"label": label,
|
|
||||||
"name": name,
|
|
||||||
"type": type,
|
|
||||||
"labelAndName": labelAndName,
|
|
||||||
"typeWithoutAttributes": typeWithoutAttributes
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
public let inheritedTypes: [InheritanceDeclaration]
|
|
||||||
public let attributes: [Attribute] = []
|
|
||||||
|
|
||||||
public func replace(children tokens: [Token]) -> ProtocolDeclaration {
|
|
||||||
return ProtocolDeclaration(name: self.name,
|
|
||||||
accessibility: self.accessibility,
|
|
||||||
range: self.range,
|
|
||||||
nameRange: self.nameRange,
|
|
||||||
bodyRange: self.bodyRange,
|
|
||||||
initializers: self.initializers,
|
|
||||||
children: tokens,
|
|
||||||
inheritedTypes: self.inheritedTypes)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to other: Token) -> Bool {
|
|
||||||
guard let other = other as? ProtocolDeclaration else { return false }
|
|
||||||
return self.name == other.name
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// 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 isOptional: Bool
|
|
||||||
public let range: CountableRange<Int>
|
|
||||||
public let nameRange: CountableRange<Int>
|
|
||||||
public let parameters: [MethodParameter]
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
//
|
|
||||||
// Token.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Filip Dolnik on 30.05.16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
public var isClassOrProtocolDefinition: Bool {
|
|
||||||
switch self {
|
|
||||||
case _ as ProtocolDeclaration:
|
|
||||||
fallthrough
|
|
||||||
case _ as ClassDeclaration:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var isInheritanceDefinition: Bool {
|
|
||||||
switch self {
|
|
||||||
case _ as InheritanceDeclaration:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
//
|
|
||||||
// String+Utility.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Tadeas Kriz on 12/01/16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// FileHeaderHandler.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Tadeas Kriz on 12/01/16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import FileKit
|
import FileKit
|
||||||
|
|
||||||
|
@ -24,9 +16,9 @@ public struct FileHeaderHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
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.library + "\n" })).sorted().joined(separator: "")
|
var imports = Array(Set(file.declarations.only(Import.self).map { "import \($0.importee)\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
|
||||||
}
|
}
|
||||||
|
@ -45,7 +37,7 @@ public struct FileHeaderHandler {
|
||||||
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.characters.count > multiLine.characters.count ? singleLine : multiLine
|
return singleLine.count > multiLine.count ? singleLine : multiLine
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getPossibleHeaderEnd(current: Int, declarations: [Token]) -> Int {
|
private static func getPossibleHeaderEnd(current: Int, declarations: [Token]) -> Int {
|
||||||
|
@ -68,7 +60,7 @@ public struct FileHeaderHandler {
|
||||||
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 text.substring(to: lastLine.upperBound)
|
return String(text[..<lastLine.upperBound])
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -76,7 +68,7 @@ public struct FileHeaderHandler {
|
||||||
|
|
||||||
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 text.substring(to: range.upperBound) + "\n"
|
return String(text[..<range.upperBound]) + "\n"
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// StderrPrint.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Filip Dolnik on 18.12.16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public private(set) var stderrUsed = false
|
public private(set) var stderrUsed = false
|
|
@ -0,0 +1,153 @@
|
||||||
|
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 %}
|
||||||
|
"""
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
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 %}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
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 %}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
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 %}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
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 %}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
|
@ -0,0 +1,603 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// Accessibility.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Filip Dolnik on 30.05.16.
|
|
||||||
// Copyright © 2016 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
public enum Accessibility: String {
|
public enum Accessibility: String {
|
||||||
case Open = "source.lang.swift.accessibility.open"
|
case Open = "source.lang.swift.accessibility.open"
|
||||||
case Public = "source.lang.swift.accessibility.public"
|
case Public = "source.lang.swift.accessibility.public"
|
||||||
|
@ -32,3 +24,25 @@ public enum Accessibility: String {
|
||||||
return self != .Private && self != .FilePrivate
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol ChildToken: Token {
|
||||||
|
var parent: Reference<ParentToken>? { get set }
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
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
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
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,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol HasAccessibility {
|
||||||
|
var accessibility: Accessibility { get set }
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
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(")))" : ""
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// InheritanceDeclaration.swift
|
|
||||||
// CuckooGenerator
|
|
||||||
//
|
|
||||||
// Created by Arjan Duijzer on 22/02/2017.
|
|
||||||
// Copyright (c) 2017 Brightify. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
public struct InheritanceDeclaration: Token {
|
public struct InheritanceDeclaration: Token {
|
||||||
public static let empty = InheritanceDeclaration(name: Tokenizer.nameNotSet)
|
public static let empty = InheritanceDeclaration(name: Tokenizer.nameNotSet)
|
||||||
public let name: String
|
public let name: String
|
||||||
|
@ -14,4 +6,4 @@ public struct InheritanceDeclaration: Token {
|
||||||
guard let other = other as? InheritanceDeclaration else { return false }
|
guard let other = other as? InheritanceDeclaration else { return false }
|
||||||
return self.name == other.name
|
return self.name == other.name
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
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,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,3 @@
|
||||||
//
|
|
||||||
// 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"
|
|
@ -1,17 +1,12 @@
|
||||||
//
|
|
||||||
// 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 Optional = "source.decl.attribute.optional"
|
case Optional = "source.decl.attribute.optional"
|
||||||
}
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
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() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
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: " ")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
import Foundation
|
||||||
|
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 = index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)
|
||||||
|
return String(self[stringRange])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String.UTF8View {
|
||||||
|
subscript(range: Range<Int>) -> String {
|
||||||
|
let stringRange = index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)
|
||||||
|
let subsequence: String.UTF8View.SubSequence = self[stringRange]
|
||||||
|
return String(subsequence) ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
func regexMatches(_ source: String) -> Bool {
|
||||||
|
let regex = try! NSRegularExpression(pattern: self)
|
||||||
|
return regex.firstMatch(in: source, range: NSRange(location: 0, length: source.count)) != nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Sequence {
|
||||||
|
#if !swift(>=4.1)
|
||||||
|
public func compactMap<O>(_ transform: (Element) -> O?) -> [O] {
|
||||||
|
return self.flatMap(transform)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
func only<T>(_ type: T.Type) -> [T] {
|
||||||
|
return compactMap { $0 as? T }
|
||||||
|
}
|
||||||
|
|
||||||
|
func noneOf<T>(_ type: T.Type) -> [Iterator.Element] {
|
||||||
|
return filter { !($0 is T) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reserved keywords that are not allowed as function names, function parameters, or local variables, etc.
|
||||||
|
fileprivate let reservedKeywordsNotAllowed: Set = [
|
||||||
|
// Keywords used in declarations:
|
||||||
|
"associatedtype", "class", "deinit", "enum", "extension", "fileprivate", "func", "import", "init", "inout",
|
||||||
|
"internal", "let", "operator", "private", "precedencegroup", "protocol", "public", "rethrows", "static",
|
||||||
|
"struct", "subscript", "typealias", "var",
|
||||||
|
// Keywords used in statements:
|
||||||
|
"break", "case", "catch", "continue", "default", "defer", "do", "else", "fallthrough", "for", "guard", "if", "in",
|
||||||
|
"repeat", "return", "throw", "switch", "where", "while",
|
||||||
|
// Keywords used in expressions and types:
|
||||||
|
"Any", "as", "catch", "false", "is", "nil", "rethrows", "self", "super", "throw", "throws", "true", "try",
|
||||||
|
// Keywords used in patterns:
|
||||||
|
"_",
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Utility function for escaping reserved keywords for a symbol name.
|
||||||
|
internal func escapeReservedKeywords(for name: String) -> String {
|
||||||
|
reservedKeywordsNotAllowed.contains(name) ? "`\(name)`" : name
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func extractRange(from dictionary: [String: SourceKitRepresentable], offset: Key, length: Key) -> CountableRange<Int>? {
|
||||||
|
guard let offset = (dictionary[offset.rawValue] as? Int64).map(Int.init),
|
||||||
|
let length = (dictionary[length.rawValue] as? Int64).map(Int.init) else { return nil }
|
||||||
|
|
||||||
|
return offset..<offset.advanced(by: length)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue