From a0ea3abf97afdfefd951dde27a9974eaa6292ade Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Fri, 29 Mar 2024 21:04:11 -0400 Subject: [PATCH 01/25] Updating Scripts --- .circleci/config.yml | 90 ---- .github/workflows/Options.yml | 203 +++++++++ .github/workflows/arm.yml | 75 ---- .github/workflows/macOS.yml | 57 --- .github/workflows/ubuntu.yml | 41 -- .gitignore | 117 ++--- .periphery.yml | 1 + .spi.yml | 4 + .swift-version | 2 +- .swiftformat | 8 +- .swiftlint.yml | 28 +- .travis.yml | 30 -- Mintfile | 2 + Package-5.9.swift | 64 +++ Package.swift | 7 - Package@swift-5.9.swift | 64 +++ Scripts/Dockerfile | 6 - Scripts/before_install.sh | 36 -- Scripts/docc.sh | 3 + Scripts/gh-md-toc | 411 ++++++++++++++++++ Scripts/images.sh | 16 - Scripts/lint.sh | 44 ++ Scripts/script.sh | 36 -- Sources/Options/EnumSet.swift | 4 +- .../MappedValueCollectionRepresented.swift | 8 +- .../MappedValueDictionaryRepresented.swift | 6 +- .../Options/MappedValueRepresentable.swift | 33 +- .../MappedValueRepresentableError.swift | 29 ++ Tests/LinuxMain.swift | 29 ++ Tests/OptionsTests/EnumSetTests.swift | 29 ++ Tests/OptionsTests/MappedEnumTests.swift | 29 ++ ...appedValueCollectionRepresentedTests.swift | 29 ++ ...appedValueDictionaryRepresentedTests.swift | 29 ++ .../MappedValueRepresentableTests.swift | 29 ++ .../Mocks/MockCollectionEnum.swift | 29 ++ .../Mocks/MockDictionaryEnum.swift | 29 ++ Tests/OptionsTests/Mocks/MockError.swift | 29 ++ Tests/OptionsTests/XCTestManifests.swift | 31 +- bitrise.yml | 40 -- project.yml | 13 + 40 files changed, 1226 insertions(+), 544 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/Options.yml delete mode 100644 .github/workflows/arm.yml delete mode 100644 .github/workflows/macOS.yml delete mode 100644 .github/workflows/ubuntu.yml create mode 100644 .periphery.yml create mode 100644 .spi.yml delete mode 100644 .travis.yml create mode 100644 Mintfile create mode 100644 Package-5.9.swift create mode 100644 Package@swift-5.9.swift delete mode 100644 Scripts/Dockerfile delete mode 100644 Scripts/before_install.sh create mode 100755 Scripts/docc.sh create mode 100755 Scripts/gh-md-toc delete mode 100755 Scripts/images.sh create mode 100755 Scripts/lint.sh delete mode 100644 Scripts/script.sh delete mode 100644 bitrise.yml create mode 100644 project.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 426b0d0..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,90 +0,0 @@ -# For a detailed guide to building and testing on iOS, read the docs: -# https://circleci.com/docs/2.0/testing-ios/ - -version: 2.1 -parameters: - package-name: - type: string - default: "Options" - swift-ver: - type: string - default: "5.3.3" - codecov-upload-file: - type: string - default: "info.lcov" -orbs: - codecov: codecov/codecov@1.0.5 -jobs: - build-xenial: - machine: - image: ubuntu-1604:201903-01 - environment: - PACKAGE_NAME: << pipeline.parameters.package-name >> - SWIFT_VER: << pipeline.parameters.swift-ver >> - steps: - - checkout - - run: - name: Update PATH and Define Environment Variable at Runtime - command: | - echo 'export RELEASE_DOT=$(lsb_release -sr)' >> $BASH_ENV - echo 'export RELEASE_NAME=$(lsb_release -sc)' >> $BASH_ENV - echo 'export RELEASE_NUM=${RELEASE_DOT//[-._]/}' >> $BASH_ENV - source $BASH_ENV - - run: - name: Download Swift 5.2 - command: wget -q https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - run: - name: Extract Swift 5.2 - command: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - run: - name: Add Path - command: echo 'export PATH=${PWD}/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin:$PATH' >> $BASH_ENV - - run: - name: Resolve - command: swift package resolve - - run: - name: Build - command: swift build -v - - run: - name: Run tests - command: swift test --enable-test-discovery --enable-code-coverage - - run: - name: Prepare Code Coverage - command: llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/debug/${PACKAGE_NAME}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov - - codecov/upload: - file: << pipeline.parameters.codecov-upload-file >> - flags: circleci,${RELEASE_NAME} - build-catalina-11_4_1: - macos: - xcode: "11.4.1" - environment: - PACKAGE_NAME: << pipeline.parameters.package-name >> - steps: - - checkout - - run: - name: Build - command: swift build - - run: - name: Lint - command: swiftformat --lint . && swiftlint - - run: - name: Run Swift Package Tests - command: swift test -v --enable-code-coverage - - run: - name: Prepare Code Coverage - command: xcrun llvm-cov export -format="lcov" .build/debug/${PACKAGE_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - - codecov/upload: - file: << pipeline.parameters.codecov-upload-file >> - flags: circleci,macOS - - run: - name: Run iOS Tests - command: xcodebuild build test -scheme ${PACKAGE_NAME}-Package -destination 'name=iPhone 11' - - codecov/upload: - file: << pipeline.parameters.codecov-upload-file >> - flags: circleci,iOS -workflows: - version: 2 - build: - jobs: - #- build-catalina-11_4_1 - - build-xenial diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml new file mode 100644 index 0000000..d1b2ec3 --- /dev/null +++ b/.github/workflows/Options.yml @@ -0,0 +1,203 @@ +name: macOS + +on: + push: + branches-ignore: + - '*WIP' + +jobs: + build-ubuntu: + name: Build on Ubuntu + env: + PACKAGE_NAME: Options + SWIFT_VER: ${{ matrix.swift-version }} + runs-on: ${{ matrix.runs-on }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + matrix: + runs-on: [ubuntu-20.04, ubuntu-22.04] + swift-version: [5.9, "5.10"] + steps: + - uses: actions/checkout@v4 + - name: Cache swift package modules + id: cache-spm-linux + uses: actions/cache@v4 + env: + cache-name: SPM + with: + path: .build + key: ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }}-${{ hashFiles('Package.resolved') }}-${{ env.RELEASE_DOT }} + restore-keys: | + ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }}-${{ hashFiles('Package.resolved') }} + ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }} + - name: Set Ubuntu Release DOT + run: echo "RELEASE_DOT=$(lsb_release -sr)" >> $GITHUB_ENV + - name: Set Ubuntu Release NUM + run: echo "RELEASE_NUM=${RELEASE_DOT//[-._]/}" >> $GITHUB_ENV + - name: Set Ubuntu Codename + run: echo "RELEASE_NAME=$(lsb_release -sc)" >> $GITHUB_ENV + - name: Cache swift + id: cache-swift-linux + uses: actions/cache@v4 + env: + cache-name: swift + with: + path: swift-${{ env.SWIFT_VER }}-RELEASE-ubuntu${{ env.RELEASE_DOT }} + key: ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }}-${{ env.RELEASE_DOT }} + - name: Download Swift + if: steps.cache-swift-linux.outputs.cache-hit != 'true' + run: curl -O https://download.swift.org/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz + - name: Extract Swift + if: steps.cache-swift-linux.outputs.cache-hit != 'true' + run: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz + - name: Add Path + run: echo "$GITHUB_WORKSPACE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin" >> $GITHUB_PATH + - name: Build + run: swift build + - name: Test + run: swift test --enable-code-coverage + - uses: sersoft-gmbh/swift-coverage-action@v4 + with: + fail-on-empty-output: true + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: spm,${{ env.RELEASE_NAME }},${{ env.SWIFT_VER }} + build-macos: + name: Build on macOS + env: + PACKAGE_NAME: Options + SWIFT_VER: ${{ matrix.swift-version }} + runs-on: ${{ matrix.runs-on }} + if: "!contains(github.event.head_commit.message, 'ci skip')" + strategy: + matrix: + runs-on: macos-11.0 + xcode: ["/Applications/Xcode_11.7.app","/Applications/Xcode_12.app","/Applications/Xcode_12.1.app","/Applications/Xcode_12.2.app","/Applications/Xcode_12.3.app", "/Applications/Xcode_12.4.app"] + steps: + - uses: actions/checkout@v4 + - name: Cache swift package modules + id: cache-spm-macos + uses: actions/cache@v4 + env: + cache-name: cache-spm + with: + path: .build + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Package.resolved') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Cache mint + if: ${{ github.event_name == 'pull_request' && ( github.base_ref == 'main' || endsWith( github.ref_name , 'Prep') ) && matrix.xcode == '/Applications/Xcode_15.2.app' }} + id: cache-mint + uses: actions/cache@v4 + env: + cache-name: cache-mint + with: + path: .mint + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Mintfile') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Set Xcode Name + run: echo "XCODE_NAME=$(basename -- ${{ matrix.xcode }} | sed 's/\.[^.]*$//' | cut -d'_' -f2)" >> $GITHUB_ENV + - name: Setup Xcode + run: sudo xcode-select -s ${{ matrix.xcode }}/Contents/Developer + - name: Install mint + if: ${{ github.event_name == 'pull_request' && ( github.base_ref == 'main' || endsWith( github.ref_name , 'Prep') ) && matrix.xcode == '/Applications/Xcode_15.2.app' }} + run: | + brew update + brew install mint + - name: Build + run: swift build + - name: Run Swift Package tests + run: swift test --enable-code-coverage + - uses: sersoft-gmbh/swift-coverage-action@v4 + with: + fail-on-empty-output: true + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + env: + token: ${{ secrets.CODECOV_TOKEN }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: macOS,${{ env.XCODE_NAME }} + - name: Clean up spm build directory + run: rm -rf .build + - name: Lint + run: ./scripts/lint.sh + if: ${{ github.event_name == 'pull_request' && ( github.base_ref == 'main' || endsWith( github.ref_name , 'Prep') ) && matrix.xcode == '/Applications/Xcode_15.2.app' }} + # - name: Run iOS target tests + # run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=${{ matrix.iPhoneName }},OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test + # - uses: sersoft-gmbh/swift-coverage-action@v4 + # - name: Upload coverage reports to Codecov + # uses: codecov/codecov-action@v4 + # env: + # token: ${{ secrets.CODECOV_TOKEN }} + # with: + # flags: iOS,iOS${{ matrix.iOSVersion }},macOS,${{ env.XCODE_NAME }} + # - name: Run watchOS target tests + # run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }} -sdk watchsimulator -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test + # - uses: sersoft-gmbh/swift-coverage-action@v4 + # with: + # fail-on-empty-output: true + # - name: Upload coverage reports to Codecov + # uses: codecov/codecov-action@v4 + # env: + # token: ${{ secrets.CODECOV_TOKEN }} + # with: + # token: ${{ secrets.CODECOV_TOKEN }} + # flags: watchOS,watchOS${{ matrix.watchOSVersion }},macOS,${{ env.XCODE_NAME }} + # - name: Test Again + # run: swift test + build-self: + name: Build on Self-Hosting macOS + runs-on: [self-hosted, macOS] + if: github.event.repository.owner.login == github.event.organization.login && !contains(github.event.head_commit.message, 'ci skip') + steps: + - uses: actions/checkout@v4 + - name: Cache swift package modules + id: cache-spm-macos + uses: actions/cache@v4 + env: + cache-name: cache-spm + with: + path: .build + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Package.resolved') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Cache mint + id: cache-mint + uses: actions/cache@v4 + env: + cache-name: cache-mint + with: + path: .mint + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Mintfile') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Build + run: swift build + - name: Run Swift Package tests + run: swift test --enable-code-coverage + - uses: sersoft-gmbh/swift-coverage-action@v4 + with: + fail-on-empty-output: true + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + env: + token: ${{ secrets.CODECOV_TOKEN }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: macOS,${{ env.XCODE_NAME }} + - name: Clean up spm build directory + run: rm -rf .build + - name: Lint + run: ./scripts/lint.sh diff --git a/.github/workflows/arm.yml b/.github/workflows/arm.yml deleted file mode 100644 index 5335a7d..0000000 --- a/.github/workflows/arm.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: arm - -on: [push] - -jobs: - build: - env: - PACKAGE_NAME: Options - SWIFT_VER: 5.2.4 - - runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, 'ci skip')" - - strategy: - matrix: - arch: [aarch64] - distro: [ubuntu18.04,ubuntu20.04] - steps: - - uses: actions/checkout@v2 - - name: Build with Swift on arm - uses: uraimo/run-on-arch-action@v2.0.4 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - with: - arch: ${{ matrix.arch }} - distro: ${{ matrix.distro }} - githubToken: ${{ github.token }} - run: | - export DEBIAN_FRONTEND=noninteractive - ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime - apt update - apt install -y curl lsb-release sudo clang git - RELEASE_DOT=$(lsb_release -sr) - RELEASE_NUM=${RELEASE_DOT//[-._]/} - RELEASE_NAME=$(lsb_release -sc) - if [[ $RELEASE_DOT == "20.04" ]]; then - sudo apt-get -y install \ - binutils \ - git \ - gnupg2 \ - libc6-dev \ - libcurl4 \ - libedit2 \ - libgcc-9-dev \ - libpython2.7 \ - libsqlite3-0 \ - libstdc++-9-dev \ - libxml2 \ - libz3-dev \ - pkg-config \ - tzdata \ - zlib1g-dev \ - libsqlite3-dev - else - apt-get -y install \ - binutils \ - git \ - libc6-dev \ - libcurl4 \ - libedit2 \ - libgcc-5-dev \ - libpython2.7 \ - libsqlite3-0 \ - libstdc++-5-dev \ - libxml2 \ - pkg-config \ - tzdata \ - zlib1g-dev \ - libsqlite3-dev - fi - dpkg-reconfigure --frontend noninteractive tzdata - curl -s https://packagecloud.io/install/repositories/swift-arm/release/script.deb.sh | sudo bash - apt-get install -y swift-lang - swift build -v - swift test --enable-test-discovery --enable-code-coverage -v diff --git a/.github/workflows/macOS.yml b/.github/workflows/macOS.yml deleted file mode 100644 index 6ee36eb..0000000 --- a/.github/workflows/macOS.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: macOS - -on: - push: - branches: - - '*' - - 'feature/*' - - 'release/*' - tags: '*' - -jobs: - build: - env: - PACKAGE_NAME: Options - - runs-on: macos-latest - if: "!contains(github.event.head_commit.message, 'ci skip')" - - strategy: - matrix: - runs-on: [macos-10.15,macos-11.0] - xcode: ["/Applications/Xcode_11.7.app","/Applications/Xcode_12.app","/Applications/Xcode_12.1.app","/Applications/Xcode_12.2.app","/Applications/Xcode_12.3.app"] - include: - - os: macos-10.15 - xcode: "/Applications/Xcode_11.5.app" - - os: macos-11.0 - xcode: "/Applications/Xcode_12.4.app" - - steps: - - uses: actions/checkout@v2 - - name: Set Xcode Name - run: echo "XCODE_NAME=$(basename -- ${{ matrix.xcode }} | sed 's/\.[^.]*$//' | cut -d'_' -f2)" >> $GITHUB_ENV - - name: Setup Xcode - run: sudo xcode-select -s ${{ matrix.xcode }}/Contents/Developer - - name: Build - run: swift build -v - - name: Lint - if: startsWith(github.ref, 'refs/tags/') != true - run: swift run swiftformat --lint . && swift run swiftlint - - name: Run tests - run: swift test -v --enable-code-coverage - - name: Prepare Code Coverage - run: xcrun llvm-cov export -format="lcov" .build/debug/${{ env.PACKAGE_NAME }}PackageTests.xctest/Contents/MacOS/${{ env.PACKAGE_NAME }}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - - name: Upload to CodeCov.io - run: bash <(curl https://codecov.io/bash) -F github -F macOS -F ${{ matrix.runs-on }} - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - name: Build Documentation - if: ${{ matrix.os == 'macos-11.0' && matrix.xcode == '/Applications/Xcode_12.4.app' && !startsWith(github.ref, 'refs/tags/') }} - run: | - swift run sourcedocs generate build -cra - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git status - git add Documentation - git diff-index --quiet HEAD || git commit -m "[github action] Update Docs" - git push diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml deleted file mode 100644 index 4c72419..0000000 --- a/.github/workflows/ubuntu.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: ubuntu - -on: [push] - -jobs: - build: - env: - PACKAGE_NAME: Options - SWIFT_VER: ${{ matrix.swift-version }} - - runs-on: ${{ matrix.runs-on }} - if: "!contains(github.event.head_commit.message, 'ci skip')" - - strategy: - matrix: - runs-on: [ubuntu-18.04,ubuntu-20.04] - swift-version: [5.2.5, 5.3.3] - steps: - - uses: actions/checkout@v2 - - name: Set Ubuntu Release DOT - run: echo "RELEASE_DOT=$(lsb_release -sr)" >> $GITHUB_ENV - - name: Set Ubuntu Release NUM - run: echo "RELEASE_NUM=${RELEASE_DOT//[-._]/}" >> $GITHUB_ENV - - name: Set Ubuntu Codename - run: echo "RELEASE_NAME=$(lsb_release -sc)" >> $GITHUB_ENV - - name: Download Swift - run: curl -O https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - name: Extract Swift - run: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - - name: Add Path - run: echo "$GITHUB_WORKSPACE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin" >> $GITHUB_PATH - - name: Build - run: swift build - - name: Run tests - run: swift test --enable-test-discovery --enable-code-coverage - - name: Prepare Code Coverage - run: llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/debug/${{ env.PACKAGE_NAME }}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov - - name: Upload to CodeCov.io - run: bash <(curl https://codecov.io/bash) -F github -F ${RELEASE_NAME} -F ${SWIFT_VER} - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 1ffb7cf..bd44408 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,16 @@ -# Created by https://www.gitignore.io/api/swiftpm,swiftpackagemanager,xcode,swift,macos -# Edit at https://www.gitignore.io/?templates=swiftpm,swiftpackagemanager,xcode,swift,macos - +# Created by https://www.toptal.com/developers/gitignore/api/swift,swiftpm,swiftpackagemanager,xcode,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=swift,swiftpm,swiftpackagemanager,xcode,macos ### macOS ### # General .DS_Store -*.DS_Store - .AppleDouble .LSOverride # Icon must end with two \r Icon + # Thumbnails ._* @@ -32,20 +30,26 @@ Network Trash Folder Temporary Items .apdisk +### macOS Patch ### +# iCloud generated files +*.icloud + ### Swift ### # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Build generated -build/ -DerivedData/ +## User settings +xcuserdata/ -## Various settings -# Xcode -# -build/ +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside *.pbxuser !default.pbxuser *.mode1v3 @@ -54,17 +58,11 @@ build/ !default.mode2v3 *.perspectivev3 !default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xccheckout -*.xcuserstate - -*.xcscmblueprint ## Obj-C/Swift specific *.hmap + +## App packaging *.ipa *.dSYM.zip *.dSYM @@ -74,43 +72,45 @@ timeline.xctimeline playground.xcworkspace # Swift Package Manager +# # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved +*.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm + .build/ -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate # CocoaPods - +# # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control - -Example/Pods/ -Example/*.xcworkspace +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace # Carthage - +# # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts -Carthage/Build +Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane - -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control @@ -120,50 +120,11 @@ fastlane/screenshots/**/*.png fastlane/test_output # Code Injection +# # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode -VCS.swift iOSInjectionProject/ -Products - - -### SwiftPackageManager ### -Packages -xcuserdata -/*.xcodeproj -.tmp - - -### SwiftPM ### - - -### Xcode ### -# Xcode -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## User settings - -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) - -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) - -## Xcode Patch -/*.xcodeproj/* -.swiftpm -!*.xcodeproj/project.pbxproj -!*.xcodeproj/xcshareddata/ -!*.xcworkspace/contents.xcworkspacedata -/*.gcno - -### Xcode Patch ### -**/xcshareddata/WorkspaceSettings.xcsettings - -# End of https://www.gitignore.io/api/swiftpm,swiftpackagemanager,xcode,swift,macos -*.xcworkspace -Pods -*.lcov -Brewfile.lock.json -Package.resolved -gh-md-toc +.mint +Output \ No newline at end of file diff --git a/.periphery.yml b/.periphery.yml new file mode 100644 index 0000000..85b884a --- /dev/null +++ b/.periphery.yml @@ -0,0 +1 @@ +retain_public: true diff --git a/.spi.yml b/.spi.yml new file mode 100644 index 0000000..e65a103 --- /dev/null +++ b/.spi.yml @@ -0,0 +1,4 @@ +version: 1 +builder: + configs: + - documentation_targets: [SimulatorServices] diff --git a/.swift-version b/.swift-version index 7ed6ff8..760606e 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5 +5.7 diff --git a/.swiftformat b/.swiftformat index 7001c5e..c510d49 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,7 +1,7 @@ --indent 2 ---header strip +--header "\n .*?\.swift\n SimulatorServices\n\n Created by Leo Dion.\n Copyright © {year} BrightDigit.\n\n Permission is hereby granted, free of charge, to any person\n obtaining a copy of this software and associated documentation\n files (the “Software”), to deal in the Software without\n restriction, including without limitation the rights to use,\n copy, modify, merge, publish, distribute, sublicense, and/or\n sell copies of the Software, and to permit persons to whom the\n Software is furnished to do so, subject to the following\n conditions:\n \n The above copyright notice and this permission notice shall be\n included in all copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,\n EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n OTHER DEALINGS IN THE SOFTWARE.\n" --commas inline ---disable wrapMultilineStatementBraces ---extensionacl on-extension +--disable wrapMultilineStatementBraces, redundantInternal +--extensionacl on-declarations --decimalgrouping 3,4 ---exclude .build, DerivedData +--exclude .build, DerivedData, .swiftpm diff --git a/.swiftlint.yml b/.swiftlint.yml index 981984b..aebc975 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,5 +1,4 @@ opt_in_rules: - - anyobject_protocol - array_init - attributes - closure_body_length @@ -22,11 +21,9 @@ opt_in_rules: - expiring_todo - explicit_acl - explicit_init - - explicit_self - explicit_top_level_acl - fallthrough - fatal_error_message - - file_header - file_name - file_name_no_space - file_types_order @@ -38,6 +35,7 @@ opt_in_rules: - identical_operands - implicit_return - implicitly_unwrapped_optional + - indentation_width - joined_default_parameter - last_where - legacy_multiple @@ -80,7 +78,6 @@ opt_in_rules: - static_operator - strict_fileprivate - strong_iboutlet - - switch_case_on_newline - toggle_bool - trailing_closure - type_contents_order @@ -88,23 +85,24 @@ opt_in_rules: - unneeded_parentheses_in_closure_argument - unowned_variable_capture - untyped_error_in_catch - - unused_declaration - - unused_import - vertical_parameter_alignment_on_call - - vertical_whitespace_between_cases - vertical_whitespace_closing_braces - vertical_whitespace_opening_braces - xct_specific_matcher - yoda_condition -cyclomatic_complexity: - - 6 - - 9 +analyzer_rules: + - explicit_self + - unused_declaration + - unused_import +type_body_length: + - 100 + - 200 file_length: - 200 - - 550 + - 300 function_body_length: - - 15 - - 25 + - 18 + - 40 function_parameter_count: 8 line_length: - 90 @@ -113,9 +111,9 @@ identifier_name: excluded: - id excluded: - - Tests/**/XCTestManifests.swift - - Tests/LinuxMain.swift + - Tests - DerivedData - .build + - .swiftpm indentation_width: indentation_width: 2 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 455bc95..0000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -jobs: - include: - - os: linux - dist: bionic - arch: amd64 - - os: linux - dist: focal - arch: amd64 - - os: linux - dist: bionic - arch: arm64 - - os: linux - dist: focal - arch: arm64 - - os: osx - osx_image: xcode11.4 - - os: osx - osx_image: xcode11.5 - - os: osx - osx_image: xcode11.6 - - os: osx - osx_image: xcode12 -env: - global: - - FRAMEWORK_NAME=Options - - SWIFT_VER=5.3.3 -before_install: - - bash -e ./Scripts/before_install.sh -script: - - bash -e ./Scripts/script.sh diff --git a/Mintfile b/Mintfile new file mode 100644 index 0000000..c1dc548 --- /dev/null +++ b/Mintfile @@ -0,0 +1,2 @@ +nicklockwood/SwiftFormat@0.53.5 +realm/SwiftLint@0.54.0 \ No newline at end of file diff --git a/Package-5.9.swift b/Package-5.9.swift new file mode 100644 index 0000000..4b5c863 --- /dev/null +++ b/Package-5.9.swift @@ -0,0 +1,64 @@ +// swift-tools-version:5.2 + +// swiftlint:disable explicit_top_level_acl +// swiftlint:disable prefixed_toplevel_constant +// swiftlint:disable line_length +// swiftlint:disable explicit_acl + +import PackageDescription + +let package = Package( + name: "Options", + products: [ + .library( + name: "Options", + targets: ["Options"] + ) + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0") + .package(url: "https://github.com/shibapm/Komondor", from: "1.0.6"), // dev + .package(url: "https://github.com/eneko/SourceDocs", from: "1.2.1"), // dev + .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.47.0"), // dev + .package(url: "https://github.com/realm/SwiftLint", from: "0.41.0"), // dev + .package(url: "https://github.com/shibapm/Rocket", .branch("master")), // dev + .package(url: "https://github.com/mattpolzin/swift-test-codecov", .branch("master")) // dev + ], + targets: [ + .target( + name: "Options", + dependencies: [] + ), + .testTarget( + name: "OptionsTests", + dependencies: ["Options"] + ) + ] +) + +#if canImport(PackageConfig) + import PackageConfig + + let requiredCoverage: Int = 90 + + let config = PackageConfiguration([ + "komondor": [ + "pre-push": [ + "swift test --enable-code-coverage --enable-test-discovery", + "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)" + ], + "pre-commit": [ + "swift test --generate-linuxmain", + "swift test --enable-code-coverage --enable-test-discovery", + "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)", + "swift run swiftformat .", + "swift run swiftlint autocorrect", + "swift run sourcedocs generate build -cra", + "git add .", + "swift run swiftformat --lint .", + "swift run swiftlint --strict" + ] + ] + ]).write() +#endif diff --git a/Package.swift b/Package.swift index 4b5c863..59ad969 100644 --- a/Package.swift +++ b/Package.swift @@ -17,13 +17,6 @@ let package = Package( ], dependencies: [ // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0") - .package(url: "https://github.com/shibapm/Komondor", from: "1.0.6"), // dev - .package(url: "https://github.com/eneko/SourceDocs", from: "1.2.1"), // dev - .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.47.0"), // dev - .package(url: "https://github.com/realm/SwiftLint", from: "0.41.0"), // dev - .package(url: "https://github.com/shibapm/Rocket", .branch("master")), // dev - .package(url: "https://github.com/mattpolzin/swift-test-codecov", .branch("master")) // dev ], targets: [ .target( diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift new file mode 100644 index 0000000..4b5c863 --- /dev/null +++ b/Package@swift-5.9.swift @@ -0,0 +1,64 @@ +// swift-tools-version:5.2 + +// swiftlint:disable explicit_top_level_acl +// swiftlint:disable prefixed_toplevel_constant +// swiftlint:disable line_length +// swiftlint:disable explicit_acl + +import PackageDescription + +let package = Package( + name: "Options", + products: [ + .library( + name: "Options", + targets: ["Options"] + ) + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0") + .package(url: "https://github.com/shibapm/Komondor", from: "1.0.6"), // dev + .package(url: "https://github.com/eneko/SourceDocs", from: "1.2.1"), // dev + .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.47.0"), // dev + .package(url: "https://github.com/realm/SwiftLint", from: "0.41.0"), // dev + .package(url: "https://github.com/shibapm/Rocket", .branch("master")), // dev + .package(url: "https://github.com/mattpolzin/swift-test-codecov", .branch("master")) // dev + ], + targets: [ + .target( + name: "Options", + dependencies: [] + ), + .testTarget( + name: "OptionsTests", + dependencies: ["Options"] + ) + ] +) + +#if canImport(PackageConfig) + import PackageConfig + + let requiredCoverage: Int = 90 + + let config = PackageConfiguration([ + "komondor": [ + "pre-push": [ + "swift test --enable-code-coverage --enable-test-discovery", + "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)" + ], + "pre-commit": [ + "swift test --generate-linuxmain", + "swift test --enable-code-coverage --enable-test-discovery", + "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)", + "swift run swiftformat .", + "swift run swiftlint autocorrect", + "swift run sourcedocs generate build -cra", + "git add .", + "swift run swiftformat --lint .", + "swift run swiftlint --strict" + ] + ] + ]).write() +#endif diff --git a/Scripts/Dockerfile b/Scripts/Dockerfile deleted file mode 100644 index 72a918b..0000000 --- a/Scripts/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -# Dockerfile -ARG TAG=latest -FROM swift:${TAG} -RUN apt-get update && apt-get install --no-install-recommends -y libsqlite3-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/Scripts/before_install.sh b/Scripts/before_install.sh deleted file mode 100644 index 3365229..0000000 --- a/Scripts/before_install.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -if [[ $TRAVIS_OS_NAME = 'osx' ]]; then - : -elif [[ $TRAVIS_OS_NAME = 'linux' ]]; then - RELEASE_DOT=$(lsb_release -sr) - RELEASE_NUM=${RELEASE_DOT//[-._]/} - - if [[ $RELEASE_DOT == "20.04" ]]; then - sudo apt-get update - sudo apt-get -y install \ - binutils \ - git \ - gnupg2 \ - libc6-dev \ - libcurl4 \ - libedit2 \ - libgcc-9-dev \ - libpython2.7 \ - libsqlite3-0 \ - libstdc++-9-dev \ - libxml2 \ - libz3-dev \ - pkg-config \ - tzdata \ - zlib1g-dev - fi - - if [[ $TRAVIS_CPU_ARCH == "arm64" ]]; then - curl -s https://packagecloud.io/install/repositories/swift-arm/release/script.deb.sh | sudo bash - sudo apt-get install swift-lang - else - wget https://swift.org/builds/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - fi -fi diff --git a/Scripts/docc.sh b/Scripts/docc.sh new file mode 100755 index 0000000..3e4c918 --- /dev/null +++ b/Scripts/docc.sh @@ -0,0 +1,3 @@ +#!/bin/sh +xcodebuild docbuild -scheme SimulatorServices -derivedDataPath DerivedData -destination 'platform=macOS' +$(xcrun --find docc) process-archive transform-for-static-hosting DerivedData/Build/Products/Debug/SimulatorServices.doccarchive --output-path Output \ No newline at end of file diff --git a/Scripts/gh-md-toc b/Scripts/gh-md-toc new file mode 100755 index 0000000..8d35839 --- /dev/null +++ b/Scripts/gh-md-toc @@ -0,0 +1,411 @@ +#!/usr/bin/env bash + +# +# Steps: +# +# 1. Download corresponding html file for some README.md: +# curl -s $1 +# +# 2. Discard rows where no substring 'user-content-' (github's markup): +# awk '/user-content-/ { ... +# +# 3.1 Get last number in each row like ' ... sitemap.js.*<\/h/)+2, RLENGTH-5) +# +# 5. Find anchor and insert it inside "(...)": +# substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) +# + +gh_toc_version="0.8.0" + +gh_user_agent="gh-md-toc v$gh_toc_version" + +# +# Download rendered into html README.md by its url. +# +# +gh_toc_load() { + local gh_url=$1 + + if type curl &>/dev/null; then + curl --user-agent "$gh_user_agent" -s "$gh_url" + elif type wget &>/dev/null; then + wget --user-agent="$gh_user_agent" -qO- "$gh_url" + else + echo "Please, install 'curl' or 'wget' and try again." + exit 1 + fi +} + +# +# Converts local md file into html by GitHub +# +# -> curl -X POST --data '{"text": "Hello world github/linguist#1 **cool**, and #1!"}' https://api.github.com/markdown +#

Hello world github/linguist#1 cool, and #1!

'" +gh_toc_md2html() { + local gh_file_md=$1 + local skip_header=$2 + + URL=https://api.github.com/markdown/raw + + if [ ! -z "$GH_TOC_TOKEN" ]; then + TOKEN=$GH_TOC_TOKEN + else + TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + if [ -f "$TOKEN_FILE" ]; then + TOKEN="$(cat $TOKEN_FILE)" + fi + fi + if [ ! -z "${TOKEN}" ]; then + AUTHORIZATION="Authorization: token ${TOKEN}" + fi + + local gh_tmp_file_md=$gh_file_md + if [ "$skip_header" = "yes" ]; then + if grep -Fxq "" $gh_src; then + # cut everything before the toc + gh_tmp_file_md=$gh_file_md~~ + sed '1,//d' $gh_file_md > $gh_tmp_file_md + fi + fi + + # echo $URL 1>&2 + OUTPUT=$(curl -s \ + --user-agent "$gh_user_agent" \ + --data-binary @"$gh_tmp_file_md" \ + -H "Content-Type:text/plain" \ + -H "$AUTHORIZATION" \ + "$URL") + + rm -f $gh_file_md~~ + + if [ "$?" != "0" ]; then + echo "XXNetworkErrorXX" + fi + if [ "$(echo "${OUTPUT}" | awk '/API rate limit exceeded/')" != "" ]; then + echo "XXRateLimitXX" + else + echo "${OUTPUT}" + fi +} + + +# +# Is passed string url +# +gh_is_url() { + case $1 in + https* | http*) + echo "yes";; + *) + echo "no";; + esac +} + +# +# TOC generator +# +gh_toc(){ + local gh_src=$1 + local gh_src_copy=$1 + local gh_ttl_docs=$2 + local need_replace=$3 + local no_backup=$4 + local no_footer=$5 + local indent=$6 + local skip_header=$7 + + if [ "$gh_src" = "" ]; then + echo "Please, enter URL or local path for a README.md" + exit 1 + fi + + + # Show "TOC" string only if working with one document + if [ "$gh_ttl_docs" = "1" ]; then + + echo "Table of Contents" + echo "=================" + echo "" + gh_src_copy="" + + fi + + if [ "$(gh_is_url "$gh_src")" == "yes" ]; then + gh_toc_load "$gh_src" | gh_toc_grab "$gh_src_copy" "$indent" + if [ "${PIPESTATUS[0]}" != "0" ]; then + echo "Could not load remote document." + echo "Please check your url or network connectivity" + exit 1 + fi + if [ "$need_replace" = "yes" ]; then + echo + echo "!! '$gh_src' is not a local file" + echo "!! Can't insert the TOC into it." + echo + fi + else + local rawhtml=$(gh_toc_md2html "$gh_src" "$skip_header") + if [ "$rawhtml" == "XXNetworkErrorXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Please make sure curl is installed and check your network connectivity" + exit 1 + fi + if [ "$rawhtml" == "XXRateLimitXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Error: You exceeded the hourly limit. See: https://developer.github.com/v3/#rate-limiting" + TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + echo "or place GitHub auth token here: ${TOKEN_FILE}" + exit 1 + fi + local toc=`echo "$rawhtml" | gh_toc_grab "$gh_src_copy" "$indent"` + echo "$toc" + if [ "$need_replace" = "yes" ]; then + if grep -Fxq "" $gh_src && grep -Fxq "" $gh_src; then + echo "Found markers" + else + echo "You don't have or in your file...exiting" + exit 1 + fi + local ts="<\!--ts-->" + local te="<\!--te-->" + local dt=`date +'%F_%H%M%S'` + local ext=".orig.${dt}" + local toc_path="${gh_src}.toc.${dt}" + local toc_createdby="" + local toc_footer="" + # http://fahdshariff.blogspot.ru/2012/12/sed-mutli-line-replacement-between-two.html + # clear old TOC + sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" + # create toc file + echo "${toc}" > "${toc_path}" + if [ "${no_footer}" != "yes" ]; then + echo -e "\n${toc_createdby}\n${toc_footer}\n" >> "$toc_path" + fi + + # insert toc file + if ! sed --version > /dev/null 2>&1; then + sed -i "" "/${ts}/r ${toc_path}" "$gh_src" + else + sed -i "/${ts}/r ${toc_path}" "$gh_src" + fi + echo + if [ "${no_backup}" = "yes" ]; then + rm ${toc_path} ${gh_src}${ext} + fi + echo "!! TOC was added into: '$gh_src'" + if [ -z "${no_backup}" ]; then + echo "!! Origin version of the file: '${gh_src}${ext}'" + echo "!! TOC added into a separate file: '${toc_path}'" + fi + echo + fi + fi +} + +# +# Grabber of the TOC from rendered html +# +# $1 - a source url of document. +# It's need if TOC is generated for multiple documents. +# $2 - number of spaces used to indent. +# +gh_toc_grab() { + common_awk_script=' + modified_href = "" + split(href, chars, "") + for (i=1;i <= length(href); i++) { + c = chars[i] + res = "" + if (c == "+") { + res = " " + } else { + if (c == "%") { + res = "\\x" + } else { + res = c "" + } + } + modified_href = modified_href res + } + print sprintf("%*s", (level-1)*'"$2"', "") "* [" text "](" gh_url modified_href ")" + ' + if [ `uname -s` == "OS/390" ]; then + grepcmd="pcregrep -o" + echoargs="" + awkscript='{ + level = substr($0, length($0), 1) + text = substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5) + href = substr($0, match($0, "href=\"([^\"]+)?\"")+6, RLENGTH-7) + '"$common_awk_script"' + }' + else + grepcmd="grep -Eo" + echoargs="-e" + awkscript='{ + level = substr($0, length($0), 1) + text = substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5) + href = substr($0, match($0, "href=\"[^\"]+?\"")+6, RLENGTH-7) + '"$common_awk_script"' + }' + fi + href_regex='href=\"[^\"]+?\"' + + # if closed is on the new line, then move it on the prev line + # for example: + # was: The command foo1 + # + # became: The command foo1 + sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' | + + # find strings that corresponds to template + $grepcmd '//g' | sed 's/<\/code>//g' | + + # remove g-emoji + sed 's/]*[^<]*<\/g-emoji> //g' | + + # now all rows are like: + # ... /dev/null`; then + echo `$tool --version | head -n 1` + else + echo "not installed" + fi + done +} + +show_help() { + local app_name=$(basename "$0") + echo "GitHub TOC generator ($app_name): $gh_toc_version" + echo "" + echo "Usage:" + echo " $app_name [options] src [src] Create TOC for a README file (url or local path)" + echo " $app_name - Create TOC for markdown from STDIN" + echo " $app_name --help Show help" + echo " $app_name --version Show version" + echo "" + echo "Options:" + echo " --indent Set indent size. Default: 3." + echo " --insert Insert new TOC into original file. For local files only. Default: false." + echo " See https://github.com/ekalinin/github-markdown-toc/issues/41 for details." + echo " --no-backup Remove backup file. Set --insert as well. Default: false." + echo " --hide-footer Do not write date & author of the last TOC update. Set --insert as well. Default: false." + echo " --skip-header Hide entry of the topmost headlines. Default: false." + echo " See https://github.com/ekalinin/github-markdown-toc/issues/125 for details." + echo "" +} + +# +# Options handlers +# +gh_toc_app() { + local need_replace="no" + local indent=3 + + if [ "$1" = '--help' ] || [ $# -eq 0 ] ; then + show_help + return + fi + + if [ "$1" = '--version' ]; then + show_version + return + fi + + if [ "$1" = '--indent' ]; then + indent="$2" + shift 2 + fi + + if [ "$1" = "-" ]; then + if [ -z "$TMPDIR" ]; then + TMPDIR="/tmp" + elif [ -n "$TMPDIR" -a ! -d "$TMPDIR" ]; then + mkdir -p "$TMPDIR" + fi + local gh_tmp_md + if [ `uname -s` == "OS/390" ]; then + local timestamp=$(date +%m%d%Y%H%M%S) + gh_tmp_md="$TMPDIR/tmp.$timestamp" + else + gh_tmp_md=$(mktemp $TMPDIR/tmp.XXXXXX) + fi + while read input; do + echo "$input" >> "$gh_tmp_md" + done + gh_toc_md2html "$gh_tmp_md" | gh_toc_grab "" "$indent" + return + fi + + if [ "$1" = '--insert' ]; then + need_replace="yes" + shift + fi + + if [ "$1" = '--no-backup' ]; then + need_replace="yes" + no_backup="yes" + shift + fi + + if [ "$1" = '--hide-footer' ]; then + need_replace="yes" + no_footer="yes" + shift + fi + + if [ "$1" = '--skip-header' ]; then + skip_header="yes" + shift + fi + + + for md in "$@" + do + echo "" + gh_toc "$md" "$#" "$need_replace" "$no_backup" "$no_footer" "$indent" "$skip_header" + done + + echo "" + echo "" +} + +# +# Entry point +# +gh_toc_app "$@" diff --git a/Scripts/images.sh b/Scripts/images.sh deleted file mode 100755 index 1af0249..0000000 --- a/Scripts/images.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -swift_versions=('5.3' '5.2') -ubuntu_versions=('bionic' 'xenial' 'focal') - -for swift_version in ${swift_versions[@]} -do - for ubuntu_version in ${ubuntu_versions[@]} - do - ( - docker build -t brightdigit/mistkit-sql:$swift_version-$ubuntu_version . --build-arg TAG=$swift_version-$ubuntu_version - docker push brightdigit/mistkit-sql:$swift_version-$ubuntu_version - ) & - done -done -wait diff --git a/Scripts/lint.sh b/Scripts/lint.sh new file mode 100755 index 0000000..fd0a0e3 --- /dev/null +++ b/Scripts/lint.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +if [ -z "$SRCROOT" ]; then + SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + PACKAGE_DIR="${SCRIPT_DIR}/.." +else + PACKAGE_DIR="${SRCROOT}" +fi + +if [ -z "$GITHUB_ACTION" ]; then + MINT_CMD="/opt/homebrew/bin/mint" +else + MINT_CMD="mint" +fi + +export MINT_PATH="$PACKAGE_DIR/.mint" +MINT_ARGS="-n -m $PACKAGE_DIR/Mintfile --silent" +MINT_RUN="$MINT_CMD run $MINT_ARGS" + +pushd $PACKAGE_DIR + +$MINT_CMD bootstrap -m Mintfile + +if [ "$LINT_MODE" == "NONE" ]; then + exit +elif [ "$LINT_MODE" == "STRICT" ]; then + SWIFTFORMAT_OPTIONS="" + SWIFTLINT_OPTIONS="--strict" +else + SWIFTFORMAT_OPTIONS="" + SWIFTLINT_OPTIONS="" +fi + +pushd $PACKAGE_DIR + +if [ -z "$CI" ]; then + $MINT_RUN swiftformat . + $MINT_RUN swiftlint autocorrect +fi + +$MINT_RUN swiftformat --lint $SWIFTFORMAT_OPTIONS . +$MINT_RUN swiftlint lint $SWIFTLINT_OPTIONS + +popd diff --git a/Scripts/script.sh b/Scripts/script.sh deleted file mode 100644 index 8915708..0000000 --- a/Scripts/script.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -if [[ $TRAVIS_OS_NAME = 'osx' ]]; then - swift run swiftformat --lint . && swift run swiftlint -elif [[ $TRAVIS_OS_NAME = 'linux' ]]; then - # What to do in Ubunutu - RELEASE_DOT=$(lsb_release -sr) - RELEASE_NUM=${RELEASE_DOT//[-._]/} - RELEASE_NAME=$(lsb_release -sc) - [[ $TRAVIS_CPU_ARCH = "arm64" ]] && ARCH_PREFIX="aarch64" || ARCH_PREFIX="x86_64" - export PATH="${PWD}/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin:$PATH" -fi - -ARCH=${TRAVIS_CPU_ARCH:-amd64} -[[ $TRAVIS_CPU_ARCH = "arm64" ]] && ARCH_PREFIX="aarch64" || ARCH_PREFIX="x86_64" - -swift build -swift test --enable-code-coverage --enable-test-discovery - -if [[ $TRAVIS_OS_NAME = 'osx' ]]; then - xcrun llvm-cov export -format="lcov" .build/debug/${FRAMEWORK_NAME}PackageTests.xctest/Contents/MacOS/${FRAMEWORK_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - bash <(curl https://codecov.io/bash) -F travis -F macOS -n $TRAVIS_JOB_NUMBER-$TRAVIS_OS_NAME -else - llvm-cov export -format="lcov" .build/${ARCH_PREFIX}-unknown-linux-gnu/debug/${FRAMEWORK_NAME}PackageTests.xctest -instr-profile .build/debug/codecov/default.profdata > info.lcov - bash <(curl https://codecov.io/bash) -F travis -F $RELEASE_NAME -F $ARCH -n $TRAVIS_JOB_NUMBER-$TRAVIS_OS_NAME -fi - -# curl -s https://raw.githubusercontent.com/daveverwer/SwiftPMLibrary/master/script.sh | bash -s -- mine - -# if [[ $TRAVIS_OS_NAME = 'osx' ]]; then -# pod lib lint -# swift package generate-xcodeproj -# xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "iOS_Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -# xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "tvOS_Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -# xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "macOS_Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -# fi diff --git a/Sources/Options/EnumSet.swift b/Sources/Options/EnumSet.swift index af24a03..c78c8df 100644 --- a/Sources/Options/EnumSet.swift +++ b/Sources/Options/EnumSet.swift @@ -15,7 +15,7 @@ public struct EnumSet: OptionSet /// Creates the EnumSet based on the values in the array. /// - Parameter values: Array of enum values. public init(values: [EnumType]) { - let set = Set(values.map { $0.rawValue }) + let set = Set(values.map(\.rawValue)) rawValue = Self.cumulativeValue(basedOnRawValues: set) } @@ -68,7 +68,7 @@ extension EnumSet: Codable var container = encoder.singleValueContainer() let values = Self.enums(basedOnRawValue: rawValue) let mappedValues = try values - .map { $0.rawValue } + .map(\.rawValue) .map(EnumType.mappedValue(basedOn:)) try container.encode(mappedValues) } diff --git a/Sources/Options/MappedValueCollectionRepresented.swift b/Sources/Options/MappedValueCollectionRepresented.swift index dc576fe..2208886 100644 --- a/Sources/Options/MappedValueCollectionRepresented.swift +++ b/Sources/Options/MappedValueCollectionRepresented.swift @@ -5,7 +5,7 @@ public protocol MappedValueCollectionRepresented: MappedValueRepresentable static var mappedValues: [MappedType] { get } } -public extension MappedValueCollectionRepresented { +extension MappedValueCollectionRepresented { /// Gets the raw value based on the MappedType by finding the index of the mapped value. /// - Parameter value: MappedType value. /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` @@ -13,7 +13,7 @@ public extension MappedValueCollectionRepresented { /// - Returns: /// The raw value of the enumeration /// based on the index the MappedType value was found at. - static func rawValue(basedOn value: MappedType) throws -> RawValue { + public static func rawValue(basedOn value: MappedType) throws -> RawValue { guard let index = mappedValues.firstIndex(of: value) else { throw MappedValueRepresentableError.valueNotFound } @@ -29,10 +29,10 @@ public extension MappedValueCollectionRepresented { /// if the raw value (i.e. index) is outside the range of the `mappedValues` array. /// - Returns: /// The Mapped Type value based on the value in the array at the raw value index. - static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { + public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { guard rawValue < mappedValues.count, rawValue >= 0 else { throw MappedValueRepresentableError.valueNotFound } - return Self.mappedValues[rawValue] + return mappedValues[rawValue] } } diff --git a/Sources/Options/MappedValueDictionaryRepresented.swift b/Sources/Options/MappedValueDictionaryRepresented.swift index cc3a551..275a598 100644 --- a/Sources/Options/MappedValueDictionaryRepresented.swift +++ b/Sources/Options/MappedValueDictionaryRepresented.swift @@ -5,7 +5,7 @@ public protocol MappedValueDictionaryRepresented: MappedValueRepresentable static var mappedValues: [Int: MappedType] { get } } -public extension MappedValueDictionaryRepresented { +extension MappedValueDictionaryRepresented { /// Gets the raw value based on the MappedType by finding the key of the mapped value. /// - Parameter value: MappedType value. /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` @@ -13,7 +13,7 @@ public extension MappedValueDictionaryRepresented { /// - Returns: /// The raw value of the enumeration /// based on the key the MappedType value. - static func rawValue(basedOn value: MappedType) throws -> RawValue { + public static func rawValue(basedOn value: MappedType) throws -> RawValue { let pair = mappedValues.first { $0.value == value } guard let key = pair?.key else { throw MappedValueRepresentableError.valueNotFound @@ -30,7 +30,7 @@ public extension MappedValueDictionaryRepresented { /// if the raw value (i.e. key) is not in the dictionary. /// - Returns: /// The Mapped Type value based on the raw value key. - static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { + public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { guard let value = mappedValues[rawValue] else { throw MappedValueRepresentableError.valueNotFound } diff --git a/Sources/Options/MappedValueRepresentable.swift b/Sources/Options/MappedValueRepresentable.swift index 68d8929..91a3907 100644 --- a/Sources/Options/MappedValueRepresentable.swift +++ b/Sources/Options/MappedValueRepresentable.swift @@ -1,3 +1,32 @@ +// +// MappedValueRepresentable.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + public protocol MappedValueRepresentable: RawRepresentable, CaseIterable { associatedtype MappedType /// Gets the raw value based on the MappedType. @@ -11,7 +40,7 @@ public protocol MappedValueRepresentable: RawRepresentable, CaseIterable { static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType } -public extension MappedValueRepresentable { +extension MappedValueRepresentable { /// Gets the mapped value of the enumeration. /// - Parameter rawValue: The raw value of the enumeration /// which pretains to its index in the `mappedValues` Array. @@ -23,7 +52,7 @@ public extension MappedValueRepresentable { /// Gets the mapped value of the enumeration. /// - Returns: The `MappedType` value - func mappedValue() throws -> MappedType { + public func mappedValue() throws -> MappedType { try Self.mappedValue(basedOn: rawValue) } } diff --git a/Sources/Options/MappedValueRepresentableError.swift b/Sources/Options/MappedValueRepresentableError.swift index 86d00f9..403c449 100644 --- a/Sources/Options/MappedValueRepresentableError.swift +++ b/Sources/Options/MappedValueRepresentableError.swift @@ -1,3 +1,32 @@ +// +// MappedValueRepresentableError.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + import Foundation /// An Error thrown when the `MappedType` value or `RawType` value diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 14cf610..78af61c 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,3 +1,32 @@ +// +// LinuxMain.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + import XCTest import OptionsTests diff --git a/Tests/OptionsTests/EnumSetTests.swift b/Tests/OptionsTests/EnumSetTests.swift index d0ee471..7b44cd3 100644 --- a/Tests/OptionsTests/EnumSetTests.swift +++ b/Tests/OptionsTests/EnumSetTests.swift @@ -1,3 +1,32 @@ +// +// EnumSetTests.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + #if canImport(XCTest) @testable import Options import XCTest diff --git a/Tests/OptionsTests/MappedEnumTests.swift b/Tests/OptionsTests/MappedEnumTests.swift index 84e0806..ee0bdc8 100644 --- a/Tests/OptionsTests/MappedEnumTests.swift +++ b/Tests/OptionsTests/MappedEnumTests.swift @@ -1,3 +1,32 @@ +// +// MappedEnumTests.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + #if canImport(XCTest) @testable import Options import XCTest diff --git a/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift b/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift index 5176814..bdf1e1e 100644 --- a/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift +++ b/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift @@ -1,3 +1,32 @@ +// +// MappedValueCollectionRepresentedTests.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + #if canImport(XCTest) @testable import Options import XCTest diff --git a/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift b/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift index fe97e0d..d3ed318 100644 --- a/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift +++ b/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift @@ -1,3 +1,32 @@ +// +// MappedValueDictionaryRepresentedTests.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + #if canImport(XCTest) @testable import Options import XCTest diff --git a/Tests/OptionsTests/MappedValueRepresentableTests.swift b/Tests/OptionsTests/MappedValueRepresentableTests.swift index d3f5ca8..86e839a 100644 --- a/Tests/OptionsTests/MappedValueRepresentableTests.swift +++ b/Tests/OptionsTests/MappedValueRepresentableTests.swift @@ -1,3 +1,32 @@ +// +// MappedValueRepresentableTests.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + #if canImport(XCTest) @testable import Options import XCTest diff --git a/Tests/OptionsTests/Mocks/MockCollectionEnum.swift b/Tests/OptionsTests/Mocks/MockCollectionEnum.swift index 93e3d80..6985d8c 100644 --- a/Tests/OptionsTests/Mocks/MockCollectionEnum.swift +++ b/Tests/OptionsTests/Mocks/MockCollectionEnum.swift @@ -1,3 +1,32 @@ +// +// MockCollectionEnum.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + import Options // swiftlint:disable identifier_name diff --git a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift index 7344654..677b36c 100644 --- a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift +++ b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift @@ -1,3 +1,32 @@ +// +// MockDictionaryEnum.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + import Options // swiftlint:disable identifier_name diff --git a/Tests/OptionsTests/Mocks/MockError.swift b/Tests/OptionsTests/Mocks/MockError.swift index 47abd6f..1a7037b 100644 --- a/Tests/OptionsTests/Mocks/MockError.swift +++ b/Tests/OptionsTests/Mocks/MockError.swift @@ -1,3 +1,32 @@ +// +// MockError.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + import Foundation internal struct MockError: Error { diff --git a/Tests/OptionsTests/XCTestManifests.swift b/Tests/OptionsTests/XCTestManifests.swift index 31ea757..a0835cc 100644 --- a/Tests/OptionsTests/XCTestManifests.swift +++ b/Tests/OptionsTests/XCTestManifests.swift @@ -1,3 +1,32 @@ +// +// XCTestManifests.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + #if !canImport(ObjectiveC) import XCTest @@ -58,7 +87,7 @@ } public func __allTests() -> [XCTestCaseEntry] { - return [ + [ testCase(EnumSetTests.__allTests__EnumSetTests), testCase(MappedEnumTests.__allTests__MappedEnumTests), testCase(MappedValueCollectionRepresentedTests.__allTests__MappedValueCollectionRepresentedTests), diff --git a/bitrise.yml b/bitrise.yml deleted file mode 100644 index 384706a..0000000 --- a/bitrise.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -format_version: '8' -default_step_lib_source: 'https://github.com/bitrise-io/bitrise-steplib.git' -project_type: other -app: - envs: - - PACKAGE_NAME: Options -workflows: - ci: - steps: - - git-clone@4: {} - - script@1: - inputs: - - content: >- - #!/usr/bin/env bash # fail if any commands fails - - set -e - - # debug log - - set -x - - pwd - - ls - - swift run swiftformat --lint . && swift run swiftlint - - swift build - - swift test --enable-code-coverage - - - xcrun llvm-cov export -format="lcov" .build/debug/${PACKAGE_NAME}PackageTests.xctest/Contents/MacOS/${PACKAGE_NAME}PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov - - bash <(curl https://codecov.io/bash) -F bitrise -F macOS -n - $BITRISE_BUILD_NUMBER -trigger_map: -- push_branch: '*' - workflow: ci diff --git a/project.yml b/project.yml new file mode 100644 index 0000000..abfabb5 --- /dev/null +++ b/project.yml @@ -0,0 +1,13 @@ +name: Options +settings: + LINT_MODE: ${LINT_MODE} +packages: + StealthyStash: + path: . +aggregateTargets: + Lint: + buildScripts: + - path: Scripts/lint.sh + name: Lint + basedOnDependencyAnalysis: false + schemes: {} \ No newline at end of file From 163d44278a96b038e4518c20f04e0fd401bf961d Mon Sep 17 00:00:00 2001 From: leogdion Date: Fri, 29 Mar 2024 21:27:30 -0400 Subject: [PATCH 02/25] Update Options.yml --- .github/workflows/Options.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml index d1b2ec3..42634f0 100644 --- a/.github/workflows/Options.yml +++ b/.github/workflows/Options.yml @@ -73,7 +73,7 @@ jobs: if: "!contains(github.event.head_commit.message, 'ci skip')" strategy: matrix: - runs-on: macos-11.0 + runs-on: ["macos-11.0"] xcode: ["/Applications/Xcode_11.7.app","/Applications/Xcode_12.app","/Applications/Xcode_12.1.app","/Applications/Xcode_12.2.app","/Applications/Xcode_12.3.app", "/Applications/Xcode_12.4.app"] steps: - uses: actions/checkout@v4 From 60c125ed2a5e4612b342c273a55577bacfec3526 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Fri, 29 Mar 2024 21:32:04 -0400 Subject: [PATCH 03/25] fixing Packages --- Package-5.9.swift | 64 ----------------------------------------- Package.swift | 28 +----------------- Package@swift-5.9.swift | 32 --------------------- 3 files changed, 1 insertion(+), 123 deletions(-) delete mode 100644 Package-5.9.swift diff --git a/Package-5.9.swift b/Package-5.9.swift deleted file mode 100644 index 4b5c863..0000000 --- a/Package-5.9.swift +++ /dev/null @@ -1,64 +0,0 @@ -// swift-tools-version:5.2 - -// swiftlint:disable explicit_top_level_acl -// swiftlint:disable prefixed_toplevel_constant -// swiftlint:disable line_length -// swiftlint:disable explicit_acl - -import PackageDescription - -let package = Package( - name: "Options", - products: [ - .library( - name: "Options", - targets: ["Options"] - ) - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0") - .package(url: "https://github.com/shibapm/Komondor", from: "1.0.6"), // dev - .package(url: "https://github.com/eneko/SourceDocs", from: "1.2.1"), // dev - .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.47.0"), // dev - .package(url: "https://github.com/realm/SwiftLint", from: "0.41.0"), // dev - .package(url: "https://github.com/shibapm/Rocket", .branch("master")), // dev - .package(url: "https://github.com/mattpolzin/swift-test-codecov", .branch("master")) // dev - ], - targets: [ - .target( - name: "Options", - dependencies: [] - ), - .testTarget( - name: "OptionsTests", - dependencies: ["Options"] - ) - ] -) - -#if canImport(PackageConfig) - import PackageConfig - - let requiredCoverage: Int = 90 - - let config = PackageConfiguration([ - "komondor": [ - "pre-push": [ - "swift test --enable-code-coverage --enable-test-discovery", - "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)" - ], - "pre-commit": [ - "swift test --generate-linuxmain", - "swift test --enable-code-coverage --enable-test-discovery", - "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)", - "swift run swiftformat .", - "swift run swiftlint autocorrect", - "swift run sourcedocs generate build -cra", - "git add .", - "swift run swiftformat --lint .", - "swift run swiftlint --strict" - ] - ] - ]).write() -#endif diff --git a/Package.swift b/Package.swift index 59ad969..a82996e 100644 --- a/Package.swift +++ b/Package.swift @@ -28,30 +28,4 @@ let package = Package( dependencies: ["Options"] ) ] -) - -#if canImport(PackageConfig) - import PackageConfig - - let requiredCoverage: Int = 90 - - let config = PackageConfiguration([ - "komondor": [ - "pre-push": [ - "swift test --enable-code-coverage --enable-test-discovery", - "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)" - ], - "pre-commit": [ - "swift test --generate-linuxmain", - "swift test --enable-code-coverage --enable-test-discovery", - "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)", - "swift run swiftformat .", - "swift run swiftlint autocorrect", - "swift run sourcedocs generate build -cra", - "git add .", - "swift run swiftformat --lint .", - "swift run swiftlint --strict" - ] - ] - ]).write() -#endif +) \ No newline at end of file diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index 4b5c863..338f7c1 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -18,12 +18,6 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0") - .package(url: "https://github.com/shibapm/Komondor", from: "1.0.6"), // dev - .package(url: "https://github.com/eneko/SourceDocs", from: "1.2.1"), // dev - .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.47.0"), // dev - .package(url: "https://github.com/realm/SwiftLint", from: "0.41.0"), // dev - .package(url: "https://github.com/shibapm/Rocket", .branch("master")), // dev - .package(url: "https://github.com/mattpolzin/swift-test-codecov", .branch("master")) // dev ], targets: [ .target( @@ -36,29 +30,3 @@ let package = Package( ) ] ) - -#if canImport(PackageConfig) - import PackageConfig - - let requiredCoverage: Int = 90 - - let config = PackageConfiguration([ - "komondor": [ - "pre-push": [ - "swift test --enable-code-coverage --enable-test-discovery", - "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)" - ], - "pre-commit": [ - "swift test --generate-linuxmain", - "swift test --enable-code-coverage --enable-test-discovery", - "swift run swift-test-codecov .build/debug/codecov/Options.json -v \(requiredCoverage)", - "swift run swiftformat .", - "swift run swiftlint autocorrect", - "swift run sourcedocs generate build -cra", - "git add .", - "swift run swiftformat --lint .", - "swift run swiftlint --strict" - ] - ] - ]).write() -#endif From f5e6414e84244047bf5fb84832ef0c9fdfea817a Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Fri, 29 Mar 2024 21:40:08 -0400 Subject: [PATCH 04/25] fixing CI --- .github/workflows/Options.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml index 42634f0..e2d244d 100644 --- a/.github/workflows/Options.yml +++ b/.github/workflows/Options.yml @@ -74,7 +74,7 @@ jobs: strategy: matrix: runs-on: ["macos-11.0"] - xcode: ["/Applications/Xcode_11.7.app","/Applications/Xcode_12.app","/Applications/Xcode_12.1.app","/Applications/Xcode_12.2.app","/Applications/Xcode_12.3.app", "/Applications/Xcode_12.4.app"] + xcode: ["/Applications/Xcode_11.7.app","/Applications/Xcode_12.4.app"] steps: - uses: actions/checkout@v4 - name: Cache swift package modules From 61a27aeb9ee87e4d9327a8ea28a9cf5a378c1ec8 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Sun, 31 Mar 2024 21:26:51 -0400 Subject: [PATCH 05/25] adding sendability --- Package@swift-5.9.swift | 15 ++++++++++++++- Sources/Options/EnumSet.swift | 2 +- Sources/Options/MappedEnum.swift | 2 +- Sources/Options/MappedValueRepresentable.swift | 2 +- .../Options/MappedValueRepresentableError.swift | 2 +- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index 338f7c1..73bcbd9 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -7,6 +7,18 @@ import PackageDescription +let swiftSettings = [ + SwiftSetting.enableUpcomingFeature("BareSlashRegexLiterals"), + SwiftSetting.enableUpcomingFeature("ConciseMagicFile"), + SwiftSetting.enableUpcomingFeature("ExistentialAny"), + SwiftSetting.enableUpcomingFeature("ForwardTrailingClosures"), + SwiftSetting.enableUpcomingFeature("ImplicitOpenExistentials"), + SwiftSetting.enableUpcomingFeature("StrictConcurrency"), + SwiftSetting.enableUpcomingFeature("DisableOutwardActorInference"), + SwiftSetting.enableExperimentalFeature("StrictConcurrency"), + SwiftSetting.unsafeFlags(["-warn-concurrency", "-enable-actor-data-race-checks"]) +] + let package = Package( name: "Options", products: [ @@ -22,7 +34,8 @@ let package = Package( targets: [ .target( name: "Options", - dependencies: [] + dependencies: [], + swiftSettings: swiftSettings ), .testTarget( name: "OptionsTests", diff --git a/Sources/Options/EnumSet.swift b/Sources/Options/EnumSet.swift index c78c8df..968f02c 100644 --- a/Sources/Options/EnumSet.swift +++ b/Sources/Options/EnumSet.swift @@ -1,5 +1,5 @@ /// Generic struct for using Enums with RawValue type of Int as an Optionset -public struct EnumSet: OptionSet +public struct EnumSet: OptionSet, Sendable where EnumType.RawValue == Int { public typealias RawValue = EnumType.RawValue diff --git a/Sources/Options/MappedEnum.swift b/Sources/Options/MappedEnum.swift index 58eba20..7f8938d 100644 --- a/Sources/Options/MappedEnum.swift +++ b/Sources/Options/MappedEnum.swift @@ -1,5 +1,5 @@ /// A generic struct for enumerations which allow for additional values attached. -public struct MappedEnum: Codable +public struct MappedEnum: Codable, Sendable where EnumType.MappedType: Codable { /// Base Enumeraion value. public let value: EnumType diff --git a/Sources/Options/MappedValueRepresentable.swift b/Sources/Options/MappedValueRepresentable.swift index 91a3907..fac41dc 100644 --- a/Sources/Options/MappedValueRepresentable.swift +++ b/Sources/Options/MappedValueRepresentable.swift @@ -27,7 +27,7 @@ // OTHER DEALINGS IN THE SOFTWARE. // -public protocol MappedValueRepresentable: RawRepresentable, CaseIterable { +public protocol MappedValueRepresentable: RawRepresentable, CaseIterable, Sendable { associatedtype MappedType /// Gets the raw value based on the MappedType. /// - Parameter value: MappedType value. diff --git a/Sources/Options/MappedValueRepresentableError.swift b/Sources/Options/MappedValueRepresentableError.swift index 403c449..494219d 100644 --- a/Sources/Options/MappedValueRepresentableError.swift +++ b/Sources/Options/MappedValueRepresentableError.swift @@ -31,6 +31,6 @@ import Foundation /// An Error thrown when the `MappedType` value or `RawType` value /// are invalid for an `Enum`. -public enum MappedValueRepresentableError: Error { +public enum MappedValueRepresentableError: Error, Sendable { case valueNotFound } From 2206e2f60f39422de2da42d2a7411ec1cf608ff3 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Sun, 31 Mar 2024 21:28:52 -0400 Subject: [PATCH 06/25] fixing swift version --- Package@swift-5.9.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index 73bcbd9..f4c9048 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.9 // swiftlint:disable explicit_top_level_acl // swiftlint:disable prefixed_toplevel_constant @@ -28,6 +28,7 @@ let package = Package( ) ], dependencies: [ + .package(url: "https://github.com/apple/swift-syntax", from: "510.0.0") // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0") ], From 93e7e7ab6f37be09e7c5d67546869daa5bd31d57 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Sun, 31 Mar 2024 21:30:15 -0400 Subject: [PATCH 07/25] fixing any missing --- Sources/Options/EnumSet.swift | 4 ++-- Sources/Options/MappedEnum.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Options/EnumSet.swift b/Sources/Options/EnumSet.swift index 968f02c..639da78 100644 --- a/Sources/Options/EnumSet.swift +++ b/Sources/Options/EnumSet.swift @@ -54,7 +54,7 @@ extension EnumSet: Codable where EnumType: MappedValueRepresentable, EnumType.MappedType: Codable { /// Decodes the EnumSet based on an Array of MappedTypes. /// - Parameter decoder: Decoder which contains info as an array of MappedTypes. - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() let values = try container.decode([EnumType.MappedType].self) let rawValues = try values.map(EnumType.rawValue(basedOn:)) @@ -64,7 +64,7 @@ extension EnumSet: Codable /// Encodes the EnumSet based on an Array of MappedTypes. /// - Parameter encoder: Encoder which will contain info as an array of MappedTypes. - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() let values = Self.enums(basedOnRawValue: rawValue) let mappedValues = try values diff --git a/Sources/Options/MappedEnum.swift b/Sources/Options/MappedEnum.swift index 7f8938d..ffce0e9 100644 --- a/Sources/Options/MappedEnum.swift +++ b/Sources/Options/MappedEnum.swift @@ -12,7 +12,7 @@ public struct MappedEnum: Codable, Sendable /// Decodes the value based on the mapped value. /// - Parameter decoder: Decoder. - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() let label = try container.decode(EnumType.MappedType.self) let rawValue = try EnumType.rawValue(basedOn: label) @@ -24,7 +24,7 @@ public struct MappedEnum: Codable, Sendable /// Encodes the value based on the mapped value. /// - Parameter encoder: Encoder. - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { let string = try EnumType.mappedValue(basedOn: value.rawValue) var container = encoder.singleValueContainer() try container.encode(string) From 0a15b2596c520729792f40e5666255950e3d3956 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Sun, 31 Mar 2024 21:31:07 -0400 Subject: [PATCH 08/25] Adding dummy Sendable protocol --- Sources/Options/MappedValueRepresentableError.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/Options/MappedValueRepresentableError.swift b/Sources/Options/MappedValueRepresentableError.swift index 494219d..698cac8 100644 --- a/Sources/Options/MappedValueRepresentableError.swift +++ b/Sources/Options/MappedValueRepresentableError.swift @@ -34,3 +34,7 @@ import Foundation public enum MappedValueRepresentableError: Error, Sendable { case valueNotFound } + +#if swift(<5.9) +protocol Sendable {} +#endif From 89a0b24515ec257645980c77cf878b57b9fc0046 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Sun, 31 Mar 2024 21:34:26 -0400 Subject: [PATCH 09/25] support for older than 5.9 --- Sources/Options/EnumSet.swift | 26 ++++++++++++++++ Sources/Options/MappedEnum.swift | 30 +++++++++++++++++-- .../MappedValueRepresentableError.swift | 2 +- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Sources/Options/EnumSet.swift b/Sources/Options/EnumSet.swift index 639da78..f72db23 100644 --- a/Sources/Options/EnumSet.swift +++ b/Sources/Options/EnumSet.swift @@ -50,6 +50,7 @@ extension EnumSet where EnumType: CaseIterable { } } +#if swift(>=5.9) extension EnumSet: Codable where EnumType: MappedValueRepresentable, EnumType.MappedType: Codable { /// Decodes the EnumSet based on an Array of MappedTypes. @@ -73,3 +74,28 @@ extension EnumSet: Codable try container.encode(mappedValues) } } +#else +extension EnumSet: Codable + where EnumType: MappedValueRepresentable, EnumType.MappedType: Codable { + /// Decodes the EnumSet based on an Array of MappedTypes. + /// - Parameter decoder: Decoder which contains info as an array of MappedTypes. + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let values = try container.decode([EnumType.MappedType].self) + let rawValues = try values.map(EnumType.rawValue(basedOn:)) + let set = Set(rawValues) + rawValue = Self.cumulativeValue(basedOnRawValues: set) + } + + /// Encodes the EnumSet based on an Array of MappedTypes. + /// - Parameter encoder: Encoder which will contain info as an array of MappedTypes. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + let values = Self.enums(basedOnRawValue: rawValue) + let mappedValues = try values + .map { $0.rawValue } + .map(EnumType.mappedValue(basedOn:)) + try container.encode(mappedValues) + } +} +#endif diff --git a/Sources/Options/MappedEnum.swift b/Sources/Options/MappedEnum.swift index ffce0e9..b80f21f 100644 --- a/Sources/Options/MappedEnum.swift +++ b/Sources/Options/MappedEnum.swift @@ -1,15 +1,18 @@ /// A generic struct for enumerations which allow for additional values attached. public struct MappedEnum: Codable, Sendable - where EnumType.MappedType: Codable { +where EnumType.MappedType: Codable { /// Base Enumeraion value. public let value: EnumType - + /// Creates an instance based on the base enumeration value. /// - Parameter value: Base Enumeration value. public init(value: EnumType) { self.value = value } +} +#if swift(>=5.9) +extension MappedEnum { /// Decodes the value based on the mapped value. /// - Parameter decoder: Decoder. public init(from decoder: any Decoder) throws { @@ -30,3 +33,26 @@ public struct MappedEnum: Codable, Sendable try container.encode(string) } } +#else +extension MappedEnum { + /// Decodes the value based on the mapped value. + /// - Parameter decoder: Decoder. + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let label = try container.decode(EnumType.MappedType.self) + let rawValue = try EnumType.rawValue(basedOn: label) + guard let value = EnumType(rawValue: rawValue) else { + preconditionFailure("Invalid Raw Value.") + } + self.value = value + } + + /// Encodes the value based on the mapped value. + /// - Parameter encoder: Encoder. + public func encode(to encoder: Encoder) throws { + let string = try EnumType.mappedValue(basedOn: value.rawValue) + var container = encoder.singleValueContainer() + try container.encode(string) + } +} +#endif diff --git a/Sources/Options/MappedValueRepresentableError.swift b/Sources/Options/MappedValueRepresentableError.swift index 698cac8..f2fa3ca 100644 --- a/Sources/Options/MappedValueRepresentableError.swift +++ b/Sources/Options/MappedValueRepresentableError.swift @@ -36,5 +36,5 @@ public enum MappedValueRepresentableError: Error, Sendable { } #if swift(<5.9) -protocol Sendable {} +public protocol Sendable {} #endif From 13249332b015136a7d608ce1d3c0da76af29fa26 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Sun, 31 Mar 2024 21:42:52 -0400 Subject: [PATCH 10/25] adding a macro --- Package@swift-5.9.swift | 13 +++++++++++-- Sources/OptionsMacros/OptionsMacro.swift | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 Sources/OptionsMacros/OptionsMacro.swift diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index f4c9048..7bd1b20 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -6,6 +6,7 @@ // swiftlint:disable explicit_acl import PackageDescription +import CompilerPluginSupport let swiftSettings = [ SwiftSetting.enableUpcomingFeature("BareSlashRegexLiterals"), @@ -21,6 +22,7 @@ let swiftSettings = [ let package = Package( name: "Options", + platforms: [.macOS(.v10_15)], products: [ .library( name: "Options", @@ -28,16 +30,23 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/apple/swift-syntax", from: "510.0.0") + .package(url: "https://github.com/apple/swift-syntax", from: "510.0.0") // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0") ], targets: [ .target( name: "Options", - dependencies: [], + dependencies: ["OptionsMacros"], swiftSettings: swiftSettings ), + .macro( + name: "OptionsMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax") + ] + ), .testTarget( name: "OptionsTests", dependencies: ["Options"] diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift new file mode 100644 index 0000000..393f711 --- /dev/null +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -0,0 +1,17 @@ +// +// File.swift +// +// +// Created by Leo Dion on 3/31/24. +// + +import Foundation +import SwiftSyntaxMacros +import SwiftCompilerPlugin + +@main +struct MacrosPlugin: CompilerPlugin { + let providingMacros: [Macro.Type] = [ + + ] +} From 4a1699b6fb59bf097653c2cc2352f4d0e0a8a363 Mon Sep 17 00:00:00 2001 From: leogdion Date: Mon, 1 Apr 2024 22:09:07 -0400 Subject: [PATCH 11/25] Update Options.yml (#10) [skip ci] --- .github/workflows/Options.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml index e2d244d..29dd08a 100644 --- a/.github/workflows/Options.yml +++ b/.github/workflows/Options.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: runs-on: [ubuntu-20.04, ubuntu-22.04] - swift-version: [5.9, "5.10"] + swift-version: ["5.9.2", "5.10"] steps: - uses: actions/checkout@v4 - name: Cache swift package modules From ced47baf6e1f3d287e80b06f2fb866188b19cd55 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Mon, 1 Apr 2024 22:10:55 -0400 Subject: [PATCH 12/25] added Macro --- Package.resolved | 14 ++ Package.swift | 2 +- Package@swift-5.9.swift | 12 +- Sources/Options/EnumSet.swift | 84 ++++++------ Sources/Options/Macro.swift | 39 ++++++ Sources/Options/MappedEnum.swift | 76 +++++------ .../Options/MappedValueRepresentable.swift | 2 +- .../MappedValueRepresentableError.swift | 2 +- Sources/OptionsMacros/OptionsMacro.swift | 128 +++++++++++++++++- .../Mocks/MockCollectionEnum.swift | 40 ++++-- .../Mocks/MockDictionaryEnum.swift | 1 - 11 files changed, 290 insertions(+), 110 deletions(-) create mode 100644 Package.resolved create mode 100644 Sources/Options/Macro.swift diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..53bea04 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax", + "state" : { + "revision" : "fa8f95c2d536d6620cc2f504ebe8a6167c9fc2dd", + "version" : "510.0.1" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift index a82996e..919dc0c 100644 --- a/Package.swift +++ b/Package.swift @@ -28,4 +28,4 @@ let package = Package( dependencies: ["Options"] ) ] -) \ No newline at end of file +) diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index 7bd1b20..757b7f6 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -5,8 +5,8 @@ // swiftlint:disable line_length // swiftlint:disable explicit_acl -import PackageDescription import CompilerPluginSupport +import PackageDescription let swiftSettings = [ SwiftSetting.enableUpcomingFeature("BareSlashRegexLiterals"), @@ -41,11 +41,11 @@ let package = Package( swiftSettings: swiftSettings ), .macro( - name: "OptionsMacros", - dependencies: [ - .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), - .product(name: "SwiftCompilerPlugin", package: "swift-syntax") - ] + name: "OptionsMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax") + ] ), .testTarget( name: "OptionsTests", diff --git a/Sources/Options/EnumSet.swift b/Sources/Options/EnumSet.swift index f72db23..69a4b3b 100644 --- a/Sources/Options/EnumSet.swift +++ b/Sources/Options/EnumSet.swift @@ -51,51 +51,51 @@ extension EnumSet where EnumType: CaseIterable { } #if swift(>=5.9) -extension EnumSet: Codable - where EnumType: MappedValueRepresentable, EnumType.MappedType: Codable { - /// Decodes the EnumSet based on an Array of MappedTypes. - /// - Parameter decoder: Decoder which contains info as an array of MappedTypes. - public init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let values = try container.decode([EnumType.MappedType].self) - let rawValues = try values.map(EnumType.rawValue(basedOn:)) - let set = Set(rawValues) - rawValue = Self.cumulativeValue(basedOnRawValues: set) - } + extension EnumSet: Codable + where EnumType: MappedValueRepresentable, EnumType.MappedType: Codable { + /// Decodes the EnumSet based on an Array of MappedTypes. + /// - Parameter decoder: Decoder which contains info as an array of MappedTypes. + public init(from decoder: any Decoder) throws { + let container = try decoder.singleValueContainer() + let values = try container.decode([EnumType.MappedType].self) + let rawValues = try values.map(EnumType.rawValue(basedOn:)) + let set = Set(rawValues) + rawValue = Self.cumulativeValue(basedOnRawValues: set) + } - /// Encodes the EnumSet based on an Array of MappedTypes. - /// - Parameter encoder: Encoder which will contain info as an array of MappedTypes. - public func encode(to encoder: any Encoder) throws { - var container = encoder.singleValueContainer() - let values = Self.enums(basedOnRawValue: rawValue) - let mappedValues = try values - .map(\.rawValue) - .map(EnumType.mappedValue(basedOn:)) - try container.encode(mappedValues) + /// Encodes the EnumSet based on an Array of MappedTypes. + /// - Parameter encoder: Encoder which will contain info as an array of MappedTypes. + public func encode(to encoder: any Encoder) throws { + var container = encoder.singleValueContainer() + let values = Self.enums(basedOnRawValue: rawValue) + let mappedValues = try values + .map(\.rawValue) + .map(EnumType.mappedValue(basedOn:)) + try container.encode(mappedValues) + } } -} #else -extension EnumSet: Codable - where EnumType: MappedValueRepresentable, EnumType.MappedType: Codable { - /// Decodes the EnumSet based on an Array of MappedTypes. - /// - Parameter decoder: Decoder which contains info as an array of MappedTypes. - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let values = try container.decode([EnumType.MappedType].self) - let rawValues = try values.map(EnumType.rawValue(basedOn:)) - let set = Set(rawValues) - rawValue = Self.cumulativeValue(basedOnRawValues: set) - } + extension EnumSet: Codable + where EnumType: MappedValueRepresentable, EnumType.MappedType: Codable { + /// Decodes the EnumSet based on an Array of MappedTypes. + /// - Parameter decoder: Decoder which contains info as an array of MappedTypes. + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let values = try container.decode([EnumType.MappedType].self) + let rawValues = try values.map(EnumType.rawValue(basedOn:)) + let set = Set(rawValues) + rawValue = Self.cumulativeValue(basedOnRawValues: set) + } - /// Encodes the EnumSet based on an Array of MappedTypes. - /// - Parameter encoder: Encoder which will contain info as an array of MappedTypes. - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - let values = Self.enums(basedOnRawValue: rawValue) - let mappedValues = try values - .map { $0.rawValue } - .map(EnumType.mappedValue(basedOn:)) - try container.encode(mappedValues) + /// Encodes the EnumSet based on an Array of MappedTypes. + /// - Parameter encoder: Encoder which will contain info as an array of MappedTypes. + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + let values = Self.enums(basedOnRawValue: rawValue) + let mappedValues = try values + .map(\.rawValue) + .map(EnumType.mappedValue(basedOn:)) + try container.encode(mappedValues) + } } -} #endif diff --git a/Sources/Options/Macro.swift b/Sources/Options/Macro.swift new file mode 100644 index 0000000..a632bdb --- /dev/null +++ b/Sources/Options/Macro.swift @@ -0,0 +1,39 @@ +// +// Macro.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation + +#if swift(>=5.9) + @attached( + extension, + conformances: MappedValueCollectionRepresented, MappedValueRepresentable, + names: named(MappedType), named(mappedValues) + ) + public macro Options() = #externalMacro(module: "OptionsMacros", type: "OptionsMacro") +#endif diff --git a/Sources/Options/MappedEnum.swift b/Sources/Options/MappedEnum.swift index b80f21f..1e651d5 100644 --- a/Sources/Options/MappedEnum.swift +++ b/Sources/Options/MappedEnum.swift @@ -1,9 +1,9 @@ /// A generic struct for enumerations which allow for additional values attached. public struct MappedEnum: Codable, Sendable -where EnumType.MappedType: Codable { + where EnumType.MappedType: Codable { /// Base Enumeraion value. public let value: EnumType - + /// Creates an instance based on the base enumeration value. /// - Parameter value: Base Enumeration value. public init(value: EnumType) { @@ -12,47 +12,47 @@ where EnumType.MappedType: Codable { } #if swift(>=5.9) -extension MappedEnum { - /// Decodes the value based on the mapped value. - /// - Parameter decoder: Decoder. - public init(from decoder: any Decoder) throws { - let container = try decoder.singleValueContainer() - let label = try container.decode(EnumType.MappedType.self) - let rawValue = try EnumType.rawValue(basedOn: label) - guard let value = EnumType(rawValue: rawValue) else { - preconditionFailure("Invalid Raw Value.") + extension MappedEnum { + /// Decodes the value based on the mapped value. + /// - Parameter decoder: Decoder. + public init(from decoder: any Decoder) throws { + let container = try decoder.singleValueContainer() + let label = try container.decode(EnumType.MappedType.self) + let rawValue = try EnumType.rawValue(basedOn: label) + guard let value = EnumType(rawValue: rawValue) else { + preconditionFailure("Invalid Raw Value.") + } + self.value = value } - self.value = value - } - /// Encodes the value based on the mapped value. - /// - Parameter encoder: Encoder. - public func encode(to encoder: any Encoder) throws { - let string = try EnumType.mappedValue(basedOn: value.rawValue) - var container = encoder.singleValueContainer() - try container.encode(string) + /// Encodes the value based on the mapped value. + /// - Parameter encoder: Encoder. + public func encode(to encoder: any Encoder) throws { + let string = try EnumType.mappedValue(basedOn: value.rawValue) + var container = encoder.singleValueContainer() + try container.encode(string) + } } -} #else -extension MappedEnum { - /// Decodes the value based on the mapped value. - /// - Parameter decoder: Decoder. - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let label = try container.decode(EnumType.MappedType.self) - let rawValue = try EnumType.rawValue(basedOn: label) - guard let value = EnumType(rawValue: rawValue) else { - preconditionFailure("Invalid Raw Value.") + extension MappedEnum { + /// Decodes the value based on the mapped value. + /// - Parameter decoder: Decoder. + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let label = try container.decode(EnumType.MappedType.self) + let rawValue = try EnumType.rawValue(basedOn: label) + guard let value = EnumType(rawValue: rawValue) else { + preconditionFailure("Invalid Raw Value.") + } + self.value = value } - self.value = value - } - /// Encodes the value based on the mapped value. - /// - Parameter encoder: Encoder. - public func encode(to encoder: Encoder) throws { - let string = try EnumType.mappedValue(basedOn: value.rawValue) - var container = encoder.singleValueContainer() - try container.encode(string) + /// Encodes the value based on the mapped value. + /// - Parameter encoder: Encoder. + public func encode(to encoder: Encoder) throws { + let string = try EnumType.mappedValue(basedOn: value.rawValue) + var container = encoder.singleValueContainer() + try container.encode(string) + } } -} #endif diff --git a/Sources/Options/MappedValueRepresentable.swift b/Sources/Options/MappedValueRepresentable.swift index fac41dc..415ca82 100644 --- a/Sources/Options/MappedValueRepresentable.swift +++ b/Sources/Options/MappedValueRepresentable.swift @@ -28,7 +28,7 @@ // public protocol MappedValueRepresentable: RawRepresentable, CaseIterable, Sendable { - associatedtype MappedType + associatedtype MappedType = String /// Gets the raw value based on the MappedType. /// - Parameter value: MappedType value. /// - Returns: The raw value of the enumeration based on the `MappedType `value. diff --git a/Sources/Options/MappedValueRepresentableError.swift b/Sources/Options/MappedValueRepresentableError.swift index f2fa3ca..3572e90 100644 --- a/Sources/Options/MappedValueRepresentableError.swift +++ b/Sources/Options/MappedValueRepresentableError.swift @@ -36,5 +36,5 @@ public enum MappedValueRepresentableError: Error, Sendable { } #if swift(<5.9) -public protocol Sendable {} + public protocol Sendable {} #endif diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift index 393f711..bec57de 100644 --- a/Sources/OptionsMacros/OptionsMacro.swift +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -1,17 +1,135 @@ // -// File.swift -// +// OptionsMacro.swift +// SimulatorServices // -// Created by Leo Dion on 3/31/24. +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. // import Foundation -import SwiftSyntaxMacros import SwiftCompilerPlugin +import SwiftSyntax +import SwiftSyntaxMacros @main struct MacrosPlugin: CompilerPlugin { let providingMacros: [Macro.Type] = [ - + OptionsMacro.self ] } + +enum CustomError: Error { case message(String) } +extension EnumDeclSyntax { + var caseElements: [EnumCaseElementSyntax] { + memberBlock.members.flatMap { member in + guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { + return [EnumCaseElementSyntax]() + } + + return Array(caseDecl.elements) + } + } +} + +extension DeclModifierSyntax { + var isNeededAccessLevelModifier: Bool { + switch name.tokenKind { + case .keyword(.public): return true + default: return false + } + } +} + +public struct OptionsMacro: ExtensionMacro { + public static func expansion( + of _: SwiftSyntax.AttributeSyntax, + attachedTo declaration: some SwiftSyntax.DeclGroupSyntax, + providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol, + conformingTo _: [SwiftSyntax.TypeSyntax], + in _: some SwiftSyntaxMacros.MacroExpansionContext + ) throws -> [SwiftSyntax.ExtensionDeclSyntax] { + guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { + throw CustomError.message("Type must be struct.") + } + + let access = enumDecl.modifiers.first(where: \.isNeededAccessLevelModifier) + + let values = enumDecl.caseElements.map { caseElement in + ArrayElementSyntax(expression: StringLiteralExprSyntax(content: caseElement.name.trimmed.text)) + } + + var arrayElement = ArrayElementListSyntax { + .init(values) + } + + let arrayExpression = ArrayExprSyntax(elements: arrayElement) + + let typeName = enumDecl.name + + let typeAlias = TypeAliasDeclSyntax( + name: "MappedType", + initializer: .init(value: IdentifierTypeSyntax(name: "String")) + ) + + let inheritanceClause = InheritanceClauseSyntax( + inheritedTypes: .init(itemsBuilder: { + InheritedTypeSyntax(type: IdentifierTypeSyntax(name: "MappedValueRepresentable")) + InheritedTypeSyntax(type: IdentifierTypeSyntax(name: "MappedValueCollectionRepresented")) + + }) + ) + + let mappedValuesSyntax = VariableDeclSyntax( + modifiers: .init(itemsBuilder: { + DeclModifierSyntax(name: .keyword(.static)) + + }), + bindingSpecifier: .keyword(.let), + bindings: .init(itemsBuilder: { + PatternBindingSyntax( + pattern: IdentifierPatternSyntax(identifier: .identifier("mappedValues")), + initializer: .init(value: arrayExpression) + ) + + }) + ) + +// static let mappedValues = [ +// "github", +// "travisci", +// "circleci", +// "bitrise" +// ] + let extensionDecl = ExtensionDeclSyntax( + modifiers: DeclModifierListSyntax([access].compactMap { $0 }), + extendedType: IdentifierTypeSyntax(name: typeName), + inheritanceClause: inheritanceClause, + memberBlock: MemberBlockSyntax(members: MemberBlockItemListSyntax(itemsBuilder: { + typeAlias + mappedValuesSyntax + })) + ) + return [extensionDecl] + } +} diff --git a/Tests/OptionsTests/Mocks/MockCollectionEnum.swift b/Tests/OptionsTests/Mocks/MockCollectionEnum.swift index 6985d8c..53803a5 100644 --- a/Tests/OptionsTests/Mocks/MockCollectionEnum.swift +++ b/Tests/OptionsTests/Mocks/MockCollectionEnum.swift @@ -29,18 +29,28 @@ import Options -// swiftlint:disable identifier_name - -internal enum MockCollectionEnum: Int, MappedValueCollectionRepresented { - case a - case b - case c - case d - internal typealias MappedType = String - internal static let mappedValues = [ - "a", - "b", - "c", - "d" - ] -} +#if swift(>=5.9) + // swiftlint:disable identifier_name + @Options + internal enum MockCollectionEnum: Int, Sendable { + case a + case b + case c + case d + } +#else + // swiftlint:disable identifier_name + internal enum MockCollectionEnum: Int, MappedValueCollectionRepresented { + case a + case b + case c + case d + internal typealias MappedType = String + internal static let mappedValues = [ + "a", + "b", + "c", + "d" + ] + } +#endif diff --git a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift index 677b36c..4313f78 100644 --- a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift +++ b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift @@ -30,7 +30,6 @@ import Options // swiftlint:disable identifier_name - internal enum MockDictionaryEnum: Int, MappedValueDictionaryRepresented { case a = 2 case b = 5 From c0a8446359e1b53b482c06299365352713af18dc Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 2 Apr 2024 07:41:42 -0400 Subject: [PATCH 13/25] fixing workflow for newer versions --- .github/workflows/Options.yml | 4 ++-- Package.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml index 29dd08a..4a20534 100644 --- a/.github/workflows/Options.yml +++ b/.github/workflows/Options.yml @@ -73,8 +73,8 @@ jobs: if: "!contains(github.event.head_commit.message, 'ci skip')" strategy: matrix: - runs-on: ["macos-11.0"] - xcode: ["/Applications/Xcode_11.7.app","/Applications/Xcode_12.4.app"] + runs-on: ["macos-12.0"] + xcode: ["/Applications/Xcode_14.1.app","/Applications/Xcode_14.2.app"] steps: - uses: actions/checkout@v4 - name: Cache swift package modules diff --git a/Package.swift b/Package.swift index 919dc0c..398f453 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.7.1 // swiftlint:disable explicit_top_level_acl // swiftlint:disable prefixed_toplevel_constant From f86f97f53d3d03701634aa1a6fa715ebacd3dbcc Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 2 Apr 2024 12:44:45 -0400 Subject: [PATCH 14/25] refactoring macro --- Sources/OptionsMacros/OptionsMacro.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift index bec57de..9b724b5 100644 --- a/Sources/OptionsMacros/OptionsMacro.swift +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -61,6 +61,16 @@ extension DeclModifierSyntax { } } +extension ArrayExprSyntax { + init(from items: some Collection, _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol) { + let values = items.map(closure).map{ArrayElementSyntax(expression: $0)} + var arrayElement = ArrayElementListSyntax { + .init(values) + } + self.init(elements: arrayElement) + } +} + public struct OptionsMacro: ExtensionMacro { public static func expansion( of _: SwiftSyntax.AttributeSyntax, From ab662fa45ac6f034cc2aed7566ab550a128a9521 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 2 Apr 2024 13:39:42 -0400 Subject: [PATCH 15/25] [skip ci] refractoiring macros --- Sources/OptionsMacros/OptionsMacro.swift | 74 +++++++++++++----------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift index 9b724b5..e17a510 100644 --- a/Sources/OptionsMacros/OptionsMacro.swift +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -63,57 +63,68 @@ extension DeclModifierSyntax { extension ArrayExprSyntax { init(from items: some Collection, _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol) { - let values = items.map(closure).map{ArrayElementSyntax(expression: $0)} - var arrayElement = ArrayElementListSyntax { + let values = items.map(closure).map { ArrayElementSyntax(expression: $0) } + let arrayElement = ArrayElementListSyntax { .init(values) } self.init(elements: arrayElement) } } +extension InheritanceClauseSyntax { + init(protocols: [SwiftSyntax.TypeSyntax]) { + self.init( + inheritedTypes: .init(itemsBuilder: { + .init( + protocols.map { typeSyntax in + InheritedTypeSyntax(type: typeSyntax) + } + ) + }) + ) + } +} + +// extension VariableDeclSyntax { +// init ( +// tokenModifier: TokenSyntax?, +// bindingSpecifier: TokenSyntax, +// variableIdenfitier: TokenSyntax, +// initializer: PatternBindingSyntax? +// ) { +// +// } +// } public struct OptionsMacro: ExtensionMacro { public static func expansion( of _: SwiftSyntax.AttributeSyntax, attachedTo declaration: some SwiftSyntax.DeclGroupSyntax, - providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol, - conformingTo _: [SwiftSyntax.TypeSyntax], + providingExtensionsOf _: some SwiftSyntax.TypeSyntaxProtocol, + conformingTo protocols: [SwiftSyntax.TypeSyntax], in _: some SwiftSyntaxMacros.MacroExpansionContext ) throws -> [SwiftSyntax.ExtensionDeclSyntax] { guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { throw CustomError.message("Type must be struct.") } + let typeName = enumDecl.name + let access = enumDecl.modifiers.first(where: \.isNeededAccessLevelModifier) - let values = enumDecl.caseElements.map { caseElement in - ArrayElementSyntax(expression: StringLiteralExprSyntax(content: caseElement.name.trimmed.text)) + let arrayExpression = ArrayExprSyntax(from: enumDecl.caseElements) { caseElement in + StringLiteralExprSyntax(content: caseElement.name.trimmed.text) } - var arrayElement = ArrayElementListSyntax { - .init(values) - } - - let arrayExpression = ArrayExprSyntax(elements: arrayElement) - - let typeName = enumDecl.name - let typeAlias = TypeAliasDeclSyntax( name: "MappedType", initializer: .init(value: IdentifierTypeSyntax(name: "String")) ) - let inheritanceClause = InheritanceClauseSyntax( - inheritedTypes: .init(itemsBuilder: { - InheritedTypeSyntax(type: IdentifierTypeSyntax(name: "MappedValueRepresentable")) - InheritedTypeSyntax(type: IdentifierTypeSyntax(name: "MappedValueCollectionRepresented")) - - }) - ) + let inheritanceClause = InheritanceClauseSyntax(protocols: protocols) let mappedValuesSyntax = VariableDeclSyntax( modifiers: .init(itemsBuilder: { DeclModifierSyntax(name: .keyword(.static)) - }), bindingSpecifier: .keyword(.let), bindings: .init(itemsBuilder: { @@ -121,24 +132,21 @@ public struct OptionsMacro: ExtensionMacro { pattern: IdentifierPatternSyntax(identifier: .identifier("mappedValues")), initializer: .init(value: arrayExpression) ) - }) ) -// static let mappedValues = [ -// "github", -// "travisci", -// "circleci", -// "bitrise" -// ] let extensionDecl = ExtensionDeclSyntax( modifiers: DeclModifierListSyntax([access].compactMap { $0 }), extendedType: IdentifierTypeSyntax(name: typeName), inheritanceClause: inheritanceClause, - memberBlock: MemberBlockSyntax(members: MemberBlockItemListSyntax(itemsBuilder: { - typeAlias - mappedValuesSyntax - })) + memberBlock: MemberBlockSyntax( + members: MemberBlockItemListSyntax( + itemsBuilder: { + typeAlias + mappedValuesSyntax + } + ) + ) ) return [extensionDecl] } From 7981663f97ed60d17b001c3136c90887865eac3b Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 2 Apr 2024 15:59:25 -0400 Subject: [PATCH 16/25] linting new Macro --- Sources/OptionsMacros/OptionsMacro.swift | 55 +++++++++++++++++++----- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift index e17a510..027b376 100644 --- a/Sources/OptionsMacros/OptionsMacro.swift +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -62,7 +62,10 @@ extension DeclModifierSyntax { } extension ArrayExprSyntax { - init(from items: some Collection, _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol) { + init( + from items: some Collection, + _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol + ) { let values = items.map(closure).map { ArrayElementSyntax(expression: $0) } let arrayElement = ArrayElementListSyntax { .init(values) @@ -85,16 +88,46 @@ extension InheritanceClauseSyntax { } } -// extension VariableDeclSyntax { -// init ( -// tokenModifier: TokenSyntax?, -// bindingSpecifier: TokenSyntax, -// variableIdenfitier: TokenSyntax, -// initializer: PatternBindingSyntax? -// ) { -// -// } -// } +extension VariableDeclSyntax { + init( + keywordModifier: Keyword?, + bindingKeyword: Keyword, + variableName: String, + initializerExpression: (some ExprSyntaxProtocol)? + ) { + let modifiers: DeclModifierListSyntax + + if let keywordModifier { + modifiers = .init(itemsBuilder: { + DeclModifierSyntax(name: .keyword(keywordModifier)) + }) + } else { + modifiers = [] + } + + let initializer: InitializerClauseSyntax? + + if let initializerExpression { + initializer = .init(value: initializerExpression) + } else { + initializer = nil + } + + self.init( + modifiers: modifiers, + bindingSpecifier: .keyword(bindingKeyword), + bindings: .init( + itemsBuilder: { + PatternBindingSyntax( + pattern: IdentifierPatternSyntax(identifier: .identifier(variableName)), + initializer: initializer + ) + } + ) + ) + } +} + public struct OptionsMacro: ExtensionMacro { public static func expansion( of _: SwiftSyntax.AttributeSyntax, From b345326a8d97e36ce4460e92804fc9881396bf45 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 2 Apr 2024 20:25:17 -0400 Subject: [PATCH 17/25] fixing linting --- Package.swift | 5 +- Package@swift-5.9.swift | 8 +- .../MappedValueRepresentableError.swift | 22 ++- .../Extensions/ArrayExprSyntax.swift | 43 ++++++ .../Extensions/DeclModifierListSyntax.swift | 42 ++++++ .../Extensions/DeclModifierSyntax.swift | 39 +++++ .../Extensions/EnumDeclSyntax.swift | 42 ++++++ .../Extensions/ExtensionDeclSyntax.swift | 53 +++++++ .../Extensions/InheritanceClauseSyntax.swift | 44 ++++++ .../Extensions/InvalidDeclError.swift | 34 +++++ .../Extensions/TypeAliasDeclSyntax.swift | 39 +++++ .../Extensions/VariableDeclSyntax.swift | 70 +++++++++ Sources/OptionsMacros/MacrosPlugin.swift | 39 +++++ Sources/OptionsMacros/OptionsMacro.swift | 140 +----------------- 14 files changed, 471 insertions(+), 149 deletions(-) create mode 100644 Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/DeclModifierListSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/DeclModifierSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/EnumDeclSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/InheritanceClauseSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/InvalidDeclError.swift create mode 100644 Sources/OptionsMacros/Extensions/TypeAliasDeclSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift create mode 100644 Sources/OptionsMacros/MacrosPlugin.swift diff --git a/Package.swift b/Package.swift index 398f453..603ef85 100644 --- a/Package.swift +++ b/Package.swift @@ -2,7 +2,6 @@ // swiftlint:disable explicit_top_level_acl // swiftlint:disable prefixed_toplevel_constant -// swiftlint:disable line_length // swiftlint:disable explicit_acl import PackageDescription @@ -29,3 +28,7 @@ let package = Package( ) ] ) + +// swiftlint:enable explicit_top_level_acl +// swiftlint:enable prefixed_toplevel_constant +// swiftlint:enable explicit_acl diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index 757b7f6..a80de37 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -2,7 +2,6 @@ // swiftlint:disable explicit_top_level_acl // swiftlint:disable prefixed_toplevel_constant -// swiftlint:disable line_length // swiftlint:disable explicit_acl import CompilerPluginSupport @@ -45,7 +44,8 @@ let package = Package( dependencies: [ .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftCompilerPlugin", package: "swift-syntax") - ] + ], + swiftSettings: swiftSettings ), .testTarget( name: "OptionsTests", @@ -53,3 +53,7 @@ let package = Package( ) ] ) + +// swiftlint:enable explicit_top_level_acl +// swiftlint:enable prefixed_toplevel_constant +// swiftlint:enable explicit_acl diff --git a/Sources/Options/MappedValueRepresentableError.swift b/Sources/Options/MappedValueRepresentableError.swift index 3572e90..582e869 100644 --- a/Sources/Options/MappedValueRepresentableError.swift +++ b/Sources/Options/MappedValueRepresentableError.swift @@ -29,12 +29,18 @@ import Foundation -/// An Error thrown when the `MappedType` value or `RawType` value -/// are invalid for an `Enum`. -public enum MappedValueRepresentableError: Error, Sendable { - case valueNotFound -} - -#if swift(<5.9) - public protocol Sendable {} +// swiftlint:disable file_types_order +#if swift(>=5.9) + /// An Error thrown when the `MappedType` value or `RawType` value + /// are invalid for an `Enum`. + public enum MappedValueRepresentableError: Error { + case valueNotFound + } +#else + /// An Error thrown when the `MappedType` value or `RawType` value + /// are invalid for an `Enum`. + public enum MappedValueRepresentableError: Error { + case valueNotFound + } #endif +// swiftlint:enable file_types_order diff --git a/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift b/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift new file mode 100644 index 0000000..2cab374 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift @@ -0,0 +1,43 @@ +// +// ArrayExprSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension ArrayExprSyntax { + internal init( + from items: some Collection, + _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol + ) { + let values = items.map(closure).map { ArrayElementSyntax(expression: $0) } + let arrayElement = ArrayElementListSyntax { + .init(values) + } + self.init(elements: arrayElement) + } +} diff --git a/Sources/OptionsMacros/Extensions/DeclModifierListSyntax.swift b/Sources/OptionsMacros/Extensions/DeclModifierListSyntax.swift new file mode 100644 index 0000000..c155633 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/DeclModifierListSyntax.swift @@ -0,0 +1,42 @@ +// +// DeclModifierListSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension DeclModifierListSyntax { + internal init(keywordModifier: Keyword?) { + if let keywordModifier { + self.init { + DeclModifierSyntax(name: .keyword(keywordModifier)) + } + } else { + self.init([]) + } + } +} diff --git a/Sources/OptionsMacros/Extensions/DeclModifierSyntax.swift b/Sources/OptionsMacros/Extensions/DeclModifierSyntax.swift new file mode 100644 index 0000000..f4981a4 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/DeclModifierSyntax.swift @@ -0,0 +1,39 @@ +// +// DeclModifierSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension DeclModifierSyntax { + internal var isNeededAccessLevelModifier: Bool { + switch name.tokenKind { + case .keyword(.public): return true + default: return false + } + } +} diff --git a/Sources/OptionsMacros/Extensions/EnumDeclSyntax.swift b/Sources/OptionsMacros/Extensions/EnumDeclSyntax.swift new file mode 100644 index 0000000..07035df --- /dev/null +++ b/Sources/OptionsMacros/Extensions/EnumDeclSyntax.swift @@ -0,0 +1,42 @@ +// +// EnumDeclSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension EnumDeclSyntax { + internal var caseElements: [EnumCaseElementSyntax] { + memberBlock.members.flatMap { member in + guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { + return [EnumCaseElementSyntax]() + } + + return Array(caseDecl.elements) + } + } +} diff --git a/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift b/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift new file mode 100644 index 0000000..9c9d34a --- /dev/null +++ b/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift @@ -0,0 +1,53 @@ +// +// ExtensionDeclSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension ExtensionDeclSyntax { + internal init( + enumDecl: EnumDeclSyntax, + conformingTo protocols: [SwiftSyntax.TypeSyntax] + ) { + let typeName = enumDecl.name + + let access = enumDecl.modifiers.first(where: \.isNeededAccessLevelModifier) + + self.init( + modifiers: DeclModifierListSyntax([access].compactMap { $0 }), + extendedType: IdentifierTypeSyntax(name: typeName), + inheritanceClause: InheritanceClauseSyntax(protocols: protocols), + memberBlock: MemberBlockSyntax( + members: MemberBlockItemListSyntax { + TypeAliasDeclSyntax(name: "MappedType", for: "String") + VariableDeclSyntax.mappedValuesDeclarationForCases(enumDecl.caseElements) + } + ) + ) + } +} diff --git a/Sources/OptionsMacros/Extensions/InheritanceClauseSyntax.swift b/Sources/OptionsMacros/Extensions/InheritanceClauseSyntax.swift new file mode 100644 index 0000000..be7dc57 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/InheritanceClauseSyntax.swift @@ -0,0 +1,44 @@ +// +// InheritanceClauseSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension InheritanceClauseSyntax { + internal init(protocols: [SwiftSyntax.TypeSyntax]) { + self.init( + inheritedTypes: .init { + .init( + protocols.map { typeSyntax in + InheritedTypeSyntax(type: typeSyntax) + } + ) + } + ) + } +} diff --git a/Sources/OptionsMacros/Extensions/InvalidDeclError.swift b/Sources/OptionsMacros/Extensions/InvalidDeclError.swift new file mode 100644 index 0000000..3ed8741 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/InvalidDeclError.swift @@ -0,0 +1,34 @@ +// +// InvalidDeclError.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +@preconcurrency import SwiftSyntax + +internal struct InvalidDeclError: Error, Sendable { + internal let kind: SyntaxKind +} diff --git a/Sources/OptionsMacros/Extensions/TypeAliasDeclSyntax.swift b/Sources/OptionsMacros/Extensions/TypeAliasDeclSyntax.swift new file mode 100644 index 0000000..b682f69 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/TypeAliasDeclSyntax.swift @@ -0,0 +1,39 @@ +// +// TypeAliasDeclSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension TypeAliasDeclSyntax { + internal init(name: TokenSyntax, for initializerTypeName: TokenSyntax) { + self.init( + name: name, + initializer: .init(value: IdentifierTypeSyntax(name: initializerTypeName)) + ) + } +} diff --git a/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift b/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift new file mode 100644 index 0000000..692811c --- /dev/null +++ b/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift @@ -0,0 +1,70 @@ +// +// VariableDeclSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension VariableDeclSyntax { + internal init( + keywordModifier: Keyword?, + bindingKeyword: Keyword, + variableName: String, + initializerExpression: (some ExprSyntaxProtocol)? + ) { + let modifiers = DeclModifierListSyntax(keywordModifier: keywordModifier) + + let initializer: InitializerClauseSyntax? = + initializerExpression.map { .init(value: $0) } + + self.init( + modifiers: modifiers, + bindingSpecifier: .keyword(bindingKeyword), + bindings: .init { + PatternBindingSyntax( + pattern: IdentifierPatternSyntax(identifier: .identifier(variableName)), + initializer: initializer + ) + } + ) + } + + internal static func mappedValuesDeclarationForCases( + _ caseElements: [EnumCaseElementSyntax] + ) -> VariableDeclSyntax { + let arrayExpression = ArrayExprSyntax(from: caseElements) { caseElement in + StringLiteralExprSyntax(content: caseElement.name.trimmed.text) + } + + return VariableDeclSyntax( + keywordModifier: .static, + bindingKeyword: .let, + variableName: "mappedValues", + initializerExpression: arrayExpression + ) + } +} diff --git a/Sources/OptionsMacros/MacrosPlugin.swift b/Sources/OptionsMacros/MacrosPlugin.swift new file mode 100644 index 0000000..1bc8833 --- /dev/null +++ b/Sources/OptionsMacros/MacrosPlugin.swift @@ -0,0 +1,39 @@ +// +// MacrosPlugin.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftCompilerPlugin +import SwiftSyntax +import SwiftSyntaxMacros + +@main +internal struct MacrosPlugin: CompilerPlugin { + internal let providingMacros: [any Macro.Type] = [ + OptionsMacro.self + ] +} diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift index 027b376..461be5a 100644 --- a/Sources/OptionsMacros/OptionsMacro.swift +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -27,107 +27,9 @@ // OTHER DEALINGS IN THE SOFTWARE. // -import Foundation -import SwiftCompilerPlugin import SwiftSyntax import SwiftSyntaxMacros -@main -struct MacrosPlugin: CompilerPlugin { - let providingMacros: [Macro.Type] = [ - OptionsMacro.self - ] -} - -enum CustomError: Error { case message(String) } -extension EnumDeclSyntax { - var caseElements: [EnumCaseElementSyntax] { - memberBlock.members.flatMap { member in - guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { - return [EnumCaseElementSyntax]() - } - - return Array(caseDecl.elements) - } - } -} - -extension DeclModifierSyntax { - var isNeededAccessLevelModifier: Bool { - switch name.tokenKind { - case .keyword(.public): return true - default: return false - } - } -} - -extension ArrayExprSyntax { - init( - from items: some Collection, - _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol - ) { - let values = items.map(closure).map { ArrayElementSyntax(expression: $0) } - let arrayElement = ArrayElementListSyntax { - .init(values) - } - self.init(elements: arrayElement) - } -} - -extension InheritanceClauseSyntax { - init(protocols: [SwiftSyntax.TypeSyntax]) { - self.init( - inheritedTypes: .init(itemsBuilder: { - .init( - protocols.map { typeSyntax in - InheritedTypeSyntax(type: typeSyntax) - } - ) - }) - ) - } -} - -extension VariableDeclSyntax { - init( - keywordModifier: Keyword?, - bindingKeyword: Keyword, - variableName: String, - initializerExpression: (some ExprSyntaxProtocol)? - ) { - let modifiers: DeclModifierListSyntax - - if let keywordModifier { - modifiers = .init(itemsBuilder: { - DeclModifierSyntax(name: .keyword(keywordModifier)) - }) - } else { - modifiers = [] - } - - let initializer: InitializerClauseSyntax? - - if let initializerExpression { - initializer = .init(value: initializerExpression) - } else { - initializer = nil - } - - self.init( - modifiers: modifiers, - bindingSpecifier: .keyword(bindingKeyword), - bindings: .init( - itemsBuilder: { - PatternBindingSyntax( - pattern: IdentifierPatternSyntax(identifier: .identifier(variableName)), - initializer: initializer - ) - } - ) - ) - } -} - public struct OptionsMacro: ExtensionMacro { public static func expansion( of _: SwiftSyntax.AttributeSyntax, @@ -137,49 +39,11 @@ public struct OptionsMacro: ExtensionMacro { in _: some SwiftSyntaxMacros.MacroExpansionContext ) throws -> [SwiftSyntax.ExtensionDeclSyntax] { guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { - throw CustomError.message("Type must be struct.") + throw InvalidDeclError(kind: declaration.kind) } - let typeName = enumDecl.name - - let access = enumDecl.modifiers.first(where: \.isNeededAccessLevelModifier) - - let arrayExpression = ArrayExprSyntax(from: enumDecl.caseElements) { caseElement in - StringLiteralExprSyntax(content: caseElement.name.trimmed.text) - } - - let typeAlias = TypeAliasDeclSyntax( - name: "MappedType", - initializer: .init(value: IdentifierTypeSyntax(name: "String")) - ) - - let inheritanceClause = InheritanceClauseSyntax(protocols: protocols) - - let mappedValuesSyntax = VariableDeclSyntax( - modifiers: .init(itemsBuilder: { - DeclModifierSyntax(name: .keyword(.static)) - }), - bindingSpecifier: .keyword(.let), - bindings: .init(itemsBuilder: { - PatternBindingSyntax( - pattern: IdentifierPatternSyntax(identifier: .identifier("mappedValues")), - initializer: .init(value: arrayExpression) - ) - }) - ) - let extensionDecl = ExtensionDeclSyntax( - modifiers: DeclModifierListSyntax([access].compactMap { $0 }), - extendedType: IdentifierTypeSyntax(name: typeName), - inheritanceClause: inheritanceClause, - memberBlock: MemberBlockSyntax( - members: MemberBlockItemListSyntax( - itemsBuilder: { - typeAlias - mappedValuesSyntax - } - ) - ) + enumDecl: enumDecl, conformingTo: protocols ) return [extensionDecl] } From bfabd45ad5f34f084c1cabba2fdbab8b8da444c5 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Wed, 3 Apr 2024 13:05:26 -0400 Subject: [PATCH 18/25] adding typealias for Enum Set --- Sources/Options/Macro.swift | 1 + Sources/OptionsMacros/OptionsMacro.swift | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Sources/Options/Macro.swift b/Sources/Options/Macro.swift index a632bdb..1d4a7f0 100644 --- a/Sources/Options/Macro.swift +++ b/Sources/Options/Macro.swift @@ -35,5 +35,6 @@ import Foundation conformances: MappedValueCollectionRepresented, MappedValueRepresentable, names: named(MappedType), named(mappedValues) ) + @attached(peer) public macro Options() = #externalMacro(module: "OptionsMacros", type: "OptionsMacro") #endif diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift index 461be5a..478de7d 100644 --- a/Sources/OptionsMacros/OptionsMacro.swift +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -30,7 +30,26 @@ import SwiftSyntax import SwiftSyntaxMacros -public struct OptionsMacro: ExtensionMacro { +public struct OptionsMacro: ExtensionMacro, PeerMacro { + public static func expansion( + of _: SwiftSyntax.AttributeSyntax, + providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, + in _: some SwiftSyntaxMacros.MacroExpansionContext + ) throws -> [SwiftSyntax.DeclSyntax] { + guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { + throw InvalidDeclError(kind: declaration.kind) + } + let typeName = enumDecl.name + + let aliasName: TokenSyntax = "\(typeName.trimmed)Set" + + let initializerName: TokenSyntax = "EnumSet<\(typeName)>" + + return [ + .init(TypeAliasDeclSyntax(name: aliasName, for: initializerName)) + ] + } + public static func expansion( of _: SwiftSyntax.AttributeSyntax, attachedTo declaration: some SwiftSyntax.DeclGroupSyntax, From ba2752431e2c68df321b56f2050d7e75f1f52efa Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Wed, 3 Apr 2024 13:51:12 -0400 Subject: [PATCH 19/25] further clarification on macros --- Sources/Options/Macro.swift | 4 +- .../MappedValueCollectionRepresented.swift | 81 +++++++++++------- .../MappedValueDictionaryRepresented.swift | 83 ++++++++++++------- .../MappedValueGenericRepresented.swift | 48 +++++++++++ 4 files changed, 151 insertions(+), 65 deletions(-) create mode 100644 Sources/Options/MappedValueGenericRepresented.swift diff --git a/Sources/Options/Macro.swift b/Sources/Options/Macro.swift index 1d4a7f0..f1483c2 100644 --- a/Sources/Options/Macro.swift +++ b/Sources/Options/Macro.swift @@ -32,9 +32,9 @@ import Foundation #if swift(>=5.9) @attached( extension, - conformances: MappedValueCollectionRepresented, MappedValueRepresentable, + conformances: MappedValueRepresentable, MappedValueGenericRepresented, names: named(MappedType), named(mappedValues) ) - @attached(peer) + @attached(peer, names: suffixed(Set)) public macro Options() = #externalMacro(module: "OptionsMacros", type: "OptionsMacro") #endif diff --git a/Sources/Options/MappedValueCollectionRepresented.swift b/Sources/Options/MappedValueCollectionRepresented.swift index 2208886..434a292 100644 --- a/Sources/Options/MappedValueCollectionRepresented.swift +++ b/Sources/Options/MappedValueCollectionRepresented.swift @@ -1,38 +1,57 @@ -/// Protocol which simplifies mapped value by using an ordered Array of values. -public protocol MappedValueCollectionRepresented: MappedValueRepresentable - where RawValue == Int, MappedType: Equatable { + +///// Protocol which simplifies mapped value by using an ordered Array of values. +public protocol MappedValueCollectionRepresented: MappedValueGenericRepresented where MappedValueType : Sequence { /// An array of the mapped values which lines up with each case. - static var mappedValues: [MappedType] { get } + } -extension MappedValueCollectionRepresented { - /// Gets the raw value based on the MappedType by finding the index of the mapped value. - /// - Parameter value: MappedType value. - /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` - /// If the value was not found in the array - /// - Returns: - /// The raw value of the enumeration - /// based on the index the MappedType value was found at. - public static func rawValue(basedOn value: MappedType) throws -> RawValue { - guard let index = mappedValues.firstIndex(of: value) else { - throw MappedValueRepresentableError.valueNotFound - } - +extension Array : MappedValues where Element : Equatable { + + + public func key(value: Element) throws -> Int { + guard let index = self.firstIndex(of: value) else { + throw MappedValueRepresentableError.valueNotFound + } + return index } - - /// Gets the mapped value based on the rawValue - /// by access the array at the raw value subscript. - /// - Parameter rawValue: The raw value of the enumeration - /// which pretains to its index in the `mappedValues` Array. - /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` - /// if the raw value (i.e. index) is outside the range of the `mappedValues` array. - /// - Returns: - /// The Mapped Type value based on the value in the array at the raw value index. - public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { - guard rawValue < mappedValues.count, rawValue >= 0 else { - throw MappedValueRepresentableError.valueNotFound - } - return mappedValues[rawValue] + + public func value(key: Int) throws -> Element { + guard key < count, key >= 0 else { + throw MappedValueRepresentableError.valueNotFound + } + return self[key] } } +// +//extension MappedValueCollectionRepresented { +// /// Gets the raw value based on the MappedType by finding the index of the mapped value. +// /// - Parameter value: MappedType value. +// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` +// /// If the value was not found in the array +// /// - Returns: +// /// The raw value of the enumeration +// /// based on the index the MappedType value was found at. +// public static func rawValue(basedOn value: MappedType) throws -> RawValue { +// guard let index = mappedValues.firstIndex(of: value) else { +// throw MappedValueRepresentableError.valueNotFound +// } +// +// return index +// } +// +// /// Gets the mapped value based on the rawValue +// /// by access the array at the raw value subscript. +// /// - Parameter rawValue: The raw value of the enumeration +// /// which pretains to its index in the `mappedValues` Array. +// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` +// /// if the raw value (i.e. index) is outside the range of the `mappedValues` array. +// /// - Returns: +// /// The Mapped Type value based on the value in the array at the raw value index. +// public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { +// guard rawValue < mappedValues.count, rawValue >= 0 else { +// throw MappedValueRepresentableError.valueNotFound +// } +// return mappedValues[rawValue] +// } +//} diff --git a/Sources/Options/MappedValueDictionaryRepresented.swift b/Sources/Options/MappedValueDictionaryRepresented.swift index 275a598..b6e4513 100644 --- a/Sources/Options/MappedValueDictionaryRepresented.swift +++ b/Sources/Options/MappedValueDictionaryRepresented.swift @@ -1,39 +1,58 @@ /// Protocol which simplifies mapped value by using a dictionary. -public protocol MappedValueDictionaryRepresented: MappedValueRepresentable - where RawValue == Int, MappedType: Equatable { - /// An dictionary of the mapped values. - static var mappedValues: [Int: MappedType] { get } +public protocol MappedValueDictionaryRepresented: MappedValueGenericRepresented +where MappedValueType == Dictionary { } -extension MappedValueDictionaryRepresented { - /// Gets the raw value based on the MappedType by finding the key of the mapped value. - /// - Parameter value: MappedType value. - /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` - /// If the value was not found in the dictionary - /// - Returns: - /// The raw value of the enumeration - /// based on the key the MappedType value. - public static func rawValue(basedOn value: MappedType) throws -> RawValue { - let pair = mappedValues.first { $0.value == value } - guard let key = pair?.key else { - throw MappedValueRepresentableError.valueNotFound - } - return key +extension Dictionary : MappedValues where Key == Int, Value : Equatable { + public func key(value: Value) throws -> Int { + let pair = self.first { $0.value == value } + guard let key = pair?.key else { + throw MappedValueRepresentableError.valueNotFound + } + + return key } - - /// Gets the mapped value based on the rawValue - /// by access the dictionary at the raw value key. - /// - Parameter rawValue: The raw value of the enumeration - /// which pretains to its index in the `mappedValues` Array. - /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` - /// if the raw value (i.e. key) is not in the dictionary. - /// - Returns: - /// The Mapped Type value based on the raw value key. - public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { - guard let value = mappedValues[rawValue] else { - throw MappedValueRepresentableError.valueNotFound - } - return value + + public func value(key: Int) throws -> Value { + guard let value = self[key] else { + throw MappedValueRepresentableError.valueNotFound + } + return value } + + } +// +//extension MappedValueDictionaryRepresented { +// /// Gets the raw value based on the MappedType by finding the key of the mapped value. +// /// - Parameter value: MappedType value. +// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` +// /// If the value was not found in the dictionary +// /// - Returns: +// /// The raw value of the enumeration +// /// based on the key the MappedType value. +// public static func rawValue(basedOn value: MappedType) throws -> RawValue { +// let pair = mappedValues.first { $0.value == value } +// guard let key = pair?.key else { +// throw MappedValueRepresentableError.valueNotFound +// } +// +// return key +// } +// +// /// Gets the mapped value based on the rawValue +// /// by access the dictionary at the raw value key. +// /// - Parameter rawValue: The raw value of the enumeration +// /// which pretains to its index in the `mappedValues` Array. +// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` +// /// if the raw value (i.e. key) is not in the dictionary. +// /// - Returns: +// /// The Mapped Type value based on the raw value key. +// public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { +// guard let value = mappedValues[rawValue] else { +// throw MappedValueRepresentableError.valueNotFound +// } +// return value +// } +//} diff --git a/Sources/Options/MappedValueGenericRepresented.swift b/Sources/Options/MappedValueGenericRepresented.swift new file mode 100644 index 0000000..31694db --- /dev/null +++ b/Sources/Options/MappedValueGenericRepresented.swift @@ -0,0 +1,48 @@ +public protocol MappedValues { + associatedtype Value : Equatable + func key(value: Value) throws -> Int + func value(key: Int) throws -> Value +} + +/// Protocol which simplifies mapped value by using an ordered Array of values. +public protocol MappedValueGenericRepresented: MappedValueRepresentable + where RawValue == Int, MappedType: Equatable { + + associatedtype MappedValueType : MappedValues + /// An array of the mapped values which lines up with each case. + static var mappedValues: MappedValueType { get } +} + +extension MappedValueGenericRepresented { + /// Gets the raw value based on the MappedType by finding the index of the mapped value. + /// - Parameter value: MappedType value. + /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` + /// If the value was not found in the array + /// - Returns: + /// The raw value of the enumeration + /// based on the index the MappedType value was found at. + public static func rawValue(basedOn value: MappedType) throws -> RawValue { +// guard let index = mappedValues.firstIndex(of: value) else { +// throw MappedValueRepresentableError.valueNotFound +// } +// +// return index + return try Self.mappedValues.key(value: value) + } + + /// Gets the mapped value based on the rawValue + /// by access the array at the raw value subscript. + /// - Parameter rawValue: The raw value of the enumeration + /// which pretains to its index in the `mappedValues` Array. + /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` + /// if the raw value (i.e. index) is outside the range of the `mappedValues` array. + /// - Returns: + /// The Mapped Type value based on the value in the array at the raw value index. + public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { + return try Self.mappedValues.value(key: rawValue) +// guard rawValue < mappedValues.count, rawValue >= 0 else { +// throw MappedValueRepresentableError.valueNotFound +// } +// return mappedValues[rawValue] + } +} From d57b3ea2cc5b1cb411204a6d8f92110992af895b Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 4 Apr 2024 10:34:18 -0400 Subject: [PATCH 20/25] [skip ci] Fixing Dictionary --- .../Extensions/ArrayExprSyntax.swift | 35 ++++++++++++ .../Extensions/ExtensionDeclSyntax.swift | 5 +- .../Extensions/VariableDeclSyntax.swift | 57 +++++++++++++++++-- .../{Extensions => }/InvalidDeclError.swift | 5 +- Sources/OptionsMacros/OptionsMacro.swift | 6 +- .../Mocks/MockDictionaryEnum.swift | 31 ++++++---- 6 files changed, 116 insertions(+), 23 deletions(-) rename Sources/OptionsMacros/{Extensions => }/InvalidDeclError.swift (92%) diff --git a/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift b/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift index 2cab374..5115988 100644 --- a/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift +++ b/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift @@ -29,7 +29,42 @@ import SwiftSyntax +extension DictionaryExprSyntax { + internal init(keyValues: KeyValues) { + let dictionaryElements = keyValues.dictionary.map { (key: Int, value: String) in + + return DictionaryElementSyntax( + key: IntegerLiteralExprSyntax(integerLiteral: key), + value: StringLiteralExprSyntax( + openingQuote: .stringQuoteToken(), + segments: .init([.stringSegment(.init(content: .stringSegment(value)))]), + closingQuote: .stringQuoteToken() + ) + ) + } + let list = DictionaryElementListSyntax { + .init(dictionaryElements) + } + self.init(content: .elements(list)) + } +} + +extension Array where Element == String { + init?(keyValues: KeyValues) { + self.init() + for key in 0..( from items: some Collection, _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol diff --git a/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift b/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift index 9c9d34a..7c9b30f 100644 --- a/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift +++ b/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift @@ -33,11 +33,12 @@ extension ExtensionDeclSyntax { internal init( enumDecl: EnumDeclSyntax, conformingTo protocols: [SwiftSyntax.TypeSyntax] - ) { + ) throws { let typeName = enumDecl.name let access = enumDecl.modifiers.first(where: \.isNeededAccessLevelModifier) + let mappedValues = try VariableDeclSyntax.mappedValuesDeclarationForCases(enumDecl.caseElements) self.init( modifiers: DeclModifierListSyntax([access].compactMap { $0 }), extendedType: IdentifierTypeSyntax(name: typeName), @@ -45,7 +46,7 @@ extension ExtensionDeclSyntax { memberBlock: MemberBlockSyntax( members: MemberBlockItemListSyntax { TypeAliasDeclSyntax(name: "MappedType", for: "String") - VariableDeclSyntax.mappedValuesDeclarationForCases(enumDecl.caseElements) + mappedValues } ) ) diff --git a/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift b/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift index 692811c..bd16b55 100644 --- a/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift +++ b/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift @@ -29,6 +29,43 @@ import SwiftSyntax +struct KeyValues { + var lastKey : Int? = nil + var dictionary = [Int: String]() + + var count : Int { + return dictionary.count + } + + var nextKey : Int { + return (lastKey ?? -1) + 1 + } + + mutating func append(value: String, withKey key: Int? = nil) throws { + let key = key ?? nextKey + guard dictionary[key] == nil else { + throw InvalidDeclError.rawValue(key) + } + lastKey = key + dictionary[key] = value + } + + func get(_ key: Int) -> String? { + return dictionary[key] + } +} + +extension KeyValues { + init(caseElements: [EnumCaseElementSyntax]) throws { + self.init() + for caseElement in caseElements { + let key = (caseElement.rawValue?.value.as(IntegerLiteralExprSyntax.self)?.literal.text).flatMap{Int($0)} + let value = + caseElement.name.trimmed.text + try self.append(value: value, withKey: key) + } + } +} extension VariableDeclSyntax { internal init( keywordModifier: Keyword?, @@ -52,13 +89,25 @@ extension VariableDeclSyntax { } ) } + + internal static func initializerExpression(from caseElements: [EnumCaseElementSyntax]) throws -> any ExprSyntaxProtocol { + let keyValues = try KeyValues(caseElements: caseElements) + if let array = Array(keyValues: keyValues) { + return ArrayExprSyntax(from: array) { value in + StringLiteralExprSyntax(content: value) + } + } else { + return DictionaryExprSyntax(keyValues: keyValues) + } + } internal static func mappedValuesDeclarationForCases( _ caseElements: [EnumCaseElementSyntax] - ) -> VariableDeclSyntax { - let arrayExpression = ArrayExprSyntax(from: caseElements) { caseElement in - StringLiteralExprSyntax(content: caseElement.name.trimmed.text) - } + ) throws -> VariableDeclSyntax { + + + //DictionaryElementSyntax(key: IntegerLiteralExprSyntax(integerLiteral: ), value: <#T##ExprSyntaxProtocol#>) + let arrayExpression = try Self.initializerExpression(from: caseElements) return VariableDeclSyntax( keywordModifier: .static, diff --git a/Sources/OptionsMacros/Extensions/InvalidDeclError.swift b/Sources/OptionsMacros/InvalidDeclError.swift similarity index 92% rename from Sources/OptionsMacros/Extensions/InvalidDeclError.swift rename to Sources/OptionsMacros/InvalidDeclError.swift index 3ed8741..74d58b1 100644 --- a/Sources/OptionsMacros/Extensions/InvalidDeclError.swift +++ b/Sources/OptionsMacros/InvalidDeclError.swift @@ -29,6 +29,7 @@ @preconcurrency import SwiftSyntax -internal struct InvalidDeclError: Error, Sendable { - internal let kind: SyntaxKind +internal enum InvalidDeclError: Error, Sendable { + case kind( SyntaxKind) + case rawValue(Int) } diff --git a/Sources/OptionsMacros/OptionsMacro.swift b/Sources/OptionsMacros/OptionsMacro.swift index 478de7d..8f93f5d 100644 --- a/Sources/OptionsMacros/OptionsMacro.swift +++ b/Sources/OptionsMacros/OptionsMacro.swift @@ -37,7 +37,7 @@ public struct OptionsMacro: ExtensionMacro, PeerMacro { in _: some SwiftSyntaxMacros.MacroExpansionContext ) throws -> [SwiftSyntax.DeclSyntax] { guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { - throw InvalidDeclError(kind: declaration.kind) + throw InvalidDeclError.kind(declaration.kind) } let typeName = enumDecl.name @@ -58,10 +58,10 @@ public struct OptionsMacro: ExtensionMacro, PeerMacro { in _: some SwiftSyntaxMacros.MacroExpansionContext ) throws -> [SwiftSyntax.ExtensionDeclSyntax] { guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { - throw InvalidDeclError(kind: declaration.kind) + throw InvalidDeclError.kind(declaration.kind) } - let extensionDecl = ExtensionDeclSyntax( + let extensionDecl = try ExtensionDeclSyntax( enumDecl: enumDecl, conformingTo: protocols ) return [extensionDecl] diff --git a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift index 4313f78..3dd566c 100644 --- a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift +++ b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift @@ -30,16 +30,23 @@ import Options // swiftlint:disable identifier_name -internal enum MockDictionaryEnum: Int, MappedValueDictionaryRepresented { - case a = 2 - case b = 5 - case c = 6 - case d = 12 - internal typealias MappedType = String - internal static var mappedValues = [ - 2: "a", - 5: "b", - 6: "c", - 12: "d" - ] +@Options +internal enum MockDictionaryEnum: Int { + case a = 2 + case b = 5 + case c = 6 + case d = 12 } +//internal enum MockDictionaryEnum: Int, MappedValueDictionaryRepresented { +// case a = 2 +// case b = 5 +// case c = 6 +// case d = 12 +// internal typealias MappedType = String +// internal static var mappedValues = [ +// 2: "a", +// 5: "b", +// 6: "c", +// 12: "d" +// ] +//} From bd8b57cf38c94050322a7302c03882d69b6b807b Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 4 Apr 2024 14:14:52 -0400 Subject: [PATCH 21/25] fixing linting --- .../MappedValueCollectionRepresented.swift | 87 ++++++++--------- .../MappedValueDictionaryRepresented.swift | 95 +++++++++---------- .../MappedValueGenericRepresented.swift | 49 ++++++---- Sources/Options/MappedValues.swift | 36 +++++++ Sources/OptionsMacros/Extensions/Array.swift | 42 ++++++++ .../Extensions/ArrayExprSyntax.swift | 35 ------- .../Extensions/DictionaryElementSyntax.swift | 47 +++++++++ .../Extensions/DictionaryExprSyntax.swift | 41 ++++++++ .../Extensions/ExtensionDeclSyntax.swift | 5 +- .../OptionsMacros/Extensions/KeyValues.swift | 70 ++++++++++++++ .../Extensions/VariableDeclSyntax.swift | 46 +-------- Sources/OptionsMacros/InvalidDeclError.swift | 2 +- .../Mocks/MockDictionaryEnum.swift | 38 ++++---- 13 files changed, 382 insertions(+), 211 deletions(-) create mode 100644 Sources/Options/MappedValues.swift create mode 100644 Sources/OptionsMacros/Extensions/Array.swift create mode 100644 Sources/OptionsMacros/Extensions/DictionaryElementSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/DictionaryExprSyntax.swift create mode 100644 Sources/OptionsMacros/Extensions/KeyValues.swift diff --git a/Sources/Options/MappedValueCollectionRepresented.swift b/Sources/Options/MappedValueCollectionRepresented.swift index 434a292..e595d9c 100644 --- a/Sources/Options/MappedValueCollectionRepresented.swift +++ b/Sources/Options/MappedValueCollectionRepresented.swift @@ -1,57 +1,52 @@ +// +// MappedValueCollectionRepresented.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// -///// Protocol which simplifies mapped value by using an ordered Array of values. -public protocol MappedValueCollectionRepresented: MappedValueGenericRepresented where MappedValueType : Sequence { +// swiftlint:disable:next line_length +@available(*, deprecated, renamed: "MappedValueGenericRepresented", message: "Use MappedValueGenericRepresented instead.") +public protocol MappedValueCollectionRepresented: MappedValueGenericRepresented + where MappedValueType: Sequence { /// An array of the mapped values which lines up with each case. - } -extension Array : MappedValues where Element : Equatable { - - +extension Array: MappedValues where Element: Equatable { public func key(value: Element) throws -> Int { - guard let index = self.firstIndex(of: value) else { - throw MappedValueRepresentableError.valueNotFound - } - + guard let index = firstIndex(of: value) else { + throw MappedValueRepresentableError.valueNotFound + } + return index } - + public func value(key: Int) throws -> Element { - guard key < count, key >= 0 else { - throw MappedValueRepresentableError.valueNotFound - } + guard key < count, key >= 0 else { + throw MappedValueRepresentableError.valueNotFound + } return self[key] } } -// -//extension MappedValueCollectionRepresented { -// /// Gets the raw value based on the MappedType by finding the index of the mapped value. -// /// - Parameter value: MappedType value. -// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` -// /// If the value was not found in the array -// /// - Returns: -// /// The raw value of the enumeration -// /// based on the index the MappedType value was found at. -// public static func rawValue(basedOn value: MappedType) throws -> RawValue { -// guard let index = mappedValues.firstIndex(of: value) else { -// throw MappedValueRepresentableError.valueNotFound -// } -// -// return index -// } -// -// /// Gets the mapped value based on the rawValue -// /// by access the array at the raw value subscript. -// /// - Parameter rawValue: The raw value of the enumeration -// /// which pretains to its index in the `mappedValues` Array. -// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` -// /// if the raw value (i.e. index) is outside the range of the `mappedValues` array. -// /// - Returns: -// /// The Mapped Type value based on the value in the array at the raw value index. -// public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { -// guard rawValue < mappedValues.count, rawValue >= 0 else { -// throw MappedValueRepresentableError.valueNotFound -// } -// return mappedValues[rawValue] -// } -//} diff --git a/Sources/Options/MappedValueDictionaryRepresented.swift b/Sources/Options/MappedValueDictionaryRepresented.swift index b6e4513..ad35e03 100644 --- a/Sources/Options/MappedValueDictionaryRepresented.swift +++ b/Sources/Options/MappedValueDictionaryRepresented.swift @@ -1,58 +1,51 @@ -/// Protocol which simplifies mapped value by using a dictionary. -public protocol MappedValueDictionaryRepresented: MappedValueGenericRepresented -where MappedValueType == Dictionary { -} +// +// MappedValueDictionaryRepresented.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// swiftlint:disable:next line_length +@available(*, deprecated, renamed: "MappedValueGenericRepresented", message: "Use MappedValueGenericRepresented instead.") +public protocol MappedValueDictionaryRepresented: MappedValueGenericRepresented + where MappedValueType == [Int: MappedType] {} -extension Dictionary : MappedValues where Key == Int, Value : Equatable { +extension Dictionary: MappedValues where Key == Int, Value: Equatable { public func key(value: Value) throws -> Int { - let pair = self.first { $0.value == value } - guard let key = pair?.key else { - throw MappedValueRepresentableError.valueNotFound - } - - return key + let pair = first { $0.value == value } + guard let key = pair?.key else { + throw MappedValueRepresentableError.valueNotFound + } + + return key } - + public func value(key: Int) throws -> Value { - guard let value = self[key] else { - throw MappedValueRepresentableError.valueNotFound - } - return value + guard let value = self[key] else { + throw MappedValueRepresentableError.valueNotFound + } + return value } - - } -// -//extension MappedValueDictionaryRepresented { -// /// Gets the raw value based on the MappedType by finding the key of the mapped value. -// /// - Parameter value: MappedType value. -// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` -// /// If the value was not found in the dictionary -// /// - Returns: -// /// The raw value of the enumeration -// /// based on the key the MappedType value. -// public static func rawValue(basedOn value: MappedType) throws -> RawValue { -// let pair = mappedValues.first { $0.value == value } -// guard let key = pair?.key else { -// throw MappedValueRepresentableError.valueNotFound -// } -// -// return key -// } -// -// /// Gets the mapped value based on the rawValue -// /// by access the dictionary at the raw value key. -// /// - Parameter rawValue: The raw value of the enumeration -// /// which pretains to its index in the `mappedValues` Array. -// /// - Throws: `MappedValueCollectionRepresentedError.valueNotFound` -// /// if the raw value (i.e. key) is not in the dictionary. -// /// - Returns: -// /// The Mapped Type value based on the raw value key. -// public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { -// guard let value = mappedValues[rawValue] else { -// throw MappedValueRepresentableError.valueNotFound -// } -// return value -// } -//} diff --git a/Sources/Options/MappedValueGenericRepresented.swift b/Sources/Options/MappedValueGenericRepresented.swift index 31694db..9fbc5c9 100644 --- a/Sources/Options/MappedValueGenericRepresented.swift +++ b/Sources/Options/MappedValueGenericRepresented.swift @@ -1,14 +1,36 @@ -public protocol MappedValues { - associatedtype Value : Equatable - func key(value: Value) throws -> Int - func value(key: Int) throws -> Value -} +// +// MappedValueGenericRepresented.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// /// Protocol which simplifies mapped value by using an ordered Array of values. public protocol MappedValueGenericRepresented: MappedValueRepresentable where RawValue == Int, MappedType: Equatable { - - associatedtype MappedValueType : MappedValues + associatedtype MappedValueType: MappedValues /// An array of the mapped values which lines up with each case. static var mappedValues: MappedValueType { get } } @@ -22,12 +44,7 @@ extension MappedValueGenericRepresented { /// The raw value of the enumeration /// based on the index the MappedType value was found at. public static func rawValue(basedOn value: MappedType) throws -> RawValue { -// guard let index = mappedValues.firstIndex(of: value) else { -// throw MappedValueRepresentableError.valueNotFound -// } -// -// return index - return try Self.mappedValues.key(value: value) + try mappedValues.key(value: value) } /// Gets the mapped value based on the rawValue @@ -39,10 +56,6 @@ extension MappedValueGenericRepresented { /// - Returns: /// The Mapped Type value based on the value in the array at the raw value index. public static func mappedValue(basedOn rawValue: RawValue) throws -> MappedType { - return try Self.mappedValues.value(key: rawValue) -// guard rawValue < mappedValues.count, rawValue >= 0 else { -// throw MappedValueRepresentableError.valueNotFound -// } -// return mappedValues[rawValue] + try mappedValues.value(key: rawValue) } } diff --git a/Sources/Options/MappedValues.swift b/Sources/Options/MappedValues.swift new file mode 100644 index 0000000..813952c --- /dev/null +++ b/Sources/Options/MappedValues.swift @@ -0,0 +1,36 @@ +// +// MappedValues.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation + +public protocol MappedValues { + associatedtype Value: Equatable + func key(value: Value) throws -> Int + func value(key: Int) throws -> Value +} diff --git a/Sources/OptionsMacros/Extensions/Array.swift b/Sources/OptionsMacros/Extensions/Array.swift new file mode 100644 index 0000000..347c791 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/Array.swift @@ -0,0 +1,42 @@ +// +// Array.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation + +extension [String] { + internal init?(keyValues: KeyValues) { + self.init() + for key in 0 ..< keyValues.count { + guard let value = keyValues.get(key) else { + return nil + } + append(value) + } + } +} diff --git a/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift b/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift index 5115988..2cab374 100644 --- a/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift +++ b/Sources/OptionsMacros/Extensions/ArrayExprSyntax.swift @@ -29,42 +29,7 @@ import SwiftSyntax -extension DictionaryExprSyntax { - internal init(keyValues: KeyValues) { - let dictionaryElements = keyValues.dictionary.map { (key: Int, value: String) in - - return DictionaryElementSyntax( - key: IntegerLiteralExprSyntax(integerLiteral: key), - value: StringLiteralExprSyntax( - openingQuote: .stringQuoteToken(), - segments: .init([.stringSegment(.init(content: .stringSegment(value)))]), - closingQuote: .stringQuoteToken() - ) - ) - } - let list = DictionaryElementListSyntax { - .init(dictionaryElements) - } - self.init(content: .elements(list)) - } -} - -extension Array where Element == String { - init?(keyValues: KeyValues) { - self.init() - for key in 0..( from items: some Collection, _ closure: @escaping @Sendable (T) -> some ExprSyntaxProtocol diff --git a/Sources/OptionsMacros/Extensions/DictionaryElementSyntax.swift b/Sources/OptionsMacros/Extensions/DictionaryElementSyntax.swift new file mode 100644 index 0000000..b719a24 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/DictionaryElementSyntax.swift @@ -0,0 +1,47 @@ +// +// DictionaryElementSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension DictionaryElementSyntax { + internal init(pair: (key: Int, value: String)) { + self.init(key: pair.key, value: pair.value) + } + + internal init(key: Int, value: String) { + self.init( + key: IntegerLiteralExprSyntax(integerLiteral: key), + value: StringLiteralExprSyntax( + openingQuote: .stringQuoteToken(), + segments: .init([.stringSegment(.init(content: .stringSegment(value)))]), + closingQuote: .stringQuoteToken() + ) + ) + } +} diff --git a/Sources/OptionsMacros/Extensions/DictionaryExprSyntax.swift b/Sources/OptionsMacros/Extensions/DictionaryExprSyntax.swift new file mode 100644 index 0000000..d21549b --- /dev/null +++ b/Sources/OptionsMacros/Extensions/DictionaryExprSyntax.swift @@ -0,0 +1,41 @@ +// +// DictionaryExprSyntax.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +extension DictionaryExprSyntax { + internal init(keyValues: KeyValues) { + let dictionaryElements = keyValues.dictionary.map(DictionaryElementSyntax.init(pair:)) + + let list = DictionaryElementListSyntax { + .init(dictionaryElements) + } + self.init(content: .elements(list)) + } +} diff --git a/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift b/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift index 7c9b30f..cf1eb6e 100644 --- a/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift +++ b/Sources/OptionsMacros/Extensions/ExtensionDeclSyntax.swift @@ -38,7 +38,10 @@ extension ExtensionDeclSyntax { let access = enumDecl.modifiers.first(where: \.isNeededAccessLevelModifier) - let mappedValues = try VariableDeclSyntax.mappedValuesDeclarationForCases(enumDecl.caseElements) + let mappedValues = try VariableDeclSyntax.mappedValuesDeclarationForCases( + enumDecl.caseElements + ) + self.init( modifiers: DeclModifierListSyntax([access].compactMap { $0 }), extendedType: IdentifierTypeSyntax(name: typeName), diff --git a/Sources/OptionsMacros/Extensions/KeyValues.swift b/Sources/OptionsMacros/Extensions/KeyValues.swift new file mode 100644 index 0000000..7800a73 --- /dev/null +++ b/Sources/OptionsMacros/Extensions/KeyValues.swift @@ -0,0 +1,70 @@ +// +// KeyValues.swift +// SimulatorServices +// +// Created by Leo Dion. +// Copyright © 2024 BrightDigit. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the “Software”), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import SwiftSyntax + +internal struct KeyValues { + internal private(set) var lastKey: Int? + internal private(set) var dictionary = [Int: String]() + + internal var count: Int { + dictionary.count + } + + internal var nextKey: Int { + (lastKey ?? -1) + 1 + } + + internal mutating func append(value: String, withKey key: Int? = nil) throws { + let key = key ?? nextKey + guard dictionary[key] == nil else { + throw InvalidDeclError.rawValue(key) + } + lastKey = key + dictionary[key] = value + } + + internal func get(_ key: Int) -> String? { + dictionary[key] + } +} + +extension KeyValues { + internal init(caseElements: [EnumCaseElementSyntax]) throws { + self.init() + for caseElement in caseElements { + let intText = caseElement.rawValue?.value + .as(IntegerLiteralExprSyntax.self)?.literal.text + let key = intText.flatMap { Int($0) } + let value = + caseElement.name.trimmed.text + try append(value: value, withKey: key) + } + } +} diff --git a/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift b/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift index bd16b55..48dd371 100644 --- a/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift +++ b/Sources/OptionsMacros/Extensions/VariableDeclSyntax.swift @@ -29,43 +29,6 @@ import SwiftSyntax -struct KeyValues { - var lastKey : Int? = nil - var dictionary = [Int: String]() - - var count : Int { - return dictionary.count - } - - var nextKey : Int { - return (lastKey ?? -1) + 1 - } - - mutating func append(value: String, withKey key: Int? = nil) throws { - let key = key ?? nextKey - guard dictionary[key] == nil else { - throw InvalidDeclError.rawValue(key) - } - lastKey = key - dictionary[key] = value - } - - func get(_ key: Int) -> String? { - return dictionary[key] - } -} - -extension KeyValues { - init(caseElements: [EnumCaseElementSyntax]) throws { - self.init() - for caseElement in caseElements { - let key = (caseElement.rawValue?.value.as(IntegerLiteralExprSyntax.self)?.literal.text).flatMap{Int($0)} - let value = - caseElement.name.trimmed.text - try self.append(value: value, withKey: key) - } - } -} extension VariableDeclSyntax { internal init( keywordModifier: Keyword?, @@ -89,8 +52,10 @@ extension VariableDeclSyntax { } ) } - - internal static func initializerExpression(from caseElements: [EnumCaseElementSyntax]) throws -> any ExprSyntaxProtocol { + + internal static func initializerExpression( + from caseElements: [EnumCaseElementSyntax] + ) throws -> any ExprSyntaxProtocol { let keyValues = try KeyValues(caseElements: caseElements) if let array = Array(keyValues: keyValues) { return ArrayExprSyntax(from: array) { value in @@ -104,9 +69,6 @@ extension VariableDeclSyntax { internal static func mappedValuesDeclarationForCases( _ caseElements: [EnumCaseElementSyntax] ) throws -> VariableDeclSyntax { - - - //DictionaryElementSyntax(key: IntegerLiteralExprSyntax(integerLiteral: ), value: <#T##ExprSyntaxProtocol#>) let arrayExpression = try Self.initializerExpression(from: caseElements) return VariableDeclSyntax( diff --git a/Sources/OptionsMacros/InvalidDeclError.swift b/Sources/OptionsMacros/InvalidDeclError.swift index 74d58b1..ea31645 100644 --- a/Sources/OptionsMacros/InvalidDeclError.swift +++ b/Sources/OptionsMacros/InvalidDeclError.swift @@ -30,6 +30,6 @@ @preconcurrency import SwiftSyntax internal enum InvalidDeclError: Error, Sendable { - case kind( SyntaxKind) + case kind(SyntaxKind) case rawValue(Int) } diff --git a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift index 3dd566c..8bca484 100644 --- a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift +++ b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift @@ -29,24 +29,28 @@ import Options -// swiftlint:disable identifier_name -@Options -internal enum MockDictionaryEnum: Int { +#if swift(>=5.9) + // swiftlint:disable identifier_name + @Options + internal enum MockDictionaryEnum: Int, Sendable { case a = 2 case b = 5 case c = 6 case d = 12 -} -//internal enum MockDictionaryEnum: Int, MappedValueDictionaryRepresented { -// case a = 2 -// case b = 5 -// case c = 6 -// case d = 12 -// internal typealias MappedType = String -// internal static var mappedValues = [ -// 2: "a", -// 5: "b", -// 6: "c", -// 12: "d" -// ] -//} + } +#else + // swiftlint:disable identifier_name + internal enum MockDictionaryEnum: Int, MappedValueDictionaryRepresented { + case a = 2 + case b = 5 + case c = 6 + case d = 12 + internal typealias MappedType = String + internal static var mappedValues = [ + 2: "a", + 5: "b", + 6: "c", + 12: "d" + ] + } +#endif From b622ba8786a358831b2d42143657ef8235a6a2be Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 4 Apr 2024 21:08:45 -0400 Subject: [PATCH 22/25] fixing linting --- Sources/Options/MappedValueGenericRepresented.swift | 2 +- Sources/Options/MappedValues.swift | 4 ++++ Sources/OptionsMacros/Extensions/Array.swift | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/Options/MappedValueGenericRepresented.swift b/Sources/Options/MappedValueGenericRepresented.swift index 9fbc5c9..271510b 100644 --- a/Sources/Options/MappedValueGenericRepresented.swift +++ b/Sources/Options/MappedValueGenericRepresented.swift @@ -27,7 +27,7 @@ // OTHER DEALINGS IN THE SOFTWARE. // -/// Protocol which simplifies mapped value by using an ordered Array of values. +/// Protocol which simplifies ``MappedValueRepresentable``by using a ``MappedValues``. public protocol MappedValueGenericRepresented: MappedValueRepresentable where RawValue == Int, MappedType: Equatable { associatedtype MappedValueType: MappedValues diff --git a/Sources/Options/MappedValues.swift b/Sources/Options/MappedValues.swift index 813952c..8d067eb 100644 --- a/Sources/Options/MappedValues.swift +++ b/Sources/Options/MappedValues.swift @@ -29,8 +29,12 @@ import Foundation +/// Protocol which provides a method for ``MappedValueGenericRepresented`` to pull values. public protocol MappedValues { + /// Raw Value Type associatedtype Value: Equatable + /// get the key vased on the value. func key(value: Value) throws -> Int + /// get the value based on the key/index. func value(key: Int) throws -> Value } diff --git a/Sources/OptionsMacros/Extensions/Array.swift b/Sources/OptionsMacros/Extensions/Array.swift index 347c791..78275fe 100644 --- a/Sources/OptionsMacros/Extensions/Array.swift +++ b/Sources/OptionsMacros/Extensions/Array.swift @@ -29,8 +29,8 @@ import Foundation -extension [String] { - internal init?(keyValues: KeyValues) { +extension Array { + internal init?(keyValues: KeyValues) where Element == String { self.init() for key in 0 ..< keyValues.count { guard let value = keyValues.get(key) else { From bd9d2f2fc9df2dff9f152e7c9e6a4d948d7840e2 Mon Sep 17 00:00:00 2001 From: leogdion Date: Thu, 4 Apr 2024 21:40:47 -0400 Subject: [PATCH 23/25] Update Options.yml --- .github/workflows/Options.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml index 4a20534..65ce283 100644 --- a/.github/workflows/Options.yml +++ b/.github/workflows/Options.yml @@ -73,7 +73,7 @@ jobs: if: "!contains(github.event.head_commit.message, 'ci skip')" strategy: matrix: - runs-on: ["macos-12.0"] + runs-on: ["macos-12"] xcode: ["/Applications/Xcode_14.1.app","/Applications/Xcode_14.2.app"] steps: - uses: actions/checkout@v4 From a36491076e07d2e238b28db62836e01f32c1e0e9 Mon Sep 17 00:00:00 2001 From: leogdion Date: Thu, 4 Apr 2024 21:44:59 -0400 Subject: [PATCH 24/25] Update Options.yml --- .github/workflows/Options.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml index 65ce283..93f78b6 100644 --- a/.github/workflows/Options.yml +++ b/.github/workflows/Options.yml @@ -120,11 +120,9 @@ jobs: fail-on-empty-output: true - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 - env: - token: ${{ secrets.CODECOV_TOKEN }} with: token: ${{ secrets.CODECOV_TOKEN }} - flags: macOS,${{ env.XCODE_NAME }} + flags: macOS,${{ env.XCODE_NAME }},${{ matrix.runs-on }} - name: Clean up spm build directory run: rm -rf .build - name: Lint @@ -192,8 +190,6 @@ jobs: fail-on-empty-output: true - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 - env: - token: ${{ secrets.CODECOV_TOKEN }} with: token: ${{ secrets.CODECOV_TOKEN }} flags: macOS,${{ env.XCODE_NAME }} From 9d7ac7f7b25d3bef2bc1a607bae630164c646fd0 Mon Sep 17 00:00:00 2001 From: leogdion Date: Mon, 8 Apr 2024 15:01:20 -0400 Subject: [PATCH 25/25] Adding more to the matrix (#12) --- .github/workflows/Options.yml | 137 ++++++++++++------ Package.resolved | 14 -- Package.swift | 2 +- ...wift-5.9.swift => Package@swift-5.10.swift | 14 +- ...ollectionRepresented.swift => Array.swift} | 6 +- ...naryRepresented.swift => Dictionary.swift} | 2 +- Sources/Options/Macro.swift | 2 +- .../MappedValueRepresentableError.swift | 4 +- Tests/LinuxMain.swift | 37 ----- Tests/OptionsTests/EnumSetTests.swift | 98 ++++++------- Tests/OptionsTests/MappedEnumTests.swift | 69 +++++---- ...appedValueCollectionRepresentedTests.swift | 83 +++++------ ...appedValueDictionaryRepresentedTests.swift | 83 +++++------ .../MappedValueRepresentableTests.swift | 21 ++- .../Mocks/MockCollectionEnum.swift | 2 +- .../Mocks/MockDictionaryEnum.swift | 2 +- Tests/OptionsTests/XCTestManifests.swift | 98 ------------- 17 files changed, 279 insertions(+), 395 deletions(-) delete mode 100644 Package.resolved rename Package@swift-5.9.swift => Package@swift-5.10.swift (87%) rename Sources/Options/{MappedValueCollectionRepresented.swift => Array.swift} (92%) rename Sources/Options/{MappedValueDictionaryRepresented.swift => Dictionary.swift} (97%) delete mode 100644 Tests/LinuxMain.swift delete mode 100644 Tests/OptionsTests/XCTestManifests.swift diff --git a/.github/workflows/Options.yml b/.github/workflows/Options.yml index 93f78b6..1f13ac5 100644 --- a/.github/workflows/Options.yml +++ b/.github/workflows/Options.yml @@ -1,10 +1,10 @@ name: macOS - on: push: branches-ignore: - '*WIP' - +env: + PACKAGE_NAME: Options jobs: build-ubuntu: name: Build on Ubuntu @@ -16,34 +16,36 @@ jobs: strategy: matrix: runs-on: [ubuntu-20.04, ubuntu-22.04] - swift-version: ["5.9.2", "5.10"] + swift-version: ["5.7.1", "5.8.1", "5.9", "5.9.2", "5.10"] steps: - uses: actions/checkout@v4 + - name: Set Ubuntu Release DOT + run: echo "RELEASE_DOT=$(lsb_release -sr)" >> $GITHUB_ENV + - name: Set Ubuntu Release NUM + run: echo "RELEASE_NUM=${RELEASE_DOT//[-._]/}" >> $GITHUB_ENV + - name: Set Ubuntu Codename + run: echo "RELEASE_NAME=$(lsb_release -sc)" >> $GITHUB_ENV - name: Cache swift package modules id: cache-spm-linux uses: actions/cache@v4 env: - cache-name: SPM + cache-name: cache-spm with: path: .build - key: ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }}-${{ hashFiles('Package.resolved') }}-${{ env.RELEASE_DOT }} + key: ${{ runner.os }}-${{ env.RELEASE_DOT }}-${{ env.cache-name }}-${{ matrix.swift-version }}-${{ hashFiles('Package.resolved') }} restore-keys: | - ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }}-${{ hashFiles('Package.resolved') }} - ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }} - - name: Set Ubuntu Release DOT - run: echo "RELEASE_DOT=$(lsb_release -sr)" >> $GITHUB_ENV - - name: Set Ubuntu Release NUM - run: echo "RELEASE_NUM=${RELEASE_DOT//[-._]/}" >> $GITHUB_ENV - - name: Set Ubuntu Codename - run: echo "RELEASE_NAME=$(lsb_release -sc)" >> $GITHUB_ENV + ${{ runner.os }}-${{ env.RELEASE_DOT }}-${{ env.cache-name }}-${{ matrix.swift-version }}- + ${{ runner.os }}-${{ env.RELEASE_DOT }}-${{ env.cache-name }}- - name: Cache swift id: cache-swift-linux uses: actions/cache@v4 env: - cache-name: swift + cache-name: cache-swift with: path: swift-${{ env.SWIFT_VER }}-RELEASE-ubuntu${{ env.RELEASE_DOT }} - key: ${{ env.cache-name }}-${{ runner.os }}-${{ env.SWIFT_VER }}-${{ env.RELEASE_DOT }} + key: ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.swift-version }}-${{ env.RELEASE_DOT }} + restore-keys: | + ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.swift-version }}- - name: Download Swift if: steps.cache-swift-linux.outputs.cache-hit != 'true' run: curl -O https://download.swift.org/swift-${SWIFT_VER}-release/ubuntu${RELEASE_NUM}/swift-${SWIFT_VER}-RELEASE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz @@ -52,29 +54,65 @@ jobs: run: tar xzf swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}.tar.gz - name: Add Path run: echo "$GITHUB_WORKSPACE/swift-${SWIFT_VER}-RELEASE-ubuntu${RELEASE_DOT}/usr/bin" >> $GITHUB_PATH - - name: Build - run: swift build - name: Test - run: swift test --enable-code-coverage + run: swift test --enable-code-coverage - uses: sersoft-gmbh/swift-coverage-action@v4 + id: coverage-files with: fail-on-empty-output: true - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4 + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: spm,${{ env.RELEASE_NAME }},${{ env.SWIFT_VER }} + fail_ci_if_error: true + flags: swift-${{ matrix.swift-version }},ubuntu-${{ matrix.RELEASE_DOT }} + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} + files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} build-macos: name: Build on macOS + runs-on: ${{ matrix.os }} + if: "!contains(github.event.head_commit.message, 'ci skip')" env: PACKAGE_NAME: Options - SWIFT_VER: ${{ matrix.swift-version }} - runs-on: ${{ matrix.runs-on }} - if: "!contains(github.event.head_commit.message, 'ci skip')" strategy: matrix: - runs-on: ["macos-12"] - xcode: ["/Applications/Xcode_14.1.app","/Applications/Xcode_14.2.app"] + include: + - xcode: "/Applications/Xcode_14.1.app" + os: macos-12 + iOSVersion: "16.1" + watchOSVersion: "9.0" + watchName: "Apple Watch Series 5 - 40mm" + iPhoneName: "iPhone 12 mini" + - xcode: "/Applications/Xcode_14.2.app" + os: macos-12 + iOSVersion: "16.2" + watchOSVersion: "9.1" + watchName: "Apple Watch Ultra (49mm)" + iPhoneName: "iPhone 14" + - xcode: "/Applications/Xcode_15.0.1.app" + os: macos-13 + iOSVersion: "17.0.1" + watchOSVersion: "10.0" + watchName: "Apple Watch Series 9 (41mm)" + iPhoneName: "iPhone 15" + - xcode: "/Applications/Xcode_15.1.app" + os: macos-13 + iOSVersion: "17.2" + watchOSVersion: "10.2" + watchName: "Apple Watch Series 9 (45mm)" + iPhoneName: "iPhone 15 Plus" + - xcode: "/Applications/Xcode_15.2.app" + os: macos-14 + iOSVersion: "17.2" + watchOSVersion: "10.2" + watchName: "Apple Watch Ultra (49mm)" + iPhoneName: "iPhone 15 Pro" + - xcode: "/Applications/Xcode_15.3.app" + os: macos-14 + iOSVersion: "17.4" + watchOSVersion: "10.4" + watchName: "Apple Watch Ultra 2 (49mm)" + iPhoneName: "iPhone 15 Pro Max" steps: - uses: actions/checkout@v4 - name: Cache swift package modules @@ -84,13 +122,11 @@ jobs: cache-name: cache-spm with: path: .build - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Package.resolved') }} + key: ${{ matrix.os }}-build-${{ env.cache-name }}-${{ matrix.xcode }}-${{ hashFiles('Package.resolved') }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + ${{ matrix.os }}-build-${{ env.cache-name }}-${{ matrix.xcode }}- - name: Cache mint - if: ${{ github.event_name == 'pull_request' && ( github.base_ref == 'main' || endsWith( github.ref_name , 'Prep') ) && matrix.xcode == '/Applications/Xcode_15.2.app' }} + if: startsWith(matrix.xcode,'/Applications/Xcode_15.3') id: cache-mint uses: actions/cache@v4 env: @@ -107,7 +143,7 @@ jobs: - name: Setup Xcode run: sudo xcode-select -s ${{ matrix.xcode }}/Contents/Developer - name: Install mint - if: ${{ github.event_name == 'pull_request' && ( github.base_ref == 'main' || endsWith( github.ref_name , 'Prep') ) && matrix.xcode == '/Applications/Xcode_15.2.app' }} + if: startsWith(matrix.xcode,'/Applications/Xcode_15.3') run: | brew update brew install mint @@ -116,41 +152,48 @@ jobs: - name: Run Swift Package tests run: swift test --enable-code-coverage - uses: sersoft-gmbh/swift-coverage-action@v4 + id: coverage-files-spm with: fail-on-empty-output: true - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 with: + files: ${{ join(fromJSON(steps.coverage-files-spm.outputs.files), ',') }} token: ${{ secrets.CODECOV_TOKEN }} flags: macOS,${{ env.XCODE_NAME }},${{ matrix.runs-on }} - name: Clean up spm build directory run: rm -rf .build - name: Lint run: ./scripts/lint.sh - if: ${{ github.event_name == 'pull_request' && ( github.base_ref == 'main' || endsWith( github.ref_name , 'Prep') ) && matrix.xcode == '/Applications/Xcode_15.2.app' }} + if: startsWith(matrix.xcode,'/Applications/Xcode_15.3') # - name: Run iOS target tests - # run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=${{ matrix.iPhoneName }},OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test + # run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }} -sdk "iphonesimulator" -destination 'platform=iOS Simulator,name=${{ matrix.iPhoneName }},OS=${{ matrix.iOSVersion }}' -enableCodeCoverage YES build test # - uses: sersoft-gmbh/swift-coverage-action@v4 - # - name: Upload coverage reports to Codecov - # uses: codecov/codecov-action@v4 - # env: - # token: ${{ secrets.CODECOV_TOKEN }} + # id: coverage-files-iOS + # with: + # fail-on-empty-output: true + # - name: Upload coverage to Codecov + # uses: codecov/codecov-action@v4 # with: + # fail_ci_if_error: true + # verbose: true + # token: ${{ secrets.CODECOV_TOKEN }} + # files: ${{ join(fromJSON(steps.coverage-files-iOS.outputs.files), ',') }} # flags: iOS,iOS${{ matrix.iOSVersion }},macOS,${{ env.XCODE_NAME }} # - name: Run watchOS target tests - # run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }} -sdk watchsimulator -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test + # run: xcodebuild test -scheme ${{ env.PACKAGE_NAME }} -sdk "watchsimulator" -destination 'platform=watchOS Simulator,name=${{ matrix.watchName }},OS=${{ matrix.watchOSVersion }}' -enableCodeCoverage YES build test # - uses: sersoft-gmbh/swift-coverage-action@v4 + # id: coverage-files-watchOS # with: # fail-on-empty-output: true - # - name: Upload coverage reports to Codecov - # uses: codecov/codecov-action@v4 - # env: - # token: ${{ secrets.CODECOV_TOKEN }} + # - name: Upload coverage to Codecov + # uses: codecov/codecov-action@v4 # with: - # token: ${{ secrets.CODECOV_TOKEN }} + # fail_ci_if_error: true + # verbose: true + # token: ${{ secrets.CODECOV_TOKEN }} + # files: ${{ join(fromJSON(steps.coverage-files-watchOS.outputs.files), ',') }} # flags: watchOS,watchOS${{ matrix.watchOSVersion }},macOS,${{ env.XCODE_NAME }} - # - name: Test Again - # run: swift test build-self: name: Build on Self-Hosting macOS runs-on: [self-hosted, macOS] diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 53bea04..0000000 --- a/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax", - "state" : { - "revision" : "fa8f95c2d536d6620cc2f504ebe8a6167c9fc2dd", - "version" : "510.0.1" - } - } - ], - "version" : 2 -} diff --git a/Package.swift b/Package.swift index 603ef85..5646b68 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7.1 +// swift-tools-version: 5.7.1 // swiftlint:disable explicit_top_level_acl // swiftlint:disable prefixed_toplevel_constant diff --git a/Package@swift-5.9.swift b/Package@swift-5.10.swift similarity index 87% rename from Package@swift-5.9.swift rename to Package@swift-5.10.swift index a80de37..da62733 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.10.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version: 5.10 // swiftlint:disable explicit_top_level_acl // swiftlint:disable prefixed_toplevel_constant @@ -15,13 +15,19 @@ let swiftSettings = [ SwiftSetting.enableUpcomingFeature("ImplicitOpenExistentials"), SwiftSetting.enableUpcomingFeature("StrictConcurrency"), SwiftSetting.enableUpcomingFeature("DisableOutwardActorInference"), - SwiftSetting.enableExperimentalFeature("StrictConcurrency"), - SwiftSetting.unsafeFlags(["-warn-concurrency", "-enable-actor-data-race-checks"]) + SwiftSetting.enableExperimentalFeature("StrictConcurrency") ] let package = Package( name: "Options", - platforms: [.macOS(.v10_15)], + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .tvOS(.v13), + .watchOS(.v6), + .macCatalyst(.v13), + .visionOS(.v1) + ], products: [ .library( name: "Options", diff --git a/Sources/Options/MappedValueCollectionRepresented.swift b/Sources/Options/Array.swift similarity index 92% rename from Sources/Options/MappedValueCollectionRepresented.swift rename to Sources/Options/Array.swift index e595d9c..e192aa1 100644 --- a/Sources/Options/MappedValueCollectionRepresented.swift +++ b/Sources/Options/Array.swift @@ -1,5 +1,5 @@ // -// MappedValueCollectionRepresented.swift +// Array.swift // SimulatorServices // // Created by Leo Dion. @@ -30,9 +30,7 @@ // swiftlint:disable:next line_length @available(*, deprecated, renamed: "MappedValueGenericRepresented", message: "Use MappedValueGenericRepresented instead.") public protocol MappedValueCollectionRepresented: MappedValueGenericRepresented - where MappedValueType: Sequence { - /// An array of the mapped values which lines up with each case. -} + where MappedValueType: Sequence {} extension Array: MappedValues where Element: Equatable { public func key(value: Element) throws -> Int { diff --git a/Sources/Options/MappedValueDictionaryRepresented.swift b/Sources/Options/Dictionary.swift similarity index 97% rename from Sources/Options/MappedValueDictionaryRepresented.swift rename to Sources/Options/Dictionary.swift index ad35e03..79e2b8e 100644 --- a/Sources/Options/MappedValueDictionaryRepresented.swift +++ b/Sources/Options/Dictionary.swift @@ -1,5 +1,5 @@ // -// MappedValueDictionaryRepresented.swift +// Dictionary.swift // SimulatorServices // // Created by Leo Dion. diff --git a/Sources/Options/Macro.swift b/Sources/Options/Macro.swift index f1483c2..e120423 100644 --- a/Sources/Options/Macro.swift +++ b/Sources/Options/Macro.swift @@ -29,7 +29,7 @@ import Foundation -#if swift(>=5.9) +#if swift(>=5.10) @attached( extension, conformances: MappedValueRepresentable, MappedValueGenericRepresented, diff --git a/Sources/Options/MappedValueRepresentableError.swift b/Sources/Options/MappedValueRepresentableError.swift index 582e869..3d1389f 100644 --- a/Sources/Options/MappedValueRepresentableError.swift +++ b/Sources/Options/MappedValueRepresentableError.swift @@ -30,10 +30,10 @@ import Foundation // swiftlint:disable file_types_order -#if swift(>=5.9) +#if swift(>=5.10) /// An Error thrown when the `MappedType` value or `RawType` value /// are invalid for an `Enum`. - public enum MappedValueRepresentableError: Error { + public enum MappedValueRepresentableError: Error, Sendable { case valueNotFound } #else diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 78af61c..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// LinuxMain.swift -// SimulatorServices -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -import XCTest - -import OptionsTests - -var tests = [XCTestCaseEntry]() -tests += OptionsTests.__allTests() - -XCTMain(tests) diff --git a/Tests/OptionsTests/EnumSetTests.swift b/Tests/OptionsTests/EnumSetTests.swift index 7b44cd3..c741779 100644 --- a/Tests/OptionsTests/EnumSetTests.swift +++ b/Tests/OptionsTests/EnumSetTests.swift @@ -27,64 +27,62 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#if canImport(XCTest) - @testable import Options - import XCTest +@testable import Options +import XCTest - internal final class EnumSetTests: XCTestCase { - private static let text = "[\"a\",\"b\",\"c\"]" +internal final class EnumSetTests: XCTestCase { + private static let text = "[\"a\",\"b\",\"c\"]" - internal func testDecoder() { - // swiftlint:disable:next force_unwrapping - let data = Self.text.data(using: .utf8)! - let decoder = JSONDecoder() - let actual: EnumSet - do { - actual = try decoder.decode(EnumSet.self, from: data) - } catch { - XCTAssertNil(error) - return - } - XCTAssertEqual(actual.rawValue, 7) + internal func testDecoder() { + // swiftlint:disable:next force_unwrapping + let data = Self.text.data(using: .utf8)! + let decoder = JSONDecoder() + let actual: EnumSet + do { + actual = try decoder.decode(EnumSet.self, from: data) + } catch { + XCTAssertNil(error) + return } + XCTAssertEqual(actual.rawValue, 7) + } - internal func testEncoder() { - let enumSet = EnumSet(values: [.a, .b, .c]) - let encoder = JSONEncoder() - let data: Data - do { - data = try encoder.encode(enumSet) - } catch { - XCTAssertNil(error) - return - } - - let dataText = String(bytes: data, encoding: .utf8) + internal func testEncoder() { + let enumSet = EnumSet(values: [.a, .b, .c]) + let encoder = JSONEncoder() + let data: Data + do { + data = try encoder.encode(enumSet) + } catch { + XCTAssertNil(error) + return + } - guard let text = dataText else { - XCTAssertNotNil(dataText) - return - } + let dataText = String(bytes: data, encoding: .utf8) - XCTAssertEqual(text, Self.text) + guard let text = dataText else { + XCTAssertNotNil(dataText) + return } - internal func testInitValue() { - let set = EnumSet(rawValue: 7) - XCTAssertEqual(set.rawValue, 7) - } + XCTAssertEqual(text, Self.text) + } - internal func testInitValues() { - let values: [MockCollectionEnum] = [.a, .b, .c] - let set = EnumSet(values: values) - XCTAssertEqual(set.rawValue, 7) - } + internal func testInitValue() { + let set = EnumSet(rawValue: 7) + XCTAssertEqual(set.rawValue, 7) + } - internal func testArray() { - let expected: [MockCollectionEnum] = [.b, .d] - let enumSet = EnumSet(values: expected) - let actual = enumSet.array() - XCTAssertEqual(actual, expected) - } + internal func testInitValues() { + let values: [MockCollectionEnum] = [.a, .b, .c] + let set = EnumSet(values: values) + XCTAssertEqual(set.rawValue, 7) + } + + internal func testArray() { + let expected: [MockCollectionEnum] = [.b, .d] + let enumSet = EnumSet(values: expected) + let actual = enumSet.array() + XCTAssertEqual(actual, expected) } -#endif +} diff --git a/Tests/OptionsTests/MappedEnumTests.swift b/Tests/OptionsTests/MappedEnumTests.swift index ee0bdc8..1e19650 100644 --- a/Tests/OptionsTests/MappedEnumTests.swift +++ b/Tests/OptionsTests/MappedEnumTests.swift @@ -27,46 +27,43 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#if canImport(XCTest) - @testable import Options - import XCTest +@testable import Options +import XCTest - internal final class MappedEnumTests: XCTestCase { - private static let text = "\"a\"" - internal func testDecoder() throws { - // swiftlint:disable:next force_unwrapping - let data = Self.text.data(using: .utf8)! - let decoder = JSONDecoder() - let actual: MappedEnum - do { - actual = try decoder.decode(MappedEnum.self, from: data) - } catch { - XCTAssertNil(error) - return - } - XCTAssertEqual(actual.value, .a) +internal final class MappedEnumTests: XCTestCase { + private static let text = "\"a\"" + internal func testDecoder() throws { + // swiftlint:disable:next force_unwrapping + let data = Self.text.data(using: .utf8)! + let decoder = JSONDecoder() + let actual: MappedEnum + do { + actual = try decoder.decode(MappedEnum.self, from: data) + } catch { + XCTAssertNil(error) + return } + XCTAssertEqual(actual.value, .a) + } - internal func testEncoder() throws { - let encoder = JSONEncoder() - let describedEnum: MappedEnum = .init(value: .a) - let data: Data - do { - data = try encoder.encode(describedEnum) - } catch { - XCTAssertNil(error) - return - } - - let dataText = String(bytes: data, encoding: .utf8) + internal func testEncoder() throws { + let encoder = JSONEncoder() + let describedEnum: MappedEnum = .init(value: .a) + let data: Data + do { + data = try encoder.encode(describedEnum) + } catch { + XCTAssertNil(error) + return + } - guard let text = dataText else { - XCTAssertNotNil(dataText) - return - } + let dataText = String(bytes: data, encoding: .utf8) - XCTAssertEqual(text, Self.text) + guard let text = dataText else { + XCTAssertNotNil(dataText) + return } - } -#endif + XCTAssertEqual(text, Self.text) + } +} diff --git a/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift b/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift index bdf1e1e..8719c74 100644 --- a/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift +++ b/Tests/OptionsTests/MappedValueCollectionRepresentedTests.swift @@ -27,54 +27,51 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#if canImport(XCTest) - @testable import Options - import XCTest +@testable import Options +import XCTest - internal final class MappedValueCollectionRepresentedTests: XCTestCase { - internal func testRawValue() { - try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "a"), 0) - try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "b"), 1) - try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "c"), 2) - try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "d"), 3) - } - - internal func testString() { - try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 0), "a") - try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 1), "b") - try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 2), "c") - try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 3), "d") - } +internal final class MappedValueCollectionRepresentedTests: XCTestCase { + internal func testRawValue() { + try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "a"), 0) + try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "b"), 1) + try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "c"), 2) + try XCTAssertEqual(MockCollectionEnum.rawValue(basedOn: "d"), 3) + } - internal func testRawValueFailure() { - let caughtError: MappedValueRepresentableError? - do { - _ = try MockCollectionEnum.rawValue(basedOn: "e") - caughtError = nil - } catch let error as MappedValueRepresentableError { - caughtError = error - } catch { - XCTAssertNil(error) - caughtError = nil - } + internal func testString() { + try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 0), "a") + try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 1), "b") + try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 2), "c") + try XCTAssertEqual(MockCollectionEnum.mappedValue(basedOn: 3), "d") + } - XCTAssertEqual(caughtError, .valueNotFound) + internal func testRawValueFailure() { + let caughtError: MappedValueRepresentableError? + do { + _ = try MockCollectionEnum.rawValue(basedOn: "e") + caughtError = nil + } catch let error as MappedValueRepresentableError { + caughtError = error + } catch { + XCTAssertNil(error) + caughtError = nil } - internal func testStringFailure() { - let caughtError: MappedValueRepresentableError? - do { - _ = try MockCollectionEnum.mappedValue(basedOn: .max) - caughtError = nil - } catch let error as MappedValueRepresentableError { - caughtError = error - } catch { - XCTAssertNil(error) - caughtError = nil - } + XCTAssertEqual(caughtError, .valueNotFound) + } - XCTAssertEqual(caughtError, .valueNotFound) + internal func testStringFailure() { + let caughtError: MappedValueRepresentableError? + do { + _ = try MockCollectionEnum.mappedValue(basedOn: .max) + caughtError = nil + } catch let error as MappedValueRepresentableError { + caughtError = error + } catch { + XCTAssertNil(error) + caughtError = nil } - } -#endif + XCTAssertEqual(caughtError, .valueNotFound) + } +} diff --git a/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift b/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift index d3ed318..8aca268 100644 --- a/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift +++ b/Tests/OptionsTests/MappedValueDictionaryRepresentedTests.swift @@ -27,54 +27,51 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#if canImport(XCTest) - @testable import Options - import XCTest +@testable import Options +import XCTest - internal final class MappedValueDictionaryRepresentedTests: XCTestCase { - internal func testRawValue() { - try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "a"), 2) - try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "b"), 5) - try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "c"), 6) - try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "d"), 12) - } - - internal func testString() { - try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 2), "a") - try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 5), "b") - try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 6), "c") - try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 12), "d") - } +internal final class MappedValueDictionaryRepresentedTests: XCTestCase { + internal func testRawValue() { + try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "a"), 2) + try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "b"), 5) + try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "c"), 6) + try XCTAssertEqual(MockDictionaryEnum.rawValue(basedOn: "d"), 12) + } - internal func testRawValueFailure() { - let caughtError: MappedValueRepresentableError? - do { - _ = try MockDictionaryEnum.rawValue(basedOn: "e") - caughtError = nil - } catch let error as MappedValueRepresentableError { - caughtError = error - } catch { - XCTAssertNil(error) - caughtError = nil - } + internal func testString() { + try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 2), "a") + try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 5), "b") + try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 6), "c") + try XCTAssertEqual(MockDictionaryEnum.mappedValue(basedOn: 12), "d") + } - XCTAssertEqual(caughtError, .valueNotFound) + internal func testRawValueFailure() { + let caughtError: MappedValueRepresentableError? + do { + _ = try MockDictionaryEnum.rawValue(basedOn: "e") + caughtError = nil + } catch let error as MappedValueRepresentableError { + caughtError = error + } catch { + XCTAssertNil(error) + caughtError = nil } - internal func testStringFailure() { - let caughtError: MappedValueRepresentableError? - do { - _ = try MockDictionaryEnum.mappedValue(basedOn: 0) - caughtError = nil - } catch let error as MappedValueRepresentableError { - caughtError = error - } catch { - XCTAssertNil(error) - caughtError = nil - } + XCTAssertEqual(caughtError, .valueNotFound) + } - XCTAssertEqual(caughtError, .valueNotFound) + internal func testStringFailure() { + let caughtError: MappedValueRepresentableError? + do { + _ = try MockDictionaryEnum.mappedValue(basedOn: 0) + caughtError = nil + } catch let error as MappedValueRepresentableError { + caughtError = error + } catch { + XCTAssertNil(error) + caughtError = nil } - } -#endif + XCTAssertEqual(caughtError, .valueNotFound) + } +} diff --git a/Tests/OptionsTests/MappedValueRepresentableTests.swift b/Tests/OptionsTests/MappedValueRepresentableTests.swift index 86e839a..e400539 100644 --- a/Tests/OptionsTests/MappedValueRepresentableTests.swift +++ b/Tests/OptionsTests/MappedValueRepresentableTests.swift @@ -27,17 +27,14 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#if canImport(XCTest) - @testable import Options - import XCTest +@testable import Options +import XCTest - internal final class MappedValueRepresentableTests: XCTestCase { - internal func testStringValue() { - try XCTAssertEqual(MockCollectionEnum.a.mappedValue(), "a") - try XCTAssertEqual(MockCollectionEnum.b.mappedValue(), "b") - try XCTAssertEqual(MockCollectionEnum.c.mappedValue(), "c") - try XCTAssertEqual(MockCollectionEnum.d.mappedValue(), "d") - } +internal final class MappedValueRepresentableTests: XCTestCase { + internal func testStringValue() { + try XCTAssertEqual(MockCollectionEnum.a.mappedValue(), "a") + try XCTAssertEqual(MockCollectionEnum.b.mappedValue(), "b") + try XCTAssertEqual(MockCollectionEnum.c.mappedValue(), "c") + try XCTAssertEqual(MockCollectionEnum.d.mappedValue(), "d") } - -#endif +} diff --git a/Tests/OptionsTests/Mocks/MockCollectionEnum.swift b/Tests/OptionsTests/Mocks/MockCollectionEnum.swift index 53803a5..c588f4f 100644 --- a/Tests/OptionsTests/Mocks/MockCollectionEnum.swift +++ b/Tests/OptionsTests/Mocks/MockCollectionEnum.swift @@ -29,7 +29,7 @@ import Options -#if swift(>=5.9) +#if swift(>=5.10) // swiftlint:disable identifier_name @Options internal enum MockCollectionEnum: Int, Sendable { diff --git a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift index 8bca484..77be088 100644 --- a/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift +++ b/Tests/OptionsTests/Mocks/MockDictionaryEnum.swift @@ -29,7 +29,7 @@ import Options -#if swift(>=5.9) +#if swift(>=5.10) // swiftlint:disable identifier_name @Options internal enum MockDictionaryEnum: Int, Sendable { diff --git a/Tests/OptionsTests/XCTestManifests.swift b/Tests/OptionsTests/XCTestManifests.swift deleted file mode 100644 index a0835cc..0000000 --- a/Tests/OptionsTests/XCTestManifests.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// XCTestManifests.swift -// SimulatorServices -// -// Created by Leo Dion. -// Copyright © 2024 BrightDigit. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the “Software”), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#if !canImport(ObjectiveC) - import XCTest - - extension EnumSetTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__EnumSetTests = [ - ("testArray", testArray), - ("testDecoder", testDecoder), - ("testEncoder", testEncoder), - ("testInitValue", testInitValue), - ("testInitValues", testInitValues) - ] - } - - extension MappedEnumTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__MappedEnumTests = [ - ("testDecoder", testDecoder), - ("testEncoder", testEncoder) - ] - } - - extension MappedValueCollectionRepresentedTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__MappedValueCollectionRepresentedTests = [ - ("testRawValue", testRawValue), - ("testRawValueFailure", testRawValueFailure), - ("testString", testString), - ("testStringFailure", testStringFailure) - ] - } - - extension MappedValueDictionaryRepresentedTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__MappedValueDictionaryRepresentedTests = [ - ("testRawValue", testRawValue), - ("testRawValueFailure", testRawValueFailure), - ("testString", testString), - ("testStringFailure", testStringFailure) - ] - } - - extension MappedValueRepresentableTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__MappedValueRepresentableTests = [ - ("testStringValue", testStringValue) - ] - } - - public func __allTests() -> [XCTestCaseEntry] { - [ - testCase(EnumSetTests.__allTests__EnumSetTests), - testCase(MappedEnumTests.__allTests__MappedEnumTests), - testCase(MappedValueCollectionRepresentedTests.__allTests__MappedValueCollectionRepresentedTests), - testCase(MappedValueDictionaryRepresentedTests.__allTests__MappedValueDictionaryRepresentedTests), - testCase(MappedValueRepresentableTests.__allTests__MappedValueRepresentableTests) - ] - } -#endif