From 2cd1c5582a7925dc6115fc3e3a411dfc6b8d305f Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Wed, 30 Oct 2024 13:24:35 +0000 Subject: [PATCH] [CI] Adopt GitHub Actions (#775) * [CI] Adopt GitHub Actions # Motivation We want to adopt GitHub actions in all of our Swift repositories to replace our current Jenkins CI. # Modification This PR adopts the reusable workflows from NIO and the swiftlang organization to ensure soundness and test coverage. Additionally, this PR enables a sematic label check and adds a `release.yml` to make releasing this package easy. # Result Modern CI and release flow. --- .github/release.yml | 14 + .github/workflows/main.yml | 22 + .github/workflows/pull_request.yml | 26 + .github/workflows/pull_request_label.yml | 18 + .licenseignore | 34 + .swift-format | 58 + .swiftformat | 19 - .vscode/launch.json | 76 + CONTRIBUTING.md | 15 +- NOTICE.txt | 4 +- Package.swift | 13 +- .../Inbound/ResponseRoundtripHandler.swift | 74 +- .../Outbound/CommandRoundtripHandler.swift | 12 +- .../NIOIMAP/Client/AppendStateMachine.swift | 20 +- Sources/NIOIMAP/Client/FramingParser.swift | 66 +- .../NIOIMAP/Coders/ClientStateMachine.swift | 96 +- Sources/NIOIMAP/Coders/CommandEncoder.swift | 6 +- .../NIOIMAP/Coders/IMAPClientHandler.swift | 29 +- .../NIOIMAP/Coders/IMAPServerHandler.swift | 18 +- Sources/NIOIMAPCore/Base64/Base64.swift | 75 +- Sources/NIOIMAPCore/Base64/Chromium.swift | 575 +++-- .../NIOIMAPCore/Base64/DecodingError.swift | 74 + .../NIOIMAPCore/ByteBuffer+WriteLiteral.swift | 69 +- Sources/NIOIMAPCore/CommandEncodeBuffer.swift | 18 +- Sources/NIOIMAPCore/EncodeBuffer.swift | 48 +- .../Grammar/AbsoluteMessagePath.swift | 4 +- .../Grammar/Append/AppendData.swift | 4 +- .../Grammar/Append/AppendMessage.swift | 4 +- .../Grammar/Append/AppendOptions.swift | 17 +- .../Grammar/AuthenticatedURL.swift | 3 +- .../Grammar/AuthenticatedURLRump.swift | 4 +- .../Grammar/AuthenticatedURLVerifier.swift | 6 +- Sources/NIOIMAPCore/Grammar/Body/Body.swift | 7 +- .../Grammar/Body/Field/Disposition.swift | 15 +- .../Body/Field/DispositionAndLanguage.swift | 7 +- .../Grammar/Body/Field/Fields.swift | 19 +- .../Grammar/Body/Field/LanguageLocation.swift | 5 +- .../Body/Field/LocationAndExtensions.swift | 12 +- .../Grammar/Body/Field/ParameterPair.swift | 4 +- .../NIOIMAPCore/Grammar/Body/Multipart.swift | 18 +- .../NIOIMAPCore/Grammar/Body/Singlepart.swift | 42 +- .../Grammar/ByteRange/ByteRange.swift | 4 +- .../ByteRange/MessagePathByteRange.swift | 6 +- Sources/NIOIMAPCore/Grammar/Capability.swift | 9 +- .../NIOIMAPCore/Grammar/Command/Command.swift | 347 +-- .../Grammar/Command/CommandStreamPart.swift | 20 +- .../NIOIMAPCore/Grammar/CreateParameter.swift | 4 +- .../Grammar/Date/InternalDate.swift | 31 +- .../ESearch/ExtendedSearchOptions.swift | 12 +- .../ESearch/ExtendedSearchSourceOptions.swift | 15 +- .../NIOIMAPCore/Grammar/EmailAddress.swift | 12 +- .../Grammar/Enable/EnableData.swift | 4 +- .../Grammar/EncodedSearchQuery.swift | 4 +- Sources/NIOIMAPCore/Grammar/EntryValue.swift | 8 +- Sources/NIOIMAPCore/Grammar/Envelope.swift | 36 +- Sources/NIOIMAPCore/Grammar/Expire.swift | 3 +- .../NIOIMAPCore/Grammar/FetchAttribute.swift | 37 +- .../Grammar/FetchModificationResponse.swift | 5 +- .../NIOIMAPCore/Grammar/FetchModifier.swift | 4 +- .../Grammar/Flag/FlagKeyword.swift | 8 +- .../NIOIMAPCore/Grammar/Flag/StoreFlags.swift | 7 +- .../Grammar/FullAuthenticatedURL.swift | 3 +- .../NIOIMAPCore/Grammar/FullDateTime.swift | 13 +- Sources/NIOIMAPCore/Grammar/ID/ID.swift | 7 +- Sources/NIOIMAPCore/Grammar/IMAPServer.swift | 5 +- Sources/NIOIMAPCore/Grammar/IMAPURL.swift | 6 +- .../IMAPURLAuthenticationMechanism.swift | 3 +- .../NIOIMAPCore/Grammar/InitialResponse.swift | 5 +- .../Grammar/List/ListReturnOps.swift | 8 +- .../Grammar/List/ListSelectBaseOption.swift | 4 +- .../Grammar/List/ListSelectOption.swift | 13 +- .../Grammar/Mailbox/MailboxAttribute.swift | 4 +- .../Grammar/Mailbox/MailboxData.swift | 44 +- .../Grammar/Mailbox/MailboxGroup.swift | 27 +- .../Grammar/Mailbox/MailboxInfo.swift | 17 +- .../Grammar/Mailbox/MailboxName.swift | 5 +- .../Grammar/MailboxUIDValidity.swift | 4 +- .../NIOIMAPCore/Grammar/MechanismBase64.swift | 7 +- Sources/NIOIMAPCore/Grammar/Media/Media.swift | 8 +- .../Grammar/Message/GmailLabel.swift | 7 +- .../Grammar/Message/MessageAttributes.swift | 79 +- .../Grammar/Message/MessageData.swift | 8 +- Sources/NIOIMAPCore/Grammar/MessagePath.swift | 16 +- .../Grammar/MetadataResponse.swift | 12 +- Sources/NIOIMAPCore/Grammar/NString.swift | 10 +- .../Namespace/NamespaceDescription.swift | 6 +- .../Grammar/Namespace/NamespaceResponse.swift | 15 +- .../NamespaceResponseExtension.swift | 11 +- .../Grammar/NetworkMessagePath.swift | 6 +- Sources/NIOIMAPCore/Grammar/NetworkPath.swift | 6 +- .../Grammar/Option/OptionExtension.swift | 8 +- .../Grammar/Option/OptionValueComp.swift | 4 +- .../Grammar/Partial/PartialRange.swift | 11 +- .../Grammar/Quota/QuotaLimit.swift | 4 +- .../Grammar/Quota/QuotaResource.swift | 3 +- .../Grammar/Quota/QuotaResponse.swift | 11 +- .../Grammar/Quota/QuotaRootResponse.swift | 5 +- .../Response/ExtendedSearchResponse.swift | 26 +- .../Grammar/Response/ResponseCodeAppend.swift | 3 +- .../Grammar/Response/ResponseCodeCopy.swift | 6 +- .../Grammar/Response/ResponseData.swift | 4 +- .../Grammar/Response/ResponseFatal.swift | 4 +- .../Grammar/Response/ResponseText.swift | 8 +- .../Grammar/Response/ResponseTextCode.swift | 19 +- .../Grammar/Response/TaggedResponse.swift | 4 +- .../Response/TaggedResponseState.swift | 9 +- .../Response/UntaggedResponseState.swift | 15 +- .../Grammar/RumpAuthenticatedURL.swift | 3 +- .../Grammar/RumpURLAndMechanism.swift | 4 +- .../Grammar/Search/SearchCorrelator.swift | 13 +- .../Grammar/Search/SearchKey.swift | 238 +- .../Search/SearchModificationSequence.swift | 20 +- .../Grammar/Search/SearchReturnData.swift | 7 +- .../Search/SearchReturnDataExtension.swift | 4 +- .../Grammar/Search/SearchReturnOption.swift | 7 +- .../Search/SearchReturnOptionExtension.swift | 11 +- Sources/NIOIMAPCore/Grammar/SectionSpec.swift | 28 +- .../NIOIMAPCore/Grammar/SelectParameter.swift | 24 +- .../Grammar/Sequence/SequenceNumber.swift | 5 +- .../Grammar/Sequence/SequenceRange.swift | 7 +- .../Grammar/SequenceMatchData.swift | 7 +- Sources/NIOIMAPCore/Grammar/SortData.swift | 11 +- .../Grammar/Tagged/ParameterValue.swift | 4 +- .../Grammar/Tagged/TaggedExtension.swift | 15 +- .../Grammar/UID/MessageIdentifier.swift | 9 +- .../Grammar/UID/MessageIdentifierRange.swift | 29 +- .../Grammar/UID/MessageIdentifierSet.swift | 71 +- .../UID/MessageIdentifierSetNonEmpty.swift | 4 +- Sources/NIOIMAPCore/Grammar/URLCommand.swift | 4 +- .../NIOIMAPCore/Grammar/URLFetchData.swift | 4 +- .../NIOIMAPCore/Grammar/URLFetchType.swift | 43 +- Sources/NIOIMAPCore/Grammar/UserInfo.swift | 4 +- Sources/NIOIMAPCore/ModifiedUTF7.swift | 5 +- .../NIOIMAPCore/Parser/CommandParser.swift | 116 +- .../Parser/Grammar/GrammarParser+Append.swift | 47 +- .../Parser/Grammar/GrammarParser+Body.swift | 224 +- .../Grammar/GrammarParser+Commands.swift | 179 +- .../Parser/Grammar/GrammarParser+Date.swift | 10 +- .../Parser/Grammar/GrammarParser+Entry.swift | 73 +- .../Grammar/GrammarParser+Envelope.swift | 20 +- .../Parser/Grammar/GrammarParser+Fetch.swift | 124 +- .../Parser/Grammar/GrammarParser+List.swift | 80 +- .../Grammar/GrammarParser+Mailbox.swift | 155 +- .../Grammar/GrammarParser+Message.swift | 159 +- .../Grammar/GrammarParser+Partial.swift | 14 +- .../Grammar/GrammarParser+Response.swift | 167 +- .../Parser/Grammar/GrammarParser+Search.swift | 272 ++- .../Grammar/GrammarParser+Sequence.swift | 79 +- .../Parser/Grammar/GrammarParser+UID.swift | 8 +- .../Parser/Grammar/GrammarParser.swift | 891 ++++--- .../NIOIMAPCore/Parser/ParserLibrary.swift | 139 +- .../NIOIMAPCore/Parser/ResponseParser.swift | 116 +- .../Parser/SynchronizingLiteralParser.swift | 44 +- .../Parser/UInt8+ParseTypeMembership.swift | 35 +- Sources/NIOIMAPCore/Pipelining.swift | 495 ++-- .../NIOIMAPCore/ResponseEncodeBuffer.swift | 35 +- Sources/NIOIMAPCore/String+ByteBuffer.swift | 4 +- Sources/NIOIMAPCore/_ByteBufferProtocol.swift | 35 +- .../CommandTester.swift | 2 +- Sources/NIOIMAPPerformanceTester/main.swift | 205 +- Sources/Proxy/MailClientToProxyHandler.swift | 5 +- Tests/LinuxMain.swift | 15 - Tests/NIOIMAPCoreTests/Base64Tests.swift | 181 +- .../Grammar/Append/AppendMessage+Tests.swift | 41 +- .../Grammar/Append/AppendOptions+Tests.swift | 15 +- .../Grammar/AuthImapUrl+Tests.swift | 10 +- .../Grammar/AuthImapUrlFull+Tests.swift | 16 +- .../Grammar/AuthImapUrlRump+Tests.swift | 13 +- .../Grammar/Body/BodyStructure+Tests.swift | 1665 +++++++++++-- .../Grammar/Body/Field/BodyFieldsTests.swift | 10 +- .../Field/FieldLanguageLocationTests.swift | 5 +- .../Body/Type/BodyMultipartTests.swift | 88 +- .../Body/Type/BodySinglepartTests.swift | 28 +- .../Grammar/ByteBufferWriteLiteralTests.swift | 15 +- .../Grammar/ByteRange/ByteRange+Tests.swift | 12 +- .../Grammar/ChangedSinceModifier+Tests.swift | 4 +- .../CommandType/CommandStream+Tests.swift | 58 +- .../CommandType/CommandType+Tests.swift | 139 +- .../Grammar/DateTests/InternalDateTests.swift | 30 +- .../ESearch/ESearchOptions+Tests.swift | 34 +- .../ESearch/ESearchSourceOptions+Tests.swift | 12 +- .../Grammar/EmailAddressTests.swift | 7 +- .../Grammar/EncodeTestClass.swift | 42 +- .../EncodedAuthenticationType+Tests.swift | 2 +- .../Grammar/EncodedMailbox+Tests.swift | 2 +- .../Grammar/EncodedSearch+Tests.swift | 2 +- .../Grammar/EncodedSection+Tests.swift | 2 +- .../Grammar/EncodedUrlAuth+Tests.swift | 2 +- .../Grammar/EncodedUser+Tests.swift | 2 +- .../Grammar/Entry+Tests.swift | 2 +- .../Grammar/Entry/EntryFlagName+Tests.swift | 2 +- .../Grammar/EnvelopeTests.swift | 39 +- .../Grammar/Expire+Tests.swift | 2 +- .../Grammar/FetchAttributeTests.swift | 35 +- .../Grammar/FetchModifiedResponse+Tests.swift | 2 +- .../Grammar/FetchModifier+Tests.swift | 12 +- .../Grammar/FullDateTime+Tests.swift | 7 +- .../Grammar/IAbsolutePath+Tests.swift | 5 +- .../Grammar/ICommand+Tests.swift | 20 +- .../Grammar/ID/ID+Tests.swift | 36 +- .../Grammar/IMAPRangeTests.swift | 4 +- .../Grammar/IMAPServer+Tests.swift | 15 +- .../Grammar/IMAPURLSection+Tests.swift | 4 +- .../Grammar/IMessageList+Tests.swift | 10 +- .../Grammar/IMessageOrPartial+Tests.swift | 51 +- .../Grammar/IMessagePart+Tests.swift | 28 +- .../Grammar/INetworkPath+Tests.swift | 2 +- .../Grammar/IUAVerifier+Tests.swift | 2 +- .../Grammar/IURLAuthRump+Tests.swift | 10 +- .../Grammar/IUrlAuth+Tests.swift | 7 +- .../Grammar/ImapUrl+Tests.swift | 2 +- .../Grammar/ImapUrlRel+Tests.swift | 8 +- .../List/ListSelectBaseOption+Tests.swift | 2 +- .../Mailbox/MailboxAttribute+Tests.swift | 13 +- .../Grammar/Mailbox/MailboxData+Tests.swift | 32 +- .../Grammar/Mailbox/MailboxInfo+Tests.swift | 5 +- .../Grammar/Mailbox/MailboxName+Tests.swift | 23 +- .../Message/MessageAtributesTests.swift | 69 +- .../MessageIdentifierRange+Tests.swift | 8 +- .../Grammar/MessageIdentifierSet+Tests.swift | 8 +- .../Namespace/NamespaceCommand+Tests.swift | 2 +- .../NamespaceDescription+Tests.swift | 5 +- .../Namespace/NamespaceResponse+Tests.swift | 2 +- .../Option/OptionExtension+Tests.swift | 5 +- .../Grammar/Option/OptionValue+Tests.swift | 2 +- .../Option/OptionVendorTag+Tests.swift | 2 +- .../Grammar/Partial/PartialRange+Tests.swift | 32 +- .../Grammar/Patterns+Tests.swift | 2 +- .../Grammar/Quota/QuotaLimit+Tests.swift | 2 +- .../Grammar/Quota/QuotaResource+Tests.swift | 2 +- .../Response/ContinuationRequestTests.swift | 40 +- .../Response/ESearchResponse+Tests.swift | 83 +- .../Grammar/Response/Response+Tests.swift | 50 +- .../Response/ResponseCodeAppend+Tests.swift | 2 +- .../Response/ResponseCodeCopy+Tests.swift | 8 +- .../Grammar/Response/ResponseData+Tests.swift | 2 +- .../Grammar/Response/ResponseFatalTests.swift | 2 +- .../Grammar/Response/ResponseText+Tests.swift | 12 +- .../Response/ResponseTextCodeTests.swift | 13 +- .../Response/TaggedResponse+Tests.swift | 5 +- .../Response/UntaggedStatus+Tests.swift | 10 +- .../Search/SearchCorrelator+Tests.swift | 5 +- .../Grammar/Search/SearchKeyTests.swift | 17 +- .../Search/SearchModifiedSequence+Tests.swift | 20 +- .../Search/SearchReturnData+Tests.swift | 6 +- .../SearchReturnDataExtension+Tests.swift | 2 +- .../Search/SearchReturnOption+Tests.swift | 6 +- .../Grammar/SelectParameter+Tests.swift | 26 +- .../Sequence/SequenceNumberTests.swift | 2 +- .../TaggedExtension+Tests.swift | 2 +- .../Grammar/UID/IUID+Tests.swift | 4 +- .../Grammar/UID/UIDRangeTests.swift | 104 +- .../Grammar/UID/UIDSetNonEmptyTests.swift | 36 +- .../Grammar/UID/UIDSetTests.swift | 532 ++-- .../Grammar/UID/UIDTests.swift | 2 +- .../Grammar/UID/UIDValidity+Tests.swift | 2 +- .../Grammar/URLRumpMechanism+Tests.swift | 2 +- .../NIOIMAPCoreTests/ModifiedUTF7+Tests.swift | 5 +- .../Parser/CommandParser+Tests.swift | 41 +- .../Grammar/GrammarParser+Append+Tests.swift | 36 +- .../Grammar/GrammarParser+Body+Tests.swift | 226 +- .../GrammarParser+Commands+Tests.swift | 235 +- .../Grammar/GrammarParser+Entry+Tests.swift | 30 +- .../GrammarParser+Envelope+Tests.swift | 151 +- .../Grammar/GrammarParser+Fetch+Tests.swift | 35 +- .../Grammar/GrammarParser+List+Tests.swift | 2 +- .../Grammar/GrammarParser+Mailbox+Tests.swift | 44 +- .../Grammar/GrammarParser+Message+Tests.swift | 242 +- .../GrammarParser+Response+Tests.swift | 40 +- .../Grammar/GrammarParser+Search+Tests.swift | 62 +- .../Grammar/GrammarParser+Sequence.swift | 18 +- .../Grammar/GrammarParser+UID+Tests.swift | 31 +- .../Parser/IMAPParserTests.swift | 1013 +++++--- .../Parser/ParserLibraryTests.swift | 286 ++- .../Parser/ResponseParser+Tests.swift | 1067 +++++++- .../Parser/RoundtripTests.swift | 30 +- .../SynchronizingLiteralParserTests.swift | 129 +- .../Parser/UInt8+ParseTypeMembership.swift | 24 +- Tests/NIOIMAPCoreTests/PipeliningTests.swift | 2171 ++++++++++------- .../ResponseStreamingTests.swift | 328 ++- .../String+ByteBuffer+Tests.swift | 2 +- Tests/NIOIMAPCoreTests/TestUtilities.swift | 27 +- Tests/NIOIMAPTests/B2MV+Tests.swift | 235 +- .../Client/AppendStateMachineTests.swift | 17 +- .../AuthenticationStateMachineTests.swift | 8 +- .../Coders/ClientStateMachineTests.swift | 162 +- .../Coders/IMAPClientHandlerTests.swift | 532 ++-- Tests/NIOIMAPTests/CommandDecoder+Tests.swift | 11 +- Tests/NIOIMAPTests/CommandEncoder+Tests.swift | 179 +- Tests/NIOIMAPTests/FramingParserTests.swift | 65 +- Tests/NIOIMAPTests/IntegrationTests.swift | 32 +- Tests/NIOIMAPTests/ParserStressTests.swift | 22 +- Tests/NIOIMAPTests/RealWorldTests.swift | 72 +- Tests/NIOIMAPTests/TestUtilities.swift | 27 +- dev/process_grammar.sh | 11 +- docker/Dockerfile | 31 - docker/docker-compose.2004.57.yaml | 22 - docker/docker-compose.2204.58.yaml | 22 - docker/docker-compose.2204.59.yaml | 21 - docker/docker-compose.2204.main.yaml | 21 - docker/docker-compose.yaml | 41 - scripts/analyze_performance_results.rb | 183 -- scripts/check_no_api_breakages.sh | 122 - scripts/generate_contributors_list.sh | 39 - scripts/generate_docs.sh | 119 - scripts/soundness.sh | 124 - 306 files changed, 12892 insertions(+), 6512 deletions(-) create mode 100644 .github/release.yml create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/pull_request.yml create mode 100644 .github/workflows/pull_request_label.yml create mode 100644 .licenseignore create mode 100644 .swift-format delete mode 100644 .swiftformat create mode 100644 .vscode/launch.json delete mode 100644 Tests/LinuxMain.swift delete mode 100644 docker/Dockerfile delete mode 100644 docker/docker-compose.2004.57.yaml delete mode 100644 docker/docker-compose.2204.58.yaml delete mode 100644 docker/docker-compose.2204.59.yaml delete mode 100644 docker/docker-compose.2204.main.yaml delete mode 100644 docker/docker-compose.yaml delete mode 100755 scripts/analyze_performance_results.rb delete mode 100755 scripts/check_no_api_breakages.sh delete mode 100755 scripts/generate_contributors_list.sh delete mode 100755 scripts/generate_docs.sh delete mode 100755 scripts/soundness.sh diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000..f96b51492 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,14 @@ +changelog: + categories: + - title: SemVer Major + labels: + - ⚠️ semver/major + - title: SemVer Minor + labels: + - semver/minor + - title: SemVer Patch + labels: + - semver/patch + - title: Other Changes + labels: + - semver/none diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..cb37bb973 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,22 @@ +name: Main + +on: + push: + branches: [main] + schedule: + - cron: "0 8,20 * * *" + +jobs: + unit-tests: + name: Unit tests + uses: apple/swift-nio/.github/workflows/unit_tests.yml@main + with: + linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error" + linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error" + + cxx-interop: + name: Cxx interop + uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 000000000..8552de814 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,26 @@ +name: PR + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + soundness: + name: Soundness + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + license_header_check_project_name: "SwiftNIO" + + unit-tests: + name: Unit tests + uses: apple/swift-nio/.github/workflows/unit_tests.yml@main + with: + linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error" + linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error" + linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error" + + cxx-interop: + name: Cxx interop + uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml new file mode 100644 index 000000000..86f199f32 --- /dev/null +++ b/.github/workflows/pull_request_label.yml @@ -0,0 +1,18 @@ +name: PR label + +on: + pull_request: + types: [labeled, unlabeled, opened, reopened, synchronize] + +jobs: + semver-label-check: + name: Semantic Version label check + runs-on: ubuntu-latest + timeout-minutes: 1 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Check for Semantic Version label + uses: apple/swift-nio/.github/actions/pull_request_semver_label_checker@main diff --git a/.licenseignore b/.licenseignore new file mode 100644 index 000000000..6e692ed5e --- /dev/null +++ b/.licenseignore @@ -0,0 +1,34 @@ +.gitignore +**/.gitignore +.gitattributes +.git-blame-ignore-revs +.mailfilter +.mailmap +.spi.yml +.swift-format +.editorconfig +.github/* +*.md +*.txt +*.yml +*.yaml +*.json +Package.swift +**/Package.swift +Package@-*.swift +**/Package@-*.swift +Package.resolved +**/Package.resolved +Makefile +*.modulemap +**/*.modulemap +**/*.docc/* +*.xcprivacy +**/*.xcprivacy +*.symlink +**/*.symlink +Dockerfile +**/Dockerfile +Snippets/* +dev/git.commit.template +Sources/NIOIMAPCore/Base64 diff --git a/.swift-format b/.swift-format new file mode 100644 index 000000000..26b3f51cc --- /dev/null +++ b/.swift-format @@ -0,0 +1,58 @@ +{ + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "indentation" : { + "spaces" : 4 + }, + "indentConditionalCompilationBlocks" : false, + "indentSwitchCaseLabels" : false, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : true, + "lineBreakBeforeEachGenericRequirement" : true, + "lineLength" : 120, + "maximumBlankLines" : 1, + "prioritizeKeepingFunctionOutputTogether" : true, + "respectsExistingLineBreaks" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLowerCamelCase" : false, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : false, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : false, + "ReturnVoidInsteadOfEmptyTuple" : true, + "UseEarlyExits" : true, + "UseLetInEveryBoundCaseVariable" : false, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : false, + "UseSynthesizedInitializer" : false, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + }, + "spacesAroundRangeFormationOperators" : false, + "tabWidth" : 4, + "version" : 1 +} \ No newline at end of file diff --git a/.swiftformat b/.swiftformat deleted file mode 100644 index e0de0cfbe..000000000 --- a/.swiftformat +++ /dev/null @@ -1,19 +0,0 @@ -# file options - ---swiftversion 5.7 ---exclude .build - -# format options - ---self insert ---patternlet inline ---stripunusedargs unnamed-only ---ifdef no-indent - -# rules ---disable redundantParens ---decimalgrouping ignore ---binarygrouping ignore ---octalgrouping ignore ---extensionacl on-declarations - diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..41a119fff --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,76 @@ +{ + "configurations": [ + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Debug Proxy", + "program": "${workspaceFolder:swift-nio-imap}/.build/debug/Proxy", + "preLaunchTask": "swift: Build Debug Proxy" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Release Proxy", + "program": "${workspaceFolder:swift-nio-imap}/.build/release/Proxy", + "preLaunchTask": "swift: Build Release Proxy" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Debug NIOIMAPPerformanceTester", + "program": "${workspaceFolder:swift-nio-imap}/.build/debug/NIOIMAPPerformanceTester", + "preLaunchTask": "swift: Build Debug NIOIMAPPerformanceTester" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Release NIOIMAPPerformanceTester", + "program": "${workspaceFolder:swift-nio-imap}/.build/release/NIOIMAPPerformanceTester", + "preLaunchTask": "swift: Build Release NIOIMAPPerformanceTester" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Debug NIOIMAPFuzzer", + "program": "${workspaceFolder:swift-nio-imap}/.build/debug/NIOIMAPFuzzer", + "preLaunchTask": "swift: Build Debug NIOIMAPFuzzer" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Release NIOIMAPFuzzer", + "program": "${workspaceFolder:swift-nio-imap}/.build/release/NIOIMAPFuzzer", + "preLaunchTask": "swift: Build Release NIOIMAPFuzzer" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Debug CLI", + "program": "${workspaceFolder:swift-nio-imap}/.build/debug/CLI", + "preLaunchTask": "swift: Build Debug CLI" + }, + { + "type": "lldb", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:swift-nio-imap}", + "name": "Release CLI", + "program": "${workspaceFolder:swift-nio-imap}/.build/release/CLI", + "preLaunchTask": "swift: Build Release CLI" + } + ] +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32409cbcc..fb9d49504 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,25 +65,14 @@ We require that your commit messages match our template. The easiest way to do t The default policy for taking contributions is “Squash and Merge” - because of this the commit message format rule above applies to the PR rather than every commit contained within it. -### Make sure Tests work on Linux +### Run CI checks locally -SwiftNIO IMAP uses XCTest to run tests on both macOS and Linux. While the macOS version of XCTest is able to use the Objective-C runtime to discover tests at execution time, the Linux version is not (prior to swift 5.1). -For this reason, whenever you add new tests **you have to run a script** that generates the hooks needed to run those tests on Linux, or our CI will complain that the tests are not all present on Linux. To do this, merely execute `ruby ./scripts/generate_linux_tests.rb` at the root of the package and check the changes it made. - -### Make sure your patch works for all supported versions of swift - -The CI will do this for you. You can use the docker-compose files included if you wish to check locally. Currently all versions of swift >= 5.4 are supported. For example usage of docker compose see the main [README](./README.md#an-alternative-using-docker-compose) +You can run the Github Actions workflows locally using [act](https://github.com/nektos/act). For detailed steps on how to do this please see [https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally](https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally). ### Make sure your code is performant SwiftNIO has been created to be high performance. The integration tests cover some measures of performance including allocations which should be avoided if possible. For help with allocation problems refer to the guide to [allocation debugging](./docs/debugging-allocations.md) -### Formatting - -Try to keep your lines less than 120 characters long so github can correctly display your changes. - -It is intended SwiftNIO IMAP will use the swift-format tool in the future to bring consistency to code formatting. To follow the discussion on this topic see the swift evolution proposal [SE-250](https://github.com/apple/swift-evolution/blob/main/proposals/0250-swift-style-guide-and-formatter.md) - ### Extensibility Try to make sure your code is robust to future extensions. The public interface is very hard to change after release - please refer to the [SwiftNIO API guidelines](https://github.com/apple/swift-nio/blob/main/docs/public-api.md) diff --git a/NOTICE.txt b/NOTICE.txt index ec9afc9eb..025671074 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -35,12 +35,12 @@ This product contains "Base64.swift", "DecodingError.swift", and "Chromium.swift * https://github.com/swift-extras/swift-extras-base64 * LICENSE (BSD 2-Clause): - * https://github.com/lemire/fastbase64/blob/master/LICENSE + * https://github.com/lemire/fastbase64/blob/63ed168854875c065bad6c1e6023fbc71f4211df/LICENSE * HOMEPAGE: * https://github.com/lemire/fastbase64 * LICENSE (The MIT License): - * https://github.com/client9/stringencoders/blob/master/LICENSE + * https://github.com/client9/stringencoders/blob/7755852327c06a17d95a978dcd013d6789323ded/LICENSE * HOMEPAGE: * https://github.com/client9/stringencoders diff --git a/Package.swift b/Package.swift index 194acc082..b9dbee20c 100644 --- a/Package.swift +++ b/Package.swift @@ -6,25 +6,26 @@ import PackageDescription let package = Package( name: "swift-nio-imap", products: [ - .library(name: "NIOIMAP", targets: ["NIOIMAP"]), - ], dependencies: [ + .library(name: "NIOIMAP", targets: ["NIOIMAP"]) + ], + dependencies: [ .package(url: "https://github.com/apple/swift-nio", from: "2.64.0"), .package(url: "https://github.com/apple/swift-nio-ssl", from: "2.24.0"), .package(url: "https://github.com/apple/swift-log", from: "1.4.4"), .package(url: "https://github.com/apple/swift-se0270-range-set.git", from: "1.0.1"), - .package(url: "https://github.com/apple/swift-collections.git", "1.1.0" ..< "2.0.0"), + .package(url: "https://github.com/apple/swift-collections.git", "1.1.0"..<"2.0.0"), ], targets: [ .executableTarget( name: "NIOIMAPPerformanceTester", dependencies: [ - "NIOIMAP", + "NIOIMAP" ] ), .target( name: "NIOIMAP", dependencies: [ - "NIOIMAPCore", + "NIOIMAPCore" ] ), .testTarget( @@ -57,7 +58,7 @@ let package = Package( .executableTarget( name: "CLI", dependencies: [ - "CLILib", + "CLILib" ] ), .target( diff --git a/Sources/CLILib/Handlers/Inbound/ResponseRoundtripHandler.swift b/Sources/CLILib/Handlers/Inbound/ResponseRoundtripHandler.swift index 98e2bc69b..1dda500e4 100644 --- a/Sources/CLILib/Handlers/Inbound/ResponseRoundtripHandler.swift +++ b/Sources/CLILib/Handlers/Inbound/ResponseRoundtripHandler.swift @@ -22,7 +22,7 @@ public class ResponseRoundtripHandler: ChannelInboundHandler { public typealias InboundIn = ByteBuffer public typealias InboundOut = ByteBuffer -// let processor = NIOSingleStepByteToMessageProcessor(ResponseDecoder()) + // let processor = NIOSingleStepByteToMessageProcessor(ResponseDecoder()) let logger: Logger private var parser = ResponseParser() @@ -33,41 +33,41 @@ public class ResponseRoundtripHandler: ChannelInboundHandler { } public func channelRead(context: ChannelHandlerContext, data: NIOAny) { -// var buffer = self.unwrapInboundIn(data) -// let originalString = String(buffer: buffer) -// -// var responses = [ResponseOrContinuationRequest]() -// do { -// try self.processor.process(buffer: buffer) { (response) in -// responses.append(response) -// } -// } catch { -// self.logger.error("Response parsing error: \(error)") -// self.logger.error("Response: \(originalString)") -// context.fireErrorCaught(error) -// return -// } -// -// buffer.clear() -// var encodeBuffer = ResponseEncodeBuffer(buffer: buffer, capabilities: self.capabilities) -// for response in responses { -// switch response { -// case .response(let response): -// encodeBuffer.writeResponse(response) -// case .continuationRequest(let req): -// encodeBuffer.writeContinuationRequest(req) -// } -// } -// -// let roundtripString = String(buffer: encodeBuffer.bytes) -// if originalString != roundtripString { -// self.logger.warning("Input response vs roundtrip output is different") -// self.logger.warning("Response (original):\n\(originalString)") -// self.logger.warning("Response (roundtrip):\n\(roundtripString)") -// } else { -// self.logger.info("\(originalString)") -// } -// -// context.fireChannelRead(self.wrapInboundOut(buffer)) + // var buffer = self.unwrapInboundIn(data) + // let originalString = String(buffer: buffer) + // + // var responses = [ResponseOrContinuationRequest]() + // do { + // try self.processor.process(buffer: buffer) { (response) in + // responses.append(response) + // } + // } catch { + // self.logger.error("Response parsing error: \(error)") + // self.logger.error("Response: \(originalString)") + // context.fireErrorCaught(error) + // return + // } + // + // buffer.clear() + // var encodeBuffer = ResponseEncodeBuffer(buffer: buffer, capabilities: self.capabilities) + // for response in responses { + // switch response { + // case .response(let response): + // encodeBuffer.writeResponse(response) + // case .continuationRequest(let req): + // encodeBuffer.writeContinuationRequest(req) + // } + // } + // + // let roundtripString = String(buffer: encodeBuffer.bytes) + // if originalString != roundtripString { + // self.logger.warning("Input response vs roundtrip output is different") + // self.logger.warning("Response (original):\n\(originalString)") + // self.logger.warning("Response (roundtrip):\n\(roundtripString)") + // } else { + // self.logger.info("\(originalString)") + // } + // + // context.fireChannelRead(self.wrapInboundOut(buffer)) } } diff --git a/Sources/CLILib/Handlers/Outbound/CommandRoundtripHandler.swift b/Sources/CLILib/Handlers/Outbound/CommandRoundtripHandler.swift index 5c07a23cc..7160a873e 100644 --- a/Sources/CLILib/Handlers/Outbound/CommandRoundtripHandler.swift +++ b/Sources/CLILib/Handlers/Outbound/CommandRoundtripHandler.swift @@ -38,7 +38,9 @@ public class CommandRoundtripHandler: ChannelOutboundHandler { var originalBuffer = self.unwrapOutboundIn(data) do { var originalBufferCopy = originalBuffer - guard let commandStream = try parser.parseCommandStream(buffer: &originalBufferCopy), let command = commandStream.commandPart else { + guard let commandStream = try parser.parseCommandStream(buffer: &originalBufferCopy), + let command = commandStream.commandPart + else { // this is fine because the command is input by the user, so *should* be valid throw CommandRoundtripError.incompleteCommand } @@ -53,8 +55,12 @@ public class CommandRoundtripHandler: ChannelOutboundHandler { if originalBuffer != roundtripBuffer { self.logger.warning("Input command vs roundtrip output is different") - self.logger.warning("Command (original):\n\(originalBuffer.readString(length: originalBuffer.readableBytes)!)") - self.logger.warning("Command (roundtrip):\n\(roundtripBuffer.readString(length: roundtripBuffer.readableBytes)!)") + self.logger.warning( + "Command (original):\n\(originalBuffer.readString(length: originalBuffer.readableBytes)!)" + ) + self.logger.warning( + "Command (roundtrip):\n\(roundtripBuffer.readString(length: roundtripBuffer.readableBytes)!)" + ) } context.write(data, promise: promise) diff --git a/Sources/NIOIMAP/Client/AppendStateMachine.swift b/Sources/NIOIMAP/Client/AppendStateMachine.swift index ec882451b..90a5eedd0 100644 --- a/Sources/NIOIMAP/Client/AppendStateMachine.swift +++ b/Sources/NIOIMAP/Client/AppendStateMachine.swift @@ -57,7 +57,8 @@ extension ClientStateMachine { var hasCatenatedAtLeastOneObject: Bool { switch self.state { - case .started, .waitingForAppendContinuationRequest, .sendingMessageBytes, .waitingForCatenateContinuationRequest, .sendingCatenateBytes, .waitingForTaggedResponse, .finished: + case .started, .waitingForAppendContinuationRequest, .sendingMessageBytes, + .waitingForCatenateContinuationRequest, .sendingCatenateBytes, .waitingForTaggedResponse, .finished: return false case .catenating(sentFirstObject: let sentFirstObject): return sentFirstObject @@ -69,7 +70,7 @@ extension ClientStateMachine { mutating func receiveResponse(_ response: Response) throws -> Bool { switch self.state { case .started, .waitingForAppendContinuationRequest, .sendingMessageBytes, .catenating, - .waitingForCatenateContinuationRequest, .sendingCatenateBytes, .finished: + .waitingForCatenateContinuationRequest, .sendingCatenateBytes, .finished: throw UnexpectedContinuationRequest() case .waitingForTaggedResponse: break @@ -87,7 +88,7 @@ extension ClientStateMachine { mutating func receiveContinuationRequest(_: ContinuationRequest) throws { switch self.state { case .started, .sendingMessageBytes, .catenating, - .sendingCatenateBytes, .finished, .waitingForTaggedResponse: + .sendingCatenateBytes, .finished, .waitingForTaggedResponse: throw UnexpectedContinuationRequest() case .waitingForAppendContinuationRequest: self.state = .sendingMessageBytes @@ -108,7 +109,8 @@ extension ClientStateMachine { } switch self.state { - case .waitingForTaggedResponse, .waitingForAppendContinuationRequest, .waitingForCatenateContinuationRequest, .finished: + case .waitingForTaggedResponse, .waitingForAppendContinuationRequest, + .waitingForCatenateContinuationRequest, .finished: preconditionFailure("Invalid state: \(self.state)") case .started: return self.sendCommand_startedState(appendCommand) @@ -157,14 +159,15 @@ extension ClientStateMachine.Append { case .endMessage: self.state = .started(canFinish: true) case .messageBytes: - self.state = .sendingMessageBytes // continue sending bytes until we're told to stop + self.state = .sendingMessageBytes // continue sending bytes until we're told to stop } } /// `true` if a continuation is required, otherwise `false` private mutating func sendCommand_catenatingState(_ command: AppendCommand) -> Bool { switch command { - case .start, .beginMessage, .beginCatenate, .messageBytes, .endMessage, .finish, .catenateData(.bytes), .catenateData(.end): + case .start, .beginMessage, .beginCatenate, .messageBytes, .endMessage, .finish, .catenateData(.bytes), + .catenateData(.end): preconditionFailure("Invalid command for state: \(self.state)") case .catenateURL: self.state = .catenating(sentFirstObject: true) @@ -181,12 +184,13 @@ extension ClientStateMachine.Append { /// `true` if a continuation is required, otherwise `false` private mutating func sendCommand_sendingCatenateBytesState(_ command: AppendCommand) { switch command { - case .start, .beginMessage, .beginCatenate, .endMessage, .catenateURL, .messageBytes, .finish, .endCatenate, .catenateData(.begin): + case .start, .beginMessage, .beginCatenate, .endMessage, .catenateURL, .messageBytes, .finish, .endCatenate, + .catenateData(.begin): preconditionFailure("Invalid command for state: \(self.state)") case .catenateData(.end): self.state = .catenating(sentFirstObject: true) case .catenateData(.bytes): - self.state = .sendingCatenateBytes // continue sending bytes until we're told to stop + self.state = .sendingCatenateBytes // continue sending bytes until we're told to stop } } } diff --git a/Sources/NIOIMAP/Client/FramingParser.swift b/Sources/NIOIMAP/Client/FramingParser.swift index 6abd11dfe..b1db96c5e 100644 --- a/Sources/NIOIMAP/Client/FramingParser.swift +++ b/Sources/NIOIMAP/Client/FramingParser.swift @@ -150,7 +150,9 @@ public struct FramingParser: Hashable, Sendable { /// - returns: An array of frames. /// - throws: `InvalidFrame` if a frame was found to be unparsable. /// - throws: `LiteralSizeParsingError` if when parsing a literal header we found an invalid size field. - @_spi(NIOIMAPInternal) public mutating func appendAndFrameBuffer(_ buffer: inout ByteBuffer) throws -> [FramingResult] { + @_spi(NIOIMAPInternal) public mutating func appendAndFrameBuffer( + _ buffer: inout ByteBuffer + ) throws -> [FramingResult] { // fast paths should be fast guard buffer.readableBytes > 0 else { return [] @@ -160,7 +162,9 @@ public struct FramingParser: Hashable, Sendable { return try self.adjustBufferAndParseFrames() } - @_spi(NIOIMAPInternal) public mutating func appendAndFrameBytes(_ bytes: UnsafeRawBufferPointer) throws -> [FramingResult] { + @_spi(NIOIMAPInternal) public mutating func appendAndFrameBytes( + _ bytes: UnsafeRawBufferPointer + ) throws -> [FramingResult] { // fast paths should be fast guard !bytes.isEmpty else { return [] @@ -247,12 +251,11 @@ public struct FramingParser: Hashable, Sendable { case .insideLiteral(lineFeedStrategy: let lfs, remaining: let remaining): self.readByte_state_insideLiteral(lineFeedStrategy: lfs, remainingLiteralBytes: remaining) - if self.frameLength > 0 { - let buffer = self.readFrame() - return .insideLiteral(buffer, remainingBytes: remaining - UInt64(buffer.readableBytes)) - } else { + guard self.frameLength > 0 else { return .incomplete(self.generateIncompleteFramingResult()) } + let buffer = self.readFrame() + return .insideLiteral(buffer, remainingBytes: remaining - UInt64(buffer.readableBytes)) } } return .incomplete(self.generateIncompleteFramingResult()) @@ -276,27 +279,27 @@ public struct FramingParser: Hashable, Sendable { private func generateIncompleteFramingResult() -> Int { switch self.state { case .normalTraversal: - return 2 // we need the CRLF (not strictly _need_ as we're lenient) + return 2 // we need the CRLF (not strictly _need_ as we're lenient) case .insideQuoted: - return 3 // we need the last quote + CRLF + return 3 // we need the last quote + CRLF case .foundCR: return 0 case .searchingForLiteralHeader(let substate): switch substate { case .findingBinaryFlag: - return 4 // + } + CRLF + return 4 // + } + CRLF case .findingSize(let buffer): // if we have some size bytes already we don't strictly _need_ more return buffer.readableBytes > 0 ? 3 : 4 case .findingLiteralExtension: - return 3 // } + CRLF + return 3 // } + CRLF case .findingClosingCurly: - return 3 // } + CRLF + return 3 // } + CRLF case .findingCR: - return 2 // CRLF + return 2 // CRLF } case .insideLiteral: - return 1 // always forward on bytes instantly + return 1 } } @@ -306,7 +309,8 @@ public struct FramingParser: Hashable, Sendable { defer { self.frameLength &+= 1 } - return self.buffer.getInteger(at: self.buffer.readerIndex + self.frameLength)! // we've asserted this is ok + // we've asserted this is ok + return self.buffer.getInteger(at: self.buffer.readerIndex + self.frameLength)! } private mutating func maybeReadByte() -> UInt8? { @@ -329,7 +333,8 @@ public struct FramingParser: Hashable, Sendable { guard self.frameLength < self.buffer.readableBytes else { return nil } - return self.buffer.getInteger(at: self.buffer.readerIndex + self.frameLength, as: UInt8.self)! // we've asserted this is ok + // we've asserted this is ok + return self.buffer.getInteger(at: self.buffer.readerIndex + self.frameLength, as: UInt8.self)! } } @@ -445,7 +450,10 @@ extension FramingParser { return try self.readByte_state_searchingForLiteralHeader_findingSize(sizeBuffer: sizeBuffer) } - private mutating func readByte_state_insideLiteral(lineFeedStrategy: LineFeedByteStrategy, remainingLiteralBytes: UInt64) { + private mutating func readByte_state_insideLiteral( + lineFeedStrategy: LineFeedByteStrategy, + remainingLiteralBytes: UInt64 + ) { switch lineFeedStrategy { case .ignoreFirst: if let byte = self.peekByte(), byte == LF { @@ -461,17 +469,22 @@ extension FramingParser { self.state = .normalTraversal(.includeInFrame) } else { self.frameLength &+= bytesAvailable - self.state = .insideLiteral(lineFeedStrategy: .includeInFrame, remaining: remainingLiteralBytes - UInt64(bytesAvailable)) + self.state = .insideLiteral( + lineFeedStrategy: .includeInFrame, + remaining: remainingLiteralBytes - UInt64(bytesAvailable) + ) } } - private mutating func readByte_state_searchingForLiteralHeader_findingSize(sizeBuffer: ByteBuffer) throws -> FrameStatus { + private mutating func readByte_state_searchingForLiteralHeader_findingSize( + sizeBuffer: ByteBuffer + ) throws -> FrameStatus { var sizeBuffer = sizeBuffer // First scan for the end of the literal size while let byte = self.maybeReadByte() { switch byte { - case DIGIT_0 ... DIGIT_9: + case DIGIT_0...DIGIT_9: sizeBuffer.writeInteger(byte) guard sizeBuffer.readableBytes <= UInt64.maximumAllowedCharacters else { throw LiteralSizeParsingError(buffer: sizeBuffer) @@ -497,7 +510,9 @@ extension FramingParser { return .continueParsing } - private mutating func readByte_state_searchingForLiteralHeader_findingLiteralExtension(_ size: UInt64) throws -> FrameStatus { + private mutating func readByte_state_searchingForLiteralHeader_findingLiteralExtension( + _ size: UInt64 + ) throws -> FrameStatus { guard let byte = self.maybeReadByte() else { return .continueParsing } @@ -514,17 +529,18 @@ extension FramingParser { } } - private mutating func readByte_state_searchingForLiteralHeader_findingClosingCurly(_ size: UInt64) throws -> FrameStatus { + private mutating func readByte_state_searchingForLiteralHeader_findingClosingCurly( + _ size: UInt64 + ) throws -> FrameStatus { guard let byte = self.maybeReadByte() else { return .continueParsing } - if byte == LITERAL_HEADER_END { - self.state = .searchingForLiteralHeader(.findingCR(size)) - return try self.readByte_state_searchingForLiteralHeader_findingCR(size) - } else { + guard byte == LITERAL_HEADER_END else { throw InvalidFrame() } + self.state = .searchingForLiteralHeader(.findingCR(size)) + return try self.readByte_state_searchingForLiteralHeader_findingCR(size) } private mutating func readByte_state_searchingForLiteralHeader_findingCR(_ size: UInt64) throws -> FrameStatus { diff --git a/Sources/NIOIMAP/Coders/ClientStateMachine.swift b/Sources/NIOIMAP/Coders/ClientStateMachine.swift index 6a8f33fac..39c23ed26 100644 --- a/Sources/NIOIMAP/Coders/ClientStateMachine.swift +++ b/Sources/NIOIMAP/Coders/ClientStateMachine.swift @@ -57,7 +57,8 @@ struct OutgoingChunk: Equatable { var shouldSucceedPromise: Bool static func == (lhs: OutgoingChunk, rhs: OutgoingChunk) -> Bool { - lhs.shouldSucceedPromise == rhs.shouldSucceedPromise && lhs.bytes == rhs.bytes && lhs.promise?.futureResult === rhs.promise?.futureResult + lhs.shouldSucceedPromise == rhs.shouldSucceedPromise && lhs.bytes == rhs.bytes + && lhs.promise?.futureResult === rhs.promise?.futureResult } } @@ -91,7 +92,10 @@ struct ClientStateMachine { return .init(bytes: chunk.bytes, promise: promise, shouldSucceedPromise: !chunk.waitForContinuation) } - static func == (lhs: ClientStateMachine.ActiveEncodeContext, rhs: ClientStateMachine.ActiveEncodeContext) -> Bool { + static func == ( + lhs: ClientStateMachine.ActiveEncodeContext, + rhs: ClientStateMachine.ActiveEncodeContext + ) -> Bool { lhs.buffer == rhs.buffer && lhs.promise?.futureResult == rhs.promise?.futureResult } } @@ -138,7 +142,9 @@ struct ClientStateMachine { // We mark where we should write up to at the next opportunity // using the `flush` method called from the channel handler. - private var queuedCommands: MarkedCircularBuffer<(CommandStreamPart, EventLoopPromise?)> = .init(initialCapacity: 16) + private var queuedCommands: MarkedCircularBuffer<(CommandStreamPart, EventLoopPromise?)> = .init( + initialCapacity: 16 + ) init(encodingOptions: CommandEncodingOptions) { self.encodingOptions = encodingOptions @@ -231,7 +237,10 @@ struct ClientStateMachine { /// Tells the state machine that the client would like to send a command. /// We then return any chunks that can be written. - mutating func sendCommand(_ command: CommandStreamPart, promise: EventLoopPromise? = nil) throws -> OutgoingChunk? { + mutating func sendCommand( + _ command: CommandStreamPart, + promise: EventLoopPromise? = nil + ) throws -> OutgoingChunk? { precondition(self.state != .error, "Already in error state, make sure to handle appropriately") if let tag = command.tag { @@ -288,8 +297,11 @@ struct ClientStateMachine { // MARK: - Receive extension ClientStateMachine { - private mutating func receiveContinuationRequest_appending(request: ContinuationRequest) throws -> ContinuationRequestAction { - guard case .appending(var appendingStateMachine, pendingContinuation: let pendingContinuation) = self.state else { + private mutating func receiveContinuationRequest_appending( + request: ContinuationRequest + ) throws -> ContinuationRequestAction { + guard case .appending(var appendingStateMachine, pendingContinuation: let pendingContinuation) = self.state + else { preconditionFailure("Invalid state: \(self.state)") } guard pendingContinuation else { @@ -313,7 +325,7 @@ extension ClientStateMachine { // safe to bang as if we've successfully received a // continuation request then MUST be something to send - if let nextContext = result.nextContext { // we've found another continuation + if let nextContext = result.nextContext { // we've found another continuation self.state = .expectingLiteralContinuationRequest(nextContext) } else { self.state = .expectingNormalResponse @@ -321,7 +333,9 @@ extension ClientStateMachine { return .sendChunks(result.chunks) } - private mutating func receiveContinuationRequest_authenticating(request: ContinuationRequest) throws -> ContinuationRequestAction { + private mutating func receiveContinuationRequest_authenticating( + request: ContinuationRequest + ) throws -> ContinuationRequestAction { guard case .authenticating(var authenticatingStateMachine) = self.state else { preconditionFailure("Invalid state: \(self.state)") } @@ -338,7 +352,9 @@ extension ClientStateMachine { return .fireAuthenticationChallenge } - private mutating func receiveContinuationRequest_idle(request: ContinuationRequest) throws -> ContinuationRequestAction { + private mutating func receiveContinuationRequest_idle( + request: ContinuationRequest + ) throws -> ContinuationRequestAction { guard case .idle(var idleStateMachine) = self.state else { preconditionFailure("Invalid state: \(self.state)") } @@ -353,7 +369,10 @@ extension ClientStateMachine { // MARK: - Send extension ClientStateMachine { - private mutating func sendTaggedCommand(_ command: TaggedCommand, promise: EventLoopPromise?) -> SendableChunks { + private mutating func sendTaggedCommand( + _ command: TaggedCommand, + promise: EventLoopPromise? + ) -> SendableChunks { guard case .expectingNormalResponse = self.state else { preconditionFailure("Invalid state: \(self.state)") } @@ -374,18 +393,20 @@ extension ClientStateMachine { let chunk = context.nextChunk() // if we're meant to succeed the promise then there can't be any next context - if chunk.shouldSucceedPromise { - precondition(context.nextChunk().bytes.readableBytes == 0) - self.state = .expectingNormalResponse - return .init(chunks: [chunk], nextContext: nil) - } else { + guard chunk.shouldSucceedPromise else { self.state = .expectingLiteralContinuationRequest(context) return .init(chunks: [chunk], nextContext: context) } + precondition(context.nextChunk().bytes.readableBytes == 0) + self.state = .expectingNormalResponse + return .init(chunks: [chunk], nextContext: nil) } } - private mutating func sendAppendCommand(_ command: AppendCommand, promise: EventLoopPromise?) -> SendableChunks { + private mutating func sendAppendCommand( + _ command: AppendCommand, + promise: EventLoopPromise? + ) -> SendableChunks { guard case .expectingNormalResponse = self.state else { preconditionFailure("Invalid state: \(self.state)") } @@ -412,12 +433,11 @@ extension ClientStateMachine { var results: [OutgoingChunk] = [] if var currentContext = currentContext { let chunk = currentContext.nextChunk() - if chunk.shouldSucceedPromise { - results.append(chunk) - } else { + guard chunk.shouldSucceedPromise else { self.state = .expectingLiteralContinuationRequest(currentContext) return .init(chunks: [chunk], nextContext: currentContext) } + results.append(chunk) } while self.queuedCommands.hasMark, let sendableChunk = self.sendNextCommand() { @@ -483,7 +503,10 @@ extension ClientStateMachine { /// command we want to send may require a continuation itself. We begin by writing the command to /// an encode buffer to isolate any required continuations, and then send every chunk until we run out of chunks /// to send, or we find a chunk that requires a continuation request. - private mutating func sendNextCommand_expectingNormalResponse(command: CommandStreamPart, promise: EventLoopPromise?) -> SendableChunks { + private mutating func sendNextCommand_expectingNormalResponse( + command: CommandStreamPart, + promise: EventLoopPromise? + ) -> SendableChunks { guard case .expectingNormalResponse = self.state else { preconditionFailure("Invalid state: \(self.state)") } @@ -501,7 +524,10 @@ extension ClientStateMachine { /// When idle we need to first defer to the idle state machine to make sure we can send the /// the next part of the authentication. If we can, then just send the message. There's no need /// to wait for a continuation request. - private mutating func sendNextCommand_idle(command: CommandStreamPart, promise: EventLoopPromise?) -> SendableChunks { + private mutating func sendNextCommand_idle( + command: CommandStreamPart, + promise: EventLoopPromise? + ) -> SendableChunks { guard case .idle(var idleStateMachine) = self.state else { preconditionFailure("Invalid state: \(self.state)") } @@ -514,13 +540,19 @@ extension ClientStateMachine { var encodeBuffer = self.makeEncodeBuffer(command) let chunk = encodeBuffer.buffer.nextChunk() precondition(!chunk.waitForContinuation) - return .init(chunks: [.init(bytes: chunk.bytes, promise: promise, shouldSucceedPromise: true)], nextContext: nil) + return .init( + chunks: [.init(bytes: chunk.bytes, promise: promise, shouldSucceedPromise: true)], + nextContext: nil + ) } /// When authenticating we need to first defer to the authentication state machine to make sure /// can send the next part of the authentication. If we can, then just send the message. There's /// no need to wait for a continuation request. - private mutating func sendNextCommand_authenticating(command: CommandStreamPart, promise: EventLoopPromise?) -> SendableChunks { + private mutating func sendNextCommand_authenticating( + command: CommandStreamPart, + promise: EventLoopPromise? + ) -> SendableChunks { guard case .authenticating(var authenticatingStateMachine) = self.state else { preconditionFailure("Invalid state: \(self.state)") } @@ -533,15 +565,22 @@ extension ClientStateMachine { var encodeBuffer = self.makeEncodeBuffer(.continuationResponse(data)) let chunk = encodeBuffer.buffer.nextChunk() precondition(!chunk.waitForContinuation) - return .init(chunks: [.init(bytes: chunk.bytes, promise: promise, shouldSucceedPromise: true)], nextContext: nil) + return .init( + chunks: [.init(bytes: chunk.bytes, promise: promise, shouldSucceedPromise: true)], + nextContext: nil + ) } /// When appending we need to first defer to the appending state machine to see if we can actually /// send a command given our current state. If we can then we need to check what kind of command /// is being sent. If we're beginning an append or catenation then we need to wait for a continuation /// request, otherwise we can send the command and continue. - private mutating func sendNextCommand_appending(command: CommandStreamPart, promise: EventLoopPromise?) -> SendableChunks? { - guard case .appending(var appendingStateMachine, pendingContinuation: let pendingContinuation) = self.state else { + private mutating func sendNextCommand_appending( + command: CommandStreamPart, + promise: EventLoopPromise? + ) -> SendableChunks? { + guard case .appending(var appendingStateMachine, pendingContinuation: let pendingContinuation) = self.state + else { preconditionFailure("Invalid state: \(self.state)") } guard case .append(let command) = command else { @@ -568,6 +607,9 @@ extension ClientStateMachine { // We always need append commands to be sent instantly so we can receive continuations // so the write promise should always be succeeded. - return .init(chunks: [.init(bytes: chunk.bytes, promise: promise, shouldSucceedPromise: true)], nextContext: nil) + return .init( + chunks: [.init(bytes: chunk.bytes, promise: promise, shouldSucceedPromise: true)], + nextContext: nil + ) } } diff --git a/Sources/NIOIMAP/Coders/CommandEncoder.swift b/Sources/NIOIMAP/Coders/CommandEncoder.swift index 98e69bc7b..e9fb98977 100644 --- a/Sources/NIOIMAP/Coders/CommandEncoder.swift +++ b/Sources/NIOIMAP/Coders/CommandEncoder.swift @@ -36,7 +36,11 @@ class CommandEncoder: MessageToByteEncoder { } func encode(data: CommandStreamPart, out: inout ByteBuffer) { - var encodeBuffer = CommandEncodeBuffer(buffer: out, capabilities: self.capabilities, loggingMode: self.loggingMode) + var encodeBuffer = CommandEncodeBuffer( + buffer: out, + capabilities: self.capabilities, + loggingMode: self.loggingMode + ) encodeBuffer.writeCommandStream(data) out = encodeBuffer.buffer.nextChunk().bytes } diff --git a/Sources/NIOIMAP/Coders/IMAPClientHandler.swift b/Sources/NIOIMAP/Coders/IMAPClientHandler.swift index 07e083605..bd6ffb0c7 100644 --- a/Sources/NIOIMAP/Coders/IMAPClientHandler.swift +++ b/Sources/NIOIMAP/Coders/IMAPClientHandler.swift @@ -42,9 +42,17 @@ public final class IMAPClientHandler: ChannelDuplexHandler { /// capabilities. var encodingChangeCallback: (OrderedDictionary, inout CommandEncodingOptions) -> Void - public init(encodingChangeCallback: @escaping (OrderedDictionary, inout CommandEncodingOptions) -> Void = { _, _ in }) { + public init( + encodingChangeCallback: @escaping (OrderedDictionary, inout CommandEncodingOptions) -> Void = { + _, + _ in + } + ) { self.state = .init(encodingOptions: CommandEncodingOptions(capabilities: self.lastKnownCapabilities)) - self.decoder = NIOSingleStepByteToMessageProcessor(ResponseDecoder(), maximumBufferSize: IMAPDefaults.lineLengthLimit) + self.decoder = NIOSingleStepByteToMessageProcessor( + ResponseDecoder(), + maximumBufferSize: IMAPDefaults.lineLengthLimit + ) self.encodingChangeCallback = encodingChangeCallback self.lastKnownCapabilities = [] } @@ -74,7 +82,10 @@ public final class IMAPClientHandler: ChannelDuplexHandler { context.fireChannelActive() } - private func handleResponseOrContinuationRequest(_ response: ResponseOrContinuationRequest, context: ChannelHandlerContext) throws { + private func handleResponseOrContinuationRequest( + _ response: ResponseOrContinuationRequest, + context: ChannelHandlerContext + ) throws { switch response { case .continuationRequest(let continuationRequest): let action = try self.state.receiveContinuationRequest(continuationRequest) @@ -95,7 +106,11 @@ public final class IMAPClientHandler: ChannelDuplexHandler { } } - private func handleContinuationRequestAction(_ action: ClientStateMachine.ContinuationRequestAction, request: ContinuationRequest, context: ChannelHandlerContext) { + private func handleContinuationRequestAction( + _ action: ClientStateMachine.ContinuationRequestAction, + request: ContinuationRequest, + context: ChannelHandlerContext + ) { switch action { case .sendChunks(let chunks): self.writeChunks(chunks, context: context) @@ -107,7 +122,9 @@ public final class IMAPClientHandler: ChannelDuplexHandler { switch request { case .responseText: // No valid base64, so forward on an empty BB - context.fireChannelRead(self.wrapInboundOut(.authenticationChallenge(context.channel.allocator.buffer(capacity: 0)))) + context.fireChannelRead( + self.wrapInboundOut(.authenticationChallenge(context.channel.allocator.buffer(capacity: 0))) + ) case .data(let byteBuffer): context.fireChannelRead(self.wrapInboundOut(.authenticationChallenge(byteBuffer))) } @@ -131,7 +148,7 @@ public final class IMAPClientHandler: ChannelDuplexHandler { chunks.forEach { chunk in self.writeChunk(chunk, context: context) } - context.flush() // we wouldn't reach this point if we hadn't already flushed + context.flush() // we wouldn't reach this point if we hadn't already flushed } private func writeChunk(_ chunk: OutgoingChunk, context: ChannelHandlerContext) { diff --git a/Sources/NIOIMAP/Coders/IMAPServerHandler.swift b/Sources/NIOIMAP/Coders/IMAPServerHandler.swift index f1986538f..33659b313 100644 --- a/Sources/NIOIMAP/Coders/IMAPServerHandler.swift +++ b/Sources/NIOIMAP/Coders/IMAPServerHandler.swift @@ -55,12 +55,22 @@ public final class IMAPServerHandler: ChannelDuplexHandler { private var numberOfOutstandingContinuationRequests = 0 private var continuationRequestBytes: ByteBuffer - private var responseEncodeBuffer = ResponseEncodeBuffer(buffer: ByteBuffer(string: ""), options: .init(), loggingMode: false) + private var responseEncodeBuffer = ResponseEncodeBuffer( + buffer: ByteBuffer(string: ""), + options: .init(), + loggingMode: false + ) public var capabilities: [Capability] = [] - public init(continuationRequest: ContinuationRequest = .responseText(ResponseText(text: "OK")), literalSizeLimit: Int = IMAPDefaults.literalSizeLimit) { - self.decoder = NIOSingleStepByteToMessageProcessor(CommandDecoder(literalSizeLimit: literalSizeLimit), maximumBufferSize: IMAPDefaults.lineLengthLimit) + public init( + continuationRequest: ContinuationRequest = .responseText(ResponseText(text: "OK")), + literalSizeLimit: Int = IMAPDefaults.literalSizeLimit + ) { + self.decoder = NIOSingleStepByteToMessageProcessor( + CommandDecoder(literalSizeLimit: literalSizeLimit), + maximumBufferSize: IMAPDefaults.lineLengthLimit + ) self._continuationRequest = continuationRequest let buffer = ByteBufferAllocator().buffer(capacity: 16) var encodeBuffer = ResponseEncodeBuffer(buffer: buffer, capabilities: self.capabilities, loggingMode: false) @@ -77,7 +87,7 @@ public final class IMAPServerHandler: ChannelDuplexHandler { return } - for _ in 0 ..< outstanding { + for _ in 0..(base64Encoding bytes: Buffer, options: Base64.EncodingOptions = []) - where Buffer.Element == UInt8 - { + where Buffer.Element == UInt8 { self = Base64.encodeString(bytes: bytes, options: options) } diff --git a/Sources/NIOIMAPCore/Base64/Chromium.swift b/Sources/NIOIMAPCore/Base64/Chromium.swift index 7aee8db31..1b8089eeb 100644 --- a/Sources/NIOIMAPCore/Base64/Chromium.swift +++ b/Sources/NIOIMAPCore/Base64/Chromium.swift @@ -1,8 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// This base64 implementation is heavily inspired by: +// swift-format-ignore: NoBlockComments +// https://github.com/lemire/fastbase64/blob/master/src/chromiumbase64.c ignore-unacceptable-language + +/* + Copyright (c) 2015-2016, Wojciech Muła, Alfred Klomp, Daniel Lemire + (Unless otherwise stated in the source code) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// https://github.com/client9/stringencoders/blob/master/src/modp_b64.c ignore-unacceptable-language + +/* + The MIT License (MIT) + + Copyright (c) 2016 Nick Galbreath + + 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. + */ + // https://github.com/swift-extras/swift-extras-base64 // minor modifications to remove public attributes -// https://github.com/lemire/fastbase64/blob/master/src/chromiumbase64.c +// https://github.com/lemire/fastbase64/blob/1bdf8fdd8d1182a7c5c29ec24ea38aa46da11f64/src/chromiumbase64.c // MARK: - Encoding - @@ -12,126 +85,233 @@ extension Base64 { @usableFromInline static let encoding0: [UInt8] = [ - UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "C"), - UInt8(ascii: "C"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), - UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "H"), - UInt8(ascii: "H"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), - UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "M"), - UInt8(ascii: "M"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), - UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "R"), - UInt8(ascii: "R"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), - UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "W"), - UInt8(ascii: "W"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), - UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "b"), - UInt8(ascii: "b"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), - UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "g"), - UInt8(ascii: "g"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), - UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "l"), - UInt8(ascii: "l"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), - UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "q"), - UInt8(ascii: "q"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), - UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "v"), - UInt8(ascii: "v"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), - UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "0"), - UInt8(ascii: "0"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), - UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "5"), - UInt8(ascii: "5"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), - UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "+"), - UInt8(ascii: "+"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "/"), UInt8(ascii: "/"), UInt8(ascii: "/"), + UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "B"), + UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "C"), + UInt8(ascii: "C"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), + UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), + UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "G"), + UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "H"), + UInt8(ascii: "H"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), + UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "L"), + UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "M"), + UInt8(ascii: "M"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), + UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), + UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "Q"), + UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "R"), + UInt8(ascii: "R"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), + UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "V"), + UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "W"), + UInt8(ascii: "W"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), + UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), + UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "a"), + UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "b"), + UInt8(ascii: "b"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "f"), + UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "g"), + UInt8(ascii: "g"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), + UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), + UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "k"), + UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "l"), + UInt8(ascii: "l"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), + UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "p"), + UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "q"), + UInt8(ascii: "q"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), + UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), + UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "u"), + UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "v"), + UInt8(ascii: "v"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), + UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "z"), + UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "0"), + UInt8(ascii: "0"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), + UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), + UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "4"), + UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "5"), + UInt8(ascii: "5"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), + UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "9"), + UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "+"), + UInt8(ascii: "+"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "/"), UInt8(ascii: "/"), + UInt8(ascii: "/"), ] @usableFromInline static let encoding1: [UInt8] = [ - UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), - UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), - UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), - UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), - UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), - UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), - UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), - UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), - UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), - UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), - UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), - UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), - UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), - UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), - UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), - UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), - UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), - UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), - UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), - UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), - UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), - UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), - UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), - UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), - UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), - UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), + UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), + UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), + UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), + UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), + UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), + UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), + UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), + UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), + UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), + UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), + UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), + UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), + UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), + UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), + UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), + UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), + UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), + UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), + UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), + UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), + UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), + UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), + UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), + UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), + UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), + UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), + UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), + UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), + UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), + UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), + UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), + UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), + UInt8(ascii: "+"), UInt8(ascii: "/"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), + UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), + UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), + UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), + UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), + UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), + UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), + UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), + UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), + UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), + UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), + UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), + UInt8(ascii: "/"), ] @usableFromInline static let encoding0url: [UInt8] = [ - UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "C"), - UInt8(ascii: "C"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), - UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "H"), - UInt8(ascii: "H"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), - UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "M"), - UInt8(ascii: "M"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), - UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "R"), - UInt8(ascii: "R"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), - UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "W"), - UInt8(ascii: "W"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), - UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "b"), - UInt8(ascii: "b"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), - UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "g"), - UInt8(ascii: "g"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), - UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "l"), - UInt8(ascii: "l"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), - UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "q"), - UInt8(ascii: "q"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), - UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "v"), - UInt8(ascii: "v"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), - UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "0"), - UInt8(ascii: "0"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), - UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "5"), - UInt8(ascii: "5"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), - UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "-"), - UInt8(ascii: "-"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "_"), UInt8(ascii: "_"), UInt8(ascii: "_"), + UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "A"), UInt8(ascii: "B"), + UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "C"), + UInt8(ascii: "C"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "D"), UInt8(ascii: "D"), + UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), UInt8(ascii: "E"), + UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "F"), UInt8(ascii: "G"), + UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "H"), + UInt8(ascii: "H"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "I"), UInt8(ascii: "I"), + UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "K"), UInt8(ascii: "L"), + UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "M"), + UInt8(ascii: "M"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "N"), UInt8(ascii: "N"), + UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), UInt8(ascii: "O"), + UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "P"), UInt8(ascii: "Q"), + UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "R"), + UInt8(ascii: "R"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "S"), UInt8(ascii: "S"), + UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "U"), UInt8(ascii: "V"), + UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "W"), + UInt8(ascii: "W"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "X"), UInt8(ascii: "X"), + UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), UInt8(ascii: "Y"), + UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "Z"), UInt8(ascii: "a"), + UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "b"), + UInt8(ascii: "b"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "c"), UInt8(ascii: "c"), + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "e"), UInt8(ascii: "f"), + UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "g"), + UInt8(ascii: "g"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "h"), UInt8(ascii: "h"), + UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), UInt8(ascii: "i"), + UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "j"), UInt8(ascii: "k"), + UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "l"), + UInt8(ascii: "l"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "m"), UInt8(ascii: "m"), + UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "o"), UInt8(ascii: "p"), + UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "q"), + UInt8(ascii: "q"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "r"), UInt8(ascii: "r"), + UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), UInt8(ascii: "s"), + UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "t"), UInt8(ascii: "u"), + UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "v"), + UInt8(ascii: "v"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "w"), UInt8(ascii: "w"), + UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "y"), UInt8(ascii: "z"), + UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "0"), + UInt8(ascii: "0"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "1"), UInt8(ascii: "1"), + UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), UInt8(ascii: "2"), + UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "3"), UInt8(ascii: "4"), + UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "5"), + UInt8(ascii: "5"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "6"), UInt8(ascii: "6"), + UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "8"), UInt8(ascii: "9"), + UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "-"), + UInt8(ascii: "-"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "_"), UInt8(ascii: "_"), + UInt8(ascii: "_"), ] @usableFromInline static let encoding1url: [UInt8] = [ - UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), - UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), - UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), - UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), - UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), - UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), - UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), - UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), - UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), - UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), - UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), - UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), - UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), - UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), - UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), - UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), - UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), - UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), - UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), - UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), - UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), - UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), - UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), - UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), - UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), - UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), + UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), + UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), + UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), + UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), + UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), + UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), + UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), + UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), + UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), + UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), + UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), + UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), + UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), + UInt8(ascii: "G"), UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), + UInt8(ascii: "L"), UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), + UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), + UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), + UInt8(ascii: "a"), UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), + UInt8(ascii: "f"), UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), + UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), + UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), + UInt8(ascii: "u"), UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), + UInt8(ascii: "z"), UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), + UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), + UInt8(ascii: "9"), UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), + UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), + UInt8(ascii: "H"), UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), + UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), + UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), + UInt8(ascii: "W"), UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), + UInt8(ascii: "b"), UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), + UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), + UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), + UInt8(ascii: "q"), UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), + UInt8(ascii: "v"), UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), + UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), + UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), + UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), + UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), + UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), UInt8(ascii: "M"), + UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), UInt8(ascii: "Q"), UInt8(ascii: "R"), + UInt8(ascii: "S"), UInt8(ascii: "T"), UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), + UInt8(ascii: "X"), UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), UInt8(ascii: "g"), + UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), UInt8(ascii: "k"), UInt8(ascii: "l"), + UInt8(ascii: "m"), UInt8(ascii: "n"), UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), + UInt8(ascii: "r"), UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), + UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), UInt8(ascii: "0"), + UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), UInt8(ascii: "4"), UInt8(ascii: "5"), + UInt8(ascii: "6"), UInt8(ascii: "7"), UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "-"), + UInt8(ascii: "_"), ] @inlinable - static func encodeBytes(bytes: Buffer, options: EncodingOptions = []) + static func encodeBytes( + bytes: Buffer, + options: EncodingOptions = [] + ) -> [UInt8] where Buffer.Element == UInt8 { let newCapacity = ((bytes.count + 2) / 3) * 4 @@ -148,27 +328,29 @@ extension Base64 { } @inlinable - static func encodeString(bytes: Buffer, options: EncodingOptions = []) + static func encodeString( + bytes: Buffer, + options: EncodingOptions = [] + ) -> String where Buffer.Element == UInt8 { #if swift(>=5.3) let newCapacity = ((bytes.count + 2) / 3) * 4 - if #available(OSX 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) { - if let result = bytes.withContiguousStorageIfAvailable({ (input) -> String in - String(unsafeUninitializedCapacity: newCapacity) { (buffer) -> Int in - var length = newCapacity - Self._encodeChromium(input: input, buffer: buffer, length: &length, options: options) - return length - } - }) { - return result - } - - return self.encodeString(bytes: Array(bytes), options: options) - } else { + guard #available(OSX 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) else { let bytes: [UInt8] = self.encodeBytes(bytes: bytes, options: options) return String(decoding: bytes, as: Unicode.UTF8.self) } + if let result = bytes.withContiguousStorageIfAvailable({ (input) -> String in + String(unsafeUninitializedCapacity: newCapacity) { (buffer) -> Int in + var length = newCapacity + Self._encodeChromium(input: input, buffer: buffer, length: &length, options: options) + return length + } + }) { + return result + } + + return self.encodeString(bytes: Array(bytes), options: options) #else let bytes: [UInt8] = self.encodeBytes(bytes: bytes, options: options) return String(decoding: bytes, as: Unicode.UTF8.self) @@ -176,7 +358,12 @@ extension Base64 { } @inlinable - static func _encodeChromium(input: UnsafeBufferPointer, buffer: UnsafeMutableBufferPointer, length: inout Int, options: EncodingOptions) { + static func _encodeChromium( + input: UnsafeBufferPointer, + buffer: UnsafeMutableBufferPointer, + length: inout Int, + options: EncodingOptions + ) { let omitPaddingCharacter = options.contains(.omitPaddingCharacter) Self.withUnsafeEncodingTablesAsBufferPointers(options: options) { e0, e1 in @@ -231,7 +418,10 @@ extension Base64 { } @inlinable - static func withUnsafeEncodingTablesAsBufferPointers(options: Base64.EncodingOptions, _ body: (UnsafeBufferPointer, UnsafeBufferPointer) throws -> R) rethrows -> R { + static func withUnsafeEncodingTablesAsBufferPointers( + options: Base64.EncodingOptions, + _ body: (UnsafeBufferPointer, UnsafeBufferPointer) throws -> R + ) rethrows -> R { let encoding0 = options.contains(.base64UrlAlphabet) ? Self.encoding0url : Self.encoding0 let encoding1 = options.contains(.base64UrlAlphabet) ? Self.encoding1url : Self.encoding1 @@ -275,7 +465,8 @@ extension Base64 { } @inlinable - static func decode(bytes: Buffer, options: DecodingOptions = []) throws -> [UInt8] where Buffer.Element == UInt8 { + static func decode(bytes: Buffer, options: DecodingOptions = []) throws -> [UInt8] + where Buffer.Element == UInt8 { // needed to add this to fix a crash guard bytes.count > 0 else { return [] @@ -323,7 +514,7 @@ extension Base64 { try Self.withUnsafeDecodingTablesAsBufferPointers(options: options) { d0, d1, d2, d3 in var outIndex = 0 if fullchunks > 0 { - for chunk in 0 ..< fullchunks { + for chunk in 0..(options: Base64.DecodingOptions, _ body: (UnsafeBufferPointer, UnsafeBufferPointer, UnsafeBufferPointer, UnsafeBufferPointer) throws -> R) rethrows -> R { + static func withUnsafeDecodingTablesAsBufferPointers( + options: Base64.DecodingOptions, + _ body: ( + UnsafeBufferPointer, UnsafeBufferPointer, UnsafeBufferPointer, + UnsafeBufferPointer + ) throws -> R + ) rethrows -> R { let decoding0 = options.contains(.base64UrlAlphabet) ? Self.decoding0url : Self.decoding0 let decoding1 = options.contains(.base64UrlAlphabet) ? Self.decoding1url : Self.decoding1 let decoding2 = options.contains(.base64UrlAlphabet) ? Self.decoding2url : Self.decoding2 @@ -601,22 +798,22 @@ extension Base64 { @usableFromInline static let decoding0url: [UInt32] = [ - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_00F8, 0x01FF_FFFF, 0x01FF_FFFF, // 42 - 0x0000_00D0, 0x0000_00D4, 0x0000_00D8, 0x0000_00DC, 0x0000_00E0, 0x0000_00E4, // 48 - 0x0000_00E8, 0x0000_00EC, 0x0000_00F0, 0x0000_00F4, 0x01FF_FFFF, 0x01FF_FFFF, // 54 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 - 0x0000_0004, 0x0000_0008, 0x0000_000C, 0x0000_0010, 0x0000_0014, 0x0000_0018, // 66 - 0x0000_001C, 0x0000_0020, 0x0000_0024, 0x0000_0028, 0x0000_002C, 0x0000_0030, // 72 - 0x0000_0034, 0x0000_0038, 0x0000_003C, 0x0000_0040, 0x0000_0044, 0x0000_0048, // 78 - 0x0000_004C, 0x0000_0050, 0x0000_0054, 0x0000_0058, 0x0000_005C, 0x0000_0060, // 84 - 0x0000_0064, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_00FC, // 90 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_00F8, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0000_00D0, 0x0000_00D4, 0x0000_00D8, 0x0000_00DC, 0x0000_00E0, 0x0000_00E4, // 48 + 0x0000_00E8, 0x0000_00EC, 0x0000_00F0, 0x0000_00F4, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0000_0004, 0x0000_0008, 0x0000_000C, 0x0000_0010, 0x0000_0014, 0x0000_0018, // 66 + 0x0000_001C, 0x0000_0020, 0x0000_0024, 0x0000_0028, 0x0000_002C, 0x0000_0030, // 72 + 0x0000_0034, 0x0000_0038, 0x0000_003C, 0x0000_0040, 0x0000_0044, 0x0000_0048, // 78 + 0x0000_004C, 0x0000_0050, 0x0000_0054, 0x0000_0058, 0x0000_005C, 0x0000_0060, // 84 + 0x0000_0064, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_00FC, // 90 0x01FF_FFFF, 0x0000_0068, 0x0000_006C, 0x0000_0070, 0x0000_0074, 0x0000_0078, 0x0000_007C, 0x0000_0080, 0x0000_0084, 0x0000_0088, 0x0000_008C, 0x0000_0090, 0x0000_0094, 0x0000_0098, 0x0000_009C, 0x0000_00A0, 0x0000_00A4, 0x0000_00A8, @@ -648,22 +845,22 @@ extension Base64 { @usableFromInline static let decoding1url: [UInt32] = [ - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_E003, 0x01FF_FFFF, 0x01FF_FFFF, // 42 - 0x0000_4003, 0x0000_5003, 0x0000_6003, 0x0000_7003, 0x0000_8003, 0x0000_9003, // 48 - 0x0000_A003, 0x0000_B003, 0x0000_C003, 0x0000_D003, 0x01FF_FFFF, 0x01FF_FFFF, // 54 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 - 0x0000_1000, 0x0000_2000, 0x0000_3000, 0x0000_4000, 0x0000_5000, 0x0000_6000, // 66 - 0x0000_7000, 0x0000_8000, 0x0000_9000, 0x0000_A000, 0x0000_B000, 0x0000_C000, // 72 - 0x0000_D000, 0x0000_E000, 0x0000_F000, 0x0000_0001, 0x0000_1001, 0x0000_2001, // 78 - 0x0000_3001, 0x0000_4001, 0x0000_5001, 0x0000_6001, 0x0000_7001, 0x0000_8001, // 84 - 0x0000_9001, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_F003, // 90 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_E003, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0000_4003, 0x0000_5003, 0x0000_6003, 0x0000_7003, 0x0000_8003, 0x0000_9003, // 48 + 0x0000_A003, 0x0000_B003, 0x0000_C003, 0x0000_D003, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0000_1000, 0x0000_2000, 0x0000_3000, 0x0000_4000, 0x0000_5000, 0x0000_6000, // 66 + 0x0000_7000, 0x0000_8000, 0x0000_9000, 0x0000_A000, 0x0000_B000, 0x0000_C000, // 72 + 0x0000_D000, 0x0000_E000, 0x0000_F000, 0x0000_0001, 0x0000_1001, 0x0000_2001, // 78 + 0x0000_3001, 0x0000_4001, 0x0000_5001, 0x0000_6001, 0x0000_7001, 0x0000_8001, // 84 + 0x0000_9001, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_F003, // 90 0x01FF_FFFF, 0x0000_A001, 0x0000_B001, 0x0000_C001, 0x0000_D001, 0x0000_E001, 0x0000_F001, 0x0000_0002, 0x0000_1002, 0x0000_2002, 0x0000_3002, 0x0000_4002, 0x0000_5002, 0x0000_6002, 0x0000_7002, 0x0000_8002, 0x0000_9002, 0x0000_A002, @@ -695,22 +892,22 @@ extension Base64 { @usableFromInline static let decoding2url: [UInt32] = [ - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0080_0F00, 0x01FF_FFFF, 0x01FF_FFFF, // 42 - 0x0000_0D00, 0x0040_0D00, 0x0080_0D00, 0x00C0_0D00, 0x0000_0E00, 0x0040_0E00, // 48 - 0x0080_0E00, 0x00C0_0E00, 0x0000_0F00, 0x0040_0F00, 0x01FF_FFFF, 0x01FF_FFFF, // 54 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 - 0x0040_0000, 0x0080_0000, 0x00C0_0000, 0x0000_0100, 0x0040_0100, 0x0080_0100, // 66 - 0x00C0_0100, 0x0000_0200, 0x0040_0200, 0x0080_0200, 0x00C0_0200, 0x0000_0300, // 72 - 0x0040_0300, 0x0080_0300, 0x00C0_0300, 0x0000_0400, 0x0040_0400, 0x0080_0400, // 78 - 0x00C0_0400, 0x0000_0500, 0x0040_0500, 0x0080_0500, 0x00C0_0500, 0x0000_0600, // 84 - 0x0040_0600, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x00C0_0F00, // 90 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0080_0F00, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0000_0D00, 0x0040_0D00, 0x0080_0D00, 0x00C0_0D00, 0x0000_0E00, 0x0040_0E00, // 48 + 0x0080_0E00, 0x00C0_0E00, 0x0000_0F00, 0x0040_0F00, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0040_0000, 0x0080_0000, 0x00C0_0000, 0x0000_0100, 0x0040_0100, 0x0080_0100, // 66 + 0x00C0_0100, 0x0000_0200, 0x0040_0200, 0x0080_0200, 0x00C0_0200, 0x0000_0300, // 72 + 0x0040_0300, 0x0080_0300, 0x00C0_0300, 0x0000_0400, 0x0040_0400, 0x0080_0400, // 78 + 0x00C0_0400, 0x0000_0500, 0x0040_0500, 0x0080_0500, 0x00C0_0500, 0x0000_0600, // 84 + 0x0040_0600, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x00C0_0F00, // 90 0x01FF_FFFF, 0x0080_0600, 0x00C0_0600, 0x0000_0700, 0x0040_0700, 0x0080_0700, 0x00C0_0700, 0x0000_0800, 0x0040_0800, 0x0080_0800, 0x00C0_0800, 0x0000_0900, 0x0040_0900, 0x0080_0900, 0x00C0_0900, 0x0000_0A00, 0x0040_0A00, 0x0080_0A00, @@ -742,22 +939,22 @@ extension Base64 { @usableFromInline static let decoding3url: [UInt32] = [ - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x003E_0000, 0x01FF_FFFF, 0x01FF_FFFF, // 42 - 0x0034_0000, 0x0035_0000, 0x0036_0000, 0x0037_0000, 0x0038_0000, 0x0039_0000, // 48 - 0x003A_0000, 0x003B_0000, 0x003C_0000, 0x003D_0000, 0x01FF_FFFF, 0x01FF_FFFF, // 54 - 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 - 0x0001_0000, 0x0002_0000, 0x0003_0000, 0x0004_0000, 0x0005_0000, 0x0006_0000, // 66 - 0x0007_0000, 0x0008_0000, 0x0009_0000, 0x000A_0000, 0x000B_0000, 0x000C_0000, // 72 - 0x000D_0000, 0x000E_0000, 0x000F_0000, 0x0010_0000, 0x0011_0000, 0x0012_0000, // 78 - 0x0013_0000, 0x0014_0000, 0x0015_0000, 0x0016_0000, 0x0017_0000, 0x0018_0000, // 84 - 0x0019_0000, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x003F_0000, // 90 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 18 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 24 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 30 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 36 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x003E_0000, 0x01FF_FFFF, 0x01FF_FFFF, // 42 + 0x0034_0000, 0x0035_0000, 0x0036_0000, 0x0037_0000, 0x0038_0000, 0x0039_0000, // 48 + 0x003A_0000, 0x003B_0000, 0x003C_0000, 0x003D_0000, 0x01FF_FFFF, 0x01FF_FFFF, // 54 + 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x0000_0000, // 60 + 0x0001_0000, 0x0002_0000, 0x0003_0000, 0x0004_0000, 0x0005_0000, 0x0006_0000, // 66 + 0x0007_0000, 0x0008_0000, 0x0009_0000, 0x000A_0000, 0x000B_0000, 0x000C_0000, // 72 + 0x000D_0000, 0x000E_0000, 0x000F_0000, 0x0010_0000, 0x0011_0000, 0x0012_0000, // 78 + 0x0013_0000, 0x0014_0000, 0x0015_0000, 0x0016_0000, 0x0017_0000, 0x0018_0000, // 84 + 0x0019_0000, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x003F_0000, // 90 0x01FF_FFFF, 0x001A_0000, 0x001B_0000, 0x001C_0000, 0x001D_0000, 0x001E_0000, 0x001F_0000, 0x0020_0000, 0x0021_0000, 0x0022_0000, 0x0023_0000, 0x0024_0000, 0x0025_0000, 0x0026_0000, 0x0027_0000, 0x0028_0000, 0x0029_0000, 0x002A_0000, diff --git a/Sources/NIOIMAPCore/Base64/DecodingError.swift b/Sources/NIOIMAPCore/Base64/DecodingError.swift index 89e084085..9830e3591 100644 --- a/Sources/NIOIMAPCore/Base64/DecodingError.swift +++ b/Sources/NIOIMAPCore/Base64/DecodingError.swift @@ -1,3 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// This base64 implementation is heavily inspired by: + +// swift-format-ignore: NoBlockComments +// https://github.com/lemire/fastbase64/blob/master/src/chromiumbase64.c ignore-unacceptable-language + +/* + Copyright (c) 2015-2016, Wojciech Muła, Alfred Klomp, Daniel Lemire + (Unless otherwise stated in the source code) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// https://github.com/client9/stringencoders/blob/master/src/modp_b64.c ignore-unacceptable-language + +/* + The MIT License (MIT) + + Copyright (c) 2016 Nick Galbreath + + 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. + */ + // https://github.com/swift-extras/swift-extras-base64 // minor modifications to remove public attributes diff --git a/Sources/NIOIMAPCore/ByteBuffer+WriteLiteral.swift b/Sources/NIOIMAPCore/ByteBuffer+WriteLiteral.swift index 04b830ce9..0b68ad33c 100644 --- a/Sources/NIOIMAPCore/ByteBuffer+WriteLiteral.swift +++ b/Sources/NIOIMAPCore/ByteBuffer+WriteLiteral.swift @@ -97,11 +97,10 @@ extension EncodeBuffer { return .clientSynchronizingLiteral } case .server(_, options: let options): - if options.useQuotedString, canUseQuotedString(for: bytes) { - return .quotedString - } else { + guard options.useQuotedString, canUseQuotedString(for: bytes) else { return .serverLiteral } + return .quotedString } } @@ -128,9 +127,7 @@ extension EncodeBuffer { @discardableResult mutating func writeLiteral8(_ bytes: T) -> Int where T.Element == UInt8 { let length = "~{\(bytes.count)}\r\n" return - self.writeString(length) + - self.markStopPoint() + - self.writeBytes(bytes) + self.writeString(length) + self.markStopPoint() + self.writeBytes(bytes) } /// Writes the string `"NIL"` to self. @@ -154,29 +151,35 @@ extension EncodeBuffer { /// - parenthesis: Writes `(` immediately before the first element, and `)` immediately after the last. Enabled by default. /// - writer: The closure to call for each element that writes the element. /// - returns: The number of bytes written. - @discardableResult mutating func writeArray(_ collection: C, prefix: String = "", separator: String = " ", suffix: String = "", parenthesis: Bool = true, _ writer: (T, inout EncodeBuffer) -> Int) -> Int where C: RandomAccessCollection, C.Element == T { + @discardableResult mutating func writeArray( + _ collection: C, + prefix: String = "", + separator: String = " ", + suffix: String = "", + parenthesis: Bool = true, + _ writer: (T, inout EncodeBuffer) -> Int + ) -> Int where C: RandomAccessCollection, C.Element == T { // TODO: This should probably check // collection.count != 0 // such that an empty collection gets encoded as "()". self.write(if: collection.count > 0) { self.writeString(prefix) - } + - self.write(if: parenthesis) { () -> Int in + } + + self.write(if: parenthesis) { () -> Int in self.writeString("(") - } + - collection.enumerated().reduce(0) { (size, row) in + } + + collection.enumerated().reduce(0) { (size, row) in let (i, element) = row return - size + - writer(element, &self) + - self.write(if: i < collection.count - 1) { () -> Int in + size + writer(element, &self) + + self.write(if: i < collection.count - 1) { () -> Int in self.writeString(separator) } - } + - self.write(if: parenthesis) { () -> Int in + } + + self.write(if: parenthesis) { () -> Int in self.writeString(")") - } + - self.write(if: collection.count > 0) { + } + + self.write(if: collection.count > 0) { self.writeString(suffix) } } @@ -190,29 +193,35 @@ extension EncodeBuffer { /// - parenthesis: Writes `(` immediately before the first element, and `)` immediately after the last. Enabled by default. /// - writer: The closure to call for each element that writes the element. /// - returns: The number of bytes written. - @discardableResult mutating func writeOrderedDictionary(_ values: OrderedDictionary, prefix: String = "", separator: String = " ", suffix: String = "", parenthesis: Bool = true, _ writer: (KeyValue, inout EncodeBuffer) -> Int) -> Int { + @discardableResult mutating func writeOrderedDictionary( + _ values: OrderedDictionary, + prefix: String = "", + separator: String = " ", + suffix: String = "", + parenthesis: Bool = true, + _ writer: (KeyValue, inout EncodeBuffer) -> Int + ) -> Int { // TODO: This should probably check // collection.count != 0 // such that an empty collection gets encoded as "()". self.write(if: values.count > 0) { self.writeString(prefix) - } + - self.write(if: parenthesis) { () -> Int in + } + + self.write(if: parenthesis) { () -> Int in self.writeString("(") - } + - values.enumerated().reduce(0) { (size, row) in + } + + values.enumerated().reduce(0) { (size, row) in let (i, element) = row return - size + - writer(.init(key: element.0, value: element.1), &self) + - self.write(if: i < values.count - 1) { () -> Int in + size + writer(.init(key: element.0, value: element.1), &self) + + self.write(if: i < values.count - 1) { () -> Int in self.writeString(separator) } - } + - self.write(if: parenthesis) { () -> Int in + } + + self.write(if: parenthesis) { () -> Int in self.writeString(")") - } + - self.write(if: values.count > 0) { + } + + self.write(if: values.count > 0) { self.writeString(suffix) } } diff --git a/Sources/NIOIMAPCore/CommandEncodeBuffer.swift b/Sources/NIOIMAPCore/CommandEncodeBuffer.swift index d9fd2fb55..605330e0d 100644 --- a/Sources/NIOIMAPCore/CommandEncodeBuffer.swift +++ b/Sources/NIOIMAPCore/CommandEncodeBuffer.swift @@ -25,7 +25,12 @@ public struct CommandEncodeBuffer: Hashable, Sendable { /// Creates a new `CommandEncodeBuffer` from a given initial `ByteBuffer` and configuration options. /// - parameter buffer: The initial `ByteBuffer` to build upon. /// - parameter options: The options to use when writing commands and data. - public init(buffer: ByteBuffer, options: CommandEncodingOptions, encodedAtLeastOneCatenateElement: Bool = false, loggingMode: Bool) { + public init( + buffer: ByteBuffer, + options: CommandEncodingOptions, + encodedAtLeastOneCatenateElement: Bool = false, + loggingMode: Bool + ) { self.buffer = .clientEncodeBuffer(buffer: buffer, options: options, loggingMode: loggingMode) self.encodedAtLeastOneCatenateElement = encodedAtLeastOneCatenateElement } @@ -35,7 +40,9 @@ extension CommandEncodeBuffer { /// The options used when writing commands and data. public var options: CommandEncodingOptions { get { - guard case .client(let options) = buffer.mode else { preconditionFailure("Command encoder mode must be 'client'.") } + guard case .client(let options) = buffer.mode else { + preconditionFailure("Command encoder mode must be 'client'.") + } return options } set { @@ -46,7 +53,12 @@ extension CommandEncodeBuffer { /// Creates a new `CommandEncodeBuffer` from a given initial `ByteBuffer` and configuration options. /// - parameter buffer: The initial `ByteBuffer` to build upon. /// - parameter capabilities: Capabilities to use when writing commands and data. Will be converted to `CommandEncodingOptions`. - public init(buffer: ByteBuffer, capabilities: [Capability], encodedAtLeastOneCatenateElement: Bool = false, loggingMode: Bool) { + public init( + buffer: ByteBuffer, + capabilities: [Capability], + encodedAtLeastOneCatenateElement: Bool = false, + loggingMode: Bool + ) { self.buffer = .clientEncodeBuffer(buffer: buffer, capabilities: capabilities, loggingMode: loggingMode) self.encodedAtLeastOneCatenateElement = encodedAtLeastOneCatenateElement } diff --git a/Sources/NIOIMAPCore/EncodeBuffer.swift b/Sources/NIOIMAPCore/EncodeBuffer.swift index ddf92e938..0c2138496 100644 --- a/Sources/NIOIMAPCore/EncodeBuffer.swift +++ b/Sources/NIOIMAPCore/EncodeBuffer.swift @@ -46,7 +46,11 @@ extension EncodeBuffer { /// - parameter buffer: An initial `ByteBuffer` to write to. Note that this is copied and not taken as `inout`. /// - parameter options: Configuration to use when writing. /// - returns: A new `EncodeBuffer` configured for client use. - static func clientEncodeBuffer(buffer: ByteBuffer, options: CommandEncodingOptions, loggingMode: Bool) -> EncodeBuffer { + static func clientEncodeBuffer( + buffer: ByteBuffer, + options: CommandEncodingOptions, + loggingMode: Bool + ) -> EncodeBuffer { EncodeBuffer(buffer, mode: .client(options: options), loggingMode: loggingMode) } @@ -55,14 +59,22 @@ extension EncodeBuffer { /// - parameter options: Configuration to use when writing. /// - returns: A new `EncodeBuffer` configured for client use. static func clientEncodeBuffer(buffer: ByteBuffer, capabilities: [Capability], loggingMode: Bool) -> EncodeBuffer { - clientEncodeBuffer(buffer: buffer, options: CommandEncodingOptions(capabilities: capabilities), loggingMode: loggingMode) + clientEncodeBuffer( + buffer: buffer, + options: CommandEncodingOptions(capabilities: capabilities), + loggingMode: loggingMode + ) } /// Creates a new `EncodeBuffer` suitable for a client to write commands. /// - parameter buffer: An initial `ByteBuffer` to write to. Note that this is copied and not taken as `inout`. /// - parameter options: Configuration to use when writing. /// - returns: A new `EncodeBuffer` configured for server use. - static func serverEncodeBuffer(buffer: ByteBuffer, options: ResponseEncodingOptions, loggingMode: Bool) -> EncodeBuffer { + static func serverEncodeBuffer( + buffer: ByteBuffer, + options: ResponseEncodingOptions, + loggingMode: Bool + ) -> EncodeBuffer { EncodeBuffer(buffer, mode: .server(streamingAttributes: false, options: options), loggingMode: loggingMode) } @@ -71,7 +83,11 @@ extension EncodeBuffer { /// - parameter options: Configuration to use when writing. /// - returns: A new `EncodeBuffer` configured for server use. static func serverEncodeBuffer(buffer: ByteBuffer, capabilities: [Capability], loggingMode: Bool) -> EncodeBuffer { - serverEncodeBuffer(buffer: buffer, options: ResponseEncodingOptions(capabilities: capabilities), loggingMode: loggingMode) + serverEncodeBuffer( + buffer: buffer, + options: ResponseEncodingOptions(capabilities: capabilities), + loggingMode: loggingMode + ) } } @@ -116,13 +132,17 @@ extension EncodeBuffer { @_spi(NIOIMAPInternal) public mutating func nextChunk(allowEmptyChunk: Bool) -> Chunk { switch self.mode { case .client: - if let stopPoint = self.stopPoints.popFirst() { - return .init(bytes: self.buffer.readSlice(length: stopPoint - self.buffer.readerIndex)!, - waitForContinuation: stopPoint != self.buffer.writerIndex) - } else { + guard let stopPoint = self.stopPoints.popFirst() else { precondition(allowEmptyChunk || self.buffer.readableBytes > 0, "No next chunk to send.") - return .init(bytes: self.buffer.readSlice(length: self.buffer.readableBytes)!, waitForContinuation: false) + return .init( + bytes: self.buffer.readSlice(length: self.buffer.readableBytes)!, + waitForContinuation: false + ) } + return .init( + bytes: self.buffer.readSlice(length: stopPoint - self.buffer.readerIndex)!, + waitForContinuation: stopPoint != self.buffer.writerIndex + ) case .server: return .init(bytes: self.buffer.readSlice(length: self.buffer.readableBytes)!, waitForContinuation: false) } @@ -154,11 +174,10 @@ extension EncodeBuffer { @discardableResult @inlinable public mutating func writeBytes(_ bytes: Bytes) -> Int where Bytes.Element == UInt8 { - if loggingMode { - return self.buffer.writeString("[\(Array(bytes).count) bytes]") - } else { + guard loggingMode else { return self.buffer.writeBytes(bytes) } + return self.buffer.writeString("[\(Array(bytes).count) bytes]") } /// Writes a `ByteBuffer` to the buffer. @@ -167,11 +186,10 @@ extension EncodeBuffer { @discardableResult @inlinable public mutating func writeBuffer(_ buffer: inout ByteBuffer) -> Int { - if loggingMode { - return self.buffer.writeString("[\(buffer.readableBytes) bytes]") - } else { + guard loggingMode else { return self.buffer.writeBuffer(&buffer) } + return self.buffer.writeString("[\(buffer.readableBytes) bytes]") } /// Erases all data from the buffer. diff --git a/Sources/NIOIMAPCore/Grammar/AbsoluteMessagePath.swift b/Sources/NIOIMAPCore/Grammar/AbsoluteMessagePath.swift index e872c7527..633fca7d2 100644 --- a/Sources/NIOIMAPCore/Grammar/AbsoluteMessagePath.swift +++ b/Sources/NIOIMAPCore/Grammar/AbsoluteMessagePath.swift @@ -28,8 +28,8 @@ public struct AbsoluteMessagePath: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeAbsoluteMessagePath(_ path: AbsoluteMessagePath) -> Int { - self.writeString("/") + - self.writeIfExists(path.command) { command in + self.writeString("/") + + self.writeIfExists(path.command) { command in self.writeURLCommand(command) } } diff --git a/Sources/NIOIMAPCore/Grammar/Append/AppendData.swift b/Sources/NIOIMAPCore/Grammar/Append/AppendData.swift index 1ade29874..c90926292 100644 --- a/Sources/NIOIMAPCore/Grammar/Append/AppendData.swift +++ b/Sources/NIOIMAPCore/Grammar/Append/AppendData.swift @@ -37,7 +37,9 @@ public struct AppendData: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeAppendData(_ data: AppendData) -> Int { - guard case .client(let options) = mode else { preconditionFailure("Trying to send command, but not in 'client' mode.") } + guard case .client(let options) = mode else { + preconditionFailure("Trying to send command, but not in 'client' mode.") + } switch (options.useNonSynchronizingLiteralPlus, data.withoutContentTransferEncoding) { case (true, true): return self.writeString("~{\(data.byteCount)+}\r\n") diff --git a/Sources/NIOIMAPCore/Grammar/Append/AppendMessage.swift b/Sources/NIOIMAPCore/Grammar/Append/AppendMessage.swift index 937bb8cfc..348fe4f9f 100644 --- a/Sources/NIOIMAPCore/Grammar/Append/AppendMessage.swift +++ b/Sources/NIOIMAPCore/Grammar/Append/AppendMessage.swift @@ -38,8 +38,6 @@ extension EncodeBuffer { /// - parameter `message`: The `AppendMessage` to write. /// - returns: The number of bytes written. @discardableResult mutating func writeAppendMessage(_ message: AppendMessage) -> Int { - self.writeAppendOptions(message.options) + - self.writeSpace() + - self.writeAppendData(message.data) + self.writeAppendOptions(message.options) + self.writeSpace() + self.writeAppendData(message.data) } } diff --git a/Sources/NIOIMAPCore/Grammar/Append/AppendOptions.swift b/Sources/NIOIMAPCore/Grammar/Append/AppendOptions.swift index beac8e52c..fa81cc56a 100644 --- a/Sources/NIOIMAPCore/Grammar/Append/AppendOptions.swift +++ b/Sources/NIOIMAPCore/Grammar/Append/AppendOptions.swift @@ -34,7 +34,11 @@ public struct AppendOptions: Hashable, Sendable { /// - parameter flagList: Flags that will be added to the message. Defaults to `[]`. /// - parameter internalDate: An optional date to be associated with the message, typically representing the date of delivery. Defaults to `nil`. /// - parameter extensions: Any additional pieces of information to be associated with the message. Implemented as a "catch-all" to support future extensions. Defaults to `[:]`. - public init(flagList: [Flag] = [], internalDate: ServerMessageDate? = nil, extensions: OrderedDictionary = [:]) { + public init( + flagList: [Flag] = [], + internalDate: ServerMessageDate? = nil, + extensions: OrderedDictionary = [:] + ) { self.flagList = flagList self.internalDate = internalDate self.extensions = extensions @@ -47,12 +51,11 @@ extension EncodeBuffer { @discardableResult mutating func writeAppendOptions(_ options: AppendOptions) -> Int { self.write(if: options.flagList.count >= 1) { self.writeSpace() + self.writeFlags(options.flagList) - } + - self.writeIfExists(options.internalDate) { (internalDate) -> Int in - self.writeSpace() + - self.writeInternalDate(internalDate) - } + - self.writeOrderedDictionary(options.extensions, prefix: " ", parenthesis: false) { (ext, self) -> Int in + } + + self.writeIfExists(options.internalDate) { (internalDate) -> Int in + self.writeSpace() + self.writeInternalDate(internalDate) + } + + self.writeOrderedDictionary(options.extensions, prefix: " ", parenthesis: false) { (ext, self) -> Int in self.writeTaggedExtension(ext) } } diff --git a/Sources/NIOIMAPCore/Grammar/AuthenticatedURL.swift b/Sources/NIOIMAPCore/Grammar/AuthenticatedURL.swift index b57658b53..c386c6b39 100644 --- a/Sources/NIOIMAPCore/Grammar/AuthenticatedURL.swift +++ b/Sources/NIOIMAPCore/Grammar/AuthenticatedURL.swift @@ -33,7 +33,6 @@ public struct AuthenticatedURL: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeIAuthenticatedURL(_ data: AuthenticatedURL) -> Int { - self.writeAuthenticatedURLRump(data.authenticatedURL) + - self.writeAuthenticatedURLVerifier(data.verifier) + self.writeAuthenticatedURLRump(data.authenticatedURL) + self.writeAuthenticatedURLVerifier(data.verifier) } } diff --git a/Sources/NIOIMAPCore/Grammar/AuthenticatedURLRump.swift b/Sources/NIOIMAPCore/Grammar/AuthenticatedURLRump.swift index b4cde753d..421a56b4f 100644 --- a/Sources/NIOIMAPCore/Grammar/AuthenticatedURLRump.swift +++ b/Sources/NIOIMAPCore/Grammar/AuthenticatedURLRump.swift @@ -35,8 +35,6 @@ extension EncodeBuffer { @discardableResult mutating func writeAuthenticatedURLRump(_ data: AuthenticatedURLRump) -> Int { self.writeIfExists(data.expire) { expire in self.writeExpire(expire) - } + - self.writeString(";URLAUTH=") + - self.writeAccess(data.access) + } + self.writeString(";URLAUTH=") + self.writeAccess(data.access) } } diff --git a/Sources/NIOIMAPCore/Grammar/AuthenticatedURLVerifier.swift b/Sources/NIOIMAPCore/Grammar/AuthenticatedURLVerifier.swift index 4bb4126b4..3b1c71a2f 100644 --- a/Sources/NIOIMAPCore/Grammar/AuthenticatedURLVerifier.swift +++ b/Sources/NIOIMAPCore/Grammar/AuthenticatedURLVerifier.swift @@ -33,9 +33,7 @@ public struct AuthenticatedURLVerifier: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeAuthenticatedURLVerifier(_ data: AuthenticatedURLVerifier) -> Int { - self.writeString(":") + - self.writeURLAuthenticationMechanism(data.urlAuthenticationMechanism) + - self.writeString(":") + - self.writeEncodedAuthenticationURL(data.encodedAuthenticationURL) + self.writeString(":") + self.writeURLAuthenticationMechanism(data.urlAuthenticationMechanism) + + self.writeString(":") + self.writeEncodedAuthenticationURL(data.encodedAuthenticationURL) } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Body.swift b/Sources/NIOIMAPCore/Grammar/Body/Body.swift index 33d529abc..0dba512e6 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Body.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Body.swift @@ -178,9 +178,12 @@ extension BodyStructure { try self.recursiveEnumerateParts(parent: [], closure) } - private func recursiveEnumerateParts(parent: SectionSpecifier.Part, _ closure: (SectionSpecifier.Part, BodyStructure) throws -> Void) rethrows { + private func recursiveEnumerateParts( + parent: SectionSpecifier.Part, + _ closure: (SectionSpecifier.Part, BodyStructure) throws -> Void + ) rethrows { guard self.subpartCount > 0 else { return } - for part in 1 ... self.subpartCount { + for part in 1...self.subpartCount { let spec = SectionSpecifier.Part(Array(parent) + [part]) let bs = self[[part]] try closure(spec, bs) diff --git a/Sources/NIOIMAPCore/Grammar/Body/Field/Disposition.swift b/Sources/NIOIMAPCore/Grammar/Body/Field/Disposition.swift index 4153b23be..88d895348 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Field/Disposition.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Field/Disposition.swift @@ -46,9 +46,11 @@ extension BodyStructure { /// Attempts to find and convert the value for the common field "SIZE". If the field doesn't exist or is not a valid integer then `nil` is returned. public var size: Int? { - guard let value = self.parameters.first(where: { (pair) -> Bool in - pair.0.lowercased() == "size" - })?.1 else { + guard + let value = self.parameters.first(where: { (pair) -> Bool in + pair.0.lowercased() == "size" + })?.1 + else { return nil } return Int(value) @@ -78,10 +80,7 @@ extension EncodeBuffer { } return - self.writeString("(") + - self.writeIMAPString(dsp.kind.rawValue) + - self.writeSpace() + - self.writeBodyParameterPairs(dsp.parameters) + - self.writeString(")") + self.writeString("(") + self.writeIMAPString(dsp.kind.rawValue) + self.writeSpace() + + self.writeBodyParameterPairs(dsp.parameters) + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Field/DispositionAndLanguage.swift b/Sources/NIOIMAPCore/Grammar/Body/Field/DispositionAndLanguage.swift index fd48e01a3..771da75f4 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Field/DispositionAndLanguage.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Field/DispositionAndLanguage.swift @@ -37,8 +37,9 @@ extension BodyStructure { // MARK: - Encoding extension EncodeBuffer { - @discardableResult mutating func writeBodyDispositionAndLanguage(_ desc: BodyStructure.DispositionAndLanguage) -> Int { - self.writeSpace() + - self.writeBodyDisposition(desc.disposition) + @discardableResult mutating func writeBodyDispositionAndLanguage( + _ desc: BodyStructure.DispositionAndLanguage + ) -> Int { + self.writeSpace() + self.writeBodyDisposition(desc.disposition) } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Field/Fields.swift b/Sources/NIOIMAPCore/Grammar/Body/Field/Fields.swift index adab4c56c..8bb689210 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Field/Fields.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Field/Fields.swift @@ -39,7 +39,13 @@ extension BodyStructure { /// - parameter description: A string giving the content description as defined in MIME-IMB /// - parameter encoding: The string giving the content transfer encoding as defined in MIME-IMB /// - parameter octetCount: The size of the body in octets. Note that this is in the encoded state, before any decoding takes place. - public init(parameters: OrderedDictionary, id: String?, contentDescription: String?, encoding: BodyStructure.Encoding?, octetCount: Int) { + public init( + parameters: OrderedDictionary, + id: String?, + contentDescription: String?, + encoding: BodyStructure.Encoding?, + octetCount: Int + ) { self.parameters = parameters self.id = id self.contentDescription = contentDescription @@ -53,13 +59,8 @@ extension BodyStructure { extension EncodeBuffer { @discardableResult mutating func writeBodyFields(_ fields: BodyStructure.Fields) -> Int { - self.writeBodyParameterPairs(fields.parameters) + - self.writeSpace() + - self.writeNString(fields.id) + - self.writeSpace() + - self.writeNString(fields.contentDescription) + - self.writeSpace() + - self.writeBodyEncoding(fields.encoding) + - self.writeString(" \(fields.octetCount)") + self.writeBodyParameterPairs(fields.parameters) + self.writeSpace() + self.writeNString(fields.id) + + self.writeSpace() + self.writeNString(fields.contentDescription) + self.writeSpace() + + self.writeBodyEncoding(fields.encoding) + self.writeString(" \(fields.octetCount)") } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Field/LanguageLocation.swift b/Sources/NIOIMAPCore/Grammar/Body/Field/LanguageLocation.swift index 801a8a2de..0e9c1aa66 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Field/LanguageLocation.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Field/LanguageLocation.swift @@ -37,9 +37,8 @@ extension BodyStructure { extension EncodeBuffer { @discardableResult mutating func writeBodyFieldLanguageLocation(_ langLoc: BodyStructure.LanguageLocation) -> Int { - self.writeSpace() + - self.writeBodyLanguages(langLoc.languages) + - self.writeIfExists(langLoc.location) { (location) -> Int in + self.writeSpace() + self.writeBodyLanguages(langLoc.languages) + + self.writeIfExists(langLoc.location) { (location) -> Int in self.writeBodyLocationAndExtensions(location) } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Field/LocationAndExtensions.swift b/Sources/NIOIMAPCore/Grammar/Body/Field/LocationAndExtensions.swift index a6e133ab2..95f61fc4b 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Field/LocationAndExtensions.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Field/LocationAndExtensions.swift @@ -38,12 +38,12 @@ extension BodyStructure { // MARK: - Encoding extension EncodeBuffer { - @discardableResult mutating func writeBodyLocationAndExtensions(_ locationExtension: BodyStructure.LocationAndExtensions) -> Int { - self.writeSpace() + - self.writeNString(locationExtension.location) + - self.write(if: !locationExtension.extensions.isEmpty) { - self.writeSpace() + - self.writeBodyExtensions(locationExtension.extensions) + @discardableResult mutating func writeBodyLocationAndExtensions( + _ locationExtension: BodyStructure.LocationAndExtensions + ) -> Int { + self.writeSpace() + self.writeNString(locationExtension.location) + + self.write(if: !locationExtension.extensions.isEmpty) { + self.writeSpace() + self.writeBodyExtensions(locationExtension.extensions) } } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Field/ParameterPair.swift b/Sources/NIOIMAPCore/Grammar/Body/Field/ParameterPair.swift index 4fd17512a..002307bff 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Field/ParameterPair.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Field/ParameterPair.swift @@ -22,9 +22,7 @@ extension EncodeBuffer { return self.writeNil() } return self.writeOrderedDictionary(params) { (element, self) in - self.writeIMAPString(element.key) + - self.writeSpace() + - self.writeIMAPString(element.value) + self.writeIMAPString(element.key) + self.writeSpace() + self.writeIMAPString(element.value) } } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Multipart.swift b/Sources/NIOIMAPCore/Grammar/Body/Multipart.swift index 7fb75ccb7..8f1439bc9 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Multipart.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Multipart.swift @@ -53,7 +53,10 @@ extension BodyStructure.Multipart { /// Creates a new `Multipart.Extension`. /// - parameter parameters : An array of *key/value* pairs. /// - parameter dispositionAndLanguage: A disposition paired to an array of languages. - public init(parameters: OrderedDictionary, dispositionAndLanguage: BodyStructure.DispositionAndLanguage?) { + public init( + parameters: OrderedDictionary, + dispositionAndLanguage: BodyStructure.DispositionAndLanguage? + ) { self.parameters = parameters self.dispositionAndLanguage = dispositionAndLanguage } @@ -66,18 +69,15 @@ extension EncodeBuffer { @discardableResult mutating func writeBodyMultipart(_ part: BodyStructure.Multipart) -> Int { part.parts.reduce(into: 0) { (result, body) in result += self.writeBody(body) - } + - self.writeSpace() + - self.writeMediaSubtype(part.mediaSubtype) + - self.writeIfExists(part.extension) { (ext) -> Int in - self.writeSpace() + - self.writeBodyExtensionMultipart(ext) + } + self.writeSpace() + self.writeMediaSubtype(part.mediaSubtype) + + self.writeIfExists(part.extension) { (ext) -> Int in + self.writeSpace() + self.writeBodyExtensionMultipart(ext) } } @discardableResult mutating func writeBodyExtensionMultipart(_ ext: BodyStructure.Multipart.Extension) -> Int { - self.writeBodyParameterPairs(ext.parameters) + - self.writeIfExists(ext.dispositionAndLanguage) { (dspLanguage) -> Int in + self.writeBodyParameterPairs(ext.parameters) + + self.writeIfExists(ext.dispositionAndLanguage) { (dspLanguage) -> Int in self.writeBodyDispositionAndLanguage(dspLanguage) } } diff --git a/Sources/NIOIMAPCore/Grammar/Body/Singlepart.swift b/Sources/NIOIMAPCore/Grammar/Body/Singlepart.swift index 4fc2ae125..10f56d6dc 100644 --- a/Sources/NIOIMAPCore/Grammar/Body/Singlepart.swift +++ b/Sources/NIOIMAPCore/Grammar/Body/Singlepart.swift @@ -137,35 +137,33 @@ extension EncodeBuffer { return size } - @discardableResult private mutating func writeBodyKindText(_ body: BodyStructure.Singlepart.Text, fields: BodyStructure.Fields) -> Int { - self.writeString(#""TEXT" "#) + - self.writeMediaSubtype(body.mediaSubtype) + - self.writeSpace() + - self.writeBodyFields(fields) + - self.writeString(" \(body.lineCount)") + @discardableResult private mutating func writeBodyKindText( + _ body: BodyStructure.Singlepart.Text, + fields: BodyStructure.Fields + ) -> Int { + self.writeString(#""TEXT" "#) + self.writeMediaSubtype(body.mediaSubtype) + self.writeSpace() + + self.writeBodyFields(fields) + self.writeString(" \(body.lineCount)") } - @discardableResult private mutating func writeBodyKindMessage(_ message: BodyStructure.Singlepart.Message, fields: BodyStructure.Fields) -> Int { - self.writeString(#""MESSAGE" "#) + - self.writeMediaSubtype(message.message) + - self.writeSpace() + - self.writeBodyFields(fields) + - self.writeSpace() + - self.writeEnvelope(message.envelope) + - self.writeSpace() + - self.writeBody(message.body) + - self.writeString(" \(message.lineCount)") + @discardableResult private mutating func writeBodyKindMessage( + _ message: BodyStructure.Singlepart.Message, + fields: BodyStructure.Fields + ) -> Int { + self.writeString(#""MESSAGE" "#) + self.writeMediaSubtype(message.message) + self.writeSpace() + + self.writeBodyFields(fields) + self.writeSpace() + self.writeEnvelope(message.envelope) + + self.writeSpace() + self.writeBody(message.body) + self.writeString(" \(message.lineCount)") } - @discardableResult private mutating func writeBodyKindBasic(mediaType: Media.MediaType, fields: BodyStructure.Fields) -> Int { - self.writeMediaType(mediaType) + - self.writeSpace() + - self.writeBodyFields(fields) + @discardableResult private mutating func writeBodyKindBasic( + mediaType: Media.MediaType, + fields: BodyStructure.Fields + ) -> Int { + self.writeMediaType(mediaType) + self.writeSpace() + self.writeBodyFields(fields) } @discardableResult mutating func writeBodyExtensionSinglePart(_ ext: BodyStructure.Singlepart.Extension) -> Int { - self.writeNString(ext.digest) + - self.writeIfExists(ext.dispositionAndLanguage) { (dsp) -> Int in + self.writeNString(ext.digest) + + self.writeIfExists(ext.dispositionAndLanguage) { (dsp) -> Int in self.writeBodyDispositionAndLanguage(dsp) } } diff --git a/Sources/NIOIMAPCore/Grammar/ByteRange/ByteRange.swift b/Sources/NIOIMAPCore/Grammar/ByteRange/ByteRange.swift index 4ae58ccec..c992a668c 100644 --- a/Sources/NIOIMAPCore/Grammar/ByteRange/ByteRange.swift +++ b/Sources/NIOIMAPCore/Grammar/ByteRange/ByteRange.swift @@ -39,8 +39,8 @@ extension EncodeBuffer { } @discardableResult mutating func writeByteRange(_ data: ByteRange) -> Int { - self.writeString("\(data.offset)") + - self.writeIfExists(data.length) { length in + self.writeString("\(data.offset)") + + self.writeIfExists(data.length) { length in self.writeString(".\(length)") } } diff --git a/Sources/NIOIMAPCore/Grammar/ByteRange/MessagePathByteRange.swift b/Sources/NIOIMAPCore/Grammar/ByteRange/MessagePathByteRange.swift index 3fd17c900..6cf6afc7b 100644 --- a/Sources/NIOIMAPCore/Grammar/ByteRange/MessagePathByteRange.swift +++ b/Sources/NIOIMAPCore/Grammar/ByteRange/MessagePathByteRange.swift @@ -30,12 +30,10 @@ extension MessagePath { extension EncodeBuffer { @discardableResult mutating func writeMessagePathByteRange(_ data: MessagePath.ByteRange) -> Int { - self.writeString("/;PARTIAL=") + - self.writeByteRange(data.range) + self.writeString("/;PARTIAL=") + self.writeByteRange(data.range) } @discardableResult mutating func writeMessagePathByteRangeOnly(_ data: MessagePath.ByteRange) -> Int { - self.writeString(";PARTIAL=") + - self.writeByteRange(data.range) + self.writeString(";PARTIAL=") + self.writeByteRange(data.range) } } diff --git a/Sources/NIOIMAPCore/Grammar/Capability.swift b/Sources/NIOIMAPCore/Grammar/Capability.swift index 6deb5801a..2b53670cd 100644 --- a/Sources/NIOIMAPCore/Grammar/Capability.swift +++ b/Sources/NIOIMAPCore/Grammar/Capability.swift @@ -403,11 +403,10 @@ extension Capability { /// - parameter type: The `SortKind`. /// - returns: A new `Capability`. public static func sort(_ type: SortKind?) -> Self { - if let type = type { - return Self("SORT=\(type.rawValue)") - } else { + guard let type = type else { return Self("SORT") } + return Self("SORT=\(type.rawValue)") } /// Creates a new *UTF8* capability. @@ -462,8 +461,8 @@ extension EncodeBuffer { } @discardableResult mutating func writeCapabilityData(_ data: [Capability]) -> Int { - self.writeString("CAPABILITY") + - self.writeArray(data, prefix: " ", parenthesis: false) { (capability, self) -> Int in + self.writeString("CAPABILITY") + + self.writeArray(data, prefix: " ", parenthesis: false) { (capability, self) -> Int in self.writeCapability(capability) } } diff --git a/Sources/NIOIMAPCore/Grammar/Command/Command.swift b/Sources/NIOIMAPCore/Grammar/Command/Command.swift index 67dec40d9..084c96aec 100644 --- a/Sources/NIOIMAPCore/Grammar/Command/Command.swift +++ b/Sources/NIOIMAPCore/Grammar/Command/Command.swift @@ -218,9 +218,19 @@ extension CommandEncodeBuffer { case .examine(let mailbox, let params): return self.writeCommandKind_examine(mailbox: mailbox, parameters: params) case .list(let selectOptions, let mailbox, let mailboxPatterns, let returnOptions): - return self.writeCommandKind_list(selectOptions: selectOptions, mailbox: mailbox, mailboxPatterns: mailboxPatterns, returnOptions: returnOptions) + return self.writeCommandKind_list( + selectOptions: selectOptions, + mailbox: mailbox, + mailboxPatterns: mailboxPatterns, + returnOptions: returnOptions + ) case .listIndependent(let selectOptions, let mailbox, let mailboxPatterns, let returnOptions): - return self.writeCommandKind_listIndependent(selectOptions: selectOptions, mailbox: mailbox, mailboxPatterns: mailboxPatterns, returnOptions: returnOptions) + return self.writeCommandKind_listIndependent( + selectOptions: selectOptions, + mailbox: mailbox, + mailboxPatterns: mailboxPatterns, + returnOptions: returnOptions + ) case .lsub(let mailbox, let listMailbox): return self.writeCommandKind_lsub(mailbox: mailbox, listMailbox: listMailbox) case .rename(let from, let to, let params): @@ -303,24 +313,27 @@ extension CommandEncodeBuffer { } private mutating func writeCommandKind_urlFetch(urls: [ByteBuffer]) -> Int { - self.buffer.writeString("URLFETCH") + - self.buffer.writeArray(urls, prefix: " ", parenthesis: false) { url, buffer in + self.buffer.writeString("URLFETCH") + + self.buffer.writeArray(urls, prefix: " ", parenthesis: false) { url, buffer in buffer.writeBytes(url.readableBytesView) } } private mutating func writeCommandKind_generateAuthorizedURL(mechanisms: [RumpURLAndMechanism]) -> Int { - self.buffer.writeString("GENURLAUTH") + - self.buffer.writeArray(mechanisms, prefix: " ", parenthesis: false) { mechanism, buffer in + self.buffer.writeString("GENURLAUTH") + + self.buffer.writeArray(mechanisms, prefix: " ", parenthesis: false) { mechanism, buffer in buffer.writeURLRumpMechanism(mechanism) } } - private mutating func writeCommandKind_resetKey(mailbox: MailboxName?, mechanisms: [URLAuthenticationMechanism]) -> Int { - self.buffer.writeString("RESETKEY") + - self.buffer.writeIfExists(mailbox) { mailbox in - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) + + private mutating func writeCommandKind_resetKey( + mailbox: MailboxName?, + mechanisms: [URLAuthenticationMechanism] + ) -> Int { + self.buffer.writeString("RESETKEY") + + self.buffer.writeIfExists(mailbox) { mailbox in + self.buffer.writeSpace() + self.buffer.writeMailbox(mailbox) + + self.buffer.writeArray(mechanisms, prefix: " ", parenthesis: false) { mechanism, buffer in buffer.writeURLAuthenticationMechanism(mechanism) @@ -328,22 +341,24 @@ extension CommandEncodeBuffer { } } - private mutating func writeCommandKind_getMetadata(options: [MetadataOption], mailbox: MailboxName, entries: [MetadataEntryName]) -> Int { - self.buffer.writeString("GETMETADATA") + - self.buffer.write(if: options.count >= 1) { + private mutating func writeCommandKind_getMetadata( + options: [MetadataOption], + mailbox: MailboxName, + entries: [MetadataEntryName] + ) -> Int { + self.buffer.writeString("GETMETADATA") + + self.buffer.write(if: options.count >= 1) { buffer.writeSpace() + buffer.writeMetadataOptions(options) - } + - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeSpace() + - self.buffer.writeEntries(entries) + } + self.buffer.writeSpace() + self.buffer.writeMailbox(mailbox) + self.buffer.writeSpace() + + self.buffer.writeEntries(entries) } - private mutating func writeCommandKind_setMetadata(mailbox: MailboxName, entries: OrderedDictionary) -> Int { - self.buffer.writeString("SETMETADATA ") + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeSpace() + - self.buffer.writeEntryValues(entries) + private mutating func writeCommandKind_setMetadata( + mailbox: MailboxName, + entries: OrderedDictionary + ) -> Int { + self.buffer.writeString("SETMETADATA ") + self.buffer.writeMailbox(mailbox) + self.buffer.writeSpace() + + self.buffer.writeEntryValues(entries) } private mutating func writeCommandKind_capability() -> Int { @@ -359,99 +374,91 @@ extension CommandEncodeBuffer { } private mutating func writeCommandKind_create(mailbox: MailboxName, parameters: [CreateParameter]) -> Int { - self.buffer.writeString("CREATE ") + - self.buffer.writeMailbox(mailbox) + - self.buffer.write(if: parameters.count > 0) { - self.buffer.writeSpace() + - self.buffer.writeArray(parameters, separator: "", parenthesis: true) { (param, buffer) -> Int in + self.buffer.writeString("CREATE ") + self.buffer.writeMailbox(mailbox) + + self.buffer.write(if: parameters.count > 0) { + self.buffer.writeSpace() + + self.buffer.writeArray(parameters, separator: "", parenthesis: true) { (param, buffer) -> Int in buffer.writeCreateParameter(param) } } } private mutating func writeCommandKind_delete(mailbox: MailboxName) -> Int { - self.buffer.writeString("DELETE ") + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("DELETE ") + self.buffer.writeMailbox(mailbox) } private mutating func writeCommandKind_examine(mailbox: MailboxName, parameters: [SelectParameter]) -> Int { - self.buffer.writeString("EXAMINE ") + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeSelectParameters(parameters) - } - - private mutating func writeCommandKind_list(selectOptions: ListSelectOptions?, mailbox: MailboxName, mailboxPatterns: MailboxPatterns, returnOptions: [ReturnOption]) -> Int { - self.buffer.writeString("LIST") + - self.buffer.writeIfExists(selectOptions) { (options) -> Int in - self.buffer.writeSpace() + - self.buffer.writeListSelectOptions(options) - } + - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeSpace() + - self.buffer.writeMailboxPatterns(mailboxPatterns) + - self.buffer.write(if: returnOptions.count >= 1) { - self.buffer.writeSpace() + - self.buffer.writeListReturnOptions(returnOptions) + self.buffer.writeString("EXAMINE ") + self.buffer.writeMailbox(mailbox) + + self.buffer.writeSelectParameters(parameters) + } + + private mutating func writeCommandKind_list( + selectOptions: ListSelectOptions?, + mailbox: MailboxName, + mailboxPatterns: MailboxPatterns, + returnOptions: [ReturnOption] + ) -> Int { + self.buffer.writeString("LIST") + + self.buffer.writeIfExists(selectOptions) { (options) -> Int in + self.buffer.writeSpace() + self.buffer.writeListSelectOptions(options) + } + self.buffer.writeSpace() + self.buffer.writeMailbox(mailbox) + self.buffer.writeSpace() + + self.buffer.writeMailboxPatterns(mailboxPatterns) + + self.buffer.write(if: returnOptions.count >= 1) { + self.buffer.writeSpace() + self.buffer.writeListReturnOptions(returnOptions) } } - private mutating func writeCommandKind_listIndependent(selectOptions: [ListSelectIndependentOption], mailbox: MailboxName, mailboxPatterns: MailboxPatterns, returnOptions: [ReturnOption]) -> Int { - self.buffer.writeString("LIST") + - self.buffer.write(if: selectOptions.count >= 1) { + private mutating func writeCommandKind_listIndependent( + selectOptions: [ListSelectIndependentOption], + mailbox: MailboxName, + mailboxPatterns: MailboxPatterns, + returnOptions: [ReturnOption] + ) -> Int { + self.buffer.writeString("LIST") + + self.buffer.write(if: selectOptions.count >= 1) { self.buffer.writeArray(selectOptions) { element, buffer in buffer.writeListSelectIndependentOption(element) } - } + - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeSpace() + - self.buffer.writeMailboxPatterns(mailboxPatterns) + - self.buffer.write(if: returnOptions.count >= 1) { - self.buffer.writeSpace() + - self.buffer.writeListReturnOptions(returnOptions) + } + self.buffer.writeSpace() + self.buffer.writeMailbox(mailbox) + self.buffer.writeSpace() + + self.buffer.writeMailboxPatterns(mailboxPatterns) + + self.buffer.write(if: returnOptions.count >= 1) { + self.buffer.writeSpace() + self.buffer.writeListReturnOptions(returnOptions) } } private mutating func writeCommandKind_lsub(mailbox: MailboxName, listMailbox: ByteBuffer) -> Int { - self.buffer.writeString("LSUB ") + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeSpace() + - self.buffer.writeIMAPString(listMailbox) - } - - private mutating func writeCommandKind_rename(from: MailboxName, to: MailboxName, parameters: OrderedDictionary) -> Int { - self.buffer.writeString("RENAME ") + - self.buffer.writeMailbox(from) + - self.buffer.writeSpace() + - self.buffer.writeMailbox(to) + - self.buffer.writeIfExists(parameters) { (params) -> Int in + self.buffer.writeString("LSUB ") + self.buffer.writeMailbox(mailbox) + self.buffer.writeSpace() + + self.buffer.writeIMAPString(listMailbox) + } + + private mutating func writeCommandKind_rename( + from: MailboxName, + to: MailboxName, + parameters: OrderedDictionary + ) -> Int { + self.buffer.writeString("RENAME ") + self.buffer.writeMailbox(from) + self.buffer.writeSpace() + + self.buffer.writeMailbox(to) + + self.buffer.writeIfExists(parameters) { (params) -> Int in self.buffer.writeParameters(params) } } private mutating func writeCommandKind_select(mailbox: MailboxName, parameters: [SelectParameter]) -> Int { - self.buffer.writeString("SELECT ") + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeSelectParameters(parameters) + self.buffer.writeString("SELECT ") + self.buffer.writeMailbox(mailbox) + + self.buffer.writeSelectParameters(parameters) } private mutating func writeCommandKind_status(mailbox: MailboxName, attributes: [MailboxAttribute]) -> Int { - self.buffer.writeString("STATUS ") + - self.buffer.writeMailbox(mailbox) + - self.buffer.writeString(" (") + - self.buffer.writeMailboxAttributes(attributes) + - self.buffer.writeString(")") + self.buffer.writeString("STATUS ") + self.buffer.writeMailbox(mailbox) + self.buffer.writeString(" (") + + self.buffer.writeMailboxAttributes(attributes) + self.buffer.writeString(")") } private mutating func writeCommandKind_subscribe(mailbox: MailboxName) -> Int { - self.buffer.writeString("SUBSCRIBE ") + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("SUBSCRIBE ") + self.buffer.writeMailbox(mailbox) } private mutating func writeCommandKind_unsubscribe(mailbox: MailboxName) -> Int { - self.buffer.writeString("UNSUBSCRIBE ") + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("UNSUBSCRIBE ") + self.buffer.writeMailbox(mailbox) } private mutating func writeCommandKind_compress(kind: Capability.CompressionKind) -> Int { @@ -459,16 +466,18 @@ extension CommandEncodeBuffer { } private mutating func writeCommandKind_custom(name: String, payloads: [Command.CustomCommandPayload]) -> Int { - self.buffer.writeString("\(name)") + - self.buffer.writeArray(payloads, prefix: " ", separator: "", parenthesis: false) { (payload, self) in + self.buffer.writeString("\(name)") + + self.buffer.writeArray(payloads, prefix: " ", separator: "", parenthesis: false) { (payload, self) in self.writeCustomCommandPayload(payload) } } - private mutating func writeCommandKind_authenticate(mechanism: AuthenticationMechanism, initialResponse: InitialResponse?) -> Int { - self.buffer.writeString("AUTHENTICATE ") + - self.buffer.writeAuthenticationMechanism(mechanism) + - self.buffer.writeIfExists(initialResponse) { resp in + private mutating func writeCommandKind_authenticate( + mechanism: AuthenticationMechanism, + initialResponse: InitialResponse? + ) -> Int { + self.buffer.writeString("AUTHENTICATE ") + self.buffer.writeAuthenticationMechanism(mechanism) + + self.buffer.writeIfExists(initialResponse) { resp in var c = self.buffer.writeSpace() if self.buffer.loggingMode { c += self.buffer.writeString("∅") @@ -480,10 +489,8 @@ extension CommandEncodeBuffer { } private mutating func writeCommandKind_login(userID: String, password: String) -> Int { - self.buffer.writeString("LOGIN ") + - self.buffer.writeIMAPString(userID) + - self.buffer.writeSpace() + - self.buffer.writeIMAPString(password) + self.buffer.writeString("LOGIN ") + self.buffer.writeIMAPString(userID) + self.buffer.writeSpace() + + self.buffer.writeIMAPString(password) } private mutating func writeCommandKind_startTLS() -> Int { @@ -503,8 +510,7 @@ extension CommandEncodeBuffer { } private mutating func writeCommandKind_uidExpunge(_ set: LastCommandSet) -> Int { - self.buffer.writeString("EXPUNGE ") + - self.buffer.writeLastCommandSet(set) + self.buffer.writeString("EXPUNGE ") + self.buffer.writeLastCommandSet(set) } private mutating func writeCommandKind_unselect() -> Int { @@ -520,96 +526,98 @@ extension CommandEncodeBuffer { } private mutating func writeCommandKind_enable(capabilities: [Capability]) -> Int { - self.buffer.writeString("ENABLE ") + - self.buffer.writeArray(capabilities, parenthesis: false) { (element, self) in + self.buffer.writeString("ENABLE ") + + self.buffer.writeArray(capabilities, parenthesis: false) { (element, self) in self.writeCapability(element) } } private mutating func writeCommandKind_copy(set: LastCommandSet, mailbox: MailboxName) -> Int { - self.buffer.writeString("COPY ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("COPY ") + self.buffer.writeLastCommandSet(set) + self.buffer.writeSpace() + + self.buffer.writeMailbox(mailbox) } private mutating func writeCommandKind_uidCopy(set: LastCommandSet, mailbox: MailboxName) -> Int { - self.buffer.writeString("UID COPY ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) - } - - private mutating func writeCommandKind_fetch(set: LastCommandSet, atts: [FetchAttribute], modifiers: [FetchModifier]) -> Int { - self.buffer.writeString("FETCH ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.writeSpace() + - self.buffer.writeFetchAttributeList(atts) + - self.buffer.writeFetchModifiers(modifiers) - } - - private mutating func writeCommandKind_uidFetch(set: LastCommandSet, atts: [FetchAttribute], modifiers: [FetchModifier]) -> Int { - self.buffer.writeString("UID FETCH ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.writeSpace() + - self.buffer.writeFetchAttributeList(atts) + - self.buffer.writeFetchModifiers(modifiers) - } - - private mutating func writeCommandKind_store(set: LastCommandSet, modifiers: [StoreModifier], data: StoreData) -> Int { - self.buffer.writeString("STORE ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.write(if: modifiers.count >= 1) { - self.buffer.writeSpace() + - self.buffer.writeArray(modifiers) { (element, buffer) -> Int in + self.buffer.writeString("UID COPY ") + self.buffer.writeLastCommandSet(set) + self.buffer.writeSpace() + + self.buffer.writeMailbox(mailbox) + } + + private mutating func writeCommandKind_fetch( + set: LastCommandSet, + atts: [FetchAttribute], + modifiers: [FetchModifier] + ) -> Int { + self.buffer.writeString("FETCH ") + self.buffer.writeLastCommandSet(set) + self.buffer.writeSpace() + + self.buffer.writeFetchAttributeList(atts) + self.buffer.writeFetchModifiers(modifiers) + } + + private mutating func writeCommandKind_uidFetch( + set: LastCommandSet, + atts: [FetchAttribute], + modifiers: [FetchModifier] + ) -> Int { + self.buffer.writeString("UID FETCH ") + self.buffer.writeLastCommandSet(set) + self.buffer.writeSpace() + + self.buffer.writeFetchAttributeList(atts) + self.buffer.writeFetchModifiers(modifiers) + } + + private mutating func writeCommandKind_store( + set: LastCommandSet, + modifiers: [StoreModifier], + data: StoreData + ) -> Int { + self.buffer.writeString("STORE ") + self.buffer.writeLastCommandSet(set) + + self.buffer.write(if: modifiers.count >= 1) { + self.buffer.writeSpace() + + self.buffer.writeArray(modifiers) { (element, buffer) -> Int in buffer.writeStoreModifier(element) } - } + - self.buffer.writeSpace() + - self.buffer.writeStoreData(data) + } + self.buffer.writeSpace() + self.buffer.writeStoreData(data) } - private mutating func writeCommandKind_uidStore(set: LastCommandSet, modifiers: [StoreModifier], data: StoreData) -> Int { - self.buffer.writeString("UID STORE ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.write(if: modifiers.count >= 1) { + private mutating func writeCommandKind_uidStore( + set: LastCommandSet, + modifiers: [StoreModifier], + data: StoreData + ) -> Int { + self.buffer.writeString("UID STORE ") + self.buffer.writeLastCommandSet(set) + + self.buffer.write(if: modifiers.count >= 1) { self.buffer.writeStoreModifiers(modifiers) - } + - self.buffer.writeSpace() + - self.buffer.writeStoreData(data) + } + self.buffer.writeSpace() + self.buffer.writeStoreData(data) } - private mutating func writeCommandKind_search(key: SearchKey, charset: String? = nil, returnOptions: [SearchReturnOption] = []) -> Int { - self.buffer.writeString("SEARCH") + - self.buffer.writeIfExists(returnOptions) { (options) -> Int in + private mutating func writeCommandKind_search( + key: SearchKey, + charset: String? = nil, + returnOptions: [SearchReturnOption] = [] + ) -> Int { + self.buffer.writeString("SEARCH") + + self.buffer.writeIfExists(returnOptions) { (options) -> Int in self.buffer.writeSearchReturnOptions(options) - } + - self.buffer.writeSpace() + - self.buffer.write(if: key.usesString) { + } + self.buffer.writeSpace() + + self.buffer.write(if: key.usesString) { self.buffer.writeIfExists(charset) { (charset) -> Int in self.buffer.writeString("CHARSET \(charset) ") } - } + - self.buffer.writeSearchKey(key) + } + self.buffer.writeSearchKey(key) } - private mutating func writeCommandKind_uidSearch(key: SearchKey, charset: String? = nil, returnOptions: [SearchReturnOption] = []) -> Int { - self.buffer.writeString("UID ") + - self.writeCommandKind_search(key: key, charset: charset, returnOptions: returnOptions) + private mutating func writeCommandKind_uidSearch( + key: SearchKey, + charset: String? = nil, + returnOptions: [SearchReturnOption] = [] + ) -> Int { + self.buffer.writeString("UID ") + + self.writeCommandKind_search(key: key, charset: charset, returnOptions: returnOptions) } private mutating func writeCommandKind_move(set: LastCommandSet, mailbox: MailboxName) -> Int { - self.buffer.writeString("MOVE ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("MOVE ") + self.buffer.writeLastCommandSet(set) + self.buffer.writeSpace() + + self.buffer.writeMailbox(mailbox) } private mutating func writeCommandKind_uidMove(set: LastCommandSet, mailbox: MailboxName) -> Int { - self.buffer.writeString("UID MOVE ") + - self.buffer.writeLastCommandSet(set) + - self.buffer.writeSpace() + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("UID MOVE ") + self.buffer.writeLastCommandSet(set) + self.buffer.writeSpace() + + self.buffer.writeMailbox(mailbox) } private mutating func writeCommandKind_namespace() -> Int { @@ -617,32 +625,26 @@ extension CommandEncodeBuffer { } @discardableResult mutating func writeCommandKind_id(_ id: OrderedDictionary) -> Int { - self.buffer.writeString("ID ") + - self.buffer.writeIDParameters(id) + self.buffer.writeString("ID ") + self.buffer.writeIDParameters(id) } private mutating func writeCommandKind_getQuota(quotaRoot: QuotaRoot) -> Int { - self.buffer.writeString("GETQUOTA ") + - self.buffer.writeQuotaRoot(quotaRoot) + self.buffer.writeString("GETQUOTA ") + self.buffer.writeQuotaRoot(quotaRoot) } private mutating func writeCommandKind_getQuotaRoot(mailbox: MailboxName) -> Int { - self.buffer.writeString("GETQUOTAROOT ") + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("GETQUOTAROOT ") + self.buffer.writeMailbox(mailbox) } private mutating func writeCommandKind_setQuota(quotaRoot: QuotaRoot, resourceLimits: [QuotaLimit]) -> Int { - self.buffer.writeString("SETQUOTA ") + - self.buffer.writeQuotaRoot(quotaRoot) + - self.buffer.writeSpace() + - self.buffer.writeArray(resourceLimits) { (limit, self) in + self.buffer.writeString("SETQUOTA ") + self.buffer.writeQuotaRoot(quotaRoot) + self.buffer.writeSpace() + + self.buffer.writeArray(resourceLimits) { (limit, self) in self.writeQuotaLimit(limit) } } private mutating func writeCommandKind_extendedSearch(options: ExtendedSearchOptions) -> Int { - self.buffer.writeString("ESEARCH") + - self.buffer.writeExtendedSearchOptions(options) + self.buffer.writeString("ESEARCH") + self.buffer.writeExtendedSearchOptions(options) } } @@ -679,7 +681,8 @@ extension Command { /// - parameter attributes: Which attributes to retrieve. /// - parameter modifiers: Fetch modifiers. /// - returns: `nil` if `messages` is empty, otherwise a `Command`. - public static func uidFetch(messages: UIDSet, attributes: [FetchAttribute], modifiers: [FetchModifier]) -> Command? { + public static func uidFetch(messages: UIDSet, attributes: [FetchAttribute], modifiers: [FetchModifier]) -> Command? + { guard let set = MessageIdentifierSetNonEmpty(set: messages) else { return nil } diff --git a/Sources/NIOIMAPCore/Grammar/Command/CommandStreamPart.swift b/Sources/NIOIMAPCore/Grammar/Command/CommandStreamPart.swift index d124b5c98..bc5b209af 100644 --- a/Sources/NIOIMAPCore/Grammar/Command/CommandStreamPart.swift +++ b/Sources/NIOIMAPCore/Grammar/Command/CommandStreamPart.swift @@ -64,8 +64,8 @@ public enum AppendCommand: Hashable, Sendable { case .start(let tag, _): return tag case .beginMessage, .messageBytes, .endMessage, - .beginCatenate, .catenateURL, .catenateData, - .endCatenate, .finish: + .beginCatenate, .catenateURL, .catenateData, + .endCatenate, .finish: return nil } } @@ -171,8 +171,7 @@ extension CommandEncodeBuffer { switch command { case .start(tag: let tag, appendingTo: let mailbox): return - self.buffer.writeString("\(tag) APPEND ") + - self.buffer.writeMailbox(mailbox) + self.buffer.writeString("\(tag) APPEND ") + self.buffer.writeMailbox(mailbox) case .beginMessage(message: let message): return self.buffer.writeAppendMessage(message) case .messageBytes(var bytes): @@ -184,19 +183,18 @@ extension CommandEncodeBuffer { } return 0 case .beginCatenate(options: let options): - return self.buffer.writeAppendOptions(options) + - self.buffer.writeString(" CATENATE (") + return self.buffer.writeAppendOptions(options) + self.buffer.writeString(" CATENATE (") case .catenateURL(let url): defer { self.encodedAtLeastOneCatenateElement = true } - return self.buffer.write(if: self.encodedAtLeastOneCatenateElement) { self.buffer.writeSpace() } + - self.buffer.writeString("URL ") + - self.buffer.writeIMAPString(url) + return self.buffer.write(if: self.encodedAtLeastOneCatenateElement) { self.buffer.writeSpace() } + + self.buffer.writeString("URL ") + self.buffer.writeIMAPString(url) case .catenateData(.begin(let size)): - var written = self.buffer.write(if: self.encodedAtLeastOneCatenateElement) { self.buffer.writeSpace() } + - self.buffer.writeString("TEXT ") + var written = + self.buffer.write(if: self.encodedAtLeastOneCatenateElement) { self.buffer.writeSpace() } + + self.buffer.writeString("TEXT ") if self.options.useNonSynchronizingLiteralPlus { written += self.buffer.writeString("{\(size)+}\r\n") diff --git a/Sources/NIOIMAPCore/Grammar/CreateParameter.swift b/Sources/NIOIMAPCore/Grammar/CreateParameter.swift index eb6e3a1fd..c9dcc6776 100644 --- a/Sources/NIOIMAPCore/Grammar/CreateParameter.swift +++ b/Sources/NIOIMAPCore/Grammar/CreateParameter.swift @@ -29,8 +29,8 @@ extension EncodeBuffer { @discardableResult mutating func writeCreateParameter(_ parameter: CreateParameter) -> Int { switch parameter { case .attributes(let attributes): - return self.writeString("USE ") + - self.writeArray(attributes) { (att, buffer) -> Int in + return self.writeString("USE ") + + self.writeArray(attributes) { (att, buffer) -> Int in buffer.writeUseAttribute(att) } case .labelled(let parameter): diff --git a/Sources/NIOIMAPCore/Grammar/Date/InternalDate.swift b/Sources/NIOIMAPCore/Grammar/Date/InternalDate.swift index e93ee488b..65e30e30d 100644 --- a/Sources/NIOIMAPCore/Grammar/Date/InternalDate.swift +++ b/Sources/NIOIMAPCore/Grammar/Date/InternalDate.swift @@ -44,7 +44,15 @@ public struct ServerMessageDate: Hashable, Sendable { let zoneMinutes = Int(zoneValue) * ((zoneIsNegative == 0) ? 1 : -1) // safe to bang as we can't have an invalid `ServerMessageDate` - return Components(year: year, month: month, day: day, hour: hour, minute: minute, second: second, timeZoneMinutes: zoneMinutes)! + return Components( + year: year, + month: month, + day: day, + hour: hour, + minute: minute, + second: second, + timeZoneMinutes: zoneMinutes + )! } /// Creates a new `ServerMessageDate` from a given collection of `Components` @@ -105,13 +113,13 @@ extension ServerMessageDate { /// - parameter zoneMinutes: The timezone as an offset in minutes from UTC. public init?(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, timeZoneMinutes: Int) { guard - (1 ... 31).contains(day), - (1 ... 12).contains(month), - (0 ... 23).contains(hour), - (0 ... 59).contains(minute), - (0 ... 60).contains(second), - ((-24 * 60) ... (24 * 60)).contains(timeZoneMinutes), - (1 ... Int(UInt16.max)).contains(year) + (1...31).contains(day), + (1...12).contains(month), + (0...23).contains(hour), + (0...59).contains(minute), + (0...60).contains(second), + ((-24 * 60)...(24 * 60)).contains(timeZoneMinutes), + (1...Int(UInt16.max)).contains(year) else { return nil } @@ -180,11 +188,8 @@ extension EncodeBuffer { } return - self.writeString("\"\(p.0.day)-\(monthName)-\(p.0.year) ") + - self.writeTime(p.1) + - self.writeSpace() + - self.writeTimezone(p.2) + - self.writeString("\"") + self.writeString("\"\(p.0.day)-\(monthName)-\(p.0.year) ") + self.writeTime(p.1) + self.writeSpace() + + self.writeTimezone(p.2) + self.writeString("\"") } } diff --git a/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchOptions.swift b/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchOptions.swift index ad18b4f9f..d3b08af75 100644 --- a/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchOptions.swift +++ b/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchOptions.swift @@ -52,14 +52,12 @@ extension EncodeBuffer { @discardableResult mutating func writeExtendedSearchOptions(_ options: ExtendedSearchOptions) -> Int { self.writeIfExists(options.sourceOptions) { (options) -> Int in self.writeSpace() + self.writeExtendedSearchSourceOptions(options) - } + - self.writeIfExists(options.returnOptions) { (options) -> Int in + } + + self.writeIfExists(options.returnOptions) { (options) -> Int in self.writeSearchReturnOptions(options) - } + - self.writeSpace() + - self.writeIfExists(options.charset) { (charset) -> Int in + } + self.writeSpace() + + self.writeIfExists(options.charset) { (charset) -> Int in self.writeString("CHARSET \(charset) ") - } + - self.writeSearchKey(options.key) + } + self.writeSearchKey(options.key) } } diff --git a/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchSourceOptions.swift b/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchSourceOptions.swift index f30825c78..38e9a7fe0 100644 --- a/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchSourceOptions.swift +++ b/Sources/NIOIMAPCore/Grammar/ESearch/ExtendedSearchSourceOptions.swift @@ -38,15 +38,12 @@ public struct ExtendedSearchSourceOptions: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeExtendedSearchSourceOptions(_ options: ExtendedSearchSourceOptions) -> Int { - self.writeString("IN (") + - self.writeArray(options.sourceMailbox, parenthesis: false) { (filter, buffer) -> Int in + self.writeString("IN (") + + self.writeArray(options.sourceMailbox, parenthesis: false) { (filter, buffer) -> Int in buffer.writeMailboxFilter(filter) - } + - self.writeIfExists(options.scopeOptions) { scopeOptions in - self.writeString(" (") + - self.writeExtendedSearchScopeOptions(scopeOptions) + - self.writeString(")") - } + - self.writeString(")") + } + + self.writeIfExists(options.scopeOptions) { scopeOptions in + self.writeString(" (") + self.writeExtendedSearchScopeOptions(scopeOptions) + self.writeString(")") + } + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/EmailAddress.swift b/Sources/NIOIMAPCore/Grammar/EmailAddress.swift index 928cee2a1..64a4d51a3 100644 --- a/Sources/NIOIMAPCore/Grammar/EmailAddress.swift +++ b/Sources/NIOIMAPCore/Grammar/EmailAddress.swift @@ -46,14 +46,8 @@ public struct EmailAddress: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeEmailAddress(_ address: EmailAddress) -> Int { - self.writeString("(") + - self.writeNString(address.personName) + - self.writeSpace() + - self.writeNString(address.sourceRoot) + - self.writeSpace() + - self.writeNString(address.mailbox) + - self.writeSpace() + - self.writeNString(address.host) + - self.writeString(")") + self.writeString("(") + self.writeNString(address.personName) + self.writeSpace() + + self.writeNString(address.sourceRoot) + self.writeSpace() + self.writeNString(address.mailbox) + + self.writeSpace() + self.writeNString(address.host) + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Enable/EnableData.swift b/Sources/NIOIMAPCore/Grammar/Enable/EnableData.swift index d8838eac5..23cc147d6 100644 --- a/Sources/NIOIMAPCore/Grammar/Enable/EnableData.swift +++ b/Sources/NIOIMAPCore/Grammar/Enable/EnableData.swift @@ -18,8 +18,8 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeEnableData(_ data: [Capability]) -> Int { - self.writeString("ENABLED") + - data.reduce(0) { (result, capability) in + self.writeString("ENABLED") + + data.reduce(0) { (result, capability) in result + self.writeSpace() + self.writeCapability(capability) } } diff --git a/Sources/NIOIMAPCore/Grammar/EncodedSearchQuery.swift b/Sources/NIOIMAPCore/Grammar/EncodedSearchQuery.swift index 237134a60..7612c21be 100644 --- a/Sources/NIOIMAPCore/Grammar/EncodedSearchQuery.swift +++ b/Sources/NIOIMAPCore/Grammar/EncodedSearchQuery.swift @@ -33,8 +33,8 @@ public struct EncodedSearchQuery: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeEncodedSearchQuery(_ ref: EncodedSearchQuery) -> Int { - self.writeEncodedMailboxUIDValidity(ref.mailboxUIDValidity) + - self.writeIfExists(ref.encodedSearch) { search in + self.writeEncodedMailboxUIDValidity(ref.mailboxUIDValidity) + + self.writeIfExists(ref.encodedSearch) { search in self.writeString("?") + self.writeEncodedSearch(search) } } diff --git a/Sources/NIOIMAPCore/Grammar/EntryValue.swift b/Sources/NIOIMAPCore/Grammar/EntryValue.swift index 392d3ff8c..b3880e1f3 100644 --- a/Sources/NIOIMAPCore/Grammar/EntryValue.swift +++ b/Sources/NIOIMAPCore/Grammar/EntryValue.swift @@ -17,12 +17,12 @@ import struct OrderedCollections.OrderedDictionary extension EncodeBuffer { @discardableResult mutating func writeEntry(_ entry: KeyValue) -> Int { - self.writeIMAPString(String(entry.key)) + - self.writeSpace() + - self.writeMetadataValue(entry.value) + self.writeIMAPString(String(entry.key)) + self.writeSpace() + self.writeMetadataValue(entry.value) } - @discardableResult mutating func writeEntryValues(_ array: OrderedDictionary) -> Int { + @discardableResult mutating func writeEntryValues( + _ array: OrderedDictionary + ) -> Int { self.writeOrderedDictionary(array) { element, buffer in buffer.writeEntry(element) } diff --git a/Sources/NIOIMAPCore/Grammar/Envelope.swift b/Sources/NIOIMAPCore/Grammar/Envelope.swift index a294a6373..318304053 100644 --- a/Sources/NIOIMAPCore/Grammar/Envelope.swift +++ b/Sources/NIOIMAPCore/Grammar/Envelope.swift @@ -92,11 +92,10 @@ extension EncodeBuffer { } return - self.writeString("(") + - self.writeArray(addresses, separator: "", parenthesis: false) { (aog, self) -> Int in + self.writeString("(") + + self.writeArray(addresses, separator: "", parenthesis: false) { (aog, self) -> Int in self.writeEmailAddressOrGroup(aog) - } + - self.writeString(")") + } + self.writeString(")") } @discardableResult mutating func writeOptionalMessageID(_ id: MessageID?) -> Int { @@ -107,26 +106,13 @@ extension EncodeBuffer { } @discardableResult mutating func writeEnvelope(_ envelope: Envelope) -> Int { - self.writeString("(") + - self.writeNString(envelope.date?.value) + - self.writeSpace() + - self.writeNString(envelope.subject) + - self.writeSpace() + - self.writeEnvelopeAddresses(envelope.from) + - self.writeSpace() + - self.writeEnvelopeAddresses(envelope.sender) + - self.writeSpace() + - self.writeEnvelopeAddresses(envelope.reply) + - self.writeSpace() + - self.writeEnvelopeAddresses(envelope.to) + - self.writeSpace() + - self.writeEnvelopeAddresses(envelope.cc) + - self.writeSpace() + - self.writeEnvelopeAddresses(envelope.bcc) + - self.writeSpace() + - self.writeOptionalMessageID(envelope.inReplyTo) + - self.writeSpace() + - self.writeOptionalMessageID(envelope.messageID) + - self.writeString(")") + self.writeString("(") + self.writeNString(envelope.date?.value) + self.writeSpace() + + self.writeNString(envelope.subject) + self.writeSpace() + self.writeEnvelopeAddresses(envelope.from) + + self.writeSpace() + self.writeEnvelopeAddresses(envelope.sender) + self.writeSpace() + + self.writeEnvelopeAddresses(envelope.reply) + self.writeSpace() + self.writeEnvelopeAddresses(envelope.to) + + self.writeSpace() + self.writeEnvelopeAddresses(envelope.cc) + self.writeSpace() + + self.writeEnvelopeAddresses(envelope.bcc) + self.writeSpace() + + self.writeOptionalMessageID(envelope.inReplyTo) + self.writeSpace() + + self.writeOptionalMessageID(envelope.messageID) + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Expire.swift b/Sources/NIOIMAPCore/Grammar/Expire.swift index 8838673ba..516301ef1 100644 --- a/Sources/NIOIMAPCore/Grammar/Expire.swift +++ b/Sources/NIOIMAPCore/Grammar/Expire.swift @@ -28,7 +28,6 @@ public struct Expire: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeExpire(_ data: Expire) -> Int { - self.writeString(";EXPIRE=") + - self.writeFullDateTime(data.dateTime) + self.writeString(";EXPIRE=") + self.writeFullDateTime(data.dateTime) } } diff --git a/Sources/NIOIMAPCore/Grammar/FetchAttribute.swift b/Sources/NIOIMAPCore/Grammar/FetchAttribute.swift index 163b50207..cc27a6efd 100644 --- a/Sources/NIOIMAPCore/Grammar/FetchAttribute.swift +++ b/Sources/NIOIMAPCore/Grammar/FetchAttribute.swift @@ -82,7 +82,9 @@ extension Array where Element == FetchAttribute { public static let fast: [Element] = [.flags, .internalDate, .rfc822Size] /// Macro equivalent to `[.flags, .internalDate, .rfc822Size, envelope, body]` - public static let full: [Element] = [.flags, .internalDate, .rfc822Size, .envelope, .bodyStructure(extensions: false)] + public static let full: [Element] = [ + .flags, .internalDate, .rfc822Size, .envelope, .bodyStructure(extensions: false), + ] } // MARK: - Encoding @@ -109,7 +111,9 @@ extension EncodeBuffer { if atts.count == 4, atts.contains(.rfc822Size), atts.contains(.envelope) { return self.writeString("ALL") } - if atts.count == 5, atts.contains(.rfc822Size), atts.contains(.envelope), atts.contains(.bodyStructure(extensions: false)) { + if atts.count == 5, atts.contains(.rfc822Size), atts.contains(.envelope), + atts.contains(.bodyStructure(extensions: false)) + { return self.writeString("FULL") } } @@ -200,26 +204,31 @@ extension EncodeBuffer { self.writeString(extensions ? "BODYSTRUCTURE" : "BODY") } - @discardableResult mutating func writeFetchAttribute_body(peek: Bool, section: SectionSpecifier?, partial: ClosedRange?) -> Int { - self.writeString(peek ? "BODY.PEEK" : "BODY") + - self.writeSection(section) + - self.writeIfExists(partial) { (partial) -> Int in + @discardableResult mutating func writeFetchAttribute_body( + peek: Bool, + section: SectionSpecifier?, + partial: ClosedRange? + ) -> Int { + self.writeString(peek ? "BODY.PEEK" : "BODY") + self.writeSection(section) + + self.writeIfExists(partial) { (partial) -> Int in self.writeByteRange(partial) } } @discardableResult mutating func writeFetchAttribute_binarySize(_ section: SectionSpecifier.Part) -> Int { - self.writeString("BINARY.SIZE") + - self.writeSectionBinary(section) + self.writeString("BINARY.SIZE") + self.writeSectionBinary(section) } - @discardableResult mutating func writeFetchAttribute_binary(peek: Bool, section: SectionSpecifier.Part, partial: ClosedRange?) -> Int { - self.writeString("BINARY") + - self.write(if: peek) { + @discardableResult mutating func writeFetchAttribute_binary( + peek: Bool, + section: SectionSpecifier.Part, + partial: ClosedRange? + ) -> Int { + self.writeString("BINARY") + + self.write(if: peek) { self.writeString(".PEEK") - } + - self.writeSectionBinary(section) + - self.writeIfExists(partial) { (partial) -> Int in + } + self.writeSectionBinary(section) + + self.writeIfExists(partial) { (partial) -> Int in self.writeByteRange(partial) } } diff --git a/Sources/NIOIMAPCore/Grammar/FetchModificationResponse.swift b/Sources/NIOIMAPCore/Grammar/FetchModificationResponse.swift index 0373ba8ba..94f35b746 100644 --- a/Sources/NIOIMAPCore/Grammar/FetchModificationResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/FetchModificationResponse.swift @@ -28,8 +28,7 @@ public struct FetchModificationResponse: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeFetchModificationResponse(_ resp: FetchModificationResponse) -> Int { - self.writeString("MODSEQ (") + - self.writeModificationSequenceValue(resp.modificationSequenceValue) + - self.writeString(")") + self.writeString("MODSEQ (") + self.writeModificationSequenceValue(resp.modificationSequenceValue) + + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/FetchModifier.swift b/Sources/NIOIMAPCore/Grammar/FetchModifier.swift index 67b29bc1f..8a283781e 100644 --- a/Sources/NIOIMAPCore/Grammar/FetchModifier.swift +++ b/Sources/NIOIMAPCore/Grammar/FetchModifier.swift @@ -45,8 +45,8 @@ extension EncodeBuffer { if a.isEmpty { return 0 } - return self.writeSpace() + - self.writeArray(a) { (modifier, self) in + return self.writeSpace() + + self.writeArray(a) { (modifier, self) in self.writeFetchModifier(modifier) } } diff --git a/Sources/NIOIMAPCore/Grammar/Flag/FlagKeyword.swift b/Sources/NIOIMAPCore/Grammar/Flag/FlagKeyword.swift index 58e4f55e1..649c6bbb8 100644 --- a/Sources/NIOIMAPCore/Grammar/Flag/FlagKeyword.swift +++ b/Sources/NIOIMAPCore/Grammar/Flag/FlagKeyword.swift @@ -48,9 +48,11 @@ extension Flag { init(unchecked string: String) { /// RFC 3501 defines `flag-keyword` as `atom`, /// but Gmail sends flags with `[` and `]` in them. - assert(string.utf8.allSatisfy { (c) -> Bool in - c.isAtomChar || c.isResponseSpecial - }) + assert( + string.utf8.allSatisfy { (c) -> Bool in + c.isAtomChar || c.isResponseSpecial + } + ) self.rawValue = string } diff --git a/Sources/NIOIMAPCore/Grammar/Flag/StoreFlags.swift b/Sources/NIOIMAPCore/Grammar/Flag/StoreFlags.swift index 2f843c0e3..e8cf7e2e1 100644 --- a/Sources/NIOIMAPCore/Grammar/Flag/StoreFlags.swift +++ b/Sources/NIOIMAPCore/Grammar/Flag/StoreFlags.swift @@ -108,8 +108,7 @@ extension EncodeBuffer { @discardableResult mutating func writeStoreAttributeFlags(_ flags: StoreFlags) -> Int { let silentString = flags.silent ? ".SILENT" : "" return - self.writeString("\(flags.operation.rawValue)FLAGS\(silentString) ") + - self.writeFlags(flags.flags) + self.writeString("\(flags.operation.rawValue)FLAGS\(silentString) ") + self.writeFlags(flags.flags) } @discardableResult mutating func writeStoreData(_ data: StoreData) -> Int { @@ -124,7 +123,7 @@ extension EncodeBuffer { @discardableResult mutating func writeStoreGmailLabels(_ labels: StoreGmailLabels) -> Int { let silentString = labels.silent ? ".SILENT" : "" return - self.writeString("\(labels.operation.rawValue)X-GM-LABELS\(silentString) ") + - self.writeGmailLabels(labels.gmailLabels) + self.writeString("\(labels.operation.rawValue)X-GM-LABELS\(silentString) ") + + self.writeGmailLabels(labels.gmailLabels) } } diff --git a/Sources/NIOIMAPCore/Grammar/FullAuthenticatedURL.swift b/Sources/NIOIMAPCore/Grammar/FullAuthenticatedURL.swift index d5bbf6e23..056e454df 100644 --- a/Sources/NIOIMAPCore/Grammar/FullAuthenticatedURL.swift +++ b/Sources/NIOIMAPCore/Grammar/FullAuthenticatedURL.swift @@ -35,7 +35,6 @@ public struct FullAuthenticatedURL: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeAuthIMAPURLFull(_ data: FullAuthenticatedURL) -> Int { - self.writeAuthenticatedURL(data.networkMessagePath) + - self.writeIAuthenticatedURL(data.authenticatedURL) + self.writeAuthenticatedURL(data.networkMessagePath) + self.writeIAuthenticatedURL(data.authenticatedURL) } } diff --git a/Sources/NIOIMAPCore/Grammar/FullDateTime.swift b/Sources/NIOIMAPCore/Grammar/FullDateTime.swift index d0a094fbf..2d4bc00b0 100644 --- a/Sources/NIOIMAPCore/Grammar/FullDateTime.swift +++ b/Sources/NIOIMAPCore/Grammar/FullDateTime.swift @@ -86,9 +86,7 @@ public struct FullTime: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeFullDateTime(_ data: FullDateTime) -> Int { - self.writeFullDate(data.date) + - self.writeString("T") + - self.writeFullTime(data.time) + self.writeFullDate(data.date) + self.writeString("T") + self.writeFullTime(data.time) } @discardableResult mutating func writeFullDate(_ data: FullDate) -> Int { @@ -102,18 +100,17 @@ extension EncodeBuffer { let hour = self.padInteger(data.hour, minimum: 2) let minute = self.padInteger(data.minute, minimum: 2) let second = self.padInteger(data.second, minimum: 2) - return self.writeString("\(hour):\(minute):\(second)") + - self.writeIfExists(data.fraction) { fraction in + return self.writeString("\(hour):\(minute):\(second)") + + self.writeIfExists(data.fraction) { fraction in self.writeString(".\(fraction)") } } func padInteger(_ int: Int, minimum: Int) -> String { let short = "\(int)" - if short.count < minimum { - return String(repeating: "0", count: (minimum - short.count)) + short - } else { + guard short.count < minimum else { return short } + return String(repeating: "0", count: (minimum - short.count)) + short } } diff --git a/Sources/NIOIMAPCore/Grammar/ID/ID.swift b/Sources/NIOIMAPCore/Grammar/ID/ID.swift index 5f7e1c19e..ad9101650 100644 --- a/Sources/NIOIMAPCore/Grammar/ID/ID.swift +++ b/Sources/NIOIMAPCore/Grammar/ID/ID.swift @@ -24,17 +24,14 @@ extension EncodeBuffer { } return self.withoutLoggingMode { buffer in buffer.writeOrderedDictionary(values) { (e, buffer) in - buffer.writeIMAPString(e.key) + - buffer.writeSpace() + - buffer.writeNString(e.value) + buffer.writeIMAPString(e.key) + buffer.writeSpace() + buffer.writeNString(e.value) } } } @discardableResult mutating func writeIDResponse(_ response: OrderedDictionary) -> Int { self.withoutLoggingMode { - $0.writeString("ID ") + - $0.writeIDParameters(response) + $0.writeString("ID ") + $0.writeIDParameters(response) } } } diff --git a/Sources/NIOIMAPCore/Grammar/IMAPServer.swift b/Sources/NIOIMAPCore/Grammar/IMAPServer.swift index 1286aa391..253b9b4f9 100644 --- a/Sources/NIOIMAPCore/Grammar/IMAPServer.swift +++ b/Sources/NIOIMAPCore/Grammar/IMAPServer.swift @@ -43,9 +43,8 @@ extension EncodeBuffer { @discardableResult mutating func writeIMAPServer(_ server: IMAPServer) -> Int { self.writeIfExists(server.userAuthenticationMechanism) { authMechanism in self.writeUserAuthenticationMechanism(authMechanism) + self.writeString("@") - } + - self.writeString("\(server.host)") + - self.writeIfExists(server.port) { port in + } + self.writeString("\(server.host)") + + self.writeIfExists(server.port) { port in self.writeString(":\(port)") } } diff --git a/Sources/NIOIMAPCore/Grammar/IMAPURL.swift b/Sources/NIOIMAPCore/Grammar/IMAPURL.swift index ff461378b..724f99ddf 100644 --- a/Sources/NIOIMAPCore/Grammar/IMAPURL.swift +++ b/Sources/NIOIMAPCore/Grammar/IMAPURL.swift @@ -33,10 +33,8 @@ public struct IMAPURL: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeIMAPURL(_ url: IMAPURL) -> Int { - self.writeString("imap://") + - self.writeIMAPServer(url.server) + - self.writeString("/") + - self.writeIfExists(url.command) { command in + self.writeString("imap://") + self.writeIMAPServer(url.server) + self.writeString("/") + + self.writeIfExists(url.command) { command in self.writeURLCommand(command) } } diff --git a/Sources/NIOIMAPCore/Grammar/IMAPURLAuthenticationMechanism.swift b/Sources/NIOIMAPCore/Grammar/IMAPURLAuthenticationMechanism.swift index b4b71732e..42f845725 100644 --- a/Sources/NIOIMAPCore/Grammar/IMAPURLAuthenticationMechanism.swift +++ b/Sources/NIOIMAPCore/Grammar/IMAPURLAuthenticationMechanism.swift @@ -24,7 +24,8 @@ public enum IMAPURLAuthenticationMechanism: Hashable, Sendable { // MARK: - Encoding extension EncodeBuffer { - @discardableResult mutating func writeIMAPURLAuthenticationMechanism(_ data: IMAPURLAuthenticationMechanism) -> Int { + @discardableResult mutating func writeIMAPURLAuthenticationMechanism(_ data: IMAPURLAuthenticationMechanism) -> Int + { switch data { case .any: return self.writeString(";AUTH=*") diff --git a/Sources/NIOIMAPCore/Grammar/InitialResponse.swift b/Sources/NIOIMAPCore/Grammar/InitialResponse.swift index bd4590026..113a0e949 100644 --- a/Sources/NIOIMAPCore/Grammar/InitialResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/InitialResponse.swift @@ -34,11 +34,10 @@ public struct InitialResponse: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeInitialResponse(_ resp: InitialResponse) -> Int { - if resp.data.readableBytes == 0 { - return self.writeString("=") - } else { + guard resp.data.readableBytes == 0 else { let encoded = Base64.encodeBytes(bytes: resp.data.readableBytesView) return self.writeBytes(encoded) } + return self.writeString("=") } } diff --git a/Sources/NIOIMAPCore/Grammar/List/ListReturnOps.swift b/Sources/NIOIMAPCore/Grammar/List/ListReturnOps.swift index e05b88c34..c953f12d7 100644 --- a/Sources/NIOIMAPCore/Grammar/List/ListReturnOps.swift +++ b/Sources/NIOIMAPCore/Grammar/List/ListReturnOps.swift @@ -18,13 +18,11 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeListReturnOptions(_ options: [ReturnOption]) -> Int { - self.writeString("RETURN ") + - self.writeString("(") + - self.write(if: options.count >= 1) { + self.writeString("RETURN ") + self.writeString("(") + + self.write(if: options.count >= 1) { self.writeArray(options, parenthesis: false) { (option, self) in self.writeReturnOption(option) } - } + - self.writeString(")") + } + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/List/ListSelectBaseOption.swift b/Sources/NIOIMAPCore/Grammar/List/ListSelectBaseOption.swift index 49705cafd..16d5d9474 100644 --- a/Sources/NIOIMAPCore/Grammar/List/ListSelectBaseOption.swift +++ b/Sources/NIOIMAPCore/Grammar/List/ListSelectBaseOption.swift @@ -36,8 +36,6 @@ extension EncodeBuffer { } @discardableResult mutating func writeListSelectBaseOptionQuoted(_ option: ListSelectBaseOption) -> Int { - self.writeString("\"") + - self.writeListSelectBaseOption(option) + - self.writeString("\"") + self.writeString("\"") + self.writeListSelectBaseOption(option) + self.writeString("\"") } } diff --git a/Sources/NIOIMAPCore/Grammar/List/ListSelectOption.swift b/Sources/NIOIMAPCore/Grammar/List/ListSelectOption.swift index 1545c0f13..a405122c2 100644 --- a/Sources/NIOIMAPCore/Grammar/List/ListSelectOption.swift +++ b/Sources/NIOIMAPCore/Grammar/List/ListSelectOption.swift @@ -71,14 +71,11 @@ extension EncodeBuffer { } @discardableResult mutating func writeListSelectOptions(_ options: ListSelectOptions?) -> Int { - self.writeString("(") + - self.writeIfExists(options) { (optionsData) -> Int in + self.writeString("(") + + self.writeIfExists(options) { (optionsData) -> Int in self.writeArray(optionsData.options, separator: "", parenthesis: false) { (option, self) -> Int in - self.writeListSelectOption(option) + - self.writeSpace() - } + - self.writeListSelectBaseOption(optionsData.baseOption) - } + - self.writeString(")") + self.writeListSelectOption(option) + self.writeSpace() + } + self.writeListSelectBaseOption(optionsData.baseOption) + } + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxAttribute.swift b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxAttribute.swift index a9a73f2de..297487b2e 100644 --- a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxAttribute.swift +++ b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxAttribute.swift @@ -129,8 +129,8 @@ extension EncodeBuffer { } @discardableResult mutating func writeMailboxOptions(_ option: [MailboxAttribute]) -> Int { - self.writeString("STATUS ") + - self.writeArray(option) { (att, self) in + self.writeString("STATUS ") + + self.writeArray(option) { (att, self) in self.writeMailboxAttribute(att) } } diff --git a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxData.swift b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxData.swift index 1329c4bce..bdb3a17fb 100644 --- a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxData.swift +++ b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxData.swift @@ -70,15 +70,12 @@ extension MailboxData { extension EncodeBuffer { @discardableResult mutating func writeMailboxDataSearchSort(_ data: MailboxData.SearchSort?) -> Int { - self.writeString("SEARCH") + - self.writeIfExists(data) { (data) -> Int in + self.writeString("SEARCH") + + self.writeIfExists(data) { (data) -> Int in self.writeArray(data.identifiers, prefix: " ", parenthesis: false) { (element, buffer) -> Int in buffer.writeString("\(element)") - } + - self.writeSpace() + - self.writeString("(MODSEQ ") + - self.writeModificationSequenceValue(data.modificationSequence) + - self.writeString(")") + } + self.writeSpace() + self.writeString("(MODSEQ ") + + self.writeModificationSequenceValue(data.modificationSequence) + self.writeString(")") } } @@ -107,38 +104,33 @@ extension EncodeBuffer { } } - private mutating func writeMailboxData_search(_ list: [UnknownMessageIdentifier], modificationSequence: ModificationSequenceValue?) -> Int { - self.writeString("SEARCH") + - self.writeArray(list, prefix: " ", separator: " ", parenthesis: false) { (id, buffer) -> Int in + private mutating func writeMailboxData_search( + _ list: [UnknownMessageIdentifier], + modificationSequence: ModificationSequenceValue? + ) -> Int { + self.writeString("SEARCH") + + self.writeArray(list, prefix: " ", separator: " ", parenthesis: false) { (id, buffer) -> Int in buffer.writeMessageIdentifier(id) - } + - self.writeIfExists(modificationSequence) { value -> Int in - self.writeString(" (MODSEQ ") + - self.writeModificationSequenceValue(value) + - self.writeString(")") + } + + self.writeIfExists(modificationSequence) { value -> Int in + self.writeString(" (MODSEQ ") + self.writeModificationSequenceValue(value) + self.writeString(")") } } private mutating func writeMailboxData_flags(_ flags: [Flag]) -> Int { - self.writeString("FLAGS ") + - self.writeFlags(flags) + self.writeString("FLAGS ") + self.writeFlags(flags) } private mutating func writeMailboxData_list(_ list: MailboxInfo) -> Int { - self.writeString("LIST ") + - self.writeMailboxInfo(list) + self.writeString("LIST ") + self.writeMailboxInfo(list) } private mutating func writeMailboxData_lsub(_ list: MailboxInfo) -> Int { - self.writeString("LSUB ") + - self.writeMailboxInfo(list) + self.writeString("LSUB ") + self.writeMailboxInfo(list) } private mutating func writeMailboxData_status(mailbox: MailboxName, status: MailboxStatus) -> Int { - self.writeString("STATUS ") + - self.writeMailbox(mailbox) + - self.writeString(" (") + - self.writeMailboxStatus(status) + - self.writeString(")") + self.writeString("STATUS ") + self.writeMailbox(mailbox) + self.writeString(" (") + + self.writeMailboxStatus(status) + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxGroup.swift b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxGroup.swift index f905ab9c8..3d15b1a88 100644 --- a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxGroup.swift +++ b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxGroup.swift @@ -49,22 +49,25 @@ public indirect enum EmailAddressListElement: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeEmailAddressGroup(_ group: EmailAddressGroup) -> Int { - self.writeEmailAddress(.init( - personName: nil, - sourceRoot: group.sourceRoot, - mailbox: group.groupName, - host: nil - ) - ) + - self.writeArray(group.children, prefix: "", separator: "", suffix: "", parenthesis: false) { (child, self) in - self.writeEmailAddressOrGroup(child) - } + - self.writeEmailAddress(.init( + self.writeEmailAddress( + .init( personName: nil, sourceRoot: group.sourceRoot, - mailbox: nil, + mailbox: group.groupName, host: nil ) + ) + + self.writeArray(group.children, prefix: "", separator: "", suffix: "", parenthesis: false) { + (child, self) in + self.writeEmailAddressOrGroup(child) + } + + self.writeEmailAddress( + .init( + personName: nil, + sourceRoot: group.sourceRoot, + mailbox: nil, + host: nil + ) ) } diff --git a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxInfo.swift b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxInfo.swift index d7c492f63..4c1697a24 100644 --- a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxInfo.swift +++ b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxInfo.swift @@ -30,7 +30,11 @@ public struct MailboxInfo: Hashable, Sendable { /// - parameter attributes: An array of mailbox attributes. /// - parameter path: The mailbox path. /// - parameter extensions: A catch-all to support any attributes added in future extensions. - public init(attributes: [Attribute] = [], path: MailboxPath, extensions: OrderedDictionary) { + public init( + attributes: [Attribute] = [], + path: MailboxPath, + extensions: OrderedDictionary + ) { self.attributes = attributes self.path = path self.extensions = extensions @@ -111,14 +115,11 @@ extension EncodeBuffer { } @discardableResult mutating func writeMailboxInfo(_ list: MailboxInfo) -> Int { - self.writeString("(") + - self.writeIfExists(list.attributes) { (flags) -> Int in + self.writeString("(") + + self.writeIfExists(list.attributes) { (flags) -> Int in self.writeMailboxListFlags(flags) - } + - self.writeString(") ") + - self.writeMailboxPathSeparator(list.path.pathSeparator) + - self.writeSpace() + - self.writeMailbox(list.path.name) + } + self.writeString(") ") + self.writeMailboxPathSeparator(list.path.pathSeparator) + self.writeSpace() + + self.writeMailbox(list.path.name) } @discardableResult mutating func writeMailboxListFlags(_ flags: [MailboxInfo.Attribute]) -> Int { diff --git a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxName.swift b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxName.swift index 67a62664b..14570ab45 100644 --- a/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxName.swift +++ b/Sources/NIOIMAPCore/Grammar/Mailbox/MailboxName.swift @@ -211,9 +211,8 @@ public struct MailboxName: Sendable { /// `true` if the internal storage reads "INBOX" /// otherwise `false` public var isInbox: Bool { - self.hashValue == MailboxName.inboxHashValue && - self.bytes.count == 5 && - self.bytes.map { $0 & 0xDF }.elementsEqual("INBOX".utf8) + self.hashValue == MailboxName.inboxHashValue && self.bytes.count == 5 + && self.bytes.map { $0 & 0xDF }.elementsEqual("INBOX".utf8) } private static let inboxHashValue: Int = MailboxName.inbox.hashValue diff --git a/Sources/NIOIMAPCore/Grammar/MailboxUIDValidity.swift b/Sources/NIOIMAPCore/Grammar/MailboxUIDValidity.swift index 86fcc649a..9d2c0bc3a 100644 --- a/Sources/NIOIMAPCore/Grammar/MailboxUIDValidity.swift +++ b/Sources/NIOIMAPCore/Grammar/MailboxUIDValidity.swift @@ -34,8 +34,8 @@ public struct MailboxUIDValidity: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeEncodedMailboxUIDValidity(_ ref: MailboxUIDValidity) -> Int { - self.writeEncodedMailbox(ref.encodedMailbox) + - self.writeIfExists(ref.uidValidity) { value in + self.writeEncodedMailbox(ref.encodedMailbox) + + self.writeIfExists(ref.uidValidity) { value in self.writeString(";UIDVALIDITY=") + self.writeUIDValidity(value) } } diff --git a/Sources/NIOIMAPCore/Grammar/MechanismBase64.swift b/Sources/NIOIMAPCore/Grammar/MechanismBase64.swift index e9eb565c3..c620dd69c 100644 --- a/Sources/NIOIMAPCore/Grammar/MechanismBase64.swift +++ b/Sources/NIOIMAPCore/Grammar/MechanismBase64.swift @@ -35,10 +35,9 @@ public struct MechanismBase64: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeMechanismBase64(_ data: MechanismBase64) -> Int { - self.writeURLAuthenticationMechanism(data.mechanism) + - self.writeIfExists(data.base64) { base64 in - self.writeString("=") + - self.writeBuffer(&base64) + self.writeURLAuthenticationMechanism(data.mechanism) + + self.writeIfExists(data.base64) { base64 in + self.writeString("=") + self.writeBuffer(&base64) } } } diff --git a/Sources/NIOIMAPCore/Grammar/Media/Media.swift b/Sources/NIOIMAPCore/Grammar/Media/Media.swift index e2605c3b8..72628aca8 100644 --- a/Sources/NIOIMAPCore/Grammar/Media/Media.swift +++ b/Sources/NIOIMAPCore/Grammar/Media/Media.swift @@ -163,9 +163,7 @@ extension BodyStructure { extension EncodeBuffer { @discardableResult mutating func writeMediaType(_ media: Media.MediaType) -> Int { - self.writeMediaTopLevelType(media.topLevel) + - self.writeSpace() + - self.writeMediaSubtype(media.sub) + self.writeMediaTopLevelType(media.topLevel) + self.writeSpace() + self.writeMediaSubtype(media.sub) } @discardableResult mutating func writeMediaTopLevelType(_ type: Media.TopLevelType) -> Int { @@ -173,8 +171,6 @@ extension EncodeBuffer { } @discardableResult mutating func writeMediaSubtype(_ type: Media.Subtype) -> Int { - self.writeString("\"") + - self.writeString(String(type).uppercased()) + - self.writeString("\"") + self.writeString("\"") + self.writeString(String(type).uppercased()) + self.writeString("\"") } } diff --git a/Sources/NIOIMAPCore/Grammar/Message/GmailLabel.swift b/Sources/NIOIMAPCore/Grammar/Message/GmailLabel.swift index fffbb97e4..41da9b3c3 100644 --- a/Sources/NIOIMAPCore/Grammar/Message/GmailLabel.swift +++ b/Sources/NIOIMAPCore/Grammar/Message/GmailLabel.swift @@ -56,11 +56,10 @@ extension EncodeBuffer { } @discardableResult mutating func writeGmailLabel(_ label: GmailLabel) -> Int { - if label.buffer.getInteger(at: label.buffer.readerIndex) == UInt8(ascii: "\\") { - var stringValue = label.buffer - return self.writeBuffer(&stringValue) - } else { + guard label.buffer.getInteger(at: label.buffer.readerIndex) == UInt8(ascii: "\\") else { return self.writeIMAPString(label.buffer) } + var stringValue = label.buffer + return self.writeBuffer(&stringValue) } } diff --git a/Sources/NIOIMAPCore/Grammar/Message/MessageAttributes.swift b/Sources/NIOIMAPCore/Grammar/Message/MessageAttributes.swift index 3950b186e..55b0fde0e 100644 --- a/Sources/NIOIMAPCore/Grammar/Message/MessageAttributes.swift +++ b/Sources/NIOIMAPCore/Grammar/Message/MessageAttributes.swift @@ -114,78 +114,70 @@ extension EncodeBuffer { } } - @discardableResult mutating func writeMessageAttribute_binarySize(section: SectionSpecifier.Part, number: Int) -> Int { - self.writeString("BINARY.SIZE") + - self.writeSectionBinary(section) + - self.writeString(" \(number)") + @discardableResult mutating func writeMessageAttribute_binarySize( + section: SectionSpecifier.Part, + number: Int + ) -> Int { + self.writeString("BINARY.SIZE") + self.writeSectionBinary(section) + self.writeString(" \(number)") } @discardableResult mutating func writeMessageAttributeFlags(_ atts: [Flag]) -> Int { - self.writeString("FLAGS ") + - self.writeArray(atts) { (element, self) in + self.writeString("FLAGS ") + + self.writeArray(atts) { (element, self) in self.writeFlag(element) } } @discardableResult mutating func writeMessageAttributeNilBody(_ kind: StreamingKind) -> Int { - self.writeStreamingKind(kind) + - self.writeSpace() + - self.writeNil() + self.writeStreamingKind(kind) + self.writeSpace() + self.writeNil() } @discardableResult mutating func writeMessageAttribute_envelope(_ env: Envelope) -> Int { - self.writeString("ENVELOPE ") + - self.writeEnvelope(env) + self.writeString("ENVELOPE ") + self.writeEnvelope(env) } @discardableResult mutating func writeMessageAttribute_internalDate(_ date: ServerMessageDate) -> Int { - self.writeString("INTERNALDATE ") + - self.writeInternalDate(date) + self.writeString("INTERNALDATE ") + self.writeInternalDate(date) } @discardableResult mutating func writeMessageAttribute_rfc822(_ string: ByteBuffer?) -> Int { - self.writeString("RFC822") + - self.writeSpace() + - self.writeNString(string) + self.writeString("RFC822") + self.writeSpace() + self.writeNString(string) } @discardableResult mutating func writeMessageAttribute_rfc822Text(_ string: ByteBuffer?) -> Int { - self.writeString("RFC822.TEXT") + - self.writeSpace() + - self.writeNString(string) + self.writeString("RFC822.TEXT") + self.writeSpace() + self.writeNString(string) } @discardableResult mutating func writeMessageAttribute_rfc822Header(_ string: ByteBuffer?) -> Int { - self.writeString("RFC822.HEADER") + - self.writeSpace() + - self.writeNString(string) + self.writeString("RFC822.HEADER") + self.writeSpace() + self.writeNString(string) } - @discardableResult mutating func writeMessageAttribute_body(_ body: MessageAttribute.BodyStructure, hasExtensionData: Bool) -> Int { - self.writeString("BODY") + - self.write(if: hasExtensionData) { () -> Int in + @discardableResult mutating func writeMessageAttribute_body( + _ body: MessageAttribute.BodyStructure, + hasExtensionData: Bool + ) -> Int { + self.writeString("BODY") + + self.write(if: hasExtensionData) { () -> Int in self.writeString("STRUCTURE") - } + - self.writeSpace() + - self.writeBody(body) + } + self.writeSpace() + self.writeBody(body) } - @discardableResult mutating func writeMessageAttribute_bodySection(_ section: SectionSpecifier?, number: Int?, string: ByteBuffer?) -> Int { - self.writeString("BODY") + - self.writeSection(section) + - self.writeIfExists(number) { (number) -> Int in + @discardableResult mutating func writeMessageAttribute_bodySection( + _ section: SectionSpecifier?, + number: Int?, + string: ByteBuffer? + ) -> Int { + self.writeString("BODY") + self.writeSection(section) + + self.writeIfExists(number) { (number) -> Int in self.writeString("<\(number)>") - } + - self.writeSpace() + - self.writeNString(string) + } + self.writeSpace() + self.writeNString(string) } @discardableResult mutating func writeMessageAttribute_bodySectionText(number: Int?, size: Int) -> Int { - self.writeString("BODY[TEXT]") + - self.writeIfExists(number) { (number) -> Int in + self.writeString("BODY[TEXT]") + + self.writeIfExists(number) { (number) -> Int in self.writeString("<\(number)>") - } + - self.writeString(" {\(size)}\r\n") + } + self.writeString(" {\(size)}\r\n") } @discardableResult mutating func writeMessageAttribute_gmailMessageID(_ id: UInt64) -> Int { @@ -197,16 +189,13 @@ extension EncodeBuffer { } @discardableResult mutating func writeMessageAttribute_gmailLabels(_ labels: [GmailLabel]) -> Int { - self.writeString("X-GM-LABELS") + - self.writeSpace() + - self.writeArray(labels) { label, buffer in + self.writeString("X-GM-LABELS") + self.writeSpace() + + self.writeArray(labels) { label, buffer in buffer.writeGmailLabel(label) } } @discardableResult mutating func writeMessageAttribute_preview(_ previewText: PreviewText?) -> Int { - self.writeString("PREVIEW") + - self.writeSpace() + - self.writeNString(previewText.map { String($0) }) + self.writeString("PREVIEW") + self.writeSpace() + self.writeNString(previewText.map { String($0) }) } } diff --git a/Sources/NIOIMAPCore/Grammar/Message/MessageData.swift b/Sources/NIOIMAPCore/Grammar/Message/MessageData.swift index f66c3996a..c18f8eacb 100644 --- a/Sources/NIOIMAPCore/Grammar/Message/MessageData.swift +++ b/Sources/NIOIMAPCore/Grammar/Message/MessageData.swift @@ -51,13 +51,13 @@ extension EncodeBuffer { case .vanishedEarlier(let set): return self.writeString("VANISHED (EARLIER) ") + self.writeUIDSet(set) case .generateAuthorizedURL(let array): - return self.writeString("GENURLAUTH") + - self.writeArray(array, prefix: " ", parenthesis: false) { data, buffer in + return self.writeString("GENURLAUTH") + + self.writeArray(array, prefix: " ", parenthesis: false) { data, buffer in buffer.writeIMAPString(data) } case .urlFetch(let array): - return self.writeString("URLFETCH") + - self.writeArray(array) { data, buffer in + return self.writeString("URLFETCH") + + self.writeArray(array) { data, buffer in buffer.writeURLFetchData(data) } } diff --git a/Sources/NIOIMAPCore/Grammar/MessagePath.swift b/Sources/NIOIMAPCore/Grammar/MessagePath.swift index a7499a78e..5a82894aa 100644 --- a/Sources/NIOIMAPCore/Grammar/MessagePath.swift +++ b/Sources/NIOIMAPCore/Grammar/MessagePath.swift @@ -31,7 +31,12 @@ public struct MessagePath: Hashable, Sendable { /// - parameter iUID: The UID of the message in question. /// - parameter IMAPURLSection: An optional section of the message in question. /// - parameter iPartial: A specific range of bytes of the message/section in question. - public init(mailboxReference: MailboxUIDValidity, iUID: IUID, section: URLMessageSection? = nil, range: MessagePath.ByteRange? = nil) { + public init( + mailboxReference: MailboxUIDValidity, + iUID: IUID, + section: URLMessageSection? = nil, + range: MessagePath.ByteRange? = nil + ) { self.mailboxReference = mailboxReference self.iUID = iUID self.section = section @@ -43,12 +48,11 @@ public struct MessagePath: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeMessagePath(_ data: MessagePath) -> Int { - self.writeEncodedMailboxUIDValidity(data.mailboxReference) + - self.writeIUID(data.iUID) + - self.writeIfExists(data.section) { section in + self.writeEncodedMailboxUIDValidity(data.mailboxReference) + self.writeIUID(data.iUID) + + self.writeIfExists(data.section) { section in self.writeURLMessageSection(section) - } + - self.writeIfExists(data.range) { partial in + } + + self.writeIfExists(data.range) { partial in self.writeMessagePathByteRange(partial) } } diff --git a/Sources/NIOIMAPCore/Grammar/MetadataResponse.swift b/Sources/NIOIMAPCore/Grammar/MetadataResponse.swift index 28ff910e1..15804582d 100644 --- a/Sources/NIOIMAPCore/Grammar/MetadataResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/MetadataResponse.swift @@ -30,15 +30,11 @@ extension EncodeBuffer { @discardableResult mutating func writeMetadataResponse(_ resp: MetadataResponse) -> Int { switch resp { case .values(values: let values, mailbox: let mailbox): - return self.writeString("METADATA ") + - self.writeMailbox(mailbox) + - self.writeSpace() + - self.writeEntryValues(values) + return self.writeString("METADATA ") + self.writeMailbox(mailbox) + self.writeSpace() + + self.writeEntryValues(values) case .list(list: let list, mailbox: let mailbox): - return self.writeString("METADATA ") + - self.writeMailbox(mailbox) + - self.writeSpace() + - self.writeEntryList(list) + return self.writeString("METADATA ") + self.writeMailbox(mailbox) + self.writeSpace() + + self.writeEntryList(list) } } } diff --git a/Sources/NIOIMAPCore/Grammar/NString.swift b/Sources/NIOIMAPCore/Grammar/NString.swift index df4697780..53f3a9448 100644 --- a/Sources/NIOIMAPCore/Grammar/NString.swift +++ b/Sources/NIOIMAPCore/Grammar/NString.swift @@ -18,18 +18,16 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeNString(_ string: ByteBuffer?) -> Int { - if let string = string { - return self.writeIMAPString(string) - } else { + guard let string = string else { return self.writeNil() } + return self.writeIMAPString(string) } @discardableResult mutating func writeNString(_ string: String?) -> Int { - if let string = string { - return self.writeIMAPString(string) - } else { + guard let string = string else { return self.writeNil() } + return self.writeIMAPString(string) } } diff --git a/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceDescription.swift b/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceDescription.swift index 047998e30..5e9c6348f 100644 --- a/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceDescription.swift +++ b/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceDescription.swift @@ -31,7 +31,11 @@ public struct NamespaceDescription: Hashable, Sendable { /// - parameter string: The full namespace string. /// - parameter char: A hierarchy delimiter. /// - parameter responseExtensions: A catch-all to provide support fo future extensions. - public init(string: ByteBuffer, char: Character? = nil, responseExtensions: OrderedDictionary) { + public init( + string: ByteBuffer, + char: Character? = nil, + responseExtensions: OrderedDictionary + ) { self.string = string self.delimiter = char self.responseExtensions = responseExtensions diff --git a/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponse.swift b/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponse.swift index a50d21bca..155cf231e 100644 --- a/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponse.swift @@ -30,7 +30,11 @@ public struct NamespaceResponse: Hashable, Sendable { /// - parameter userNamespace: Descriptions of the current user's namespaces. /// - parameter otherUserNamespace: Descriptions of other user's namespaces. /// - parameter sharedNamespace: Descriptions of shared namespaces. - public init(userNamespace: [NamespaceDescription], otherUserNamespace: [NamespaceDescription], sharedNamespace: [NamespaceDescription]) { + public init( + userNamespace: [NamespaceDescription], + otherUserNamespace: [NamespaceDescription], + sharedNamespace: [NamespaceDescription] + ) { self.userNamespace = userNamespace self.otherUserNamespace = otherUserNamespace self.sharedNamespace = sharedNamespace @@ -41,11 +45,8 @@ public struct NamespaceResponse: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeNamespaceResponse(_ response: NamespaceResponse) -> Int { - self.writeString("NAMESPACE ") + - self.writeNamespace(response.userNamespace) + - self.writeSpace() + - self.writeNamespace(response.otherUserNamespace) + - self.writeSpace() + - self.writeNamespace(response.sharedNamespace) + self.writeString("NAMESPACE ") + self.writeNamespace(response.userNamespace) + self.writeSpace() + + self.writeNamespace(response.otherUserNamespace) + self.writeSpace() + + self.writeNamespace(response.sharedNamespace) } } diff --git a/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponseExtension.swift b/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponseExtension.swift index 5c2598cf5..11f18874e 100644 --- a/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponseExtension.swift +++ b/Sources/NIOIMAPCore/Grammar/Namespace/NamespaceResponseExtension.swift @@ -18,12 +18,13 @@ import struct OrderedCollections.OrderedDictionary // MARK: - Encoding extension EncodeBuffer { - @discardableResult mutating func writeNamespaceResponseExtensions(_ extensions: OrderedDictionary) -> Int { + @discardableResult mutating func writeNamespaceResponseExtensions( + _ extensions: OrderedDictionary + ) -> Int { extensions.reduce(into: 0) { (res, ext) in - res += self.writeSpace() + - self.writeIMAPString(ext.0) + - self.writeSpace() + - self.writeArray(ext.1) { (string, self) in + res += + self.writeSpace() + self.writeIMAPString(ext.0) + self.writeSpace() + + self.writeArray(ext.1) { (string, self) in self.writeIMAPString(string) } } diff --git a/Sources/NIOIMAPCore/Grammar/NetworkMessagePath.swift b/Sources/NIOIMAPCore/Grammar/NetworkMessagePath.swift index d2bc09d95..71cc08cba 100644 --- a/Sources/NIOIMAPCore/Grammar/NetworkMessagePath.swift +++ b/Sources/NIOIMAPCore/Grammar/NetworkMessagePath.swift @@ -35,9 +35,7 @@ public struct NetworkMessagePath: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeAuthenticatedURL(_ data: NetworkMessagePath) -> Int { - self.writeString("imap://") + - self.writeIMAPServer(data.server) + - self.writeString("/") + - self.writeMessagePath(data.messagePath) + self.writeString("imap://") + self.writeIMAPServer(data.server) + self.writeString("/") + + self.writeMessagePath(data.messagePath) } } diff --git a/Sources/NIOIMAPCore/Grammar/NetworkPath.swift b/Sources/NIOIMAPCore/Grammar/NetworkPath.swift index f0551e97e..7fd48fbfe 100644 --- a/Sources/NIOIMAPCore/Grammar/NetworkPath.swift +++ b/Sources/NIOIMAPCore/Grammar/NetworkPath.swift @@ -33,10 +33,8 @@ public struct NetworkPath: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeNetworkPath(_ path: NetworkPath) -> Int { - self.writeString("//") + - self.writeIMAPServer(path.server) + - self.writeString("/") + - self.writeIfExists(path.command) { command in + self.writeString("//") + self.writeIMAPServer(path.server) + self.writeString("/") + + self.writeIfExists(path.command) { command in self.writeURLCommand(command) } } diff --git a/Sources/NIOIMAPCore/Grammar/Option/OptionExtension.swift b/Sources/NIOIMAPCore/Grammar/Option/OptionExtension.swift index eb0fbd4fe..27b7eeb5c 100644 --- a/Sources/NIOIMAPCore/Grammar/Option/OptionExtension.swift +++ b/Sources/NIOIMAPCore/Grammar/Option/OptionExtension.swift @@ -26,7 +26,9 @@ public enum OptionExtensionKind: Hashable, Sendable { // MARK: - Encoding extension EncodeBuffer { - @discardableResult mutating func writeOptionExtension(_ option: KeyValue) -> Int { + @discardableResult mutating func writeOptionExtension( + _ option: KeyValue + ) -> Int { var size = 0 switch option.key { case .standard(let atom): @@ -43,8 +45,6 @@ extension EncodeBuffer { } @discardableResult mutating func writeOptionVendorTag(_ tag: KeyValue) -> Int { - self.writeString(tag.key) + - self.writeString("-") + - self.writeString(tag.value) + self.writeString(tag.key) + self.writeString("-") + self.writeString(tag.value) } } diff --git a/Sources/NIOIMAPCore/Grammar/Option/OptionValueComp.swift b/Sources/NIOIMAPCore/Grammar/Option/OptionValueComp.swift index 87324e230..302b0dc7d 100644 --- a/Sources/NIOIMAPCore/Grammar/Option/OptionValueComp.swift +++ b/Sources/NIOIMAPCore/Grammar/Option/OptionValueComp.swift @@ -41,9 +41,7 @@ extension OptionValueComp: ExpressibleByArrayLiteral { extension EncodeBuffer { @discardableResult mutating func writeOptionValue(_ value: OptionValueComp) -> Int { - self.writeString("(") + - self.writeOptionValueComp(value) + - self.writeString(")") + self.writeString("(") + self.writeOptionValueComp(value) + self.writeString(")") } @discardableResult mutating func writeOptionValueComp(_ option: OptionValueComp) -> Int { diff --git a/Sources/NIOIMAPCore/Grammar/Partial/PartialRange.swift b/Sources/NIOIMAPCore/Grammar/Partial/PartialRange.swift index 832301556..491f35f40 100644 --- a/Sources/NIOIMAPCore/Grammar/Partial/PartialRange.swift +++ b/Sources/NIOIMAPCore/Grammar/Partial/PartialRange.swift @@ -34,14 +34,11 @@ extension EncodeBuffer { @discardableResult mutating func writePartialRange(_ range: PartialRange) -> Int { switch range { case .first(let r): - return self.writeSequenceNumberOrWildcard(r.range.lowerBound) + - self.writeString(":") + - self.writeSequenceNumberOrWildcard(r.range.upperBound) + return self.writeSequenceNumberOrWildcard(r.range.lowerBound) + self.writeString(":") + + self.writeSequenceNumberOrWildcard(r.range.upperBound) case .last(let r): - return self.writeString("-") + - self.writeSequenceNumberOrWildcard(r.range.lowerBound) + - self.writeString(":-") + - self.writeSequenceNumberOrWildcard(r.range.upperBound) + return self.writeString("-") + self.writeSequenceNumberOrWildcard(r.range.lowerBound) + + self.writeString(":-") + self.writeSequenceNumberOrWildcard(r.range.upperBound) } } } diff --git a/Sources/NIOIMAPCore/Grammar/Quota/QuotaLimit.swift b/Sources/NIOIMAPCore/Grammar/Quota/QuotaLimit.swift index 224f155be..824064565 100644 --- a/Sources/NIOIMAPCore/Grammar/Quota/QuotaLimit.swift +++ b/Sources/NIOIMAPCore/Grammar/Quota/QuotaLimit.swift @@ -35,8 +35,6 @@ public struct QuotaLimit: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeQuotaLimit(_ quotaLimit: QuotaLimit) -> Int { - self.writeAtom(quotaLimit.resourceName) + - self.writeSpace() + - self.writeString("\(quotaLimit.limit)") + self.writeAtom(quotaLimit.resourceName) + self.writeSpace() + self.writeString("\(quotaLimit.limit)") } } diff --git a/Sources/NIOIMAPCore/Grammar/Quota/QuotaResource.swift b/Sources/NIOIMAPCore/Grammar/Quota/QuotaResource.swift index 4a48c638d..f9e7e09f4 100644 --- a/Sources/NIOIMAPCore/Grammar/Quota/QuotaResource.swift +++ b/Sources/NIOIMAPCore/Grammar/Quota/QuotaResource.swift @@ -40,7 +40,6 @@ public struct QuotaResource: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeQuotaResource(_ quotaDetails: QuotaResource) -> Int { - self.writeAtom(quotaDetails.resourceName) + - self.writeString(" \(quotaDetails.usage) \(quotaDetails.limit)") + self.writeAtom(quotaDetails.resourceName) + self.writeString(" \(quotaDetails.usage) \(quotaDetails.limit)") } } diff --git a/Sources/NIOIMAPCore/Grammar/Quota/QuotaResponse.swift b/Sources/NIOIMAPCore/Grammar/Quota/QuotaResponse.swift index 0d0df8be9..951153056 100644 --- a/Sources/NIOIMAPCore/Grammar/Quota/QuotaResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/Quota/QuotaResponse.swift @@ -18,15 +18,12 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeQuotaResponse(quotaRoot: QuotaRoot, resources: [QuotaResource]) -> Int { - self.writeString("QUOTA ") + - self.writeQuotaRoot(quotaRoot) + - self.writeSpace() + - self.writeQuotaResources(resources) + self.writeString("QUOTA ") + self.writeQuotaRoot(quotaRoot) + self.writeSpace() + + self.writeQuotaResources(resources) } mutating func writeQuotaResources(_ resources: [QuotaResource]) -> Int { - self.writeString("(") + - resources.map { resource in self.writeQuotaResource(resource) }.reduce(0, +) + - self.writeString(")") + self.writeString("(") + resources.map { resource in self.writeQuotaResource(resource) }.reduce(0, +) + + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Quota/QuotaRootResponse.swift b/Sources/NIOIMAPCore/Grammar/Quota/QuotaRootResponse.swift index a84a89a4e..234b5ee9c 100644 --- a/Sources/NIOIMAPCore/Grammar/Quota/QuotaRootResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/Quota/QuotaRootResponse.swift @@ -18,9 +18,6 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeQuotaRootResponse(mailbox: MailboxName, quotaRoot: QuotaRoot) -> Int { - self.writeString("QUOTAROOT ") + - self.writeMailbox(mailbox) + - self.writeSpace() + - self.writeQuotaRoot(quotaRoot) + self.writeString("QUOTAROOT ") + self.writeMailbox(mailbox) + self.writeSpace() + self.writeQuotaRoot(quotaRoot) } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/ExtendedSearchResponse.swift b/Sources/NIOIMAPCore/Grammar/Response/ExtendedSearchResponse.swift index b1341350c..c0063dc09 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/ExtendedSearchResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/ExtendedSearchResponse.swift @@ -85,12 +85,10 @@ extension ExtendedSearchResponse { returnData.lazy.compactMap { data -> MessageIdentifierSet? in guard case .all(.set(let set)) = data else { return nil } return set.set - }.first ?? - returnData.lazy.compactMap { - guard case .partial(_, let set) = $0 else { return nil } - return set - }.first ?? - MessageIdentifierSet() + }.first ?? returnData.lazy.compactMap { + guard case .partial(_, let set) = $0 else { return nil } + return set + }.first ?? MessageIdentifierSet() } /// Returns the count value in the response. @@ -154,17 +152,17 @@ extension ExtendedSearchResponse { extension EncodeBuffer { @discardableResult mutating func writeExtendedSearchResponse(_ response: ExtendedSearchResponse) -> Int { - self.writeString("ESEARCH") + - self.writeIfExists(response.correlator) { (correlator) -> Int in + self.writeString("ESEARCH") + + self.writeIfExists(response.correlator) { (correlator) -> Int in self.writeSearchCorrelator(correlator) - } + - self.write(if: response.kind == .uid) { + } + + self.write(if: response.kind == .uid) { self.writeString(" UID") - } + - self.write(if: response.returnData.count > 0) { + } + + self.write(if: response.returnData.count > 0) { self.writeSpace() - } + - self.writeArray(response.returnData, parenthesis: false) { (data, self) in + } + + self.writeArray(response.returnData, parenthesis: false) { (data, self) in self.writeSearchReturnData(data) } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeAppend.swift b/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeAppend.swift index 4b703d997..f31d2234e 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeAppend.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeAppend.swift @@ -35,7 +35,6 @@ public struct ResponseCodeAppend: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeResponseCodeAppend(_ data: ResponseCodeAppend) -> Int { - self.writeString("APPENDUID \(data.uidValidity.rawValue) ") + - self.writeUIDSet(data.uids) + self.writeString("APPENDUID \(data.uidValidity.rawValue) ") + self.writeUIDSet(data.uids) } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeCopy.swift b/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeCopy.swift index e836a5473..01279f6b5 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeCopy.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/ResponseCodeCopy.swift @@ -41,10 +41,8 @@ public struct ResponseCodeCopy: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeResponseCodeCopy(_ data: ResponseCodeCopy) -> Int { - self.writeString("COPYUID \(data.destinationUIDValidity.rawValue) ") + - self.writeUIDRangeArray(data.sourceUIDs) + - self.writeSpace() + - self.writeUIDRangeArray(data.destinationUIDs) + self.writeString("COPYUID \(data.destinationUIDValidity.rawValue) ") + self.writeUIDRangeArray(data.sourceUIDs) + + self.writeSpace() + self.writeUIDRangeArray(data.destinationUIDs) } @discardableResult private mutating func writeUIDRangeArray(_ array: [UIDRange]) -> Int { diff --git a/Sources/NIOIMAPCore/Grammar/Response/ResponseData.swift b/Sources/NIOIMAPCore/Grammar/Response/ResponseData.swift index 2b56126e8..0a44c5ecd 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/ResponseData.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/ResponseData.swift @@ -18,8 +18,6 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeResponseData(_ data: ResponsePayload) -> Int { - self.writeString("* ") + - self.writeResponsePayload(data) + - self.writeString("\r\n") + self.writeString("* ") + self.writeResponsePayload(data) + self.writeString("\r\n") } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/ResponseFatal.swift b/Sources/NIOIMAPCore/Grammar/Response/ResponseFatal.swift index 22840d14e..ce51c4d3d 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/ResponseFatal.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/ResponseFatal.swift @@ -18,8 +18,6 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeResponseFatal(_ fatal: ResponseText) -> Int { - self.writeString("* ") + - self.writeUntaggedStatus(.bye(fatal)) + - self.writeString("\r\n") + self.writeString("* ") + self.writeUntaggedStatus(.bye(fatal)) + self.writeString("\r\n") } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/ResponseText.swift b/Sources/NIOIMAPCore/Grammar/Response/ResponseText.swift index ced76faef..2a49f5b54 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/ResponseText.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/ResponseText.swift @@ -36,15 +36,13 @@ public struct ResponseText: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeResponseText(_ text: ResponseText) -> Int { self.writeIfExists(text.code) { (code) -> Int in - self.writeString("[") + - self.writeResponseTextCode(code) + - self.writeString("] ") - } + + self.writeString("[") + self.writeResponseTextCode(code) + self.writeString("] ") + } // If the text is empty, write an additional space // to enforce standard compliance. Oddly, this is // perfectly legal IMAP. - self.writeText(text.text.count > 0 ? text.text : " ") + + self.writeText(text.text.count > 0 ? text.text : " ") } @discardableResult mutating func writeText(_ text: String) -> Int { diff --git a/Sources/NIOIMAPCore/Grammar/Response/ResponseTextCode.swift b/Sources/NIOIMAPCore/Grammar/Response/ResponseTextCode.swift index edb30614b..73eeece47 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/ResponseTextCode.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/ResponseTextCode.swift @@ -297,8 +297,7 @@ extension EncodeBuffer { return self.writeString("PARSE") case .permanentFlags(let flags): return - self.writeString("PERMANENTFLAGS ") + - self.writePermanentFlags(flags) + self.writeString("PERMANENTFLAGS ") + self.writePermanentFlags(flags) case .readOnly: return self.writeString("READ-ONLY") case .readWrite: @@ -342,8 +341,8 @@ extension EncodeBuffer { case .metadataNoPrivate: return self.writeString("METADATA NOPRIVATE") case .urlMechanisms(let array): - return self.writeString("URLMECH INTERNAL") + - self.writeArray(array, prefix: " ", parenthesis: false) { mechanism, buffer in + return self.writeString("URLMECH INTERNAL") + + self.writeArray(array, prefix: " ", parenthesis: false) { mechanism, buffer in buffer.writeMechanismBase64(mechanism) } case .referral(let url): @@ -390,18 +389,18 @@ extension EncodeBuffer { } private mutating func writeResponseTextCode_badCharsets(_ charsets: [String]) -> Int { - self.writeString("BADCHARSET") + - self.write(if: charsets.count >= 1) { - self.writeSpace() + - self.writeArray(charsets) { (charset, self) in + self.writeString("BADCHARSET") + + self.write(if: charsets.count >= 1) { + self.writeSpace() + + self.writeArray(charsets) { (charset, self) in self.writeString(charset) } } } private mutating func writeResponseTextCode_other(atom: String, string: String?) -> Int { - self.writeString(atom) + - self.writeIfExists(string) { (string) -> Int in + self.writeString(atom) + + self.writeIfExists(string) { (string) -> Int in self.writeString(" \(string)") } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/TaggedResponse.swift b/Sources/NIOIMAPCore/Grammar/Response/TaggedResponse.swift index 6e8b7a00d..772184d76 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/TaggedResponse.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/TaggedResponse.swift @@ -36,8 +36,6 @@ public struct TaggedResponse: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeTaggedResponse(_ response: TaggedResponse) -> Int { - self.writeString("\(response.tag) ") + - self.writeTaggedResponseState(response.state) + - self.writeString("\r\n") + self.writeString("\(response.tag) ") + self.writeTaggedResponseState(response.state) + self.writeString("\r\n") } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/TaggedResponseState.swift b/Sources/NIOIMAPCore/Grammar/Response/TaggedResponseState.swift index 26d316111..58a8c1116 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/TaggedResponseState.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/TaggedResponseState.swift @@ -46,16 +46,13 @@ extension EncodeBuffer { switch cond { case .ok(let text): return - self.writeString("OK ") + - self.writeResponseText(text) + self.writeString("OK ") + self.writeResponseText(text) case .no(let text): return - self.writeString("NO ") + - self.writeResponseText(text) + self.writeString("NO ") + self.writeResponseText(text) case .bad(let text): return - self.writeString("BAD ") + - self.writeResponseText(text) + self.writeString("BAD ") + self.writeResponseText(text) } } } diff --git a/Sources/NIOIMAPCore/Grammar/Response/UntaggedResponseState.swift b/Sources/NIOIMAPCore/Grammar/Response/UntaggedResponseState.swift index c43c41a6b..67da46358 100644 --- a/Sources/NIOIMAPCore/Grammar/Response/UntaggedResponseState.swift +++ b/Sources/NIOIMAPCore/Grammar/Response/UntaggedResponseState.swift @@ -66,24 +66,19 @@ extension EncodeBuffer { switch cond { case .ok(let text): return - self.writeString("OK ") + - self.writeResponseText(text) + self.writeString("OK ") + self.writeResponseText(text) case .no(let text): return - self.writeString("NO ") + - self.writeResponseText(text) + self.writeString("NO ") + self.writeResponseText(text) case .bad(let text): return - self.writeString("BAD ") + - self.writeResponseText(text) + self.writeString("BAD ") + self.writeResponseText(text) case .preauth(let text): return - self.writeString("PREAUTH ") + - self.writeResponseText(text) + self.writeString("PREAUTH ") + self.writeResponseText(text) case .bye(let text): return - self.writeString("BYE ") + - self.writeResponseText(text) + self.writeString("BYE ") + self.writeResponseText(text) } } } diff --git a/Sources/NIOIMAPCore/Grammar/RumpAuthenticatedURL.swift b/Sources/NIOIMAPCore/Grammar/RumpAuthenticatedURL.swift index edff0b6c9..347269a11 100644 --- a/Sources/NIOIMAPCore/Grammar/RumpAuthenticatedURL.swift +++ b/Sources/NIOIMAPCore/Grammar/RumpAuthenticatedURL.swift @@ -35,7 +35,6 @@ public struct RumpAuthenticatedURL: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeAuthIMAPURLRump(_ data: RumpAuthenticatedURL) -> Int { - self.writeAuthenticatedURL(data.authenticatedURL) + - self.writeAuthenticatedURLRump(data.authenticatedURLRump) + self.writeAuthenticatedURL(data.authenticatedURL) + self.writeAuthenticatedURLRump(data.authenticatedURLRump) } } diff --git a/Sources/NIOIMAPCore/Grammar/RumpURLAndMechanism.swift b/Sources/NIOIMAPCore/Grammar/RumpURLAndMechanism.swift index a00033013..1576b9031 100644 --- a/Sources/NIOIMAPCore/Grammar/RumpURLAndMechanism.swift +++ b/Sources/NIOIMAPCore/Grammar/RumpURLAndMechanism.swift @@ -35,8 +35,6 @@ public struct RumpURLAndMechanism: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeURLRumpMechanism(_ data: RumpURLAndMechanism) -> Int { - self.writeIMAPString(data.urlRump) + - self.writeSpace() + - self.writeURLAuthenticationMechanism(data.mechanism) + self.writeIMAPString(data.urlRump) + self.writeSpace() + self.writeURLAuthenticationMechanism(data.mechanism) } } diff --git a/Sources/NIOIMAPCore/Grammar/Search/SearchCorrelator.swift b/Sources/NIOIMAPCore/Grammar/Search/SearchCorrelator.swift index cc5a105aa..06c8ad101 100644 --- a/Sources/NIOIMAPCore/Grammar/Search/SearchCorrelator.swift +++ b/Sources/NIOIMAPCore/Grammar/Search/SearchCorrelator.swift @@ -41,15 +41,12 @@ public struct SearchCorrelator: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeSearchCorrelator(_ correlator: SearchCorrelator) -> Int { - self.writeString(" (TAG \"") + - self.writeString(correlator.tag) + - self.writeString("\"") + - self.writeIfExists(correlator.mailbox) { mailbox in + self.writeString(" (TAG \"") + self.writeString(correlator.tag) + self.writeString("\"") + + self.writeIfExists(correlator.mailbox) { mailbox in self.writeString(" MAILBOX ") + self.writeMailbox(mailbox) - } + - self.writeIfExists(correlator.uidValidity) { uidValidity in + } + + self.writeIfExists(correlator.uidValidity) { uidValidity in self.writeString(" UIDVALIDITY ") + self.writeUIDValidity(uidValidity) - } + - self.writeString(")") + } + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Search/SearchKey.swift b/Sources/NIOIMAPCore/Grammar/Search/SearchKey.swift index 33e693496..3d755fcf5 100644 --- a/Sources/NIOIMAPCore/Grammar/Search/SearchKey.swift +++ b/Sources/NIOIMAPCore/Grammar/Search/SearchKey.swift @@ -150,47 +150,47 @@ extension SearchKey { var usesString: Bool { switch self { case .all, - .answered, - .deleted, - .flagged, - .new, - .old, - .recent, - .seen, - .unanswered, - .undeleted, - .unflagged, - .unseen, - .draft, - .undraft, - .before, - .keyword, - .on, - .since, - .unkeyword, - .messageSizeLarger, - .sentBefore, - .sentOn, - .sentSince, - .messageSizeSmaller, - .uid, - .uidAfter, - .uidBefore, - .sequenceNumbers, - .older, - .younger, - .modificationSequence, - .filter: + .answered, + .deleted, + .flagged, + .new, + .old, + .recent, + .seen, + .unanswered, + .undeleted, + .unflagged, + .unseen, + .draft, + .undraft, + .before, + .keyword, + .on, + .since, + .unkeyword, + .messageSizeLarger, + .sentBefore, + .sentOn, + .sentSince, + .messageSizeSmaller, + .uid, + .uidAfter, + .uidBefore, + .sequenceNumbers, + .older, + .younger, + .modificationSequence, + .filter: return false case .bcc, - .body, - .cc, - .from, - .subject, - .text, - .to, - .header: + .body, + .cc, + .from, + .subject, + .text, + .to, + .header: return true case .not(let key): @@ -207,45 +207,45 @@ extension SearchKey { fileprivate var count: Int { switch self { case .all, - .answered, - .bcc, - .before, - .body, - .cc, - .deleted, - .flagged, - .from, - .keyword, - .new, - .old, - .on, - .recent, - .seen, - .since, - .subject, - .text, - .to, - .unanswered, - .undeleted, - .unflagged, - .unkeyword, - .unseen, - .draft, - .header, - .messageSizeLarger, - .sentBefore, - .sentOn, - .sentSince, - .messageSizeSmaller, - .uid, - .uidAfter, - .uidBefore, - .undraft, - .sequenceNumbers, - .older, - .younger, - .modificationSequence, - .filter: + .answered, + .bcc, + .before, + .body, + .cc, + .deleted, + .flagged, + .from, + .keyword, + .new, + .old, + .on, + .recent, + .seen, + .since, + .subject, + .text, + .to, + .unanswered, + .undeleted, + .unflagged, + .unkeyword, + .unseen, + .draft, + .header, + .messageSizeLarger, + .sentBefore, + .sentOn, + .sentSince, + .messageSizeSmaller, + .uid, + .uidAfter, + .uidBefore, + .undraft, + .sequenceNumbers, + .older, + .younger, + .modificationSequence, + .filter: return 1 case .not(let inner): return 1 + inner.count @@ -260,15 +260,13 @@ extension SearchKey { // MARK: - IMAP extension EncodeBuffer { - @discardableResult mutating func writeSearchKey(_ key: SearchKey, encloseInParenthesisIfNeeded: Bool = false) -> Int { + @discardableResult mutating func writeSearchKey(_ key: SearchKey, encloseInParenthesisIfNeeded: Bool = false) -> Int + { let encloseInParenthesis = encloseInParenthesisIfNeeded && key.count > 1 - if encloseInParenthesis { - return writeString("(") + - _writeSearchKey(key) + - writeString(")") - } else { + guard encloseInParenthesis else { return _writeSearchKey(key) } + return writeString("(") + _writeSearchKey(key) + writeString(")") } private mutating func _writeSearchKey(_ key: SearchKey) -> Int { @@ -303,103 +301,83 @@ extension EncodeBuffer { return self.writeString("UNDRAFT") case .bcc(let str): return - self.writeString("BCC ") + - self.writeIMAPString(str) + self.writeString("BCC ") + self.writeIMAPString(str) case .before(let date): return - self.writeString("BEFORE ") + - self.writeDate(date) + self.writeString("BEFORE ") + self.writeDate(date) case .body(let str): return - self.writeString("BODY ") + - self.writeIMAPString(str) + self.writeString("BODY ") + self.writeIMAPString(str) case .cc(let str): return - self.writeString("CC ") + - self.writeIMAPString(str) + self.writeString("CC ") + self.writeIMAPString(str) case .from(let str): return - self.writeString("FROM ") + - self.writeIMAPString(str) + self.writeString("FROM ") + self.writeIMAPString(str) case .keyword(let flag): return - self.writeString("KEYWORD ") + - self.writeFlagKeyword(flag) + self.writeString("KEYWORD ") + self.writeFlagKeyword(flag) case .on(let date): return - self.writeString("ON ") + - self.writeDate(date) + self.writeString("ON ") + self.writeDate(date) case .since(let date): return - self.writeString("SINCE ") + - self.writeDate(date) + self.writeString("SINCE ") + self.writeDate(date) case .subject(let str): return - self.writeString("SUBJECT ") + - self.writeIMAPString(str) + self.writeString("SUBJECT ") + self.writeIMAPString(str) case .text(let str): return - self.writeString("TEXT ") + - self.writeIMAPString(str) + self.writeString("TEXT ") + self.writeIMAPString(str) case .to(let str): return - self.writeString("TO ") + - self.writeIMAPString(str) + self.writeString("TO ") + self.writeIMAPString(str) case .unkeyword(let keyword): return - self.writeString("UNKEYWORD ") + - self.writeFlagKeyword(keyword) + self.writeString("UNKEYWORD ") + self.writeFlagKeyword(keyword) case .header(let field, let value): return - self.writeString("HEADER ") + - self.writeIMAPString(field) + - self.writeSpace() + - self.writeIMAPString(value) + self.writeString("HEADER ") + self.writeIMAPString(field) + self.writeSpace() + + self.writeIMAPString(value) case .messageSizeLarger(let n): return self.writeString("LARGER \(n)") case .not(let key): return - self.writeString("NOT ") + - self.writeSearchKey(key, encloseInParenthesisIfNeeded: true) + self.writeString("NOT ") + self.writeSearchKey(key, encloseInParenthesisIfNeeded: true) case .or(let k1, let k2): return - self.writeString("OR ") + - self.writeSearchKey(k1, encloseInParenthesisIfNeeded: true) + - self.writeSpace() + - self.writeSearchKey(k2, encloseInParenthesisIfNeeded: true) + self.writeString("OR ") + self.writeSearchKey(k1, encloseInParenthesisIfNeeded: true) + + self.writeSpace() + self.writeSearchKey(k2, encloseInParenthesisIfNeeded: true) case .messageSizeSmaller(let n): return self.writeString("SMALLER \(n)") case .uid(let set): return - self.writeString("UID ") + - self.writeLastCommandSet(set) + self.writeString("UID ") + self.writeLastCommandSet(set) case .uidAfter(let uid): return - self.writeString("UIDAFTER ") + - self.writeLastCommandMessageID(uid) + self.writeString("UIDAFTER ") + self.writeLastCommandMessageID(uid) case .uidBefore(let uid): return - self.writeString("UIDBEFORE ") + - self.writeLastCommandMessageID(uid) + self.writeString("UIDBEFORE ") + self.writeLastCommandMessageID(uid) case .sequenceNumbers(let set): return self.writeLastCommandSet(set) @@ -413,9 +391,8 @@ extension EncodeBuffer { return keys.enumerated().reduce(0) { (size, row) in let (i, key) = row return - size + - self.writeSearchKey(key, encloseInParenthesisIfNeeded: true) + - self.write(if: i < keys.count - 1) { () -> Int in + size + self.writeSearchKey(key, encloseInParenthesisIfNeeded: true) + + self.write(if: i < keys.count - 1) { () -> Int in self.writeString(" ") } } @@ -429,16 +406,13 @@ extension EncodeBuffer { self.writeString("FILTER \(filterName)") case .sentBefore(let date): return - self.writeString("SENTBEFORE ") + - self.writeDate(date) + self.writeString("SENTBEFORE ") + self.writeDate(date) case .sentOn(let date): return - self.writeString("SENTON ") + - self.writeDate(date) + self.writeString("SENTON ") + self.writeDate(date) case .sentSince(let date): return - self.writeString("SENTSINCE ") + - self.writeDate(date) + self.writeString("SENTSINCE ") + self.writeDate(date) case .modificationSequence(let seq): return self.writeSearchModificationSequence(seq) diff --git a/Sources/NIOIMAPCore/Grammar/Search/SearchModificationSequence.swift b/Sources/NIOIMAPCore/Grammar/Search/SearchModificationSequence.swift index 598c0a0f9..eda275aa8 100644 --- a/Sources/NIOIMAPCore/Grammar/Search/SearchModificationSequence.swift +++ b/Sources/NIOIMAPCore/Grammar/Search/SearchModificationSequence.swift @@ -25,7 +25,10 @@ public struct SearchModificationSequence: Hashable, Sendable { /// Creates a new `SearchModificationSequence`. /// - parameter extensions: Extensions defined to catch data sent as part of any future extensions. /// - parameter sequenceValue: The minimum `ModificationSequenceValue` that any messages returned as part of the search must have. - public init(extensions: OrderedDictionary, sequenceValue: ModificationSequenceValue) { + public init( + extensions: OrderedDictionary, + sequenceValue: ModificationSequenceValue + ) { self.extensions = extensions self.sequenceValue = sequenceValue } @@ -35,14 +38,11 @@ public struct SearchModificationSequence: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeSearchModificationSequence(_ data: SearchModificationSequence) -> Int { - self.writeString("MODSEQ") + - self.writeOrderedDictionary(data.extensions, separator: "", parenthesis: false) { (element, self) -> Int in - self.writeSpace() + - self.writeEntryFlagName(element.key) + - self.writeSpace() + - self.writeEntryKindRequest(element.value) - } + - self.writeSpace() + - self.writeModificationSequenceValue(data.sequenceValue) + self.writeString("MODSEQ") + + self.writeOrderedDictionary(data.extensions, separator: "", parenthesis: false) { + (element, self) -> Int in + self.writeSpace() + self.writeEntryFlagName(element.key) + self.writeSpace() + + self.writeEntryKindRequest(element.value) + } + self.writeSpace() + self.writeModificationSequenceValue(data.sequenceValue) } } diff --git a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnData.swift b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnData.swift index 374298f1a..400362cf6 100644 --- a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnData.swift +++ b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnData.swift @@ -52,14 +52,11 @@ extension EncodeBuffer { return self.writeString("MAX \(num)") case .all(let set): return - self.writeString("ALL ") + - self.writeLastCommandSet(set) + self.writeString("ALL ") + self.writeLastCommandSet(set) case .count(let num): return self.writeString("COUNT \(num)") case .partial(let range, let set): - var count = self.writeString("PARTIAL (") + - self.writePartialRange(range) + - self.writeString(" ") + var count = self.writeString("PARTIAL (") + self.writePartialRange(range) + self.writeString(" ") if set.isEmpty { count += self.writeNil() } else { diff --git a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnDataExtension.swift b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnDataExtension.swift index 0756f6a5c..2b28795b8 100644 --- a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnDataExtension.swift +++ b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnDataExtension.swift @@ -18,8 +18,6 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeSearchReturnDataExtension(_ data: KeyValue) -> Int { - self.writeString(data.key) + - self.writeSpace() + - self.writeParameterValue(data.value) + self.writeString(data.key) + self.writeSpace() + self.writeParameterValue(data.value) } } diff --git a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOption.swift b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOption.swift index d87289a36..9f28fccee 100644 --- a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOption.swift +++ b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOption.swift @@ -71,12 +71,11 @@ extension EncodeBuffer { // `RETURN ()` according to RFC 7377, but many esoteric // servers will fail to parse this correctly. return - self.writeString(" RETURN (") + - self.writeIfExists(options) { (options) -> Int in + self.writeString(" RETURN (") + + self.writeIfExists(options) { (options) -> Int in self.writeArray(options, parenthesis: false) { (option, self) in self.writeSearchReturnOption(option) } - } + - self.writeString(")") + } + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOptionExtension.swift b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOptionExtension.swift index 3d678b1a5..49cd73261 100644 --- a/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOptionExtension.swift +++ b/Sources/NIOIMAPCore/Grammar/Search/SearchReturnOptionExtension.swift @@ -17,11 +17,12 @@ import struct NIO.ByteBuffer // MARK: - Encoding extension EncodeBuffer { - @discardableResult mutating func writeSearchReturnOptionExtension(_ option: KeyValue) -> Int { - self.writeString(option.key) + - self.writeIfExists(option.value) { (params) -> Int in - self.writeSpace() + - self.writeParameterValue(params) + @discardableResult mutating func writeSearchReturnOptionExtension( + _ option: KeyValue + ) -> Int { + self.writeString(option.key) + + self.writeIfExists(option.value) { (params) -> Int in + self.writeSpace() + self.writeParameterValue(params) } } } diff --git a/Sources/NIOIMAPCore/Grammar/SectionSpec.swift b/Sources/NIOIMAPCore/Grammar/SectionSpec.swift index 8642bdb39..923bdf8cc 100644 --- a/Sources/NIOIMAPCore/Grammar/SectionSpec.swift +++ b/Sources/NIOIMAPCore/Grammar/SectionSpec.swift @@ -176,7 +176,7 @@ extension SectionSpecifier.Part: Comparable { /// - returns: `true` if `lhs` evaluates to strictly less than `rhs`, otherwise `false`. public static func < (lhs: SectionSpecifier.Part, rhs: SectionSpecifier.Part) -> Bool { let minSize = min(lhs.array.count, rhs.array.count) - for i in 0 ..< minSize { + for i in 0.. Int { - self.writeString("[") + - self.writeSectionPart(binary) + - self.writeString("]") + self.writeString("[") + self.writeSectionPart(binary) + self.writeString("]") } @discardableResult mutating func writeSection(_ section: SectionSpecifier?) -> Int { - self.writeString("[") + - self.writeIfExists(section) { (spec) -> Int in + self.writeString("[") + + self.writeIfExists(section) { (spec) -> Int in self.writeSectionSpecifier(spec) - } + - self.writeString("]") + } + self.writeString("]") } @discardableResult mutating func writeSectionSpecifier(_ spec: SectionSpecifier?) -> Int { guard let spec = spec else { - return 0 // do nothing + return 0 // do nothing } return self.writeSectionPart(spec.part) @@ -375,13 +371,9 @@ extension EncodeBuffer { case .header: return size + self.writeString("HEADER") case .headerFields(let list): - return size + - self.writeString("HEADER.FIELDS ") + - self.writeHeaderList(list) + return size + self.writeString("HEADER.FIELDS ") + self.writeHeaderList(list) case .headerFieldsNot(let list): - return size + - self.writeString("HEADER.FIELDS.NOT ") + - self.writeHeaderList(list) + return size + self.writeString("HEADER.FIELDS.NOT ") + self.writeHeaderList(list) case .text: return size + self.writeString("TEXT") case .complete: diff --git a/Sources/NIOIMAPCore/Grammar/SelectParameter.swift b/Sources/NIOIMAPCore/Grammar/SelectParameter.swift index 3be50477a..95c53d10a 100644 --- a/Sources/NIOIMAPCore/Grammar/SelectParameter.swift +++ b/Sources/NIOIMAPCore/Grammar/SelectParameter.swift @@ -32,7 +32,12 @@ public struct QResyncParameter: Hashable, Sendable { /// - parameter modificationSequenceValue: The last known modification sequence /// - parameter knownUIDs: The optional set of known UIDs. /// - parameter sequenceMatchData: An optional parenthesized list of known sequence ranges and their corresponding UIDs. - public init(uidValidity: UIDValidity, modificationSequenceValue: ModificationSequenceValue, knownUIDs: UIDSet?, sequenceMatchData: SequenceMatchData?) { + public init( + uidValidity: UIDValidity, + modificationSequenceValue: ModificationSequenceValue, + knownUIDs: UIDSet?, + sequenceMatchData: SequenceMatchData? + ) { self.uidValidity = uidValidity self.modificationSequenceValue = modificationSequenceValue self.knownUIDs = knownUIDs @@ -61,8 +66,8 @@ extension EncodeBuffer { } return - self.writeSpace() + - self.writeArray(params) { (param, self) -> Int in + self.writeSpace() + + self.writeArray(params) { (param, self) -> Int in self.writeSelectParameter(param) } } @@ -79,14 +84,13 @@ extension EncodeBuffer { } @discardableResult mutating func writeQResyncParameter(param: QResyncParameter) -> Int { - self.writeString("QRESYNC (\(param.uidValidity.rawValue) ") + - self.writeModificationSequenceValue(param.modificationSequenceValue) + - self.writeIfExists(param.knownUIDs) { (set) -> Int in + self.writeString("QRESYNC (\(param.uidValidity.rawValue) ") + + self.writeModificationSequenceValue(param.modificationSequenceValue) + + self.writeIfExists(param.knownUIDs) { (set) -> Int in self.writeSpace() + self.writeUIDSet(set) - } + - self.writeIfExists(param.sequenceMatchData) { (data) -> Int in + } + + self.writeIfExists(param.sequenceMatchData) { (data) -> Int in self.writeSpace() + self.writeSequenceMatchData(data) - } + - self.writeString(")") + } + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/Sequence/SequenceNumber.swift b/Sources/NIOIMAPCore/Grammar/Sequence/SequenceNumber.swift index 7b63103e9..16fe425a2 100644 --- a/Sources/NIOIMAPCore/Grammar/Sequence/SequenceNumber.swift +++ b/Sources/NIOIMAPCore/Grammar/Sequence/SequenceNumber.swift @@ -54,10 +54,9 @@ extension EncodeBuffer { } @discardableResult mutating func writeSequenceNumberOrWildcard(_ num: SequenceNumber) -> Int { - if num.rawValue == UInt32.max { + guard num.rawValue < UInt32.max else { return self.writeString("*") - } else { - return self.writeString("\(num.rawValue)") } + return self.writeString("\(num.rawValue)") } } diff --git a/Sources/NIOIMAPCore/Grammar/Sequence/SequenceRange.swift b/Sources/NIOIMAPCore/Grammar/Sequence/SequenceRange.swift index a3c3b441f..1f752c8ef 100644 --- a/Sources/NIOIMAPCore/Grammar/Sequence/SequenceRange.swift +++ b/Sources/NIOIMAPCore/Grammar/Sequence/SequenceRange.swift @@ -18,10 +18,9 @@ import struct NIO.ByteBuffer extension EncodeBuffer { @discardableResult mutating func writeSequenceRange(_ range: SequenceRange) -> Int { - self.writeSequenceNumberOrWildcard(range.range.lowerBound) + - self.write(if: range.range.lowerBound < range.range.upperBound) { - self.writeString(":") + - self.writeSequenceNumberOrWildcard(range.range.upperBound) + self.writeSequenceNumberOrWildcard(range.range.lowerBound) + + self.write(if: range.range.lowerBound < range.range.upperBound) { + self.writeString(":") + self.writeSequenceNumberOrWildcard(range.range.upperBound) } } } diff --git a/Sources/NIOIMAPCore/Grammar/SequenceMatchData.swift b/Sources/NIOIMAPCore/Grammar/SequenceMatchData.swift index d10457277..ef6549da7 100644 --- a/Sources/NIOIMAPCore/Grammar/SequenceMatchData.swift +++ b/Sources/NIOIMAPCore/Grammar/SequenceMatchData.swift @@ -37,10 +37,7 @@ public struct SequenceMatchData: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeSequenceMatchData(_ data: SequenceMatchData) -> Int { - self.writeString("(") + - self.writeLastCommandSet(data.knownSequenceSet) + - self.writeSpace() + - self.writeLastCommandSet(data.knownUidSet) + - self.writeString(")") + self.writeString("(") + self.writeLastCommandSet(data.knownSequenceSet) + self.writeSpace() + + self.writeLastCommandSet(data.knownUidSet) + self.writeString(")") } } diff --git a/Sources/NIOIMAPCore/Grammar/SortData.swift b/Sources/NIOIMAPCore/Grammar/SortData.swift index 0683837d9..a66f8acdf 100644 --- a/Sources/NIOIMAPCore/Grammar/SortData.swift +++ b/Sources/NIOIMAPCore/Grammar/SortData.swift @@ -35,15 +35,12 @@ public struct SortData: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeSortData(_ data: SortData?) -> Int { - self.writeString("SORT") + - self.writeIfExists(data) { (data) -> Int in + self.writeString("SORT") + + self.writeIfExists(data) { (data) -> Int in self.writeArray(data.identifiers, prefix: " ", parenthesis: false) { (element, buffer) -> Int in buffer.writeString("\(element)") - } + - self.writeSpace() + - self.writeString("(MODSEQ ") + - self.writeModificationSequenceValue(data.modificationSequence) + - self.writeString(")") + } + self.writeSpace() + self.writeString("(MODSEQ ") + + self.writeModificationSequenceValue(data.modificationSequence) + self.writeString(")") } } } diff --git a/Sources/NIOIMAPCore/Grammar/Tagged/ParameterValue.swift b/Sources/NIOIMAPCore/Grammar/Tagged/ParameterValue.swift index 09b7c5cda..710233956 100644 --- a/Sources/NIOIMAPCore/Grammar/Tagged/ParameterValue.swift +++ b/Sources/NIOIMAPCore/Grammar/Tagged/ParameterValue.swift @@ -32,9 +32,7 @@ extension EncodeBuffer { return self.writeLastCommandSet(set) case .comp(let comp): return - self.writeString("(") + - self.writeTaggedExtensionComp(comp) + - self.writeString(")") + self.writeString("(") + self.writeTaggedExtensionComp(comp) + self.writeString(")") } } } diff --git a/Sources/NIOIMAPCore/Grammar/Tagged/TaggedExtension.swift b/Sources/NIOIMAPCore/Grammar/Tagged/TaggedExtension.swift index 21dd734d0..dd6e7f8ea 100644 --- a/Sources/NIOIMAPCore/Grammar/Tagged/TaggedExtension.swift +++ b/Sources/NIOIMAPCore/Grammar/Tagged/TaggedExtension.swift @@ -19,9 +19,7 @@ import struct OrderedCollections.OrderedDictionary extension EncodeBuffer { @discardableResult mutating func writeTaggedExtension(_ ext: KeyValue) -> Int { - self.writeString(ext.key) + - self.writeSpace() + - self.writeParameterValue(ext.value) + self.writeString(ext.key) + self.writeSpace() + self.writeParameterValue(ext.value) } @discardableResult mutating func writeParameters(_ params: OrderedDictionary) -> Int { @@ -30,17 +28,16 @@ extension EncodeBuffer { } return - self.writeSpace() + - self.writeOrderedDictionary(params) { (param, self) -> Int in + self.writeSpace() + + self.writeOrderedDictionary(params) { (param, self) -> Int in self.writeParameter(param) } } @discardableResult mutating func writeParameter(_ param: KeyValue) -> Int { - self.writeString(param.key) + - self.writeIfExists(param.value) { (value) -> Int in - self.writeSpace() + - self.writeParameterValue(value) + self.writeString(param.key) + + self.writeIfExists(param.value) { (value) -> Int in + self.writeSpace() + self.writeParameterValue(value) } } } diff --git a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifier.swift b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifier.swift index ab6f9a1fa..b3c20e9e6 100644 --- a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifier.swift +++ b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifier.swift @@ -12,7 +12,9 @@ // //===----------------------------------------------------------------------===// -public protocol MessageIdentifier: Hashable, Codable, CustomDebugStringConvertible, ExpressibleByIntegerLiteral, Strideable where Stride == Int64 { +public protocol MessageIdentifier: Hashable, Codable, CustomDebugStringConvertible, ExpressibleByIntegerLiteral, + Strideable +where Stride == Int64 { var rawValue: UInt32 { get set } init(rawValue: UInt32) @@ -119,10 +121,9 @@ extension MessageIdentifier { extension EncodeBuffer { @discardableResult mutating func writeMessageIdentifier(_ id: IdentifierType) -> Int { - if id == .max { + guard id != .max else { return self.writeString("*") - } else { - return self.writeString("\(id.rawValue)") } + return self.writeString("\(id.rawValue)") } } diff --git a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierRange.swift b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierRange.swift index 8716d5fa1..2a845e6c8 100644 --- a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierRange.swift +++ b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierRange.swift @@ -28,13 +28,13 @@ public struct MessageIdentifierRange: Hashabl /// Creates a new `MessageIdentifierRange` from a partial range, using `.min` as the lower bound. /// - parameter range: A partial with a `MessageIdentifier` as the upper bound. public init(_ range: PartialRangeThrough) { - self.init(IdentifierType.min ... range.upperBound) + self.init(IdentifierType.min...range.upperBound) } /// Creates a new `MessageIdentifierRange` from a partial range, using `.max` as the upper bound. /// - parameter rawValue: A partial with a `MessageIdentifier` as the lower bound. public init(_ range: PartialRangeFrom) { - self.init(range.lowerBound ... IdentifierType.max) + self.init(range.lowerBound...IdentifierType.max) } } @@ -63,14 +63,14 @@ extension MessageIdentifierRange: ExpressibleByIntegerLiteral { /// Creates a range from a single number - essentially a range containing one value. /// - parameter value: The raw number to use as both the upper and lower bounds. public init(_ value: IdentifierType) { - self.init(value ... value) + self.init(value...value) } } extension MessageIdentifierRange { /// Creates a range that covers every valid `MessageIdentifier`. public static var all: Self { - Self((.min) ... (.max)) + Self((.min)...(.max)) } } @@ -79,14 +79,14 @@ extension MessageIdentifier { /// - parameter value: The upper bound. /// - returns: A new `MessageIdentifierRange`. public static prefix func ... (value: Self) -> MessageIdentifierRange { - MessageIdentifierRange((.min) ... value) + MessageIdentifierRange((.min)...value) } /// Creates a new `MessageIdentifierRange` from `value` to `.max`. /// - parameter value: The lower bound. /// - returns: A new `MessageIdentifierRange`. public static postfix func ... (value: Self) -> MessageIdentifierRange { - MessageIdentifierRange(value ... (.max)) + MessageIdentifierRange(value...(.max)) } /// Creates a `MessageIdentifierRange` from lower and upper bounds. @@ -94,29 +94,29 @@ extension MessageIdentifier { /// - parameter upper: The upper bound. /// - returns: A new `MessageIdentifier`. public static func ... (lower: Self, upper: Self) -> MessageIdentifierRange { - MessageIdentifierRange(lower ... upper) + MessageIdentifierRange(lower...upper) } } extension MessageIdentifierRange where IdentifierType == SequenceNumber { init(_ range: MessageIdentifierRange) { - self.init(SequenceNumber(range.range.lowerBound) ... SequenceNumber(range.range.upperBound)) + self.init(SequenceNumber(range.range.lowerBound)...SequenceNumber(range.range.upperBound)) } } extension MessageIdentifierRange where IdentifierType == UID { init(_ range: MessageIdentifierRange) { - self.init(UID(range.range.lowerBound) ... UID(range.range.upperBound)) + self.init(UID(range.range.lowerBound)...UID(range.range.upperBound)) } } extension MessageIdentifierRange where IdentifierType == UnknownMessageIdentifier { init(_ range: MessageIdentifierRange) { - self.init(IdentifierType(range.range.lowerBound) ... IdentifierType(range.range.upperBound)) + self.init(IdentifierType(range.range.lowerBound)...IdentifierType(range.range.upperBound)) } init(_ range: MessageIdentifierRange) { - self.init(IdentifierType(range.range.lowerBound) ... IdentifierType(range.range.upperBound)) + self.init(IdentifierType(range.range.lowerBound)...IdentifierType(range.range.upperBound)) } } @@ -167,10 +167,9 @@ extension MessageIdentifierRange { extension EncodeBuffer { @discardableResult mutating func writeMessageIdentifierRange(_ range: MessageIdentifierRange) -> Int { - self.writeMessageIdentifier(range.range.lowerBound) + - self.write(if: range.range.lowerBound < range.range.upperBound) { - self.writeString(":") + - self.writeMessageIdentifier(range.range.upperBound) + self.writeMessageIdentifier(range.range.lowerBound) + + self.write(if: range.range.lowerBound < range.range.upperBound) { + self.writeString(":") + self.writeMessageIdentifier(range.range.upperBound) } } } diff --git a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSet.swift b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSet.swift index d0e713fde..c2383b8c8 100644 --- a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSet.swift +++ b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSet.swift @@ -51,7 +51,9 @@ public struct MessageIdentifierSet: Hashable, } public init(_ id: IdentifierType) { - self._ranges = RangeSet(MessageIdentificationShiftWrapper(id) ..< (MessageIdentificationShiftWrapper(id).advanced(by: 1))) + self._ranges = RangeSet( + MessageIdentificationShiftWrapper(id)..<(MessageIdentificationShiftWrapper(id).advanced(by: 1)) + ) } } @@ -89,17 +91,20 @@ extension MessageIdentificationShiftWrapper: Strideable { extension Range where Element == MessageIdentificationShiftWrapper { @usableFromInline init(_ r: MessageIdentifierRange) { - self = MessageIdentificationShiftWrapper(r.range.lowerBound) ..< MessageIdentificationShiftWrapper(r.range.upperBound).advanced(by: 1) + self = + MessageIdentificationShiftWrapper( + r.range.lowerBound + )..(_ id: IdentifierType) { - self = MessageIdentificationShiftWrapper(id) ..< MessageIdentificationShiftWrapper(id).advanced(by: 1) + self = MessageIdentificationShiftWrapper(id)..) { - self.init(IdentifierType(r.lowerBound) ... IdentifierType(r.upperBound.advanced(by: -1))) + self.init(IdentifierType(r.lowerBound)...IdentifierType(r.upperBound.advanced(by: -1))) } } @@ -165,7 +170,10 @@ extension MessageIdentifierSet.RangeView: ExpressibleByArrayLiteral { } extension MessageIdentifierSet.RangeView: Equatable { - public static func == (lhs: MessageIdentifierSet.RangeView, rhs: MessageIdentifierSet.RangeView) -> Bool { + public static func == ( + lhs: MessageIdentifierSet.RangeView, + rhs: MessageIdentifierSet.RangeView + ) -> Bool { lhs.elementsEqual(rhs) } } @@ -197,7 +205,7 @@ extension MessageIdentifierSet { if range.isEmpty { self.init() } else { - self.init(range.lowerBound ... range.upperBound.advanced(by: -1)) + self.init(range.lowerBound...range.upperBound.advanced(by: -1)) } } @@ -269,23 +277,7 @@ extension MessageIdentifierSet: BidirectionalCollection { /// /// - Complexity: O(n) public func index(_ i: Self.Index, offsetBy distance: Int) -> Self.Index { - if distance < 0 { - var result = i - result.indexInRange = result.indexInRange.advanced(by: distance) - while true { - if result.indexInRange >= 0 { - break - } - guard _ranges.ranges.startIndex < result.rangeIndex else { - break - } - // We need to find the previous range: - result.rangeIndex = _ranges.ranges.index(before: result.rangeIndex) - let indexCount = IdentifierType.Stride(_ranges.ranges[result.rangeIndex].count) - result.indexInRange = result.indexInRange.advanced(by: Int(indexCount)) - } - return result - } else { + guard distance < 0 else { var remainingDistance = distance var result = i while remainingDistance > 0 { @@ -299,12 +291,29 @@ extension MessageIdentifierSet: BidirectionalCollection { break } let nextRange = _ranges.ranges.index(after: result.rangeIndex) - let step = result.indexInRange.distance(to: IdentifierType.Stride(_ranges.ranges[result.rangeIndex].count)) + let step = result.indexInRange.distance( + to: IdentifierType.Stride(_ranges.ranges[result.rangeIndex].count) + ) result = Index(rangeIndex: nextRange, indexInRange: 0) remainingDistance -= step } return result } + var result = i + result.indexInRange = result.indexInRange.advanced(by: distance) + while true { + if result.indexInRange >= 0 { + break + } + guard _ranges.ranges.startIndex < result.rangeIndex else { + break + } + // We need to find the previous range: + result.rangeIndex = _ranges.ranges.index(before: result.rangeIndex) + let indexCount = IdentifierType.Stride(_ranges.ranges[result.rangeIndex].count) + result.indexInRange = result.indexInRange.advanced(by: Int(indexCount)) + } + return result } /// Returns the distance between two indices. @@ -344,19 +353,17 @@ extension MessageIdentifierSet: BidirectionalCollection { extension MessageIdentifierSet.Index: Comparable { public static func < (lhs: MessageIdentifierSet.Index, rhs: MessageIdentifierSet.Index) -> Bool { - if lhs.rangeIndex == rhs.rangeIndex { - return lhs.indexInRange < rhs.indexInRange - } else { + guard lhs.rangeIndex == rhs.rangeIndex else { return lhs.rangeIndex < rhs.rangeIndex } + return lhs.indexInRange < rhs.indexInRange } public static func > (lhs: MessageIdentifierSet.Index, rhs: MessageIdentifierSet.Index) -> Bool { - if lhs.rangeIndex == rhs.rangeIndex { - return lhs.indexInRange > rhs.indexInRange - } else { + guard lhs.rangeIndex == rhs.rangeIndex else { return lhs.rangeIndex > rhs.rangeIndex } + return lhs.indexInRange > rhs.indexInRange } public static func == (lhs: MessageIdentifierSet.Index, rhs: MessageIdentifierSet.Index) -> Bool { @@ -482,7 +489,9 @@ extension MessageIdentifierSet { } extension EncodeBuffer { - @discardableResult mutating func writeUIDSet(_ set: MessageIdentifierSet) -> Int { + @discardableResult mutating func writeUIDSet( + _ set: MessageIdentifierSet + ) -> Int { set.writeIntoBuffer(&self) } } diff --git a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSetNonEmpty.swift b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSetNonEmpty.swift index be034023b..e6a7b4973 100644 --- a/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSetNonEmpty.swift +++ b/Sources/NIOIMAPCore/Grammar/UID/MessageIdentifierSetNonEmpty.swift @@ -106,7 +106,9 @@ extension MessageIdentifierSetNonEmpty { } extension EncodeBuffer { - @discardableResult mutating func writeUIDSet(_ set: MessageIdentifierSetNonEmpty) -> Int { + @discardableResult mutating func writeUIDSet( + _ set: MessageIdentifierSetNonEmpty + ) -> Int { set.writeIntoBuffer(&self) } } diff --git a/Sources/NIOIMAPCore/Grammar/URLCommand.swift b/Sources/NIOIMAPCore/Grammar/URLCommand.swift index 3dd469055..295a0be6d 100644 --- a/Sources/NIOIMAPCore/Grammar/URLCommand.swift +++ b/Sources/NIOIMAPCore/Grammar/URLCommand.swift @@ -29,8 +29,8 @@ extension EncodeBuffer { case .messageList(let list): return self.writeEncodedSearchQuery(list) case .fetch(path: let path, authenticatedURL: let authenticatedURL): - return self.writeMessagePath(path) + - self.writeIfExists(authenticatedURL) { authenticatedURL in + return self.writeMessagePath(path) + + self.writeIfExists(authenticatedURL) { authenticatedURL in self.writeIAuthenticatedURL(authenticatedURL) } } diff --git a/Sources/NIOIMAPCore/Grammar/URLFetchData.swift b/Sources/NIOIMAPCore/Grammar/URLFetchData.swift index c5b4c4db6..1dc5fdc13 100644 --- a/Sources/NIOIMAPCore/Grammar/URLFetchData.swift +++ b/Sources/NIOIMAPCore/Grammar/URLFetchData.swift @@ -37,8 +37,6 @@ public struct URLFetchData: Hashable, Sendable { extension EncodeBuffer { @discardableResult mutating func writeURLFetchData(_ data: URLFetchData) -> Int { - self.writeIMAPString(data.url) + - self.writeSpace() + - self.writeNString(data.data) + self.writeIMAPString(data.url) + self.writeSpace() + self.writeNString(data.data) } } diff --git a/Sources/NIOIMAPCore/Grammar/URLFetchType.swift b/Sources/NIOIMAPCore/Grammar/URLFetchType.swift index d7a83f6d8..6d40a6a88 100644 --- a/Sources/NIOIMAPCore/Grammar/URLFetchType.swift +++ b/Sources/NIOIMAPCore/Grammar/URLFetchType.swift @@ -15,7 +15,12 @@ /// Provides a variety of ways to load message data. public enum URLFetchType: Hashable, Sendable { /// Uses a mailbox reference and message UID to load a message, and optional message section and part. - case refUidSectionPartial(ref: MailboxUIDValidity, uid: IUID, section: URLMessageSection?, partial: MessagePath.ByteRange?) + case refUidSectionPartial( + ref: MailboxUIDValidity, + uid: IUID, + section: URLMessageSection?, + partial: MessagePath.ByteRange? + ) /// Specifies the section of a message to fetch using a message UID, and optionally a specific part of that message. case uidSectionPartial(uid: IUID, section: URLMessageSection?, partial: MessagePath.ByteRange?) @@ -33,31 +38,25 @@ extension EncodeBuffer { @discardableResult mutating func writeURLFetchType(_ data: URLFetchType) -> Int { switch data { case .refUidSectionPartial(ref: let ref, uid: let uid, section: let section, partial: let range): - return self.writeEncodedMailboxUIDValidity(ref) + - self.writeIUIDOnly(uid) + - self.writeIfExists(section) { section in - self.writeString("/") + - self.writeURLMessageSectionOnly(section) - } + - self.writeIfExists(range) { range in - self.writeString("/") + - self.writeMessagePathByteRangeOnly(range) + return self.writeEncodedMailboxUIDValidity(ref) + self.writeIUIDOnly(uid) + + self.writeIfExists(section) { section in + self.writeString("/") + self.writeURLMessageSectionOnly(section) + } + + self.writeIfExists(range) { range in + self.writeString("/") + self.writeMessagePathByteRangeOnly(range) } case .uidSectionPartial(uid: let uid, section: let section, partial: let range): - return self.writeIUIDOnly(uid) + - self.writeIfExists(section) { section in - self.writeString("/") + - self.writeURLMessageSectionOnly(section) - } + - self.writeIfExists(range) { range in - self.writeString("/") + - self.writeMessagePathByteRangeOnly(range) + return self.writeIUIDOnly(uid) + + self.writeIfExists(section) { section in + self.writeString("/") + self.writeURLMessageSectionOnly(section) + } + + self.writeIfExists(range) { range in + self.writeString("/") + self.writeMessagePathByteRangeOnly(range) } case .sectionPartial(section: let section, partial: let partial): - return self.writeURLMessageSectionOnly(section) + - self.writeIfExists(partial) { partial in - self.writeString("/") + - self.writeMessagePathByteRangeOnly(partial) + return self.writeURLMessageSectionOnly(section) + + self.writeIfExists(partial) { partial in + self.writeString("/") + self.writeMessagePathByteRangeOnly(partial) } case .partialOnly(let partial): return self.writeMessagePathByteRangeOnly(partial) diff --git a/Sources/NIOIMAPCore/Grammar/UserInfo.swift b/Sources/NIOIMAPCore/Grammar/UserInfo.swift index 9a8a4601f..f5e31c841 100644 --- a/Sources/NIOIMAPCore/Grammar/UserInfo.swift +++ b/Sources/NIOIMAPCore/Grammar/UserInfo.swift @@ -36,8 +36,8 @@ extension EncodeBuffer { @discardableResult mutating func writeUserAuthenticationMechanism(_ data: UserAuthenticationMechanism) -> Int { self.writeIfExists(data.encodedUser) { user in self.writeEncodedUser(user) - } + - self.writeIfExists(data.authenticationMechanism) { iAuth in + } + + self.writeIfExists(data.authenticationMechanism) { iAuth in self.writeIMAPURLAuthenticationMechanism(iAuth) } } diff --git a/Sources/NIOIMAPCore/ModifiedUTF7.swift b/Sources/NIOIMAPCore/ModifiedUTF7.swift index 97d488e03..45daac4a6 100644 --- a/Sources/NIOIMAPCore/ModifiedUTF7.swift +++ b/Sources/NIOIMAPCore/ModifiedUTF7.swift @@ -49,7 +49,7 @@ public enum ModifiedUTF7 { } else { // complicated character, time for Base64 var specials: [UInt8] = [] - while index < string.endIndex { // append all non-ascii chars to an array + while index < string.endIndex { // append all non-ascii chars to an array let char = string[index] if let ascVal = char.asciiValue, ascVal > 0x1F, ascVal < 0x7F { break @@ -63,7 +63,8 @@ public enum ModifiedUTF7 { } // convert the buffer to base64 - let b64 = Base64.encodeBytes(bytes: specials).map { $0 == UInt8(ascii: "/") ? UInt8(ascii: ",") : $0 }.filter { $0 != UInt8(ascii: "=") } + let b64 = Base64.encodeBytes(bytes: specials).map { $0 == UInt8(ascii: "/") ? UInt8(ascii: ",") : $0 } + .filter { $0 != UInt8(ascii: "=") } buffer.writeInteger(UInt8(ascii: "&")) buffer.writeBytes(b64) buffer.writeInteger(UInt8(ascii: "-")) diff --git a/Sources/NIOIMAPCore/Parser/CommandParser.swift b/Sources/NIOIMAPCore/Parser/CommandParser.swift index 44df90ddf..67ee28c45 100644 --- a/Sources/NIOIMAPCore/Parser/CommandParser.swift +++ b/Sources/NIOIMAPCore/Parser/CommandParser.swift @@ -44,11 +44,10 @@ public struct CommandParser: Parser, Sendable { case streamingCatenateEnd var isStreamingAppend: Bool { - if case .streamingBytes = self { - return true - } else { + guard case .streamingBytes = self else { return false } + return true } } @@ -59,7 +58,8 @@ public struct CommandParser: Parser, Sendable { /// Creates a new `CommandParser` with a built in buffer limit. Used to prevent DOS attacks, an error will be thrown if this limit is exceeded. /// - parameter bufferLimit. The maximum size of the buffer in bytes at any one time. Defaults to 8192 bytes. - public init(bufferLimit: Int = IMAPDefaults.lineLengthLimit, literalSizeLimit: Int = IMAPDefaults.literalSizeLimit) { + public init(bufferLimit: Int = IMAPDefaults.lineLengthLimit, literalSizeLimit: Int = IMAPDefaults.literalSizeLimit) + { self.bufferLimit = bufferLimit self.parser = GrammarParser(literalSizeLimit: literalSizeLimit) } @@ -75,29 +75,43 @@ public struct CommandParser: Parser, Sendable { let save = buffer let framingResult = try self.synchronisingLiteralParser.parseContinuationsNecessary(buffer) - var actuallyVisible = ParseBuffer(buffer.getSlice(at: buffer.readerIndex, length: framingResult.maximumValidBytes)!) + var actuallyVisible = ParseBuffer( + buffer.getSlice(at: buffer.readerIndex, length: framingResult.maximumValidBytes)! + ) func parseCommand() throws -> CommandStreamPart? { do { - if let command = try self.parseCommandStream0(buffer: &actuallyVisible, tracker: .makeNewDefaultLimitStackTracker) { - // We need to discard the bytes we consumed from the real buffer. - let consumedBytes = framingResult.maximumValidBytes - actuallyVisible.readableBytes - buffer.moveReaderIndex(forwardBy: consumedBytes) - - assert(buffer.writerIndex == save.writerIndex, - "the writer index of the buffer moved whilst parsing which is not supported: \(buffer), \(save)") - assert(consumedBytes >= 0, - "allegedly, we consumed a negative amount of bytes: \(consumedBytes)") - self.synchronisingLiteralParser.consumed(consumedBytes) - assert(consumedBytes <= framingResult.maximumValidBytes, - "We consumed \(consumedBytes) which is more than the framing parser thought are maximally " + - "valid: \(framingResult), \(self.synchronisingLiteralParser)") - return command - } else { - assert(framingResult.maximumValidBytes == actuallyVisible.readableBytes, - "parser consumed bytes on nil: readableBytes before parse: \(framingResult.maximumValidBytes), buffer: \(actuallyVisible)") + guard + let command = try self.parseCommandStream0( + buffer: &actuallyVisible, + tracker: .makeNewDefault + ) + else { + assert( + framingResult.maximumValidBytes == actuallyVisible.readableBytes, + "parser consumed bytes on nil: readableBytes before parse: \(framingResult.maximumValidBytes), buffer: \(actuallyVisible)" + ) return nil } + // We need to discard the bytes we consumed from the real buffer. + let consumedBytes = framingResult.maximumValidBytes - actuallyVisible.readableBytes + buffer.moveReaderIndex(forwardBy: consumedBytes) + + assert( + buffer.writerIndex == save.writerIndex, + "the writer index of the buffer moved whilst parsing which is not supported: \(buffer), \(save)" + ) + assert( + consumedBytes >= 0, + "allegedly, we consumed a negative amount of bytes: \(consumedBytes)" + ) + self.synchronisingLiteralParser.consumed(consumedBytes) + assert( + consumedBytes <= framingResult.maximumValidBytes, + "We consumed \(consumedBytes) which is more than the framing parser thought are maximally " + + "valid: \(framingResult), \(self.synchronisingLiteralParser)" + ) + return command } catch is IncompleteMessage { return nil } @@ -112,7 +126,10 @@ public struct CommandParser: Parser, Sendable { } } - private mutating func parseCommandStream0(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CommandStreamPart? { + private mutating func parseCommandStream0( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> CommandStreamPart? { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in switch self.mode { case .idle: @@ -126,7 +143,11 @@ public struct CommandParser: Parser, Sendable { case .streamingEnd: return try self.handleStreamingEnd(buffer: &buffer, tracker: tracker) case .waitingForCatenatePart(seenPreviousPart: let seenPreviousPart): - return try self.handleCatenatePart(expectPrecedingSpace: seenPreviousPart, buffer: &buffer, tracker: tracker) + return try self.handleCatenatePart( + expectPrecedingSpace: seenPreviousPart, + buffer: &buffer, + tracker: tracker + ) case .streamingCatenateBytes(let remaining): return try self.handleStreamingCatenateBytes(buffer: &buffer, tracker: tracker, remaining: remaining) case .streamingCatenateEnd: @@ -135,7 +156,11 @@ public struct CommandParser: Parser, Sendable { } } - private mutating func handleStreamingBytes(buffer: inout ParseBuffer, tracker: StackTracker, remaining: Int) throws -> CommandStreamPart { + private mutating func handleStreamingBytes( + buffer: inout ParseBuffer, + tracker: StackTracker, + remaining: Int + ) throws -> CommandStreamPart { assert(self.mode.isStreamingAppend) let bytes = try PL.parseBytes(buffer: &buffer, tracker: tracker, upTo: remaining) @@ -164,7 +189,10 @@ public struct CommandParser: Parser, Sendable { return appendCommand } - func parseAuthenticationChallengeResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CommandStreamPart { + func parseAuthenticationChallengeResponse( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> CommandStreamPart { let authenticationChallengeResponse = try self.parser.parseBase64(buffer: &buffer, tracker: tracker) try PL.parseNewline(buffer: &buffer, tracker: tracker) return .continuationResponse(authenticationChallengeResponse) @@ -174,11 +202,15 @@ public struct CommandParser: Parser, Sendable { parseCommand, parseAppend, parseAuthenticationChallengeResponse, - buffer: &buffer, tracker: tracker + buffer: &buffer, + tracker: tracker ) } - private mutating func handleWaitingForMessage(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CommandStreamPart { + private mutating func handleWaitingForMessage( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> CommandStreamPart { do { let command = try self.parser.parseAppendOrCatenateMessage(buffer: &buffer, tracker: tracker) @@ -203,7 +235,10 @@ public struct CommandParser: Parser, Sendable { } } - private mutating func handleStreamingEnd(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CommandStreamPart { + private mutating func handleStreamingEnd( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> CommandStreamPart { self.mode = .waitingForMessage return .append(.endMessage) } @@ -214,8 +249,16 @@ public struct CommandParser: Parser, Sendable { return .idleDone } - private mutating func handleCatenatePart(expectPrecedingSpace: Bool, buffer: inout ParseBuffer, tracker: StackTracker) throws -> CommandStreamPart { - let result = try self.parser.parseCatenatePart(expectPrecedingSpace: expectPrecedingSpace, buffer: &buffer, tracker: tracker) + private mutating func handleCatenatePart( + expectPrecedingSpace: Bool, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> CommandStreamPart { + let result = try self.parser.parseCatenatePart( + expectPrecedingSpace: expectPrecedingSpace, + buffer: &buffer, + tracker: tracker + ) switch result { case .url(let url): self.mode = .waitingForCatenatePart(seenPreviousPart: true) @@ -229,7 +272,11 @@ public struct CommandParser: Parser, Sendable { } } - private mutating func handleStreamingCatenateBytes(buffer: inout ParseBuffer, tracker: StackTracker, remaining: Int) throws -> CommandStreamPart { + private mutating func handleStreamingCatenateBytes( + buffer: inout ParseBuffer, + tracker: StackTracker, + remaining: Int + ) throws -> CommandStreamPart { let bytes = try PL.parseBytes(buffer: &buffer, tracker: tracker, upTo: remaining) assert(bytes.readableBytes <= remaining) @@ -242,7 +289,10 @@ public struct CommandParser: Parser, Sendable { return .append(.catenateData(.bytes(bytes))) } - private mutating func handleStreamingCatenateEnd(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CommandStreamPart { + private mutating func handleStreamingCatenateEnd( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> CommandStreamPart { self.mode = .waitingForCatenatePart(seenPreviousPart: true) return .append(.catenateData(.end)) } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Append.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Append.swift index b473f43fb..db7fc8d21 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Append.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Append.swift @@ -38,14 +38,16 @@ extension GrammarParser { // append-data = literal / literal8 / append-data-ext func parseAppendData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> AppendData { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> AppendData in - let withoutContentTransferEncoding = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - try PL.parseFixedString("~", buffer: &buffer, tracker: tracker) - }.map { () in true } ?? false + let withoutContentTransferEncoding = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in + try PL.parseFixedString("~", buffer: &buffer, tracker: tracker) + }.map { () in true } ?? false try PL.parseFixedString("{", buffer: &buffer, tracker: tracker) let length = try self.parseNumber(buffer: &buffer, tracker: tracker) - _ = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - try PL.parseFixedString("+", buffer: &buffer, tracker: tracker) - }.map { () in false } ?? true + _ = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in + try PL.parseFixedString("+", buffer: &buffer, tracker: tracker) + }.map { () in false } ?? true try PL.parseFixedString("}", buffer: &buffer, tracker: tracker) try PL.parseNewline(buffer: &buffer, tracker: tracker) return .init(byteCount: length, withoutContentTransferEncoding: withoutContentTransferEncoding) @@ -77,7 +79,10 @@ extension GrammarParser { case catenate(AppendOptions) } - func parseAppendOrCatenateMessage(buffer: inout ParseBuffer, tracker: StackTracker) throws -> AppendOrCatenateMessage { + func parseAppendOrCatenateMessage( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> AppendOrCatenateMessage { func parseAppend(buffer: inout ParseBuffer, tracker: StackTracker) throws -> AppendOrCatenateMessage { try .append(self.parseAppendMessage(buffer: &buffer, tracker: tracker)) } @@ -97,16 +102,19 @@ extension GrammarParser { // append-options = [SP flag-list] [SP date-time] *(SP append-ext) func parseAppendOptions(buffer: inout ParseBuffer, tracker: StackTracker) throws -> AppendOptions { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - let flagList = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [Flag] in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseFlagList(buffer: &buffer, tracker: tracker) - } ?? [] - let internalDate = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ServerMessageDate in + let flagList = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [Flag] in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseFlagList(buffer: &buffer, tracker: tracker) + } ?? [] + let internalDate = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ServerMessageDate in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseInternalDate(buffer: &buffer, tracker: tracker) } var kvs = OrderedDictionary() - try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { (buffer, tracker) -> KeyValue in + try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { + (buffer, tracker) -> KeyValue in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseTaggedExtension(buffer: &buffer, tracker: tracker) } @@ -120,7 +128,11 @@ extension GrammarParser { case end } - func parseCatenatePart(expectPrecedingSpace: Bool, buffer: inout ParseBuffer, tracker: StackTracker) throws -> CatenatePart { + func parseCatenatePart( + expectPrecedingSpace: Bool, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> CatenatePart { func parseCatenateURL(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CatenatePart { if expectPrecedingSpace { try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -136,9 +148,10 @@ extension GrammarParser { } try PL.parseFixedString("TEXT {", buffer: &buffer, tracker: tracker) let length = try self.parseNumber(buffer: &buffer, tracker: tracker) - _ = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - try PL.parseFixedString("+", buffer: &buffer, tracker: tracker) - }.map { () in false } ?? true + _ = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in + try PL.parseFixedString("+", buffer: &buffer, tracker: tracker) + }.map { () in false } ?? true try PL.parseFixedString("}", buffer: &buffer, tracker: tracker) try PL.parseNewline(buffer: &buffer, tracker: tracker) return .text(length) diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Body.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Body.swift index aca417112..caed62e21 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Body.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Body.swift @@ -25,7 +25,10 @@ import struct NIO.ByteBufferView import struct OrderedCollections.OrderedDictionary extension GrammarParser { - func parseMessageAttributeBody(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute.BodyStructure { + func parseMessageAttributeBody( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute.BodyStructure { func parseValid(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute.BodyStructure { try .valid(self.parseBody(buffer: &buffer, tracker: tracker)) } @@ -92,15 +95,24 @@ extension GrammarParser { // 7b `{` -> literal // 28 `(` / 29 `)` -> count // 22 `"` -> quoted string - byteCount += try PL.parseZeroOrMoreCharacters(buffer: &buffer, tracker: tracker, where: { - $0 != 0x7B && $0 != 0x28 && $0 != 0x29 && $0 != 0x22 - }).readableBytes + 1 + byteCount += + try PL.parseZeroOrMoreCharacters( + buffer: &buffer, + tracker: tracker, + where: { + $0 != 0x7B && $0 != 0x28 && $0 != 0x29 && $0 != 0x22 + } + ).readableBytes + 1 let byte = try PL.parseByte(buffer: &buffer, tracker: tracker) switch byte { case 0x7B: let literalCount: Int do { - literalCount = try parseLiteralLength(buffer: &buffer, tracker: tracker, maxLength: max(0, maximumByteCount - byteCount)) + literalCount = try parseLiteralLength( + buffer: &buffer, + tracker: tracker, + maxLength: max(0, maximumByteCount - byteCount) + ) } catch { throw ParserError(hint: "Run-away literal in body structure") } @@ -123,9 +135,14 @@ extension GrammarParser { // Skip anything except for // 5c `\` -> escaped // 22 `"` -> quoted string - byteCount += try PL.parseZeroOrMoreCharacters(buffer: &buffer, tracker: tracker, where: { - $0 != 0x5C && $0 != 0x22 - }).readableBytes + 1 + byteCount += + try PL.parseZeroOrMoreCharacters( + buffer: &buffer, + tracker: tracker, + where: { + $0 != 0x5C && $0 != 0x22 + } + ).readableBytes + 1 let byte = try PL.parseByte(buffer: &buffer, tracker: tracker) switch byte { case 0x5C: @@ -152,7 +169,11 @@ extension GrammarParser { try .number(self.parseNumber(buffer: &buffer, tracker: tracker)) } - func parseBodyExtensionKind(buffer: inout ParseBuffer, tracker: StackTracker, into array: inout [BodyExtension]) throws { + func parseBodyExtensionKind( + buffer: inout ParseBuffer, + tracker: StackTracker, + into array: inout [BodyExtension] + ) throws { let element = try PL.parseOneOf( parseBodyExtensionKind_string, parseBodyExtensionKind_number, @@ -162,7 +183,11 @@ extension GrammarParser { array.append(element) } - func parseBodyExtension_array(buffer: inout ParseBuffer, tracker: StackTracker, into array: inout [BodyExtension]) throws { + func parseBodyExtension_array( + buffer: inout ParseBuffer, + tracker: StackTracker, + into array: inout [BodyExtension] + ) throws { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) try parseBodyExtension_arrayOrStatic(buffer: &buffer, tracker: tracker, into: &array) var save = buffer @@ -178,7 +203,11 @@ extension GrammarParser { try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) } - func parseBodyExtension_arrayOrStatic(buffer: inout ParseBuffer, tracker: StackTracker, into array: inout [BodyExtension]) throws { + func parseBodyExtension_arrayOrStatic( + buffer: inout ParseBuffer, + tracker: StackTracker, + into array: inout [BodyExtension] + ) throws { let save = buffer do { try parseBodyExtensionKind(buffer: &buffer, tracker: tracker, into: &array) @@ -194,12 +223,17 @@ extension GrammarParser { } // body-ext-1part = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]] - func parseBodyExtSinglePart(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Singlepart.Extension { + func parseBodyExtSinglePart( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Singlepart.Extension { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> BodyStructure.Singlepart.Extension in - let md5 = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { (buffer: ByteBuffer) -> String in + let md5 = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { + (buffer: ByteBuffer) -> String in try ParserLibrary.parseBufferAsUTF8(buffer) } - let dsp = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> BodyStructure.DispositionAndLanguage in + let dsp = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> BodyStructure.DispositionAndLanguage in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseBodyDescriptionLanguage(buffer: &buffer, tracker: tracker) } @@ -208,10 +242,12 @@ extension GrammarParser { } // body-ext-mpart = body-fld-param [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]] - func parseBodyExtMpart(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Multipart.Extension { + func parseBodyExtMpart(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Multipart.Extension + { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> BodyStructure.Multipart.Extension in let param = try self.parseBodyFieldParam(buffer: &buffer, tracker: tracker) - let dsp = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> BodyStructure.DispositionAndLanguage in + let dsp = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> BodyStructure.DispositionAndLanguage in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseBodyDescriptionLanguage(buffer: &buffer, tracker: tracker) } @@ -225,11 +261,13 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> BodyStructure.Fields in let fieldParam = try self.parseBodyFieldParam(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let fieldID = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { (buffer: ByteBuffer) -> String in + let fieldID = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { + (buffer: ByteBuffer) -> String in try ParserLibrary.parseBufferAsUTF8(buffer) } try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let fieldDescription = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { (buffer: ByteBuffer) -> String in + let fieldDescription = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { + (buffer: ByteBuffer) -> String in try ParserLibrary.parseBufferAsUTF8(buffer) } try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -248,12 +286,18 @@ extension GrammarParser { // body-fld-dsp = "(" string SP body-fld-param ")" / nil func parseBodyFieldDsp(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Disposition? { - func parseBodyFieldDsp_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Disposition? { + func parseBodyFieldDsp_nil( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Disposition? { try self.parseNil(buffer: &buffer, tracker: tracker) return nil } - func parseBodyFieldDsp_some(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Disposition? { + func parseBodyFieldDsp_some( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Disposition? { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) let parsed = try self.parseString(buffer: &buffer, tracker: tracker) let string = try ParserLibrary.parseBufferAsUTF8(parsed) @@ -275,36 +319,60 @@ extension GrammarParser { // body-fld-enc = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ // "QUOTED-PRINTABLE") DQUOTE) / string (or NIL because of some implementations) func parseBodyEncoding(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { - func parseBodyEncoding_string(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { + func parseBodyEncoding_string( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Encoding? { let parsed = try self.parseString(buffer: &buffer, tracker: tracker) return try .init(ParserLibrary.parseBufferAsUTF8(parsed)) } - func parseBodyEncoding_option(_ option: String, result: BodyStructure.Encoding, buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { + func parseBodyEncoding_option( + _ option: String, + result: BodyStructure.Encoding, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Encoding? { try PL.parseFixedString("\"", buffer: &buffer, tracker: tracker) try PL.parseFixedString(option, buffer: &buffer, tracker: tracker) try PL.parseFixedString("\"", buffer: &buffer, tracker: tracker) return result } - func parseBodyEncoding_7bit(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { + func parseBodyEncoding_7bit(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? + { try parseBodyEncoding_option("7BIT", result: .sevenBit, buffer: &buffer, tracker: tracker) } - func parseBodyEncoding_8bit(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { + func parseBodyEncoding_8bit(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? + { try parseBodyEncoding_option("8BIT", result: .eightBit, buffer: &buffer, tracker: tracker) } - func parseBodyEncoding_binary(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { + func parseBodyEncoding_binary( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Encoding? { try parseBodyEncoding_option("BINARY", result: .binary, buffer: &buffer, tracker: tracker) } - func parseBodyEncoding_base64(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { + func parseBodyEncoding_base64( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Encoding? { try parseBodyEncoding_option("BASE64", result: .base64, buffer: &buffer, tracker: tracker) } - func parseBodyEncoding_quotePrintable(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { - try parseBodyEncoding_option("QUOTED-PRINTABLE", result: .quotedPrintable, buffer: &buffer, tracker: tracker) + func parseBodyEncoding_quotePrintable( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Encoding? { + try parseBodyEncoding_option( + "QUOTED-PRINTABLE", + result: .quotedPrintable, + buffer: &buffer, + tracker: tracker + ) } func parseBodyEncoding_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Encoding? { @@ -312,15 +380,19 @@ extension GrammarParser { return nil } - return try PL.parseOneOf([ - parseBodyEncoding_7bit, - parseBodyEncoding_8bit, - parseBodyEncoding_binary, - parseBodyEncoding_base64, - parseBodyEncoding_quotePrintable, - parseBodyEncoding_string, - parseBodyEncoding_nil, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseBodyEncoding_7bit, + parseBodyEncoding_8bit, + parseBodyEncoding_binary, + parseBodyEncoding_base64, + parseBodyEncoding_quotePrintable, + parseBodyEncoding_string, + parseBodyEncoding_nil, + ], + buffer: &buffer, + tracker: tracker + ) } // body-fld-lang = nstring / "(" string *(SP string) ")" @@ -357,13 +429,20 @@ extension GrammarParser { } // body-fld-param = "(" string SP string *(SP string SP string) ")" / nil - func parseBodyFieldParam(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { - func parseBodyFieldParam_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseBodyFieldParam( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { + func parseBodyFieldParam_nil( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { try parseNil(buffer: &buffer, tracker: tracker) return [:] } - func parseBodyFieldParam_singlePair(buffer: inout ParseBuffer, tracker: StackTracker) throws -> (String, String) { + func parseBodyFieldParam_singlePair(buffer: inout ParseBuffer, tracker: StackTracker) throws -> (String, String) + { let parsedField = try parseString(buffer: &buffer, tracker: tracker) let field = try ParserLibrary.parseBufferAsUTF8(parsedField) try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -372,12 +451,16 @@ extension GrammarParser { return (field, value) } - func parseBodyFieldParam_pairs(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseBodyFieldParam_pairs( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var kvs = OrderedDictionary() let keyValue = try parseBodyFieldParam_singlePair(buffer: &buffer, tracker: tracker) kvs[keyValue.0] = keyValue.1 - try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { (buffer, tracker) -> (String, String) in + try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { + (buffer, tracker) -> (String, String) in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try parseBodyFieldParam_singlePair(buffer: &buffer, tracker: tracker) } @@ -396,14 +479,20 @@ extension GrammarParser { // body-type-1part = (body-type-basic / body-type-msg / body-type-text) // [SP body-ext-1part] func parseBodyKindSinglePart(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Singlepart { - func parseBodyKindSinglePart_extension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Singlepart.Extension? { + func parseBodyKindSinglePart_extension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Singlepart.Extension? { try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseBodyExtSinglePart(buffer: &buffer, tracker: tracker) } } - func parseBodyKindSinglePart_basic(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Singlepart { + func parseBodyKindSinglePart_basic( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Singlepart { let media = try self.parseMediaType(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let fields = try self.parseBodyFields(buffer: &buffer, tracker: tracker) @@ -411,7 +500,10 @@ extension GrammarParser { return BodyStructure.Singlepart(kind: .basic(media), fields: fields, extension: ext) } - func parseBodyKindSinglePart_message(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Singlepart { + func parseBodyKindSinglePart_message( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Singlepart { let mediaMessage = try self.parseMediaMessage(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let fields = try self.parseBodyFields(buffer: &buffer, tracker: tracker) @@ -421,12 +513,20 @@ extension GrammarParser { let body = try self.parseBody(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let fieldLines = try self.parseNumber(buffer: &buffer, tracker: tracker) - let message = BodyStructure.Singlepart.Message(message: mediaMessage, envelope: envelope, body: body, lineCount: fieldLines) + let message = BodyStructure.Singlepart.Message( + message: mediaMessage, + envelope: envelope, + body: body, + lineCount: fieldLines + ) let ext = try parseBodyKindSinglePart_extension(buffer: &buffer, tracker: tracker) return BodyStructure.Singlepart(kind: .message(message), fields: fields, extension: ext) } - func parseBodyKindSinglePart_text(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.Singlepart { + func parseBodyKindSinglePart_text( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.Singlepart { let media = try self.parseMediaText(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let fields = try self.parseBodyFields(buffer: &buffer, tracker: tracker) @@ -456,7 +556,8 @@ extension GrammarParser { } try PL.parseSpaces(buffer: &buffer, tracker: tracker) let media = try self.parseMediaSubtype(buffer: &buffer, tracker: tracker) - let ext = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> BodyStructure.Multipart.Extension in + let ext = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> BodyStructure.Multipart.Extension in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseBodyExtMpart(buffer: &buffer, tracker: tracker) } @@ -464,29 +565,42 @@ extension GrammarParser { } } - func parseBodyLocationExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.LocationAndExtensions { - let fieldLocation = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { (buffer: ByteBuffer) -> String in + func parseBodyLocationExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.LocationAndExtensions { + let fieldLocation = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { + (buffer: ByteBuffer) -> String in try ParserLibrary.parseBufferAsUTF8(buffer) } - let extensions = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [BodyExtension] in + let extensions = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> [BodyExtension] in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseBodyExtension(buffer: &buffer, tracker: tracker) } return BodyStructure.LocationAndExtensions(location: fieldLocation, extensions: extensions.reduce([], +)) } - func parseBodyLanguageLocation(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.LanguageLocation { + func parseBodyLanguageLocation( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.LanguageLocation { let fieldLanguage = try self.parseBodyFieldLanguage(buffer: &buffer, tracker: tracker) - let locationExtension = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> BodyStructure.LocationAndExtensions in + let locationExtension = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> BodyStructure.LocationAndExtensions in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseBodyLocationExtension(buffer: &buffer, tracker: tracker) } return BodyStructure.LanguageLocation(languages: fieldLanguage, location: locationExtension) } - func parseBodyDescriptionLanguage(buffer: inout ParseBuffer, tracker: StackTracker) throws -> BodyStructure.DispositionAndLanguage { + func parseBodyDescriptionLanguage( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> BodyStructure.DispositionAndLanguage { let description = try self.parseBodyFieldDsp(buffer: &buffer, tracker: tracker) - let language = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> BodyStructure.LanguageLocation in + let language = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> BodyStructure.LanguageLocation in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseBodyLanguageLocation(buffer: &buffer, tracker: tracker) } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Commands.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Commands.swift index 43cfa63ea..009f58b18 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Commands.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Commands.swift @@ -92,10 +92,14 @@ extension GrammarParser { extension GrammarParser { func parseCommandSuffix_urlFetch(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { - let array = try PL.parseOneOrMore(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> ByteBuffer in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseAString(buffer: &buffer, tracker: tracker) - }) + let array = try PL.parseOneOrMore( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> ByteBuffer in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseAString(buffer: &buffer, tracker: tracker) + } + ) return .urlFetch(array) } @@ -130,10 +134,14 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseSpaces(buffer: &buffer, tracker: tracker) let mechanism = AuthenticationMechanism(try self.parseAtom(buffer: &buffer, tracker: tracker)) - let initialResponse = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> InitialResponse in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseInitialResponse(buffer: &buffer, tracker: tracker) - }) + let initialResponse = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> InitialResponse in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseInitialResponse(buffer: &buffer, tracker: tracker) + } + ) return .authenticate(mechanism: mechanism, initialResponse: initialResponse) } } @@ -175,7 +183,9 @@ extension GrammarParser { let mailbox = try self.parseMailbox(buffer: &buffer, tracker: tracker) try PL.parseFixedString(" (", buffer: &buffer, tracker: tracker) var atts = [try self.parseStatusAttribute(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &atts, tracker: tracker) { buffer, tracker -> MailboxAttribute in + try PL.parseZeroOrMore(buffer: &buffer, into: &atts, tracker: tracker) { + buffer, + tracker -> MailboxAttribute in try PL.parseFixedString(" ", buffer: &buffer, tracker: tracker) return try self.parseStatusAttribute(buffer: &buffer, tracker: tracker) } @@ -231,8 +241,12 @@ extension GrammarParser { func parseCommandSuffix_store(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let sequence: LastCommandSet = try self.parseMessageIdentifierSetOrLast(buffer: &buffer, tracker: tracker) - let modifiers = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseStoreModifiers) ?? [] + let sequence: LastCommandSet = try self.parseMessageIdentifierSetOrLast( + buffer: &buffer, + tracker: tracker + ) + let modifiers = + try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseStoreModifiers) ?? [] try PL.parseSpaces(buffer: &buffer, tracker: tracker) let storeData = try self.parseStoreData(buffer: &buffer, tracker: tracker) return .store(sequence, modifiers, storeData) @@ -250,7 +264,10 @@ extension GrammarParser { func parseCommandSuffix_move(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let set: LastCommandSet = try self.parseMessageIdentifierSetOrLast(buffer: &buffer, tracker: tracker) + let set: LastCommandSet = try self.parseMessageIdentifierSetOrLast( + buffer: &buffer, + tracker: tracker + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let mailbox = try self.parseMailbox(buffer: &buffer, tracker: tracker) return .move(set, mailbox) @@ -270,7 +287,10 @@ extension GrammarParser { func parseCommandSuffix_copy(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let sequence: LastCommandSet = try self.parseMessageIdentifierSetOrLast(buffer: &buffer, tracker: tracker) + let sequence: LastCommandSet = try self.parseMessageIdentifierSetOrLast( + buffer: &buffer, + tracker: tracker + ) try PL.parseFixedString(" ", buffer: &buffer, tracker: tracker) let mailbox = try self.parseMailbox(buffer: &buffer, tracker: tracker) return .copy(sequence, mailbox) @@ -282,7 +302,8 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseSpaces(buffer: &buffer, tracker: tracker) let mailbox = try self.parseMailbox(buffer: &buffer, tracker: tracker) - let params = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseCreateParameters) ?? [] + let params = + try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseCreateParameters) ?? [] return .create(mailbox, params) } } @@ -307,11 +328,16 @@ extension GrammarParser { func parseCommandSuffix_getMetadata(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let options = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> [MetadataOption] in - let options = try self.parseMetadataOptions(buffer: &buffer, tracker: tracker) - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return options - }) ?? [] + let options = + try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> [MetadataOption] in + let options = try self.parseMetadataOptions(buffer: &buffer, tracker: tracker) + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return options + } + ) ?? [] let mailbox = try self.parseMailbox(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let entries = try self.parseEntries(buffer: &buffer, tracker: tracker) @@ -331,52 +357,68 @@ extension GrammarParser { func parseCommandSuffix_resetKey(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in - let _mailbox = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> MailboxName in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseMailbox(buffer: &buffer, tracker: tracker) - }) + let _mailbox = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> MailboxName in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseMailbox(buffer: &buffer, tracker: tracker) + } + ) // don't bother parsing mechanisms if there's no mailbox guard let mailbox = _mailbox else { return .resetKey(mailbox: nil, mechanisms: []) } - let mechanisms = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> URLAuthenticationMechanism in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseUAuthMechanism(buffer: &buffer, tracker: tracker) - }) + let mechanisms = try PL.parseZeroOrMore( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> URLAuthenticationMechanism in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseUAuthMechanism(buffer: &buffer, tracker: tracker) + } + ) return .resetKey(mailbox: mailbox, mechanisms: mechanisms) } } func parseCommandSuffix_genURLAuth(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { - let array = try PL.parseOneOrMore(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> RumpURLAndMechanism in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseURLRumpMechanism(buffer: &buffer, tracker: tracker) - }) + let array = try PL.parseOneOrMore( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> RumpURLAndMechanism in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseURLRumpMechanism(buffer: &buffer, tracker: tracker) + } + ) return .generateAuthorizedURL(array) } // search = "SEARCH" [search-return-opts] SP search-program func parseCommandSuffix_search(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - let returnOpts = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseSearchReturnOptions) + let returnOpts = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseSearchReturnOptions + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let (charset, program) = try parseSearchProgram(buffer: &buffer, tracker: tracker) - if let o = returnOpts { - // We have options. Map an empty options list to `[.all]`: - return .search(key: program, charset: charset, returnOptions: (o == []) ? [.all] : o) - } else { + guard let o = returnOpts else { // No options -> normal search with `returnOptions: []` return .search(key: program, charset: charset, returnOptions: []) } + // We have options. Map an empty options list to `[.all]`: + return .search(key: program, charset: charset, returnOptions: (o == []) ? [.all] : o) } } // list = "LIST" [SP list-select-opts] SP mailbox SP mbox-or-pat [SP list-return-opts] func parseCommandSuffix_list(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - let selectOptions = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ListSelectOptions in + let selectOptions = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ListSelectOptions in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseListSelectOptions(buffer: &buffer, tracker: tracker) } @@ -384,10 +426,11 @@ extension GrammarParser { let mailbox = try self.parseMailbox(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let mailboxPatterns = try self.parseMailboxPatterns(buffer: &buffer, tracker: tracker) - let returnOptions = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [ReturnOption] in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseListReturnOptions(buffer: &buffer, tracker: tracker) - } ?? [] + let returnOptions = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [ReturnOption] in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseListReturnOptions(buffer: &buffer, tracker: tracker) + } ?? [] return .list(selectOptions, reference: mailbox, mailboxPatterns, returnOptions) } } @@ -421,7 +464,8 @@ extension GrammarParser { let set = try self.parseUIDSetNonEmpty(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let att = try parseFetch_type(buffer: &buffer, tracker: tracker) - let modifiers = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseFetchModifiers) ?? [] + let modifiers = + try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseFetchModifiers) ?? [] return .uidFetch(.set(set), att, modifiers) } } @@ -429,7 +473,12 @@ extension GrammarParser { func parseUid_search(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseFixedString("SEARCH", buffer: &buffer, tracker: tracker) - guard case .search(let key, let charset, let returnOptions) = try self.parseCommandSuffix_search(buffer: &buffer, tracker: tracker) else { + guard + case .search(let key, let charset, let returnOptions) = try self.parseCommandSuffix_search( + buffer: &buffer, + tracker: tracker + ) + else { fatalError("This should never happen") } return .uidSearch(key: key, charset: charset, returnOptions: returnOptions) @@ -440,7 +489,8 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseFixedString("STORE ", buffer: &buffer, tracker: tracker) let set = try self.parseUIDSetNonEmpty(buffer: &buffer, tracker: tracker) - let modifiers = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseStoreModifiers) ?? [] + let modifiers = + try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseStoreModifiers) ?? [] try PL.parseSpaces(buffer: &buffer, tracker: tracker) let storeData = try self.parseStoreData(buffer: &buffer, tracker: tracker) return .uidStore(.set(set), modifiers, storeData) @@ -455,14 +505,18 @@ extension GrammarParser { return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try PL.parseOneOf([ - parseUid_copy, - parseUid_move, - parseUid_fetch, - parseUid_search, - parseUid_store, - parseUid_expunge, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseUid_copy, + parseUid_move, + parseUid_fetch, + parseUid_search, + parseUid_store, + parseUid_expunge, + ], + buffer: &buffer, + tracker: tracker + ) } } @@ -471,10 +525,14 @@ extension GrammarParser { func parseCommandSuffix_fetch(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Command { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Command in try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let sequence: LastCommandSet = try self.parseMessageIdentifierSetOrLast(buffer: &buffer, tracker: tracker) + let sequence: LastCommandSet = try self.parseMessageIdentifierSetOrLast( + buffer: &buffer, + tracker: tracker + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let att = try parseFetch_type(buffer: &buffer, tracker: tracker) - let modifiers = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseFetchModifiers) ?? [] + let modifiers = + try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseFetchModifiers) ?? [] return .fetch(sequence, att, modifiers) } } @@ -495,10 +553,15 @@ extension GrammarParser { try PL.parseSpaces(buffer: &buffer, tracker: tracker) try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var array = [try self.parseSelectParameter(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker, parser: { (buffer, tracker) in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseSelectParameter(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &array, + tracker: tracker, + parser: { (buffer, tracker) in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseSelectParameter(buffer: &buffer, tracker: tracker) + } + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return array } ?? [] diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Date.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Date.swift index 9e13ff1ae..0ab30c306 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Date.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Date.swift @@ -156,7 +156,15 @@ extension GrammarParser { try PL.parseFixedString("\"", buffer: &buffer, tracker: tracker) guard - let components = ServerMessageDate.Components(year: year, month: month, day: day, hour: hour, minute: minute, second: second, timeZoneMinutes: zone) + let components = ServerMessageDate.Components( + year: year, + month: month, + day: day, + hour: hour, + minute: minute, + second: second, + timeZoneMinutes: zone + ) else { throw ParserError(hint: "Invalid internal date.") } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Entry.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Entry.swift index cae4e5db0..3aaf55e2d 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Entry.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Entry.swift @@ -30,8 +30,13 @@ extension GrammarParser { return MetadataEntryName(buffer) } - func parseEntryValue(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { - try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> KeyValue in + func parseEntryValue( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { + try PL.composite(buffer: &buffer, tracker: tracker) { + buffer, + tracker -> KeyValue in let name = try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let value = try self.parseMetadataValue(buffer: &buffer, tracker: tracker) @@ -39,33 +44,51 @@ extension GrammarParser { } } - func parseEntryValues(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { - try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> OrderedDictionary in + func parseEntryValues( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { + try PL.composite(buffer: &buffer, tracker: tracker) { + buffer, + tracker -> OrderedDictionary in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var kvs = OrderedDictionary() let ev = try self.parseEntryValue(buffer: &buffer, tracker: tracker) kvs[ev.key] = ev.value - try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker, parser: { buffer, tracker -> KeyValue in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseEntryValue(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &kvs, + tracker: tracker, + parser: { buffer, tracker -> KeyValue in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseEntryValue(buffer: &buffer, tracker: tracker) + } + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return kvs } } func parseEntries(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [MetadataEntryName] { - func parseEntries_singleUnbracketed(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [MetadataEntryName] { + func parseEntries_singleUnbracketed( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> [MetadataEntryName] { [try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker)] } func parseEntries_bracketed(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [MetadataEntryName] { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var array = [try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker, parser: { buffer, tracker in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &array, + tracker: tracker, + parser: { buffer, tracker in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker) + } + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return array } @@ -81,10 +104,15 @@ extension GrammarParser { func parseEntryList(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [MetadataEntryName] { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in var array = [try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker, parser: { buffer, tracker in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &array, + tracker: tracker, + parser: { buffer, tracker in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseMetadataEntryName(buffer: &buffer, tracker: tracker) + } + ) return array } } @@ -105,7 +133,8 @@ extension GrammarParser { return .all } - func parseEntryKindRequest_private(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EntryKindRequest { + func parseEntryKindRequest_private(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EntryKindRequest + { try PL.parseFixedString("priv", buffer: &buffer, tracker: tracker) return .private } @@ -126,12 +155,16 @@ extension GrammarParser { // entry-type-resp = "priv" / "shared" func parseEntryKindResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EntryKindResponse { - func parseEntryKindResponse_private(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EntryKindResponse { + func parseEntryKindResponse_private( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> EntryKindResponse { try PL.parseFixedString("priv", buffer: &buffer, tracker: tracker) return .private } - func parseEntryKindResponse_shared(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EntryKindResponse { + func parseEntryKindResponse_shared(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EntryKindResponse + { try PL.parseFixedString("shared", buffer: &buffer, tracker: tracker) return .shared } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Envelope.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Envelope.swift index 3ea497c8b..af2d81826 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Envelope.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Envelope.swift @@ -31,15 +31,15 @@ extension GrammarParser { for address in addresses { // RFC 2822 Syntaxt: If the host is nil then the group has started // if the mailbox is also nil, then the group has finished. - if address.host == nil, let name = address.mailbox { // start of group + if address.host == nil, let name = address.mailbox { // start of group stack.append(EmailAddressGroup(groupName: name, sourceRoot: address.sourceRoot, children: [])) - } else if address.host == nil, let group = stack.popLast() { // end of group + } else if address.host == nil, let group = stack.popLast() { // end of group if stack.last == nil { results.append(.group(group)) } else { stack[stack.count - 1].children.append(.group(group)) } - } else { // normal address + } else { // normal address if stack.last == nil { results.append(.singleAddress(address)) } else { @@ -61,8 +61,14 @@ extension GrammarParser { return addresses } - func parseOptionalEnvelopeEmailAddresses(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [EmailAddressListElement] { - func parseOptionalEnvelopeEmailAddresses_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [EmailAddress] { + func parseOptionalEnvelopeEmailAddresses( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> [EmailAddressListElement] { + func parseOptionalEnvelopeEmailAddresses_nil( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> [EmailAddress] { try self.parseNil(buffer: &buffer, tracker: tracker) return [] } @@ -108,7 +114,9 @@ extension GrammarParser { func parseEnvelope(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Envelope { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Envelope in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let date = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { InternetMessageDate(String(buffer: $0)) } + let date = try self.parseNString(buffer: &buffer, tracker: tracker).flatMap { + InternetMessageDate(String(buffer: $0)) + } try PL.parseSpaces(buffer: &buffer, tracker: tracker) let subject = try self.parseNString(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Fetch.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Fetch.swift index c400b77f5..d1befbe25 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Fetch.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Fetch.swift @@ -47,7 +47,8 @@ extension GrammarParser { func parseFetch_type_multiAtt(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [FetchAttribute] { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var array = [try self.parseFetchAttribute(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> FetchAttribute in + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> FetchAttribute in try PL.parseFixedString(" ", buffer: &buffer, tracker: tracker) return try self.parseFetchAttribute(buffer: &buffer, tracker: tracker) } @@ -55,13 +56,17 @@ extension GrammarParser { return array } - return try PL.parseOneOf([ - parseFetch_type_all, - parseFetch_type_full, - parseFetch_type_fast, - parseFetch_type_singleAtt, - parseFetch_type_multiAtt, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseFetch_type_all, + parseFetch_type_full, + parseFetch_type_fast, + parseFetch_type_singleAtt, + parseFetch_type_multiAtt, + ], + buffer: &buffer, + tracker: tracker + ) } // fetch-att = "ENVELOPE" / "FLAGS" / "INTERNALDATE" / @@ -73,13 +78,15 @@ extension GrammarParser { // "BINARY.SIZE" section-binary // TODO: rev2 func parseFetchAttribute(buffer: inout ParseBuffer, tracker: StackTracker) throws -> FetchAttribute { - func parseFetchAttribute_bodySection(buffer: inout ParseBuffer, tracker: StackTracker) throws -> FetchAttribute { + func parseFetchAttribute_bodySection(buffer: inout ParseBuffer, tracker: StackTracker) throws -> FetchAttribute + { // Try to parse a section, `[something]`. If this fails, then it's a normal, boring body, without extensions // (with extensions is sent as `BODYSTRUCTURE`). // This is one of the few cases where we need to explicitly catch the "incompleteMessage" case and *NOT* // propogate it forward. if let section = try? self.parseSection(buffer: &buffer, tracker: tracker) { - let chevronNumber = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ClosedRange in + let chevronNumber = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ClosedRange in try self.parsePartial(buffer: &buffer, tracker: tracker) } return .bodySection(peek: false, section, chevronNumber) @@ -87,15 +94,22 @@ extension GrammarParser { return .bodyStructure(extensions: false) } - func parseFetchAttribute_bodyPeekSection(buffer: inout ParseBuffer, tracker: StackTracker) throws -> FetchAttribute { + func parseFetchAttribute_bodyPeekSection( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> FetchAttribute { let section = try self.parseSection(buffer: &buffer, tracker: tracker) - let chevronNumber = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ClosedRange in + let chevronNumber = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ClosedRange in try self.parsePartial(buffer: &buffer, tracker: tracker) } return .bodySection(peek: true, section, chevronNumber) } - func parseFetchAttribute_modificationSequence(buffer: inout ParseBuffer, tracker: StackTracker) throws -> FetchAttribute { + func parseFetchAttribute_modificationSequence( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> FetchAttribute { .modificationSequenceValue(try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker)) } @@ -165,15 +179,24 @@ extension GrammarParser { } } - func parseFetchStreamingResponse_rfc822Text(buffer: inout ParseBuffer, tracker: StackTracker) throws -> StreamingKind { + func parseFetchStreamingResponse_rfc822Text( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> StreamingKind { .rfc822Text } - func parseFetchStreamingResponse_rfc822Header(buffer: inout ParseBuffer, tracker: StackTracker) throws -> StreamingKind { + func parseFetchStreamingResponse_rfc822Header( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> StreamingKind { .rfc822Header } - func parseFetchStreamingResponse_bodySectionText(buffer: inout ParseBuffer, tracker: StackTracker) throws -> StreamingKind { + func parseFetchStreamingResponse_bodySectionText( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> StreamingKind { let section = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseSection) ?? .init() let offset = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> Int in try PL.parseFixedString("<", buffer: &buffer, tracker: tracker) @@ -186,12 +209,16 @@ extension GrammarParser { func parseFetchStreamingResponse_binary(buffer: inout ParseBuffer, tracker: StackTracker) throws -> StreamingKind { let section = try self.parseSectionBinary(buffer: &buffer, tracker: tracker) - let offset = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> Int in - try PL.parseFixedString("<", buffer: &buffer, tracker: tracker) - let num = try self.parseNumber(buffer: &buffer, tracker: tracker) - try PL.parseFixedString(">", buffer: &buffer, tracker: tracker) - return num - }) + let offset = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> Int in + try PL.parseFixedString("<", buffer: &buffer, tracker: tracker) + let num = try self.parseNumber(buffer: &buffer, tracker: tracker) + try PL.parseFixedString(">", buffer: &buffer, tracker: tracker) + return num + } + ) return .binary(section: section, offset: offset) } @@ -210,7 +237,9 @@ extension GrammarParser { try PL.parseSpaces(buffer: &buffer, tracker: tracker) try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var array = [try self.parseFetchModifier(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { buffer, tracker -> FetchModifier in + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + buffer, + tracker -> FetchModifier in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseFetchModifier(buffer: &buffer, tracker: tracker) } @@ -262,10 +291,14 @@ extension GrammarParser { } } - return try PL.parseOneOf([ - parseFetchResponseStart_normal, - parseFetchResponseStart_uid, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseFetchResponseStart_normal, + parseFetchResponseStart_uid, + ], + buffer: &buffer, + tracker: tracker + ) } // needed to tell the response parser which type of streaming is @@ -280,19 +313,32 @@ extension GrammarParser { } func parseFetchResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> _FetchResponse { - func parseFetchResponse_simpleAttribute(buffer: inout ParseBuffer, tracker: StackTracker) throws -> _FetchResponse { + func parseFetchResponse_simpleAttribute( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> _FetchResponse { let attribute = try self.parseMessageAttribute(buffer: &buffer, tracker: tracker) return .simpleAttribute(attribute) } - func parseFetchResponse_streamingBegin(buffer: inout ParseBuffer, tracker: StackTracker) throws -> _FetchResponse { + func parseFetchResponse_streamingBegin( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> _FetchResponse { let type = try self.parseFetchStreamingResponse(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let literalSize = try self.parseLiteralSize(buffer: &buffer, tracker: tracker, maxLength: self.messageBodySizeLimit) + let literalSize = try self.parseLiteralSize( + buffer: &buffer, + tracker: tracker, + maxLength: self.messageBodySizeLimit + ) return .literalStreamingBegin(kind: type, byteCount: literalSize) } - func parseFetchResponse_streamingBeginQuoted(buffer: inout ParseBuffer, tracker: StackTracker) throws -> _FetchResponse { + func parseFetchResponse_streamingBeginQuoted( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> _FetchResponse { let type = try self.parseFetchStreamingResponse(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let save = buffer @@ -307,11 +353,15 @@ extension GrammarParser { return .finish } - return try PL.parseOneOf([ - parseFetchResponse_streamingBegin, - parseFetchResponse_streamingBeginQuoted, - parseFetchResponse_simpleAttribute, - parseFetchResponse_finish, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseFetchResponse_streamingBegin, + parseFetchResponse_streamingBeginQuoted, + parseFetchResponse_simpleAttribute, + parseFetchResponse_finish, + ], + buffer: &buffer, + tracker: tracker + ) } } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+List.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+List.swift index 6705d30c3..e26e45453 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+List.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+List.swift @@ -26,12 +26,18 @@ import struct NIO.ByteBufferView extension GrammarParser { // list-select-base-opt = "SUBSCRIBED" / option-extension func parseListSelectBaseOption(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectBaseOption { - func parseListSelectBaseOption_subscribed(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectBaseOption { + func parseListSelectBaseOption_subscribed( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectBaseOption { try PL.parseFixedString("SUBSCRIBED", buffer: &buffer, tracker: tracker) return .subscribed } - func parseListSelectBaseOption_optionExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectBaseOption { + func parseListSelectBaseOption_optionExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectBaseOption { .option(try self.parseOptionExtension(buffer: &buffer, tracker: tracker)) } @@ -44,7 +50,10 @@ extension GrammarParser { } // list-select-base-opt-quoted = DQUOTE list-select-base-opt DQUOTE - func parseListSelectBaseOptionQuoted(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectBaseOption { + func parseListSelectBaseOptionQuoted( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectBaseOption { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> ListSelectBaseOption in try PL.parseFixedString("\"", buffer: &buffer, tracker: tracker) let option = try self.parseListSelectBaseOption(buffer: &buffer, tracker: tracker) @@ -54,13 +63,22 @@ extension GrammarParser { } // list-select-independent-opt = "REMOTE" / option-extension - func parseListSelectIndependentOption(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectIndependentOption { - func parseListSelectIndependentOption_subscribed(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectIndependentOption { + func parseListSelectIndependentOption( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectIndependentOption { + func parseListSelectIndependentOption_subscribed( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectIndependentOption { try PL.parseFixedString("REMOTE", buffer: &buffer, tracker: tracker) return .remote } - func parseListSelectIndependentOption_optionExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectIndependentOption { + func parseListSelectIndependentOption_optionExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectIndependentOption { .option(try self.parseOptionExtension(buffer: &buffer, tracker: tracker)) } @@ -75,7 +93,10 @@ extension GrammarParser { // list-select-opt = list-select-base-opt / list-select-independent-opt // / list-select-mod-opt func parseListSelectOption(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectOption { - func parseListSelectOption_subscribed(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectOption { + func parseListSelectOption_subscribed( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectOption { try PL.parseFixedString("SUBSCRIBED", buffer: &buffer, tracker: tracker) return .subscribed } @@ -85,27 +106,40 @@ extension GrammarParser { return .remote } - func parseListSelectOption_recursiveMatch(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectOption { + func parseListSelectOption_recursiveMatch( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectOption { try PL.parseFixedString("RECURSIVEMATCH", buffer: &buffer, tracker: tracker) return .recursiveMatch } - func parseListSelectOption_specialUse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectOption { + func parseListSelectOption_specialUse( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectOption { try PL.parseFixedString("SPECIAL-USE", buffer: &buffer, tracker: tracker) return .specialUse } - func parseListSelectOption_optionExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectOption { + func parseListSelectOption_optionExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ListSelectOption { .option(try self.parseOptionExtension(buffer: &buffer, tracker: tracker)) } - return try PL.parseOneOf([ - parseListSelectOption_subscribed, - parseListSelectOption_remote, - parseListSelectOption_recursiveMatch, - parseListSelectOption_specialUse, - parseListSelectOption_optionExtension, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseListSelectOption_subscribed, + parseListSelectOption_remote, + parseListSelectOption_recursiveMatch, + parseListSelectOption_specialUse, + parseListSelectOption_optionExtension, + ], + buffer: &buffer, + tracker: tracker + ) } // list-select-opts = "(" [ @@ -117,13 +151,15 @@ extension GrammarParser { func parseListSelectOptions(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ListSelectOptions { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - var selectOptions = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ListSelectOption in + var selectOptions = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ListSelectOption in let option = try self.parseListSelectOption(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) return option } let baseOption = try self.parseListSelectBaseOption(buffer: &buffer, tracker: tracker) - try PL.parseZeroOrMore(buffer: &buffer, into: &selectOptions, tracker: tracker) { (buffer, tracker) -> ListSelectOption in + try PL.parseZeroOrMore(buffer: &buffer, into: &selectOptions, tracker: tracker) { + (buffer, tracker) -> ListSelectOption in let option = try self.parseListSelectOption(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) return option @@ -137,9 +173,11 @@ extension GrammarParser { func parseListReturnOptions(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [ReturnOption] { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in try PL.parseFixedString("RETURN (", buffer: &buffer, tracker: tracker) - let options = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [ReturnOption] in + let options = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> [ReturnOption] in var array = [try self.parseReturnOption(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> ReturnOption in + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> ReturnOption in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseReturnOption(buffer: &buffer, tracker: tracker) } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Mailbox.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Mailbox.swift index bd5a0d04d..04e7f6d36 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Mailbox.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Mailbox.swift @@ -57,7 +57,8 @@ extension GrammarParser { func parseMailboxData_search(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxData { try PL.parseFixedString("SEARCH", buffer: &buffer, tracker: tracker) - let nums = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> UnknownMessageIdentifier in + let nums = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> UnknownMessageIdentifier in try PL.parseSpaces(buffer: &buffer, tracker: tracker) let num = try self.parseNZNumber(buffer: &buffer, tracker: tracker) guard let id = UnknownMessageIdentifier(exactly: num) else { @@ -72,10 +73,15 @@ extension GrammarParser { try PL.parseFixedString("SEARCH", buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) var array = [try self.parseNZNumber(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker, parser: { (buffer, tracker) in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseNZNumber(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &array, + tracker: tracker, + parser: { (buffer, tracker) in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseNZNumber(buffer: &buffer, tracker: tracker) + } + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let seq = try self.parseSearchSortModificationSequence(buffer: &buffer, tracker: tracker) return .searchSort(.init(identifiers: array, modificationSequence: seq)) @@ -107,18 +113,22 @@ extension GrammarParser { .namespace(try self.parseNamespaceResponse(buffer: &buffer, tracker: tracker)) } - return try PL.parseOneOf([ - parseMailboxData_flags, - parseMailboxData_list, - parseMailboxData_lsub, - parseMailboxData_extendedSearch, - parseMailboxData_status, - parseMailboxData_exists, - parseMailboxData_recent, - parseMailboxData_searchSort, - parseMailboxData_search, - parseMailboxData_namespace, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseMailboxData_flags, + parseMailboxData_list, + parseMailboxData_lsub, + parseMailboxData_extendedSearch, + parseMailboxData_status, + parseMailboxData_exists, + parseMailboxData_recent, + parseMailboxData_searchSort, + parseMailboxData_search, + parseMailboxData_namespace, + ], + buffer: &buffer, + tracker: tracker + ) } // mailbox-list = "(" [mbx-list-flags] ")" SP @@ -131,7 +141,9 @@ extension GrammarParser { let character = try PL.parseByte(buffer: &buffer, tracker: tracker) guard character.isQuotedChar else { - throw ParserError(hint: "Expected quoted char found \(String(decoding: [character], as: Unicode.UTF8.self))") + throw ParserError( + hint: "Expected quoted char found \(String(decoding: [character], as: Unicode.UTF8.self))" + ) } try PL.parseFixedString("\"", buffer: &buffer, tracker: tracker) @@ -146,7 +158,8 @@ extension GrammarParser { return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> MailboxInfo in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let flags = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseMailboxListFlags) ?? [] + let flags = + try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseMailboxListFlags) ?? [] try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let character = try PL.parseOneOf( @@ -157,29 +170,46 @@ extension GrammarParser { ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let mailbox = try self.parseMailbox(buffer: &buffer, tracker: tracker) - let listExtended = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { (buffer, tracker) -> OrderedDictionary in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseMailboxListExtended(buffer: &buffer, tracker: tracker) - }) ?? [:] - return MailboxInfo(attributes: flags, path: try .init(name: mailbox, pathSeparator: character), extensions: listExtended) + let listExtended = + try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { (buffer, tracker) -> OrderedDictionary in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseMailboxListExtended(buffer: &buffer, tracker: tracker) + } + ) ?? [:] + return MailboxInfo( + attributes: flags, + path: try .init(name: mailbox, pathSeparator: character), + extensions: listExtended + ) } } // mbox-list-extended = "(" [mbox-list-extended-item // *(SP mbox-list-extended-item)] ")" - func parseMailboxListExtended(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { - try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> OrderedDictionary in + func parseMailboxListExtended( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { + try PL.composite(buffer: &buffer, tracker: tracker) { + buffer, + tracker -> OrderedDictionary in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let data = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> OrderedDictionary in - var kvs = OrderedDictionary() - let item = try self.parseMailboxListExtendedItem(buffer: &buffer, tracker: tracker) - kvs[item.key] = item.value - try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { (buffer, tracker) -> KeyValue in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseMailboxListExtendedItem(buffer: &buffer, tracker: tracker) - } - return kvs - } ?? [:] + let data = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> OrderedDictionary in + var kvs = OrderedDictionary() + let item = try self.parseMailboxListExtendedItem(buffer: &buffer, tracker: tracker) + kvs[item.key] = item.value + try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { + (buffer, tracker) -> KeyValue in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseMailboxListExtendedItem(buffer: &buffer, tracker: tracker) + } + return kvs + } ?? [:] try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return data } @@ -187,7 +217,10 @@ extension GrammarParser { // mbox-list-extended-item = mbox-list-extended-item-tag SP // tagged-ext-val - func parseMailboxListExtendedItem(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { + func parseMailboxListExtendedItem( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> KeyValue in let tag = try self.parseAString(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -244,17 +277,22 @@ extension GrammarParser { case appendLimit(Int) } - func parseStatusAttributeValue_messages(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue { + func parseStatusAttributeValue_messages(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue + { try PL.parseFixedString("MESSAGES ", buffer: &buffer, tracker: tracker) return .messages(try self.parseNumber(buffer: &buffer, tracker: tracker)) } - func parseStatusAttributeValue_uidnext(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue { + func parseStatusAttributeValue_uidnext(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue + { try PL.parseFixedString("UIDNEXT ", buffer: &buffer, tracker: tracker) return .uidNext(try self.parseMessageIdentifier(buffer: &buffer, tracker: tracker)) } - func parseStatusAttributeValue_uidvalidity(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue { + func parseStatusAttributeValue_uidvalidity( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MailboxValue { try PL.parseFixedString("UIDVALIDITY ", buffer: &buffer, tracker: tracker) return .uidValidity(try self.parseUIDValidity(buffer: &buffer, tracker: tracker)) } @@ -269,7 +307,10 @@ extension GrammarParser { return .size(try self.parseNumber(buffer: &buffer, tracker: tracker)) } - func parseStatusAttributeValue_modificationSequence(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue { + func parseStatusAttributeValue_modificationSequence( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MailboxValue { try PL.parseFixedString("HIGHESTMODSEQ ", buffer: &buffer, tracker: tracker) return .highestModifierSequence(try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker)) } @@ -279,28 +320,36 @@ extension GrammarParser { return .recent(try self.parseNumber(buffer: &buffer, tracker: tracker)) } - func parseStatusAttributeValue_appendLimit(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue { + func parseStatusAttributeValue_appendLimit( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MailboxValue { try PL.parseFixedString("APPENDLIMIT ", buffer: &buffer, tracker: tracker) return .appendLimit(try self.parseNumber(buffer: &buffer, tracker: tracker)) } func parseStatusAttributeValue(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxValue { - try PL.parseOneOf([ - parseStatusAttributeValue_messages, - parseStatusAttributeValue_uidnext, - parseStatusAttributeValue_uidvalidity, - parseStatusAttributeValue_unseen, - parseStatusAttributeValue_size, - parseStatusAttributeValue_modificationSequence, - parseStatusAttributeValue_recent, - parseStatusAttributeValue_appendLimit, - ], buffer: &buffer, tracker: tracker) + try PL.parseOneOf( + [ + parseStatusAttributeValue_messages, + parseStatusAttributeValue_uidnext, + parseStatusAttributeValue_uidvalidity, + parseStatusAttributeValue_unseen, + parseStatusAttributeValue_size, + parseStatusAttributeValue_modificationSequence, + parseStatusAttributeValue_recent, + parseStatusAttributeValue_appendLimit, + ], + buffer: &buffer, + tracker: tracker + ) } return try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> MailboxStatus in var array = [try parseStatusAttributeValue(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> MailboxValue in + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> MailboxValue in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try parseStatusAttributeValue(buffer: &buffer, tracker: tracker) } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Message.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Message.swift index 937c053c1..50facb3d9 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Message.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Message.swift @@ -42,31 +42,46 @@ extension GrammarParser { return try .vanishedEarlier(self.parseUIDSet(buffer: &buffer, tracker: tracker)) } - func parseMessageData_generateAuthorizedURL(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageData { + func parseMessageData_generateAuthorizedURL( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageData { try PL.parseFixedString("GENURLAUTH", buffer: &buffer, tracker: tracker) - let array = try PL.parseOneOrMore(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> ByteBuffer in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseAString(buffer: &buffer, tracker: tracker) - }) + let array = try PL.parseOneOrMore( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> ByteBuffer in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseAString(buffer: &buffer, tracker: tracker) + } + ) return .generateAuthorizedURL(array) } func parseMessageData_fetchData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageData { try PL.parseFixedString("URLFETCH", buffer: &buffer, tracker: tracker) - let array = try PL.parseOneOrMore(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> URLFetchData in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseURLFetchData(buffer: &buffer, tracker: tracker) - }) + let array = try PL.parseOneOrMore( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> URLFetchData in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseURLFetchData(buffer: &buffer, tracker: tracker) + } + ) return .urlFetch(array) } - return try PL.parseOneOf([ - parseMessageData_expunge, - parseMessageData_vanished, - parseMessageData_vanishedEarlier, - parseMessageData_generateAuthorizedURL, - parseMessageData_fetchData, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseMessageData_expunge, + parseMessageData_vanished, + parseMessageData_vanishedEarlier, + parseMessageData_generateAuthorizedURL, + parseMessageData_fetchData, + ], + buffer: &buffer, + tracker: tracker + ) } // msg-att-static = "ENVELOPE" SP envelope / "INTERNALDATE" SP date-time / @@ -87,17 +102,24 @@ extension GrammarParser { } } - func parseMessageAttribute_envelope(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_envelope(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute + { try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try .envelope(self.parseEnvelope(buffer: &buffer, tracker: tracker)) } - func parseMessageAttribute_internalDate(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_internalDate( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try .internalDate(self.parseInternalDate(buffer: &buffer, tracker: tracker)) } - func parseMessageAttribute_rfc822Size(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_rfc822Size( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try .rfc822Size(self.parseNumber(buffer: &buffer, tracker: tracker)) } @@ -105,19 +127,29 @@ extension GrammarParser { func parseMessageAttribute_body(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { // `BODY` can either be a body(structure) or a body section: - func parseMessageAttribute_body_structure(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_body_structure( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let body = try self.parseMessageAttributeBody(buffer: &buffer, tracker: tracker) return .body(body, hasExtensionData: false) } - return try PL.parseOneOf([ - parseMessageAttribute_body_structure, - parseMessageAttribute_bodySection_nilBody, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseMessageAttribute_body_structure, + parseMessageAttribute_bodySection_nilBody, + ], + buffer: &buffer, + tracker: tracker + ) } - func parseMessageAttribute_bodyStructure(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_bodyStructure( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let body = try self.parseMessageAttributeBody(buffer: &buffer, tracker: tracker) return .body(body, hasExtensionData: true) @@ -128,14 +160,20 @@ extension GrammarParser { return try .uid(self.parseMessageIdentifier(buffer: &buffer, tracker: tracker)) } - func parseMessageAttribute_binarySize(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_binarySize( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { let section = try self.parseSectionBinary(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let number = try self.parseNumber(buffer: &buffer, tracker: tracker) return .binarySize(section: section, size: number) } - func parseMessageAttribute_fetchModifierResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_fetchModifierResponse( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> MessageAttribute in try PL.parseSpaces(buffer: &buffer, tracker: tracker) try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) @@ -145,19 +183,28 @@ extension GrammarParser { } } - func parseMessageAttribute_gmailMessageID(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_gmailMessageID( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let (id, _) = try PL.parseUnsignedInt64(buffer: &buffer, tracker: tracker) return .gmailMessageID(id) } - func parseMessageAttribute_gmailThreadID(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_gmailThreadID( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let (id, _) = try PL.parseUnsignedInt64(buffer: &buffer, tracker: tracker) return .gmailThreadID(id) } - func parseMessageAttribute_gmailLabels(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_gmailLabels( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { var attributes: [GmailLabel] = [] try PL.parseSpaces(buffer: &buffer, tracker: tracker) try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) @@ -179,58 +226,84 @@ extension GrammarParser { return .gmailLabels(attributes) } - func parseMessageAttribute_rfc822Text_nilBody(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_rfc822Text_nilBody( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { let kind = try self.parseFetchStreamingResponse_rfc822Text(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) try self.parseNil(buffer: &buffer, tracker: tracker) return .nilBody(kind) } - func parseMessageAttribute_rfc822Header_nilBody(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_rfc822Header_nilBody( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { let kind = try self.parseFetchStreamingResponse_rfc822Header(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) try self.parseNil(buffer: &buffer, tracker: tracker) return .nilBody(kind) } - func parseMessageAttribute_bodySection_nilBody(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_bodySection_nilBody( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { let kind = try self.parseFetchStreamingResponse_bodySectionText(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) try self.parseNil(buffer: &buffer, tracker: tracker) return .nilBody(kind) } - func parseMessageAttribute_binary_nilBody(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_binary_nilBody( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { let kind = try self.parseFetchStreamingResponse_binary(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) try self.parseNil(buffer: &buffer, tracker: tracker) return .nilBody(kind) } - func parseMessageAttribute_preview(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { - func parseMessageAttribute_preview_literal(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_preview(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute + { + func parseMessageAttribute_preview_literal( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let raw = try self.parseLiteral(buffer: &buffer, tracker: tracker) return .preview(PreviewText(String(buffer: raw))) } - func parseMessageAttribute_preview_inline(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_preview_inline( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let raw = try self.parseQuoted(buffer: &buffer, tracker: tracker) return .preview(PreviewText(String(buffer: raw))) } - func parseMessageAttribute_preview_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageAttribute { + func parseMessageAttribute_preview_nil( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageAttribute { try PL.parseSpaces(buffer: &buffer, tracker: tracker) try self.parseNil(buffer: &buffer, tracker: tracker) return .preview(nil) } - return try PL.parseOneOf([ - parseMessageAttribute_preview_literal, - parseMessageAttribute_preview_inline, - parseMessageAttribute_preview_nil, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseMessageAttribute_preview_literal, + parseMessageAttribute_preview_inline, + parseMessageAttribute_preview_nil, + ], + buffer: &buffer, + tracker: tracker + ) } let parsers: [String: (inout ParseBuffer, StackTracker) throws -> MessageAttribute] = [ diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Partial.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Partial.swift index a44c3585f..71e0ef237 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Partial.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Partial.swift @@ -43,7 +43,7 @@ extension GrammarParser { let a: SequenceNumber = try parseMessageIdentifier(buffer: &buffer, tracker: tracker) try PL.parseFixedString(":", buffer: &buffer, tracker: tracker) let b: SequenceNumber = try parseMessageIdentifier(buffer: &buffer, tracker: tracker) - let range = (a <= b) ? SequenceRange(a ... b) : SequenceRange(b ... a) + let range = (a <= b) ? SequenceRange(a...b) : SequenceRange(b...a) return .first(range) } @@ -52,7 +52,7 @@ extension GrammarParser { let a: SequenceNumber = try parseMessageIdentifier(buffer: &buffer, tracker: tracker) try PL.parseFixedString(":-", buffer: &buffer, tracker: tracker) let b: SequenceNumber = try parseMessageIdentifier(buffer: &buffer, tracker: tracker) - let range = (a <= b) ? SequenceRange(a ... b) : SequenceRange(b ... a) + let range = (a <= b) ? SequenceRange(a...b) : SequenceRange(b...a) return .last(range) } @@ -74,11 +74,17 @@ extension GrammarParser { /// ;; NIL indicates no results correspond to the requested range. /// ``` func parseSearchReturnData_partial(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnData { - func parseSearchReturnData_partial_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierSet { + func parseSearchReturnData_partial_nil( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierSet { try PL.parseFixedString("NIL", buffer: &buffer, tracker: tracker) return MessageIdentifierSet() } - func parseSearchReturnData_partial_set(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierSet { + func parseSearchReturnData_partial_set( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierSet { let set: LastCommandSet set = try parseMessageIdentifierSetOrLast(buffer: &buffer, tracker: tracker) guard case .set(let result) = set else { diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Response.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Response.swift index 0bd37e5c7..3de6396dd 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Response.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Response.swift @@ -65,7 +65,13 @@ extension GrammarParser { let sourceUIDRanges = try self.parseUIDRangeArray(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let destinationUIDRanges = try self.parseUIDRangeArray(buffer: &buffer, tracker: tracker) - return .uidCopy(ResponseCodeCopy(destinationUIDValidity: uidValidity, sourceUIDs: sourceUIDRanges, destinationUIDs: destinationUIDRanges)) + return .uidCopy( + ResponseCodeCopy( + destinationUIDValidity: uidValidity, + sourceUIDs: sourceUIDRanges, + destinationUIDs: destinationUIDRanges + ) + ) } } @@ -131,27 +137,41 @@ extension GrammarParser { // response-payload = resp-cond-state / resp-cond-bye / mailbox-data / message-data / capability-data / id-response / enable-data func parseResponsePayload(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload { - func parseResponsePayload_conditionalState(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload { + func parseResponsePayload_conditionalState( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponsePayload { .conditionalState(try self.parseUntaggedResponseStatus(buffer: &buffer, tracker: tracker)) } - func parseResponsePayload_mailboxData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload { + func parseResponsePayload_mailboxData( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponsePayload { .mailboxData(try self.parseMailboxData(buffer: &buffer, tracker: tracker)) } - func parseResponsePayload_messageData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload { + func parseResponsePayload_messageData( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponsePayload { .messageData(try self.parseMessageData(buffer: &buffer, tracker: tracker)) } - func parseResponsePayload_capabilityData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload { + func parseResponsePayload_capabilityData( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponsePayload { .capabilityData(try self.parseCapabilityData(buffer: &buffer, tracker: tracker)) } - func parseResponsePayload_idResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload { + func parseResponsePayload_idResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload + { .id(try self.parseIDResponse(buffer: &buffer, tracker: tracker)) } - func parseResponsePayload_enableData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload { + func parseResponsePayload_enableData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponsePayload + { .enableData(try self.parseEnableData(buffer: &buffer, tracker: tracker)) } @@ -159,23 +179,28 @@ extension GrammarParser { .metadata(try self.parseMetadataResponse(buffer: &buffer, tracker: tracker)) } - return try PL.parseOneOf([ - parseResponsePayload_mailboxData, - parseResponsePayload_messageData, - parseResponsePayload_capabilityData, - parseResponsePayload_idResponse, - parseResponsePayload_enableData, - parseResponsePayload_quota, - parseResponsePayload_quotaRoot, - parseResponsePayload_metadata, - parseResponsePayload_conditionalState, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseResponsePayload_mailboxData, + parseResponsePayload_messageData, + parseResponsePayload_capabilityData, + parseResponsePayload_idResponse, + parseResponsePayload_enableData, + parseResponsePayload_quota, + parseResponsePayload_quotaRoot, + parseResponsePayload_metadata, + parseResponsePayload_conditionalState, + ], + buffer: &buffer, + tracker: tracker + ) } // resp-text = ["[" resp-text-code "]" SP] text func parseResponseText(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseText { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> ResponseText in - let code = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ResponseTextCode in + let code = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ResponseTextCode in try PL.parseFixedString("[", buffer: &buffer, tracker: tracker) let code = try self.parseResponseTextCode(buffer: &buffer, tracker: tracker) try PL.parseFixedString("]", buffer: &buffer, tracker: tracker) @@ -204,9 +229,14 @@ extension GrammarParser { return .modified(try self.parseMessageIdentifierSetOrLast(buffer: &buffer, tracker: tracker)) } - func parseSuffix_highestModifiedSequence(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { + func parseSuffix_highestModifiedSequence( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseTextCode { try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return .highestModificationSequence(try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker)) + return .highestModificationSequence( + try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker) + ) } func parseSuffix_referral(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { @@ -215,16 +245,18 @@ extension GrammarParser { } func parseSuffix_badCharset(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { - let charsets = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [String] in - try PL.parseFixedString(" (", buffer: &buffer, tracker: tracker) - var array = [try self.parseCharset(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> String in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseCharset(buffer: &buffer, tracker: tracker) - } - try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) - return array - } ?? [] + let charsets = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [String] in + try PL.parseFixedString(" (", buffer: &buffer, tracker: tracker) + var array = [try self.parseCharset(buffer: &buffer, tracker: tracker)] + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> String in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseCharset(buffer: &buffer, tracker: tracker) + } + try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) + return array + } ?? [] return .badCharset(charsets) } @@ -235,7 +267,8 @@ extension GrammarParser { func parseSuffix_permanentFlags(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { try PL.parseSpaces(buffer: &buffer, tracker: tracker) try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let array = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [PermanentFlag] in + let array = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> [PermanentFlag] in var array = [try self.parseFlagPerm(buffer: &buffer, tracker: tracker)] try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) in try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -263,45 +296,65 @@ extension GrammarParser { } func parseSuffix_metadata(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { - func parseSuffix_metadataLongEntries(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { + func parseSuffix_metadataLongEntries( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseTextCode { try PL.parseFixedString("LONGENTRIES ", buffer: &buffer, tracker: tracker) let num = try self.parseNumber(buffer: &buffer, tracker: tracker) return .metadataLongEntries(num) } - func parseSuffix_metadataMaxSize(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { + func parseSuffix_metadataMaxSize( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseTextCode { try PL.parseFixedString("MAXSIZE ", buffer: &buffer, tracker: tracker) let num = try self.parseNumber(buffer: &buffer, tracker: tracker) return .metadataMaxsize(num) } - func parseSuffix_metadataTooMany(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { + func parseSuffix_metadataTooMany( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseTextCode { try PL.parseFixedString("TOOMANY", buffer: &buffer, tracker: tracker) return .metadataTooMany } - func parseSuffix_metadataNoPrivate(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { + func parseSuffix_metadataNoPrivate( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseTextCode { try PL.parseFixedString("NOPRIVATE", buffer: &buffer, tracker: tracker) return .metadataNoPrivate } try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try PL.parseOneOf([ - parseSuffix_metadataLongEntries, - parseSuffix_metadataMaxSize, - parseSuffix_metadataTooMany, - parseSuffix_metadataNoPrivate, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseSuffix_metadataLongEntries, + parseSuffix_metadataMaxSize, + parseSuffix_metadataTooMany, + parseSuffix_metadataNoPrivate, + ], + buffer: &buffer, + tracker: tracker + ) } func parseSuffix_urlMechanisms(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseTextCode { try PL.parseSpaces(buffer: &buffer, tracker: tracker) try PL.parseFixedString("INTERNAL", buffer: &buffer, tracker: tracker) - let array = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> MechanismBase64 in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseMechanismBase64(buffer: &buffer, tracker: tracker) - }) + let array = try PL.parseZeroOrMore( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> MechanismBase64 in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseMechanismBase64(buffer: &buffer, tracker: tracker) + } + ) return .urlMechanisms(array) } @@ -372,10 +425,14 @@ extension GrammarParser { try parseFromLookupTable(buffer: &buffer, tracker: tracker, parsers: commandParsers) } - return try PL.parseOneOf([ - parseKnownResponseTextCode, - parseResponseTextCode_atom, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseKnownResponseTextCode, + parseResponseTextCode_atom, + ], + buffer: &buffer, + tracker: tracker + ) } // quota_response ::= "QUOTA" SP astring SP quota_list @@ -397,7 +454,8 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var resources: [QuotaResource] = [] - while let resource = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: parseQuotaResource) { + while let resource = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: parseQuotaResource) + { resources.append(resource) if try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: PL.parseSpaces) == nil { break @@ -418,9 +476,10 @@ extension GrammarParser { } // quotaroot_response ::= "QUOTAROOT" SP astring *(SP astring) - func parseResponsePayload_quotaRoot(buffer: inout ParseBuffer, - tracker: StackTracker) throws -> ResponsePayload - { + func parseResponsePayload_quotaRoot( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponsePayload { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseFixedString("QUOTAROOT ", buffer: &buffer, tracker: tracker) let mailbox = try parseMailbox(buffer: &buffer, tracker: tracker) diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Search.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Search.swift index afbe3204d..e24e134cc 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Search.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Search.swift @@ -97,17 +97,15 @@ extension GrammarParser { try parseSearchCorrelator_once(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) try parseSearchCorrelator_once(buffer: &buffer, tracker: tracker) - if let tag = tag, mailbox != nil, uidValidity != nil { - result = SearchCorrelator(tag: tag, mailbox: mailbox, uidValidity: uidValidity) - } else { + guard let tag = tag, mailbox != nil, uidValidity != nil else { throw ParserError(hint: "Not all components present for SearchCorrelator") } + result = SearchCorrelator(tag: tag, mailbox: mailbox, uidValidity: uidValidity) } else { - if let tag = tag { - result = SearchCorrelator(tag: tag) - } else { + guard let tag = tag else { throw ParserError(hint: "tag missing for SearchCorrelator") } + result = SearchCorrelator(tag: tag) } try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) @@ -144,7 +142,12 @@ extension GrammarParser { // "UID" SP sequence-set / "UNDRAFT" / sequence-set / // "(" search-key *(SP search-key) ")" func parseSearchKey(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchKey { - func parseSearchKey_fixed(string: String, result: SearchKey, buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchKey { + func parseSearchKey_fixed( + string: String, + result: SearchKey, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SearchKey { try PL.parseFixedString(string, buffer: &buffer, tracker: tracker) return result } @@ -290,17 +293,31 @@ extension GrammarParser { func parseSearchKey_uid(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchKey { try PL.parseFixedString("UID ", buffer: &buffer, tracker: tracker) - return .uid(try self.parseLastCommandSet(buffer: &buffer, tracker: tracker, setParser: self.parseUIDSetNonEmpty)) + return .uid( + try self.parseLastCommandSet(buffer: &buffer, tracker: tracker, setParser: self.parseUIDSetNonEmpty) + ) } func parseSearchKey_uidAfter(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchKey { try PL.parseFixedString("UIDAFTER ", buffer: &buffer, tracker: tracker) - return .uidAfter(try self.parseLastCommandMessageID(buffer: &buffer, tracker: tracker, setParser: self.parseMessageIdentifier)) + return .uidAfter( + try self.parseLastCommandMessageID( + buffer: &buffer, + tracker: tracker, + setParser: self.parseMessageIdentifier + ) + ) } func parseSearchKey_uidBefore(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchKey { try PL.parseFixedString("UIDBEFORE ", buffer: &buffer, tracker: tracker) - return .uidBefore(try self.parseLastCommandMessageID(buffer: &buffer, tracker: tracker, setParser: self.parseMessageIdentifier)) + return .uidBefore( + try self.parseLastCommandMessageID( + buffer: &buffer, + tracker: tracker, + setParser: self.parseMessageIdentifier + ) + ) } func parseSearchKey_sequenceSet(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchKey { @@ -316,11 +333,10 @@ extension GrammarParser { } try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) - if array.count == 1 { - return array.first! - } else { + guard array.count == 1 else { return .and(array) } + return array.first! } func parseSearchKey_older(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchKey { @@ -337,53 +353,69 @@ extension GrammarParser { .modificationSequence(try self.parseSearchModificationSequence(buffer: &buffer, tracker: tracker)) } - return try PL.parseOneOf([ - parseSearchKey_older, - parseSearchKey_fixedOptions, - parseSearchKey_younger, - parseSearchKey_bcc, - parseSearchKey_before, - parseSearchKey_body, - parseSearchKey_cc, - parseSearchKey_from, - parseSearchKey_keyword, - parseSearchKey_on, - parseSearchKey_since, - parseSearchKey_subject, - parseSearchKey_text, - parseSearchKey_to, - parseSearchKey_unkeyword, - parseSearchKey_header, - parseSearchKey_larger, - parseSearchKey_smaller, - parseSearchKey_not, - parseSearchKey_or, - parseSearchKey_sentBefore, - parseSearchKey_sentOn, - parseSearchKey_sentSince, - parseSearchKey_uid, - parseSearchKey_uidAfter, - parseSearchKey_uidBefore, - parseSearchKey_sequenceSet, - parseSearchKey_array, - parseSearchKey_filter, - parseSearchKey_modificationSequence, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseSearchKey_older, + parseSearchKey_fixedOptions, + parseSearchKey_younger, + parseSearchKey_bcc, + parseSearchKey_before, + parseSearchKey_body, + parseSearchKey_cc, + parseSearchKey_from, + parseSearchKey_keyword, + parseSearchKey_on, + parseSearchKey_since, + parseSearchKey_subject, + parseSearchKey_text, + parseSearchKey_to, + parseSearchKey_unkeyword, + parseSearchKey_header, + parseSearchKey_larger, + parseSearchKey_smaller, + parseSearchKey_not, + parseSearchKey_or, + parseSearchKey_sentBefore, + parseSearchKey_sentOn, + parseSearchKey_sentSince, + parseSearchKey_uid, + parseSearchKey_uidAfter, + parseSearchKey_uidBefore, + parseSearchKey_sequenceSet, + parseSearchKey_array, + parseSearchKey_filter, + parseSearchKey_modificationSequence, + ], + buffer: &buffer, + tracker: tracker + ) } - func parseSearchModificationSequence(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchModificationSequence { + func parseSearchModificationSequence( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SearchModificationSequence { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> SearchModificationSequence in try PL.parseFixedString("MODSEQ", buffer: &buffer, tracker: tracker) var extensions = OrderedDictionary() - try PL.parseZeroOrMore(buffer: &buffer, into: &extensions, tracker: tracker, parser: self.parseSearchModificationSequenceExtension) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &extensions, + tracker: tracker, + parser: self.parseSearchModificationSequenceExtension + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let val = try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker) return .init(extensions: extensions, sequenceValue: val) } } - func parseSearchModificationSequenceExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { - try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> KeyValue in + func parseSearchModificationSequenceExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { + try PL.composite(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> KeyValue in try PL.parseSpaces(buffer: &buffer, tracker: tracker) let flag = try self.parseEntryFlagName(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -393,7 +425,10 @@ extension GrammarParser { } // search-ret-data-ext = search-modifier-name SP search-return-value - func parseSearchReturnDataExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { + func parseSearchReturnDataExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> KeyValue in let modifier = try self.parseParameterName(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -429,17 +464,27 @@ extension GrammarParser { return .count(try self.parseNumber(buffer: &buffer, tracker: tracker)) } - func parseSearchReturnData_modificationSequence(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnData { + func parseSearchReturnData_modificationSequence( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SearchReturnData { try PL.parseFixedString("MODSEQ ", buffer: &buffer, tracker: tracker) return .modificationSequence(try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker)) } - func parseSearchReturnData_partial(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnData { - func parseSearchReturnData_partial_set(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierSet { + func parseSearchReturnData_partial(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnData + { + func parseSearchReturnData_partial_set( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierSet { try self.parseMessageIdentifierSet(buffer: &buffer, tracker: tracker).set } - func parseSearchReturnData_partial_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierSet { + func parseSearchReturnData_partial_nil( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierSet { try self.parseNil(buffer: &buffer, tracker: tracker) return [] } @@ -447,41 +492,54 @@ extension GrammarParser { try PL.parseFixedString("PARTIAL (", buffer: &buffer, tracker: tracker) let range = try parsePartialRange(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let identifiers = try PL.parseOneOf([ - parseSearchReturnData_partial_set, - parseSearchReturnData_partial_nil, - ], buffer: &buffer, tracker: tracker) + let identifiers = try PL.parseOneOf( + [ + parseSearchReturnData_partial_set, + parseSearchReturnData_partial_nil, + ], + buffer: &buffer, + tracker: tracker + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return .partial(range, identifiers) } - func parseSearchReturnData_dataExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnData { + func parseSearchReturnData_dataExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SearchReturnData { .dataExtension(try self.parseSearchReturnDataExtension(buffer: &buffer, tracker: tracker)) } - return try PL.parseOneOf([ - parseSearchReturnData_min, - parseSearchReturnData_max, - parseSearchReturnData_all, - parseSearchReturnData_count, - parseSearchReturnData_modificationSequence, - parseSearchReturnData_partial, - parseSearchReturnData_dataExtension, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseSearchReturnData_min, + parseSearchReturnData_max, + parseSearchReturnData_all, + parseSearchReturnData_count, + parseSearchReturnData_modificationSequence, + parseSearchReturnData_partial, + parseSearchReturnData_dataExtension, + ], + buffer: &buffer, + tracker: tracker + ) } // search-return-opts = SP "RETURN" SP "(" [search-return-opt *(SP search-return-opt)] ")" func parseSearchReturnOptions(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [SearchReturnOption] { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in try PL.parseFixedString(" RETURN (", buffer: &buffer, tracker: tracker) - let array = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [SearchReturnOption] in - var array = [try self.parseSearchReturnOption(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> SearchReturnOption in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseSearchReturnOption(buffer: &buffer, tracker: tracker) - } - return array - } ?? [] + let array = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> [SearchReturnOption] in + var array = [try self.parseSearchReturnOption(buffer: &buffer, tracker: tracker)] + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> SearchReturnOption in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseSearchReturnOption(buffer: &buffer, tracker: tracker) + } + return array + } ?? [] try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return array } @@ -492,59 +550,80 @@ extension GrammarParser { // "PARTIAL" SP partial-range // search-ret-opt-ext func parseSearchReturnOption(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { - func parseSearchReturnOption_min(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { + func parseSearchReturnOption_min(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption + { try PL.parseFixedString("MIN", buffer: &buffer, tracker: tracker) return .min } - func parseSearchReturnOption_max(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { + func parseSearchReturnOption_max(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption + { try PL.parseFixedString("MAX", buffer: &buffer, tracker: tracker) return .max } - func parseSearchReturnOption_all(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { + func parseSearchReturnOption_all(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption + { try PL.parseFixedString("ALL", buffer: &buffer, tracker: tracker) return .all } - func parseSearchReturnOption_count(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { + func parseSearchReturnOption_count( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SearchReturnOption { try PL.parseFixedString("COUNT", buffer: &buffer, tracker: tracker) return .count } - func parseSearchReturnOption_save(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { + func parseSearchReturnOption_save(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption + { try PL.parseFixedString("SAVE", buffer: &buffer, tracker: tracker) return .save } - func parseSearchReturnOption_partial(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { + func parseSearchReturnOption_partial( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SearchReturnOption { try PL.parseFixedString("PARTIAL", buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let range = try parsePartialRange(buffer: &buffer, tracker: tracker) return .partial(range) } - func parseSearchReturnOption_extension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SearchReturnOption { + func parseSearchReturnOption_extension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SearchReturnOption { let optionExtension = try self.parseSearchReturnOptionExtension(buffer: &buffer, tracker: tracker) return .optionExtension(optionExtension) } - return try PL.parseOneOf([ - parseSearchReturnOption_min, - parseSearchReturnOption_max, - parseSearchReturnOption_all, - parseSearchReturnOption_count, - parseSearchReturnOption_save, - parseSearchReturnOption_partial, - parseSearchReturnOption_extension, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseSearchReturnOption_min, + parseSearchReturnOption_max, + parseSearchReturnOption_all, + parseSearchReturnOption_count, + parseSearchReturnOption_save, + parseSearchReturnOption_partial, + parseSearchReturnOption_extension, + ], + buffer: &buffer, + tracker: tracker + ) } // search-ret-opt-ext = search-modifier-name [SP search-mod-params] - func parseSearchReturnOptionExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { + func parseSearchReturnOptionExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> KeyValue in let name = try self.parseParameterName(buffer: &buffer, tracker: tracker) - let params = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ParameterValue in + let params = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ParameterValue in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseParameterValue(buffer: &buffer, tracker: tracker) } @@ -552,7 +631,10 @@ extension GrammarParser { } } - func parseSearchSortModificationSequence(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ModificationSequenceValue { + func parseSearchSortModificationSequence( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ModificationSequenceValue { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ModificationSequenceValue in try PL.parseFixedString("(MODSEQ ", buffer: &buffer, tracker: tracker) let modSeq = try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker) diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Sequence.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Sequence.swift index 5e0e22b7b..a59f8db56 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Sequence.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+Sequence.swift @@ -25,7 +25,10 @@ import struct NIO.ByteBufferView extension GrammarParser { // Sequence Range - func parseMessageIdentifierRange(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierRange { + func parseMessageIdentifierRange( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierRange { func parse_wildcard(buffer: inout ParseBuffer, tracker: StackTracker) throws -> T { try PL.parseFixedString("*", buffer: &buffer, tracker: tracker) return .max @@ -47,24 +50,35 @@ extension GrammarParser { return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> MessageIdentifierRange in let id1: T = try parse_identifierOrWildcard(buffer: &buffer, tracker: tracker) - let id2: T? = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: parse_colonAndIdentifierOrWildcard) - if let id2 = id2 { - guard id1 <= id2 else { - throw ParserError(hint: "Invalid range, \(id1):\(id2)") - } - return MessageIdentifierRange(id1 ... id2) - } else { + let id2: T? = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: parse_colonAndIdentifierOrWildcard + ) + guard let id2 = id2 else { return MessageIdentifierRange(id1) } + guard id1 <= id2 else { + throw ParserError(hint: "Invalid range, \(id1):\(id2)") + } + return MessageIdentifierRange(id1...id2) } } func parseSequenceMatchData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SequenceMatchData { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let knownSequenceSet = try self.parseLastCommandSet(buffer: &buffer, tracker: tracker, setParser: self.parseUIDSetNonEmpty) + let knownSequenceSet = try self.parseLastCommandSet( + buffer: &buffer, + tracker: tracker, + setParser: self.parseUIDSetNonEmpty + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let knownUidSet = try self.parseLastCommandSet(buffer: &buffer, tracker: tracker, setParser: self.parseUIDSetNonEmpty) + let knownUidSet = try self.parseLastCommandSet( + buffer: &buffer, + tracker: tracker, + setParser: self.parseUIDSetNonEmpty + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return SequenceMatchData(knownSequenceSet: knownSequenceSet, knownUidSet: knownUidSet) } @@ -81,12 +95,21 @@ extension GrammarParser { } // sequence-set = (seq-number / seq-range) ["," sequence-set] - func parseMessageIdentifierSet(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierSetNonEmpty { - func parseMessageIdentifierSet_number(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierRange { + func parseMessageIdentifierSet( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierSetNonEmpty { + func parseMessageIdentifierSet_number( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierRange { MessageIdentifierRange(try self.parseMessageIdentifier(buffer: &buffer, tracker: tracker)) } - func parseMessageIdentifierSet_element(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierRange { + func parseMessageIdentifierSet_element( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierRange { try PL.parseOneOf( self.parseMessageIdentifierRange, parseMessageIdentifierSet_number, @@ -95,7 +118,9 @@ extension GrammarParser { ) } - var output: [MessageIdentifierRange] = [try parseMessageIdentifierSet_element(buffer: &buffer, tracker: tracker)] + var output: [MessageIdentifierRange] = [ + try parseMessageIdentifierSet_element(buffer: &buffer, tracker: tracker) + ] try PL.parseZeroOrMore(buffer: &buffer, into: &output, tracker: tracker) { buffer, tracker in try PL.parseFixedString(",", buffer: &buffer, tracker: tracker) return try parseMessageIdentifierSet_element(buffer: &buffer, tracker: tracker) @@ -111,12 +136,21 @@ extension GrammarParser { // And from RFC 5182 // sequence-set =/ seq-last-command // seq-last-command = "$" - func parseMessageIdentifierSetOrLast(buffer: inout ParseBuffer, tracker: StackTracker) throws -> LastCommandSet { - func parseMessageIdentifierSet_base(buffer: inout ParseBuffer, tracker: StackTracker) throws -> LastCommandSet { + func parseMessageIdentifierSetOrLast( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> LastCommandSet { + func parseMessageIdentifierSet_base( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> LastCommandSet { .set(try self.parseMessageIdentifierSet(buffer: &buffer, tracker: tracker)) } - func parseMessageIdentifierSet_lastCommand(buffer: inout ParseBuffer, tracker: StackTracker) throws -> LastCommandSet { + func parseMessageIdentifierSet_lastCommand( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> LastCommandSet { try PL.parseFixedString("$", buffer: &buffer, tracker: tracker) return .lastCommand } @@ -130,9 +164,16 @@ extension GrammarParser { } // mod-sequence-valzer = "0" / mod-sequence-value - func parseModificationSequenceValue(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ModificationSequenceValue { + func parseModificationSequenceValue( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ModificationSequenceValue { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in - let (number, _) = try ParserLibrary.parseUnsignedInt64(buffer: &buffer, tracker: tracker, allowLeadingZeros: true) + let (number, _) = try ParserLibrary.parseUnsignedInt64( + buffer: &buffer, + tracker: tracker, + allowLeadingZeros: true + ) guard let v = ModificationSequenceValue(exactly: number) else { throw ParserError(hint: "Mod-seq value is too large.") } diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+UID.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+UID.swift index 9a30061d6..9aad4e882 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+UID.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser+UID.swift @@ -61,9 +61,13 @@ extension GrammarParser { } } - func parseUIDSetNonEmpty(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessageIdentifierSetNonEmpty { + func parseUIDSetNonEmpty( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MessageIdentifierSetNonEmpty { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - guard let set = MessageIdentifierSetNonEmpty(set: try self.parseUIDSet(buffer: &buffer, tracker: tracker)) else { + guard let set = MessageIdentifierSetNonEmpty(set: try self.parseUIDSet(buffer: &buffer, tracker: tracker)) + else { throw ParserError(hint: "Need at least one UID") } return set diff --git a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser.swift b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser.swift index bdb668620..0114989b8 100644 --- a/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser.swift +++ b/Sources/NIOIMAPCore/Parser/Grammar/GrammarParser.swift @@ -44,7 +44,11 @@ struct GrammarParser { let messageBodySizeLimit: Int /// - parameter parseCache - init(literalSizeLimit: Int = IMAPDefaults.literalSizeLimit, messageBodySizeLimit: Int = IMAPDefaults.bodySizeLimit, parsedStringCache: ((String) -> String)? = nil) { + init( + literalSizeLimit: Int = IMAPDefaults.literalSizeLimit, + messageBodySizeLimit: Int = IMAPDefaults.bodySizeLimit, + parsedStringCache: ((String) -> String)? = nil + ) { self.literalSizeLimit = literalSizeLimit self.messageBodySizeLimit = messageBodySizeLimit self.parsedStringCache = parsedStringCache ?? Self.defaultParsedStringCache @@ -63,7 +67,11 @@ extension GrammarParser { /// - parameter parsers: A dictionary that maps a string to a sub-parser. /// - returns: `T` if a suitable sub-parser was located and executed. /// - throws: A `ParserError` if a parser wasn't found. - func parseFromLookupTable(buffer: inout ParseBuffer, tracker: StackTracker, parsers: [String: (inout ParseBuffer, StackTracker) throws -> T]) throws -> T { + func parseFromLookupTable( + buffer: inout ParseBuffer, + tracker: StackTracker, + parsers: [String: (inout ParseBuffer, StackTracker) throws -> T] + ) throws -> T { let save = buffer do { let parsed = try PL.parseOneOrMoreCharacters(buffer: &buffer, tracker: tracker) { char -> Bool in @@ -180,7 +188,9 @@ extension GrammarParser { // base64 = *(4base64-char) [base64-terminal] func parseBase64(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ByteBuffer { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> ByteBuffer in - let bytes = try PL.parseZeroOrMoreCharacters(buffer: &buffer, tracker: tracker) { $0.isBase64Char || $0 == UInt8(ascii: "=") } + let bytes = try PL.parseZeroOrMoreCharacters(buffer: &buffer, tracker: tracker) { + $0.isBase64Char || $0 == UInt8(ascii: "=") + } do { let decoded = try Base64.decode(bytes: bytes.readableBytesView) return ByteBuffer(bytes: decoded) @@ -243,7 +253,8 @@ extension GrammarParser { } } - func parseUnchangedSinceModifier(buffer: inout ParseBuffer, tracker: StackTracker) throws -> UnchangedSinceModifier { + func parseUnchangedSinceModifier(buffer: inout ParseBuffer, tracker: StackTracker) throws -> UnchangedSinceModifier + { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> UnchangedSinceModifier in try PL.parseFixedString("UNCHANGEDSINCE ", buffer: &buffer, tracker: tracker) let val = try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker) @@ -258,7 +269,8 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> [ListSelectBaseOption] in try PL.parseFixedString("CHILDINFO (", buffer: &buffer, tracker: tracker) var array = [try self.parseListSelectBaseOptionQuoted(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> ListSelectBaseOption in + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> ListSelectBaseOption in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseListSelectBaseOptionQuoted(buffer: &buffer, tracker: tracker) } @@ -274,7 +286,10 @@ extension GrammarParser { // continue-req = "+" SP (resp-text / base64) CRLF func parseContinuationRequest(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ContinuationRequest { - func parseContinuationRequest_base64(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ContinuationRequest { + func parseContinuationRequest_base64( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ContinuationRequest { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let base64 = try self.parseBase64(buffer: &buffer, tracker: tracker) try PL.parseNewline(buffer: &buffer, tracker: tracker) @@ -286,7 +301,10 @@ extension GrammarParser { return .data(base64) } - func parseContinuationRequest_responseText(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ContinuationRequest { + func parseContinuationRequest_responseText( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ContinuationRequest { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let responseText = try self.parseResponseText(buffer: &buffer, tracker: tracker) try PL.parseNewline(buffer: &buffer, tracker: tracker) @@ -294,17 +312,24 @@ extension GrammarParser { } // Allow no space and no additional text after "+": - func parseContinuationRequest_empty(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ContinuationRequest { + func parseContinuationRequest_empty( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ContinuationRequest { try PL.parseNewline(buffer: &buffer, tracker: tracker) return .responseText(ResponseText(code: nil, text: "")) } try PL.parseFixedString("+", buffer: &buffer, tracker: tracker) - return try PL.parseOneOf([ - parseContinuationRequest_base64, - parseContinuationRequest_responseText, - parseContinuationRequest_empty, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseContinuationRequest_base64, + parseContinuationRequest_responseText, + parseContinuationRequest_empty, + ], + buffer: &buffer, + tracker: tracker + ) } // create-param = create-param-name [SP create-param-value] @@ -322,11 +347,13 @@ extension GrammarParser { } func parseCreateParameter(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CreateParameter { - func parseCreateParameter_parameter(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CreateParameter { + func parseCreateParameter_parameter(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CreateParameter + { .labelled(try self.parseParameter(buffer: &buffer, tracker: tracker)) } - func parseCreateParameter_specialUse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CreateParameter { + func parseCreateParameter_specialUse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> CreateParameter + { try PL.parseFixedString("USE (", buffer: &buffer, tracker: tracker) var array = [try self.parseUseAttribute(buffer: &buffer, tracker: tracker)] try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) in @@ -357,7 +384,12 @@ extension GrammarParser { } func parseUseAttribute(buffer: inout ParseBuffer, tracker: StackTracker) throws -> UseAttribute { - func parseUseAttribute_fixed(expected: String, returning: UseAttribute, buffer: inout ParseBuffer, tracker: StackTracker) throws -> UseAttribute { + func parseUseAttribute_fixed( + expected: String, + returning: UseAttribute, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> UseAttribute { try PL.parseFixedString(expected, buffer: &buffer, tracker: tracker) return returning } @@ -396,16 +428,20 @@ extension GrammarParser { return .init("\\" + att) } - return try PL.parseOneOf([ - parseUseAttribute_all, - parseUseAttribute_archive, - parseUseAttribute_drafts, - parseUseAttribute_flagged, - parseUseAttribute_junk, - parseUseAttribute_sent, - parseUseAttribute_trash, - parseUseAttribute_other, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseUseAttribute_all, + parseUseAttribute_archive, + parseUseAttribute_drafts, + parseUseAttribute_flagged, + parseUseAttribute_junk, + parseUseAttribute_sent, + parseUseAttribute_trash, + parseUseAttribute_other, + ], + buffer: &buffer, + tracker: tracker + ) } // eitem-vendor-tag = vendor-token "-" atom @@ -418,7 +454,10 @@ extension GrammarParser { } } - func parseEncodedAuthenticationType(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EncodedAuthenticationType { + func parseEncodedAuthenticationType( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> EncodedAuthenticationType { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> EncodedAuthenticationType in let array = try PL.parseOneOrMore(buffer: &buffer, tracker: tracker, parser: self.parseAChar).reduce([], +) return .init(authenticationType: String(decoding: array, as: Unicode.UTF8.self)) @@ -479,15 +518,18 @@ extension GrammarParser { // esearch-response = "ESEARCH" [search-correlator] [SP "UID"] // *(SP search-return-data) - func parseExtendedSearchResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ExtendedSearchResponse { + func parseExtendedSearchResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ExtendedSearchResponse + { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in try PL.parseFixedString("ESEARCH", buffer: &buffer, tracker: tracker) let correlator = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseSearchCorrelator) - let kind: ExtendedSearchResponse.Kind = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in - try PL.parseFixedString(" UID", buffer: &buffer, tracker: tracker) - return .uid - } ?? .sequenceNumber - let searchReturnData = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> SearchReturnData in + let kind: ExtendedSearchResponse.Kind = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) in + try PL.parseFixedString(" UID", buffer: &buffer, tracker: tracker) + return .uid + } ?? .sequenceNumber + let searchReturnData = try PL.parseZeroOrMore(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> SearchReturnData in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseSearchReturnData(buffer: &buffer, tracker: tracker) } @@ -524,12 +566,16 @@ extension GrammarParser { return .anonymous } - return try PL.parseOneOf([ - parseAccess_submit, - parseAccess_user, - parseAccess_authenticatedUser, - parseAccess_anonymous, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseAccess_submit, + parseAccess_user, + parseAccess_authenticatedUser, + parseAccess_anonymous, + ], + buffer: &buffer, + tracker: tracker + ) } // filter-name = 1* @@ -553,10 +599,14 @@ extension GrammarParser { return .extension(word) } - return try PL.parseOneOf([ - parseFlag_keyword, - parseFlag_extension, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseFlag_keyword, + parseFlag_extension, + ], + buffer: &buffer, + tracker: tracker + ) } // flag-extension = "\" atom @@ -585,14 +635,15 @@ extension GrammarParser { func parseFlagList(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [Flag] { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let flags = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, _) -> [Flag] in - var output = [try self.parseFlag(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &output, tracker: tracker) { buffer, tracker in - try PL.parseFixedString(" ", buffer: &buffer, tracker: tracker) - return try self.parseFlag(buffer: &buffer, tracker: tracker) - } - return output - } ?? [] + let flags = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, _) -> [Flag] in + var output = [try self.parseFlag(buffer: &buffer, tracker: tracker)] + try PL.parseZeroOrMore(buffer: &buffer, into: &output, tracker: tracker) { buffer, tracker in + try PL.parseFixedString(" ", buffer: &buffer, tracker: tracker) + return try self.parseFlag(buffer: &buffer, tracker: tracker) + } + return output + } ?? [] try PL.parseFixedString(")", allowLeadingSpaces: true, buffer: &buffer, tracker: tracker) return flags } @@ -669,8 +720,15 @@ extension GrammarParser { } } - func parseLastCommandSet(buffer: inout ParseBuffer, tracker: StackTracker, setParser: SubParser>) throws -> LastCommandSet { - func parseLastCommandSet_lastCommand(buffer: inout ParseBuffer, tracker: StackTracker) throws -> LastCommandSet { + func parseLastCommandSet( + buffer: inout ParseBuffer, + tracker: StackTracker, + setParser: SubParser> + ) throws -> LastCommandSet { + func parseLastCommandSet_lastCommand( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> LastCommandSet { try PL.parseFixedString("$", buffer: &buffer, tracker: tracker) return .lastCommand } @@ -687,13 +745,23 @@ extension GrammarParser { ) } - func parseLastCommandMessageID(buffer: inout ParseBuffer, tracker: StackTracker, setParser: SubParser) throws -> LastCommandMessageID { - func parseLastCommandMessageID_lastCommand(buffer: inout ParseBuffer, tracker: StackTracker) throws -> LastCommandMessageID { + func parseLastCommandMessageID( + buffer: inout ParseBuffer, + tracker: StackTracker, + setParser: SubParser + ) throws -> LastCommandMessageID { + func parseLastCommandMessageID_lastCommand( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> LastCommandMessageID { try PL.parseFixedString("$", buffer: &buffer, tracker: tracker) return .lastCommand } - func parseLastCommandMessageID_id(buffer: inout ParseBuffer, tracker: StackTracker) throws -> LastCommandMessageID { + func parseLastCommandMessageID_id( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> LastCommandMessageID { .id(try setParser(&buffer, tracker)) } @@ -713,13 +781,22 @@ extension GrammarParser { } } - func parseIMAPURLAuthenticationMechanism(buffer: inout ParseBuffer, tracker: StackTracker) throws -> IMAPURLAuthenticationMechanism { - func parseIMAPURLAuthenticationMechanism_any(buffer: inout ParseBuffer, tracker: StackTracker) throws -> IMAPURLAuthenticationMechanism { + func parseIMAPURLAuthenticationMechanism( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> IMAPURLAuthenticationMechanism { + func parseIMAPURLAuthenticationMechanism_any( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> IMAPURLAuthenticationMechanism { try PL.parseFixedString("*", buffer: &buffer, tracker: tracker) return .any } - func parseIMAPURLAuthenticationMechanism_encoded(buffer: inout ParseBuffer, tracker: StackTracker) throws -> IMAPURLAuthenticationMechanism { + func parseIMAPURLAuthenticationMechanism_encoded( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> IMAPURLAuthenticationMechanism { let type = try self.parseEncodedAuthenticationType(buffer: &buffer, tracker: tracker) return .type(type) } @@ -736,7 +813,8 @@ extension GrammarParser { } // id-response = "ID" SP id-params-list - func parseIDResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseIDResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary + { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in try PL.parseFixedString("ID ", buffer: &buffer, tracker: tracker) let result = try parseIDParamsList(buffer: &buffer, tracker: tracker) @@ -749,16 +827,21 @@ extension GrammarParser { } // id-params-list = "(" *(string SP nstring) ")" / nil - func parseIDParamsList(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseIDParamsList( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { func parseIDValue(buffer: inout ParseBuffer, tracker: StackTracker) throws -> String? { - if let value = try self.parseNString(buffer: &buffer, tracker: tracker) { - return try ModifiedUTF7.decode(value) - } else { + guard let value = try self.parseNString(buffer: &buffer, tracker: tracker) else { return nil } + return try ModifiedUTF7.decode(value) } - func parseIDParamsList_nil(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseIDParamsList_nil( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { try self.parseNil(buffer: &buffer, tracker: tracker) return [:] } @@ -770,7 +853,10 @@ extension GrammarParser { return (key, try parseIDValue(buffer: &buffer, tracker: tracker)) } - func parseIDParamsList_empty(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseIDParamsList_empty( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) _ = try PL.parseOptional(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseSpaces(buffer: &buffer, tracker: tracker) @@ -779,14 +865,18 @@ extension GrammarParser { return [:] } - func parseIDParamsList_some(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseIDParamsList_some( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) _ = try PL.parseOptional(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseSpaces(buffer: &buffer, tracker: tracker) } let (key, value) = try parseIDParamsList_element(buffer: &buffer, tracker: tracker) var dic: OrderedDictionary = [key: value] - try PL.parseZeroOrMore(buffer: &buffer, into: &dic, tracker: tracker) { (buffer, tracker) -> (String, String?) in + try PL.parseZeroOrMore(buffer: &buffer, into: &dic, tracker: tracker) { + (buffer, tracker) -> (String, String?) in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try parseIDParamsList_element(buffer: &buffer, tracker: tracker) } @@ -817,7 +907,8 @@ extension GrammarParser { } } - func parseMessagePathByteRangeOnly(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessagePath.ByteRange { + func parseMessagePathByteRangeOnly(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessagePath.ByteRange + { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> MessagePath.ByteRange in try PL.parseFixedString(";PARTIAL=", buffer: &buffer, tracker: tracker) return .init(range: try self.parseByteRange(buffer: &buffer, tracker: tracker)) @@ -840,16 +931,24 @@ extension GrammarParser { func parseIMAPServer(buffer: inout ParseBuffer, tracker: StackTracker) throws -> IMAPServer { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> IMAPServer in - let info = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> UserAuthenticationMechanism in - let info = try self.parseUserAuthenticationMechanism(buffer: &buffer, tracker: tracker) - try PL.parseFixedString("@", buffer: &buffer, tracker: tracker) - return info - }) + let info = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> UserAuthenticationMechanism in + let info = try self.parseUserAuthenticationMechanism(buffer: &buffer, tracker: tracker) + try PL.parseFixedString("@", buffer: &buffer, tracker: tracker) + return info + } + ) let host = try self.parseHost(buffer: &buffer, tracker: tracker) - let port = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> Int in - try PL.parseFixedString(":", buffer: &buffer, tracker: tracker) - return try self.parseNumber(buffer: &buffer, tracker: tracker) - }) + let port = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> Int in + try PL.parseFixedString(":", buffer: &buffer, tracker: tracker) + return try self.parseNumber(buffer: &buffer, tracker: tracker) + } + ) return .init(userAuthenticationMechanism: info, host: host, port: port) } } @@ -917,10 +1016,14 @@ extension GrammarParser { func parseEncodedMailboxUIDValidity(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxUIDValidity { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> MailboxUIDValidity in let mailbox = try self.parseEncodedMailbox(buffer: &buffer, tracker: tracker) - let uidValidity = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> UIDValidity in - try PL.parseFixedString(";UIDVALIDITY=", buffer: &buffer, tracker: tracker) - return try self.parseUIDValidity(buffer: &buffer, tracker: tracker) - }) + let uidValidity = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> UIDValidity in + try PL.parseFixedString(";UIDVALIDITY=", buffer: &buffer, tracker: tracker) + return try self.parseUIDValidity(buffer: &buffer, tracker: tracker) + } + ) return .init(encodeMailbox: mailbox, uidValidity: uidValidity) } } @@ -928,10 +1031,14 @@ extension GrammarParser { func parseEncodedSearchQuery(buffer: inout ParseBuffer, tracker: StackTracker) throws -> EncodedSearchQuery { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> EncodedSearchQuery in let mailboxRef = try self.parseEncodedMailboxUIDValidity(buffer: &buffer, tracker: tracker) - let query = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> EncodedSearch in - try PL.parseFixedString("?", buffer: &buffer, tracker: tracker) - return try self.parseEncodedSearch(buffer: &buffer, tracker: tracker) - }) + let query = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> EncodedSearch in + try PL.parseFixedString("?", buffer: &buffer, tracker: tracker) + return try self.parseEncodedSearch(buffer: &buffer, tracker: tracker) + } + ) return .init(mailboxUIDValidity: mailboxRef, encodedSearch: query) } } @@ -959,11 +1066,15 @@ extension GrammarParser { .empty } - return try PL.parseOneOf([ - parseRelativeIMAPURL_network, - parseRelativeIMAPURL_absolute, - parseRelativeIMAPURL_empty, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseRelativeIMAPURL_network, + parseRelativeIMAPURL_absolute, + parseRelativeIMAPURL_empty, + ], + buffer: &buffer, + tracker: tracker + ) } func parseMessagePath(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MessagePath { @@ -987,10 +1098,18 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in section!.encodedSection.section = String(section!.encodedSection.section.dropLast()) - byteRange = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseMessagePathByteRangeOnly) + byteRange = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseMessagePathByteRangeOnly + ) } } else { - byteRange = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseMessagePathByteRange) + byteRange = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseMessagePathByteRange + ) } return .init(mailboxReference: ref, iUID: uid, section: section, range: byteRange) } @@ -1007,73 +1126,115 @@ extension GrammarParser { if section.encodedSection.section.last == Character(.init(UInt8(ascii: "/"))) { section.encodedSection.section = String(section.encodedSection.section.dropLast()) do { - let partial = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseMessagePathByteRangeOnly) + let partial = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseMessagePathByteRangeOnly + ) return .sectionPartial(section: section, partial: partial) } catch is ParserError { section.encodedSection.section.append("/") return .sectionPartial(section: section, partial: nil) } } - let partial = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> MessagePath.ByteRange in - try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) - return try self.parseMessagePathByteRangeOnly(buffer: &buffer, tracker: tracker) - }) + let partial = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> MessagePath.ByteRange in + try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) + return try self.parseMessagePathByteRangeOnly(buffer: &buffer, tracker: tracker) + } + ) return .sectionPartial(section: section, partial: partial) } - func parseURLFetchType_uidSectionPartial(buffer: inout ParseBuffer, tracker: StackTracker) throws -> URLFetchType { + func parseURLFetchType_uidSectionPartial( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> URLFetchType { let uid = try self.parseIUIDOnly(buffer: &buffer, tracker: tracker) - var section = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> URLMessageSection in - try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) - return try self.parseIMAPURLSectionOnly(buffer: &buffer, tracker: tracker) - }) + var section = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> URLMessageSection in + try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) + return try self.parseIMAPURLSectionOnly(buffer: &buffer, tracker: tracker) + } + ) if section?.encodedSection.section.last == Character(.init(UInt8(ascii: "/"))) { section!.encodedSection.section = String(section!.encodedSection.section.dropLast()) do { - let partial = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseMessagePathByteRangeOnly) + let partial = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseMessagePathByteRangeOnly + ) return .uidSectionPartial(uid: uid, section: section, partial: partial) } catch is ParserError { section?.encodedSection.section.append("/") return .uidSectionPartial(uid: uid, section: section, partial: nil) } } - let partial = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> MessagePath.ByteRange in - try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) - return try self.parseMessagePathByteRangeOnly(buffer: &buffer, tracker: tracker) - }) + let partial = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> MessagePath.ByteRange in + try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) + return try self.parseMessagePathByteRangeOnly(buffer: &buffer, tracker: tracker) + } + ) return .uidSectionPartial(uid: uid, section: section, partial: partial) } - func parseURLFetchType_refUidSectionPartial(buffer: inout ParseBuffer, tracker: StackTracker) throws -> URLFetchType { + func parseURLFetchType_refUidSectionPartial( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> URLFetchType { let ref = try self.parseEncodedMailboxUIDValidity(buffer: &buffer, tracker: tracker) let uid = try self.parseIUIDOnly(buffer: &buffer, tracker: tracker) - var section = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> URLMessageSection in - try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) - return try self.parseIMAPURLSectionOnly(buffer: &buffer, tracker: tracker) - }) + var section = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> URLMessageSection in + try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) + return try self.parseIMAPURLSectionOnly(buffer: &buffer, tracker: tracker) + } + ) if section?.encodedSection.section.last == Character(.init(UInt8(ascii: "/"))) { section!.encodedSection.section = String(section!.encodedSection.section.dropLast()) do { - let partial = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseMessagePathByteRangeOnly) + let partial = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseMessagePathByteRangeOnly + ) return .refUidSectionPartial(ref: ref, uid: uid, section: section, partial: partial) } catch is ParserError { section?.encodedSection.section.append("/") return .refUidSectionPartial(ref: ref, uid: uid, section: section, partial: nil) } } - let partial = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> MessagePath.ByteRange in - try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) - return try self.parseMessagePathByteRangeOnly(buffer: &buffer, tracker: tracker) - }) + let partial = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> MessagePath.ByteRange in + try PL.parseFixedString("/", buffer: &buffer, tracker: tracker) + return try self.parseMessagePathByteRangeOnly(buffer: &buffer, tracker: tracker) + } + ) return .refUidSectionPartial(ref: ref, uid: uid, section: section, partial: partial) } - return try PL.parseOneOf([ - parseURLFetchType_refUidSectionPartial, - parseURLFetchType_uidSectionPartial, - parseURLFetchType_sectionPartial, - parseURLFetchType_partialOnly, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseURLFetchType_refUidSectionPartial, + parseURLFetchType_uidSectionPartial, + parseURLFetchType_sectionPartial, + parseURLFetchType_partialOnly, + ], + buffer: &buffer, + tracker: tracker + ) } func parseUChar(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [UInt8] { @@ -1211,7 +1372,10 @@ extension GrammarParser { } } - func parseAuthenticatedURLVerifier(buffer: inout ParseBuffer, tracker: StackTracker) throws -> AuthenticatedURLVerifier { + func parseAuthenticatedURLVerifier( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> AuthenticatedURLVerifier { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> AuthenticatedURLVerifier in try PL.parseFixedString(":", buffer: &buffer, tracker: tracker) let authMechanism = try self.parseUAuthMechanism(buffer: &buffer, tracker: tracker) @@ -1221,11 +1385,18 @@ extension GrammarParser { } } - func parseUserAuthenticationMechanism(buffer: inout ParseBuffer, tracker: StackTracker) throws -> UserAuthenticationMechanism { + func parseUserAuthenticationMechanism( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> UserAuthenticationMechanism { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> UserAuthenticationMechanism in let encodedUser = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseEncodedUser) - let authenticationMechanism = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseIMAPURLAuthenticationMechanism) - guard (encodedUser != nil || authenticationMechanism != nil) else { + let authenticationMechanism = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseIMAPURLAuthenticationMechanism + ) + guard encodedUser != nil || authenticationMechanism != nil else { throw ParserError(hint: "Need one of encoded user or iauth") } return .init(encodedUser: encodedUser, authenticationMechanism: authenticationMechanism) @@ -1258,10 +1429,14 @@ extension GrammarParser { let minute = try parse2Digit(buffer: &buffer, tracker: tracker) try PL.parseFixedString(":", buffer: &buffer, tracker: tracker) let second = try parse2Digit(buffer: &buffer, tracker: tracker) - let fraction = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { buffer, tracker -> Int in - try PL.parseFixedString(".", buffer: &buffer, tracker: tracker) - return try self.parseNumber(buffer: &buffer, tracker: tracker) - }) + let fraction = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { buffer, tracker -> Int in + try PL.parseFixedString(".", buffer: &buffer, tracker: tracker) + return try self.parseNumber(buffer: &buffer, tracker: tracker) + } + ) return .init(hour: hour, minute: minute, second: second, fraction: fraction) } @@ -1341,14 +1516,22 @@ extension GrammarParser { // "MESSAGE" / "VIDEO") DQUOTE) / string) SP // media-subtype func parseMediaType(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Media.MediaType { - func parseMediaTopLevel_defined(_ option: String, result: Media.TopLevelType, buffer: inout ParseBuffer, tracker: StackTracker) throws -> Media.TopLevelType { + func parseMediaTopLevel_defined( + _ option: String, + result: Media.TopLevelType, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> Media.TopLevelType { try PL.parseFixedString("\"", buffer: &buffer, tracker: tracker) try PL.parseFixedString(option, buffer: &buffer, tracker: tracker) try PL.parseFixedString("\"", buffer: &buffer, tracker: tracker) return result } - func parseMediaTopLevel_application(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Media.TopLevelType { + func parseMediaTopLevel_application( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> Media.TopLevelType { try parseMediaTopLevel_defined("APPLICATION", result: .application, buffer: &buffer, tracker: tracker) } @@ -1374,14 +1557,18 @@ extension GrammarParser { } return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> Media.MediaType in - let topLevel = try PL.parseOneOf([ - parseMediaTopLevel_application, - parseMediaTopLevel_audio, - parseMediaTopLevel_image, - parseMediaTopLevel_message, - parseMediaTopLevel_video, - parseMediaTopLevel_other, - ], buffer: &buffer, tracker: tracker) + let topLevel = try PL.parseOneOf( + [ + parseMediaTopLevel_application, + parseMediaTopLevel_audio, + parseMediaTopLevel_image, + parseMediaTopLevel_message, + parseMediaTopLevel_video, + parseMediaTopLevel_other, + ], + buffer: &buffer, + tracker: tracker + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let sub = try self.parseMediaSubtype(buffer: &buffer, tracker: tracker) return Media.MediaType(topLevel: topLevel, sub: sub) @@ -1452,10 +1639,15 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var array = [try self.parseMetadataOption(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker, parser: { buffer, tracker in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseMetadataOption(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &array, + tracker: tracker, + parser: { buffer, tracker in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseMetadataOption(buffer: &buffer, tracker: tracker) + } + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return array } @@ -1543,7 +1735,11 @@ extension GrammarParser { func parseNamespace_some(buffer: inout ParseBuffer, tracker: StackTracker) throws -> [NamespaceDescription] { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let descriptions = try PL.parseOneOrMore(buffer: &buffer, tracker: tracker, parser: self.parseNamespaceDescription) + let descriptions = try PL.parseOneOrMore( + buffer: &buffer, + tracker: tracker, + parser: self.parseNamespaceDescription + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return descriptions } @@ -1592,9 +1788,13 @@ extension GrammarParser { } // Namespace-Response-Extensions = *(Namespace-Response-Extension) - func parseNamespaceResponseExtensions(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { + func parseNamespaceResponseExtensions( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { var kvs = OrderedDictionary() - try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { (buffer, tracker) -> KeyValue in + try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { + (buffer, tracker) -> KeyValue in try self.parseNamespaceResponseExtension(buffer: &buffer, tracker: tracker) } return kvs @@ -1602,7 +1802,10 @@ extension GrammarParser { // Namespace-Response-Extension = SP string SP // "(" string *(SP string) ")" - func parseNamespaceResponseExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { + func parseNamespaceResponseExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> KeyValue in try PL.parseSpaces(buffer: &buffer, tracker: tracker) let s1 = try self.parseString(buffer: &buffer, tracker: tracker) @@ -1677,23 +1880,35 @@ extension GrammarParser { // option-extension = (option-standard-tag / option-vendor-tag) // [SP option-value] - func parseOptionExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { - func parseOptionExtensionKind_standard(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OptionExtensionKind { + func parseOptionExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { + func parseOptionExtensionKind_standard( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OptionExtensionKind { .standard(try self.parseAtom(buffer: &buffer, tracker: tracker)) } - func parseOptionExtensionKind_vendor(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OptionExtensionKind { + func parseOptionExtensionKind_vendor( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OptionExtensionKind { .vendor(try self.parseOptionVendorTag(buffer: &buffer, tracker: tracker)) } - return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> KeyValue in + return try PL.composite(buffer: &buffer, tracker: tracker) { + buffer, + tracker -> KeyValue in let type = try PL.parseOneOf( parseOptionExtensionKind_standard, parseOptionExtensionKind_vendor, buffer: &buffer, tracker: tracker ) - let value = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> OptionValueComp in + let value = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> OptionValueComp in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseOptionValue(buffer: &buffer, tracker: tracker) } @@ -1718,7 +1933,8 @@ extension GrammarParser { func parseOptionValueComp_array(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OptionValueComp { var array = [try self.parseOptionValueComp(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> OptionValueComp in + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> OptionValueComp in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseOptionValueComp(buffer: &buffer, tracker: tracker) } @@ -1771,7 +1987,7 @@ extension GrammarParser { guard !upper1.overflow else { throw ParserError(hint: "Range is invalid: <\(num1).\(num2)>.") } let upper2 = upper1.partialValue.subtractingReportingOverflow(1) guard !upper2.overflow else { throw ParserError(hint: "Range is invalid: <\(num1).\(num2)>.") } - return num1 ... upper2.partialValue + return num1...upper2.partialValue } } @@ -1843,7 +2059,10 @@ extension GrammarParser { let special = escaped.readBytes(length: 1)?.first, special.isQuotedSpecial else { - throw ParserError(hint: "Invalid escape in quoted string '\(String(decoding: data.readableBytesView, as: Unicode.UTF8.self))'") + throw ParserError( + hint: + "Invalid escape in quoted string '\(String(decoding: data.readableBytesView, as: Unicode.UTF8.self))'" + ) } unescaped.writeBytes([special]) } else { @@ -1885,7 +2104,10 @@ extension GrammarParser { let special = escaped.readBytes(length: 1)?.first, special.isQuotedSpecial else { - throw ParserError(hint: "Invalid escape in quoted string '\(String(decoding: data.readableBytesView, as: Unicode.UTF8.self))'") + throw ParserError( + hint: + "Invalid escape in quoted string '\(String(decoding: data.readableBytesView, as: Unicode.UTF8.self))'" + ) } unescaped.writeBytes([special]) } else { @@ -1913,16 +2135,21 @@ extension GrammarParser { .statusOption(try self.parseStatusOption(buffer: &buffer, tracker: tracker)) } - func parseReturnOption_optionExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ReturnOption { + func parseReturnOption_optionExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ReturnOption + { .optionExtension(try self.parseOptionExtension(buffer: &buffer, tracker: tracker)) } - return try PL.parseOneOf([ - parseReturnOption_subscribed, - parseReturnOption_children, - parseReturnOption_statusOption, - parseReturnOption_optionExtension, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseReturnOption_subscribed, + parseReturnOption_children, + parseReturnOption_statusOption, + parseReturnOption_optionExtension, + ], + buffer: &buffer, + tracker: tracker + ) } func parseScopeOption(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ScopeOption { @@ -2013,12 +2240,14 @@ extension GrammarParser { } } - func parseSectionSpecifier_withPart(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier { + func parseSectionSpecifier_withPart(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier + { let part = try self.parseSectionPart(buffer: &buffer, tracker: tracker) - let kind = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> SectionSpecifier.Kind in - try PL.parseFixedString(".", buffer: &buffer, tracker: tracker) - return try self.parseSectionSpecifierKind(buffer: &buffer, tracker: tracker) - } ?? .complete + let kind = + try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> SectionSpecifier.Kind in + try PL.parseFixedString(".", buffer: &buffer, tracker: tracker) + return try self.parseSectionSpecifierKind(buffer: &buffer, tracker: tracker) + } ?? .complete return .init(part: part, kind: kind) } @@ -2032,43 +2261,65 @@ extension GrammarParser { // section-text = section-msgtext / "MIME" func parseSectionSpecifierKind(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier.Kind { - func parseSectionSpecifierKind_mime(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier.Kind { + func parseSectionSpecifierKind_mime( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SectionSpecifier.Kind { try PL.parseFixedString("MIME", buffer: &buffer, tracker: tracker) return .MIMEHeader } - func parseSectionSpecifierKind_header(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier.Kind { + func parseSectionSpecifierKind_header( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SectionSpecifier.Kind { try PL.parseFixedString("HEADER", buffer: &buffer, tracker: tracker) return .header } - func parseSectionSpecifierKind_headerFields(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier.Kind { + func parseSectionSpecifierKind_headerFields( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SectionSpecifier.Kind { try PL.parseFixedString("HEADER.FIELDS ", buffer: &buffer, tracker: tracker) return .headerFields(try self.parseHeaderList(buffer: &buffer, tracker: tracker)) } - func parseSectionSpecifierKind_notHeaderFields(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier.Kind { + func parseSectionSpecifierKind_notHeaderFields( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SectionSpecifier.Kind { try PL.parseFixedString("HEADER.FIELDS.NOT ", buffer: &buffer, tracker: tracker) return .headerFieldsNot(try self.parseHeaderList(buffer: &buffer, tracker: tracker)) } - func parseSectionSpecifierKind_text(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier.Kind { + func parseSectionSpecifierKind_text( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SectionSpecifier.Kind { try PL.parseFixedString("TEXT", buffer: &buffer, tracker: tracker) return .text } - func parseSectionSpecifierKind_complete(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SectionSpecifier.Kind { + func parseSectionSpecifierKind_complete( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> SectionSpecifier.Kind { .complete } - return try PL.parseOneOf([ - parseSectionSpecifierKind_mime, - parseSectionSpecifierKind_headerFields, - parseSectionSpecifierKind_notHeaderFields, - parseSectionSpecifierKind_header, - parseSectionSpecifierKind_text, - parseSectionSpecifierKind_complete, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseSectionSpecifierKind_mime, + parseSectionSpecifierKind_headerFields, + parseSectionSpecifierKind_notHeaderFields, + parseSectionSpecifierKind_header, + parseSectionSpecifierKind_text, + parseSectionSpecifierKind_complete, + ], + buffer: &buffer, + tracker: tracker + ) } func parseSelectParameter(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SelectParameter { @@ -2076,7 +2327,8 @@ extension GrammarParser { .basic(try self.parseParameter(buffer: &buffer, tracker: tracker)) } - func parseSelectParameter_condstore(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SelectParameter { + func parseSelectParameter_condstore(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SelectParameter + { try PL.parseFixedString("CONDSTORE", buffer: &buffer, tracker: tracker) return .condStore } @@ -2086,16 +2338,31 @@ extension GrammarParser { let uidValidity = try self.parseUIDValidity(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let modSeqVal = try self.parseModificationSequenceValue(buffer: &buffer, tracker: tracker) - let knownUIDs = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { (buffer, tracker) -> UIDSet in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseUIDSet(buffer: &buffer, tracker: tracker) - }) - let seqMatchData = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: { (buffer, tracker) -> SequenceMatchData in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseSequenceMatchData(buffer: &buffer, tracker: tracker) - }) + let knownUIDs = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { (buffer, tracker) -> UIDSet in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseUIDSet(buffer: &buffer, tracker: tracker) + } + ) + let seqMatchData = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: { (buffer, tracker) -> SequenceMatchData in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseSequenceMatchData(buffer: &buffer, tracker: tracker) + } + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) - return .qresync(.init(uidValidity: uidValidity, modificationSequenceValue: modSeqVal, knownUIDs: knownUIDs, sequenceMatchData: seqMatchData)) + return .qresync( + .init( + uidValidity: uidValidity, + modificationSequenceValue: modSeqVal, + knownUIDs: knownUIDs, + sequenceMatchData: seqMatchData + ) + ) } return try PL.parseOneOf( @@ -2108,13 +2375,18 @@ extension GrammarParser { } // select-params = SP "(" select-param *(SP select-param ")" - func parseParameters(buffer: inout ParseBuffer, tracker: StackTracker) throws -> OrderedDictionary { - try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> OrderedDictionary in + func parseParameters( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> OrderedDictionary { + try PL.composite(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> OrderedDictionary in try PL.parseFixedString(" (", buffer: &buffer, tracker: tracker) var kvs = OrderedDictionary() let param = try self.parseParameter(buffer: &buffer, tracker: tracker) kvs[param.key] = param.value - try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { (buffer, tracker) -> KeyValue in + try PL.parseZeroOrMore(buffer: &buffer, into: &kvs, tracker: tracker) { + (buffer, tracker) -> KeyValue in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseParameter(buffer: &buffer, tracker: tracker) } @@ -2126,13 +2398,19 @@ extension GrammarParser { func parseSortData(buffer: inout ParseBuffer, tracker: StackTracker) throws -> SortData? { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> SortData? in try PL.parseFixedString("SORT", buffer: &buffer, tracker: tracker) - let _components = try PL.parseOptional(buffer: &buffer, tracker: tracker) { (buffer, tracker) -> ([Int], ModificationSequenceValue) in + let _components = try PL.parseOptional(buffer: &buffer, tracker: tracker) { + (buffer, tracker) -> ([Int], ModificationSequenceValue) in try PL.parseSpaces(buffer: &buffer, tracker: tracker) var array = [try self.parseNZNumber(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker, parser: { (buffer, tracker) in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseNZNumber(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &array, + tracker: tracker, + parser: { (buffer, tracker) in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseNZNumber(buffer: &buffer, tracker: tracker) + } + ) try PL.parseSpaces(buffer: &buffer, tracker: tracker) let seq = try self.parseSearchSortModificationSequence(buffer: &buffer, tracker: tracker) return (array, seq) @@ -2163,7 +2441,8 @@ extension GrammarParser { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> [MailboxAttribute] in try PL.parseFixedString("STATUS (", buffer: &buffer, tracker: tracker) var array = [try self.parseStatusAttribute(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { (buffer, tracker) -> MailboxAttribute in + try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker) { + (buffer, tracker) -> MailboxAttribute in try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try self.parseStatusAttribute(buffer: &buffer, tracker: tracker) } @@ -2177,17 +2456,23 @@ extension GrammarParser { try PL.parseSpaces(buffer: &buffer, tracker: tracker) try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) var array = [try self.parseStoreModifier(buffer: &buffer, tracker: tracker)] - try PL.parseZeroOrMore(buffer: &buffer, into: &array, tracker: tracker, parser: { (buffer, tracker) in - try PL.parseSpaces(buffer: &buffer, tracker: tracker) - return try self.parseStoreModifier(buffer: &buffer, tracker: tracker) - }) + try PL.parseZeroOrMore( + buffer: &buffer, + into: &array, + tracker: tracker, + parser: { (buffer, tracker) in + try PL.parseSpaces(buffer: &buffer, tracker: tracker) + return try self.parseStoreModifier(buffer: &buffer, tracker: tracker) + } + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return array } } func parseStoreModifier(buffer: inout ParseBuffer, tracker: StackTracker) throws -> StoreModifier { - func parseFetchModifier_unchangedSince(buffer: inout ParseBuffer, tracker: StackTracker) throws -> StoreModifier { + func parseFetchModifier_unchangedSince(buffer: inout ParseBuffer, tracker: StackTracker) throws -> StoreModifier + { .unchangedSince(try self.parseUnchangedSinceModifier(buffer: &buffer, tracker: tracker)) } @@ -2264,10 +2549,14 @@ extension GrammarParser { try PL.parseFixedString("FLAGS", allowLeadingSpaces: false, buffer: &buffer, tracker: tracker) let silent = try self.parseStoreSilent(buffer: &buffer, tracker: tracker) try PL.parseSpaces(buffer: &buffer, tracker: tracker) - let flags = try PL.parseOneOf([ - self.parseFlagList, - parseStoreFlags_array, - ], buffer: &buffer, tracker: tracker) + let flags = try PL.parseOneOf( + [ + self.parseFlagList, + parseStoreFlags_array, + ], + buffer: &buffer, + tracker: tracker + ) return .init(operation: operation, silent: silent, flags: flags) } } @@ -2319,7 +2608,10 @@ extension GrammarParser { } // tagged-ext = tagged-ext-label SP tagged-ext-val - func parseTaggedExtension(buffer: inout ParseBuffer, tracker: StackTracker) throws -> KeyValue { + func parseTaggedExtension( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> KeyValue { try PL.composite(buffer: &buffer, tracker: tracker) { (buffer, tracker) in let key = try self.parseParameterName(buffer: &buffer, tracker: tracker) @@ -2430,7 +2722,8 @@ extension GrammarParser { func parseTaggedExtensionVal_comp(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ParameterValue { try PL.parseFixedString("(", buffer: &buffer, tracker: tracker) - let comp = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseTaggedExtensionComplex) ?? [] + let comp = + try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: self.parseTaggedExtensionComplex) ?? [] try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) return .comp(comp) } @@ -2451,25 +2744,30 @@ extension GrammarParser { } func parseUAuthMechanism(buffer: inout ParseBuffer, tracker: StackTracker) throws -> URLAuthenticationMechanism { - let parsed = try PL.parseOneOrMoreCharacters(buffer: &buffer, tracker: tracker, where: { char in - switch char { - case UInt8(ascii: "a") ... UInt8(ascii: "z"), - UInt8(ascii: "A") ... UInt8(ascii: "Z"), - UInt8(ascii: "0") ... UInt8(ascii: "9"), - UInt8(ascii: "-"), - UInt8(ascii: "."): - return true - default: - return false + let parsed = try PL.parseOneOrMoreCharacters( + buffer: &buffer, + tracker: tracker, + where: { char in + switch char { + case UInt8(ascii: "a")...UInt8(ascii: "z"), + UInt8(ascii: "A")...UInt8(ascii: "Z"), + UInt8(ascii: "0")...UInt8(ascii: "9"), + UInt8(ascii: "-"), + UInt8(ascii: "."): + return true + default: + return false + } } - }) + ) return URLAuthenticationMechanism(try ParserLibrary.parseBufferAsUTF8(parsed)) } // userid = astring func parseUserId(buffer: inout ParseBuffer, tracker: StackTracker) throws -> String { var astring = try self.parseAString(buffer: &buffer, tracker: tracker) - return astring.readString(length: astring.readableBytes)! // if this fails, something has gone very, very wrong + // if this fails, something has gone very, very wrong + return astring.readString(length: astring.readableBytes)! } // vendor-token = atom (maybe?!?!?!) @@ -2517,20 +2815,18 @@ extension GrammarParser { mailboxes.append(try parseMailbox(buffer: &buffer, tracker: tracker)) } try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) - if let returnValue = Mailboxes(mailboxes) { - return returnValue - } else { + guard let returnValue = Mailboxes(mailboxes) else { throw ParserError(hint: "Failed to unwrap mailboxes which should be impossible") } + return returnValue } func parseSingleMailboxes(buffer: inout ParseBuffer, tracker: StackTracker) throws -> Mailboxes { let mailboxes: [MailboxName] = [try parseMailbox(buffer: &buffer, tracker: tracker)] - if let returnValue = Mailboxes(mailboxes) { - return returnValue - } else { + guard let returnValue = Mailboxes(mailboxes) else { throw ParserError(hint: "Failed to unwrap single mailboxes which should be impossible") } + return returnValue } return try PL.parseOneOf( @@ -2550,7 +2846,10 @@ extension GrammarParser { return .selected } - func parseFilterMailboxes_SelectedDelayed(buffer: inout ParseBuffer, tracker: StackTracker) throws -> MailboxFilter { + func parseFilterMailboxes_SelectedDelayed( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MailboxFilter { try PL.parseFixedString("selected-delayed", buffer: &buffer, tracker: tracker) return .selectedDelayed } @@ -2590,62 +2889,73 @@ extension GrammarParser { return .subtreeOne(try parseOneOrMoreMailbox(buffer: &buffer, tracker: tracker)) } - return try PL.parseOneOf([ - parseFilterMailboxes_SelectedDelayed, - parseFilterMailboxes_SubtreeOne, - parseFilterMailboxes_Selected, - parseFilterMailboxes_Inboxes, - parseFilterMailboxes_Personal, - parseFilterMailboxes_Subscribed, - parseFilterMailboxes_Subtree, - parseFilterMailboxes_Mailboxes, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseFilterMailboxes_SelectedDelayed, + parseFilterMailboxes_SubtreeOne, + parseFilterMailboxes_Selected, + parseFilterMailboxes_Inboxes, + parseFilterMailboxes_Personal, + parseFilterMailboxes_Subscribed, + parseFilterMailboxes_Subtree, + parseFilterMailboxes_Mailboxes, + ], + buffer: &buffer, + tracker: tracker + ) } // RFC 7377 // scope-options = scope-option *(SP scope-option) - func parseExtendedSearchScopeOptions(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ExtendedSearchScopeOptions { + func parseExtendedSearchScopeOptions( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ExtendedSearchScopeOptions { var options = OrderedDictionary() repeat { let param = try parseParameter(buffer: &buffer, tracker: tracker) options[param.key] = param.value } while try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: PL.parseSpaces) != nil - if let returnValue = ExtendedSearchScopeOptions(options) { - return returnValue - } else { + guard let returnValue = ExtendedSearchScopeOptions(options) else { throw ParserError(hint: "Failed to unwrap ESearchScopeOptions which should be impossible.") } + return returnValue } // RFC 7377 // esearch-source-opts = "IN" SP "(" source-mbox [SP "(" scope-options ")"] ")" - func parseExtendedSearchSourceOptions(buffer: inout ParseBuffer, - tracker: StackTracker) throws -> ExtendedSearchSourceOptions - { - func parseExtendedSearchSourceOptions_spaceFilter(buffer: inout ParseBuffer, - tracker: StackTracker) throws -> MailboxFilter - { + func parseExtendedSearchSourceOptions( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ExtendedSearchSourceOptions { + func parseExtendedSearchSourceOptions_spaceFilter( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> MailboxFilter { try PL.parseSpaces(buffer: &buffer, tracker: tracker) return try parseFilterMailboxes(buffer: &buffer, tracker: tracker) } // source-mbox = filter-mailboxes *(SP filter-mailboxes) - func parseExtendedSearchSourceOptions_sourceMBox(buffer: inout ParseBuffer, - tracker: StackTracker) throws -> [MailboxFilter] - { + func parseExtendedSearchSourceOptions_sourceMBox( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> [MailboxFilter] { var sources = [try parseFilterMailboxes(buffer: &buffer, tracker: tracker)] - while let anotherSource = try PL.parseOptional(buffer: &buffer, - tracker: tracker, - parser: parseExtendedSearchSourceOptions_spaceFilter) - { + while let anotherSource = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: parseExtendedSearchSourceOptions_spaceFilter + ) { sources.append(anotherSource) } return sources } - func parseExtendedSearchSourceOptions_scopeOptions(buffer: inout ParseBuffer, - tracker: StackTracker) throws -> ExtendedSearchScopeOptions - { + func parseExtendedSearchSourceOptions_scopeOptions( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ExtendedSearchScopeOptions { try PL.parseFixedString(" (", buffer: &buffer, tracker: tracker) let result = try parseExtendedSearchScopeOptions(buffer: &buffer, tracker: tracker) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) @@ -2655,15 +2965,16 @@ extension GrammarParser { return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try PL.parseFixedString("IN (", buffer: &buffer, tracker: tracker) let sourceMbox = try parseExtendedSearchSourceOptions_sourceMBox(buffer: &buffer, tracker: tracker) - let scopeOptions = try PL.parseOptional(buffer: &buffer, - tracker: tracker, - parser: parseExtendedSearchSourceOptions_scopeOptions) + let scopeOptions = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: parseExtendedSearchSourceOptions_scopeOptions + ) try PL.parseFixedString(")", buffer: &buffer, tracker: tracker) - if let result = ExtendedSearchSourceOptions(sourceMailbox: sourceMbox, scopeOptions: scopeOptions) { - return result - } else { + guard let result = ExtendedSearchSourceOptions(sourceMailbox: sourceMbox, scopeOptions: scopeOptions) else { throw ParserError(hint: "Failed to construct esearch source options") } + return result } } @@ -2671,26 +2982,38 @@ extension GrammarParser { // esearch = "ESEARCH" [SP esearch-source-opts] // [SP search-return-opts] SP search-program // Ignoring the command here. - func parseExtendedSearchOptions(buffer: inout ParseBuffer, - tracker: StackTracker) throws -> ExtendedSearchOptions - { - func parseExtendedSearchOptions_sourceOptions(buffer: inout ParseBuffer, - tracker: StackTracker) throws -> ExtendedSearchSourceOptions - { + func parseExtendedSearchOptions( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ExtendedSearchOptions { + func parseExtendedSearchOptions_sourceOptions( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ExtendedSearchSourceOptions { try PL.parseSpaces(buffer: &buffer, tracker: tracker) let result = try parseExtendedSearchSourceOptions(buffer: &buffer, tracker: tracker) return result } - let sourceOptions = try PL.parseOptional(buffer: &buffer, - tracker: tracker, - parser: parseExtendedSearchOptions_sourceOptions) - let returnOpts = try PL.parseOptional(buffer: &buffer, - tracker: tracker, - parser: self.parseSearchReturnOptions) ?? [] + let sourceOptions = try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: parseExtendedSearchOptions_sourceOptions + ) + let returnOpts = + try PL.parseOptional( + buffer: &buffer, + tracker: tracker, + parser: self.parseSearchReturnOptions + ) ?? [] try PL.parseSpaces(buffer: &buffer, tracker: tracker) let (charset, program) = try parseSearchProgram(buffer: &buffer, tracker: tracker) - return ExtendedSearchOptions(key: program, charset: charset, returnOptions: returnOpts, sourceOptions: sourceOptions) + return ExtendedSearchOptions( + key: program, + charset: charset, + returnOptions: returnOpts, + sourceOptions: sourceOptions + ) } } @@ -2720,7 +3043,7 @@ struct StackTracker { private var stackDepth = 0 private let maximumStackDepth: Int - static var makeNewDefaultLimitStackTracker: StackTracker { + static var makeNewDefault: StackTracker { StackTracker(maximumParserStackDepth: 100) } diff --git a/Sources/NIOIMAPCore/Parser/ParserLibrary.swift b/Sources/NIOIMAPCore/Parser/ParserLibrary.swift index 168f5d1ff..93d7cb768 100644 --- a/Sources/NIOIMAPCore/Parser/ParserLibrary.swift +++ b/Sources/NIOIMAPCore/Parser/ParserLibrary.swift @@ -84,7 +84,11 @@ extension ParserLibrary { return string } - static func parseZeroOrMoreCharacters(buffer: inout ParseBuffer, tracker: StackTracker, where: ((UInt8) -> Bool)) throws -> ByteBuffer { + static func parseZeroOrMoreCharacters( + buffer: inout ParseBuffer, + tracker: StackTracker, + where: ((UInt8) -> Bool) + ) throws -> ByteBuffer { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, _ in let maybeFirstBad = buffer.bytes.readableBytesView.firstIndex { char in !`where`(char) @@ -97,7 +101,11 @@ extension ParserLibrary { } } - static func parseOneOrMoreCharacters(buffer: inout ParseBuffer, tracker: StackTracker, where: ((UInt8) -> Bool)) throws -> ByteBuffer { + static func parseOneOrMoreCharacters( + buffer: inout ParseBuffer, + tracker: StackTracker, + where: ((UInt8) -> Bool) + ) throws -> ByteBuffer { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, _ in let maybeFirstBad = buffer.bytes.readableBytesView.firstIndex { char in !`where`(char) @@ -114,13 +122,19 @@ extension ParserLibrary { } } - static func parseOneOrMore(buffer: inout ParseBuffer, tracker: StackTracker, parser: SubParser) throws -> [T] { + static func parseOneOrMore(buffer: inout ParseBuffer, tracker: StackTracker, parser: SubParser) throws -> [T] + { var parsed: [T] = [] try Self.parseOneOrMore(buffer: &buffer, into: &parsed, tracker: tracker, parser: parser) return parsed } - static func parseOneOrMore(buffer: inout ParseBuffer, into parsed: inout [T], tracker: StackTracker, parser: SubParser) throws { + static func parseOneOrMore( + buffer: inout ParseBuffer, + into parsed: inout [T], + tracker: StackTracker, + parser: SubParser + ) throws { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in parsed.append(try parser(&buffer, tracker)) while let next = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: parser) { @@ -129,7 +143,12 @@ extension ParserLibrary { } } - static func parseZeroOrMore(buffer: inout ParseBuffer, into parsed: inout [T], tracker: StackTracker, parser: SubParser) throws { + static func parseZeroOrMore( + buffer: inout ParseBuffer, + into parsed: inout [T], + tracker: StackTracker, + parser: SubParser + ) throws { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in while let next = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: parser) { parsed.append(next) @@ -137,7 +156,12 @@ extension ParserLibrary { } } - static func parseZeroOrMore(buffer: inout ParseBuffer, into orderedDictionary: inout OrderedDictionary, tracker: StackTracker, parser: SubParser<(K, V)>) throws { + static func parseZeroOrMore( + buffer: inout ParseBuffer, + into orderedDictionary: inout OrderedDictionary, + tracker: StackTracker, + parser: SubParser<(K, V)> + ) throws { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in while let next = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: parser) { orderedDictionary[next.0] = next.1 @@ -145,7 +169,12 @@ extension ParserLibrary { } } - static func parseZeroOrMore(buffer: inout ParseBuffer, into orderedDictionary: inout OrderedDictionary, tracker: StackTracker, parser: SubParser>) throws { + static func parseZeroOrMore( + buffer: inout ParseBuffer, + into orderedDictionary: inout OrderedDictionary, + tracker: StackTracker, + parser: SubParser> + ) throws { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in while let next = try PL.parseOptional(buffer: &buffer, tracker: tracker, parser: parser) { orderedDictionary[next.key] = next.value @@ -153,22 +182,34 @@ extension ParserLibrary { } } - static func parseZeroOrMore(buffer: inout ParseBuffer, tracker: StackTracker, parser: SubParser) throws -> [T] { + static func parseZeroOrMore(buffer: inout ParseBuffer, tracker: StackTracker, parser: SubParser) throws -> [T] + { var parsed: [T] = [] try Self.parseZeroOrMore(buffer: &buffer, into: &parsed, tracker: tracker, parser: parser) return parsed } - static func parseUnsignedInteger(buffer: inout ParseBuffer, tracker: StackTracker, allowLeadingZeros: Bool = false) throws -> (number: Int, bytesConsumed: Int) { - let largeInt = try self.parseUnsignedInt64(buffer: &buffer, tracker: tracker, allowLeadingZeros: allowLeadingZeros) - if let int = Int(exactly: largeInt.number) { - return (number: int, bytesConsumed: largeInt.bytesConsumed) - } else { + static func parseUnsignedInteger( + buffer: inout ParseBuffer, + tracker: StackTracker, + allowLeadingZeros: Bool = false + ) throws -> (number: Int, bytesConsumed: Int) { + let largeInt = try self.parseUnsignedInt64( + buffer: &buffer, + tracker: tracker, + allowLeadingZeros: allowLeadingZeros + ) + guard let int = Int(exactly: largeInt.number) else { throw ParserError(hint: "integer too large") } + return (number: int, bytesConsumed: largeInt.bytesConsumed) } - static func parseUnsignedInt64(buffer: inout ParseBuffer, tracker: StackTracker, allowLeadingZeros: Bool = false) throws -> (number: UInt64, bytesConsumed: Int) { + static func parseUnsignedInt64( + buffer: inout ParseBuffer, + tracker: StackTracker, + allowLeadingZeros: Bool = false + ) throws -> (number: UInt64, bytesConsumed: Int) { return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in let parsed = try PL.parseOneOrMoreCharacters(buffer: &buffer, tracker: tracker) { char in char >= UInt8(ascii: "0") && char <= UInt8(ascii: "9") @@ -205,7 +246,13 @@ extension ParserLibrary { } } - static func parseFixedString(_ needle: String, caseSensitive: Bool = false, allowLeadingSpaces: Bool = false, buffer: inout ParseBuffer, tracker: StackTracker) throws { + static func parseFixedString( + _ needle: String, + caseSensitive: Bool = false, + allowLeadingSpaces: Bool = false, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws { try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in if allowLeadingSpaces { @@ -215,7 +262,10 @@ extension ParserLibrary { let needleCount = needle.utf8.count guard let actual = buffer.bytes.readString(length: needleCount) else { guard needle.utf8.starts(with: buffer.bytes.readableBytesView, by: { $0 & 0xDF == $1 & 0xDF }) else { - throw ParserError(hint: "Tried to parse \(needle) in \(String(decoding: buffer.bytes.readableBytesView, as: Unicode.UTF8.self))") + throw ParserError( + hint: + "Tried to parse \(needle) in \(String(decoding: buffer.bytes.readableBytesView, as: Unicode.UTF8.self))" + ) } throw IncompleteMessage() } @@ -226,7 +276,7 @@ extension ParserLibrary { } else if !caseSensitive { // we know this is all ASCII so we can do an ASCII case-insensitive compare here guard needleCount == actual.utf8.count, - actual.utf8.elementsEqual(needle.utf8, by: { ($0 & 0xDF) == ($1 & 0xDF) }) + actual.utf8.elementsEqual(needle.utf8, by: { ($0 & 0xDF) == ($1 & 0xDF) }) else { throw ParserError(hint: "case insensitively looking for \(needle) found \(actual)") } @@ -248,7 +298,13 @@ extension ParserLibrary { } } - static func parseOneOf(_ subParsers: [SubParser], buffer: inout ParseBuffer, tracker: StackTracker, file: String = (#fileID), line: Int = #line) throws -> T { + static func parseOneOf( + _ subParsers: [SubParser], + buffer: inout ParseBuffer, + tracker: StackTracker, + file: String = (#fileID), + line: Int = #line + ) throws -> T { for parser in subParsers { do { return try PL.composite(buffer: &buffer, tracker: tracker, parser) @@ -261,11 +317,14 @@ extension ParserLibrary { throw ParserError(hint: "none of the options match", file: file, line: line) } - static func parseOneOf(_ parser1: SubParser, - _ parser2: SubParser, - buffer: inout ParseBuffer, - tracker: StackTracker, file: String = (#fileID), line: Int = #line) throws -> T - { + static func parseOneOf( + _ parser1: SubParser, + _ parser2: SubParser, + buffer: inout ParseBuffer, + tracker: StackTracker, + file: String = (#fileID), + line: Int = #line + ) throws -> T { do { return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try parser1(&buffer, tracker) @@ -282,12 +341,15 @@ extension ParserLibrary { } } - static func parseOneOf(_ parser1: SubParser, - _ parser2: SubParser, - _ parser3: SubParser, - buffer: inout ParseBuffer, - tracker: StackTracker, file: String = (#fileID), line: Int = #line) throws -> T - { + static func parseOneOf( + _ parser1: SubParser, + _ parser2: SubParser, + _ parser3: SubParser, + buffer: inout ParseBuffer, + tracker: StackTracker, + file: String = (#fileID), + line: Int = #line + ) throws -> T { do { return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker in try parser1(&buffer, tracker) @@ -338,7 +400,7 @@ extension ParserLibrary { static func parseNewline(buffer: inout ParseBuffer, tracker: StackTracker) throws { switch buffer.bytes.getInteger(at: buffer.bytes.readerIndex, as: UInt16.self) { - case .some(UInt16(0x0D0A /* CRLF */ )): + case .some(UInt16(0x0D0A)): // CRLF // fast path: we find CRLF buffer.bytes.moveReaderIndex(forwardBy: 2) return @@ -371,19 +433,17 @@ extension ParserLibrary { } static func parseByte(buffer: inout ParseBuffer, tracker: StackTracker) throws -> UInt8 { - if let byte = buffer.bytes.readInteger(as: UInt8.self) { - return byte - } else { + guard let byte = buffer.bytes.readInteger(as: UInt8.self) else { throw IncompleteMessage() } + return byte } static func parseBytes(buffer: inout ParseBuffer, tracker: StackTracker, length: Int) throws -> ByteBuffer { - if let bytes = buffer.bytes.readSlice(length: length) { - return bytes - } else { + guard let bytes = buffer.bytes.readSlice(length: length) else { throw IncompleteMessage() } + return bytes } static func parseBytes(buffer: inout ParseBuffer, tracker: StackTracker, upTo maxLength: Int) throws -> ByteBuffer { @@ -391,10 +451,9 @@ extension ParserLibrary { throw IncompleteMessage() } - if buffer.bytes.readableBytes >= maxLength { - return buffer.bytes.readSlice(length: maxLength)! // safe, those bytes are readable. - } else { - return buffer.bytes.readSlice(length: buffer.bytes.readableBytes)! // safe, those bytes are readable. + guard buffer.bytes.readableBytes >= maxLength else { + return buffer.bytes.readSlice(length: buffer.bytes.readableBytes)! // safe, those bytes are readable. } + return buffer.bytes.readSlice(length: maxLength)! // safe, those bytes are readable. } } diff --git a/Sources/NIOIMAPCore/Parser/ResponseParser.swift b/Sources/NIOIMAPCore/Parser/ResponseParser.swift index d4c7e0b4c..371acf735 100644 --- a/Sources/NIOIMAPCore/Parser/ResponseParser.swift +++ b/Sources/NIOIMAPCore/Parser/ResponseParser.swift @@ -79,12 +79,16 @@ public struct ResponseParser: Parser, Sendable { /// - parameter buffer: The `ByteBuffer` to parse data from. /// - returns: `nil` if there wasn't enough data, otherwise a `ResponseOrContinuationRequest` if parsing was successful. /// - throws: A `ParserError` with a desription as to why parsing failed. - public mutating func parseResponseStream(buffer inputBytes: inout ByteBuffer) throws -> ResponseOrContinuationRequest? { - let tracker = StackTracker.makeNewDefaultLimitStackTracker + public mutating func parseResponseStream( + buffer inputBytes: inout ByteBuffer + ) throws -> ResponseOrContinuationRequest? { + let tracker = StackTracker.makeNewDefault var parseBuffer = ParseBuffer(inputBytes) defer { - assert(inputBytes.readableBytes >= parseBuffer.readableBytes, - "illegal state, parse buffer has more remaining than input had: \(inputBytes), \(parseBuffer)") + assert( + inputBytes.readableBytes >= parseBuffer.readableBytes, + "illegal state, parse buffer has more remaining than input had: \(inputBytes), \(parseBuffer)" + ) // Discard everything that has been parsed off. inputBytes.moveReaderIndex(forwardBy: inputBytes.readableBytes - parseBuffer.readableBytes) @@ -94,7 +98,14 @@ public struct ResponseParser: Parser, Sendable { case .response(let state): return try self.parseResponse(state: state, buffer: &parseBuffer, tracker: tracker) case .attributeBytes(let remaining, attributeCount: let attributeCount): - return .response(try self.parseBytes(buffer: &parseBuffer, tracker: tracker, remaining: remaining, attributeCount: attributeCount)) + return .response( + try self.parseBytes( + buffer: &parseBuffer, + tracker: tracker, + remaining: remaining, + attributeCount: attributeCount + ) + ) case .streamingQuoted(attributeCount: let attributeCount): return .response(try self.parseQuotedBytes(buffer: &parseBuffer, attributeCount: attributeCount)) } @@ -120,7 +131,11 @@ public struct ResponseParser: Parser, Sendable { // MARK: - Parse responses extension ResponseParser { - private mutating func parseResponse(state: ResponseState, buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseOrContinuationRequest { + private mutating func parseResponse( + state: ResponseState, + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseOrContinuationRequest { enum _Response: Hashable { case untaggedResponse(ResponsePayload) case fetchResponse(GrammarParser._FetchResponse) @@ -144,32 +159,48 @@ extension ResponseParser { try? PL.parseSpaces(buffer: &buffer, tracker: tracker) do { let response = try PL.parseOneOf( - parseResponse_fetch, parseResponse_normal, + parseResponse_fetch, + parseResponse_normal, buffer: &buffer, tracker: tracker ) switch response { case .fetchResponse(.start(let num)): - self.moveStateMachine(expected: .response(.fetchOrNormal), next: .response(.fetchMiddle(attributeCount: 0))) + self.moveStateMachine( + expected: .response(.fetchOrNormal), + next: .response(.fetchMiddle(attributeCount: 0)) + ) return .response(.fetch(.start(num))) case .fetchResponse(.startUID(let num)): - self.moveStateMachine(expected: .response(.fetchOrNormal), next: .response(.fetchMiddle(attributeCount: 0))) + self.moveStateMachine( + expected: .response(.fetchOrNormal), + next: .response(.fetchMiddle(attributeCount: 0)) + ) return .response(.fetch(.startUID(num))) case .fetchResponse(.literalStreamingBegin(kind: let kind, byteCount: let size)): try self.guardStreamingSizeLimit(size: size) let attributeCount = try self.guardFetchMiddleAttributeCount() - self.moveStateMachine(expected: .response(.fetchMiddle(attributeCount: attributeCount)), next: .attributeBytes(size, attributeCount: attributeCount + 1)) + self.moveStateMachine( + expected: .response(.fetchMiddle(attributeCount: attributeCount)), + next: .attributeBytes(size, attributeCount: attributeCount + 1) + ) return .response(.fetch(.streamingBegin(kind: kind, byteCount: size))) case .fetchResponse(.quotedStreamingBegin(kind: let kind, byteCount: let size)): try self.guardStreamingSizeLimit(size: size) let attributeCount = try self.guardFetchMiddleAttributeCount() - self.moveStateMachine(expected: .response(.fetchMiddle(attributeCount: attributeCount)), next: .streamingQuoted(attributeCount: attributeCount + 1)) + self.moveStateMachine( + expected: .response(.fetchMiddle(attributeCount: attributeCount)), + next: .streamingQuoted(attributeCount: attributeCount + 1) + ) return .response(.fetch(.streamingBegin(kind: kind, byteCount: size))) case .fetchResponse(.finish): let attributeCount = try self.guardFetchMiddleAttributeCount() - self.moveStateMachine(expected: .response(.fetchMiddle(attributeCount: attributeCount)), next: .response(.fetchOrNormal)) + self.moveStateMachine( + expected: .response(.fetchMiddle(attributeCount: attributeCount)), + next: .response(.fetchOrNormal) + ) return .response(.fetch(.finish)) case .untaggedResponse(let payload): @@ -177,7 +208,10 @@ extension ResponseParser { case .fetchResponse(.simpleAttribute(let att)): let attributeCount = try self.guardFetchMiddleAttributeCount() - self.moveStateMachine(expected: .response(.fetchMiddle(attributeCount: attributeCount)), next: .response(.fetchMiddle(attributeCount: attributeCount + 1))) + self.moveStateMachine( + expected: .response(.fetchMiddle(attributeCount: attributeCount)), + next: .response(.fetchMiddle(attributeCount: attributeCount + 1)) + ) return .response(.fetch(.simpleAttribute(att))) } } catch is ParserError { @@ -209,19 +243,32 @@ extension ResponseParser { } } - private func _parseResponse(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseOrContinuationRequest { - func parseResponse_continuation(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseOrContinuationRequest { + private func _parseResponse( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseOrContinuationRequest { + func parseResponse_continuation( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseOrContinuationRequest { .continuationRequest(try self.parser.parseContinuationRequest(buffer: &buffer, tracker: tracker)) } - func parseResponse_tagged(buffer: inout ParseBuffer, tracker: StackTracker) throws -> ResponseOrContinuationRequest { + func parseResponse_tagged( + buffer: inout ParseBuffer, + tracker: StackTracker + ) throws -> ResponseOrContinuationRequest { .response(.tagged(try self.parser.parseTaggedResponse(buffer: &buffer, tracker: tracker))) } - return try PL.parseOneOf([ - parseResponse_continuation, - parseResponse_tagged, - ], buffer: &buffer, tracker: tracker) + return try PL.parseOneOf( + [ + parseResponse_continuation, + parseResponse_tagged, + ], + buffer: &buffer, + tracker: tracker + ) } } @@ -233,17 +280,18 @@ extension ResponseParser { /// `ByteBuffer` will be emptied. /// - parameter buffer: The buffer from which bytes should be extracted. /// - returns: A new `ByteBuffer` containing extracted bytes. - private mutating func parseBytes(buffer: inout ParseBuffer, tracker: StackTracker, remaining: Int, attributeCount: Int) throws -> Response { - if remaining == 0 { - return self.moveStateMachine( - expected: .attributeBytes(remaining, attributeCount: attributeCount), - next: .response(.fetchMiddle(attributeCount: attributeCount + 1)), // we've finished parsing an attribute here so increment the count - returnValue: .fetch(.streamingEnd) + private mutating func parseBytes( + buffer: inout ParseBuffer, + tracker: StackTracker, + remaining: Int, + attributeCount: Int + ) throws -> Response { + guard remaining == 0 else { + let bytes = try PL.parseBytes( + buffer: &buffer, + tracker: .makeNewDefault, + upTo: remaining ) - } else { - let bytes = try PL.parseBytes(buffer: &buffer, - tracker: .makeNewDefaultLimitStackTracker, - upTo: remaining) let leftToRead = remaining - bytes.readableBytes assert(leftToRead >= 0, "\(leftToRead) is negative") @@ -253,10 +301,16 @@ extension ResponseParser { returnValue: .fetch(.streamingBytes(bytes)) ) } + return self.moveStateMachine( + expected: .attributeBytes(remaining, attributeCount: attributeCount), + // we've finished parsing an attribute here so increment the count + next: .response(.fetchMiddle(attributeCount: attributeCount + 1)), + returnValue: .fetch(.streamingEnd) + ) } private mutating func parseQuotedBytes(buffer: inout ParseBuffer, attributeCount: Int) throws -> Response { - let quoted = try self.parser.parseQuoted(buffer: &buffer, tracker: .makeNewDefaultLimitStackTracker) + let quoted = try self.parser.parseQuoted(buffer: &buffer, tracker: .makeNewDefault) return self.moveStateMachine( expected: .streamingQuoted(attributeCount: attributeCount), next: .attributeBytes(0, attributeCount: attributeCount), diff --git a/Sources/NIOIMAPCore/Parser/SynchronizingLiteralParser.swift b/Sources/NIOIMAPCore/Parser/SynchronizingLiteralParser.swift index 2cd058704..c82cb4b88 100644 --- a/Sources/NIOIMAPCore/Parser/SynchronizingLiteralParser.swift +++ b/Sources/NIOIMAPCore/Parser/SynchronizingLiteralParser.swift @@ -65,8 +65,10 @@ public struct SynchronizingLiteralParser: Sendable { var magnitude = 1 while true { switch buffer.readableBytesView.last { - case .some(let digit) where (UInt8(ascii: "0") ... UInt8(ascii: "9")).contains(digit): - let (newCurrent, currentOverflowed) = current.addingReportingOverflow((magnitude * Int(digit - UInt8(ascii: "0")))) + case .some(let digit) where (UInt8(ascii: "0")...UInt8(ascii: "9")).contains(digit): + let (newCurrent, currentOverflowed) = current.addingReportingOverflow( + (magnitude * Int(digit - UInt8(ascii: "0"))) + ) if currentOverflowed { throw ParserError(hint: "Overflow") } @@ -78,11 +80,10 @@ public struct SynchronizingLiteralParser: Sendable { magnitude = newMagnitude buffer.moveWriterIndex(to: buffer.writerIndex - 1) case .some: - if magnitude == 1 { - throw ParserError() - } else { + guard magnitude == 1 else { return current } + throw ParserError() case .none: throw ParserError() } @@ -94,19 +95,17 @@ public struct SynchronizingLiteralParser: Sendable { assert(fragment.readableBytes > 0, "\(fragment)") try reverseParseTrailingNewlines(&fragment) guard fragment.readableBytes > 0 else { - return .completeLine // this is just an empty line + return .completeLine // this is just an empty line } - if try reverseParseIf(UInt8(ascii: "}"), &fragment) { - if try reverseParseIf(UInt8(ascii: "+"), &fragment) || reverseParseIf(UInt8(ascii: "-"), &fragment) { - let number = try reverseParseNumber(&fragment) - return .nonSynchronisingLiteral(number) - } else { - let number = try reverseParseNumber(&fragment) - return .synchronisingLiteral(number) - } - } else { + guard try reverseParseIf(UInt8(ascii: "}"), &fragment) else { return .completeLine } + guard try reverseParseIf(UInt8(ascii: "+"), &fragment) || reverseParseIf(UInt8(ascii: "-"), &fragment) else { + let number = try reverseParseNumber(&fragment) + return .synchronisingLiteral(number) + } + let number = try reverseParseNumber(&fragment) + return .nonSynchronisingLiteral(number) } /// Contains information on the result of a call to `parseContinuationsNecessary`. @@ -126,7 +125,9 @@ public struct SynchronizingLiteralParser: Sendable { repeat { switch self.state { case .waitingForCompleteLine: - if let newlineIndex = buffer.readableBytesView[(buffer.readableBytesView.startIndex + self.offset)...].findNewlineIndex() { + if let newlineIndex = buffer.readableBytesView[(buffer.readableBytesView.startIndex + self.offset)...] + .findNewlineIndex() + { self.offset = newlineIndex - buffer.readableBytesView.startIndex + 1 switch try Self.lineFragmentType(buffer.readableBytesView[...newlineIndex]) { case .synchronisingLiteral(let length): @@ -139,11 +140,12 @@ public struct SynchronizingLiteralParser: Sendable { assert(self.state == .waitingForCompleteLine) } case .completeLine: - () // nothing to do + () // nothing to do } } case .waitingForLiteralBytes(let literalBytesLeft): - let remainingBytes = buffer.readableBytesView.endIndex - (buffer.readableBytesView.startIndex + self.offset) + let remainingBytes = + buffer.readableBytesView.endIndex - (buffer.readableBytesView.startIndex + self.offset) if remainingBytes >= literalBytesLeft { self.state = .waitingForCompleteLine self.offset += literalBytesLeft @@ -157,8 +159,10 @@ public struct SynchronizingLiteralParser: Sendable { guard lastOffset < self.offset, self.offset < buffer.readableBytesView.endIndex else { let synchronisingLiterals = self.synchronisingLiterals self.synchronisingLiterals = 0 - return FramingResult(maximumValidBytes: self.offset, - synchronizingLiteralCount: synchronisingLiterals) + return FramingResult( + maximumValidBytes: self.offset, + synchronizingLiteralCount: synchronisingLiterals + ) } lastOffset = self.offset } while true diff --git a/Sources/NIOIMAPCore/Parser/UInt8+ParseTypeMembership.swift b/Sources/NIOIMAPCore/Parser/UInt8+ParseTypeMembership.swift index 1c600cb82..c97894e23 100644 --- a/Sources/NIOIMAPCore/Parser/UInt8+ParseTypeMembership.swift +++ b/Sources/NIOIMAPCore/Parser/UInt8+ParseTypeMembership.swift @@ -62,7 +62,7 @@ extension UInt8 { var isAtomSpecial: Bool { switch self { - case 0 ... 31, UInt8(ascii: "("), UInt8(ascii: ")"), UInt8(ascii: "{"), UInt8(ascii: " "): + case 0...31, UInt8(ascii: "("), UInt8(ascii: ")"), UInt8(ascii: "{"), UInt8(ascii: " "): return true case _ where self.isListWildcard, _ where self.isResponseSpecial, _ where self.isQuotedSpecial: return true @@ -125,7 +125,7 @@ extension UInt8 { } var isNum: Bool { - (UInt8(ascii: "0") ... UInt8(ascii: "9")).contains(self) + (UInt8(ascii: "0")...UInt8(ascii: "9")).contains(self) } /// tagged-label-fchar = ALPHA / "-" / "_" / "." @@ -133,9 +133,9 @@ extension UInt8 { switch self { case UInt8(ascii: "-"), UInt8(ascii: "_"), UInt8(ascii: "."): return true - case UInt8(ascii: "a") ... UInt8(ascii: "z"): + case UInt8(ascii: "a")...UInt8(ascii: "z"): return true - case UInt8(ascii: "A") ... UInt8(ascii: "Z"): + case UInt8(ascii: "A")...UInt8(ascii: "Z"): return true default: return false @@ -147,7 +147,7 @@ extension UInt8 { switch self { case UInt8(ascii: ":"): return true - case UInt8(ascii: "0") ... UInt8(ascii: "9"): + case UInt8(ascii: "0")...UInt8(ascii: "9"): return true default: return self.isTaggedLabelFchar @@ -158,13 +158,13 @@ extension UInt8 { var isSubDelimsSh: Bool { switch self { case UInt8(ascii: "!"), - UInt8(ascii: "$"), - UInt8(ascii: "'"), - UInt8(ascii: "("), - UInt8(ascii: ")"), - UInt8(ascii: "*"), - UInt8(ascii: "+"), - UInt8(ascii: ","): + UInt8(ascii: "$"), + UInt8(ascii: "'"), + UInt8(ascii: "("), + UInt8(ascii: ")"), + UInt8(ascii: "*"), + UInt8(ascii: "+"), + UInt8(ascii: ","): return true default: return false @@ -175,10 +175,10 @@ extension UInt8 { var isUnreserved: Bool { switch self { case _ where self.isAlphaNum, - UInt8(ascii: "-"), - UInt8(ascii: "."), - UInt8(ascii: "_"), - UInt8(ascii: "~"): + UInt8(ascii: "-"), + UInt8(ascii: "."), + UInt8(ascii: "_"), + UInt8(ascii: "~"): return true default: return false @@ -187,7 +187,8 @@ extension UInt8 { var isHexCharacter: Bool { switch self { - case UInt8(ascii: "0") ... UInt8(ascii: "9"), UInt8(ascii: "a") ... UInt8(ascii: "f"), UInt8(ascii: "A") ... UInt8(ascii: "F"): + case UInt8(ascii: "0")...UInt8(ascii: "9"), UInt8(ascii: "a")...UInt8(ascii: "f"), + UInt8(ascii: "A")...UInt8(ascii: "F"): return true default: return false diff --git a/Sources/NIOIMAPCore/Pipelining.swift b/Sources/NIOIMAPCore/Pipelining.swift index a1347ee39..60d9121fd 100644 --- a/Sources/NIOIMAPCore/Pipelining.swift +++ b/Sources/NIOIMAPCore/Pipelining.swift @@ -123,9 +123,9 @@ extension Command { var pipeliningRequirements: Set { switch self { case .select, - .unselect, - .close, - .examine: + .unselect, + .close, + .examine: return [.noMailboxCommandsRunning] case .search(key: let key, charset: _, returnOptions: _): @@ -144,86 +144,86 @@ extension Command { return attributes.makePipeliningRequirements(uids) case .copy, - .move: + .move: return [.noUntaggedExpungeResponse, .noUIDBasedCommandRunning] case .uidCopy, - .uidMove, - .uidExpunge, - .expunge: + .uidMove, + .uidExpunge, + .expunge: return [] case .store(_, _, let data): switch data { case .flags(let flags): - return flags.silent ? - [.noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage] : - [.noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage, .noFlagChangesToAnyMessage] + return flags.silent + ? [.noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage] + : [ + .noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage, + .noFlagChangesToAnyMessage, + ] case .gmailLabels(let labels): - return labels.silent ? - [.noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage] : - [.noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage, .noFlagChangesToAnyMessage] + return labels.silent + ? [.noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage] + : [ + .noUntaggedExpungeResponse, .noUIDBasedCommandRunning, .noFlagReadsFromAnyMessage, + .noFlagChangesToAnyMessage, + ] } case .uidStore(.lastCommand, _, let data): switch data { case .flags(let flags): - return flags.silent ? - [.noFlagReadsFromAnyMessage] : - [.noFlagReadsFromAnyMessage, .noFlagChangesToAnyMessage] + return flags.silent + ? [.noFlagReadsFromAnyMessage] : [.noFlagReadsFromAnyMessage, .noFlagChangesToAnyMessage] case .gmailLabels(let labels): - return labels.silent ? - [.noFlagReadsFromAnyMessage] : - [.noFlagReadsFromAnyMessage, .noFlagChangesToAnyMessage] + return labels.silent + ? [.noFlagReadsFromAnyMessage] : [.noFlagReadsFromAnyMessage, .noFlagChangesToAnyMessage] } case .uidStore(.set(let uids), _, let data): switch data { case .flags(let flags): - return flags.silent ? - [.noFlagReads(uids)] : - [.noFlagReads(uids), .noFlagChanges(uids)] + return flags.silent ? [.noFlagReads(uids)] : [.noFlagReads(uids), .noFlagChanges(uids)] case .gmailLabels(let labels): - return labels.silent ? - [.noFlagReads(uids)] : - [.noFlagReads(uids), .noFlagChanges(uids)] + return labels.silent ? [.noFlagReads(uids)] : [.noFlagReads(uids), .noFlagChanges(uids)] } case .capability, - .logout, - .noop, - .check, - .authenticate, - .login, - .startTLS, - .enable, - .idleStart, - .id, - .namespace, - .compress, - - // Mailbox: - .status, - .create, - .list, - .listIndependent, - .lsub, - .subscribe, - .unsubscribe, - .delete, - .rename, - - // Quota: - .getQuota, - .getQuotaRoot, - .setQuota, - - // Metadata: - .getMetadata, - .setMetadata, - - // URL Auth: - .resetKey, - .generateAuthorizedURL, - .urlFetch: + .logout, + .noop, + .check, + .authenticate, + .login, + .startTLS, + .enable, + .idleStart, + .id, + .namespace, + .compress, + + // Mailbox: + .status, + .create, + .list, + .listIndependent, + .lsub, + .subscribe, + .unsubscribe, + .delete, + .rename, + + // Quota: + .getQuota, + .getQuotaRoot, + .setQuota, + + // Metadata: + .getMetadata, + .setMetadata, + + // URL Auth: + .resetKey, + .generateAuthorizedURL, + .urlFetch: return [] case .custom: return [ @@ -263,9 +263,9 @@ extension Command { var pipeliningBehavior: Set { switch self { case .select, - .unselect, - .examine, - .close: + .unselect, + .examine, + .close: return [.changesMailboxSelection, .mayTriggerUntaggedExpunge] case .expunge: @@ -274,17 +274,16 @@ extension Command { return [.dependsOnMailboxSelection, .isUIDBased, .mayTriggerUntaggedExpunge] case .fetch(_, let attributes, _): - return attributes.readsFlags ? - [.dependsOnMailboxSelection, .readsFlagsFromAnyMessage] : - [.dependsOnMailboxSelection] + return attributes.readsFlags + ? [.dependsOnMailboxSelection, .readsFlagsFromAnyMessage] : [.dependsOnMailboxSelection] case .uidFetch(.lastCommand, let attributes, _): - return attributes.readsFlags ? - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .readsFlagsFromAnyMessage] : - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased] + return attributes.readsFlags + ? [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .readsFlagsFromAnyMessage] + : [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased] case .uidFetch(.set(let uids), let attributes, _): - return attributes.readsFlags ? - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .readsFlags(uids)] : - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased] + return attributes.readsFlags + ? [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .readsFlags(uids)] + : [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased] case .copy, .move: return [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge] @@ -307,45 +306,57 @@ extension Command { case .store(_, _, let data): switch data { case .flags(let storeFlags): - return storeFlags.silent ? - [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage] : - [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage, .readsFlagsFromAnyMessage] + return storeFlags.silent + ? [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage] + : [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage, .readsFlagsFromAnyMessage] case .gmailLabels(let storeGmailLabels): - return storeGmailLabels.silent ? - [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage] : - [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage, .readsFlagsFromAnyMessage] + return storeGmailLabels.silent + ? [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage] + : [.dependsOnMailboxSelection, .changesFlagsOnAnyMessage, .readsFlagsFromAnyMessage] } case .uidStore(.lastCommand, _, let data): switch data { case .flags(let storeFlags): - return storeFlags.silent ? - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage] : - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage, .readsFlagsFromAnyMessage] + return storeFlags.silent + ? [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage] + : [ + .dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage, + .readsFlagsFromAnyMessage, + ] case .gmailLabels(let storeGmailLabels): - return storeGmailLabels.silent ? - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage] : - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage, .readsFlagsFromAnyMessage] + return storeGmailLabels.silent + ? [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage] + : [ + .dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlagsOnAnyMessage, + .readsFlagsFromAnyMessage, + ] } case .uidStore(.set(let uids), _, let data): switch data { case .flags(let storeFlags): - return storeFlags.silent ? - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids)] : - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids), .readsFlags(uids)] + return storeFlags.silent + ? [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids)] + : [ + .dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids), + .readsFlags(uids), + ] case .gmailLabels(let storeGmailLabels): - return storeGmailLabels.silent ? - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids)] : - [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids), .readsFlags(uids)] + return storeGmailLabels.silent + ? [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids)] + : [ + .dependsOnMailboxSelection, .mayTriggerUntaggedExpunge, .isUIDBased, .changesFlags(uids), + .readsFlags(uids), + ] } case .noop, - .check: + .check: return [.dependsOnMailboxSelection, .mayTriggerUntaggedExpunge] case .startTLS, - .logout, - .authenticate, - .compress: + .logout, + .authenticate, + .compress: return [.barrier] case .idleStart: @@ -355,36 +366,36 @@ extension Command { return [] case .capability, - .create, - .delete, - .rename, - .list, - .listIndependent, - .lsub, - .status, - .id, - .namespace, - .enable, - .resetKey: + .create, + .delete, + .rename, + .list, + .listIndependent, + .lsub, + .status, + .id, + .namespace, + .enable, + .resetKey: return [.mayTriggerUntaggedExpunge] case .generateAuthorizedURL, - .urlFetch: + .urlFetch: return [.mayTriggerUntaggedExpunge, .isUIDBased] case .subscribe, - .unsubscribe: + .unsubscribe: // TODO: Subscribe vs. LIST / LSUB ?!? return [.mayTriggerUntaggedExpunge] case .getQuota, - .getQuotaRoot, - .setQuota: + .getQuotaRoot, + .setQuota: // TODO: Quota dependencies? return [.mayTriggerUntaggedExpunge] case .getMetadata, - .setMetadata: + .setMetadata: // TODO: Metadata dependencies? return [.mayTriggerUntaggedExpunge] case .custom: @@ -425,22 +436,22 @@ extension FetchAttribute { case .flags: return true case .envelope, - .internalDate, - .rfc822, - .rfc822Header, - .rfc822Size, - .rfc822Text, - .bodyStructure, - .bodySection, - .uid, - .modificationSequence, - .modificationSequenceValue, - .binary, - .binarySize, - .gmailMessageID, - .gmailThreadID, - .gmailLabels, - .preview: + .internalDate, + .rfc822, + .rfc822Header, + .rfc822Size, + .rfc822Text, + .bodyStructure, + .bodySection, + .uid, + .modificationSequence, + .modificationSequenceValue, + .binary, + .binarySize, + .gmailMessageID, + .gmailThreadID, + .gmailLabels, + .preview: return false } } @@ -473,46 +484,46 @@ extension SearchKey { private var referencesSequenceNumbers: Bool { switch self { case .all, - .answered, - .bcc, - .before, - .body, - .cc, - .deleted, - .flagged, - .from, - .keyword, - .modificationSequence, - .new, - .old, - .on, - .recent, - .seen, - .since, - .subject, - .text, - .to, - .unanswered, - .undeleted, - .unflagged, - .unkeyword, - .unseen, - .draft, - .header, - .messageSizeLarger, - .messageSizeSmaller, - .older, - .sentBefore, - .sentOn, - .sentSince, - .uid, - .uidAfter, - .uidBefore, - .undraft, - .younger: + .answered, + .bcc, + .before, + .body, + .cc, + .deleted, + .flagged, + .from, + .keyword, + .modificationSequence, + .new, + .old, + .on, + .recent, + .seen, + .since, + .subject, + .text, + .to, + .unanswered, + .undeleted, + .unflagged, + .unkeyword, + .unseen, + .draft, + .header, + .messageSizeLarger, + .messageSizeSmaller, + .older, + .sentBefore, + .sentOn, + .sentSince, + .uid, + .uidAfter, + .uidBefore, + .undraft, + .younger: return false - case .filter, // Have to assume yes, since we can't know - .sequenceNumbers: + case .filter, // Have to assume yes, since we can't know + .sequenceNumbers: return true case .and(let keys): return keys.contains(where: \.referencesSequenceNumbers) @@ -526,46 +537,46 @@ extension SearchKey { private var referencesUIDs: Bool { switch self { case .all, - .answered, - .bcc, - .before, - .body, - .cc, - .deleted, - .flagged, - .from, - .keyword, - .modificationSequence, - .new, - .old, - .on, - .recent, - .seen, - .since, - .subject, - .text, - .to, - .unanswered, - .undeleted, - .unflagged, - .unkeyword, - .unseen, - .draft, - .header, - .messageSizeLarger, - .messageSizeSmaller, - .older, - .sentBefore, - .sentOn, - .sentSince, - .sequenceNumbers, - .undraft, - .younger: + .answered, + .bcc, + .before, + .body, + .cc, + .deleted, + .flagged, + .from, + .keyword, + .modificationSequence, + .new, + .old, + .on, + .recent, + .seen, + .since, + .subject, + .text, + .to, + .unanswered, + .undeleted, + .unflagged, + .unkeyword, + .unseen, + .draft, + .header, + .messageSizeLarger, + .messageSizeSmaller, + .older, + .sentBefore, + .sentOn, + .sentSince, + .sequenceNumbers, + .undraft, + .younger: return false - case .filter, // Have to assume yes, since we can't know - .uid, - .uidAfter, - .uidBefore: + case .filter, // Have to assume yes, since we can't know + .uid, + .uidAfter, + .uidBefore: return true case .and(let keys): return keys.contains(where: \.referencesUIDs) @@ -579,45 +590,45 @@ extension SearchKey { var referencesFlags: Bool { switch self { case .all, - .bcc, - .before, - .body, - .cc, - .from, - .sequenceNumbers, - .new, - .old, - .on, - .recent, - .since, - .subject, - .text, - .to, - .header, - .messageSizeLarger, - .messageSizeSmaller, - .older, - .sentBefore, - .sentOn, - .sentSince, - .uid, - .uidAfter, - .uidBefore, - .younger: + .bcc, + .before, + .body, + .cc, + .from, + .sequenceNumbers, + .new, + .old, + .on, + .recent, + .since, + .subject, + .text, + .to, + .header, + .messageSizeLarger, + .messageSizeSmaller, + .older, + .sentBefore, + .sentOn, + .sentSince, + .uid, + .uidAfter, + .uidBefore, + .younger: return false case .answered, - .deleted, - .filter, // Have to assume yes, since we can't know - .flagged, - .keyword, - .unanswered, - .undeleted, - .unflagged, - .unkeyword, - .seen, - .unseen, - .draft, - .undraft: + .deleted, + .filter, // Have to assume yes, since we can't know + .flagged, + .keyword, + .unanswered, + .undeleted, + .unflagged, + .unkeyword, + .seen, + .unseen, + .draft, + .undraft: return true case .and(let keys): return keys.contains(where: \.referencesFlags) diff --git a/Sources/NIOIMAPCore/ResponseEncodeBuffer.swift b/Sources/NIOIMAPCore/ResponseEncodeBuffer.swift index 7079236d2..7260c1575 100644 --- a/Sources/NIOIMAPCore/ResponseEncodeBuffer.swift +++ b/Sources/NIOIMAPCore/ResponseEncodeBuffer.swift @@ -101,45 +101,39 @@ extension ResponseEncodeBuffer { @discardableResult mutating func writeAuthenticationChallenge(_ bytes: ByteBuffer) -> Int { let base64 = Base64.encodeBytes(bytes: bytes.readableBytesView) - return self.buffer.writeString("+ ") + - self.buffer.writeBytes(base64) + - self.buffer.writeString("\r\n") + return self.buffer.writeString("+ ") + self.buffer.writeBytes(base64) + self.buffer.writeString("\r\n") } @discardableResult mutating func writeFetchResponse(_ response: FetchResponse) -> Int { switch response { case .start(let num): - return self.buffer.writeString("* ") + - self.buffer.writeSequenceNumber(num) + - self.buffer.writeString(" FETCH (") + return self.buffer.writeString("* ") + self.buffer.writeSequenceNumber(num) + + self.buffer.writeString(" FETCH (") case .startUID(let num): - return self.buffer.writeString("* ") + - self.buffer.writeMessageIdentifier(num) + - self.buffer.writeString(" UIDFETCH (") + return self.buffer.writeString("* ") + self.buffer.writeMessageIdentifier(num) + + self.buffer.writeString(" UIDFETCH (") case .simpleAttribute(let att): guard case .server(streamingAttributes: let streamingAttributes, let options) = self.buffer.mode else { preconditionFailure("Only server can write responses.") } - if streamingAttributes { - return self.buffer.writeSpace() + self.buffer.writeMessageAttribute(att) - } else { + guard streamingAttributes else { self.buffer.mode = .server(streamingAttributes: true, options: options) return self.buffer.writeMessageAttribute(att) } + return self.buffer.writeSpace() + self.buffer.writeMessageAttribute(att) case .streamingBegin(let type, let size): guard case .server(streamingAttributes: let streamingAttributes, let options) = self.buffer.mode else { preconditionFailure("Only server can write responses.") } - if streamingAttributes { - return self.buffer.writeSpace() + self.writeStreamingKind(type, size: size) - } else { + guard streamingAttributes else { self.buffer.mode = .server(streamingAttributes: true, options: options) return self.writeStreamingKind(type, size: size) } + return self.buffer.writeSpace() + self.writeStreamingKind(type, size: size) case .streamingBytes(var bytes): return self.buffer.writeBuffer(&bytes) case .streamingEnd: - return 0 // do nothing, this is a "fake" event + return 0 // do nothing, this is a "fake" event case .finish: guard case .server(_, let options) = self.buffer.mode else { preconditionFailure("Only server can write responses.") @@ -150,9 +144,7 @@ extension ResponseEncodeBuffer { } @discardableResult mutating func writeStreamingKind(_ kind: StreamingKind, size: Int) -> Int { - self.buffer.writeStreamingKind(kind) + - self.buffer.writeSpace() + - self.buffer.writeString("{\(size)}\r\n") + self.buffer.writeStreamingKind(kind) + self.buffer.writeSpace() + self.buffer.writeString("{\(size)}\r\n") } } @@ -162,9 +154,8 @@ extension EncodeBuffer { case .binary: return self.writeString("BINARY") case .body(let section, let offset): - return self.writeString("BODY") + - self.writeSection(section) + - self.writeIfExists(offset) { offset in + return self.writeString("BODY") + self.writeSection(section) + + self.writeIfExists(offset) { offset in self.writeString("<\(offset)>") } case .rfc822: diff --git a/Sources/NIOIMAPCore/String+ByteBuffer.swift b/Sources/NIOIMAPCore/String+ByteBuffer.swift index 91f9ef8cd..164b67807 100644 --- a/Sources/NIOIMAPCore/String+ByteBuffer.swift +++ b/Sources/NIOIMAPCore/String+ByteBuffer.swift @@ -29,7 +29,9 @@ extension String { return nil } } - preconditionFailure("This should never happen - either the whole string should be successfully parsed as UTF8, or an error caught.") + preconditionFailure( + "This should never happen - either the whole string should be successfully parsed as UTF8, or an error caught." + ) } /// Will try to decode the bytes as UTF8, skipping any that are invalid. diff --git a/Sources/NIOIMAPCore/_ByteBufferProtocol.swift b/Sources/NIOIMAPCore/_ByteBufferProtocol.swift index bee4763b0..56d00eeda 100644 --- a/Sources/NIOIMAPCore/_ByteBufferProtocol.swift +++ b/Sources/NIOIMAPCore/_ByteBufferProtocol.swift @@ -106,6 +106,7 @@ extension ByteBufferView: _ByteBufferViewAPITemplate {} /// ### Notes /// All `ByteBuffer` methods that don't contain the word 'unsafe' will only allow you to access the 'readable bytes'. /// +// swift-format-ignore: AmbiguousTrailingClosureOverload protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConvertible { /// The number of bytes writable until `ByteBuffer` will need to grow its underlying storage which will likely /// trigger a copy of the bytes. @@ -167,7 +168,10 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// - minimumWritableBytes: The number of writable bytes to reserve capacity for before vending the `ByteBuffer` pointer to `body`. /// - body: The closure that will accept the yielded bytes and return the number of bytes written. /// - returns: The number of bytes written. - mutating func writeWithUnsafeMutableBytes(minimumWritableBytes: Int, _ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int + mutating func writeWithUnsafeMutableBytes( + minimumWritableBytes: Int, + _ body: (UnsafeMutableRawBufferPointer) throws -> Int + ) rethrows -> Int /// This vends a pointer to the storage of the `ByteBuffer`. It's marked as _very unsafe_ because it might contain /// uninitialised memory and it's undefined behaviour to read it. In most cases you should use `withUnsafeReadableBytes`. @@ -201,10 +205,14 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// - parameters: /// - body: The closure that will accept the yielded bytes and the `storageManagement`. /// - returns: The value returned by `body`. - func withUnsafeReadableBytesWithStorageManagement(_ body: (UnsafeRawBufferPointer, Unmanaged) throws -> T) rethrows -> T + func withUnsafeReadableBytesWithStorageManagement( + _ body: (UnsafeRawBufferPointer, Unmanaged) throws -> T + ) rethrows -> T /// See `withUnsafeReadableBytesWithStorageManagement` and `withVeryUnsafeBytes`. - func withVeryUnsafeBytesWithStorageManagement(_ body: (UnsafeRawBufferPointer, Unmanaged) throws -> T) rethrows -> T + func withVeryUnsafeBytesWithStorageManagement( + _ body: (UnsafeRawBufferPointer, Unmanaged) throws -> T + ) rethrows -> T /// Returns a slice of size `length` bytes, starting at `index`. The `ByteBuffer` this is invoked on and the /// `ByteBuffer` returned will share the same underlying storage. However, the byte at `index` in this `ByteBuffer` @@ -439,6 +447,7 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// - parameters: /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed. /// - returns: The number of bytes read. + // swift-format-ignore: AmbiguousTrailingClosureOverload mutating func readWithUnsafeReadableBytes(_ body: (UnsafeRawBufferPointer) throws -> Int) rethrows -> Int /// Yields an immutable buffer pointer containing this `ByteBuffer`'s readable bytes. Will move the reader index @@ -449,6 +458,7 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// - parameters: /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value. /// - returns: The value `body` returned in the second tuple component. + // swift-format-ignore: AmbiguousTrailingClosureOverload mutating func readWithUnsafeReadableBytes(_ body: (UnsafeRawBufferPointer) throws -> (Int, T)) rethrows -> T /// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify the yielded bytes. @@ -459,7 +469,10 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// - parameters: /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed. /// - returns: The number of bytes read. - mutating func readWithUnsafeMutableReadableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int + // swift-format-ignore: AmbiguousTrailingClosureOverload + mutating func readWithUnsafeMutableReadableBytes( + _ body: (UnsafeMutableRawBufferPointer) throws -> Int + ) rethrows -> Int /// Yields a mutable buffer pointer containing this `ByteBuffer`'s readable bytes. You may modify the yielded bytes. /// Will move the reader index by the number of bytes `body` returns in the first tuple component but leave writer index as it was. @@ -469,7 +482,10 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// - parameters: /// - body: The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value. /// - returns: The value `body` returned in the second tuple component. - mutating func readWithUnsafeMutableReadableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> (Int, T)) rethrows -> T + // swift-format-ignore: AmbiguousTrailingClosureOverload + mutating func readWithUnsafeMutableReadableBytes( + _ body: (UnsafeMutableRawBufferPointer) throws -> (Int, T) + ) rethrows -> T /// Copy `buffer`'s readable bytes into this `ByteBuffer` starting at `index`. Does not move any of the reader or writer indices. /// @@ -558,7 +574,8 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// - index: The index of the first byte to write. /// - endianness: The endianness to use, defaults to big endian. /// - returns: The number of bytes written. - mutating func setInteger(_ integer: T, at index: Int, endianness: Endianness, as: T.Type) -> Int where T: FixedWidthInteger + mutating func setInteger(_ integer: T, at index: Int, endianness: Endianness, as: T.Type) -> Int + where T: FixedWidthInteger /// A view into the readable bytes of the `ByteBuffer`. var readableBytesView: ByteBufferView { get } @@ -581,7 +598,8 @@ protocol _ByteBufferAPITemplate where Self: Hashable, Self: CustomStringConverti /// /// A `ByteBufferView` is useful whenever a `Collection where Element == UInt8` representing a portion of a /// `ByteBuffer` is needed. -protocol _ByteBufferViewAPITemplate where Self: RandomAccessCollection, Self: MutableCollection, Self: RangeReplaceableCollection { +protocol _ByteBufferViewAPITemplate +where Self: RandomAccessCollection, Self: MutableCollection, Self: RangeReplaceableCollection { /// A type representing the sequence's elements. associatedtype Element = UInt8 @@ -735,5 +753,6 @@ protocol _ByteBufferViewAPITemplate where Self: RandomAccessCollection, Self: Mu /// *m* is the length of `newElements`. If the call to this method simply /// appends the contents of `newElements` to the collection, this method is /// equivalent to `append(contentsOf:)`. - mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C: Collection, C.Element == ByteBufferView.Element + mutating func replaceSubrange(_ subrange: Range, with newElements: C) + where C: Collection, C.Element == ByteBufferView.Element } diff --git a/Sources/NIOIMAPPerformanceTester/CommandTester.swift b/Sources/NIOIMAPPerformanceTester/CommandTester.swift index 1a7de51e0..4c37eb9b9 100644 --- a/Sources/NIOIMAPPerformanceTester/CommandTester.swift +++ b/Sources/NIOIMAPPerformanceTester/CommandTester.swift @@ -30,7 +30,7 @@ class CommandTester: Benchmark { func tearDown() {} @discardableResult func run() throws -> Int { - for i in 1 ... self.iterations { + for i in 1...self.iterations { var commandBuffer = CommandEncodeBuffer(buffer: ByteBuffer(), options: .init(), loggingMode: false) commandBuffer.writeCommand(.init(tag: "\(i)", command: self.command)) diff --git a/Sources/NIOIMAPPerformanceTester/main.swift b/Sources/NIOIMAPPerformanceTester/main.swift index 95a6f3c2b..0d81e23ee 100644 --- a/Sources/NIOIMAPPerformanceTester/main.swift +++ b/Sources/NIOIMAPPerformanceTester/main.swift @@ -29,65 +29,204 @@ let commands: [(String, Command)] = [ ("parse_unselect", .unselect), ("parse_create_no_atts", .create(.init(ByteBuffer(string: "My Test Mailbox")), [])), ("parse_create_one_att", .create(.init(ByteBuffer(string: "My Test Mailbox")), [.attributes([.flagged])])), - ("parse_create_many_att", .create(.init(ByteBuffer(string: "My Test Mailbox")), [.attributes([.flagged, .junk, .archive, .sent, .trash])])), + ( + "parse_create_many_att", + .create( + .init(ByteBuffer(string: "My Test Mailbox")), + [.attributes([.flagged, .junk, .archive, .sent, .trash])] + ) + ), ("parse_delete", .delete(.inbox)), - ("parse_enable_lots", .enable([.acl, .binary, .catenate, .condStore, .children, .extendedSearch, .esort, .namespace])), + ( + "parse_enable_lots", + .enable([.acl, .binary, .catenate, .condStore, .children, .extendedSearch, .esort, .namespace]) + ), ("parse_enable_one", .enable([.namespace])), ("parse_copy_last_command", .copy(.lastCommand, .inbox)), ("parse_copy_all", .copy(.set(.all), .inbox)), - ("parse_copy_set_one", .copy(.set([1 ... 2, 4 ... 5, 10 ... 20]), .inbox)), - ("parse_copy_set_many", .copy(.set([1 ... 100]), .inbox)), - ("parse_fetch_last_command_lots", .fetch(.lastCommand, [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], [])), - ("parse_fetch_all_lots", .fetch(.set(.all), [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], [])), - ("parse_fetch_set_one_lots", .fetch(.set([1 ... 10]), [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], [])), - ("parse_fetch_set_many_lots", .fetch(.set([1 ... 2, 4 ... 7, 10 ... 100]), [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], [])), + ("parse_copy_set_one", .copy(.set([1...2, 4...5, 10...20]), .inbox)), + ("parse_copy_set_many", .copy(.set([1...100]), .inbox)), + ( + "parse_fetch_last_command_lots", + .fetch(.lastCommand, [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], []) + ), + ( + "parse_fetch_all_lots", + .fetch(.set(.all), [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], []) + ), + ( + "parse_fetch_set_one_lots", + .fetch(.set([1...10]), [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], []) + ), + ( + "parse_fetch_set_many_lots", + .fetch( + .set([1...2, 4...7, 10...100]), + [.envelope, .flags, .internalDate, .gmailThreadID, .modificationSequence], + [] + ) + ), ("parse_auth_plain_nil", .authenticate(mechanism: .init("PLAIN"), initialResponse: nil)), ("parse_auth_plain_empty", .authenticate(mechanism: .init("PLAIN"), initialResponse: .empty)), - ("parse_auth_plain_initial data", .authenticate(mechanism: .init("PLAIN"), initialResponse: .init(ByteBuffer(string: "dGhpcyBpcyB0ZXN0IGJhc2U2NA==")))), + ( + "parse_auth_plain_initial data", + .authenticate( + mechanism: .init("PLAIN"), + initialResponse: .init(ByteBuffer(string: "dGhpcyBpcyB0ZXN0IGJhc2U2NA==")) + ) + ), ("parse_esearch_simple_all", .extendedSearch(.init(key: .all))), - ("parse_esearch_simple_recursive_date", .extendedSearch(.init(key: .and([.not(.answered), .not(.before(NIOIMAP.IMAPCalendarDay(year: 2000, month: 12, day: 12)!))])))), - ("parse_esearch_complex", .extendedSearch(.init(key: .and([.younger(123), .or(.keyword(.colorBit0), .keyword(.colorBit1))]), charset: "UTF-8", returnOptions: [.min, .max, .count], sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])!))), + ( + "parse_esearch_simple_recursive_date", + .extendedSearch( + .init( + key: .and([.not(.answered), .not(.before(NIOIMAP.IMAPCalendarDay(year: 2000, month: 12, day: 12)!))]) + ) + ) + ), + ( + "parse_esearch_complex", + .extendedSearch( + .init( + key: .and([.younger(123), .or(.keyword(.colorBit0), .keyword(.colorBit1))]), + charset: "UTF-8", + returnOptions: [.min, .max, .count], + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])! + ) + ) + ), ("parse_examine_no_params", .examine(.inbox, [])), ("parse_examine_1_param", .examine(.inbox, [.condStore])), - ("parse_examine_lots_param", .examine(.inbox, [.basic(.init(key: "param1", value: nil)), .basic(.init(key: "param2", value: nil)), .basic(.init(key: "param3", value: .sequence(.set([1 ... 5, 10 ... 100])))), .basic(.init(key: "param4", value: .comp(["str1"])))])), + ( + "parse_examine_lots_param", + .examine( + .inbox, + [ + .basic(.init(key: "param1", value: nil)), .basic(.init(key: "param2", value: nil)), + .basic(.init(key: "param3", value: .sequence(.set([1...5, 10...100])))), + .basic(.init(key: "param4", value: .comp(["str1"]))), + ] + ) + ), ("parse_expunge", .expunge), - ("parse_genurlauth_one", .generateAuthorizedURL([.init(urlRump: ByteBuffer(string: "test"), mechanism: .internal)])), - ("parse_genurlauth_many", .generateAuthorizedURL([.init(urlRump: ByteBuffer(string: "test1"), mechanism: .internal), .init(urlRump: ByteBuffer(string: "test2"), mechanism: .internal), .init(urlRump: ByteBuffer(string: "test3"), mechanism: .internal)])), - ("parse_getmetadata_complex", .getMetadata(options: [.maxSize(123), .scope(.infinity)], mailbox: .inbox, entries: ["test1", "test2"])), + ( + "parse_genurlauth_one", + .generateAuthorizedURL([.init(urlRump: ByteBuffer(string: "test"), mechanism: .internal)]) + ), + ( + "parse_genurlauth_many", + .generateAuthorizedURL([ + .init(urlRump: ByteBuffer(string: "test1"), mechanism: .internal), + .init(urlRump: ByteBuffer(string: "test2"), mechanism: .internal), + .init(urlRump: ByteBuffer(string: "test3"), mechanism: .internal), + ]) + ), + ( + "parse_getmetadata_complex", + .getMetadata(options: [.maxSize(123), .scope(.infinity)], mailbox: .inbox, entries: ["test1", "test2"]) + ), ("parse_getquota", .getQuota(.init("inbox"))), ("parse_select_params_none", .select(.inbox, [])), ("parse_select_params_one", .select(.inbox, [.condStore])), - ("parse_select_params_complex", .select(.inbox, [.condStore, .basic(.init(key: "param1", value: nil)), .qresync(.init(uidValidity: 123, modificationSequenceValue: .zero, knownUIDs: [1 ... 3, 5 ... 7, 10 ... 1000], sequenceMatchData: .init(knownSequenceSet: .set([1 ... 10, 10 ... 20, 30 ... 100]), knownUidSet: .set([100 ... 200, 300 ... 400]))))])), + ( + "parse_select_params_complex", + .select( + .inbox, + [ + .condStore, .basic(.init(key: "param1", value: nil)), + .qresync( + .init( + uidValidity: 123, + modificationSequenceValue: .zero, + knownUIDs: [1...3, 5...7, 10...1000], + sequenceMatchData: .init( + knownSequenceSet: .set([1...10, 10...20, 30...100]), + knownUidSet: .set([100...200, 300...400]) + ) + ) + ), + ] + ) + ), ("parse_setquota_one", .setQuota(QuotaRoot("inbox"), [.init(resourceName: "size", limit: 100)])), - ("parse_setquota_many", .setQuota(QuotaRoot("inbox"), [.init(resourceName: "size", limit: 100), .init(resourceName: "messages", limit: 100), .init(resourceName: "disk", limit: 100)])), + ( + "parse_setquota_many", + .setQuota( + QuotaRoot("inbox"), + [ + .init(resourceName: "size", limit: 100), .init(resourceName: "messages", limit: 100), + .init(resourceName: "disk", limit: 100), + ] + ) + ), ("parse_getquotaRoot", .getQuotaRoot(.inbox)), ("parse_id_one", .id(["key1": nil])), ("parse_id_many", .id(["key1": nil, "key2": "value2", "key3": "value3"])), ("parse_rename_params_none", .rename(from: .inbox, to: .init(ByteBuffer(string: "not an inbox")), parameters: [:])), - ("parse_rename_params_one", .rename(from: .inbox, to: .init(ByteBuffer(string: "not an inbox")), parameters: ["name": nil])), - ("parse_rename_params_many", .rename(from: .inbox, to: .init(ByteBuffer(string: "not an inbox")), parameters: ["name1": nil, "name2": .sequence(.set([1 ... 2, 3 ... 4]))])), + ( + "parse_rename_params_one", + .rename(from: .inbox, to: .init(ByteBuffer(string: "not an inbox")), parameters: ["name": nil]) + ), + ( + "parse_rename_params_many", + .rename( + from: .inbox, + to: .init(ByteBuffer(string: "not an inbox")), + parameters: ["name1": nil, "name2": .sequence(.set([1...2, 3...4]))] + ) + ), ("parse_subscribe", .subscribe(.inbox)), ("parse_unsubscribe", .unsubscribe(.inbox)), ("parse_lsub", .lsub(reference: .inbox, pattern: ByteBuffer(string: "pattern"))), - ("parse_store_simple_last_command", .store(.lastCommand, [.unchangedSince(.init(modificationSequence: 124))], .flags(.add(silent: false, list: [.answered])))), - ("parse_store_simple_all", .store(.set(.all), [.unchangedSince(.init(modificationSequence: 124))], .flags(.add(silent: false, list: [.answered])))), - ("parse_store_complex_all", .store(.set(.all), [.unchangedSince(.init(modificationSequence: 124))], .flags(.add(silent: false, list: [.answered, .deleted, .flagged, .seen, .draft])))), - ("parse_store_complex", .store(.set([1 ... 10, 11 ... 20, 21 ... 30, 31 ... 40]), [.unchangedSince(.init(modificationSequence: 124))], .flags(.add(silent: false, list: [.answered, .deleted, .flagged, .seen, .draft])))), + ( + "parse_store_simple_last_command", + .store( + .lastCommand, + [.unchangedSince(.init(modificationSequence: 124))], + .flags(.add(silent: false, list: [.answered])) + ) + ), + ( + "parse_store_simple_all", + .store( + .set(.all), + [.unchangedSince(.init(modificationSequence: 124))], + .flags(.add(silent: false, list: [.answered])) + ) + ), + ( + "parse_store_complex_all", + .store( + .set(.all), + [.unchangedSince(.init(modificationSequence: 124))], + .flags(.add(silent: false, list: [.answered, .deleted, .flagged, .seen, .draft])) + ) + ), + ( + "parse_store_complex", + .store( + .set([1...10, 11...20, 21...30, 31...40]), + [.unchangedSince(.init(modificationSequence: 124))], + .flags(.add(silent: false, list: [.answered, .deleted, .flagged, .seen, .draft])) + ) + ), ("parse_move_last command", .move(.lastCommand, .inbox)), ("parse_move_all", .move(.lastCommand, .inbox)), - ("parse_move_set", .move(.set([1 ... 100, 200 ... 300, 3000 ... 4000]), .inbox)), + ("parse_move_set", .move(.set([1...100, 200...300, 3000...4000]), .inbox)), ] // MARK: Test Harness var warning: String = "" -assert({ - print("======================================================") - print("= YOU ARE RUNNING NIOPerformanceTester IN DEBUG MODE =") - print("======================================================") - warning = " <<< DEBUG MODE >>>" - return true -}()) +assert( + { + print("======================================================") + print("= YOU ARE RUNNING NIOPerformanceTester IN DEBUG MODE =") + print("======================================================") + warning = " <<< DEBUG MODE >>>" + return true + }() +) func measure(_ fn: () throws -> Int) rethrows -> [TimeInterval] { func measureOne(_ fn: () throws -> Int) rethrows -> TimeInterval { @@ -97,9 +236,9 @@ func measure(_ fn: () throws -> Int) rethrows -> [TimeInterval] { return end.timeIntervalSince(start) } - _ = try measureOne(fn) /* pre-heat and throw away */ + _ = try measureOne(fn) // pre-heat and throw away var measurements = Array(repeating: 0.0, count: 10) - for i in 0 ..< 10 { + for i in 0..<10 { measurements[i] = try measureOne(fn) } diff --git a/Sources/Proxy/MailClientToProxyHandler.swift b/Sources/Proxy/MailClientToProxyHandler.swift index 104dba371..fc045e1fb 100644 --- a/Sources/Proxy/MailClientToProxyHandler.swift +++ b/Sources/Proxy/MailClientToProxyHandler.swift @@ -37,7 +37,10 @@ class MailClientToProxyHandler: ChannelInboundHandler { let boundContext = NIOLoopBound(context, eventLoop: context.eventLoop) let boundSelf = NIOLoopBound(self, eventLoop: context.eventLoop) ClientBootstrap(group: context.eventLoop).channelInitializer { channel in - let sslHandler = try! NIOSSLClientHandler(context: NIOSSLContext(configuration: .clientDefault), serverHostname: serverHost) + let sslHandler = try! NIOSSLClientHandler( + context: NIOSSLContext(configuration: .clientDefault), + serverHostname: serverHost + ) return channel.pipeline.addHandlers([ sslHandler, OutboundPrintHandler(type: "CLIENT (Encoded)"), diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index ce7dab476..000000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,15 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the SwiftNIO open source project -// -// Copyright (c) 2020 Apple Inc. and the SwiftNIO project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of SwiftNIO project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -fatalError("Please use --enable-test-discovery to run the tests instead") diff --git a/Tests/NIOIMAPCoreTests/Base64Tests.swift b/Tests/NIOIMAPCoreTests/Base64Tests.swift index 1b0be5c18..e14641e24 100644 --- a/Tests/NIOIMAPCoreTests/Base64Tests.swift +++ b/Tests/NIOIMAPCoreTests/Base64Tests.swift @@ -21,16 +21,26 @@ class Base64_Tests: XCTestCase {} extension Base64_Tests { func testRoundTrip() { let buffers: [(UInt, [UInt8])] = [ - (#line, [0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, - 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x37, 0x32, - 0x1B, 0x81, 0x84, 0xDC, 0xA9, 0x13, 0xCC, 0x17, 0x81, 0x89, 0x51, 0xDE, - 0x71, 0xE3, 0xF6, 0x09, 0x66, 0x34, 0x49, 0x1D, 0x1F, 0x01, 0x00, 0x20, - 0x00, 0x04, 0x04, 0x04, 0x04]), - (#line, [0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, - 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xB4, 0x01, - 0x1D, 0x74, 0xE9, 0x15, 0xF6, 0x60, 0xAD, 0xE8, 0xE9, 0x2E, 0x52, 0xC8, - 0x98, 0xFC, 0x24, 0x85, 0xB7, 0xDA, 0xD9, 0x0B, 0x5E, 0x01, 0x00, 0x20, - 0x00, 0x6D, 0x72, 0x63, 0x01]), + ( + #line, + [ + 0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x37, 0x32, + 0x1B, 0x81, 0x84, 0xDC, 0xA9, 0x13, 0xCC, 0x17, 0x81, 0x89, 0x51, 0xDE, + 0x71, 0xE3, 0xF6, 0x09, 0x66, 0x34, 0x49, 0x1D, 0x1F, 0x01, 0x00, 0x20, + 0x00, 0x04, 0x04, 0x04, 0x04, + ] + ), + ( + #line, + [ + 0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xB4, 0x01, + 0x1D, 0x74, 0xE9, 0x15, 0xF6, 0x60, 0xAD, 0xE8, 0xE9, 0x2E, 0x52, 0xC8, + 0x98, 0xFC, 0x24, 0x85, 0xB7, 0xDA, 0xD9, 0x0B, 0x5E, 0x01, 0x00, 0x20, + 0x00, 0x6D, 0x72, 0x63, 0x01, + ] + ), (#line, []), (#line, [0xFF]), (#line, [0x0]), @@ -56,73 +66,90 @@ extension Base64_Tests { func testDecode() { let buffers: [(UInt, String, [UInt8])] = [ - (#line, - "YIIB+wYJKoZIhvcSAQICAQBuggHqMIIB5qADAgEFoQMCAQ6iBwMFACAAAACjggEmYYIBIjCCAR6gAwIBBaESGxB1Lndhc2hpbmd0b24uZWR1oi0wK6ADAgEDoSQwIhsEaW1hcBsac2hpdmFtcy5jYWMud2FzaGluZ3Rvbi5lZHWjgdMwgdCgAwIBAaEDAgEDooHDBIHAcS1GSa5b+fXnPZNmXB9SjL8Ollj2SKyb+3S0iXMljen/jNkpJXAleKTz6BQPzj8duz8EtoOuNfKgweViyn/9B9bccy1uuAE2HI0yC/PHXNNU9ZrBziJ8Lm0tTNc98kUpjXnHZhsMcz5Mx2GR6dGknbI0iaGcRerMUsWOuBmKKKRmVMMdR9T3EZdpqsBd7jZCNMWotjhivd5zovQlFqQ2Wjc2+y46vKP/iXxWIuQJuDiisyXF0Y8+5GTpALpHDc1/pIGmMIGjoAMCAQGigZsEgZg2on5mSuxoDHEA1w9bcW9nFdFxDKpdrQhVGVRDIzcCMCTzvUboqb5KjY1NJKJsfjRQiBYBdENKfzK+g5DlV8nrw81uOcP8NOQCLR5XkoMHC0Dr/80ziQzbNqhxO6652Npft0LQwJvenwDI13YxpwOdMXzkWZN/XrEqOWp6GCgXTBvCyLWLlWnbaUkZdEYbKHBPjd8t/1x5Yg==", - [0x60, 0x82, 0x01, 0xFB, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, - 0x01, 0x02, 0x02, 0x01, 0x00, 0x6E, 0x82, 0x01, 0xEA, 0x30, 0x82, 0x01, - 0xE6, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x03, 0x02, 0x01, 0x0E, 0xA2, - 0x07, 0x03, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA3, 0x82, 0x01, 0x26, - 0x61, 0x82, 0x01, 0x22, 0x30, 0x82, 0x01, 0x1E, 0xA0, 0x03, 0x02, 0x01, - 0x05, 0xA1, 0x12, 0x1B, 0x10, 0x75, 0x2E, 0x77, 0x61, 0x73, 0x68, 0x69, - 0x6E, 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA2, 0x2D, 0x30, - 0x2B, 0xA0, 0x03, 0x02, 0x01, 0x03, 0xA1, 0x24, 0x30, 0x22, 0x1B, 0x04, - 0x69, 0x6D, 0x61, 0x70, 0x1B, 0x1A, 0x73, 0x68, 0x69, 0x76, 0x61, 0x6D, - 0x73, 0x2E, 0x63, 0x61, 0x63, 0x2E, 0x77, 0x61, 0x73, 0x68, 0x69, 0x6E, - 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA3, 0x81, 0xD3, 0x30, - 0x81, 0xD0, 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA1, 0x03, 0x02, 0x01, 0x03, - 0xA2, 0x81, 0xC3, 0x04, 0x81, 0xC0, 0x71, 0x2D, 0x46, 0x49, 0xAE, 0x5B, - 0xF9, 0xF5, 0xE7, 0x3D, 0x93, 0x66, 0x5C, 0x1F, 0x52, 0x8C, 0xBF, 0x0E, - 0x96, 0x58, 0xF6, 0x48, 0xAC, 0x9B, 0xFB, 0x74, 0xB4, 0x89, 0x73, 0x25, - 0x8D, 0xE9, 0xFF, 0x8C, 0xD9, 0x29, 0x25, 0x70, 0x25, 0x78, 0xA4, 0xF3, - 0xE8, 0x14, 0x0F, 0xCE, 0x3F, 0x1D, 0xBB, 0x3F, 0x04, 0xB6, 0x83, 0xAE, - 0x35, 0xF2, 0xA0, 0xC1, 0xE5, 0x62, 0xCA, 0x7F, 0xFD, 0x07, 0xD6, 0xDC, - 0x73, 0x2D, 0x6E, 0xB8, 0x01, 0x36, 0x1C, 0x8D, 0x32, 0x0B, 0xF3, 0xC7, - 0x5C, 0xD3, 0x54, 0xF5, 0x9A, 0xC1, 0xCE, 0x22, 0x7C, 0x2E, 0x6D, 0x2D, - 0x4C, 0xD7, 0x3D, 0xF2, 0x45, 0x29, 0x8D, 0x79, 0xC7, 0x66, 0x1B, 0x0C, - 0x73, 0x3E, 0x4C, 0xC7, 0x61, 0x91, 0xE9, 0xD1, 0xA4, 0x9D, 0xB2, 0x34, - 0x89, 0xA1, 0x9C, 0x45, 0xEA, 0xCC, 0x52, 0xC5, 0x8E, 0xB8, 0x19, 0x8A, - 0x28, 0xA4, 0x66, 0x54, 0xC3, 0x1D, 0x47, 0xD4, 0xF7, 0x11, 0x97, 0x69, - 0xAA, 0xC0, 0x5D, 0xEE, 0x36, 0x42, 0x34, 0xC5, 0xA8, 0xB6, 0x38, 0x62, - 0xBD, 0xDE, 0x73, 0xA2, 0xF4, 0x25, 0x16, 0xA4, 0x36, 0x5A, 0x37, 0x36, - 0xFB, 0x2E, 0x3A, 0xBC, 0xA3, 0xFF, 0x89, 0x7C, 0x56, 0x22, 0xE4, 0x09, - 0xB8, 0x38, 0xA2, 0xB3, 0x25, 0xC5, 0xD1, 0x8F, 0x3E, 0xE4, 0x64, 0xE9, - 0x00, 0xBA, 0x47, 0x0D, 0xCD, 0x7F, 0xA4, 0x81, 0xA6, 0x30, 0x81, 0xA3, - 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA2, 0x81, 0x9B, 0x04, 0x81, 0x98, 0x36, - 0xA2, 0x7E, 0x66, 0x4A, 0xEC, 0x68, 0x0C, 0x71, 0x00, 0xD7, 0x0F, 0x5B, - 0x71, 0x6F, 0x67, 0x15, 0xD1, 0x71, 0x0C, 0xAA, 0x5D, 0xAD, 0x08, 0x55, - 0x19, 0x54, 0x43, 0x23, 0x37, 0x02, 0x30, 0x24, 0xF3, 0xBD, 0x46, 0xE8, - 0xA9, 0xBE, 0x4A, 0x8D, 0x8D, 0x4D, 0x24, 0xA2, 0x6C, 0x7E, 0x34, 0x50, - 0x88, 0x16, 0x01, 0x74, 0x43, 0x4A, 0x7F, 0x32, 0xBE, 0x83, 0x90, 0xE5, - 0x57, 0xC9, 0xEB, 0xC3, 0xCD, 0x6E, 0x39, 0xC3, 0xFC, 0x34, 0xE4, 0x02, - 0x2D, 0x1E, 0x57, 0x92, 0x83, 0x07, 0x0B, 0x40, 0xEB, 0xFF, 0xCD, 0x33, - 0x89, 0x0C, 0xDB, 0x36, 0xA8, 0x71, 0x3B, 0xAE, 0xB9, 0xD8, 0xDA, 0x5F, - 0xB7, 0x42, 0xD0, 0xC0, 0x9B, 0xDE, 0x9F, 0x00, 0xC8, 0xD7, 0x76, 0x31, - 0xA7, 0x03, 0x9D, 0x31, 0x7C, 0xE4, 0x59, 0x93, 0x7F, 0x5E, 0xB1, 0x2A, - 0x39, 0x6A, 0x7A, 0x18, 0x28, 0x17, 0x4C, 0x1B, 0xC2, 0xC8, 0xB5, 0x8B, - 0x95, 0x69, 0xDB, 0x69, 0x49, 0x19, 0x74, 0x46, 0x1B, 0x28, 0x70, 0x4F, - 0x8D, 0xDF, 0x2D, 0xFF, 0x5C, 0x79, 0x62]), - (#line, "YGgGCSqGSIb3EgECAgIAb1kwV6ADAgEFoQMCAQ+iSzBJoAMCAQGiQgRAtHTEuOP2BXb9sBYFR4SJlDZxmg39IxmRBOhXRKdDA0uHTCOT9Bq3OsUTXUlk0CsFLoa8j+gvGDlgHuqzWHPSQg==", - [0x60, 0x68, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, - 0x02, 0x02, 0x00, 0x6F, 0x59, 0x30, 0x57, 0xA0, 0x03, 0x02, 0x01, 0x05, - 0xA1, 0x03, 0x02, 0x01, 0x0F, 0xA2, 0x4B, 0x30, 0x49, 0xA0, 0x03, 0x02, - 0x01, 0x01, 0xA2, 0x42, 0x04, 0x40, 0xB4, 0x74, 0xC4, 0xB8, 0xE3, 0xF6, - 0x05, 0x76, 0xFD, 0xB0, 0x16, 0x05, 0x47, 0x84, 0x89, 0x94, 0x36, 0x71, - 0x9A, 0x0D, 0xFD, 0x23, 0x19, 0x91, 0x04, 0xE8, 0x57, 0x44, 0xA7, 0x43, - 0x03, 0x4B, 0x87, 0x4C, 0x23, 0x93, 0xF4, 0x1A, 0xB7, 0x3A, 0xC5, 0x13, - 0x5D, 0x49, 0x64, 0xD0, 0x2B, 0x05, 0x2E, 0x86, 0xBC, 0x8F, 0xE8, 0x2F, - 0x18, 0x39, 0x60, 0x1E, 0xEA, 0xB3, 0x58, 0x73, 0xD2, 0x42]), - (#line, "YDMGCSqGSIb3EgECAgIBAAD/////6jcyG4GE3KkTzBeBiVHeceP2CWY0SR0fAQAgAAQEBAQ=", - [0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, - 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x37, 0x32, - 0x1B, 0x81, 0x84, 0xDC, 0xA9, 0x13, 0xCC, 0x17, 0x81, 0x89, 0x51, 0xDE, - 0x71, 0xE3, 0xF6, 0x09, 0x66, 0x34, 0x49, 0x1D, 0x1F, 0x01, 0x00, 0x20, - 0x00, 0x04, 0x04, 0x04, 0x04]), - (#line, "YDMGCSqGSIb3EgECAgIBAAD/////3LQBHXTpFfZgrejpLlLImPwkhbfa2QteAQAgAG1yYwE=", - [0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, - 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xB4, 0x01, - 0x1D, 0x74, 0xE9, 0x15, 0xF6, 0x60, 0xAD, 0xE8, 0xE9, 0x2E, 0x52, 0xC8, - 0x98, 0xFC, 0x24, 0x85, 0xB7, 0xDA, 0xD9, 0x0B, 0x5E, 0x01, 0x00, 0x20, - 0x00, 0x6D, 0x72, 0x63, 0x01]), + ( + #line, + "YIIB+wYJKoZIhvcSAQICAQBuggHqMIIB5qADAgEFoQMCAQ6iBwMFACAAAACjggEmYYIBIjCCAR6gAwIBBaESGxB1Lndhc2hpbmd0b24uZWR1oi0wK6ADAgEDoSQwIhsEaW1hcBsac2hpdmFtcy5jYWMud2FzaGluZ3Rvbi5lZHWjgdMwgdCgAwIBAaEDAgEDooHDBIHAcS1GSa5b+fXnPZNmXB9SjL8Ollj2SKyb+3S0iXMljen/jNkpJXAleKTz6BQPzj8duz8EtoOuNfKgweViyn/9B9bccy1uuAE2HI0yC/PHXNNU9ZrBziJ8Lm0tTNc98kUpjXnHZhsMcz5Mx2GR6dGknbI0iaGcRerMUsWOuBmKKKRmVMMdR9T3EZdpqsBd7jZCNMWotjhivd5zovQlFqQ2Wjc2+y46vKP/iXxWIuQJuDiisyXF0Y8+5GTpALpHDc1/pIGmMIGjoAMCAQGigZsEgZg2on5mSuxoDHEA1w9bcW9nFdFxDKpdrQhVGVRDIzcCMCTzvUboqb5KjY1NJKJsfjRQiBYBdENKfzK+g5DlV8nrw81uOcP8NOQCLR5XkoMHC0Dr/80ziQzbNqhxO6652Npft0LQwJvenwDI13YxpwOdMXzkWZN/XrEqOWp6GCgXTBvCyLWLlWnbaUkZdEYbKHBPjd8t/1x5Yg==", + [ + 0x60, 0x82, 0x01, 0xFB, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, + 0x01, 0x02, 0x02, 0x01, 0x00, 0x6E, 0x82, 0x01, 0xEA, 0x30, 0x82, 0x01, + 0xE6, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x03, 0x02, 0x01, 0x0E, 0xA2, + 0x07, 0x03, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA3, 0x82, 0x01, 0x26, + 0x61, 0x82, 0x01, 0x22, 0x30, 0x82, 0x01, 0x1E, 0xA0, 0x03, 0x02, 0x01, + 0x05, 0xA1, 0x12, 0x1B, 0x10, 0x75, 0x2E, 0x77, 0x61, 0x73, 0x68, 0x69, + 0x6E, 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA2, 0x2D, 0x30, + 0x2B, 0xA0, 0x03, 0x02, 0x01, 0x03, 0xA1, 0x24, 0x30, 0x22, 0x1B, 0x04, + 0x69, 0x6D, 0x61, 0x70, 0x1B, 0x1A, 0x73, 0x68, 0x69, 0x76, 0x61, 0x6D, + 0x73, 0x2E, 0x63, 0x61, 0x63, 0x2E, 0x77, 0x61, 0x73, 0x68, 0x69, 0x6E, + 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA3, 0x81, 0xD3, 0x30, + 0x81, 0xD0, 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA1, 0x03, 0x02, 0x01, 0x03, + 0xA2, 0x81, 0xC3, 0x04, 0x81, 0xC0, 0x71, 0x2D, 0x46, 0x49, 0xAE, 0x5B, + 0xF9, 0xF5, 0xE7, 0x3D, 0x93, 0x66, 0x5C, 0x1F, 0x52, 0x8C, 0xBF, 0x0E, + 0x96, 0x58, 0xF6, 0x48, 0xAC, 0x9B, 0xFB, 0x74, 0xB4, 0x89, 0x73, 0x25, + 0x8D, 0xE9, 0xFF, 0x8C, 0xD9, 0x29, 0x25, 0x70, 0x25, 0x78, 0xA4, 0xF3, + 0xE8, 0x14, 0x0F, 0xCE, 0x3F, 0x1D, 0xBB, 0x3F, 0x04, 0xB6, 0x83, 0xAE, + 0x35, 0xF2, 0xA0, 0xC1, 0xE5, 0x62, 0xCA, 0x7F, 0xFD, 0x07, 0xD6, 0xDC, + 0x73, 0x2D, 0x6E, 0xB8, 0x01, 0x36, 0x1C, 0x8D, 0x32, 0x0B, 0xF3, 0xC7, + 0x5C, 0xD3, 0x54, 0xF5, 0x9A, 0xC1, 0xCE, 0x22, 0x7C, 0x2E, 0x6D, 0x2D, + 0x4C, 0xD7, 0x3D, 0xF2, 0x45, 0x29, 0x8D, 0x79, 0xC7, 0x66, 0x1B, 0x0C, + 0x73, 0x3E, 0x4C, 0xC7, 0x61, 0x91, 0xE9, 0xD1, 0xA4, 0x9D, 0xB2, 0x34, + 0x89, 0xA1, 0x9C, 0x45, 0xEA, 0xCC, 0x52, 0xC5, 0x8E, 0xB8, 0x19, 0x8A, + 0x28, 0xA4, 0x66, 0x54, 0xC3, 0x1D, 0x47, 0xD4, 0xF7, 0x11, 0x97, 0x69, + 0xAA, 0xC0, 0x5D, 0xEE, 0x36, 0x42, 0x34, 0xC5, 0xA8, 0xB6, 0x38, 0x62, + 0xBD, 0xDE, 0x73, 0xA2, 0xF4, 0x25, 0x16, 0xA4, 0x36, 0x5A, 0x37, 0x36, + 0xFB, 0x2E, 0x3A, 0xBC, 0xA3, 0xFF, 0x89, 0x7C, 0x56, 0x22, 0xE4, 0x09, + 0xB8, 0x38, 0xA2, 0xB3, 0x25, 0xC5, 0xD1, 0x8F, 0x3E, 0xE4, 0x64, 0xE9, + 0x00, 0xBA, 0x47, 0x0D, 0xCD, 0x7F, 0xA4, 0x81, 0xA6, 0x30, 0x81, 0xA3, + 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA2, 0x81, 0x9B, 0x04, 0x81, 0x98, 0x36, + 0xA2, 0x7E, 0x66, 0x4A, 0xEC, 0x68, 0x0C, 0x71, 0x00, 0xD7, 0x0F, 0x5B, + 0x71, 0x6F, 0x67, 0x15, 0xD1, 0x71, 0x0C, 0xAA, 0x5D, 0xAD, 0x08, 0x55, + 0x19, 0x54, 0x43, 0x23, 0x37, 0x02, 0x30, 0x24, 0xF3, 0xBD, 0x46, 0xE8, + 0xA9, 0xBE, 0x4A, 0x8D, 0x8D, 0x4D, 0x24, 0xA2, 0x6C, 0x7E, 0x34, 0x50, + 0x88, 0x16, 0x01, 0x74, 0x43, 0x4A, 0x7F, 0x32, 0xBE, 0x83, 0x90, 0xE5, + 0x57, 0xC9, 0xEB, 0xC3, 0xCD, 0x6E, 0x39, 0xC3, 0xFC, 0x34, 0xE4, 0x02, + 0x2D, 0x1E, 0x57, 0x92, 0x83, 0x07, 0x0B, 0x40, 0xEB, 0xFF, 0xCD, 0x33, + 0x89, 0x0C, 0xDB, 0x36, 0xA8, 0x71, 0x3B, 0xAE, 0xB9, 0xD8, 0xDA, 0x5F, + 0xB7, 0x42, 0xD0, 0xC0, 0x9B, 0xDE, 0x9F, 0x00, 0xC8, 0xD7, 0x76, 0x31, + 0xA7, 0x03, 0x9D, 0x31, 0x7C, 0xE4, 0x59, 0x93, 0x7F, 0x5E, 0xB1, 0x2A, + 0x39, 0x6A, 0x7A, 0x18, 0x28, 0x17, 0x4C, 0x1B, 0xC2, 0xC8, 0xB5, 0x8B, + 0x95, 0x69, 0xDB, 0x69, 0x49, 0x19, 0x74, 0x46, 0x1B, 0x28, 0x70, 0x4F, + 0x8D, 0xDF, 0x2D, 0xFF, 0x5C, 0x79, 0x62, + ] + ), + ( + #line, + "YGgGCSqGSIb3EgECAgIAb1kwV6ADAgEFoQMCAQ+iSzBJoAMCAQGiQgRAtHTEuOP2BXb9sBYFR4SJlDZxmg39IxmRBOhXRKdDA0uHTCOT9Bq3OsUTXUlk0CsFLoa8j+gvGDlgHuqzWHPSQg==", + [ + 0x60, 0x68, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, + 0x02, 0x02, 0x00, 0x6F, 0x59, 0x30, 0x57, 0xA0, 0x03, 0x02, 0x01, 0x05, + 0xA1, 0x03, 0x02, 0x01, 0x0F, 0xA2, 0x4B, 0x30, 0x49, 0xA0, 0x03, 0x02, + 0x01, 0x01, 0xA2, 0x42, 0x04, 0x40, 0xB4, 0x74, 0xC4, 0xB8, 0xE3, 0xF6, + 0x05, 0x76, 0xFD, 0xB0, 0x16, 0x05, 0x47, 0x84, 0x89, 0x94, 0x36, 0x71, + 0x9A, 0x0D, 0xFD, 0x23, 0x19, 0x91, 0x04, 0xE8, 0x57, 0x44, 0xA7, 0x43, + 0x03, 0x4B, 0x87, 0x4C, 0x23, 0x93, 0xF4, 0x1A, 0xB7, 0x3A, 0xC5, 0x13, + 0x5D, 0x49, 0x64, 0xD0, 0x2B, 0x05, 0x2E, 0x86, 0xBC, 0x8F, 0xE8, 0x2F, + 0x18, 0x39, 0x60, 0x1E, 0xEA, 0xB3, 0x58, 0x73, 0xD2, 0x42, + ] + ), + ( + #line, "YDMGCSqGSIb3EgECAgIBAAD/////6jcyG4GE3KkTzBeBiVHeceP2CWY0SR0fAQAgAAQEBAQ=", + [ + 0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x37, 0x32, + 0x1B, 0x81, 0x84, 0xDC, 0xA9, 0x13, 0xCC, 0x17, 0x81, 0x89, 0x51, 0xDE, + 0x71, 0xE3, 0xF6, 0x09, 0x66, 0x34, 0x49, 0x1D, 0x1F, 0x01, 0x00, 0x20, + 0x00, 0x04, 0x04, 0x04, 0x04, + ] + ), + ( + #line, "YDMGCSqGSIb3EgECAgIBAAD/////3LQBHXTpFfZgrejpLlLImPwkhbfa2QteAQAgAG1yYwE=", + [ + 0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0xB4, 0x01, + 0x1D, 0x74, 0xE9, 0x15, 0xF6, 0x60, 0xAD, 0xE8, 0xE9, 0x2E, 0x52, 0xC8, + 0x98, 0xFC, 0x24, 0x85, 0xB7, 0xDA, 0xD9, 0x0B, 0x5E, 0x01, 0x00, 0x20, + 0x00, 0x6D, 0x72, 0x63, 0x01, + ] + ), ] buffers.forEach { element in diff --git a/Tests/NIOIMAPCoreTests/Grammar/Append/AppendMessage+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Append/AppendMessage+Tests.swift index 7d7c2b0ed..1b382085d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Append/AppendMessage+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Append/AppendMessage+Tests.swift @@ -20,14 +20,45 @@ class AppendMessage_Tests: EncodeTestClass {} extension AppendMessage_Tests { func testEncode() { - let c1 = ServerMessageDate.Components(year: 2020, month: 7, day: 2, hour: 13, minute: 42, second: 52, timeZoneMinutes: 60)! - let c2 = ServerMessageDate.Components(year: 2020, month: 7, day: 2, hour: 13, minute: 42, second: 52, timeZoneMinutes: 60)! + let c1 = ServerMessageDate.Components( + year: 2020, + month: 7, + day: 2, + hour: 13, + minute: 42, + second: 52, + timeZoneMinutes: 60 + )! + let c2 = ServerMessageDate.Components( + year: 2020, + month: 7, + day: 2, + hour: 13, + minute: 42, + second: 52, + timeZoneMinutes: 60 + )! let inputs: [(AppendMessage, CommandEncodingOptions, [String], UInt)] = [ (.init(options: .none, data: .init(byteCount: 123)), .rfc3501, [" {123}\r\n"], #line), - (.init(options: .init(flagList: [.draft, .flagged], internalDate: nil, extensions: [:]), data: .init(byteCount: 123)), .rfc3501, [" (\\Draft \\Flagged) {123}\r\n"], #line), - (.init(options: .init(flagList: [.draft, .flagged], internalDate: ServerMessageDate(c1), extensions: [:]), data: .init(byteCount: 123)), .rfc3501, [" (\\Draft \\Flagged) \"2-Jul-2020 13:42:52 +0100\" {123}\r\n"], #line), - (.init(options: .init(flagList: [], internalDate: ServerMessageDate(c2), extensions: [:]), data: .init(byteCount: 456)), .literalPlus, [" \"2-Jul-2020 13:42:52 +0100\" {456+}\r\n"], #line), + ( + .init( + options: .init(flagList: [.draft, .flagged], internalDate: nil, extensions: [:]), + data: .init(byteCount: 123) + ), .rfc3501, [" (\\Draft \\Flagged) {123}\r\n"], #line + ), + ( + .init( + options: .init(flagList: [.draft, .flagged], internalDate: ServerMessageDate(c1), extensions: [:]), + data: .init(byteCount: 123) + ), .rfc3501, [" (\\Draft \\Flagged) \"2-Jul-2020 13:42:52 +0100\" {123}\r\n"], #line + ), + ( + .init( + options: .init(flagList: [], internalDate: ServerMessageDate(c2), extensions: [:]), + data: .init(byteCount: 456) + ), .literalPlus, [" \"2-Jul-2020 13:42:52 +0100\" {456+}\r\n"], #line + ), (.init(options: .none, data: .init(byteCount: 456)), .literalPlus, [" {456+}\r\n"], #line), ] diff --git a/Tests/NIOIMAPCoreTests/Grammar/Append/AppendOptions+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Append/AppendOptions+Tests.swift index 351c7e9fc..8665db42a 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Append/AppendOptions+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Append/AppendOptions+Tests.swift @@ -20,13 +20,24 @@ class AppendOptions_Tests: EncodeTestClass {} extension AppendOptions_Tests { func testEncode() throws { - let components = ServerMessageDate.Components(year: 1994, month: 6, day: 25, hour: 1, minute: 2, second: 3, timeZoneMinutes: 0)! + let components = ServerMessageDate.Components( + year: 1994, + month: 6, + day: 25, + hour: 1, + minute: 2, + second: 3, + timeZoneMinutes: 0 + )! let date = ServerMessageDate(components) let inputs: [(AppendOptions, String, UInt)] = [ (.none, "", #line), (.init(flagList: [.answered], internalDate: nil, extensions: [:]), " (\\Answered)", #line), - (.init(flagList: [.answered], internalDate: date, extensions: [:]), " (\\Answered) \"25-Jun-1994 01:02:03 +0000\"", #line), + ( + .init(flagList: [.answered], internalDate: date, extensions: [:]), + " (\\Answered) \"25-Jun-1994 01:02:03 +0000\"", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrl+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrl+Tests.swift index 93d6fff8b..4e8c3b193 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrl+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrl+Tests.swift @@ -24,10 +24,16 @@ extension AuthIMAPURL_Tests { func testEncoding() { let inputs: [(NetworkMessagePath, String, UInt)] = [ ( - .init(server: .init(host: "localhost"), messagePath: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123))), + .init( + server: .init(host: "localhost"), + messagePath: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ) + ), "imap://localhost/test/;UID=123", #line - ), + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeAuthenticatedURL($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlFull+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlFull+Tests.swift index 4ef36f964..d1e8aabbe 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlFull+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlFull+Tests.swift @@ -24,10 +24,22 @@ extension AuthIMAPURLFull_Tests { func testEncoding() { let inputs: [(FullAuthenticatedURL, String, UInt)] = [ ( - .init(networkMessagePath: .init(server: .init(host: "localhost"), messagePath: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123))), authenticatedURL: .init(authenticatedURL: .init(access: .anonymous), verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "data")))), + .init( + networkMessagePath: .init( + server: .init(host: "localhost"), + messagePath: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ) + ), + authenticatedURL: .init( + authenticatedURL: .init(access: .anonymous), + verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "data")) + ) + ), "imap://localhost/test/;UID=123;URLAUTH=anonymous:INTERNAL:data", #line - ), + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeAuthIMAPURLFull($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlRump+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlRump+Tests.swift index 1b8b1976d..93494f7f6 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlRump+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/AuthImapUrlRump+Tests.swift @@ -24,10 +24,19 @@ extension AuthIMAPURLRump_Tests { func testEncoding() { let inputs: [(RumpAuthenticatedURL, String, UInt)] = [ ( - .init(authenticatedURL: .init(server: .init(host: "localhost"), messagePath: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123))), authenticatedURLRump: .init(access: .anonymous)), + .init( + authenticatedURL: .init( + server: .init(host: "localhost"), + messagePath: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ) + ), + authenticatedURLRump: .init(access: .anonymous) + ), "imap://localhost/test/;UID=123;URLAUTH=anonymous", #line - ), + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeAuthIMAPURLRump($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Body/BodyStructure+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Body/BodyStructure+Tests.swift index 680a0e59f..e360796de 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Body/BodyStructure+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Body/BodyStructure+Tests.swift @@ -30,7 +30,18 @@ extension BodyStructure_Tests { // are all correct. let inputs: [(BodyStructure, [SectionSpecifier.Part], UInt)] = [ ( - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), [ [], [1], @@ -43,13 +54,40 @@ extension BodyStructure_Tests { kind: .message( .init( message: .rfc822, - envelope: Envelope(date: nil, subject: nil, from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0)) + envelope: Envelope( + date: nil, + subject: nil, + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) ), lineCount: 1 ) ), - fields: BodyStructure.Fields(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 99) + fields: BodyStructure.Fields( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 99 + ) ) ), [ @@ -59,9 +97,25 @@ extension BodyStructure_Tests { #line ), ( - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .application, sub: .mixed)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 1))), - ], mediaSubtype: .mixed)), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .application, sub: .mixed)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 1 + ) + ) + ) + ], + mediaSubtype: .mixed + ) + ), [ [], [1], @@ -70,11 +124,32 @@ extension BodyStructure_Tests { #line ), ( - .multipart(.init(parts: [ - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .application, sub: .mixed)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 1))), - ], mediaSubtype: .mixed)), - ], mediaSubtype: .mixed)), + .multipart( + .init( + parts: [ + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .application, sub: .mixed)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 1 + ) + ) + ) + ], + mediaSubtype: .mixed + ) + ) + ], + mediaSubtype: .mixed + ) + ), [ [], [1], @@ -84,11 +159,49 @@ extension BodyStructure_Tests { #line ), ( - .multipart(BodyStructure.Multipart(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .mixed)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 0))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .mixed)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 0))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .mixed)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 0))), - ], mediaSubtype: .mixed)), + .multipart( + BodyStructure.Multipart( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .mixed)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 0 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .mixed)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 0 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .mixed)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 0 + ) + ) + ), + ], + mediaSubtype: .mixed + ) + ), [ [], [1], @@ -99,17 +212,87 @@ extension BodyStructure_Tests { #line ), ( - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), [ [], [1], @@ -124,17 +307,87 @@ extension BodyStructure_Tests { #line ), ( - .multipart(.init(parts: [ - .multipart(.init(parts: [ - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), - ], mediaSubtype: .init("subtype"))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .init("subtype"))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .init("subtype"))), + .multipart( + .init( + parts: [ + .multipart( + .init( + parts: [ + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), [ [], [1], @@ -151,12 +404,25 @@ extension BodyStructure_Tests { ] for input in inputs { let line = input.2 - XCTAssertEqual(input.0.startIndex, input.1.first!, "startIndex should be \(String(reflecting: input.1.first))", line: line) - XCTAssertEqual(input.0.endIndex, input.1.last!, "endIndex should be \(String(reflecting: input.1.last))", line: line) + XCTAssertEqual( + input.0.startIndex, + input.1.first!, + "startIndex should be \(String(reflecting: input.1.first))", + line: line + ) + XCTAssertEqual( + input.0.endIndex, + input.1.last!, + "endIndex should be \(String(reflecting: input.1.last))", + line: line + ) guard input.0.startIndex == input.1.first!, input.0.endIndex == input.1.last! - else { XCTFail(line: line); continue } + else { + XCTFail(line: line) + continue + } // Check index(after:) do { var index = input.0.startIndex @@ -185,15 +451,59 @@ extension BodyStructure_Tests { func testRandomAccessCollection_position() { let inputs: [(BodyStructure, SectionSpecifier.Part, BodyStructure, UInt)] = [ ( - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), [], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), #line ), ( - .singlepart(.init(kind: .text(.init(mediaSubtype: "media", lineCount: 3)), fields: BodyStructure.Fields(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 123))), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "media", lineCount: 3)), + fields: BodyStructure.Fields( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 123 + ) + ) + ), [], - .singlepart(.init(kind: .text(.init(mediaSubtype: "media", lineCount: 3)), fields: BodyStructure.Fields(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 123))), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "media", lineCount: 3)), + fields: BodyStructure.Fields( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 123 + ) + ) + ), #line ), ( @@ -202,90 +512,419 @@ extension BodyStructure_Tests { kind: .message( .init( message: .rfc822, - envelope: Envelope(date: nil, subject: nil, from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0)) + envelope: Envelope( + date: nil, + subject: nil, + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) ), lineCount: 1 ) ), - fields: BodyStructure.Fields(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 99) + fields: BodyStructure.Fields( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 99 + ) ) ), [1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), #line ), ( - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))), - ], mediaSubtype: .init("subtype"))), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), [3], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ), #line ), ( - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), [2, 2, 1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), #line ), ( - .multipart(.init(parts: [ - .singlepart(.init( - kind: .message(BodyStructure.Singlepart.Message( - message: .rfc822, - envelope: Envelope(date: nil, subject: "A", from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))), - ], mediaSubtype: .init("mixed"))), - lineCount: 321 - )), - fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0) - )), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .init("mixed"))), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .message( + BodyStructure.Singlepart.Message( + message: .rfc822, + envelope: Envelope( + date: nil, + subject: "A", + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), + lineCount: 321 + ) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), [1, 1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), #line ), ( - .multipart(.init(parts: [ - .singlepart(.init( - kind: .message(BodyStructure.Singlepart.Message( - message: .rfc822, - envelope: Envelope(date: nil, subject: "A", from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))), - ], mediaSubtype: .init("mixed"))), - lineCount: 321 - )), - fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0) - )), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .init("mixed"))), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .message( + BodyStructure.Singlepart.Message( + message: .rfc822, + envelope: Envelope( + date: nil, + subject: "A", + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), + lineCount: 321 + ) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), [1, 1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), #line ), ] inputs.forEach { (input, index, expected, line) in guard let sub = input.find(index) - else { XCTFail("Invalid part '\(index)'.", line: line); return } + else { + XCTFail("Invalid part '\(index)'.", line: line) + return + } XCTAssertEqual(sub, expected, line: line) XCTAssertEqual(input[index], expected, line: line) } @@ -294,15 +933,48 @@ extension BodyStructure_Tests { func testMediaType() { let inputs: [(BodyStructure, Media.TopLevelType, Media.Subtype, UInt)] = [ ( - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: "amr")), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: "amr")), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), "audio", "amr", #line ), ( - .singlepart(.init(kind: .basic(.init(topLevel: .image, sub: "jpeg")), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .image, sub: "jpeg")), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), "image", "jpeg", #line ), ( - .singlepart(.init(kind: .text(.init(mediaSubtype: "html", lineCount: 3)), fields: BodyStructure.Fields(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 123))), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "html", lineCount: 3)), + fields: BodyStructure.Fields( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 123 + ) + ) + ), "text", "html", #line ), ( @@ -311,27 +983,86 @@ extension BodyStructure_Tests { kind: .message( .init( message: .rfc822, - envelope: Envelope(date: nil, subject: nil, from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0)) + envelope: Envelope( + date: nil, + subject: nil, + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) ), lineCount: 1 ) ), - fields: BodyStructure.Fields(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 99) + fields: BodyStructure.Fields( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 99 + ) ) ), "message", "rfc822", #line ), ( - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .alternative)), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ) + ], + mediaSubtype: .alternative + ) + ), "multipart", "alternative", #line ), ( - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .mixed)), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ) + ], + mediaSubtype: .mixed + ) + ), "multipart", "mixed", #line ), ] @@ -345,145 +1076,677 @@ extension BodyStructure_Tests { func testEnumeratingParts() { let inputs: [(BodyStructure, [(SectionSpecifier.Part, BodyStructure)], UInt)] = [ ( - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), [ ( [], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))) - ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ) + ) ], #line ), ( - .multipart(.init(parts: [ - .singlepart(.init( - kind: .message(BodyStructure.Singlepart.Message( - message: .rfc822, - envelope: Envelope(date: nil, subject: "A", from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))), - ], mediaSubtype: .init("mixed"))), - lineCount: 321 - )), - fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0) - )), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .init("mixed"))), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .message( + BodyStructure.Singlepart.Message( + message: .rfc822, + envelope: Envelope( + date: nil, + subject: "A", + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), + lineCount: 321 + ) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), [ ( [], - .multipart(.init(parts: [ - .singlepart(.init( - kind: .message(BodyStructure.Singlepart.Message( - message: .rfc822, - envelope: Envelope(date: nil, subject: "A", from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))), - ], mediaSubtype: .init("mixed"))), - lineCount: 321 - )), - fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0) - )), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - ], mediaSubtype: .init("mixed"))) + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .message( + BodyStructure.Singlepart.Message( + message: .rfc822, + envelope: Envelope( + date: nil, + subject: "A", + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), + lineCount: 321 + ) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ) ), ( [1], - .singlepart(.init( - kind: .message(BodyStructure.Singlepart.Message( - message: .rfc822, - envelope: Envelope(date: nil, subject: "A", from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))), - ], mediaSubtype: .init("mixed"))), - lineCount: 321 - )), - fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0) - )) + .singlepart( + .init( + kind: .message( + BodyStructure.Singlepart.Message( + message: .rfc822, + envelope: Envelope( + date: nil, + subject: "A", + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ), + ], + mediaSubtype: .init("mixed") + ) + ), + lineCount: 321 + ) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ) ), ( [1, 1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 1))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 1 + ) + ) + ) ), ( [1, 2], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 2))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 2 + ) + ) + ) ), ( [2], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ) ), ], #line ), ( - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), [ ( [], - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))) + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .audio, sub: .alternative) + ), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ) ), ( [1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ) ), ( [2], - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), - ], mediaSubtype: .init("subtype"))), - ], mediaSubtype: .init("subtype"))) + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ) ), ( [2, 1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 0))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 0 + ) + ) + ) ), ( [2, 2], - .multipart(.init(parts: [ - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))), - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), - ], mediaSubtype: .init("subtype"))) + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), + ], + mediaSubtype: .init("subtype") + ) + ) ), ( [2, 2, 1], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 3))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 3 + ) + ) + ) ), ( [2, 2, 2], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 4))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 4 + ) + ) + ) ), ( [2, 2, 3], - .singlepart(.init(kind: .basic(.init(topLevel: .audio, sub: .alternative)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))) + .singlepart( + .init( + kind: .basic(.init(topLevel: .audio, sub: .alternative)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ) ), ], #line diff --git a/Tests/NIOIMAPCoreTests/Grammar/Body/Field/BodyFieldsTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Body/Field/BodyFieldsTests.swift index 6b51f3ac1..c8d2755f3 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Body/Field/BodyFieldsTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Body/Field/BodyFieldsTests.swift @@ -23,7 +23,15 @@ class BodyFieldsTests: EncodeTestClass {} extension BodyFieldsTests { func testEncode() { let inputs: [(BodyStructure.Fields, String, UInt)] = [ - (.init(parameters: ["f1": "v1"], id: "fieldID", contentDescription: "desc", encoding: .base64, octetCount: 12), "(\"f1\" \"v1\") \"fieldID\" \"desc\" \"BASE64\" 12", #line), + ( + .init( + parameters: ["f1": "v1"], + id: "fieldID", + contentDescription: "desc", + encoding: .base64, + octetCount: 12 + ), "(\"f1\" \"v1\") \"fieldID\" \"desc\" \"BASE64\" 12", #line + ) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Body/Field/FieldLanguageLocationTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Body/Field/FieldLanguageLocationTests.swift index c8dc024c8..7cd6fc76d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Body/Field/FieldLanguageLocationTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Body/Field/FieldLanguageLocationTests.swift @@ -24,7 +24,10 @@ extension FieldLanguageLocationTests { func testEncode() { let inputs: [(BodyStructure.LanguageLocation, String, UInt)] = [ (.init(languages: ["language"], location: nil), " (\"language\")", #line), - (.init(languages: ["language"], location: .init(location: "location", extensions: [])), " (\"language\") \"location\"", #line), + ( + .init(languages: ["language"], location: .init(location: "location", extensions: [])), + " (\"language\") \"location\"", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodyMultipartTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodyMultipartTests.swift index b6ac5da50..e0b926a5a 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodyMultipartTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodyMultipartTests.swift @@ -24,24 +24,84 @@ extension BodyMultipartTests { func testEncode() { let inputs: [(BodyStructure.Multipart, String, UInt)] = [ ( - .init(parts: [ - .singlepart(BodyStructure.Singlepart(kind: .text(.init(mediaSubtype: "subtype", lineCount: 5)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 6), extension: nil)), - ], mediaSubtype: .mixed, extension: nil), + .init( + parts: [ + .singlepart( + BodyStructure.Singlepart( + kind: .text(.init(mediaSubtype: "subtype", lineCount: 5)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 6 + ), + extension: nil + ) + ) + ], + mediaSubtype: .mixed, + extension: nil + ), #"("TEXT" "SUBTYPE" NIL NIL NIL "BASE64" 6 5) "MIXED""#, #line ), ( - .init(parts: [ - .singlepart(BodyStructure.Singlepart(kind: .text(.init(mediaSubtype: "html", lineCount: 5)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 6), extension: nil)), - ], mediaSubtype: .alternative, extension: .init(parameters: [:], dispositionAndLanguage: nil)), + .init( + parts: [ + .singlepart( + BodyStructure.Singlepart( + kind: .text(.init(mediaSubtype: "html", lineCount: 5)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 6 + ), + extension: nil + ) + ) + ], + mediaSubtype: .alternative, + extension: .init(parameters: [:], dispositionAndLanguage: nil) + ), #"("TEXT" "HTML" NIL NIL NIL "BASE64" 6 5) "ALTERNATIVE" NIL"#, #line ), ( - .init(parts: [ - .singlepart(BodyStructure.Singlepart(kind: .text(.init(mediaSubtype: "html", lineCount: 5)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 6), extension: nil)), - .singlepart(BodyStructure.Singlepart(kind: .text(.init(mediaSubtype: "plain", lineCount: 6)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 7), extension: nil)), - ], mediaSubtype: .related, extension: nil), + .init( + parts: [ + .singlepart( + BodyStructure.Singlepart( + kind: .text(.init(mediaSubtype: "html", lineCount: 5)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 6 + ), + extension: nil + ) + ), + .singlepart( + BodyStructure.Singlepart( + kind: .text(.init(mediaSubtype: "plain", lineCount: 6)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 7 + ), + extension: nil + ) + ), + ], + mediaSubtype: .related, + extension: nil + ), #"("TEXT" "HTML" NIL NIL NIL "BASE64" 6 5)("TEXT" "PLAIN" NIL NIL NIL "BASE64" 7 6) "RELATED""#, #line ), @@ -53,7 +113,13 @@ extension BodyMultipartTests { let inputs: [(BodyStructure.Multipart.Extension, String, UInt)] = [ (.init(parameters: ["f": "v"], dispositionAndLanguage: nil), "(\"f\" \"v\")", #line), ( - .init(parameters: ["f1": "v1"], dispositionAndLanguage: .init(disposition: .init(kind: "string", parameters: ["f2": "v2"]), language: nil)), + .init( + parameters: ["f1": "v1"], + dispositionAndLanguage: .init( + disposition: .init(kind: "string", parameters: ["f2": "v2"]), + language: nil + ) + ), "(\"f1\" \"v1\") (\"string\" (\"f2\" \"v2\"))", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodySinglepartTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodySinglepartTests.swift index eb1f5afd4..204b95660 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodySinglepartTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Body/Type/BodySinglepartTests.swift @@ -55,11 +55,28 @@ extension BodySinglepartTests { kind: .message( .init( message: .rfc822, - envelope: .init(date: "date", subject: nil, from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), + envelope: .init( + date: "date", + subject: nil, + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), body: .singlepart( .init( kind: .text(.init(mediaSubtype: "subtype", lineCount: 5)), - fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .base64, octetCount: 6), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 6 + ), extension: nil ) ), @@ -86,7 +103,12 @@ extension BodySinglepartTests { let inputs: [(BodyStructure.Singlepart.Extension, String, UInt)] = [ (.init(digest: nil, dispositionAndLanguage: nil), "NIL", #line), (.init(digest: "md5", dispositionAndLanguage: nil), "\"md5\"", #line), - (.init(digest: "md5", dispositionAndLanguage: .init(disposition: .init(kind: "string", parameters: [:]), language: nil)), "\"md5\" (\"string\" NIL)", #line), + ( + .init( + digest: "md5", + dispositionAndLanguage: .init(disposition: .init(kind: "string", parameters: [:]), language: nil) + ), "\"md5\" (\"string\" NIL)", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/ByteBufferWriteLiteralTests.swift b/Tests/NIOIMAPCoreTests/Grammar/ByteBufferWriteLiteralTests.swift index 5fd784cf1..51e3499bb 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ByteBufferWriteLiteralTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ByteBufferWriteLiteralTests.swift @@ -39,9 +39,18 @@ extension ByteBufferWriteLiteralTests { ["{71}\r\n", "01234567890123456789012345678901234567890123456789012345678901234567890"], #line ), - (ByteBuffer(string: String(repeating: "a", count: 100)), .literalMinus, ["{100+}\r\n" + String(repeating: "a", count: 100)], #line), - (ByteBuffer(string: String(repeating: "a", count: 4096)), .literalMinus, ["{4096+}\r\n" + String(repeating: "a", count: 4096)], #line), - (ByteBuffer(string: String(repeating: "a", count: 4097)), .literalMinus, ["{4097}\r\n", String(repeating: "a", count: 4097)], #line), + ( + ByteBuffer(string: String(repeating: "a", count: 100)), .literalMinus, + ["{100+}\r\n" + String(repeating: "a", count: 100)], #line + ), + ( + ByteBuffer(string: String(repeating: "a", count: 4096)), .literalMinus, + ["{4096+}\r\n" + String(repeating: "a", count: 4096)], #line + ), + ( + ByteBuffer(string: String(repeating: "a", count: 4097)), .literalMinus, + ["{4097}\r\n", String(repeating: "a", count: 4097)], #line + ), ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeIMAPString($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/ByteRange/ByteRange+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ByteRange/ByteRange+Tests.swift index 1811e8054..d0ddc94b4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ByteRange/ByteRange+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ByteRange/ByteRange+Tests.swift @@ -24,12 +24,12 @@ extension ByteRange_Tests { func testEncode() { let inputs: [(ClosedRange, String, UInt)] = [ /// Encoded format is ``: - (0 ... 199, "<0.200>", #line), - (1 ... 2, "<1.2>", #line), - (10 ... 20, "<10.11>", #line), - (100 ... 199, "<100.100>", #line), - (400 ... 479, "<400.80>", #line), - (843 ... 1_369, "<843.527>", #line), + (0...199, "<0.200>", #line), + (1...2, "<1.2>", #line), + (10...20, "<10.11>", #line), + (100...199, "<100.100>", #line), + (400...479, "<400.80>", #line), + (843...1_369, "<843.527>", #line), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/ChangedSinceModifier+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ChangedSinceModifier+Tests.swift index 54a1b0ed4..42d8e7454 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ChangedSinceModifier+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ChangedSinceModifier+Tests.swift @@ -23,14 +23,14 @@ class ChangedSinceModifier_Tests: EncodeTestClass {} extension ChangedSinceModifier_Tests { func testEncode() { let inputs: [(ChangedSinceModifier, String, UInt)] = [ - (.init(modificationSequence: 3), "CHANGEDSINCE 3", #line), + (.init(modificationSequence: 3), "CHANGEDSINCE 3", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeChangedSinceModifier($0) }) } func testEncode_unchanged() { let inputs: [(UnchangedSinceModifier, String, UInt)] = [ - (.init(modificationSequence: 3), "UNCHANGEDSINCE 3", #line), + (.init(modificationSequence: 3), "UNCHANGEDSINCE 3", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeUnchangedSinceModifier($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandStream+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandStream+Tests.swift index 3206c1e1b..ab8ab6b88 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandStream+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandStream+Tests.swift @@ -30,12 +30,19 @@ extension CommandStream_Tests { #line ), ( - .append(.beginMessage(message: .init(options: .init(flagList: [.seen, .deleted], extensions: [:]), data: .init(byteCount: 3)))), + .append( + .beginMessage( + message: .init( + options: .init(flagList: [.seen, .deleted], extensions: [:]), + data: .init(byteCount: 3) + ) + ) + ), " (\\Seen \\Deleted) {3}\r\n", #line ), (.append(.messageBytes("123")), "123", #line), - (.append(.endMessage), "", #line), // dummy command, we don't expect anything + (.append(.endMessage), "", #line), // dummy command, we don't expect anything (.append(.finish), "\r\n", #line), (.tagged(.init(tag: "1", command: .noop)), "1 NOOP\r\n", #line), (.idleDone, "DONE\r\n", #line), @@ -125,28 +132,40 @@ extension CommandStream_Tests { } var encodedCommand = buffer.buffer.nextChunk() - XCTAssertEqual(String(buffer: encodedCommand.bytes), #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {42}\#r\#n"#) + XCTAssertEqual( + String(buffer: encodedCommand.bytes), + #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {42}\#r\#n"# + ) guard encodedCommand.waitForContinuation else { XCTFail("Should have had a continuation.") return } encodedCommand = buffer.buffer.nextChunk() - XCTAssertEqual(String(buffer: encodedCommand.bytes), #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME" URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1" TEXT {42}\#r\#n"#) + XCTAssertEqual( + String(buffer: encodedCommand.bytes), + #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME" URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1" TEXT {42}\#r\#n"# + ) guard encodedCommand.waitForContinuation else { XCTFail("Should have had a continuation.") return } encodedCommand = buffer.buffer.nextChunk() - XCTAssertEqual(String(buffer: encodedCommand.bytes), #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=30" TEXT {44}\#r\#n"#) + XCTAssertEqual( + String(buffer: encodedCommand.bytes), + #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=30" TEXT {44}\#r\#n"# + ) guard encodedCommand.waitForContinuation else { XCTFail("Should have had a continuation.") return } encodedCommand = buffer.buffer.nextChunk() - XCTAssertEqual(String(buffer: encodedCommand.bytes), #"\#r\#n--------------030308070208000400050907--\#r\#n)\#r\#n"#) + XCTAssertEqual( + String(buffer: encodedCommand.bytes), + #"\#r\#n--------------030308070208000400050907--\#r\#n)\#r\#n"# + ) XCTAssertFalse(encodedCommand.waitForContinuation, "Should not have additional continuations.") } @@ -181,10 +200,10 @@ extension CommandStream_Tests { let encodedCommand = buffer.buffer.nextChunk() XCTAssertEqual( String(buffer: encodedCommand.bytes), - #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {42+}\#r\#n"# + - #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME" URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1" TEXT {42+}\#r\#n"# + - #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=30" TEXT {44+}\#r\#n"# + - #"\#r\#n--------------030308070208000400050907--\#r\#n)\#r\#n"# + #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {42+}\#r\#n"# + + #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME" URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1" TEXT {42+}\#r\#n"# + + #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=30" TEXT {44+}\#r\#n"# + + #"\#r\#n--------------030308070208000400050907--\#r\#n)\#r\#n"# ) XCTAssertFalse(encodedCommand.waitForContinuation, "Should not have additional continuations.") } @@ -208,14 +227,20 @@ extension CommandStream_Tests { } var encodedCommand = buffer.buffer.nextChunk() - XCTAssertEqual(String(buffer: encodedCommand.bytes), #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {5}\#r\#n"#) + XCTAssertEqual( + String(buffer: encodedCommand.bytes), + #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {5}\#r\#n"# + ) guard encodedCommand.waitForContinuation else { XCTFail("Should have had a continuation.") return } encodedCommand = buffer.buffer.nextChunk() - XCTAssertEqual(String(buffer: encodedCommand.bytes), #"hello)\#r\#nA003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {5}\#r\#n"#) + XCTAssertEqual( + String(buffer: encodedCommand.bytes), + #"hello)\#r\#nA003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {5}\#r\#n"# + ) guard encodedCommand.waitForContinuation else { XCTFail("Should have had a continuation.") return @@ -235,7 +260,14 @@ extension CommandStream_Tests { #line ), ( - .append(.beginMessage(message: .init(options: .init(flagList: [.seen, .deleted], extensions: [:]), data: .init(byteCount: 3)))), + .append( + .beginMessage( + message: .init( + options: .init(flagList: [.seen, .deleted], extensions: [:]), + data: .init(byteCount: 3) + ) + ) + ), " (\\Seen \\Deleted) {3}\r\n", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandType+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandType+Tests.swift index c5824045c..108b84ba4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandType+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/CommandType/CommandType+Tests.swift @@ -25,56 +25,139 @@ extension CommandType_Tests { let inputs: [(Command, CommandEncodingOptions, [String], UInt)] = [ (.list(nil, reference: .init(""), .mailbox(""), []), CommandEncodingOptions(), ["LIST \"\" \"\""], #line), (.list(nil, reference: .init(""), .mailbox("")), CommandEncodingOptions(), ["LIST \"\" \"\""], #line), - (.list(nil, reference: .init(""), .mailbox("")), CommandEncodingOptions(), ["LIST \"\" \"\""], #line), // no ret-opts but has capability - (.list(nil, reference: .inbox, .mailbox(""), [.children]), CommandEncodingOptions(), ["LIST \"INBOX\" \"\" RETURN (CHILDREN)"], #line), // ret-opts with capability + // no ret-opts but has capability + (.list(nil, reference: .init(""), .mailbox("")), CommandEncodingOptions(), ["LIST \"\" \"\""], #line), + ( + .list(nil, reference: .inbox, .mailbox(""), [.children]), CommandEncodingOptions(), + ["LIST \"INBOX\" \"\" RETURN (CHILDREN)"], #line + ), // ret-opts with capability (.namespace, CommandEncodingOptions(), ["NAMESPACE"], #line), // MARK: Login - (.login(username: "username", password: "password"), CommandEncodingOptions(), [#"LOGIN "username" "password""#], #line), - (.login(username: "david evans", password: "great password"), CommandEncodingOptions(), [#"LOGIN "david evans" "great password""#], #line), - (.login(username: "\r\n", password: "\\\""), CommandEncodingOptions(), ["LOGIN {2}\r\n", "\r\n {2}\r\n", "\\\""], #line), + ( + .login(username: "username", password: "password"), CommandEncodingOptions(), + [#"LOGIN "username" "password""#], #line + ), + ( + .login(username: "david evans", password: "great password"), CommandEncodingOptions(), + [#"LOGIN "david evans" "great password""#], #line + ), + ( + .login(username: "\r\n", password: "\\\""), CommandEncodingOptions(), + ["LOGIN {2}\r\n", "\r\n {2}\r\n", "\\\""], #line + ), (.select(MailboxName("Events")), CommandEncodingOptions(), [#"SELECT "Events""#], #line), - (.select(.inbox, [.basic(.init(key: "test", value: nil))]), CommandEncodingOptions(), [#"SELECT "INBOX" (test)"#], #line), - (.select(.inbox, [.basic(.init(key: "test1", value: nil)), .basic(.init(key: "test2", value: nil))]), CommandEncodingOptions(), [#"SELECT "INBOX" (test1 test2)"#], #line), + ( + .select(.inbox, [.basic(.init(key: "test", value: nil))]), CommandEncodingOptions(), + [#"SELECT "INBOX" (test)"#], #line + ), + ( + .select(.inbox, [.basic(.init(key: "test1", value: nil)), .basic(.init(key: "test2", value: nil))]), + CommandEncodingOptions(), [#"SELECT "INBOX" (test1 test2)"#], #line + ), (.examine(MailboxName("Events")), CommandEncodingOptions(), [#"EXAMINE "Events""#], #line), - (.examine(.inbox, [.basic(.init(key: "test", value: nil))]), CommandEncodingOptions(), [#"EXAMINE "INBOX" (test)"#], #line), + ( + .examine(.inbox, [.basic(.init(key: "test", value: nil))]), CommandEncodingOptions(), + [#"EXAMINE "INBOX" (test)"#], #line + ), (.move(.set([1]), .inbox), CommandEncodingOptions(), ["MOVE 1 \"INBOX\""], #line), (.id([:]), CommandEncodingOptions(), ["ID NIL"], #line), - (.getMetadata(options: [], mailbox: .inbox, entries: ["a"]), CommandEncodingOptions(), ["GETMETADATA \"INBOX\" (\"a\")"], #line), - (.getMetadata(options: [.maxSize(123)], mailbox: .inbox, entries: ["a"]), CommandEncodingOptions(), ["GETMETADATA (MAXSIZE 123) \"INBOX\" (\"a\")"], #line), - (.setMetadata(mailbox: .inbox, entries: ["a": nil]), CommandEncodingOptions(), ["SETMETADATA \"INBOX\" (\"a\" NIL)"], #line), - - (.fetch(.set([1 ... 40]), [.uid, .internalDate], []), CommandEncodingOptions(), ["FETCH 1:40 (UID INTERNALDATE)"], #line), - (.fetch(.set([77]), [.uid, .bodySection(peek: true, .header, nil)], [.changedSince(.init(modificationSequence: 707484939116871680))]), CommandEncodingOptions(), ["FETCH 77 (UID BODY.PEEK[HEADER]) (CHANGEDSINCE 707484939116871680)"], #line), + ( + .getMetadata(options: [], mailbox: .inbox, entries: ["a"]), CommandEncodingOptions(), + ["GETMETADATA \"INBOX\" (\"a\")"], #line + ), + ( + .getMetadata(options: [.maxSize(123)], mailbox: .inbox, entries: ["a"]), CommandEncodingOptions(), + ["GETMETADATA (MAXSIZE 123) \"INBOX\" (\"a\")"], #line + ), + ( + .setMetadata(mailbox: .inbox, entries: ["a": nil]), CommandEncodingOptions(), + ["SETMETADATA \"INBOX\" (\"a\" NIL)"], #line + ), + + ( + .fetch(.set([1...40]), [.uid, .internalDate], []), CommandEncodingOptions(), + ["FETCH 1:40 (UID INTERNALDATE)"], #line + ), + ( + .fetch( + .set([77]), + [.uid, .bodySection(peek: true, .header, nil)], + [.changedSince(.init(modificationSequence: 707_484_939_116_871_680))] + ), CommandEncodingOptions(), ["FETCH 77 (UID BODY.PEEK[HEADER]) (CHANGEDSINCE 707484939116871680)"], + #line + ), (.resetKey(mailbox: nil, mechanisms: []), CommandEncodingOptions(), ["RESETKEY"], #line), - (.resetKey(mailbox: nil, mechanisms: [.internal]), CommandEncodingOptions(), ["RESETKEY"], #line), // no mailbox, so no mechanisms written - (.resetKey(mailbox: .inbox, mechanisms: [.internal]), CommandEncodingOptions(), ["RESETKEY \"INBOX\" INTERNAL"], #line), - (.resetKey(mailbox: .inbox, mechanisms: [.internal, .init("test")]), CommandEncodingOptions(), ["RESETKEY \"INBOX\" INTERNAL test"], #line), - - (.generateAuthorizedURL([.init(urlRump: "rump1", mechanism: .internal)]), CommandEncodingOptions(), ["GENURLAUTH \"rump1\" INTERNAL"], #line), - (.generateAuthorizedURL([.init(urlRump: "rump2", mechanism: .internal), .init(urlRump: "rump3", mechanism: .init("test"))]), CommandEncodingOptions(), ["GENURLAUTH \"rump2\" INTERNAL \"rump3\" test"], #line), + // no mailbox, so no mechanisms written + (.resetKey(mailbox: nil, mechanisms: [.internal]), CommandEncodingOptions(), ["RESETKEY"], #line), + ( + .resetKey(mailbox: .inbox, mechanisms: [.internal]), CommandEncodingOptions(), + ["RESETKEY \"INBOX\" INTERNAL"], #line + ), + ( + .resetKey(mailbox: .inbox, mechanisms: [.internal, .init("test")]), CommandEncodingOptions(), + ["RESETKEY \"INBOX\" INTERNAL test"], #line + ), + + ( + .generateAuthorizedURL([.init(urlRump: "rump1", mechanism: .internal)]), CommandEncodingOptions(), + ["GENURLAUTH \"rump1\" INTERNAL"], #line + ), + ( + .generateAuthorizedURL([ + .init(urlRump: "rump2", mechanism: .internal), .init(urlRump: "rump3", mechanism: .init("test")), + ]), CommandEncodingOptions(), ["GENURLAUTH \"rump2\" INTERNAL \"rump3\" test"], #line + ), (.urlFetch(["test"]), CommandEncodingOptions(), ["URLFETCH test"], #line), (.urlFetch(["test1", "test2"]), CommandEncodingOptions(), ["URLFETCH test1 test2"], #line), (.create(.inbox, []), CommandEncodingOptions(), ["CREATE \"INBOX\""], #line), - (.create(.inbox, [.attributes([.archive, .drafts, .flagged])]), CommandEncodingOptions(), ["CREATE \"INBOX\" (USE (\\Archive \\Drafts \\Flagged))"], #line), + ( + .create(.inbox, [.attributes([.archive, .drafts, .flagged])]), CommandEncodingOptions(), + ["CREATE \"INBOX\" (USE (\\Archive \\Drafts \\Flagged))"], #line + ), (.compress(.deflate), CommandEncodingOptions(), ["COMPRESS DEFLATE"], #line), // Custom (.custom(name: "FOOBAR", payloads: []), CommandEncodingOptions(), ["FOOBAR"], #line), - (.custom(name: "FOOBAR", payloads: [.verbatim(.init(string: "A B C"))]), CommandEncodingOptions(), ["FOOBAR A B C"], #line), - (.custom(name: "FOOBAR", payloads: [.verbatim(.init(string: "A")), .verbatim(.init(string: "B"))]), CommandEncodingOptions(), ["FOOBAR AB"], #line), - (.custom(name: "FOOBAR", payloads: [.literal(.init(string: "A"))]), CommandEncodingOptions(), [#"FOOBAR "A""#], #line), - (.custom(name: "FOOBAR", payloads: [.literal(.init(string: "A B C"))]), CommandEncodingOptions(), [#"FOOBAR "A B C""#], #line), - (.custom(name: "FOOBAR", payloads: [.literal(.init(string: "A")), .literal(.init(string: "B"))]), CommandEncodingOptions(), [#"FOOBAR "A""B""#], #line), - (.custom(name: "FOOBAR", payloads: [.literal(.init(string: "A")), .verbatim(.init(string: " ")), .literal(.init(string: "B"))]), CommandEncodingOptions(), [#"FOOBAR "A" "B""#], #line), - (.custom(name: "FOOBAR", payloads: [.literal(.init(string: "¶"))]), CommandEncodingOptions(), ["FOOBAR {2}\r\n", "¶"], #line), + ( + .custom(name: "FOOBAR", payloads: [.verbatim(.init(string: "A B C"))]), CommandEncodingOptions(), + ["FOOBAR A B C"], #line + ), + ( + .custom(name: "FOOBAR", payloads: [.verbatim(.init(string: "A")), .verbatim(.init(string: "B"))]), + CommandEncodingOptions(), ["FOOBAR AB"], #line + ), + ( + .custom(name: "FOOBAR", payloads: [.literal(.init(string: "A"))]), CommandEncodingOptions(), + [#"FOOBAR "A""#], #line + ), + ( + .custom(name: "FOOBAR", payloads: [.literal(.init(string: "A B C"))]), CommandEncodingOptions(), + [#"FOOBAR "A B C""#], #line + ), + ( + .custom(name: "FOOBAR", payloads: [.literal(.init(string: "A")), .literal(.init(string: "B"))]), + CommandEncodingOptions(), [#"FOOBAR "A""B""#], #line + ), + ( + .custom( + name: "FOOBAR", + payloads: [ + .literal(.init(string: "A")), .verbatim(.init(string: " ")), .literal(.init(string: "B")), + ] + ), CommandEncodingOptions(), [#"FOOBAR "A" "B""#], #line + ), + ( + .custom(name: "FOOBAR", payloads: [.literal(.init(string: "¶"))]), CommandEncodingOptions(), + ["FOOBAR {2}\r\n", "¶"], #line + ), ] for (test, options, expectedStrings, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/DateTests/InternalDateTests.swift b/Tests/NIOIMAPCoreTests/Grammar/DateTests/InternalDateTests.swift index 4d2224add..ed0f42991 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/DateTests/InternalDateTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/DateTests/InternalDateTests.swift @@ -21,7 +21,15 @@ class InternalDateTests: XCTestCase {} extension InternalDateTests { func testInternalDateInit_1() { - let components = ServerMessageDate.Components(year: 1994, month: 6, day: 25, hour: 1, minute: 2, second: 3, timeZoneMinutes: 620)! + let components = ServerMessageDate.Components( + year: 1994, + month: 6, + day: 25, + hour: 1, + minute: 2, + second: 3, + timeZoneMinutes: 620 + )! let date = ServerMessageDate(components) let c = date.components XCTAssertEqual(c.year, 1994) @@ -34,7 +42,15 @@ extension InternalDateTests { } func testInternalDateInit_2() { - let components = ServerMessageDate.Components(year: 1900, month: 1, day: 1, hour: 0, minute: 0, second: 0, timeZoneMinutes: -959)! + let components = ServerMessageDate.Components( + year: 1900, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + timeZoneMinutes: -959 + )! let date = ServerMessageDate(components) let c = date.components XCTAssertEqual(c.year, 1900) @@ -47,7 +63,15 @@ extension InternalDateTests { } func testInternalDateInit_3() { - let components = ServerMessageDate.Components(year: 2579, month: 12, day: 31, hour: 23, minute: 59, second: 59, timeZoneMinutes: 959)! + let components = ServerMessageDate.Components( + year: 2579, + month: 12, + day: 31, + hour: 23, + minute: 59, + second: 59, + timeZoneMinutes: 959 + )! let date = ServerMessageDate(components) let c = date.components XCTAssertEqual(c.year, 2579) diff --git a/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchOptions+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchOptions+Tests.swift index 7798144af..195aab438 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchOptions+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchOptions+Tests.swift @@ -54,31 +54,39 @@ extension ExtendedSearchOptions_Tests { #line ), ( - ExtendedSearchOptions(key: .all, - charset: "Alien", - sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])), + ExtendedSearchOptions( + key: .all, + charset: "Alien", + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ), " IN (inboxes) CHARSET Alien ALL", #line ), ( - ExtendedSearchOptions(key: .all, - returnOptions: [.min], - sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])), + ExtendedSearchOptions( + key: .all, + returnOptions: [.min], + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ), " IN (inboxes) RETURN (MIN) ALL", #line ), ( - ExtendedSearchOptions(key: .all, - charset: "Alien", - returnOptions: [.min]), + ExtendedSearchOptions( + key: .all, + charset: "Alien", + returnOptions: [.min] + ), " RETURN (MIN) CHARSET Alien ALL", #line ), ( - ExtendedSearchOptions(key: .all, - charset: "Alien", - returnOptions: [.min], - sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])), + ExtendedSearchOptions( + key: .all, + charset: "Alien", + returnOptions: [.min], + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ), " IN (inboxes) RETURN (MIN) CHARSET Alien ALL", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchSourceOptions+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchSourceOptions+Tests.swift index f473e4abe..9d10009ee 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchSourceOptions+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ESearch/ESearchSourceOptions+Tests.swift @@ -29,14 +29,18 @@ extension ExtendedSearchSourceOptions_Tests { #line ), ( - ExtendedSearchSourceOptions(sourceMailbox: [.inboxes], - scopeOptions: ExtendedSearchScopeOptions(["test": nil]))!, + ExtendedSearchSourceOptions( + sourceMailbox: [.inboxes], + scopeOptions: ExtendedSearchScopeOptions(["test": nil]) + )!, "IN (inboxes (test))", #line ), ( - ExtendedSearchSourceOptions(sourceMailbox: [.inboxes, .personal], - scopeOptions: ExtendedSearchScopeOptions(["test": nil]))!, + ExtendedSearchSourceOptions( + sourceMailbox: [.inboxes, .personal], + scopeOptions: ExtendedSearchScopeOptions(["test": nil]) + )!, "IN (inboxes personal (test))", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/EmailAddressTests.swift b/Tests/NIOIMAPCoreTests/Grammar/EmailAddressTests.swift index 402a3e515..0c275914b 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EmailAddressTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EmailAddressTests.swift @@ -47,7 +47,12 @@ extension EmailAddressTests { } func testNoneNil() { - let address = EmailAddress(personName: "somename", sourceRoot: "someadl", mailbox: "somemailbox", host: "someaddress") + let address = EmailAddress( + personName: "somename", + sourceRoot: "someadl", + mailbox: "somemailbox", + host: "someaddress" + ) let expected = "(\"somename\" \"someadl\" \"somemailbox\" \"someaddress\")" let size = self.testBuffer.writeEmailAddress(address) XCTAssertEqual(size, expected.utf8.count) diff --git a/Tests/NIOIMAPCoreTests/Grammar/EncodeTestClass.swift b/Tests/NIOIMAPCoreTests/Grammar/EncodeTestClass.swift index 9e927259b..9e2f8fe2b 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EncodeTestClass.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EncodeTestClass.swift @@ -37,7 +37,11 @@ class EncodeTestClass: XCTestCase { } override func setUp() { - self.testBuffer = .serverEncodeBuffer(buffer: ByteBufferAllocator().buffer(capacity: 128), capabilities: [], loggingMode: false) + self.testBuffer = .serverEncodeBuffer( + buffer: ByteBufferAllocator().buffer(capacity: 128), + capabilities: [], + loggingMode: false + ) } override func tearDown() { @@ -45,12 +49,24 @@ class EncodeTestClass: XCTestCase { } func iterateInputs(inputs: [(T, String, UInt)], encoder: (T) throws -> Int, file: StaticString = #filePath) { - self.iterateInputs(inputs: inputs.map { ($0.0, ResponseEncodingOptions(), $0.1, $0.2) }, encoder: encoder, file: (file)) + self.iterateInputs( + inputs: inputs.map { ($0.0, ResponseEncodingOptions(), $0.1, $0.2) }, + encoder: encoder, + file: (file) + ) } - func iterateInputs(inputs: [(T, CommandEncodingOptions, [String], UInt)], encoder: (T) throws -> Int, file: StaticString = #filePath) { + func iterateInputs( + inputs: [(T, CommandEncodingOptions, [String], UInt)], + encoder: (T) throws -> Int, + file: StaticString = #filePath + ) { for (test, options, expectedStrings, line) in inputs { - self.testBuffer = EncodeBuffer.clientEncodeBuffer(buffer: ByteBufferAllocator().buffer(capacity: 128), options: options, loggingMode: false) + self.testBuffer = EncodeBuffer.clientEncodeBuffer( + buffer: ByteBufferAllocator().buffer(capacity: 128), + options: options, + loggingMode: false + ) do { let size = try encoder(test) XCTAssertEqual(size, expectedStrings.reduce(0) { $0 + $1.utf8.count }, file: (file), line: line) @@ -61,7 +77,11 @@ class EncodeTestClass: XCTestCase { } } - func iterateCommandInputs(inputs: [(T, CommandEncodingOptions, [String], UInt)], encoder: (T) throws -> Int, file: StaticString = #filePath) { + func iterateCommandInputs( + inputs: [(T, CommandEncodingOptions, [String], UInt)], + encoder: (T) throws -> Int, + file: StaticString = #filePath + ) { for (test, options, expectedStrings, line) in inputs { do { self.testBuffer.mode = .client(options: options) @@ -74,9 +94,17 @@ class EncodeTestClass: XCTestCase { } } - func iterateInputs(inputs: [(T, ResponseEncodingOptions, String, UInt)], encoder: (T) throws -> Int, file: StaticString = #filePath) { + func iterateInputs( + inputs: [(T, ResponseEncodingOptions, String, UInt)], + encoder: (T) throws -> Int, + file: StaticString = #filePath + ) { for (test, options, expectedString, line) in inputs { - self.testBuffer = EncodeBuffer.serverEncodeBuffer(buffer: ByteBufferAllocator().buffer(capacity: 128), options: options, loggingMode: false) + self.testBuffer = EncodeBuffer.serverEncodeBuffer( + buffer: ByteBufferAllocator().buffer(capacity: 128), + options: options, + loggingMode: false + ) do { let size = try encoder(test) XCTAssertEqual(size, expectedString.utf8.count, file: (file), line: line) diff --git a/Tests/NIOIMAPCoreTests/Grammar/EncodedAuthenticationType+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/EncodedAuthenticationType+Tests.swift index beed6a971..3fe15abd4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EncodedAuthenticationType+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EncodedAuthenticationType+Tests.swift @@ -23,7 +23,7 @@ class EncodedAuthenticationType_Tests: EncodeTestClass {} extension EncodedAuthenticationType_Tests { func testEncode() { let inputs: [(EncodedAuthenticationType, String, UInt)] = [ - (.init(authenticationType: "hello"), "hello", #line), + (.init(authenticationType: "hello"), "hello", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEncodedAuthenticationType($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/EncodedMailbox+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/EncodedMailbox+Tests.swift index f4ab29335..839dd3dec 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EncodedMailbox+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EncodedMailbox+Tests.swift @@ -23,7 +23,7 @@ class EncodedMailbox_Tests: EncodeTestClass {} extension EncodedMailbox_Tests { func testEncode() { let inputs: [(EncodedMailbox, String, UInt)] = [ - (.init(mailbox: "hello"), "hello", #line), + (.init(mailbox: "hello"), "hello", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEncodedMailbox($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/EncodedSearch+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/EncodedSearch+Tests.swift index a3e1e56a4..b3ee25a3f 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EncodedSearch+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EncodedSearch+Tests.swift @@ -23,7 +23,7 @@ class EncodedSearch_Tests: EncodeTestClass {} extension EncodedSearch_Tests { func testEncode() { let inputs: [(EncodedSearch, String, UInt)] = [ - (.init(query: "hello"), "hello", #line), + (.init(query: "hello"), "hello", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEncodedSearch($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/EncodedSection+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/EncodedSection+Tests.swift index e6c45e9d5..0f8d3247c 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EncodedSection+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EncodedSection+Tests.swift @@ -23,7 +23,7 @@ class EncodedSection_Tests: EncodeTestClass {} extension EncodedSection_Tests { func testEncode() { let inputs: [(EncodedSection, String, UInt)] = [ - (.init(section: "hello"), "hello", #line), + (.init(section: "hello"), "hello", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEncodedSection($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/EncodedUrlAuth+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/EncodedUrlAuth+Tests.swift index aaf28f81c..a8012f6e9 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EncodedUrlAuth+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EncodedUrlAuth+Tests.swift @@ -23,7 +23,7 @@ class EncodedURLAuth_Tests: EncodeTestClass {} extension EncodedURLAuth_Tests { func testEncode() { let inputs: [(EncodedAuthenticatedURL, String, UInt)] = [ - (.init(data: "1F"), "1F", #line), + (.init(data: "1F"), "1F", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEncodedAuthenticationURL($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/EncodedUser+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/EncodedUser+Tests.swift index ba4b0677f..77424218a 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EncodedUser+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EncodedUser+Tests.swift @@ -23,7 +23,7 @@ class EncodedUser_Tests: EncodeTestClass {} extension EncodedUser_Tests { func testEncode() { let inputs: [(EncodedUser, String, UInt)] = [ - (.init(data: "hello"), "hello", #line), + (.init(data: "hello"), "hello", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEncodedUser($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Entry+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Entry+Tests.swift index 331ee1d1f..983252af2 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Entry+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Entry+Tests.swift @@ -24,7 +24,7 @@ class Entry_Tests: EncodeTestClass {} extension Entry_Tests { func testEncode() { let inputs: [(KeyValue, String, UInt)] = [ - (.init(key: "name", value: .init("value")), "\"name\" ~{5}\r\nvalue", #line), + (.init(key: "name", value: .init("value")), "\"name\" ~{5}\r\nvalue", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEntry($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Entry/EntryFlagName+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Entry/EntryFlagName+Tests.swift index 4f83e44b1..2f82d8370 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Entry/EntryFlagName+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Entry/EntryFlagName+Tests.swift @@ -19,7 +19,7 @@ import XCTest class EntryFlagName_Tests: EncodeTestClass { func testEncoding() { let inputs: [(EntryFlagName, String, UInt)] = [ - (.init(flag: .answered), "\"/flags/\\\\answered\"", #line), // mad, but absolutely correct + (.init(flag: .answered), "\"/flags/\\\\answered\"", #line) // mad, but absolutely correct ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeEntryFlagName($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/EnvelopeTests.swift b/Tests/NIOIMAPCoreTests/Grammar/EnvelopeTests.swift index 825ae726a..4244ec3ec 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/EnvelopeTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/EnvelopeTests.swift @@ -43,13 +43,38 @@ extension EnvelopeTests { Envelope( date: "01-02-03", subject: "subject", - from: [.singleAddress(.init(personName: "name1", sourceRoot: "adl1", mailbox: "mailbox1", host: "host1"))], - sender: [.singleAddress(.init(personName: "name2", sourceRoot: "adl2", mailbox: "mailbox2", host: "host2"))], - reply: [.singleAddress(.init(personName: "name3", sourceRoot: "adl3", mailbox: "mailbox3", host: "host3"))], - to: [.singleAddress(.init(personName: "name4", sourceRoot: "adl4", mailbox: "mailbox4", host: "host4"))], - cc: [.singleAddress(.init(personName: "name5", sourceRoot: "adl5", mailbox: "mailbox5", host: "host5"))], - bcc: [.singleAddress(.init(personName: "name6", sourceRoot: "adl6", mailbox: "mailbox6", host: "host6"))], - inReplyTo: nil, messageID: "1" + from: [ + .singleAddress( + .init(personName: "name1", sourceRoot: "adl1", mailbox: "mailbox1", host: "host1") + ) + ], + sender: [ + .singleAddress( + .init(personName: "name2", sourceRoot: "adl2", mailbox: "mailbox2", host: "host2") + ) + ], + reply: [ + .singleAddress( + .init(personName: "name3", sourceRoot: "adl3", mailbox: "mailbox3", host: "host3") + ) + ], + to: [ + .singleAddress( + .init(personName: "name4", sourceRoot: "adl4", mailbox: "mailbox4", host: "host4") + ) + ], + cc: [ + .singleAddress( + .init(personName: "name5", sourceRoot: "adl5", mailbox: "mailbox5", host: "host5") + ) + ], + bcc: [ + .singleAddress( + .init(personName: "name6", sourceRoot: "adl6", mailbox: "mailbox6", host: "host6") + ) + ], + inReplyTo: nil, + messageID: "1" ), "(\"01-02-03\" \"subject\" ((\"name1\" \"adl1\" \"mailbox1\" \"host1\")) ((\"name2\" \"adl2\" \"mailbox2\" \"host2\")) ((\"name3\" \"adl3\" \"mailbox3\" \"host3\")) ((\"name4\" \"adl4\" \"mailbox4\" \"host4\")) ((\"name5\" \"adl5\" \"mailbox5\" \"host5\")) ((\"name6\" \"adl6\" \"mailbox6\" \"host6\")) NIL \"1\")", #line diff --git a/Tests/NIOIMAPCoreTests/Grammar/Expire+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Expire+Tests.swift index 3549da942..81667d136 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Expire+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Expire+Tests.swift @@ -32,7 +32,7 @@ extension Expire_Tests { ), ";EXPIRE=1234-12-20T12:34:56.123456", #line - ), + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeExpire($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/FetchAttributeTests.swift b/Tests/NIOIMAPCoreTests/Grammar/FetchAttributeTests.swift index 9008ac5c5..4931f74ba 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/FetchAttributeTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/FetchAttributeTests.swift @@ -47,7 +47,10 @@ extension FetchAttributeTests { (.preview(lazy: false), .rfc3501, "PREVIEW", #line), (.preview(lazy: true), .rfc3501, "PREVIEW (LAZY)", #line), ] - self.iterateInputs(inputs: inputs.map { ($0, $1, [$2], $3) }, encoder: { self.testBuffer.writeFetchAttribute($0) }) + self.iterateInputs( + inputs: inputs.map { ($0, $1, [$2], $3) }, + encoder: { self.testBuffer.writeFetchAttribute($0) } + ) } // Some tests may have empty fields as debug strings should use @@ -66,7 +69,10 @@ extension FetchAttributeTests { (.bodyStructure(extensions: true), "BODYSTRUCTURE", #line), (.bodySection(peek: false, .init(kind: .header), nil), "BODY[HEADER]", #line), (.bodySection(peek: false, .init(kind: .header), nil), "BODY[HEADER]", #line), - (.bodySection(peek: true, .init(kind: .headerFields(["message-id", "in-reply-to"])), nil), #"BODY.PEEK[HEADER.FIELDS ("message-id" "in-reply-to")]"#, #line), + ( + .bodySection(peek: true, .init(kind: .headerFields(["message-id", "in-reply-to"])), nil), + #"BODY.PEEK[HEADER.FIELDS ("message-id" "in-reply-to")]"#, #line + ), (.binarySize(section: [1]), "BINARY.SIZE[1]", #line), (.binary(peek: true, section: [1, 2, 3], partial: nil), "BINARY.PEEK[1.2.3]", #line), (.binary(peek: false, section: [3, 4, 5], partial: nil), "BINARY[3.4.5]", #line), @@ -91,14 +97,29 @@ extension FetchAttributeTests { ([.internalDate, .rfc822Size, .flags], .rfc3501, "FAST", #line), ([.flags, .internalDate, .rfc822Size, .envelope], .rfc3501, "ALL", #line), ([.rfc822Size, .flags, .envelope, .internalDate], .rfc3501, "ALL", #line), - ([.flags, .internalDate, .rfc822Size, .envelope, .bodyStructure(extensions: false)], .rfc3501, "FULL", #line), - ([.flags, .bodyStructure(extensions: false), .rfc822Size, .internalDate, .envelope], .rfc3501, "FULL", #line), - ([.flags, .bodyStructure(extensions: true), .rfc822Size, .internalDate, .envelope], .rfc3501, "(FLAGS BODYSTRUCTURE RFC822.SIZE INTERNALDATE ENVELOPE)", #line), - ([.flags, .bodyStructure(extensions: false), .rfc822Size, .internalDate, .envelope, .uid], .rfc3501, "(FLAGS BODY RFC822.SIZE INTERNALDATE ENVELOPE UID)", #line), + ( + [.flags, .internalDate, .rfc822Size, .envelope, .bodyStructure(extensions: false)], .rfc3501, "FULL", + #line + ), + ( + [.flags, .bodyStructure(extensions: false), .rfc822Size, .internalDate, .envelope], .rfc3501, "FULL", + #line + ), + ( + [.flags, .bodyStructure(extensions: true), .rfc822Size, .internalDate, .envelope], .rfc3501, + "(FLAGS BODYSTRUCTURE RFC822.SIZE INTERNALDATE ENVELOPE)", #line + ), + ( + [.flags, .bodyStructure(extensions: false), .rfc822Size, .internalDate, .envelope, .uid], .rfc3501, + "(FLAGS BODY RFC822.SIZE INTERNALDATE ENVELOPE UID)", #line + ), ([.gmailLabels, .gmailMessageID, .gmailThreadID], .rfc3501, "(X-GM-LABELS X-GM-MSGID X-GM-THRID)", #line), ([.preview(lazy: false)], .rfc3501, "(PREVIEW)", #line), ([.preview(lazy: true)], .rfc3501, "(PREVIEW (LAZY))", #line), ] - self.iterateInputs(inputs: inputs.map { ($0, $1, [$2], $3) }, encoder: { self.testBuffer.writeFetchAttributeList($0) }) + self.iterateInputs( + inputs: inputs.map { ($0, $1, [$2], $3) }, + encoder: { self.testBuffer.writeFetchAttributeList($0) } + ) } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/FetchModifiedResponse+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/FetchModifiedResponse+Tests.swift index 6f5d68528..a5e99b99e 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/FetchModifiedResponse+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/FetchModifiedResponse+Tests.swift @@ -23,7 +23,7 @@ class FetchModifiedResponse_Tests: EncodeTestClass {} extension FetchModifiedResponse_Tests { func testEncode() { let inputs: [(FetchModificationResponse, String, UInt)] = [ - (.init(modifierSequenceValue: 3), "MODSEQ (3)", #line), + (.init(modifierSequenceValue: 3), "MODSEQ (3)", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeFetchModificationResponse($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/FetchModifier+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/FetchModifier+Tests.swift index 6d51b4f9e..ad90b275f 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/FetchModifier+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/FetchModifier+Tests.swift @@ -24,7 +24,7 @@ extension FetchModifier_Tests { func testEncode() { let inputs: [(FetchModifier, String, UInt)] = [ (.changedSince(.init(modificationSequence: 4)), "CHANGEDSINCE 4", #line), - (.partial(.last(735 ... 88_032)), "PARTIAL -735:-88032", #line), + (.partial(.last(735...88_032)), "PARTIAL -735:-88032", #line), (.other(.init(key: "test", value: nil)), "test", #line), (.other(.init(key: "test", value: .sequence(.set([4])))), "test 4", #line), ] @@ -33,9 +33,15 @@ extension FetchModifier_Tests { func testEncodeArray() { let inputs: [([FetchModifier], String, UInt)] = [ - ([.partial(.last(1 ... 30)), .changedSince(.init(modificationSequence: 3665089505007763456))], " (PARTIAL -1:-30 CHANGEDSINCE 3665089505007763456)", #line), + ( + [.partial(.last(1...30)), .changedSince(.init(modificationSequence: 3_665_089_505_007_763_456))], + " (PARTIAL -1:-30 CHANGEDSINCE 3665089505007763456)", #line + ), ([.changedSince(.init(modificationSequence: 98305))], " (CHANGEDSINCE 98305)", #line), - ([.other(.init(key: "test", value: nil)), .other(.init(key: "test", value: .sequence(.set([4]))))], " (test test 4)", #line), + ( + [.other(.init(key: "test", value: nil)), .other(.init(key: "test", value: .sequence(.set([4]))))], + " (test test 4)", #line + ), ([], "", #line), ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeFetchModifiers($0) }) diff --git a/Tests/NIOIMAPCoreTests/Grammar/FullDateTime+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/FullDateTime+Tests.swift index 191ccf8b0..b4f1bb2dc 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/FullDateTime+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/FullDateTime+Tests.swift @@ -23,14 +23,17 @@ class FullDateTime_Tests: EncodeTestClass {} extension FullDateTime_Tests { func testEncode_fullDateTime() { let inputs: [(FullDateTime, String, UInt)] = [ - (.init(date: .init(year: 1, month: 2, day: 3), time: .init(hour: 4, minute: 5, second: 6)), "0001-02-03T04:05:06", #line), + ( + .init(date: .init(year: 1, month: 2, day: 3), time: .init(hour: 4, minute: 5, second: 6)), + "0001-02-03T04:05:06", #line + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeFullDateTime($0) }) } func testEncode_fullDate() { let inputs: [(FullDate, String, UInt)] = [ - (.init(year: 1, month: 2, day: 3), "0001-02-03", #line), + (.init(year: 1, month: 2, day: 3), "0001-02-03", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeFullDate($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/IAbsolutePath+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IAbsolutePath+Tests.swift index 316f8fb19..868c9137c 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IAbsolutePath+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IAbsolutePath+Tests.swift @@ -23,7 +23,10 @@ class AbsoluteMessagePath_Tests: EncodeTestClass {} extension AbsoluteMessagePath_Tests { func testEncode() { let inputs: [(AbsoluteMessagePath, String, UInt)] = [ - (.init(command: .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"))))), "/test", #line), + ( + .init(command: .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"))))), + "/test", #line + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeAbsoluteMessagePath($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/ICommand+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ICommand+Tests.swift index 1b5d7040e..8aded6748 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ICommand+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ICommand+Tests.swift @@ -24,8 +24,24 @@ extension URLCommand_Tests { func testEncode() { let inputs: [(URLCommand, String, UInt)] = [ (.messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test")))), "test", #line), - (.fetch(path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), authenticatedURL: nil), "test/;UID=123", #line), - (.fetch(path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), authenticatedURL: .init(authenticatedURL: .init(access: .anonymous), verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "01234567890123456789012345678901")))), "test/;UID=123;URLAUTH=anonymous:INTERNAL:01234567890123456789012345678901", #line), + ( + .fetch( + path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), + authenticatedURL: nil + ), "test/;UID=123", #line + ), + ( + .fetch( + path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), + authenticatedURL: .init( + authenticatedURL: .init(access: .anonymous), + verifier: .init( + urlAuthMechanism: .internal, + encodedAuthenticationURL: .init(data: "01234567890123456789012345678901") + ) + ) + ), "test/;UID=123;URLAUTH=anonymous:INTERNAL:01234567890123456789012345678901", #line + ), ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeURLCommand($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/ID/ID+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ID/ID+Tests.swift index e4d9c3452..e1580e3c0 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ID/ID+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ID/ID+Tests.swift @@ -37,9 +37,23 @@ extension ID_Tests { (#"ID NIL"#, "\r", .id([:]), #line), (#"ID ("key" NIL)"#, "\r", .id(["key": nil]), #line), (#"ID ("name" "Imap" "version" "1.5")"#, "\r", .id(["name": "Imap", "version": "1.5"]), #line), - (#"ID ("name" "Imap" "version" "1.5" "os" "centos" "os-version" "5.5" "support-url" "mailto:admin@xgen.in")"#, "\r", .id(["name": "Imap", "version": "1.5", "os": "centos", "os-version": "5.5", "support-url": "mailto:admin@xgen.in"]), #line), + ( + #"ID ("name" "Imap" "version" "1.5" "os" "centos" "os-version" "5.5" "support-url" "mailto:admin@xgen.in")"#, + "\r", + .id([ + "name": "Imap", "version": "1.5", "os": "centos", "os-version": "5.5", + "support-url": "mailto:admin@xgen.in", + ]), #line + ), // datamail.in appends a `+` to the ID response: - (#"ID ("name" "Imap" "version" "1.5" "os" "centos" "os-version" "5.5" "support-url" "mailto:admin@xgen.in")+"#, "\r", .id(["name": "Imap", "version": "1.5", "os": "centos", "os-version": "5.5", "support-url": "mailto:admin@xgen.in"]), #line), + ( + #"ID ("name" "Imap" "version" "1.5" "os" "centos" "os-version" "5.5" "support-url" "mailto:admin@xgen.in")+"#, + "\r", + .id([ + "name": "Imap", "version": "1.5", "os": "centos", "os-version": "5.5", + "support-url": "mailto:admin@xgen.in", + ]), #line + ), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -50,17 +64,23 @@ extension ID_Tests { extension ID_Tests { func testThatAnIDResponseDoesNotGetRedactedForLogging() { let id = Response.untagged(ResponsePayload.id(["name": "A"])) - XCTAssertEqual("\(Response.descriptionWithoutPII([id]))", #""" - * ID ("name" "A")\#r + XCTAssertEqual( + "\(Response.descriptionWithoutPII([id]))", + #""" + * ID ("name" "A")\#r - """#) + """# + ) } func testThatAnIDCommandDoesNotGetRedactedForLogging() { let part = CommandStreamPart.tagged(TaggedCommand(tag: "A1", command: .id(["name": "A"]))) - XCTAssertEqual("\(CommandStreamPart.descriptionWithoutPII([part]))", #""" - A1 ID ("name" "A")\#r + XCTAssertEqual( + "\(CommandStreamPart.descriptionWithoutPII([part]))", + #""" + A1 ID ("name" "A")\#r - """#) + """# + ) } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/IMAPRangeTests.swift b/Tests/NIOIMAPCoreTests/Grammar/IMAPRangeTests.swift index 891dba155..f56b6209c 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IMAPRangeTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IMAPRangeTests.swift @@ -30,7 +30,7 @@ extension IMAPRangeTests { func testIMAPEncoded_range() { let expected = "2:4" - let size = self.testBuffer.writeSequenceRange(MessageIdentifierRange(2 ... 4)) + let size = self.testBuffer.writeSequenceRange(MessageIdentifierRange(2...4)) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } @@ -52,7 +52,7 @@ extension IMAPRangeTests { } func testRange_closed() { - let sut = MessageIdentifierRange(3 ... 4) + let sut = MessageIdentifierRange(3...4) XCTAssertEqual(sut.range.lowerBound, 3) XCTAssertEqual(sut.range.upperBound, 4) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/IMAPServer+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IMAPServer+Tests.swift index a3d7ac361..f767ed81d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IMAPServer+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IMAPServer+Tests.swift @@ -24,9 +24,20 @@ extension IMAPServer_Tests { func testEncode() { let inputs: [(IMAPServer, String, UInt)] = [ (.init(host: "localhost"), "localhost", #line), - (.init(userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), host: "localhost"), ";AUTH=*@localhost", #line), + ( + .init( + userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), + host: "localhost" + ), ";AUTH=*@localhost", #line + ), (.init(host: "localhost", port: 1234), "localhost:1234", #line), - (.init(userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), host: "localhost", port: 1234), ";AUTH=*@localhost:1234", #line), + ( + .init( + userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), + host: "localhost", + port: 1234 + ), ";AUTH=*@localhost:1234", #line + ), ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeIMAPServer($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/IMAPURLSection+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IMAPURLSection+Tests.swift index 6a7592630..c5324299f 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IMAPURLSection+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IMAPURLSection+Tests.swift @@ -23,14 +23,14 @@ class URLMessageSection_Tests: EncodeTestClass {} extension URLMessageSection_Tests { func testEncode_URLMessageSection() { let inputs: [(URLMessageSection, String, UInt)] = [ - (.init(encodedSection: .init(section: "test")), "/;SECTION=test", #line), + (.init(encodedSection: .init(section: "test")), "/;SECTION=test", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeURLMessageSection($0) }) } func testEncode_URLMessageSectionOnly() { let inputs: [(URLMessageSection, String, UInt)] = [ - (.init(encodedSection: .init(section: "test")), ";SECTION=test", #line), + (.init(encodedSection: .init(section: "test")), ";SECTION=test", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeURLMessageSectionOnly($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/IMessageList+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IMessageList+Tests.swift index bd38c2304..ea2e5f168 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IMessageList+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IMessageList+Tests.swift @@ -24,12 +24,18 @@ extension EncodedSearchQuery_Tests { func testEncode() { let inputs: [(EncodedSearchQuery, String, UInt)] = [ ( - .init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "box"), uidValidity: nil), encodedSearch: nil), + .init( + mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "box"), uidValidity: nil), + encodedSearch: nil + ), "box", #line ), ( - .init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "box"), uidValidity: nil), encodedSearch: .init(query: "search")), + .init( + mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "box"), uidValidity: nil), + encodedSearch: .init(query: "search") + ), "box?search", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/IMessageOrPartial+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IMessageOrPartial+Tests.swift index b790cb4a4..58b084174 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IMessageOrPartial+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IMessageOrPartial+Tests.swift @@ -34,7 +34,10 @@ extension URLFetchType_Tests { #line ), ( - .sectionPartial(section: .init(encodedSection: .init(section: "section")), partial: .init(range: .init(offset: 1, length: 2))), + .sectionPartial( + section: .init(encodedSection: .init(section: "section")), + partial: .init(range: .init(offset: 1, length: 2)) + ), ";SECTION=section/;PARTIAL=1.2", #line ), @@ -44,37 +47,69 @@ extension URLFetchType_Tests { #line ), ( - .uidSectionPartial(uid: .init(uid: 123), section: .init(encodedSection: .init(section: "test")), partial: nil), + .uidSectionPartial( + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "test")), + partial: nil + ), ";UID=123/;SECTION=test", #line ), ( - .uidSectionPartial(uid: .init(uid: 123), section: nil, partial: .init(range: .init(offset: 1, length: 2))), + .uidSectionPartial( + uid: .init(uid: 123), + section: nil, + partial: .init(range: .init(offset: 1, length: 2)) + ), ";UID=123/;PARTIAL=1.2", #line ), ( - .uidSectionPartial(uid: .init(uid: 123), section: .init(encodedSection: .init(section: "test")), partial: .init(range: .init(offset: 1, length: 2))), + .uidSectionPartial( + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "test")), + partial: .init(range: .init(offset: 1, length: 2)) + ), ";UID=123/;SECTION=test/;PARTIAL=1.2", #line ), ( - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: nil, partial: nil), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: nil, + partial: nil + ), "test;UID=123", #line ), ( - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: .init(encodedSection: .init(section: "box")), partial: nil), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "box")), + partial: nil + ), "test;UID=123/;SECTION=box", #line ), ( - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: nil, partial: .init(range: .init(offset: 1, length: 2))), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: nil, + partial: .init(range: .init(offset: 1, length: 2)) + ), "test;UID=123/;PARTIAL=1.2", #line ), ( - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: .init(encodedSection: .init(section: "box")), partial: .init(range: .init(offset: 1, length: 2))), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "box")), + partial: .init(range: .init(offset: 1, length: 2)) + ), "test;UID=123/;SECTION=box/;PARTIAL=1.2", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/IMessagePart+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IMessagePart+Tests.swift index c0ac5d5e8..e75fbe673 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IMessagePart+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IMessagePart+Tests.swift @@ -24,22 +24,42 @@ extension MessagePath_Tests { func testEncode() { let inputs: [(MessagePath, String, UInt)] = [ ( - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: nil, range: nil), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: nil, + range: nil + ), "test/;UID=123", #line ), ( - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: .init(encodedSection: .init(section: "section")), range: nil), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: .init(encodedSection: .init(section: "section")), + range: nil + ), "test/;UID=123/;SECTION=section", #line ), ( - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: nil, range: .init(range: .init(offset: 123, length: 4))), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: nil, + range: .init(range: .init(offset: 123, length: 4)) + ), "test/;UID=123/;PARTIAL=123.4", #line ), ( - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: .init(encodedSection: .init(section: "section")), range: .init(range: .init(offset: 123, length: 4))), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: .init(encodedSection: .init(section: "section")), + range: .init(range: .init(offset: 123, length: 4)) + ), "test/;UID=123/;SECTION=section/;PARTIAL=123.4", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/INetworkPath+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/INetworkPath+Tests.swift index 9eb3ffd40..9dff3d29e 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/INetworkPath+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/INetworkPath+Tests.swift @@ -27,7 +27,7 @@ extension NetworkPath_Tests { .init(server: .init(host: "localhost"), query: nil), "//localhost/", #line - ), + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeNetworkPath($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/IUAVerifier+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IUAVerifier+Tests.swift index a8df6cebe..052986521 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IUAVerifier+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IUAVerifier+Tests.swift @@ -23,7 +23,7 @@ class AuthenticatedURLVerifier_Tests: EncodeTestClass {} extension AuthenticatedURLVerifier_Tests { func testEncode() { let inputs: [(AuthenticatedURLVerifier, String, UInt)] = [ - (.init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "test")), ":INTERNAL:test", #line), + (.init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "test")), ":INTERNAL:test", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeAuthenticatedURLVerifier($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/IURLAuthRump+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IURLAuthRump+Tests.swift index 75f7a79bb..4d7352090 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IURLAuthRump+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IURLAuthRump+Tests.swift @@ -25,7 +25,15 @@ extension AuthenticatedURLRump_Tests { let inputs: [(AuthenticatedURLRump, String, UInt)] = [ (.init(access: .anonymous), ";URLAUTH=anonymous", #line), ( - .init(expire: .init(dateTime: .init(date: .init(year: 1234, month: 12, day: 23), time: .init(hour: 12, minute: 34, second: 56))), access: .authenticateUser), + .init( + expire: .init( + dateTime: .init( + date: .init(year: 1234, month: 12, day: 23), + time: .init(hour: 12, minute: 34, second: 56) + ) + ), + access: .authenticateUser + ), ";EXPIRE=1234-12-23T12:34:56;URLAUTH=authuser", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/IUrlAuth+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/IUrlAuth+Tests.swift index a0824d119..eaf1c077d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/IUrlAuth+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/IUrlAuth+Tests.swift @@ -23,7 +23,12 @@ class IURLAuth_Tests: EncodeTestClass {} extension IURLAuth_Tests { func testEncode() { let inputs: [(AuthenticatedURL, String, UInt)] = [ - (.init(authenticatedURL: .init(access: .anonymous), verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "test"))), ";URLAUTH=anonymous:INTERNAL:test", #line), + ( + .init( + authenticatedURL: .init(access: .anonymous), + verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "test")) + ), ";URLAUTH=anonymous:INTERNAL:test", #line + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeIAuthenticatedURL($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/ImapUrl+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ImapUrl+Tests.swift index b976edfed..9e303e2e1 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ImapUrl+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ImapUrl+Tests.swift @@ -23,7 +23,7 @@ class IMAPURL_Tests: EncodeTestClass {} extension IMAPURL_Tests { func testEncode() { let inputs: [(IMAPURL, String, UInt)] = [ - (.init(server: .init(host: "localhost"), query: nil), "imap://localhost/", #line), + (.init(server: .init(host: "localhost"), query: nil), "imap://localhost/", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeIMAPURL($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/ImapUrlRel+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/ImapUrlRel+Tests.swift index 09e412af9..935e02d07 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/ImapUrlRel+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/ImapUrlRel+Tests.swift @@ -23,7 +23,13 @@ class RelativeIMAPURL_Tests: EncodeTestClass {} extension RelativeIMAPURL_Tests { func testEncode() { let inputs: [(RelativeIMAPURL, String, UInt)] = [ - (.absolutePath(.init(command: .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test")))))), "/test", #line), + ( + .absolutePath( + .init( + command: .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test")))) + ) + ), "/test", #line + ), (.networkPath(.init(server: .init(host: "localhost"), query: nil)), "//localhost/", #line), (.empty, "", #line), ] diff --git a/Tests/NIOIMAPCoreTests/Grammar/List/ListSelectBaseOption+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/List/ListSelectBaseOption+Tests.swift index cd40a7faf..18cfbcd36 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/List/ListSelectBaseOption+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/List/ListSelectBaseOption+Tests.swift @@ -37,7 +37,7 @@ extension ListSelectBaseOption_Tests { func testEncodeQuoted() { let inputs: [(ListSelectBaseOption, String, UInt)] = [ - (.subscribed, #""SUBSCRIBED""#, #line), + (.subscribed, #""SUBSCRIBED""#, #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxAttribute+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxAttribute+Tests.swift index 32fc50440..0527b0599 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxAttribute+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxAttribute+Tests.swift @@ -42,12 +42,21 @@ extension MailboxAttribute_Tests { let inputs: [(MailboxStatus, String, UInt)] = [ (.init(), "", #line), ( - .init(messageCount: 133701, recentCount: 255813, nextUID: 377003, uidValidity: 427421, unseenCount: 528028, size: 680543, highestModificationSequence: 797237, appendLimit: 86254193), + .init( + messageCount: 133701, + recentCount: 255813, + nextUID: 377003, + uidValidity: 427421, + unseenCount: 528028, + size: 680543, + highestModificationSequence: 797237, + appendLimit: 86_254_193 + ), "MESSAGES 133701 RECENT 255813 UIDNEXT 377003 UIDVALIDITY 427421 UNSEEN 528028 SIZE 680543 HIGHESTMODSEQ 797237 APPENDLIMIT 86254193", #line ), ( - .init(messageCount: 133701, nextUID: 377003, uidValidity: 427421, appendLimit: 86254193), + .init(messageCount: 133701, nextUID: 377003, uidValidity: 427421, appendLimit: 86_254_193), "MESSAGES 133701 UIDNEXT 377003 UIDVALIDITY 427421 APPENDLIMIT 86254193", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxData+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxData+Tests.swift index dc5669940..f95c10d54 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxData+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxData+Tests.swift @@ -25,21 +25,41 @@ extension MailboxDataTests { let inputs: [(MailboxData, String, UInt)] = [ (.exists(1), "1 EXISTS", #line), (.flags([.answered, .deleted]), "FLAGS (\\Answered \\Deleted)", #line), - (.list(MailboxInfo(attributes: [], path: try! .init(name: .inbox), extensions: [:])), "LIST () NIL \"INBOX\"", #line), ( - .lsub(.init(attributes: [.init("\\draft")], path: try! .init(name: .init("Drafts"), pathSeparator: "."), extensions: [:])), + .list(MailboxInfo(attributes: [], path: try! .init(name: .inbox), extensions: [:])), + "LIST () NIL \"INBOX\"", #line + ), + ( + .lsub( + .init( + attributes: [.init("\\draft")], + path: try! .init(name: .init("Drafts"), pathSeparator: "."), + extensions: [:] + ) + ), "LSUB (\\draft) \".\" \"Drafts\"", #line ), - (.extendedSearch(ExtendedSearchResponse(correlator: nil, kind: .sequenceNumber, returnData: [.count(1)])), "ESEARCH COUNT 1", #line), - (.extendedSearch(ExtendedSearchResponse(correlator: nil, kind: .sequenceNumber, returnData: [.count(1), .count(2)])), "ESEARCH COUNT 1 COUNT 2", #line), + ( + .extendedSearch( + ExtendedSearchResponse(correlator: nil, kind: .sequenceNumber, returnData: [.count(1)]) + ), "ESEARCH COUNT 1", #line + ), + ( + .extendedSearch( + ExtendedSearchResponse(correlator: nil, kind: .sequenceNumber, returnData: [.count(1), .count(2)]) + ), "ESEARCH COUNT 1 COUNT 2", #line + ), (.status(.inbox, .init(messageCount: 1)), "STATUS \"INBOX\" (MESSAGES 1)", #line), (.status(.inbox, .init(messageCount: 1, unseenCount: 2)), "STATUS \"INBOX\" (MESSAGES 1 UNSEEN 2)", #line), - (.namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), "NAMESPACE NIL NIL NIL", #line), + ( + .namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), + "NAMESPACE NIL NIL NIL", #line + ), (.search([]), "SEARCH", #line), (.search([1]), "SEARCH 1", #line), (.search([1, 2, 3, 4, 5]), "SEARCH 1 2 3 4 5", #line), - (.search([20, 23], ModificationSequenceValue(917162500)), "SEARCH 20 23 (MODSEQ 917162500)", #line), + (.search([20, 23], ModificationSequenceValue(917_162_500)), "SEARCH 20 23 (MODSEQ 917162500)", #line), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxInfo+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxInfo+Tests.swift index c9eebb161..3405d8ab0 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxInfo+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxInfo+Tests.swift @@ -38,7 +38,10 @@ extension MailboxInfo_Tests { func testEncode() { let inputs: [(MailboxInfo, String, UInt)] = [ (MailboxInfo(attributes: [], path: try! .init(name: .inbox), extensions: [:]), "() NIL \"INBOX\"", #line), - (MailboxInfo(attributes: [], path: try! .init(name: .inbox, pathSeparator: "a"), extensions: [:]), "() \"a\" \"INBOX\"", #line), + ( + MailboxInfo(attributes: [], path: try! .init(name: .inbox, pathSeparator: "a"), extensions: [:]), + "() \"a\" \"INBOX\"", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxName+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxName+Tests.swift index 419b17e88..0991706b1 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxName+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Mailbox/MailboxName+Tests.swift @@ -42,7 +42,7 @@ extension MailboxName_Tests { "£", try! .init(name: .init("box/&AKM-"), pathSeparator: "/"), #line - ), + ) ] for (path, newName, newPath, line) in inputs { XCTAssertEqual(try path.makeSubMailbox(displayName: newName), newPath, line: line) @@ -50,7 +50,8 @@ extension MailboxName_Tests { // sad path test make sure that mailbox size limit is enforced XCTAssertThrowsError( - try MailboxPath(name: .init(ByteBuffer(string: String(repeating: "a", count: 999))), pathSeparator: "/").makeSubMailbox(displayName: "1") + try MailboxPath(name: .init(ByteBuffer(string: String(repeating: "a", count: 999))), pathSeparator: "/") + .makeSubMailbox(displayName: "1") ) { error in XCTAssertEqual(error as! MailboxTooBigError, MailboxTooBigError(maximumSize: 1000, actualSize: 1001)) } @@ -72,7 +73,11 @@ extension MailboxName_Tests { ), ] for (newName, separator, newPath, line) in inputs { - XCTAssertEqual(try MailboxPath.makeRootMailbox(displayName: newName, pathSeparator: separator), newPath, line: line) + XCTAssertEqual( + try MailboxPath.makeRootMailbox(displayName: newName, pathSeparator: separator), + newPath, + line: line + ) } // sad path test make sure that mailbox size limit is enforced @@ -88,10 +93,16 @@ extension MailboxName_Tests { (try! .init(name: .init("ABC"), pathSeparator: "B"), true, ["A", "C"], #line), (try! .init(name: .init("ABC"), pathSeparator: "D"), true, ["ABC"], #line), (try! .init(name: .init(""), pathSeparator: "D"), true, [], #line), - (try! .init(name: .init("some/real/mailbox"), pathSeparator: "/"), true, ["some", "real", "mailbox"], #line), + ( + try! .init(name: .init("some/real/mailbox"), pathSeparator: "/"), true, ["some", "real", "mailbox"], + #line + ), (try! .init(name: .init("mailbox#test"), pathSeparator: "#"), true, ["mailbox", "test"], #line), (try! .init(name: .init("//test1//test2//"), pathSeparator: "/"), true, ["test1", "test2"], #line), - (try! .init(name: .init("//test1//test2//"), pathSeparator: "/"), false, ["", "", "test1", "", "test2", "", ""], #line), + ( + try! .init(name: .init("//test1//test2//"), pathSeparator: "/"), false, + ["", "", "test1", "", "test2", "", ""], #line + ), ] for (path, ommitEmpty, expected, line) in inputs { XCTAssertEqual(path.displayStringComponents(omittingEmptySubsequences: ommitEmpty), expected, line: line) @@ -169,7 +180,7 @@ extension MailboxName_Tests { func countBits(_ v: Int) -> Int { var value = UInt(bitPattern: v) var count = 0 - while (value != 0) { + while value != 0 { count += 1 value = value & (value &- 1) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Message/MessageAtributesTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Message/MessageAtributesTests.swift index 2f18ebd92..9680c0417 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Message/MessageAtributesTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Message/MessageAtributesTests.swift @@ -22,21 +22,74 @@ class MessageAttributesTests: EncodeTestClass {} extension MessageAttributesTests { func testEncode() throws { - let components = ServerMessageDate.Components(year: 1994, month: 6, day: 25, hour: 1, minute: 2, second: 3, timeZoneMinutes: 0)! + let components = ServerMessageDate.Components( + year: 1994, + month: 6, + day: 25, + hour: 1, + minute: 2, + second: 3, + timeZoneMinutes: 0 + )! let date = ServerMessageDate(components) let inputs: [(MessageAttribute, String, UInt)] = [ (.rfc822Size(123), "RFC822.SIZE 123", #line), (.uid(123), "UID 123", #line), - (.envelope(Envelope(date: "date", subject: "subject", from: [.singleAddress(.init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host"))], sender: [.singleAddress(.init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host"))], reply: [.singleAddress(.init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host"))], to: [.singleAddress(.init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host"))], cc: [.singleAddress(.init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host"))], bcc: [.singleAddress(.init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host"))], inReplyTo: "replyto", messageID: "abc123")), "ENVELOPE (\"date\" \"subject\" ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) \"replyto\" \"abc123\")", #line), + ( + .envelope( + Envelope( + date: "date", + subject: "subject", + from: [ + .singleAddress( + .init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host") + ) + ], + sender: [ + .singleAddress( + .init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host") + ) + ], + reply: [ + .singleAddress( + .init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host") + ) + ], + to: [ + .singleAddress( + .init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host") + ) + ], + cc: [ + .singleAddress( + .init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host") + ) + ], + bcc: [ + .singleAddress( + .init(personName: "name", sourceRoot: "adl", mailbox: "mailbox", host: "host") + ) + ], + inReplyTo: "replyto", + messageID: "abc123" + ) + ), + "ENVELOPE (\"date\" \"subject\" ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) ((\"name\" \"adl\" \"mailbox\" \"host\")) \"replyto\" \"abc123\")", + #line + ), (.internalDate(date), #"INTERNALDATE "25-Jun-1994 01:02:03 +0000""#, #line), (.binarySize(section: [2], size: 3), "BINARY.SIZE[2] 3", #line), (.flags([.draft]), "FLAGS (\\Draft)", #line), (.flags([.flagged, .draft]), "FLAGS (\\Flagged \\Draft)", #line), (.fetchModificationResponse(.init(modifierSequenceValue: 3)), "MODSEQ (3)", #line), - (.gmailMessageID(1278455344230334865), "X-GM-MSGID 1278455344230334865", #line), - (.gmailThreadID(1266894439832287888), "X-GM-THRID 1266894439832287888", #line), - (.gmailLabels([GmailLabel("\\Inbox"), GmailLabel("\\Sent"), GmailLabel("Important"), GmailLabel("Muy Importante")]), "X-GM-LABELS (\\Inbox \\Sent \"Important\" \"Muy Importante\")", #line), + (.gmailMessageID(1_278_455_344_230_334_865), "X-GM-MSGID 1278455344230334865", #line), + (.gmailThreadID(1_266_894_439_832_287_888), "X-GM-THRID 1266894439832287888", #line), + ( + .gmailLabels([ + GmailLabel("\\Inbox"), GmailLabel("\\Sent"), GmailLabel("Important"), GmailLabel("Muy Importante"), + ]), "X-GM-LABELS (\\Inbox \\Sent \"Important\" \"Muy Importante\")", #line + ), (.preview(.init("Lorem ipsum dolor sit amet")), "PREVIEW \"Lorem ipsum dolor sit amet\"", #line), (.preview(.init(#"A\B"#)), "PREVIEW {3}\r\nA\\B", #line), ] @@ -72,7 +125,11 @@ extension MessageAttributesTests { let inputs: [(MessageAttribute, String, UInt)] = [ (.rfc822Size(123), "RFC822.SIZE 123", #line), (.flags([.draft]), "FLAGS (\\Draft)", #line), - (.gmailLabels([GmailLabel("\\Inbox"), GmailLabel("\\Sent"), GmailLabel("Important"), GmailLabel("Muy Importante")]), "X-GM-LABELS (\\Inbox \\Sent \"Important\" \"Muy Importante\")", #line), + ( + .gmailLabels([ + GmailLabel("\\Inbox"), GmailLabel("\\Sent"), GmailLabel("Important"), GmailLabel("Muy Importante"), + ]), "X-GM-LABELS (\\Inbox \\Sent \"Important\" \"Muy Importante\")", #line + ), ] inputs.forEach { (part, expected, line) in XCTAssertEqual("\(part)", expected, line: line) diff --git a/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierRange+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierRange+Tests.swift index a05c6e728..05fe7eb15 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierRange+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierRange+Tests.swift @@ -22,14 +22,14 @@ class MessageIdentifierRange_Tests: EncodeTestClass {} extension MessageIdentifierRange_Tests { func testConvert_sequenceNumber() { - let input = MessageIdentifierRange(UnknownMessageIdentifier(1) ... 2) + let input = MessageIdentifierRange(UnknownMessageIdentifier(1)...2) let output = MessageIdentifierRange(input) - XCTAssertEqual(output, 1 ... 2) + XCTAssertEqual(output, 1...2) } func testConvert_uid() { - let input = MessageIdentifierRange(UnknownMessageIdentifier(5) ... 6) + let input = MessageIdentifierRange(UnknownMessageIdentifier(5)...6) let output = MessageIdentifierRange(input) - XCTAssertEqual(output, 5 ... 6) + XCTAssertEqual(output, 5...6) } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierSet+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierSet+Tests.swift index 2028acec9..5b05f0757 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierSet+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/MessageIdentifierSet+Tests.swift @@ -22,14 +22,14 @@ class MessageIdentifierSet_Tests: EncodeTestClass {} extension MessageIdentifierSet_Tests { func testConvert_sequenceNumber() { - let input = MessageIdentifierSet([1 ... 5, 10 ... 15, 20 ... 30]) + let input = MessageIdentifierSet([1...5, 10...15, 20...30]) let output = MessageIdentifierSet(input) - XCTAssertEqual(output, [1 ... 5, 10 ... 15, 20 ... 30]) + XCTAssertEqual(output, [1...5, 10...15, 20...30]) } func testConvert_uid() { - let input = MessageIdentifierSet([1 ... 5, 10 ... 15, 20 ... 30]) + let input = MessageIdentifierSet([1...5, 10...15, 20...30]) let output = MessageIdentifierSet(input) - XCTAssertEqual(output, [1 ... 5, 10 ... 15, 20 ... 30]) + XCTAssertEqual(output, [1...5, 10...15, 20...30]) } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceCommand+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceCommand+Tests.swift index f0e995409..a730ed3a4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceCommand+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceCommand+Tests.swift @@ -23,7 +23,7 @@ class NamespaceCommand_Tests: EncodeTestClass {} extension NamespaceCommand_Tests { func testEncode() { let inputs: [(String, UInt)] = [ - ("NAMESPACE", #line), + ("NAMESPACE", #line) ] for (expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceDescription+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceDescription+Tests.swift index ab4d438aa..a6c75ebc8 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceDescription+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceDescription+Tests.swift @@ -25,7 +25,10 @@ extension NamespaceDescription_Tests { let inputs: [(NamespaceDescription, String, UInt)] = [ (.init(string: "string", char: nil, responseExtensions: [:]), "(\"string\" NIL)", #line), (.init(string: "string", char: "a", responseExtensions: [:]), "(\"string\" \"a\")", #line), - (.init(string: "string", char: nil, responseExtensions: ["str2": ["str3"]]), "(\"string\" NIL \"str2\" (\"str3\"))", #line), + ( + .init(string: "string", char: nil, responseExtensions: ["str2": ["str3"]]), + "(\"string\" NIL \"str2\" (\"str3\"))", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceResponse+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceResponse+Tests.swift index 66db1a89c..28ad8bd4d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceResponse+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Namespace/NamespaceResponse+Tests.swift @@ -23,7 +23,7 @@ class NamespaceResponse_Tests: EncodeTestClass {} extension NamespaceResponse_Tests { func testEncode() { let inputs: [(NamespaceResponse, String, UInt)] = [ - (.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: []), "NAMESPACE NIL NIL NIL", #line), + (.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: []), "NAMESPACE NIL NIL NIL", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Option/OptionExtension+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Option/OptionExtension+Tests.swift index dcdce1199..e227154c4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Option/OptionExtension+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Option/OptionExtension+Tests.swift @@ -25,7 +25,10 @@ extension OptionExtension_Tests { let inputs: [(KeyValue, String, UInt)] = [ (.init(key: .standard("test"), value: .string("string")), "test (\"string\")", #line), (.init(key: .vendor(.init(key: "token", value: "atom")), value: nil), "token-atom", #line), - (.init(key: .vendor(.init(key: "token", value: "atom")), value: .string("value")), "token-atom (\"value\")", #line), + ( + .init(key: .vendor(.init(key: "token", value: "atom")), value: .string("value")), + "token-atom (\"value\")", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Option/OptionValue+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Option/OptionValue+Tests.swift index 796bf9cf3..5a0bdaa44 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Option/OptionValue+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Option/OptionValue+Tests.swift @@ -23,7 +23,7 @@ class OptionValue_Tests: EncodeTestClass {} extension OptionValue_Tests { func testEncode() { let inputs: [(OptionValueComp, String, UInt)] = [ - (.string("test"), "(\"test\")", #line), + (.string("test"), "(\"test\")", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Option/OptionVendorTag+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Option/OptionVendorTag+Tests.swift index 890787a8f..08d8f6d68 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Option/OptionVendorTag+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Option/OptionVendorTag+Tests.swift @@ -23,7 +23,7 @@ class OptionVendorTag_Tests: EncodeTestClass {} extension OptionVendorTag_Tests { func testEncode() { let inputs: [(KeyValue, String, UInt)] = [ - (.init(key: "some", value: "thing"), "some-thing", #line), + (.init(key: "some", value: "thing"), "some-thing", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Partial/PartialRange+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Partial/PartialRange+Tests.swift index c20da1cf1..190f17192 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Partial/PartialRange+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Partial/PartialRange+Tests.swift @@ -23,10 +23,10 @@ class PartialRange_Tests: EncodeTestClass, _ParserTestHelpers {} extension PartialRange_Tests { func testEncode() { let inputs: [(PartialRange, String, UInt)] = [ - (.first(1 ... 1), "1:1", #line), - (.first(100 ... 200), "100:200", #line), - (.last(1 ... 1), "-1:-1", #line), - (.last(100 ... 200), "-100:-200", #line), + (.first(1...1), "1:1", #line), + (.first(100...200), "100:200", #line), + (.last(1...1), "-1:-1", #line), + (.last(100...200), "-100:-200", #line), ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writePartialRange($0) }) } @@ -35,18 +35,18 @@ extension PartialRange_Tests { self.iterateTests( testFunction: GrammarParser().parsePartialRange, validInputs: [ - ("1:2", " ", .first(1 ... 2), #line), - ("1:1", " ", .first(1 ... 1), #line), - ("100:200", " ", .first(100 ... 200), #line), - ("200:100", " ", .first(100 ... 200), #line), - ("333:333", " ", .first(333 ... 333), #line), - ("1234567:2345678", " ", .first(1234567 ... 2345678), #line), - ("-1:-2", " ", .last(1 ... 2), #line), - ("-1:-1", " ", .last(1 ... 1), #line), - ("-100:-200", " ", .last(100 ... 200), #line), - ("-200:-100", " ", .last(100 ... 200), #line), - ("-333:-333", " ", .last(333 ... 333), #line), - ("-1234567:-2345678", " ", .last(1234567 ... 2345678), #line), + ("1:2", " ", .first(1...2), #line), + ("1:1", " ", .first(1...1), #line), + ("100:200", " ", .first(100...200), #line), + ("200:100", " ", .first(100...200), #line), + ("333:333", " ", .first(333...333), #line), + ("1234567:2345678", " ", .first(1_234_567...2_345_678), #line), + ("-1:-2", " ", .last(1...2), #line), + ("-1:-1", " ", .last(1...1), #line), + ("-100:-200", " ", .last(100...200), #line), + ("-200:-100", " ", .last(100...200), #line), + ("-333:-333", " ", .last(333...333), #line), + ("-1234567:-2345678", " ", .last(1_234_567...2_345_678), #line), ], parserErrorInputs: [ ("1", " ", #line), diff --git a/Tests/NIOIMAPCoreTests/Grammar/Patterns+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Patterns+Tests.swift index f7534b625..bd7cc79e3 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Patterns+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Patterns+Tests.swift @@ -23,7 +23,7 @@ class Patterns_Tests: EncodeTestClass {} extension Patterns_Tests { func testEncode() { let inputs: [([ByteBuffer], String, UInt)] = [ - (["Mailbox1", "Mailbox2"], "(\"Mailbox1\" \"Mailbox2\")", #line), + (["Mailbox1", "Mailbox2"], "(\"Mailbox1\" \"Mailbox2\")", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaLimit+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaLimit+Tests.swift index 534570169..f8c3b059d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaLimit+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaLimit+Tests.swift @@ -23,7 +23,7 @@ class QuotaLimitTests: EncodeTestClass {} extension QuotaLimitTests { func testEncode() { let inputs: [(QuotaLimit, String, UInt)] = [ - (QuotaLimit(resourceName: "STORAGE", limit: 512), "STORAGE 512", #line), + (QuotaLimit(resourceName: "STORAGE", limit: 512), "STORAGE 512", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaResource+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaResource+Tests.swift index 7d5e7e68d..92e914a4d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaResource+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Quota/QuotaResource+Tests.swift @@ -23,7 +23,7 @@ class QuotaResourceTests: EncodeTestClass {} extension QuotaResourceTests { func testEncode() { let inputs: [(QuotaResource, String, UInt)] = [ - (QuotaResource(resourceName: "STORAGE", usage: 10, limit: 512), "STORAGE 10 512", #line), + (QuotaResource(resourceName: "STORAGE", usage: 10, limit: 512), "STORAGE 10 512", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ContinuationRequestTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ContinuationRequestTests.swift index cbbeec647..1b7314267 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ContinuationRequestTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ContinuationRequestTests.swift @@ -28,11 +28,13 @@ extension ByteBuffer { } private let bufferA = ByteBuffer( - [0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, - 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x37, 0x32, - 0x1B, 0x81, 0x84, 0xDC, 0xA9, 0x13, 0xCC, 0x17, 0x81, 0x89, 0x51, 0xDE, - 0x71, 0xE3, 0xF6, 0x09, 0x66, 0x34, 0x49, 0x1D, 0x1F, 0x01, 0x00, 0x20, - 0x00, 0x04, 0x04, 0x04, 0x04] + [ + 0x60, 0x33, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xEA, 0x37, 0x32, + 0x1B, 0x81, 0x84, 0xDC, 0xA9, 0x13, 0xCC, 0x17, 0x81, 0x89, 0x51, 0xDE, + 0x71, 0xE3, 0xF6, 0x09, 0x66, 0x34, 0x49, 0x1D, 0x1F, 0x01, 0x00, 0x20, + 0x00, 0x04, 0x04, 0x04, 0x04, + ] ) private let fixtures: [(ContinuationRequest, String, UInt)] = [ @@ -45,20 +47,34 @@ private let fixtures: [(ContinuationRequest, String, UInt)] = [ extension ContinuationRequestTests { func testEncode() { - self.iterateInputs(inputs: fixtures, encoder: { req in - var encoder = ResponseEncodeBuffer(buffer: self.testBuffer.buffer, options: ResponseEncodingOptions(), loggingMode: false) - defer { - self.testBuffer = EncodeBuffer.serverEncodeBuffer(buffer: encoder.readBytes(), options: ResponseEncodingOptions(), loggingMode: false) + self.iterateInputs( + inputs: fixtures, + encoder: { req in + var encoder = ResponseEncodeBuffer( + buffer: self.testBuffer.buffer, + options: ResponseEncodingOptions(), + loggingMode: false + ) + defer { + self.testBuffer = EncodeBuffer.serverEncodeBuffer( + buffer: encoder.readBytes(), + options: ResponseEncodingOptions(), + loggingMode: false + ) + } + return encoder.writeContinuationRequest(req) } - return encoder.writeContinuationRequest(req) - }) + ) } func testParse() { for (expected, input, line) in fixtures { var buffer = ParseBuffer(ByteBuffer(string: input + "a")) do { - let cont = try GrammarParser().parseContinuationRequest(buffer: &buffer, tracker: StackTracker(maximumParserStackDepth: 100)) + let cont = try GrammarParser().parseContinuationRequest( + buffer: &buffer, + tracker: StackTracker(maximumParserStackDepth: 100) + ) XCTAssertEqual(cont, expected, "parse", line: line) XCTAssertEqual(buffer.readableBytes, 1, "parse", line: line) } catch { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ESearchResponse+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ESearchResponse+Tests.swift index cc0c49baf..e7ecfb20f 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ESearchResponse+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ESearchResponse+Tests.swift @@ -23,73 +23,87 @@ class ExtendedSearchResponse_Tests: EncodeTestClass {} extension ExtendedSearchResponse_Tests { func testMatchedUIDs() { XCTAssertEqual( - ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70 ... 120]))]).matchedUIDs, - [44, 70 ... 120] + ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70...120]))]).matchedUIDs, + [44, 70...120] ) XCTAssertEqual( ExtendedSearchResponse(kind: .uid, returnData: []).matchedUIDs, [] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .uid, returnData: [.count(5), .all(.set([44, 70 ... 120]))]).matchedUIDs, - [44, 70 ... 120] + ExtendedSearchResponse(kind: .uid, returnData: [.count(5), .all(.set([44, 70...120]))]).matchedUIDs, + [44, 70...120] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70 ... 120])), .max(6)]).matchedUIDs, - [44, 70 ... 120] + ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70...120])), .max(6)]).matchedUIDs, + [44, 70...120] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .uid, returnData: [.partial(.last(34 ... 10_000), [99 ... 107, 200])]).matchedUIDs, - [99 ... 107, 200] + ExtendedSearchResponse(kind: .uid, returnData: [.partial(.last(34...10_000), [99...107, 200])]).matchedUIDs, + [99...107, 200] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .uid, returnData: [.partial(.last(34 ... 10_000), [99 ... 107, 200]), .all(.set([44, 70 ... 120]))]).matchedUIDs, - [44, 70 ... 120] + ExtendedSearchResponse( + kind: .uid, + returnData: [.partial(.last(34...10_000), [99...107, 200]), .all(.set([44, 70...120]))] + ).matchedUIDs, + [44, 70...120] ) XCTAssertNil( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.all(.set([44, 70 ... 120]))]).matchedUIDs + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.all(.set([44, 70...120]))]).matchedUIDs ) XCTAssertNil( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.count(5), .all(.set([44, 70 ... 120]))]).matchedUIDs + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.count(5), .all(.set([44, 70...120]))]) + .matchedUIDs ) XCTAssertNil( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.partial(.last(34 ... 10_000), [99 ... 107, 200])]).matchedUIDs + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.partial(.last(34...10_000), [99...107, 200])]) + .matchedUIDs ) } func testMatchedSequenceNumbers() { XCTAssertEqual( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.all(.set([44, 70 ... 120]))]).matchedSequenceNumbers, - [44, 70 ... 120] + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.all(.set([44, 70...120]))]) + .matchedSequenceNumbers, + [44, 70...120] ) XCTAssertEqual( ExtendedSearchResponse(kind: .sequenceNumber, returnData: []).matchedSequenceNumbers, [] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.count(5), .all(.set([44, 70 ... 120]))]).matchedSequenceNumbers, - [44, 70 ... 120] + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.count(5), .all(.set([44, 70...120]))]) + .matchedSequenceNumbers, + [44, 70...120] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.all(.set([44, 70 ... 120])), .max(6)]).matchedSequenceNumbers, - [44, 70 ... 120] + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.all(.set([44, 70...120])), .max(6)]) + .matchedSequenceNumbers, + [44, 70...120] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.partial(.last(34 ... 10_000), [99 ... 107, 200])]).matchedSequenceNumbers, - [99 ... 107, 200] + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.partial(.last(34...10_000), [99...107, 200])]) + .matchedSequenceNumbers, + [99...107, 200] ) XCTAssertEqual( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.partial(.last(34 ... 10_000), [99 ... 107, 200]), .all(.set([44, 70 ... 120]))]).matchedSequenceNumbers, - [44, 70 ... 120] + ExtendedSearchResponse( + kind: .sequenceNumber, + returnData: [.partial(.last(34...10_000), [99...107, 200]), .all(.set([44, 70...120]))] + ).matchedSequenceNumbers, + [44, 70...120] ) XCTAssertNil( - ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70 ... 120]))]).matchedSequenceNumbers + ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70...120]))]).matchedSequenceNumbers ) XCTAssertNil( - ExtendedSearchResponse(kind: .uid, returnData: [.count(5), .all(.set([44, 70 ... 120]))]).matchedSequenceNumbers + ExtendedSearchResponse(kind: .uid, returnData: [.count(5), .all(.set([44, 70...120]))]) + .matchedSequenceNumbers ) XCTAssertNil( - ExtendedSearchResponse(kind: .uid, returnData: [.partial(.last(34 ... 10_000), [99 ... 107, 200])]).matchedSequenceNumbers + ExtendedSearchResponse(kind: .uid, returnData: [.partial(.last(34...10_000), [99...107, 200])]) + .matchedSequenceNumbers ) } @@ -99,15 +113,15 @@ extension ExtendedSearchResponse_Tests { 5 ) XCTAssertEqual( - ExtendedSearchResponse(kind: .uid, returnData: [.count(5), .all(.set([44, 70 ... 120]))]).count, + ExtendedSearchResponse(kind: .uid, returnData: [.count(5), .all(.set([44, 70...120]))]).count, 5 ) XCTAssertEqual( - ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70 ... 120])), .count(5)]).count, + ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70...120])), .count(5)]).count, 5 ) XCTAssertNil( - ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70 ... 120])), .max(5)]).count + ExtendedSearchResponse(kind: .uid, returnData: [.all(.set([44, 70...120])), .max(5)]).count ) XCTAssertNil( ExtendedSearchResponse(kind: .uid, returnData: []).count @@ -140,7 +154,8 @@ extension ExtendedSearchResponse_Tests { 73 ) XCTAssertEqual( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.max(82), .min(73), .count(8)]).minSequenceNumber, + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.max(82), .min(73), .count(8)]) + .minSequenceNumber, 73 ) XCTAssertNil( @@ -180,7 +195,8 @@ extension ExtendedSearchResponse_Tests { 103 ) XCTAssertEqual( - ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.min(82), .max(103), .count(8)]).maxSequenceNumber, + ExtendedSearchResponse(kind: .sequenceNumber, returnData: [.min(82), .max(103), .count(8)]) + .maxSequenceNumber, 103 ) XCTAssertNil( @@ -203,7 +219,10 @@ extension ExtendedSearchResponse_Tests { (.init(correlator: nil, kind: .sequenceNumber, returnData: []), "ESEARCH", #line), (.init(correlator: nil, kind: .uid, returnData: []), "ESEARCH UID", #line), (.init(correlator: nil, kind: .sequenceNumber, returnData: [.count(2)]), "ESEARCH COUNT 2", #line), - (.init(correlator: SearchCorrelator(tag: "some"), kind: .sequenceNumber, returnData: []), #"ESEARCH (TAG "some")"#, #line), + ( + .init(correlator: SearchCorrelator(tag: "some"), kind: .sequenceNumber, returnData: []), + #"ESEARCH (TAG "some")"#, #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/Response+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/Response+Tests.swift index c327a5bef..99ac67029 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/Response+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/Response+Tests.swift @@ -33,9 +33,17 @@ extension Response_Tests { for (test, expectedString, line) in inputs { self.testBuffer.clear() - var encoder = ResponseEncodeBuffer(buffer: self.testBuffer.buffer, options: ResponseEncodingOptions(), loggingMode: false) + var encoder = ResponseEncodeBuffer( + buffer: self.testBuffer.buffer, + options: ResponseEncodingOptions(), + loggingMode: false + ) let size = encoder.writeResponse(test) - self.testBuffer = EncodeBuffer.serverEncodeBuffer(buffer: encoder.readBytes(), options: ResponseEncodingOptions(), loggingMode: false) + self.testBuffer = EncodeBuffer.serverEncodeBuffer( + buffer: encoder.readBytes(), + options: ResponseEncodingOptions(), + loggingMode: false + ) XCTAssertEqual(size, expectedString.utf8.count, line: line) XCTAssertEqual(self.testBufferString, expectedString, line: line) } @@ -45,24 +53,40 @@ extension Response_Tests { let inputs: [([NIOIMAPCore.FetchResponse], String, UInt)] = [ ([.start(1), .simpleAttribute(.rfc822Size(123)), .finish], "* 1 FETCH (RFC822.SIZE 123)\r\n", #line), ([.startUID(1), .simpleAttribute(.rfc822Size(123)), .finish], "* 1 UIDFETCH (RFC822.SIZE 123)\r\n", #line), - ([.start(2), .simpleAttribute(.uid(123)), .simpleAttribute(.rfc822Size(456)), .finish], "* 2 FETCH (UID 123 RFC822.SIZE 456)\r\n", #line), ( - [.start(3), .simpleAttribute(.uid(123)), .streamingBegin(kind: .rfc822Text, byteCount: 0), .streamingEnd, .simpleAttribute(.uid(456)), .finish], + [.start(2), .simpleAttribute(.uid(123)), .simpleAttribute(.rfc822Size(456)), .finish], + "* 2 FETCH (UID 123 RFC822.SIZE 456)\r\n", #line + ), + ( + [ + .start(3), .simpleAttribute(.uid(123)), .streamingBegin(kind: .rfc822Text, byteCount: 0), + .streamingEnd, .simpleAttribute(.uid(456)), .finish, + ], "* 3 FETCH (UID 123 RFC822.TEXT {0}\r\n UID 456)\r\n", #line ), ( - [.start(3), .simpleAttribute(.uid(123)), .streamingBegin(kind: .rfc822Header, byteCount: 0), .streamingEnd, .simpleAttribute(.uid(456)), .finish], + [ + .start(3), .simpleAttribute(.uid(123)), .streamingBegin(kind: .rfc822Header, byteCount: 0), + .streamingEnd, .simpleAttribute(.uid(456)), .finish, + ], "* 3 FETCH (UID 123 RFC822.HEADER {0}\r\n UID 456)\r\n", #line ), ( - [.start(87), .simpleAttribute(.nilBody(.body(section: .init(part: [4], kind: .text), offset: nil))), .simpleAttribute(.uid(123)), .finish], + [ + .start(87), .simpleAttribute(.nilBody(.body(section: .init(part: [4], kind: .text), offset: nil))), + .simpleAttribute(.uid(123)), .finish, + ], "* 87 FETCH (BODY[4.TEXT] NIL UID 123)\r\n", #line ), ( - [.startUID(87), .simpleAttribute(.nilBody(.body(section: .init(part: [4], kind: .text), offset: nil))), .simpleAttribute(.uid(123)), .finish], + [ + .startUID(87), + .simpleAttribute(.nilBody(.body(section: .init(part: [4], kind: .text), offset: nil))), + .simpleAttribute(.uid(123)), .finish, + ], "* 87 UIDFETCH (BODY[4.TEXT] NIL UID 123)\r\n", #line ), @@ -70,11 +94,19 @@ extension Response_Tests { for (test, expectedString, line) in inputs { self.testBuffer.clear() - var encoder = ResponseEncodeBuffer(buffer: self.testBuffer.buffer, options: ResponseEncodingOptions(), loggingMode: false) + var encoder = ResponseEncodeBuffer( + buffer: self.testBuffer.buffer, + options: ResponseEncodingOptions(), + loggingMode: false + ) let size = test.reduce(into: 0) { (size, response) in size += encoder.writeFetchResponse(response) } - self.testBuffer = EncodeBuffer.serverEncodeBuffer(buffer: encoder.readBytes(), options: ResponseEncodingOptions(), loggingMode: false) + self.testBuffer = EncodeBuffer.serverEncodeBuffer( + buffer: encoder.readBytes(), + options: ResponseEncodingOptions(), + loggingMode: false + ) XCTAssertEqual(size, expectedString.utf8.count, line: line) XCTAssertEqual(self.testBufferString, expectedString, line: line) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeAppend+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeAppend+Tests.swift index ec70d1405..80c4b26de 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeAppend+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeAppend+Tests.swift @@ -23,7 +23,7 @@ class ResponseCodeAppend_Tests: EncodeTestClass {} extension ResponseCodeAppend_Tests { func testEncode() { let inputs: [(ResponseCodeAppend, String, UInt)] = [ - (.init(uidValidity: 1, uids: [MessageIdentifierRange(.max)]), "APPENDUID 1 *", #line), + (.init(uidValidity: 1, uids: [MessageIdentifierRange(.max)]), "APPENDUID 1 *", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeResponseCodeAppend($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeCopy+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeCopy+Tests.swift index 6b3c3526d..506140d35 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeCopy+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseCodeCopy+Tests.swift @@ -23,7 +23,13 @@ class ResponseCodeCopy_Tests: EncodeTestClass {} extension ResponseCodeCopy_Tests { func testEncode() { let inputs: [(ResponseCodeCopy, String, UInt)] = [ - (.init(destinationUIDValidity: 1, sourceUIDs: [MessageIdentifierRange(.max)], destinationUIDs: [MessageIdentifierRange(.max)]), "COPYUID 1 * *", #line), + ( + .init( + destinationUIDValidity: 1, + sourceUIDs: [MessageIdentifierRange(.max)], + destinationUIDs: [MessageIdentifierRange(.max)] + ), "COPYUID 1 * *", #line + ) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeResponseCodeCopy($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseData+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseData+Tests.swift index 6f4e02640..07f1a1e70 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseData+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseData+Tests.swift @@ -23,7 +23,7 @@ class ResponseDataTests: EncodeTestClass {} extension ResponseDataTests { func testEncode() { let inputs: [(ResponsePayload, String, UInt)] = [ - (.messageData(.expunge(3)), "* 3 EXPUNGE\r\n", #line), + (.messageData(.expunge(3)), "* 3 EXPUNGE\r\n", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseFatalTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseFatalTests.swift index 22ec45f23..9f5874d0d 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseFatalTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseFatalTests.swift @@ -23,7 +23,7 @@ class ResponseFatalTests: EncodeTestClass {} extension ResponseFatalTests { func testEncode() { let inputs: [(ResponseText, String, UInt)] = [ - (.init(code: .alert, text: "error"), "* BYE [ALERT] error\r\n", #line), + (.init(code: .alert, text: "error"), "* BYE [ALERT] error\r\n", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseText+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseText+Tests.swift index 82606d61e..89c2c132b 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseText+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseText+Tests.swift @@ -34,9 +34,13 @@ extension ResponseText_Tests { } func testDebugDescription() { - XCTAssertEqual(ResponseText(code: nil, text: "buffer").debugDescription, - "buffer") - XCTAssertEqual(ResponseText(code: .alert, text: "buffer").debugDescription, - "[ALERT] buffer") + XCTAssertEqual( + ResponseText(code: nil, text: "buffer").debugDescription, + "buffer" + ) + XCTAssertEqual( + ResponseText(code: .alert, text: "buffer").debugDescription, + "[ALERT] buffer" + ) } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseTextCodeTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseTextCodeTests.swift index 32ec32d88..39c24e627 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseTextCodeTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/ResponseTextCodeTests.swift @@ -46,9 +46,12 @@ extension ResponseTextCodeTests { (.metadataNoPrivate, "METADATA NOPRIVATE", #line), (.metadataTooMany, "METADATA TOOMANY", #line), (.modified(.range(MessageIdentifierRange(1))), "MODIFIED 1", #line), - (.modified(MessageIdentifierSetNonEmpty(range: 23 ... 873)), "MODIFIED 23:873", #line), + (.modified(MessageIdentifierSetNonEmpty(range: 23...873)), "MODIFIED 23:873", #line), (.modified(MessageIdentifierSetNonEmpty(set: [45, 77])!), "MODIFIED 45,77", #line), - (.namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), "NAMESPACE NIL NIL NIL", #line), + ( + .namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), + "NAMESPACE NIL NIL NIL", #line + ), (.noModificationSequence, "NOMODSEQ", #line), (.noPermission, "NOPERM", #line), (.nonExistent, "NONEXISTENT", #line), @@ -72,7 +75,11 @@ extension ResponseTextCodeTests { (.uidValidity(234), "UIDVALIDITY 234", #line), (.unavailable, "UNAVAILABLE", #line), (.unseen(345), "UNSEEN 345", #line), - (.urlMechanisms([.init(mechanism: .init("m1"), base64: "b1"), .init(mechanism: .init("m2"), base64: "b2")]), "URLMECH INTERNAL m1=b1 m2=b2", #line), + ( + .urlMechanisms([ + .init(mechanism: .init("m1"), base64: "b1"), .init(mechanism: .init("m2"), base64: "b2"), + ]), "URLMECH INTERNAL m1=b1 m2=b2", #line + ), (.urlMechanisms([.init(mechanism: .internal, base64: "test")]), "URLMECH INTERNAL INTERNAL=test", #line), (.urlMechanisms([.init(mechanism: .internal, base64: nil)]), "URLMECH INTERNAL INTERNAL", #line), (.urlMechanisms([]), "URLMECH INTERNAL", #line), diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/TaggedResponse+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/TaggedResponse+Tests.swift index 3f919c4f4..02669fd56 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/TaggedResponse+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/TaggedResponse+Tests.swift @@ -23,7 +23,10 @@ class TaggedResponse_Tests: EncodeTestClass {} extension TaggedResponse_Tests { func testEncode() { let inputs: [(TaggedResponse, String, UInt)] = [ - (TaggedResponse(tag: "tag", state: .bad(.init(code: .parse, text: "something"))), "tag BAD [PARSE] something\r\n", #line), + ( + TaggedResponse(tag: "tag", state: .bad(.init(code: .parse, text: "something"))), + "tag BAD [PARSE] something\r\n", #line + ) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Response/UntaggedStatus+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Response/UntaggedStatus+Tests.swift index 2702fd498..7106a603b 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Response/UntaggedStatus+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Response/UntaggedStatus+Tests.swift @@ -26,8 +26,14 @@ extension UntaggedStatus_Tests { (.ok(.init(code: .alert, text: "error")), "OK [ALERT] error", #line), (.no(.init(code: .readOnly, text: "everything")), "NO [READ-ONLY] everything", #line), (.bad(.init(code: .parse, text: "something")), "BAD [PARSE] something", #line), - (.preauth(.init(code: .capability([.uidPlus]), text: "logged in as Smith")), "PREAUTH [CAPABILITY UIDPLUS] logged in as Smith", #line), - (.bye(.init(code: .alert, text: "Autologout; idle for too long")), "BYE [ALERT] Autologout; idle for too long", #line), + ( + .preauth(.init(code: .capability([.uidPlus]), text: "logged in as Smith")), + "PREAUTH [CAPABILITY UIDPLUS] logged in as Smith", #line + ), + ( + .bye(.init(code: .alert, text: "Autologout; idle for too long")), + "BYE [ALERT] Autologout; idle for too long", #line + ), (.ok(.init(text: "error")), "OK error", #line), (.no(.init(text: "everything")), "NO everything", #line), diff --git a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchCorrelator+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchCorrelator+Tests.swift index c080adea2..4e4fc535e 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchCorrelator+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchCorrelator+Tests.swift @@ -24,7 +24,10 @@ extension SearchCorrelator_Tests { func testEncode() { let inputs: [(SearchCorrelator, String, UInt)] = [ (SearchCorrelator(tag: "some"), " (TAG \"some\")", #line), - (SearchCorrelator(tag: "some", mailbox: MailboxName("mb"), uidValidity: 5), " (TAG \"some\" MAILBOX \"mb\" UIDVALIDITY 5)", #line), + ( + SearchCorrelator(tag: "some", mailbox: MailboxName("mb"), uidValidity: 5), + " (TAG \"some\" MAILBOX \"mb\" UIDVALIDITY 5)", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchKeyTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchKeyTests.swift index aee6c9fd7..3710f923f 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchKeyTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchKeyTests.swift @@ -57,8 +57,11 @@ extension SearchKeyTests { (.sentBefore(IMAPCalendarDay(year: 2018, month: 12, day: 7)!), "SENTBEFORE 7-Dec-2018", #line), (.sentSince(IMAPCalendarDay(year: 2018, month: 12, day: 7)!), "SENTSINCE 7-Dec-2018", #line), (.messageSizeSmaller(555), "SMALLER 555", #line), - (.uid(.set(MessageIdentifierSetNonEmpty(set: MessageIdentifierSet(333 ... 444))!)), "UID 333:444", #line), - (.sequenceNumbers(.range(1 ... 222)), "1:222", #line), + ( + .uid(.set(MessageIdentifierSetNonEmpty(set: MessageIdentifierSet(333...444))!)), "UID 333:444", + #line + ), + (.sequenceNumbers(.range(1...222)), "1:222", #line), (.sequenceNumbers(.range(222...)), "222:*", #line), (.and([]), "()", #line), (.and([.messageSizeSmaller(444), .messageSizeLarger(333)]), "SMALLER 444 LARGER 333", #line), @@ -73,8 +76,14 @@ extension SearchKeyTests { (.not(.and([.messageSizeSmaller(444)])), "NOT SMALLER 444", #line), (.not(.and([.messageSizeSmaller(444), .messageSizeLarger(333)])), "NOT (SMALLER 444 LARGER 333)", #line), (.or(.not(.messageSizeSmaller(444)), .messageSizeLarger(333)), "OR (NOT SMALLER 444) LARGER 333", #line), - (.or(.not(.and([.messageSizeSmaller(444), .messageSizeLarger(333)])), .undeleted), "OR (NOT (SMALLER 444 LARGER 333)) UNDELETED", #line), - (.and([.or(.messageSizeSmaller(444), .messageSizeLarger(333)), .undeleted]), "(OR SMALLER 444 LARGER 333) UNDELETED", #line), + ( + .or(.not(.and([.messageSizeSmaller(444), .messageSizeLarger(333)])), .undeleted), + "OR (NOT (SMALLER 444 LARGER 333)) UNDELETED", #line + ), + ( + .and([.or(.messageSizeSmaller(444), .messageSizeLarger(333)), .undeleted]), + "(OR SMALLER 444 LARGER 333) UNDELETED", #line + ), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchModifiedSequence+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchModifiedSequence+Tests.swift index d26f4f60b..eb3ccddfc 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchModifiedSequence+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchModifiedSequence+Tests.swift @@ -25,17 +25,23 @@ extension SearchModifiedSequence_Tests { let inputs: [(SearchModificationSequence, String, UInt)] = [ (.init(extensions: [:], sequenceValue: 1), "MODSEQ 1", #line), ( - .init(extensions: [ - .init(flag: .answered): .all, - ], sequenceValue: .init(integerLiteral: 1)), + .init( + extensions: [ + .init(flag: .answered): .all + ], + sequenceValue: .init(integerLiteral: 1) + ), "MODSEQ \"/flags/\\\\answered\" all 1", #line ), ( - .init(extensions: [ - .init(flag: .answered): .all, - .init(flag: .seen): .private, - ], sequenceValue: .init(integerLiteral: 1)), + .init( + extensions: [ + .init(flag: .answered): .all, + .init(flag: .seen): .private, + ], + sequenceValue: .init(integerLiteral: 1) + ), "MODSEQ \"/flags/\\\\answered\" all \"/flags/\\\\seen\" priv 1", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnData+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnData+Tests.swift index 44164e053..c8bbe7aa8 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnData+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnData+Tests.swift @@ -25,11 +25,11 @@ extension SearchReturnData_Tests { let inputs: [(SearchReturnData, String, UInt)] = [ (.min(1), "MIN 1", #line), (.max(1), "MAX 1", #line), - (.all(LastCommandSet.range(1 ... 3)), "ALL 1:3", #line), + (.all(LastCommandSet.range(1...3)), "ALL 1:3", #line), (.count(1), "COUNT 1", #line), (.modificationSequence(1), "MODSEQ 1", #line), - (.partial(.first(23_500 ... 24_000), [67, 100 ... 102]), "PARTIAL (23500:24000 67,100:102)", #line), - (.partial(.last(55 ... 700), []), "PARTIAL (-55:-700 NIL)", #line), + (.partial(.first(23_500...24_000), [67, 100...102]), "PARTIAL (23500:24000 67,100:102)", #line), + (.partial(.last(55...700), []), "PARTIAL (-55:-700 NIL)", #line), (.dataExtension(.init(key: "modifier", value: .sequence(.set([3])))), "modifier 3", #line), ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeSearchReturnData($0) }) diff --git a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnDataExtension+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnDataExtension+Tests.swift index 4bab8ec8c..8794b4bb4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnDataExtension+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnDataExtension+Tests.swift @@ -23,7 +23,7 @@ class SearchReturnDataExtension_Tests: EncodeTestClass {} extension SearchReturnDataExtension_Tests { func testEncode() { let inputs: [(KeyValue, String, UInt)] = [ - (.init(key: "modifier", value: .sequence(.set([123]))), "modifier 123", #line), + (.init(key: "modifier", value: .sequence(.set([123]))), "modifier 123", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnOption+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnOption+Tests.swift index 776c1276a..e048ce443 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnOption+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Search/SearchReturnOption+Tests.swift @@ -29,8 +29,8 @@ extension SearchReturnOption_Tests { (.count, "COUNT", #line), (.save, "SAVE", #line), (.optionExtension(.init(key: "modifier", value: nil)), "modifier", #line), - (.partial(.first(23_500 ... 24_000)), "PARTIAL 23500:24000", #line), - (.partial(.last(1 ... 100)), "PARTIAL -1:-100", #line), + (.partial(.first(23_500...24_000)), "PARTIAL 23500:24000", #line), + (.partial(.last(1...100)), "PARTIAL -1:-100", #line), ] for (test, expectedString, line) in inputs { @@ -48,7 +48,7 @@ extension SearchReturnOption_Tests { ([.all], " RETURN (ALL)", #line), ([.min, .all], " RETURN (MIN ALL)", #line), ([.min, .max, .count], " RETURN (MIN MAX COUNT)", #line), - ([.min, .partial(.last(400 ... 1_000))], " RETURN (MIN PARTIAL -400:-1000)", #line), + ([.min, .partial(.last(400...1_000))], " RETURN (MIN PARTIAL -400:-1000)", #line), ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/SelectParameter+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/SelectParameter+Tests.swift index bd5594d8d..718fabeed 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/SelectParameter+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/SelectParameter+Tests.swift @@ -27,22 +27,40 @@ extension SelectParameter_Tests { (.basic(.init(key: "test", value: nil)), "test", #line), (.basic(.init(key: "test", value: .sequence(.set([1])))), "test 1", #line), ( - .qresync(.init(uidValidity: 1, modificationSequenceValue: .zero, knownUIDs: nil, sequenceMatchData: nil)), + .qresync( + .init(uidValidity: 1, modificationSequenceValue: .zero, knownUIDs: nil, sequenceMatchData: nil) + ), "QRESYNC (1 0)", #line ), ( - .qresync(.init(uidValidity: 1, modificationSequenceValue: .zero, knownUIDs: [1], sequenceMatchData: nil)), + .qresync( + .init(uidValidity: 1, modificationSequenceValue: .zero, knownUIDs: [1], sequenceMatchData: nil) + ), "QRESYNC (1 0 1)", #line ), ( - .qresync(.init(uidValidity: 1, modificationSequenceValue: .zero, knownUIDs: nil, sequenceMatchData: .init(knownSequenceSet: .set(.all), knownUidSet: .set(.all)))), + .qresync( + .init( + uidValidity: 1, + modificationSequenceValue: .zero, + knownUIDs: nil, + sequenceMatchData: .init(knownSequenceSet: .set(.all), knownUidSet: .set(.all)) + ) + ), "QRESYNC (1 0 (1:* 1:*))", #line ), ( - .qresync(.init(uidValidity: 1, modificationSequenceValue: .zero, knownUIDs: [1], sequenceMatchData: .init(knownSequenceSet: .set(.all), knownUidSet: .set(.all)))), + .qresync( + .init( + uidValidity: 1, + modificationSequenceValue: .zero, + knownUIDs: [1], + sequenceMatchData: .init(knownSequenceSet: .set(.all), knownUidSet: .set(.all)) + ) + ), "QRESYNC (1 0 1 (1:* 1:*))", #line ), diff --git a/Tests/NIOIMAPCoreTests/Grammar/Sequence/SequenceNumberTests.swift b/Tests/NIOIMAPCoreTests/Grammar/Sequence/SequenceNumberTests.swift index 0aa7f9537..9526bb062 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/Sequence/SequenceNumberTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/Sequence/SequenceNumberTests.swift @@ -41,7 +41,7 @@ extension SequenceNumberTests { XCTAssertFalse(SequenceNumber.max < .max) XCTAssertFalse(SequenceNumber.max < 999) XCTAssertTrue(SequenceNumber.max > 999) - XCTAssertTrue(SequenceNumber(1) < 999) // use .number to force type + XCTAssertTrue(SequenceNumber(1) < 999) // use .number to force type } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/TaggedExtension/TaggedExtension+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/TaggedExtension/TaggedExtension+Tests.swift index 3151a687c..d5ff8bfe4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/TaggedExtension/TaggedExtension+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/TaggedExtension/TaggedExtension+Tests.swift @@ -23,7 +23,7 @@ class TaggedExtension_Tests: EncodeTestClass {} extension TaggedExtension_Tests { func testEncode() { let inputs: [(KeyValue, String, UInt)] = [ - (.init(key: "label", value: .sequence(.set([1]))), "label 1", #line), + (.init(key: "label", value: .sequence(.set([1]))), "label 1", #line) ] for (test, expectedString, line) in inputs { diff --git a/Tests/NIOIMAPCoreTests/Grammar/UID/IUID+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/UID/IUID+Tests.swift index d29ea18e8..113d55052 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/UID/IUID+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/UID/IUID+Tests.swift @@ -23,14 +23,14 @@ class IUID_Tests: EncodeTestClass {} extension IUID_Tests { func testEncode_IUID() { let inputs: [(IUID, String, UInt)] = [ - (.init(uid: 123), "/;UID=123", #line), + (.init(uid: 123), "/;UID=123", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeIUID($0) }) } func testEncode_IUIDOnly() { let inputs: [(IUID, String, UInt)] = [ - (.init(uid: 123), ";UID=123", #line), + (.init(uid: 123), ";UID=123", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeIUIDOnly($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDRangeTests.swift b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDRangeTests.swift index f01fb2a21..04a7153db 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDRangeTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDRangeTests.swift @@ -44,7 +44,7 @@ extension UIDRangeTests { // here we always expect the smaller number on the left func testInit_range() { - let range = MessageIdentifierRange(1 ... 999).range + let range = MessageIdentifierRange(1...999).range XCTAssertEqual(range.lowerBound, 1) XCTAssertEqual(range.upperBound, 999) } @@ -60,75 +60,89 @@ extension UIDRangeTests { extension UIDRangeTests { func testCount() { - XCTAssertEqual(MessageIdentifierRange(654 ... 654).count, 1) + XCTAssertEqual(MessageIdentifierRange(654...654).count, 1) XCTAssertEqual(MessageIdentifierRange(654).count, 1) - XCTAssertEqual(MessageIdentifierRange(654 ... 655).count, 2) - XCTAssertEqual(MessageIdentifierRange(UID.min ... UID.max).count, 4_294_967_295) - XCTAssertEqual(MessageIdentifierRange(777 ... 999).count, 223) + XCTAssertEqual(MessageIdentifierRange(654...655).count, 2) + XCTAssertEqual(MessageIdentifierRange(UID.min...UID.max).count, 4_294_967_295) + XCTAssertEqual(MessageIdentifierRange(777...999).count, 223) } func testBounds() { XCTAssertEqual(MessageIdentifierRange(654).lowerBound, 654) XCTAssertEqual(MessageIdentifierRange(654).upperBound, 654) - XCTAssertEqual(MessageIdentifierRange(777 ... 999).lowerBound, 777) - XCTAssertEqual(MessageIdentifierRange(777 ... 999).upperBound, 999) + XCTAssertEqual(MessageIdentifierRange(777...999).lowerBound, 777) + XCTAssertEqual(MessageIdentifierRange(777...999).upperBound, 999) } func testIsEmpty() { - XCTAssertFalse(MessageIdentifierRange(654 ... 654).isEmpty) + XCTAssertFalse(MessageIdentifierRange(654...654).isEmpty) XCTAssertFalse(MessageIdentifierRange(654).isEmpty) - XCTAssertFalse(MessageIdentifierRange(654 ... 655).isEmpty) - XCTAssertFalse(MessageIdentifierRange(UID.min ... UID.max).isEmpty) + XCTAssertFalse(MessageIdentifierRange(654...655).isEmpty) + XCTAssertFalse(MessageIdentifierRange(UID.min...UID.max).isEmpty) } func testClamping() { XCTAssertEqual( - MessageIdentifierRange(654 ... 655) - .clamped(to: MessageIdentifierRange(654 ... 655)), - MessageIdentifierRange(654 ... 655) + MessageIdentifierRange(654...655) + .clamped(to: MessageIdentifierRange(654...655)), + MessageIdentifierRange(654...655) ) XCTAssertEqual( - MessageIdentifierRange(654 ... 655) - .clamped(to: MessageIdentifierRange(UID.min ... UID.max)), - MessageIdentifierRange(654 ... 655) + MessageIdentifierRange(654...655) + .clamped(to: MessageIdentifierRange(UID.min...UID.max)), + MessageIdentifierRange(654...655) ) XCTAssertEqual( - MessageIdentifierRange(UID.min ... UID.max) - .clamped(to: MessageIdentifierRange(654 ... 655)), - MessageIdentifierRange(654 ... 655) + MessageIdentifierRange(UID.min...UID.max) + .clamped(to: MessageIdentifierRange(654...655)), + MessageIdentifierRange(654...655) ) XCTAssertEqual( - MessageIdentifierRange(654 ... 655) - .clamped(to: MessageIdentifierRange(100 ... 200)), + MessageIdentifierRange(654...655) + .clamped(to: MessageIdentifierRange(100...200)), MessageIdentifierRange(200) ) } func testOverlaps() { - XCTAssert(MessageIdentifierRange(654 ... 655) - .overlaps(MessageIdentifierRange(654 ... 655))) - XCTAssert(MessageIdentifierRange(654 ... 655) - .overlaps(MessageIdentifierRange(600 ... 700))) - XCTAssert(MessageIdentifierRange(654 ... 655) - .overlaps(MessageIdentifierRange(600 ... 654))) - XCTAssert(MessageIdentifierRange(600 ... 700) - .overlaps(MessageIdentifierRange(654 ... 655))) - XCTAssert(MessageIdentifierRange(654 ... 655) - .overlaps(MessageIdentifierRange(UID.min ... UID.max))) - XCTAssertFalse(MessageIdentifierRange(100 ... 600) - .overlaps(MessageIdentifierRange(654 ... 655))) - XCTAssertFalse(MessageIdentifierRange(654 ... 655) - .overlaps(MessageIdentifierRange(100 ... 600))) + XCTAssert( + MessageIdentifierRange(654...655) + .overlaps(MessageIdentifierRange(654...655)) + ) + XCTAssert( + MessageIdentifierRange(654...655) + .overlaps(MessageIdentifierRange(600...700)) + ) + XCTAssert( + MessageIdentifierRange(654...655) + .overlaps(MessageIdentifierRange(600...654)) + ) + XCTAssert( + MessageIdentifierRange(600...700) + .overlaps(MessageIdentifierRange(654...655)) + ) + XCTAssert( + MessageIdentifierRange(654...655) + .overlaps(MessageIdentifierRange(UID.min...UID.max)) + ) + XCTAssertFalse( + MessageIdentifierRange(100...600) + .overlaps(MessageIdentifierRange(654...655)) + ) + XCTAssertFalse( + MessageIdentifierRange(654...655) + .overlaps(MessageIdentifierRange(100...600)) + ) } func testContains() { - XCTAssert(MessageIdentifierRange(654 ... 655).contains(654)) - XCTAssert(MessageIdentifierRange(654 ... 655).contains(654)) - XCTAssertFalse(MessageIdentifierRange(654 ... 655).contains(653)) - XCTAssertFalse(MessageIdentifierRange(654 ... 655).contains(656)) - XCTAssertFalse(MessageIdentifierRange(654 ... 655).contains(UID.min)) - XCTAssertFalse(MessageIdentifierRange(654 ... 655).contains(UID.max)) + XCTAssert(MessageIdentifierRange(654...655).contains(654)) + XCTAssert(MessageIdentifierRange(654...655).contains(654)) + XCTAssertFalse(MessageIdentifierRange(654...655).contains(653)) + XCTAssertFalse(MessageIdentifierRange(654...655).contains(656)) + XCTAssertFalse(MessageIdentifierRange(654...655).contains(UID.min)) + XCTAssertFalse(MessageIdentifierRange(654...655).contains(UID.max)) } } @@ -137,7 +151,7 @@ extension UIDRangeTests { extension UIDRangeTests { func testEncode() { let inputs: [(MessageIdentifierRange, String, UInt)] = [ - (33 ... 44, "33:44", #line), + (33...44, "33:44", #line), (5, "5", #line), (MessageIdentifierRange(.max), "*", #line), (.all, "1:*", #line), @@ -160,21 +174,21 @@ extension UIDRangeTests { extension UIDRangeTests { func testRangeOperator_prefix() { let expected = "5:*" - let size = self.testBuffer.writeMessageIdentifierRange(MessageIdentifierRange(5 ... (.max))) + let size = self.testBuffer.writeMessageIdentifierRange(MessageIdentifierRange(5...(.max))) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } func testRangeOperator_postfix() { let expected = "5:*" - let size = self.testBuffer.writeMessageIdentifierRange(MessageIdentifierRange(5 ... (.max))) + let size = self.testBuffer.writeMessageIdentifierRange(MessageIdentifierRange(5...(.max))) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } func testRangeOperator_postfix_complete_right_larger() { let expected = "44:55" - let size = self.testBuffer.writeMessageIdentifierRange(MessageIdentifierRange(44 ... 55)) + let size = self.testBuffer.writeMessageIdentifierRange(MessageIdentifierRange(44...55)) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetNonEmptyTests.swift b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetNonEmptyTests.swift index f607961ff..ad313c830 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetNonEmptyTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetNonEmptyTests.swift @@ -23,15 +23,15 @@ class UIDSetNonEmptyTests: EncodeTestClass {} extension UIDSetNonEmptyTests { func testInitWithSet() { XCTAssertEqual( - MessageIdentifierSetNonEmpty(set: MessageIdentifierSet([6, 100 ... 108]))?.set, - MessageIdentifierSet([6, 100 ... 108]) + MessageIdentifierSetNonEmpty(set: MessageIdentifierSet([6, 100...108]))?.set, + MessageIdentifierSet([6, 100...108]) ) } func testInitWithRange() { XCTAssertEqual( - MessageIdentifierSetNonEmpty(range: MessageIdentifierRange(100 ... 108)).set, - MessageIdentifierSet([100 ... 108]) + MessageIdentifierSetNonEmpty(range: MessageIdentifierRange(100...108)).set, + MessageIdentifierSet([100...108]) ) XCTAssertEqual( @@ -54,13 +54,15 @@ extension UIDSetNonEmptyTests { extension UIDSetNonEmptyTests { func testIMAPEncoded_full() { let expected = "1,22:30,47,55,66:*" - let size = self.testBuffer.writeUIDSet(MessageIdentifierSetNonEmpty(set: [ - 1, - 22 ... 30, - 47, - 55, - 66..., - ])!) + let size = self.testBuffer.writeUIDSet( + MessageIdentifierSetNonEmpty(set: [ + 1, + 22...30, + 47, + 55, + 66..., + ])! + ) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } @@ -76,13 +78,13 @@ extension UIDSetNonEmptyTests { XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55, 66])!.min(), 55) XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55, 66])!.max(), 66) - XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55 ... 66])!.min(), 55) - XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55 ... 66])!.max(), 66) + XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55...66])!.min(), 55) + XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55...66])!.max(), 66) - XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [44, 55 ... 66])!.min(), 44) - XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [44, 55 ... 66])!.max(), 66) + XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [44, 55...66])!.min(), 44) + XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [44, 55...66])!.max(), 66) - XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55 ... 66, 77])!.min(), 55) - XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55 ... 66, 77])!.max(), 77) + XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55...66, 77])!.min(), 55) + XCTAssertEqual(MessageIdentifierSetNonEmpty(set: [55...66, 77])!.max(), 77) } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetTests.swift b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetTests.swift index fa204ed6b..0986b4601 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDSetTests.swift @@ -22,7 +22,10 @@ class UIDSetTests: EncodeTestClass {} extension UIDSetTests { func testCustomDebugStringConvertible() { - XCTAssertEqual("\([1 ... 3, MessageIdentifierRange(6), MessageIdentifierRange(88)] as MessageIdentifierSet)", "1:3,6,88") + XCTAssertEqual( + "\([1 ... 3, MessageIdentifierRange(6), MessageIdentifierRange(88)] as MessageIdentifierSet)", + "1:3,6,88" + ) XCTAssertEqual("\([1 ... (UID.max)] as MessageIdentifierSet)", "1:*") XCTAssertEqual("\([MessageIdentifierRange(37)] as MessageIdentifierSet)", "37") XCTAssertEqual("\([MessageIdentifierRange(.max)] as MessageIdentifierSet)", "*") @@ -34,14 +37,14 @@ extension UIDSetTests { extension UIDSetTests { func testIMAPEncoded_one() { let expected = "22" - let size = self.testBuffer.writeUIDSet(MessageIdentifierSet(22 ... 22)) + let size = self.testBuffer.writeUIDSet(MessageIdentifierSet(22...22)) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } func testIMAPEncoded_oneRange() { let expected = "5:22" - let size = self.testBuffer.writeUIDSet(MessageIdentifierSet(5 ... 22)) + let size = self.testBuffer.writeUIDSet(MessageIdentifierSet(5...22)) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } @@ -55,20 +58,22 @@ extension UIDSetTests { func testIMAPEncoded_almostAll() { let expected = "1:4294967294" - let size = self.testBuffer.writeUIDSet(MessageIdentifierSet(1 ... 4_294_967_294)) + let size = self.testBuffer.writeUIDSet(MessageIdentifierSet(1...4_294_967_294)) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } func testIMAPEncoded_full() { let expected = "1,22:30,47,55,66:*" - let size = self.testBuffer.writeUIDSet(MessageIdentifierSet([ - MessageIdentifierRange(1), - MessageIdentifierRange(22 ... 30), - MessageIdentifierRange(47), - MessageIdentifierRange(55), - MessageIdentifierRange(66...), - ])) + let size = self.testBuffer.writeUIDSet( + MessageIdentifierSet([ + MessageIdentifierRange(1), + MessageIdentifierRange(22...30), + MessageIdentifierRange(47), + MessageIdentifierRange(55), + MessageIdentifierRange(66...), + ]) + ) XCTAssertEqual(size, expected.utf8.count) XCTAssertEqual(expected, self.testBufferString) } @@ -87,14 +92,14 @@ extension UIDSetTests { XCTAssertEqual(MessageIdentifierSet([55, 66]).min(), 55) XCTAssertEqual(MessageIdentifierSet([55, 66]).max(), 66) - XCTAssertEqual(MessageIdentifierSet([55 ... 66]).min(), 55) - XCTAssertEqual(MessageIdentifierSet([55 ... 66]).max(), 66) + XCTAssertEqual(MessageIdentifierSet([55...66]).min(), 55) + XCTAssertEqual(MessageIdentifierSet([55...66]).max(), 66) - XCTAssertEqual(MessageIdentifierSet([44, 55 ... 66]).min(), 44) - XCTAssertEqual(MessageIdentifierSet([44, 55 ... 66]).max(), 66) + XCTAssertEqual(MessageIdentifierSet([44, 55...66]).min(), 44) + XCTAssertEqual(MessageIdentifierSet([44, 55...66]).max(), 66) - XCTAssertEqual(MessageIdentifierSet([55 ... 66, 77]).min(), 55) - XCTAssertEqual(MessageIdentifierSet([55 ... 66, 77]).max(), 77) + XCTAssertEqual(MessageIdentifierSet([55...66, 77]).min(), 55) + XCTAssertEqual(MessageIdentifierSet([55...66, 77]).max(), 77) } } @@ -102,12 +107,12 @@ extension UIDSetTests { extension UIDSetTests { func testContains() { - XCTAssertFalse(MessageIdentifierSet(20 ... 22).contains(19)) - XCTAssert(MessageIdentifierSet(20 ... 22).contains(20)) - XCTAssert(MessageIdentifierSet(20 ... 22).contains(21)) - XCTAssert(MessageIdentifierSet(20 ... 22).contains(22)) - XCTAssertFalse(MessageIdentifierSet(20 ... 22).contains(23)) - XCTAssertFalse(MessageIdentifierSet(20 ... 22).contains(.max)) + XCTAssertFalse(MessageIdentifierSet(20...22).contains(19)) + XCTAssert(MessageIdentifierSet(20...22).contains(20)) + XCTAssert(MessageIdentifierSet(20...22).contains(21)) + XCTAssert(MessageIdentifierSet(20...22).contains(22)) + XCTAssertFalse(MessageIdentifierSet(20...22).contains(23)) + XCTAssertFalse(MessageIdentifierSet(20...22).contains(.max)) XCTAssert(MessageIdentifierSet.all.contains(1)) XCTAssert(MessageIdentifierSet.all.contains(.max)) @@ -116,8 +121,8 @@ extension UIDSetTests { func testIsContiguous() { XCTAssert(MessageIdentifierSet.empty.isContiguous) XCTAssert(MessageIdentifierSet(20 as UID).isContiguous) - XCTAssert(MessageIdentifierSet(20 ... 22).isContiguous) - XCTAssertFalse(MessageIdentifierSet([20 ... 22, 24 ... 25]).isContiguous) + XCTAssert(MessageIdentifierSet(20...22).isContiguous) + XCTAssertFalse(MessageIdentifierSet([20...22, 24...25]).isContiguous) } func testUnion() { @@ -129,7 +134,10 @@ extension UIDSetTests { XCTAssertEqual("\(MessageIdentifierSet(4 ... 39).union(MessageIdentifierSet(20 ... 35)))", "4:39") XCTAssertEqual("\(MessageIdentifierSet.all.union(MessageIdentifierSet(20 ... 35)))", "1:*") XCTAssertEqual("\(MessageIdentifierSet(20 ... 35).union(MessageIdentifierSet.all))", "1:*") - XCTAssertEqual("\(MessageIdentifierSet(20 ... 21).union(MessageIdentifierSet(4_294_967_294 as UID)))", "20:21,4294967294") + XCTAssertEqual( + "\(MessageIdentifierSet(20 ... 21).union(MessageIdentifierSet(4_294_967_294 as UID)))", + "20:21,4294967294" + ) } func testIntersection() { @@ -140,14 +148,29 @@ extension UIDSetTests { XCTAssertEqual("\(MessageIdentifierSet(20 ... 35).intersection(MessageIdentifierSet(30 ... 39)))", "30:35") XCTAssertEqual("\(MessageIdentifierSet.all.intersection(MessageIdentifierSet(20 ... 35)))", "20:35") XCTAssertEqual("\(MessageIdentifierSet(20 ... 35).intersection(MessageIdentifierSet.all))", "20:35") - XCTAssertEqual("\(MessageIdentifierSet.all.intersection(MessageIdentifierSet(2 ... 4_294_967_294)))", "2:4294967294") + XCTAssertEqual( + "\(MessageIdentifierSet.all.intersection(MessageIdentifierSet(2 ... 4_294_967_294)))", + "2:4294967294" + ) } func testSymmetricDifference() { - XCTAssertEqual("\(MessageIdentifierSet(20 as UID).symmetricDifference(MessageIdentifierSet(30 as UID)))", "20,30") - XCTAssertEqual("\(MessageIdentifierSet(20 as UID).symmetricDifference(MessageIdentifierSet(20 as UID)))", "") - XCTAssertEqual("\(MessageIdentifierSet(20 ... 35).symmetricDifference(MessageIdentifierSet(30 ... 39)))", "20:29,36:39") - XCTAssertEqual("\(MessageIdentifierSet(20 ... 35).symmetricDifference(MessageIdentifierSet.all))", "1:19,36:*") + XCTAssertEqual( + "\(MessageIdentifierSet(20 as UID).symmetricDifference(MessageIdentifierSet(30 as UID)))", + "20,30" + ) + XCTAssertEqual( + "\(MessageIdentifierSet(20 as UID).symmetricDifference(MessageIdentifierSet(20 as UID)))", + "" + ) + XCTAssertEqual( + "\(MessageIdentifierSet(20 ... 35).symmetricDifference(MessageIdentifierSet(30 ... 39)))", + "20:29,36:39" + ) + XCTAssertEqual( + "\(MessageIdentifierSet(20 ... 35).symmetricDifference(MessageIdentifierSet.all))", + "1:19,36:*" + ) } func testInsert() { @@ -164,7 +187,7 @@ extension UIDSetTests { } func testRemove_1() { - var sut = MessageIdentifierSet(4 ... 6) + var sut = MessageIdentifierSet(4...6) XCTAssertNil(sut.remove(1)) XCTAssertEqual("\(sut)", "4:6") XCTAssertEqual(sut.count, 3) @@ -175,7 +198,7 @@ extension UIDSetTests { } func testRemove_2() { - var sut = MessageIdentifierSet(1 ... 3) + var sut = MessageIdentifierSet(1...3) XCTAssertEqual(sut.remove(1), 1) XCTAssertEqual("\(sut)", "2:3") XCTAssertEqual(sut.count, 2) @@ -201,117 +224,142 @@ extension UIDSetTests { } func testFormIntersection() { - var sut = MessageIdentifierSet(20 ... 35) - sut.formIntersection(MessageIdentifierSet(30 ... 40)) + var sut = MessageIdentifierSet(20...35) + sut.formIntersection(MessageIdentifierSet(30...40)) XCTAssertEqual("\(sut)", "30:35") } func testFormSymmetricDifference() { - var sut = MessageIdentifierSet(20 ... 35) - sut.formSymmetricDifference(MessageIdentifierSet(30 ... 40)) + var sut = MessageIdentifierSet(20...35) + sut.formSymmetricDifference(MessageIdentifierSet(30...40)) XCTAssertEqual("\(sut)", "20:29,36:40") } func testSubtracting() { - let sut = MessageIdentifierSet(20 ... 35) - let a = sut.subtracting(MessageIdentifierSet(21 ... 24)) + let sut = MessageIdentifierSet(20...35) + let a = sut.subtracting(MessageIdentifierSet(21...24)) XCTAssertEqual("\(sut)", "20:35") XCTAssertEqual("\(a)", "20,25:35") } func testIsSubset() { - XCTAssert(MessageIdentifierSet(20 ... 35) - .isSubset(of: MessageIdentifierSet(20 ... 35)) + XCTAssert( + MessageIdentifierSet(20...35) + .isSubset(of: MessageIdentifierSet(20...35)) ) - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isSubset(of: MessageIdentifierSet(2 ... 3)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isSubset(of: MessageIdentifierSet(2...3)) ) - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isSubset(of: MessageIdentifierSet(24 ... 25)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isSubset(of: MessageIdentifierSet(24...25)) ) - XCTAssertFalse(MessageIdentifierSet(2 ... 3) - .isSubset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(2...3) + .isSubset(of: MessageIdentifierSet(20...35)) ) - XCTAssert(MessageIdentifierSet(24 ... 25) - .isSubset(of: MessageIdentifierSet(20 ... 35)) + XCTAssert( + MessageIdentifierSet(24...25) + .isSubset(of: MessageIdentifierSet(20...35)) ) } func testIsStrictSubset() { - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isStrictSubset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isStrictSubset(of: MessageIdentifierSet(20...35)) ) - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isStrictSubset(of: MessageIdentifierSet(2 ... 3)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isStrictSubset(of: MessageIdentifierSet(2...3)) ) - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isStrictSubset(of: MessageIdentifierSet(24 ... 25)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isStrictSubset(of: MessageIdentifierSet(24...25)) ) - XCTAssertFalse(MessageIdentifierSet(2 ... 3) - .isStrictSubset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(2...3) + .isStrictSubset(of: MessageIdentifierSet(20...35)) ) - XCTAssert(MessageIdentifierSet(24 ... 25) - .isStrictSubset(of: MessageIdentifierSet(20 ... 35)) + XCTAssert( + MessageIdentifierSet(24...25) + .isStrictSubset(of: MessageIdentifierSet(20...35)) ) } func testIsDisjoint() { - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isDisjoint(with: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isDisjoint(with: MessageIdentifierSet(20...35)) ) - XCTAssert(MessageIdentifierSet(20 ... 35) - .isDisjoint(with: MessageIdentifierSet(2 ... 3)) + XCTAssert( + MessageIdentifierSet(20...35) + .isDisjoint(with: MessageIdentifierSet(2...3)) ) - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isDisjoint(with: MessageIdentifierSet(24 ... 25)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isDisjoint(with: MessageIdentifierSet(24...25)) ) - XCTAssert(MessageIdentifierSet(2 ... 3) - .isDisjoint(with: MessageIdentifierSet(20 ... 35)) + XCTAssert( + MessageIdentifierSet(2...3) + .isDisjoint(with: MessageIdentifierSet(20...35)) ) - XCTAssertFalse(MessageIdentifierSet(24 ... 25) - .isDisjoint(with: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(24...25) + .isDisjoint(with: MessageIdentifierSet(20...35)) ) } func testIsSuperset() { - XCTAssert(MessageIdentifierSet(20 ... 35) - .isSuperset(of: MessageIdentifierSet(20 ... 35)) + XCTAssert( + MessageIdentifierSet(20...35) + .isSuperset(of: MessageIdentifierSet(20...35)) ) - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isSuperset(of: MessageIdentifierSet(2 ... 3)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isSuperset(of: MessageIdentifierSet(2...3)) ) - XCTAssert(MessageIdentifierSet(20 ... 35) - .isSuperset(of: MessageIdentifierSet(24 ... 25)) + XCTAssert( + MessageIdentifierSet(20...35) + .isSuperset(of: MessageIdentifierSet(24...25)) ) - XCTAssertFalse(MessageIdentifierSet(2 ... 3) - .isSuperset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(2...3) + .isSuperset(of: MessageIdentifierSet(20...35)) ) - XCTAssertFalse(MessageIdentifierSet(24 ... 25) - .isSuperset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(24...25) + .isSuperset(of: MessageIdentifierSet(20...35)) ) } func testIsStrictSuperset() { - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isStrictSuperset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isStrictSuperset(of: MessageIdentifierSet(20...35)) ) - XCTAssertFalse(MessageIdentifierSet(20 ... 35) - .isStrictSuperset(of: MessageIdentifierSet(2 ... 3)) + XCTAssertFalse( + MessageIdentifierSet(20...35) + .isStrictSuperset(of: MessageIdentifierSet(2...3)) ) - XCTAssert(MessageIdentifierSet(20 ... 35) - .isStrictSuperset(of: MessageIdentifierSet(24 ... 25)) + XCTAssert( + MessageIdentifierSet(20...35) + .isStrictSuperset(of: MessageIdentifierSet(24...25)) ) - XCTAssertFalse(MessageIdentifierSet(2 ... 3) - .isStrictSuperset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(2...3) + .isStrictSuperset(of: MessageIdentifierSet(20...35)) ) - XCTAssertFalse(MessageIdentifierSet(24 ... 25) - .isStrictSuperset(of: MessageIdentifierSet(20 ... 35)) + XCTAssertFalse( + MessageIdentifierSet(24...25) + .isStrictSuperset(of: MessageIdentifierSet(20...35)) ) } func testSubtract() { - var sut = MessageIdentifierSet(20 ... 35) - sut.subtract(MessageIdentifierSet(21 ... 24)) + var sut = MessageIdentifierSet(20...35) + sut.subtract(MessageIdentifierSet(21...24)) XCTAssertEqual("\(sut)", "20,25:35") } @@ -329,21 +377,21 @@ extension UIDSetTests { } func testSingleRangeCollection() { - let sut = MessageIdentifierSet(55 ... 57) + let sut = MessageIdentifierSet(55...57) XCTAssertEqual(sut.map { "\($0)" }, ["55", "56", "57"]) XCTAssertEqual(sut.count, 3) XCTAssertFalse(sut.isEmpty) } func testCollection_A() { - let sut = MessageIdentifierSet([MessageIdentifierRange(55 ... 57), MessageIdentifierRange(80)]) + let sut = MessageIdentifierSet([MessageIdentifierRange(55...57), MessageIdentifierRange(80)]) XCTAssertEqual(sut.map { "\($0)" }, ["55", "56", "57", "80"]) XCTAssertEqual(sut.count, 4) XCTAssertFalse(sut.isEmpty) } func testCollection_B() { - let sut = MessageIdentifierSet([MessageIdentifierRange(8), MessageIdentifierRange(55 ... 57)]) + let sut = MessageIdentifierSet([MessageIdentifierRange(8), MessageIdentifierRange(55...57)]) XCTAssertEqual(sut.map { "\($0)" }, ["8", "55", "56", "57"]) XCTAssertEqual(sut.count, 4) XCTAssertFalse(sut.isEmpty) @@ -352,29 +400,49 @@ extension UIDSetTests { extension UIDSetTests { func testIndexes_singleRange() { - let sut = MessageIdentifierSet(40 ... 89) - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: 4), offsetBy: 6)) - XCTAssertEqual(sut.index(sut.index(sut.startIndex, offsetBy: 33), offsetBy: -33), - sut.startIndex) - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 50), - sut.endIndex) - XCTAssertEqual(sut.index(sut.endIndex, offsetBy: -50), - sut.startIndex) - - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: 110), offsetBy: -100)) - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: -100), offsetBy: 110)) - - XCTAssertGreaterThan(sut.index(sut.startIndex, offsetBy: 51), - sut.endIndex) - XCTAssertGreaterThan(sut.index(sut.startIndex, offsetBy: 52), - sut.index(sut.startIndex, offsetBy: 51)) - XCTAssertLessThan(sut.index(sut.endIndex, offsetBy: -51), - sut.startIndex) - XCTAssertLessThan(sut.index(sut.endIndex, offsetBy: -52), - sut.index(sut.endIndex, offsetBy: -51)) + let sut = MessageIdentifierSet(40...89) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: 4), offsetBy: 6) + ) + XCTAssertEqual( + sut.index(sut.index(sut.startIndex, offsetBy: 33), offsetBy: -33), + sut.startIndex + ) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 50), + sut.endIndex + ) + XCTAssertEqual( + sut.index(sut.endIndex, offsetBy: -50), + sut.startIndex + ) + + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: 110), offsetBy: -100) + ) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: -100), offsetBy: 110) + ) + + XCTAssertGreaterThan( + sut.index(sut.startIndex, offsetBy: 51), + sut.endIndex + ) + XCTAssertGreaterThan( + sut.index(sut.startIndex, offsetBy: 52), + sut.index(sut.startIndex, offsetBy: 51) + ) + XCTAssertLessThan( + sut.index(sut.endIndex, offsetBy: -51), + sut.startIndex + ) + XCTAssertLessThan( + sut.index(sut.endIndex, offsetBy: -52), + sut.index(sut.endIndex, offsetBy: -51) + ) XCTAssertEqual(sut.index(sut.endIndex, offsetBy: -50, limitedBy: sut.startIndex), sut.startIndex) XCTAssertNil(sut.index(sut.endIndex, offsetBy: -51, limitedBy: sut.startIndex)) @@ -386,39 +454,60 @@ extension UIDSetTests { func testIndexes_multipleSingleValues() { let sut: MessageIdentifierSet = { var sut = MessageIdentifierSet() - for uid in [762 as UID, 7370, 8568, 11423, 11708, 11889, 12679, - 18833, 22152, 22374, 22733, 23838, 30058, 30985, 32465, - 33579, 39714, 43224, 44377, 46424, 53884, 61461, 71310, - 75310, 77045, 81983, 82711, 85170, 95660, 99173] - { + for uid in [ + 762 as UID, 7370, 8568, 11423, 11708, 11889, 12679, + 18833, 22152, 22374, 22733, 23838, 30058, 30985, 32465, + 33579, 39714, 43224, 44377, 46424, 53884, 61461, 71310, + 75310, 77045, 81983, 82711, 85170, 95660, 99173, + ] { sut.insert(uid) } return sut }() XCTAssertEqual(sut.count, 30, "30 values") - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: 4), offsetBy: 6)) - XCTAssertEqual(sut.index(sut.index(sut.startIndex, offsetBy: 17), offsetBy: -17), - sut.startIndex) - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 30), - sut.endIndex) - XCTAssertEqual(sut.index(sut.endIndex, offsetBy: -30), - sut.startIndex) - - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: 110), offsetBy: -100)) - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: -100), offsetBy: 110)) - - XCTAssertGreaterThan(sut.index(sut.startIndex, offsetBy: 31), - sut.endIndex) - XCTAssertGreaterThan(sut.index(sut.startIndex, offsetBy: 32), - sut.index(sut.startIndex, offsetBy: 31)) - XCTAssertLessThan(sut.index(sut.endIndex, offsetBy: -31), - sut.startIndex) - XCTAssertLessThan(sut.index(sut.endIndex, offsetBy: -32), - sut.index(sut.endIndex, offsetBy: -31)) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: 4), offsetBy: 6) + ) + XCTAssertEqual( + sut.index(sut.index(sut.startIndex, offsetBy: 17), offsetBy: -17), + sut.startIndex + ) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 30), + sut.endIndex + ) + XCTAssertEqual( + sut.index(sut.endIndex, offsetBy: -30), + sut.startIndex + ) + + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: 110), offsetBy: -100) + ) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: -100), offsetBy: 110) + ) + + XCTAssertGreaterThan( + sut.index(sut.startIndex, offsetBy: 31), + sut.endIndex + ) + XCTAssertGreaterThan( + sut.index(sut.startIndex, offsetBy: 32), + sut.index(sut.startIndex, offsetBy: 31) + ) + XCTAssertLessThan( + sut.index(sut.endIndex, offsetBy: -31), + sut.startIndex + ) + XCTAssertLessThan( + sut.index(sut.endIndex, offsetBy: -32), + sut.index(sut.endIndex, offsetBy: -31) + ) XCTAssertEqual(sut.index(sut.endIndex, offsetBy: -30, limitedBy: sut.startIndex), sut.startIndex) XCTAssertNil(sut.index(sut.endIndex, offsetBy: -31, limitedBy: sut.startIndex)) @@ -429,58 +518,84 @@ extension UIDSetTests { func testIndexes_multipleShortRanges() { let sut = MessageIdentifierSet([ - MessageIdentifierRange(55 ... 57), - MessageIdentifierRange(155 ... 157), - MessageIdentifierRange(255 ... 257), - MessageIdentifierRange(355 ... 357), - MessageIdentifierRange(455 ... 457), - MessageIdentifierRange(555 ... 557), - MessageIdentifierRange(655 ... 657), - MessageIdentifierRange(755 ... 757), - MessageIdentifierRange(855 ... 857), - MessageIdentifierRange(955 ... 957), + MessageIdentifierRange(55...57), + MessageIdentifierRange(155...157), + MessageIdentifierRange(255...257), + MessageIdentifierRange(355...357), + MessageIdentifierRange(455...457), + MessageIdentifierRange(555...557), + MessageIdentifierRange(655...657), + MessageIdentifierRange(755...757), + MessageIdentifierRange(855...857), + MessageIdentifierRange(955...957), ]) XCTAssertEqual(sut.count, 30, "30 values") - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: 4), offsetBy: 6)) - XCTAssertEqual(sut.index(sut.index(sut.startIndex, offsetBy: 17), offsetBy: -17), - sut.startIndex) - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 30), - sut.endIndex) - XCTAssertEqual(sut.index(sut.endIndex, offsetBy: -30), - sut.startIndex) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: 4), offsetBy: 6) + ) + XCTAssertEqual( + sut.index(sut.index(sut.startIndex, offsetBy: 17), offsetBy: -17), + sut.startIndex + ) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 30), + sut.endIndex + ) + XCTAssertEqual( + sut.index(sut.endIndex, offsetBy: -30), + sut.startIndex + ) - for step in 1 ... 15 { + for step in 1...15 { let count = sut.count / step var a = sut.startIndex - for c in 1 ... count { + for c in 1...count { a = sut.index(a, offsetBy: step) - XCTAssertEqual(a, - sut.index(sut.startIndex, offsetBy: step * c), - "c = \(c), step = \(step)") - XCTAssertEqual(sut.distance(from: sut.startIndex, to: a), - step * c, - "c = \(c), step = \(step)") - XCTAssertEqual(sut.distance(from: sut.endIndex, to: a), - step * c - 30, - "c = \(c), step = \(step)") + XCTAssertEqual( + a, + sut.index(sut.startIndex, offsetBy: step * c), + "c = \(c), step = \(step)" + ) + XCTAssertEqual( + sut.distance(from: sut.startIndex, to: a), + step * c, + "c = \(c), step = \(step)" + ) + XCTAssertEqual( + sut.distance(from: sut.endIndex, to: a), + step * c - 30, + "c = \(c), step = \(step)" + ) } } - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: 110), offsetBy: -100)) - XCTAssertEqual(sut.index(sut.startIndex, offsetBy: 10), - sut.index(sut.index(sut.startIndex, offsetBy: -100), offsetBy: 110)) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: 110), offsetBy: -100) + ) + XCTAssertEqual( + sut.index(sut.startIndex, offsetBy: 10), + sut.index(sut.index(sut.startIndex, offsetBy: -100), offsetBy: 110) + ) - XCTAssertGreaterThan(sut.index(sut.startIndex, offsetBy: 31), - sut.endIndex) - XCTAssertGreaterThan(sut.index(sut.startIndex, offsetBy: 32), - sut.index(sut.startIndex, offsetBy: 31)) - XCTAssertLessThan(sut.index(sut.endIndex, offsetBy: -31), - sut.startIndex) - XCTAssertLessThan(sut.index(sut.endIndex, offsetBy: -32), - sut.index(sut.endIndex, offsetBy: -31)) + XCTAssertGreaterThan( + sut.index(sut.startIndex, offsetBy: 31), + sut.endIndex + ) + XCTAssertGreaterThan( + sut.index(sut.startIndex, offsetBy: 32), + sut.index(sut.startIndex, offsetBy: 31) + ) + XCTAssertLessThan( + sut.index(sut.endIndex, offsetBy: -31), + sut.startIndex + ) + XCTAssertLessThan( + sut.index(sut.endIndex, offsetBy: -32), + sut.index(sut.endIndex, offsetBy: -31) + ) XCTAssertEqual(sut.index(sut.endIndex, offsetBy: -30, limitedBy: sut.startIndex), sut.startIndex) XCTAssertNil(sut.index(sut.endIndex, offsetBy: -31, limitedBy: sut.startIndex)) @@ -491,24 +606,35 @@ extension UIDSetTests { func testRangeView() { XCTAssertEqual(Array(MessageIdentifierSet().ranges), []) - XCTAssertEqual(Array(MessageIdentifierSet([1_234]).ranges), [ - MessageIdentifierRange(1_234 ... 1_234), - ]) - XCTAssertEqual(Array(MessageIdentifierSet([1, 4]).ranges), [ - MessageIdentifierRange(1 ... 1), - MessageIdentifierRange(4 ... 4), - ]) - XCTAssertEqual(Array(MessageIdentifierSet([ - 17 ... 32, - 400 ... 1_234, - 2_001 ... 2_001, - 20_800 ... 21_044, - ]).ranges), [ - MessageIdentifierRange(17 ... 32), - MessageIdentifierRange(400 ... 1_234), - MessageIdentifierRange(2_001 ... 2_001), - MessageIdentifierRange(20_800 ... 21_044), - ]) + XCTAssertEqual( + Array(MessageIdentifierSet([1_234]).ranges), + [ + MessageIdentifierRange(1_234...1_234) + ] + ) + XCTAssertEqual( + Array(MessageIdentifierSet([1, 4]).ranges), + [ + MessageIdentifierRange(1...1), + MessageIdentifierRange(4...4), + ] + ) + XCTAssertEqual( + Array( + MessageIdentifierSet([ + 17...32, + 400...1_234, + 2_001...2_001, + 20_800...21_044, + ]).ranges + ), + [ + MessageIdentifierRange(17...32), + MessageIdentifierRange(400...1_234), + MessageIdentifierRange(2_001...2_001), + MessageIdentifierRange(20_800...21_044), + ] + ) } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDTests.swift b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDTests.swift index f5389f641..ef99751e4 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDTests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDTests.swift @@ -41,7 +41,7 @@ extension UIDTests { XCTAssertFalse(UID.max < .max) XCTAssertFalse(UID.max < 999) XCTAssertTrue(UID.max > 999) - XCTAssertTrue(UID(1) < 999) // use .number to force type + XCTAssertTrue(UID(1) < 999) // use .number to force type } } diff --git a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDValidity+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDValidity+Tests.swift index a018c97f8..3bc431a48 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/UID/UIDValidity+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/UID/UIDValidity+Tests.swift @@ -23,7 +23,7 @@ class UIDValidity_Tests: EncodeTestClass {} extension UIDValidity_Tests { func testEncode() { let inputs: [(UIDValidity, String, UInt)] = [ - (123, "123", #line), + (123, "123", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeUIDValidity($0) }) } diff --git a/Tests/NIOIMAPCoreTests/Grammar/URLRumpMechanism+Tests.swift b/Tests/NIOIMAPCoreTests/Grammar/URLRumpMechanism+Tests.swift index cc545a8e0..849683550 100644 --- a/Tests/NIOIMAPCoreTests/Grammar/URLRumpMechanism+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Grammar/URLRumpMechanism+Tests.swift @@ -23,7 +23,7 @@ class URLRumpMechanism_Tests: EncodeTestClass {} extension URLRumpMechanism_Tests { func testEncode() { let inputs: [(RumpURLAndMechanism, String, UInt)] = [ - (.init(urlRump: "test", mechanism: .internal), "\"test\" INTERNAL", #line), + (.init(urlRump: "test", mechanism: .internal), "\"test\" INTERNAL", #line) ] self.iterateInputs(inputs: inputs, encoder: { self.testBuffer.writeURLRumpMechanism($0) }) } diff --git a/Tests/NIOIMAPCoreTests/ModifiedUTF7+Tests.swift b/Tests/NIOIMAPCoreTests/ModifiedUTF7+Tests.swift index 78b801d23..995241f6b 100644 --- a/Tests/NIOIMAPCoreTests/ModifiedUTF7+Tests.swift +++ b/Tests/NIOIMAPCoreTests/ModifiedUTF7+Tests.swift @@ -60,7 +60,10 @@ extension ModifiedUTF7_Tests { ("~ab", "~ab", #line), ] for (input, expected, line) in inputs { - XCTAssertNoThrow(XCTAssertEqual(expected, try ModifiedUTF7.decode(ByteBuffer(string: input)), line: line), line: line) + XCTAssertNoThrow( + XCTAssertEqual(expected, try ModifiedUTF7.decode(ByteBuffer(string: input)), line: line), + line: line + ) } } diff --git a/Tests/NIOIMAPCoreTests/Parser/CommandParser+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/CommandParser+Tests.swift index bb28fd098..81592a605 100644 --- a/Tests/NIOIMAPCoreTests/Parser/CommandParser+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/CommandParser+Tests.swift @@ -38,7 +38,7 @@ extension CommandParser_Tests { // test that we don't just get returned an empty byte case if // we haven't yet recieved any literal data from the network func testParseEmptyByteBufferAppend() { - var input = ByteBuffer("1 APPEND INBOX {5}\r\n") // everything except the literal data + var input = ByteBuffer("1 APPEND INBOX {5}\r\n") // everything except the literal data var parser = CommandParser() XCTAssertNoThrow(XCTAssertNotNil(try parser.parseCommandStream(buffer: &input))) XCTAssertNoThrow(XCTAssertNotNil(try parser.parseCommandStream(buffer: &input))) @@ -57,13 +57,33 @@ extension CommandParser_Tests { XCTAssertEqual( try parser.parseCommandStream(buffer: &inputA), - .init(.tagged(.init(tag: "A26", command: .uidFetch(messages: UIDSet([1 ... 10002]), attributes: [.uid, .flags, .modificationSequence], modifiers: [])!)), numberOfSynchronisingLiterals: 0) + .init( + .tagged( + .init( + tag: "A26", + command: .uidFetch( + messages: UIDSet([1...10002]), + attributes: [.uid, .flags, .modificationSequence], + modifiers: [] + )! + ) + ), + numberOfSynchronisingLiterals: 0 + ) ) // Send in another line: var inputB = ByteBuffer("A27 UID FETCH 2:22 (UID FLAGS)\r") XCTAssertEqual( try parser.parseCommandStream(buffer: &inputB), - .init(.tagged(.init(tag: "A27", command: .uidFetch(messages: UIDSet([2 ... 22]), attributes: [.uid, .flags], modifiers: [])!)), numberOfSynchronisingLiterals: 0) + .init( + .tagged( + .init( + tag: "A27", + command: .uidFetch(messages: UIDSet([2...22]), attributes: [.uid, .flags], modifiers: [])! + ) + ), + numberOfSynchronisingLiterals: 0 + ) ) } @@ -86,13 +106,19 @@ extension CommandParser_Tests { XCTAssertNoThrow( XCTAssertEqual( try parser.parseCommandStream(buffer: &input), - .init(.tagged(.init(tag: "2", command: .login(username: "", password: ""))), numberOfSynchronisingLiterals: 2) + .init( + .tagged(.init(tag: "2", command: .login(username: "", password: ""))), + numberOfSynchronisingLiterals: 2 + ) ) ) XCTAssertEqual(input, "") input = "3 APPEND INBOX {3+}\r\n123 {3+}\r\n456 {3+}\r\n789\r\n" - XCTAssertEqual(try! parser.parseCommandStream(buffer: &input), .init(.append(.start(tag: "3", appendingTo: .inbox)), numberOfSynchronisingLiterals: 0)) + XCTAssertEqual( + try! parser.parseCommandStream(buffer: &input), + .init(.append(.start(tag: "3", appendingTo: .inbox)), numberOfSynchronisingLiterals: 0) + ) XCTAssertEqual(input, " {3+}\r\n123 {3+}\r\n456 {3+}\r\n789\r\n") } @@ -100,7 +126,10 @@ extension CommandParser_Tests { let inputs: [[UInt8]] = [ Array("+000000000000000000000000000000000000000000000000000000000}\n".utf8), Array("eSequence468117eY SEARCH 4:1 000,0\n000059?000000600=)O".utf8), - [0x41, 0x5D, 0x20, 0x55, 0x49, 0x44, 0x20, 0x43, 0x4F, 0x50, 0x59, 0x20, 0x35, 0x2C, 0x35, 0x3A, 0x34, 0x00, 0x3D, 0x0C, 0x0A, 0x43, 0x20, 0x22, 0xE8], + [ + 0x41, 0x5D, 0x20, 0x55, 0x49, 0x44, 0x20, 0x43, 0x4F, 0x50, 0x59, 0x20, 0x35, 0x2C, 0x35, 0x3A, 0x34, + 0x00, 0x3D, 0x0C, 0x0A, 0x43, 0x20, 0x22, 0xE8, + ], ] for input in inputs { diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Append+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Append+Tests.swift index 7c38ee49d..00298da9e 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Append+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Append+Tests.swift @@ -72,13 +72,19 @@ extension GrammarParser_Append_Tests { ( " (\\Answered) {123}\r\n", "test", - .init(options: .init(flagList: [.answered], internalDate: nil, extensions: [:]), data: .init(byteCount: 123)), + .init( + options: .init(flagList: [.answered], internalDate: nil, extensions: [:]), + data: .init(byteCount: 123) + ), #line ), ( " (\\Answered) ~{456}\r\n", "test", - .init(options: .init(flagList: [.answered], internalDate: nil, extensions: [:]), data: .init(byteCount: 456, withoutContentTransferEncoding: true)), + .init( + options: .init(flagList: [.answered], internalDate: nil, extensions: [:]), + data: .init(byteCount: 456, withoutContentTransferEncoding: true) + ), #line ), ], @@ -92,7 +98,15 @@ extension GrammarParser_Append_Tests { extension GrammarParser_Append_Tests { func testParseAppendOptions() throws { - let components = ServerMessageDate.Components(year: 1994, month: 6, day: 25, hour: 1, minute: 2, second: 3, timeZoneMinutes: 0) + let components = ServerMessageDate.Components( + year: 1994, + month: 6, + day: 25, + hour: 1, + minute: 2, + second: 3, + timeZoneMinutes: 0 + ) let date = ServerMessageDate(components!) self.iterateTests( @@ -109,17 +123,21 @@ extension GrammarParser_Append_Tests { ( " name1 1:2", "\r", - .init(flagList: [], internalDate: nil, extensions: ["name1": .sequence(.range(1 ... 2))]), + .init(flagList: [], internalDate: nil, extensions: ["name1": .sequence(.range(1...2))]), #line ), ( " name1 1:2 name2 2:3 name3 3:4", "\r", - .init(flagList: [], internalDate: nil, extensions: [ - "name1": .sequence(.range(1 ... 2)), - "name2": .sequence(.range(2 ... 3)), - "name3": .sequence(.range(3 ... 4)), - ]), + .init( + flagList: [], + internalDate: nil, + extensions: [ + "name1": .sequence(.range(1...2)), + "name2": .sequence(.range(2...3)), + "name3": .sequence(.range(3...4)), + ] + ), #line ), ], diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Body+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Body+Tests.swift index 078d00e59..9246575a4 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Body+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Body+Tests.swift @@ -29,8 +29,11 @@ extension GrammarParser_Body_Tests { ("\"s\"", "\r", [.string("s")], #line), ("(1)", "\r", [.number(1)], #line), ("(1 \"2\" 3)", "\r", [.number(1), .string("2"), .number(3)], #line), - ("(1 2 3 (4 (5 (6))))", "\r", [.number(1), .number(2), .number(3), .number(4), .number(5), .number(6)], #line), - ("(((((1)))))", "\r", [.number(1)], #line), // yeh, this is valid, don't ask + ( + "(1 2 3 (4 (5 (6))))", "\r", + [.number(1), .number(2), .number(3), .number(4), .number(5), .number(6)], #line + ), + ("(((((1)))))", "\r", [.number(1)], #line), // yeh, this is valid, don't ask ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -181,7 +184,13 @@ extension GrammarParser_Body_Tests { "\r\n", .init( kind: .basic(.init(topLevel: .application, sub: .mixed)), - fields: .init(parameters: [:], id: "id", contentDescription: "description", encoding: .sevenBit, octetCount: 2), + fields: .init( + parameters: [:], + id: "id", + contentDescription: "description", + encoding: .sevenBit, + octetCount: 2 + ), extension: nil ), #line @@ -191,7 +200,13 @@ extension GrammarParser_Body_Tests { "\r\n", .init( kind: .basic(.init(topLevel: .video, sub: .related)), - fields: .init(parameters: ["f1": "v1"], id: nil, contentDescription: nil, encoding: .eightBit, octetCount: 3), + fields: .init( + parameters: ["f1": "v1"], + id: nil, + contentDescription: nil, + encoding: .eightBit, + octetCount: 3 + ), extension: nil ), #line @@ -206,8 +221,30 @@ extension GrammarParser_Body_Tests { kind: .message( .init( message: .rfc822, - envelope: Envelope(date: nil, subject: nil, from: [], sender: [], reply: [], to: [], cc: [], bcc: [], inReplyTo: nil, messageID: nil), - body: .singlepart(.init(kind: .basic(.init(topLevel: .image, sub: .related)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 5))), + envelope: Envelope( + date: nil, + subject: nil, + from: [], + sender: [], + reply: [], + to: [], + cc: [], + bcc: [], + inReplyTo: nil, + messageID: nil + ), + body: .singlepart( + .init( + kind: .basic(.init(topLevel: .image, sub: .related)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 5 + ) + ) + ), lineCount: 8 ) ), @@ -215,7 +252,7 @@ extension GrammarParser_Body_Tests { extension: nil ), #line - ), + ) ] let textInputs: [(String, String, BodyStructure.Singlepart, UInt)] = [ @@ -224,11 +261,17 @@ extension GrammarParser_Body_Tests { "\r\n", .init( kind: .text(.init(mediaSubtype: "media", lineCount: 2)), - fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 1), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 1 + ), extension: nil ), #line - ), + ) ] let inputs = basicInputs + messageInputs + textInputs @@ -249,93 +292,132 @@ extension GrammarParser_Body_Tests { ( #"("text" "plain" ("CHARSET" "UTF-8") NIL NIL NIL 1423 44 NIL NIL NIL NIL)"#, "\r\n", - .singlepart(.init( - kind: .text(.init(mediaSubtype: "plain", lineCount: 44)), - fields: .init( - parameters: ["CHARSET": "UTF-8"], - id: nil, - contentDescription: nil, - encoding: nil, - octetCount: 1423 - ), - extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))) - )), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "plain", lineCount: 44)), + fields: .init( + parameters: ["CHARSET": "UTF-8"], + id: nil, + contentDescription: nil, + encoding: nil, + octetCount: 1423 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: [], location: .init(location: nil, extensions: [])) + ) + ) + ) + ), #line - ), + ) ] let multiParts: [(String, String, BodyStructure, UInt)] = [ ( #"((("text" "plain" ("CHARSET" "UTF-8") NIL NIL NIL 1423 44 NIL NIL NIL NIL)("text" "html" ("CHARSET" "UTF-8") NIL NIL "quoted-printable" 2524 34 NIL NIL NIL NIL) "alternative" ("BOUNDARY" "000000000000ccac3a05a5ef76c3") NIL NIL NIL)("text" "plain" ("CHARSET" "us-ascii") NIL NIL "7bit" 151 4 NIL NIL NIL NIL) "mixed" ("BOUNDARY" "===============5781602957316160403==") NIL NIL NIL)"#, "\r\n", - .multipart(.init( - parts: [ - .multipart(.init( - parts: [ - .singlepart(.init( - kind: .text(.init(mediaSubtype: "plain", lineCount: 44)), - fields: .init( - parameters: ["CHARSET": "UTF-8"], - id: nil, - contentDescription: nil, - encoding: nil, - octetCount: 1423 - ), + .multipart( + .init( + parts: [ + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "plain", lineCount: 44)), + fields: .init( + parameters: ["CHARSET": "UTF-8"], + id: nil, + contentDescription: nil, + encoding: nil, + octetCount: 1423 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "html", lineCount: 34)), + fields: .init( + parameters: ["CHARSET": "UTF-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 2524 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, extension: .init( - digest: nil, - dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))) + parameters: ["BOUNDARY": "000000000000ccac3a05a5ef76c3"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) ) - )), - .singlepart(.init( - kind: .text(.init(mediaSubtype: "html", lineCount: 34)), + ) + ), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "plain", lineCount: 4)), fields: .init( - parameters: ["CHARSET": "UTF-8"], + parameters: ["CHARSET": "us-ascii"], id: nil, contentDescription: nil, - encoding: .quotedPrintable, - octetCount: 2524 + encoding: .sevenBit, + octetCount: 151 ), extension: .init( digest: nil, dispositionAndLanguage: .init( disposition: nil, - language: .init(languages: [], location: .init(location: nil, extensions: [])) + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) ) ) - )), - ], - mediaSubtype: .alternative, - extension: .init( - parameters: ["BOUNDARY": "000000000000ccac3a05a5ef76c3"], - dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))) - ) - )), - .singlepart(.init( - kind: .text(.init(mediaSubtype: "plain", lineCount: 4)), - fields: .init( - parameters: ["CHARSET": "us-ascii"], - id: nil, - contentDescription: nil, - encoding: .sevenBit, - octetCount: 151 + ) ), - extension: .init( - digest: nil, - dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))) + ], + mediaSubtype: .mixed, + extension: .init( + parameters: ["BOUNDARY": "===============5781602957316160403=="], + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: [], location: .init(location: nil, extensions: [])) ) - )), - ], - mediaSubtype: .mixed, - extension: .init( - parameters: ["BOUNDARY": "===============5781602957316160403=="], - dispositionAndLanguage: .init( - disposition: nil, - language: .init(languages: [], location: .init(location: nil, extensions: [])) ) ) - )), + ), #line - ), + ) ] let inputs = singleParts + multiParts diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Commands+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Commands+Tests.swift index d232910d5..d32f7b906 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Commands+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Commands+Tests.swift @@ -24,10 +24,10 @@ extension GrammarParser_Commands_Tests { func testParserLiteralLengthLimit() { let parser = GrammarParser(literalSizeLimit: 5) var b1 = ParseBuffer("{5}\r\nabcde") - XCTAssertEqual(try parser.parseLiteral(buffer: &b1, tracker: .makeNewDefaultLimitStackTracker), "abcde") + XCTAssertEqual(try parser.parseLiteral(buffer: &b1, tracker: .makeNewDefault), "abcde") var b2 = ParseBuffer("{6}\r\nabcdef") - XCTAssertThrowsError(try parser.parseLiteral(buffer: &b2, tracker: .makeNewDefaultLimitStackTracker)) { e in + XCTAssertThrowsError(try parser.parseLiteral(buffer: &b2, tracker: .makeNewDefault)) { e in XCTAssertTrue(e is ExceededLiteralSizeLimitError) } } @@ -42,10 +42,10 @@ extension GrammarParser_Commands_Tests { ("a1 CAPABILITY", "\r", .init(tag: "a1", command: .capability), #line), ], parserErrorInputs: [ - ("(", "CAPABILITY", #line), + ("(", "CAPABILITY", #line) ], incompleteMessageInputs: [ - ("a CAPABILITY", "", #line), + ("a CAPABILITY", "", #line) ] ) } @@ -98,17 +98,26 @@ extension GrammarParser_Commands_Tests { ("NAMESPACE", "\r", .namespace, #line), ("ID NIL", "\r", .id(.init()), #line), ("ENABLE BINARY", "\r", .enable([.binary]), #line), - ("GETMETADATA INBOX (test)", "\r", .getMetadata(options: [], mailbox: .inbox, entries: ["test"]), #line), + ( + "GETMETADATA INBOX (test)", "\r", .getMetadata(options: [], mailbox: .inbox, entries: ["test"]), + #line + ), ("SETMETADATA INBOX (test NIL)", "\r", .setMetadata(mailbox: .inbox, entries: ["test": nil]), #line), ("RESETKEY INBOX INTERNAL", "\r", .resetKey(mailbox: .inbox, mechanisms: [.internal]), #line), - ("GENURLAUTH rump INTERNAL", "\r", .generateAuthorizedURL([.init(urlRump: "rump", mechanism: .internal)]), #line), + ( + "GENURLAUTH rump INTERNAL", "\r", + .generateAuthorizedURL([.init(urlRump: "rump", mechanism: .internal)]), #line + ), ("URLFETCH test", "\r", .urlFetch(["test"]), #line), ("COPY 1 INBOX", "\r", .copy(.set([1]), .inbox), #line), ("DELETE INBOX", "\r", .delete(.inbox), #line), ("MOVE $ INBOX", "\r", .move(.lastCommand, .inbox), #line), ("SEARCH ALL", "\r", .search(key: .all, charset: nil, returnOptions: []), #line), ("ESEARCH ALL", "\r", .extendedSearch(.init(key: .all)), #line), - ("STORE $ +FLAGS \\Answered", "\r", .store(.lastCommand, [], .flags(.add(silent: false, list: [.answered]))), #line), + ( + "STORE $ +FLAGS \\Answered", "\r", + .store(.lastCommand, [], .flags(.add(silent: false, list: [.answered]))), #line + ), ("EXAMINE INBOX", "\r", .examine(.inbox, .init()), #line), ("LIST INBOX test", "\r", .list(nil, reference: .inbox, .mailbox("test"), []), #line), ("LSUB INBOX test", "\r", .lsub(reference: .inbox, pattern: "test"), #line), @@ -117,14 +126,20 @@ extension GrammarParser_Commands_Tests { ("STATUS INBOX (SIZE)", "\r", .status(.inbox, [.size]), #line), ("SUBSCRIBE INBOX", "\r", .subscribe(.inbox), #line), ("UNSUBSCRIBE INBOX", "\r", .unsubscribe(.inbox), #line), - ("UID EXPUNGE 1:2", "\r", .uidExpunge(.set([1 ... 2])), #line), + ("UID EXPUNGE 1:2", "\r", .uidExpunge(.set([1...2])), #line), ("FETCH $ (FLAGS)", "\r", .fetch(.lastCommand, [.flags], .init()), #line), ("LOGIN \"user\" \"password\"", "\r", .login(username: "user", password: "password"), #line), - ("AUTHENTICATE GSSAPI", "\r", .authenticate(mechanism: AuthenticationMechanism("GSSAPI"), initialResponse: nil), #line), + ( + "AUTHENTICATE GSSAPI", "\r", + .authenticate(mechanism: AuthenticationMechanism("GSSAPI"), initialResponse: nil), #line + ), ("CREATE test", "\r", .create(.init("test"), []), #line), ("GETQUOTA root", "\r", .getQuota(.init("root")), #line), ("GETQUOTAROOT INBOX", "\r", .getQuotaRoot(.inbox), #line), - ("SETQUOTA ROOT (resource 123)", "\r", .setQuota(.init("ROOT"), [.init(resourceName: "resource", limit: 123)]), #line), + ( + "SETQUOTA ROOT (resource 123)", "\r", + .setQuota(.init("ROOT"), [.init(resourceName: "resource", limit: 123)]), #line + ), ("COMPRESS DEFLATE", "\r", .compress(.deflate), #line), ], parserErrorInputs: [ @@ -171,10 +186,10 @@ extension GrammarParser_Commands_Tests { (" ACL BINARY CHILDREN", "\r", .enable([.acl, .binary, .children]), #line), ], parserErrorInputs: [ - (" (ACL)", "\r", #line), + (" (ACL)", "\r", #line) ], incompleteMessageInputs: [ - (" ACL", "", #line), + (" ACL", "", #line) ] ) } @@ -184,10 +199,13 @@ extension GrammarParser_Commands_Tests { testFunction: GrammarParser().parseCommandSuffix_getMetadata, validInputs: [ (" INBOX a", " ", .getMetadata(options: [], mailbox: .inbox, entries: ["a"]), #line), - (" (MAXSIZE 123) INBOX (a b)", " ", .getMetadata(options: [.maxSize(123)], mailbox: .inbox, entries: ["a", "b"]), #line), + ( + " (MAXSIZE 123) INBOX (a b)", " ", + .getMetadata(options: [.maxSize(123)], mailbox: .inbox, entries: ["a", "b"]), #line + ), ], parserErrorInputs: [ - (" (MAXSIZE 123 rogue) INBOX", "\r", #line), + (" (MAXSIZE 123 rogue) INBOX", "\r", #line) ], incompleteMessageInputs: [ (" (key", "", #line), @@ -201,10 +219,10 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_setMetadata, validInputs: [ - (" INBOX (a NIL)", " ", .setMetadata(mailbox: .inbox, entries: ["a": .init(nil)]), #line), + (" INBOX (a NIL)", " ", .setMetadata(mailbox: .inbox, entries: ["a": .init(nil)]), #line) ], parserErrorInputs: [ - (" (a NIL)", "", #line), + (" (a NIL)", "", #line) ], incompleteMessageInputs: [ (" INBOX", "", #line), @@ -221,10 +239,12 @@ extension GrammarParser_Commands_Tests { ("", "\r", .resetKey(mailbox: nil, mechanisms: []), #line), (" INBOX", "\r", .resetKey(mailbox: .inbox, mechanisms: []), #line), (" INBOX INTERNAL", "\r", .resetKey(mailbox: .inbox, mechanisms: [.internal]), #line), - (" INBOX INTERNAL test", "\r", .resetKey(mailbox: .inbox, mechanisms: [.internal, .init("test")]), #line), - ], - parserErrorInputs: [ + ( + " INBOX INTERNAL test", "\r", .resetKey(mailbox: .inbox, mechanisms: [.internal, .init("test")]), + #line + ), ], + parserErrorInputs: [], incompleteMessageInputs: [ (" INBOX", "", #line), (" INBOX INTERNAL", "", #line), @@ -238,10 +258,15 @@ extension GrammarParser_Commands_Tests { testFunction: GrammarParser().parseCommandSuffix_genURLAuth, validInputs: [ (" test INTERNAL", "\r", .generateAuthorizedURL([.init(urlRump: "test", mechanism: .internal)]), #line), - (" test INTERNAL test2 INTERNAL", "\r", .generateAuthorizedURL([.init(urlRump: "test", mechanism: .internal), .init(urlRump: "test2", mechanism: .internal)]), #line), + ( + " test INTERNAL test2 INTERNAL", "\r", + .generateAuthorizedURL([ + .init(urlRump: "test", mechanism: .internal), .init(urlRump: "test2", mechanism: .internal), + ]), #line + ), ], parserErrorInputs: [ - (" \\", "", #line), + (" \\", "", #line) ], incompleteMessageInputs: [ (" ", "", #line), @@ -259,7 +284,7 @@ extension GrammarParser_Commands_Tests { (" test1 test2", "\r", .urlFetch(["test1", "test2"]), #line), ], parserErrorInputs: [ - (" \\ ", "", #line), + (" \\ ", "", #line) ], incompleteMessageInputs: [ (" test", "", #line), @@ -275,7 +300,7 @@ extension GrammarParser_Commands_Tests { (" $ inbox", "\r", .copy(.lastCommand, .inbox), #line), (" 1 inbox", "\r", .copy(.set([1]), .inbox), #line), (" 1,5,7 inbox", "\r", .copy(.set([1, 5, 7]), .inbox), #line), - (" 1:100 inbox", "\r", .copy(.set([1 ... 100]), .inbox), #line), + (" 1:100 inbox", "\r", .copy(.set([1...100]), .inbox), #line), ], parserErrorInputs: [ (" a inbox", "\r", #line), @@ -292,13 +317,13 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_delete, validInputs: [ - (" INBOX", "\r\n", .delete(.inbox), #line), + (" INBOX", "\r\n", .delete(.inbox), #line) ], parserErrorInputs: [ - (" {5}12345", " ", #line), + (" {5}12345", " ", #line) ], incompleteMessageInputs: [ - (" INBOX", "", #line), + (" INBOX", "", #line) ] ) } @@ -310,7 +335,7 @@ extension GrammarParser_Commands_Tests { (" $ inbox", "\r", .move(.lastCommand, .inbox), #line), (" 1 inbox", "\r", .move(.set([1]), .inbox), #line), (" 1,5,7 inbox", "\r", .move(.set([1, 5, 7]), .inbox), #line), - (" 1:100 inbox", "\r", .move(.set([1 ... 100]), .inbox), #line), + (" 1:100 inbox", "\r", .move(.set([1...100]), .inbox), #line), ], parserErrorInputs: [ (" a inbox", "\r", #line), @@ -344,7 +369,11 @@ extension GrammarParser_Commands_Tests { ( #" RETURN (MIN MAX) CHARSET UTF-8 OR (FROM "me" FROM "you") (NEW UNSEEN)"#, "\r", - .search(key: .or(.and([.from("me"), .from("you")]), .and([.new, .unseen])), charset: "UTF-8", returnOptions: [.min, .max]), + .search( + key: .or(.and([.from("me"), .from("you")]), .and([.new, .unseen])), + charset: "UTF-8", + returnOptions: [.min, .max] + ), #line ), ], @@ -360,14 +389,23 @@ extension GrammarParser_Commands_Tests { (" ALL", "\r", .extendedSearch(.init(key: .all)), #line), ( " IN (mailboxes \"folder1\" subtree \"folder2\") unseen", "\r", - .extendedSearch(ExtendedSearchOptions(key: .unseen, charset: nil, returnOptions: [], sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.mailboxes(Mailboxes([MailboxName("folder1")])!), .subtree(Mailboxes([MailboxName("folder2")])!)]))), + .extendedSearch( + ExtendedSearchOptions( + key: .unseen, + charset: nil, + returnOptions: [], + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [ + .mailboxes(Mailboxes([MailboxName("folder1")])!), + .subtree(Mailboxes([MailboxName("folder2")])!), + ]) + ) + ), #line ), ], - parserErrorInputs: [ - ], + parserErrorInputs: [], incompleteMessageInputs: [ - (" IN (mailboxes ", "", #line), + (" IN (mailboxes ", "", #line) ] ) } @@ -376,12 +414,29 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_store, validInputs: [ - (" 1 +FLAGS \\answered", "\r", .store(.set([1]), [], .flags(.add(silent: false, list: [.answered]))), #line), - (" 1 (label) -FLAGS \\seen", "\r", .store(.set([1]), [.other(.init(key: "label", value: nil))], .flags(.remove(silent: false, list: [.seen]))), #line), - (" 1 (label UNCHANGEDSINCE 5) -FLAGS \\seen", "\r", .store(.set([1]), [.other(.init(key: "label", value: nil)), .unchangedSince(.init(modificationSequence: 5))], .flags(.remove(silent: false, list: [.seen]))), #line), + ( + " 1 +FLAGS \\answered", "\r", .store(.set([1]), [], .flags(.add(silent: false, list: [.answered]))), + #line + ), + ( + " 1 (label) -FLAGS \\seen", "\r", + .store( + .set([1]), + [.other(.init(key: "label", value: nil))], + .flags(.remove(silent: false, list: [.seen])) + ), #line + ), + ( + " 1 (label UNCHANGEDSINCE 5) -FLAGS \\seen", "\r", + .store( + .set([1]), + [.other(.init(key: "label", value: nil)), .unchangedSince(.init(modificationSequence: 5))], + .flags(.remove(silent: false, list: [.seen])) + ), #line + ), ], parserErrorInputs: [ - (" +FLAGS \\answered", "\r", #line), + (" +FLAGS \\answered", "\r", #line) ], incompleteMessageInputs: [ (" ", "", #line), @@ -407,7 +462,7 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_list, validInputs: [ - (#" "" """#, "\r", .list(nil, reference: MailboxName(""), .mailbox(""), []), #line), + (#" "" """#, "\r", .list(nil, reference: MailboxName(""), .mailbox(""), []), #line) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -422,7 +477,7 @@ extension GrammarParser_Commands_Tests { (" \"inbox\" \"someList\"", " ", .lsub(reference: .inbox, pattern: "someList"), #line), ], parserErrorInputs: [ - (" {5}inbox", "", #line), + (" {5}inbox", "", #line) ], incompleteMessageInputs: [ (" inbox", "", #line), @@ -435,7 +490,11 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_rename, validInputs: [ - (" box1 box2", "\r", .rename(from: .init(.init(string: "box1")), to: .init(.init(string: "box2")), parameters: [:]), #line), + ( + " box1 box2", "\r", + .rename(from: .init(.init(string: "box1")), to: .init(.init(string: "box2")), parameters: [:]), + #line + ) ], parserErrorInputs: [ (" {2}b1 {2}b2", "", #line), @@ -456,10 +515,10 @@ extension GrammarParser_Commands_Tests { (" inbox (some1)", "\r", .select(.inbox, [.basic(.init(key: "some1", value: nil))]), #line), ], parserErrorInputs: [ - (" ", "\r", #line), + (" ", "\r", #line) ], incompleteMessageInputs: [ - (" ", "", #line), + (" ", "", #line) ] ) } @@ -469,10 +528,13 @@ extension GrammarParser_Commands_Tests { testFunction: GrammarParser().parseCommandSuffix_status, validInputs: [ (" inbox (messages unseen)", "\r\n", .status(.inbox, [.messageCount, .unseenCount]), #line), - (" Deleted (messages unseen HIGHESTMODSEQ)", "\r\n", .status(MailboxName("Deleted"), [.messageCount, .unseenCount, .highestModificationSequence]), #line), + ( + " Deleted (messages unseen HIGHESTMODSEQ)", "\r\n", + .status(MailboxName("Deleted"), [.messageCount, .unseenCount, .highestModificationSequence]), #line + ), ], parserErrorInputs: [ - (" inbox (messages unseen", "\r\n", #line), + (" inbox (messages unseen", "\r\n", #line) ], incompleteMessageInputs: [ ("", "", #line), @@ -485,13 +547,13 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_subscribe, validInputs: [ - (" INBOX", "\r", .subscribe(.inbox), #line), + (" INBOX", "\r", .subscribe(.inbox), #line) ], parserErrorInputs: [ - ("inbox", "", #line), + ("inbox", "", #line) ], incompleteMessageInputs: [ - (" inbox", "", #line), + (" inbox", "", #line) ] ) } @@ -500,13 +562,13 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_unsubscribe, validInputs: [ - (" inbox", "\r", .unsubscribe(.inbox), #line), + (" inbox", "\r", .unsubscribe(.inbox), #line) ], parserErrorInputs: [ - ("inbox", "", #line), + ("inbox", "", #line) ], incompleteMessageInputs: [ - (" inbox", "", #line), + (" inbox", "", #line) ] ) } @@ -519,12 +581,22 @@ extension GrammarParser_Commands_Tests { (" COPY 1 Inbox", "\r\n", .uidCopy(.set([1]), .inbox), #line), (" FETCH 1 FLAGS", "\r\n", .uidFetch(.set([1]), [.flags], []), #line), (" SEARCH CHARSET UTF8 ALL", "\r\n", .uidSearch(key: .all, charset: "UTF8"), #line), - (" STORE 1 +FLAGS (Test)", "\r\n", .uidStore(.set([1]), [], .flags(.add(silent: false, list: ["Test"]))), #line), - (" STORE 1 (UNCHANGEDSINCE 5 test) +FLAGS (Test)", "\r\n", .uidStore(.set([1]), [.unchangedSince(.init(modificationSequence: 5)), .other(.init(key: "test", value: nil))], .flags(.add(silent: false, list: ["Test"]))), #line), + ( + " STORE 1 +FLAGS (Test)", "\r\n", + .uidStore(.set([1]), [], .flags(.add(silent: false, list: ["Test"]))), #line + ), + ( + " STORE 1 (UNCHANGEDSINCE 5 test) +FLAGS (Test)", "\r\n", + .uidStore( + .set([1]), + [.unchangedSince(.init(modificationSequence: 5)), .other(.init(key: "test", value: nil))], + .flags(.add(silent: false, list: ["Test"])) + ), #line + ), (" COPY * Inbox", "\r\n", .uidCopy(.set([MessageIdentifierRange(.max)]), .inbox), #line), ], parserErrorInputs: [ - ("UID RENAME inbox other", " ", #line), + ("UID RENAME inbox other", " ", #line) ], incompleteMessageInputs: [ // ("UID COPY 1", " ", #line), @@ -536,13 +608,19 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_fetch, validInputs: [ - (" 1:3 ALL", "\r", .fetch(.set([1 ... 3]), .all, []), #line), - (" 2:4 FULL", "\r", .fetch(.set([2 ... 4]), .full, []), #line), - (" 3:5 FAST", "\r", .fetch(.set([3 ... 5]), .fast, []), #line), - (" 4:6 ENVELOPE", "\r", .fetch(.set([4 ... 6]), [.envelope], []), #line), - (" 5:7 (ENVELOPE FLAGS)", "\r", .fetch(.set([5 ... 7]), [.envelope, .flags], []), #line), - (" 3:5 FAST (name)", "\r", .fetch(.set([3 ... 5]), .fast, [.other(.init(key: "name", value: nil))]), #line), - (" 1 BODY[TEXT]", "\r", .fetch(.set([1]), [.bodySection(peek: false, .init(kind: .text), nil)], []), #line), + (" 1:3 ALL", "\r", .fetch(.set([1...3]), .all, []), #line), + (" 2:4 FULL", "\r", .fetch(.set([2...4]), .full, []), #line), + (" 3:5 FAST", "\r", .fetch(.set([3...5]), .fast, []), #line), + (" 4:6 ENVELOPE", "\r", .fetch(.set([4...6]), [.envelope], []), #line), + (" 5:7 (ENVELOPE FLAGS)", "\r", .fetch(.set([5...7]), [.envelope, .flags], []), #line), + ( + " 3:5 FAST (name)", "\r", .fetch(.set([3...5]), .fast, [.other(.init(key: "name", value: nil))]), + #line + ), + ( + " 1 BODY[TEXT]", "\r", .fetch(.set([1]), [.bodySection(peek: false, .init(kind: .text), nil)], []), + #line + ), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -558,7 +636,7 @@ extension GrammarParser_Commands_Tests { (" {5}\r\nemail {8}\r\npassword", "\r", .login(username: "email", password: "password"), #line), ], parserErrorInputs: [ - ("email password", "", #line), + ("email password", "", #line) ], incompleteMessageInputs: [ (" email", "", #line), @@ -573,13 +651,16 @@ extension GrammarParser_Commands_Tests { testFunction: GrammarParser().parseCommandSuffix_authenticate, validInputs: [ (" GSSAPI", "\r", .authenticate(mechanism: .gssAPI, initialResponse: nil), #line), - (" GSSAPI aGV5", "\r", .authenticate(mechanism: .gssAPI, initialResponse: .init(.init(.init(string: "hey")))), #line), + ( + " GSSAPI aGV5", "\r", + .authenticate(mechanism: .gssAPI, initialResponse: .init(.init(.init(string: "hey")))), #line + ), ], parserErrorInputs: [ - (" \"GSSAPI\"", "", #line), + (" \"GSSAPI\"", "", #line) ], incompleteMessageInputs: [ - (" gssapi", "", #line), + (" gssapi", "", #line) ] ) } @@ -595,7 +676,13 @@ extension GrammarParser_Commands_Tests { ( " inbox (USE (\\All \\Flagged) some1 2 USE (\\Sent))", "\r", - .create(.inbox, [.attributes([.all, .flagged]), .labelled(.init(key: "some1", value: .sequence(.set([2])))), .attributes([.sent])]), + .create( + .inbox, + [ + .attributes([.all, .flagged]), .labelled(.init(key: "some1", value: .sequence(.set([2])))), + .attributes([.sent]), + ] + ), #line ), ], @@ -615,10 +702,10 @@ extension GrammarParser_Commands_Tests { (" \"quota\"", "\r", .getQuota(.init("quota")), #line), ], parserErrorInputs: [ - (" {5}quota", "\r", #line), + (" {5}quota", "\r", #line) ], incompleteMessageInputs: [ - (" \"root", "", #line), + (" \"root", "", #line) ] ) } @@ -627,15 +714,21 @@ extension GrammarParser_Commands_Tests { self.iterateTests( testFunction: GrammarParser().parseCommandSuffix_setQuota, validInputs: [ - (#" "" (STORAGE 512)"#, "\r", .setQuota(.init(""), [.init(resourceName: "STORAGE", limit: 512)]), #line), + ( + #" "" (STORAGE 512)"#, "\r", .setQuota(.init(""), [.init(resourceName: "STORAGE", limit: 512)]), + #line + ), ( #" "" (STORAGE 512 BANDWIDTH 123)"#, "\r", - .setQuota(.init(""), [.init(resourceName: "STORAGE", limit: 512), .init(resourceName: "BANDWIDTH", limit: 123)]), + .setQuota( + .init(""), + [.init(resourceName: "STORAGE", limit: 512), .init(resourceName: "BANDWIDTH", limit: 123)] + ), #line ), ], parserErrorInputs: [ - (#" "" STORAGE 512"#, "", #line), + (#" "" STORAGE 512"#, "", #line) ], incompleteMessageInputs: [ (#" ""#, "", #line), @@ -656,10 +749,10 @@ extension GrammarParser_Commands_Tests { (" {5}\r\nINBOX", "\r", .getQuotaRoot(.inbox), #line), ], parserErrorInputs: [ - (" {5}INBOX", "", #line), + (" {5}INBOX", "", #line) ], incompleteMessageInputs: [ - (" INBOX", "", #line), + (" INBOX", "", #line) ] ) } diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Entry+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Entry+Tests.swift index 50f3699cf..a3deaba86 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Entry+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Entry+Tests.swift @@ -28,10 +28,8 @@ extension GrammarParser_Entry_Tests { ("\"name\" \"value\"", "", .init(key: "name", value: .init("value")), #line), ("\"name\" NIL", "", .init(key: "name", value: .init(nil)), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -56,10 +54,8 @@ extension GrammarParser_Entry_Tests { #line ), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -75,10 +71,8 @@ extension GrammarParser_Entry_Tests { ("(\"name\")", "", ["name"], #line), ("(\"name1\" \"name2\")", "", ["name1", "name2"], #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -93,10 +87,8 @@ extension GrammarParser_Entry_Tests { ("\"name\"", "\r", ["name"], #line), ("\"name1\" \"name2\"", "\r", ["name1", "name2"], #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -108,13 +100,13 @@ extension GrammarParser_Entry_Tests { self.iterateTests( testFunction: GrammarParser().parseEntryFlagName, validInputs: [ - ("\"/flags/\\\\Answered\"", "", .init(flag: .answered), #line), + ("\"/flags/\\\\Answered\"", "", .init(flag: .answered), #line) ], parserErrorInputs: [ - ("/flags/\\Answered", "", #line), + ("/flags/\\Answered", "", #line) ], incompleteMessageInputs: [ - ("\"/flags", "", #line), + ("\"/flags", "", #line) ] ) } diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Envelope+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Envelope+Tests.swift index 19baa9cd6..f9a23c44a 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Envelope+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Envelope+Tests.swift @@ -23,38 +23,50 @@ class GrammarParser_Envelope_Tests: XCTestCase, _ParserTestHelpers {} extension GrammarParser_Envelope_Tests { func testParseEnvelopeEmailAddressGroups() { let inputs: [([EmailAddress], [EmailAddressListElement], UInt)] = [ - ([], [], #line), // extreme case, this should never happen, but we don't want to crash - ( // single address + ([], [], #line), // extreme case, this should never happen, but we don't want to crash + ( // single address [.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a")], [.singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"))], #line ), - ( // multiple addresses - [.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"), .init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b")], - [.singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a")), .singleAddress(.init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b"))], + ( // multiple addresses + [ + .init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"), + .init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b"), + ], + [ + .singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a")), + .singleAddress(.init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b")), + ], #line ), - ( // single group: 1 address + ( // single group: 1 address [ .init(personName: nil, sourceRoot: nil, mailbox: "group", host: nil), .init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"), .init(personName: nil, sourceRoot: nil, mailbox: nil, host: nil), ], [ - .group(.init(groupName: "group", sourceRoot: nil, children: [.singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"))])), + .group( + .init( + groupName: "group", + sourceRoot: nil, + children: [.singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"))] + ) + ) ], #line ), - ( // 1 address with no information + ( // 1 address with no information [ - .init(personName: nil, sourceRoot: nil, mailbox: nil, host: nil), + .init(personName: nil, sourceRoot: nil, mailbox: nil, host: nil) ], [ - .singleAddress(.init(personName: nil, sourceRoot: nil, mailbox: nil, host: nil)), + .singleAddress(.init(personName: nil, sourceRoot: nil, mailbox: nil, host: nil)) ], #line ), - ( // single group: 1 address + ( // single group: 1 address [ .init(personName: nil, sourceRoot: nil, mailbox: "group", host: nil), .init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"), @@ -63,15 +75,21 @@ extension GrammarParser_Envelope_Tests { .init(personName: nil, sourceRoot: nil, mailbox: nil, host: nil), ], [ - .group(.init(groupName: "group", sourceRoot: nil, children: [ - .singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a")), - .singleAddress(.init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b")), - .singleAddress(.init(personName: "c", sourceRoot: "c", mailbox: "c", host: "c")), - ])), + .group( + .init( + groupName: "group", + sourceRoot: nil, + children: [ + .singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a")), + .singleAddress(.init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b")), + .singleAddress(.init(personName: "c", sourceRoot: "c", mailbox: "c", host: "c")), + ] + ) + ) ], #line ), - ( // nested groups + ( // nested groups [ .init(personName: nil, sourceRoot: nil, mailbox: "group1", host: nil), .init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a"), @@ -84,15 +102,42 @@ extension GrammarParser_Envelope_Tests { .init(personName: nil, sourceRoot: nil, mailbox: nil, host: nil), ], [ - .group(.init(groupName: "group1", sourceRoot: nil, children: [ - .singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a")), - .group(.init(groupName: "group2", sourceRoot: nil, children: [ - .singleAddress(.init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b")), - .group(.init(groupName: "group3", sourceRoot: nil, children: [ - .singleAddress(.init(personName: "c", sourceRoot: "c", mailbox: "c", host: "c")), - ])), - ])), - ])), + .group( + .init( + groupName: "group1", + sourceRoot: nil, + children: [ + .singleAddress(.init(personName: "a", sourceRoot: "a", mailbox: "a", host: "a")), + .group( + .init( + groupName: "group2", + sourceRoot: nil, + children: [ + .singleAddress( + .init(personName: "b", sourceRoot: "b", mailbox: "b", host: "b") + ), + .group( + .init( + groupName: "group3", + sourceRoot: nil, + children: [ + .singleAddress( + .init( + personName: "c", + sourceRoot: "c", + mailbox: "c", + host: "c" + ) + ) + ] + ) + ), + ] + ) + ), + ] + ) + ) ], #line ), @@ -130,7 +175,8 @@ extension GrammarParser_Envelope_Tests { #line ), ], - parserErrorInputs: [], incompleteMessageInputs: [] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -142,9 +188,10 @@ extension GrammarParser_Envelope_Tests { self.iterateTests( testFunction: GrammarParser().parseOptionalEnvelopeEmailAddresses, validInputs: [ - ("NIL", " ", [], #line), + ("NIL", " ", [], #line) ], - parserErrorInputs: [], incompleteMessageInputs: [] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -153,22 +200,42 @@ extension GrammarParser_Envelope_Tests { extension GrammarParser_Envelope_Tests { func testParseEnvelopeTo_valid() { - TestUtilities.withParseBuffer(#"("date" "subject" (("name1" "adl1" "mailbox1" "host1")) (("name2" "adl2" "mailbox2" "host2")) (("name3" "adl3" "mailbox3" "host3")) (("name4" "adl4" "mailbox4" "host4") ("name5" "adl5" "mailbox5" "host5")) (("name6" "adl6" "mailbox6" "host6")("name7" "adl7" "mailbox7" "host7")) (("name8" "adl8" "mailbox8" "host8")) "someone" "messageid")"#) { (buffer) in + TestUtilities.withParseBuffer( + #"("date" "subject" (("name1" "adl1" "mailbox1" "host1")) (("name2" "adl2" "mailbox2" "host2")) (("name3" "adl3" "mailbox3" "host3")) (("name4" "adl4" "mailbox4" "host4") ("name5" "adl5" "mailbox5" "host5")) (("name6" "adl6" "mailbox6" "host6")("name7" "adl7" "mailbox7" "host7")) (("name8" "adl8" "mailbox8" "host8")) "someone" "messageid")"# + ) { (buffer) in let envelope = try GrammarParser().parseEnvelope(buffer: &buffer, tracker: .testTracker) XCTAssertEqual(envelope.date, "date") XCTAssertEqual(envelope.subject, "subject") - XCTAssertEqual(envelope.from, [.singleAddress(.init(personName: "name1", sourceRoot: "adl1", mailbox: "mailbox1", host: "host1"))]) - XCTAssertEqual(envelope.sender, [.singleAddress(.init(personName: "name2", sourceRoot: "adl2", mailbox: "mailbox2", host: "host2"))]) - XCTAssertEqual(envelope.reply, [.singleAddress(.init(personName: "name3", sourceRoot: "adl3", mailbox: "mailbox3", host: "host3"))]) - XCTAssertEqual(envelope.to, [ - .singleAddress(.init(personName: "name4", sourceRoot: "adl4", mailbox: "mailbox4", host: "host4")), - .singleAddress(.init(personName: "name5", sourceRoot: "adl5", mailbox: "mailbox5", host: "host5")), - ]) - XCTAssertEqual(envelope.cc, [ - .singleAddress(.init(personName: "name6", sourceRoot: "adl6", mailbox: "mailbox6", host: "host6")), - .singleAddress(.init(personName: "name7", sourceRoot: "adl7", mailbox: "mailbox7", host: "host7")), - ]) - XCTAssertEqual(envelope.bcc, [.singleAddress(.init(personName: "name8", sourceRoot: "adl8", mailbox: "mailbox8", host: "host8"))]) + XCTAssertEqual( + envelope.from, + [.singleAddress(.init(personName: "name1", sourceRoot: "adl1", mailbox: "mailbox1", host: "host1"))] + ) + XCTAssertEqual( + envelope.sender, + [.singleAddress(.init(personName: "name2", sourceRoot: "adl2", mailbox: "mailbox2", host: "host2"))] + ) + XCTAssertEqual( + envelope.reply, + [.singleAddress(.init(personName: "name3", sourceRoot: "adl3", mailbox: "mailbox3", host: "host3"))] + ) + XCTAssertEqual( + envelope.to, + [ + .singleAddress(.init(personName: "name4", sourceRoot: "adl4", mailbox: "mailbox4", host: "host4")), + .singleAddress(.init(personName: "name5", sourceRoot: "adl5", mailbox: "mailbox5", host: "host5")), + ] + ) + XCTAssertEqual( + envelope.cc, + [ + .singleAddress(.init(personName: "name6", sourceRoot: "adl6", mailbox: "mailbox6", host: "host6")), + .singleAddress(.init(personName: "name7", sourceRoot: "adl7", mailbox: "mailbox7", host: "host7")), + ] + ) + XCTAssertEqual( + envelope.bcc, + [.singleAddress(.init(personName: "name8", sourceRoot: "adl8", mailbox: "mailbox8", host: "host8"))] + ) XCTAssertEqual(envelope.inReplyTo, "someone") XCTAssertEqual(envelope.messageID, "messageid") } diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Fetch+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Fetch+Tests.swift index e6d28e215..017313f4a 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Fetch+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Fetch+Tests.swift @@ -35,15 +35,21 @@ extension GrammarParser_Fetch_Tests { ("BODY", " ", .bodyStructure(extensions: false), #line), ("BODYSTRUCTURE", " ", .bodyStructure(extensions: true), #line), ("UID", " ", .uid, #line), - ("BODY[1]<1.2>", " ", .bodySection(peek: false, .init(part: [1], kind: .complete), 1 ... 2 as ClosedRange), #line), + ( + "BODY[1]<1.2>", " ", + .bodySection(peek: false, .init(part: [1], kind: .complete), 1...2 as ClosedRange), #line + ), ("BODY[1.TEXT]", " ", .bodySection(peek: false, .init(part: [1], kind: .text), nil), #line), ("BODY[4.2.TEXT]", " ", .bodySection(peek: false, .init(part: [4, 2], kind: .text), nil), #line), ("BODY[HEADER]", " ", .bodySection(peek: false, .init(kind: .header), nil), #line), - ("BODY.PEEK[HEADER]<3.4>", " ", .bodySection(peek: true, .init(kind: .header), 3 ... 6 as ClosedRange), #line), + ( + "BODY.PEEK[HEADER]<3.4>", " ", .bodySection(peek: true, .init(kind: .header), 3...6 as ClosedRange), + #line + ), ("BODY.PEEK[HEADER]", " ", .bodySection(peek: true, .init(kind: .header), nil), #line), ("BINARY.PEEK[1]", " ", .binary(peek: true, section: [1], partial: nil), #line), - ("BINARY.PEEK[1]<3.4>", " ", .binary(peek: true, section: [1], partial: 3 ... 6 as ClosedRange), #line), - ("BINARY[2]<4.5>", " ", .binary(peek: false, section: [2], partial: 4 ... 8 as ClosedRange), #line), + ("BINARY.PEEK[1]<3.4>", " ", .binary(peek: true, section: [1], partial: 3...6 as ClosedRange), #line), + ("BINARY[2]<4.5>", " ", .binary(peek: false, section: [2], partial: 4...8 as ClosedRange), #line), ("BINARY.SIZE[5]", " ", .binarySize(section: [5]), #line), ("X-GM-MSGID", " ", .gmailMessageID, #line), ("X-GM-THRID", " ", .gmailThreadID, #line), @@ -69,9 +75,15 @@ extension GrammarParser_Fetch_Tests { ("RFC822.SIZE 40639", " ", .simpleAttribute(.rfc822Size(40639)), #line), ("FLAGS ()", " ", .simpleAttribute(.flags([])), #line), ("FLAGS (\\seen)", " ", .simpleAttribute(.flags([.seen])), #line), - ("FLAGS (\\seen \\answered \\draft)", " ", .simpleAttribute(.flags([.seen, .answered, .draft])), #line), + ( + "FLAGS (\\seen \\answered \\draft)", " ", .simpleAttribute(.flags([.seen, .answered, .draft])), + #line + ), (")\r\n", " ", .finish, #line), - ("PREVIEW \"Lorem ipsum dolor sit amet\"", " ", .simpleAttribute(.preview(.init("Lorem ipsum dolor sit amet"))), #line), + ( + "PREVIEW \"Lorem ipsum dolor sit amet\"", " ", + .simpleAttribute(.preview(.init("Lorem ipsum dolor sit amet"))), #line + ), ("PREVIEW NIL", " ", .simpleAttribute(.preview(nil)), #line), ], parserErrorInputs: [], @@ -104,12 +116,12 @@ extension GrammarParser_Fetch_Tests { testFunction: GrammarParser().parseFetchModifier, validInputs: [ ("CHANGEDSINCE 2", " ", .changedSince(.init(modificationSequence: 2)), #line), - ("PARTIAL -735:-88032", " ", .partial(.last(735 ... 88_032)), #line), + ("PARTIAL -735:-88032", " ", .partial(.last(735...88_032)), #line), ("test", "\r", .other(.init(key: "test", value: nil)), #line), ("test 1", " ", .other(.init(key: "test", value: .sequence(.set([1])))), #line), ], parserErrorInputs: [ - ("1", " ", #line), + ("1", " ", #line) ], incompleteMessageInputs: [ ("CHANGEDSINCE 1", "", #line), @@ -123,8 +135,11 @@ extension GrammarParser_Fetch_Tests { testFunction: GrammarParser().parseFetchModifiers, validInputs: [ (" (CHANGEDSINCE 2)", " ", [.changedSince(.init(modificationSequence: 2))], #line), - (" (PARTIAL -735:-88032)", " ", [.partial(.last(735 ... 88_032))], #line), - (" (PARTIAL -1:-30 CHANGEDSINCE 98305)", " ", [.partial(.last(1 ... 30)), .changedSince(.init(modificationSequence: 98305))], #line), + (" (PARTIAL -735:-88032)", " ", [.partial(.last(735...88_032))], #line), + ( + " (PARTIAL -1:-30 CHANGEDSINCE 98305)", " ", + [.partial(.last(1...30)), .changedSince(.init(modificationSequence: 98305))], #line + ), ], parserErrorInputs: [], incompleteMessageInputs: [] diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+List+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+List+Tests.swift index e0043ce36..13280b51e 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+List+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+List+Tests.swift @@ -23,7 +23,7 @@ class GrammarParser_List_Tests: XCTestCase, _ParserTestHelpers {} extension GrammarParser_List_Tests { func testWildcard() { let valid: Set = [UInt8(ascii: "%"), UInt8(ascii: "*")] - let invalid: Set = Set(UInt8.min ... UInt8.max).subtracting(valid) + let invalid: Set = Set(UInt8.min...UInt8.max).subtracting(valid) for v in valid { var buffer = TestUtilities.makeParseBuffer(for: String(decoding: [v], as: UTF8.self)) diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Mailbox+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Mailbox+Tests.swift index 0aa61b580..91f513e0c 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Mailbox+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Mailbox+Tests.swift @@ -29,10 +29,20 @@ extension GrammarParser_Mailbox_Tests { ( "LIST (\\oflag1 \\oflag2) NIL inbox", "\r\n", - .list(.init(attributes: [.init("\\oflag1"), .init("\\oflag2")], path: try! .init(name: .inbox), extensions: [:])), + .list( + .init( + attributes: [.init("\\oflag1"), .init("\\oflag2")], + path: try! .init(name: .inbox), + extensions: [:] + ) + ), + #line + ), + ( + "ESEARCH MIN 1 MAX 2", "\r\n", + .extendedSearch(.init(correlator: nil, kind: .sequenceNumber, returnData: [.min(1), .max(2)])), #line ), - ("ESEARCH MIN 1 MAX 2", "\r\n", .extendedSearch(.init(correlator: nil, kind: .sequenceNumber, returnData: [.min(1), .max(2)])), #line), ("1234 EXISTS", "\r\n", .exists(1234), #line), ("5678 RECENT", "\r\n", .recent(5678), #line), ("STATUS INBOX ()", "\r\n", .status(.inbox, .init()), #line), @@ -40,14 +50,26 @@ extension GrammarParser_Mailbox_Tests { ( "LSUB (\\seen \\draft) NIL inbox", "\r\n", - .lsub(.init(attributes: [.init("\\seen"), .init("\\draft")], path: try! .init(name: .inbox), extensions: [:])), + .lsub( + .init( + attributes: [.init("\\seen"), .init("\\draft")], + path: try! .init(name: .inbox), + extensions: [:] + ) + ), #line ), ("SEARCH", "\r\n", .search([]), #line), ("SEARCH 1", "\r\n", .search([1]), #line), ("SEARCH 1 2 3 4 5", "\r\n", .search([1, 2, 3, 4, 5]), #line), - ("NAMESPACE NIL NIL NIL", "\r\n", .namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), #line), - ("SEARCH 1 2 3 (MODSEQ 4)", "\r\n", .searchSort(.init(identifiers: [1, 2, 3], modificationSequence: 4)), #line), + ( + "NAMESPACE NIL NIL NIL", "\r\n", + .namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), #line + ), + ( + "SEARCH 1 2 3 (MODSEQ 4)", "\r\n", + .searchSort(.init(identifiers: [1, 2, 3], modificationSequence: 4)), #line + ), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -77,13 +99,21 @@ extension GrammarParser_Mailbox_Tests { ( "(\\oflag1 \\oflag2) NIL inbox", "\r", - .init(attributes: [.init("\\oflag1"), .init("\\oflag2")], path: try! .init(name: .inbox), extensions: [:]), + .init( + attributes: [.init("\\oflag1"), .init("\\oflag2")], + path: try! .init(name: .inbox), + extensions: [:] + ), #line ), ( "(\\oflag1 \\oflag2) \"d\" inbox", "\r", - .init(attributes: [.init("\\oflag1"), .init("\\oflag2")], path: try! .init(name: .inbox, pathSeparator: "d"), extensions: [:]), + .init( + attributes: [.init("\\oflag1"), .init("\\oflag2")], + path: try! .init(name: .inbox, pathSeparator: "d"), + extensions: [:] + ), #line ), ], diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Message+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Message+Tests.swift index baaa484cb..426bb5525 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Message+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Message+Tests.swift @@ -47,9 +47,25 @@ extension GrammarParser_Message_Tests { } func testParseMessageAttribute() throws { - let components1 = ServerMessageDate.Components(year: 1994, month: 6, day: 25, hour: 1, minute: 2, second: 3, timeZoneMinutes: 0) + let components1 = ServerMessageDate.Components( + year: 1994, + month: 6, + day: 25, + hour: 1, + minute: 2, + second: 3, + timeZoneMinutes: 0 + ) let date1 = ServerMessageDate(components1!) - let components2 = ServerMessageDate.Components(year: 2023, month: 3, day: 8, hour: 12, minute: 16, second: 47, timeZoneMinutes: 8 * 60) + let components2 = ServerMessageDate.Components( + year: 2023, + month: 3, + day: 8, + hour: 12, + minute: 16, + second: 47, + timeZoneMinutes: 8 * 60 + ) let date2 = ServerMessageDate(components2!) self.iterateTests( @@ -61,42 +77,208 @@ extension GrammarParser_Message_Tests { ("RFC822.SIZE 1234", " ", .rfc822Size(1234), #line), ("BINARY.SIZE[3] 4", " ", .binarySize(section: [3], size: 4), #line), (#"INTERNALDATE "25-jun-1994 01:02:03 +0000""#, " ", .internalDate(date1), #line), - (#"INTERNALDATE "8-Mar-2023 12:16:47 +0800""#, " ", .internalDate(date2), #line), // qq.com can return a day without a leading zero + // qq.com can return a day without a leading zero + (#"INTERNALDATE "8-Mar-2023 12:16:47 +0800""#, " ", .internalDate(date2), #line), (#"INTERNALDATE "08-Mar-2023 12:16:47 +0800""#, " ", .internalDate(date2), #line), ( #"ENVELOPE ("date" "subject" (("from1" "from2" "from3" "from4")) (("sender1" "sender2" "sender3" "sender4")) (("reply1" "reply2" "reply3" "reply4")) (("to1" "to2" "to3" "to4")) (("cc1" "cc2" "cc3" "cc4")) (("bcc1" "bcc2" "bcc3" "bcc4")) "inreplyto" "messageid")"#, " ", - .envelope(Envelope( - date: "date", - subject: "subject", - from: [.singleAddress(.init(personName: "from1", sourceRoot: "from2", mailbox: "from3", host: "from4"))], - sender: [.singleAddress(.init(personName: "sender1", sourceRoot: "sender2", mailbox: "sender3", host: "sender4"))], - reply: [.singleAddress(.init(personName: "reply1", sourceRoot: "reply2", mailbox: "reply3", host: "reply4"))], - to: [.singleAddress(.init(personName: "to1", sourceRoot: "to2", mailbox: "to3", host: "to4"))], - cc: [.singleAddress(.init(personName: "cc1", sourceRoot: "cc2", mailbox: "cc3", host: "cc4"))], - bcc: [.singleAddress(.init(personName: "bcc1", sourceRoot: "bcc2", mailbox: "bcc3", host: "bcc4"))], - inReplyTo: "inreplyto", - messageID: "messageid" - )), + .envelope( + Envelope( + date: "date", + subject: "subject", + from: [ + .singleAddress( + .init(personName: "from1", sourceRoot: "from2", mailbox: "from3", host: "from4") + ) + ], + sender: [ + .singleAddress( + .init( + personName: "sender1", + sourceRoot: "sender2", + mailbox: "sender3", + host: "sender4" + ) + ) + ], + reply: [ + .singleAddress( + .init(personName: "reply1", sourceRoot: "reply2", mailbox: "reply3", host: "reply4") + ) + ], + to: [ + .singleAddress(.init(personName: "to1", sourceRoot: "to2", mailbox: "to3", host: "to4")) + ], + cc: [ + .singleAddress(.init(personName: "cc1", sourceRoot: "cc2", mailbox: "cc3", host: "cc4")) + ], + bcc: [ + .singleAddress( + .init(personName: "bcc1", sourceRoot: "bcc2", mailbox: "bcc3", host: "bcc4") + ) + ], + inReplyTo: "inreplyto", + messageID: "messageid" + ) + ), #line ), - (#"BODY (("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 1772 47 NIL NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 2778 40 NIL NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015") NIL NIL NIL)"#, " ", .body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 47)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 1772), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 40)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 2778), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: false), #line), - (#"BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 1772 47 NIL NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 2778 40 NIL NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015") NIL NIL NIL)"#, " ", .body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 47)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 1772), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 40)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 2778), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: true), #line), + ( + #"BODY (("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 1772 47 NIL NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 2778 40 NIL NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015") NIL NIL NIL)"#, + " ", + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 47)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 1772 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 40)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 2778 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: ["BOUNDARY": "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: false + ), #line + ), + ( + #"BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 1772 47 NIL NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 2778 40 NIL NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015") NIL NIL NIL)"#, + " ", + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 47)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 1772 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 40)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 2778 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: ["BOUNDARY": "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ), #line + ), (#"BODYSTRUCTURE ("text")"#, " ", .body(.invalid, hasExtensionData: true), #line), ("RFC822.TEXT NIL", " ", .nilBody(.rfc822Text), #line), ("RFC822.HEADER NIL", " ", .nilBody(.rfc822Header), #line), ("BINARY[4]<5> NIL", " ", .nilBody(.binary(section: [4], offset: 5)), #line), ("BODY[4.TEXT]<5> NIL", " ", .nilBody(.body(section: .init(part: [4], kind: .text), offset: 5)), #line), ("MODSEQ (3)", " ", .fetchModificationResponse(.init(modifierSequenceValue: 3)), #line), - ("X-GM-MSGID 1278455344230334865", " ", .gmailMessageID(1278455344230334865), #line), - ("X-GM-THRID 1278455344230334865", " ", .gmailThreadID(1278455344230334865), #line), - ("X-GM-LABELS (\\Inbox \\Sent Important \"Muy Importante\")", " ", .gmailLabels([GmailLabel("\\Inbox"), GmailLabel("\\Sent"), GmailLabel("Important"), GmailLabel("Muy Importante")]), #line), + ("X-GM-MSGID 1278455344230334865", " ", .gmailMessageID(1_278_455_344_230_334_865), #line), + ("X-GM-THRID 1278455344230334865", " ", .gmailThreadID(1_278_455_344_230_334_865), #line), + ( + "X-GM-LABELS (\\Inbox \\Sent Important \"Muy Importante\")", " ", + .gmailLabels([ + GmailLabel("\\Inbox"), GmailLabel("\\Sent"), GmailLabel("Important"), + GmailLabel("Muy Importante"), + ]), #line + ), ("X-GM-LABELS (foo)", " ", .gmailLabels([GmailLabel("foo")]), #line), ("X-GM-LABELS ()", " ", .gmailLabels([]), #line), (#"X-GM-LABELS (\Drafts)"#, " ", .gmailLabels([GmailLabel(#"\Drafts"#)]), #line), @@ -117,15 +299,17 @@ extension GrammarParser_Message_Tests { testFunction: GrammarParser().parseMessageData, validInputs: [ ("3 EXPUNGE", "\r", .expunge(3), #line), - ("VANISHED 1:3", "\r", .vanished([1 ... 3]), #line), - ("VANISHED (EARLIER) 1:3", "\r", .vanishedEarlier([1 ... 3]), #line), + ("VANISHED 1:3", "\r", .vanished([1...3]), #line), + ("VANISHED (EARLIER) 1:3", "\r", .vanishedEarlier([1...3]), #line), ("GENURLAUTH test", "\r", .generateAuthorizedURL(["test"]), #line), ("GENURLAUTH test1 test2", "\r", .generateAuthorizedURL(["test1", "test2"]), #line), ("URLFETCH url NIL", "\r", .urlFetch([.init(url: "url", data: nil)]), #line), ( "URLFETCH url1 NIL url2 NIL url3 \"data\"", "\r", - .urlFetch([.init(url: "url1", data: nil), .init(url: "url2", data: nil), .init(url: "url3", data: "data")]), + .urlFetch([ + .init(url: "url1", data: nil), .init(url: "url2", data: nil), .init(url: "url3", data: "data"), + ]), #line ), ], diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Response+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Response+Tests.swift index a287ced0a..68f6826ea 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Response+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Response+Tests.swift @@ -25,7 +25,7 @@ extension GrammarParser_Response_Tests { self.iterateTests( testFunction: GrammarParser().parseResponseData, validInputs: [ - ("* CAPABILITY ENABLE\r\n", " ", .capabilityData([.enable]), #line), + ("* CAPABILITY ENABLE\r\n", " ", .capabilityData([.enable]), #line) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -77,7 +77,10 @@ extension GrammarParser_Response_Tests { ("CLOSED", "\r", .closed, #line), ("COMPRESSIONACTIVE", "\r", .compressionActive, #line), ("CONTACTADMIN", "\r", .contactAdmin, #line), - ("COPYUID 443 3:5 6:8", "\r", .uidCopy(.init(destinationUIDValidity: 443, sourceUIDs: [3 ... 5], destinationUIDs: [6 ... 8])), #line), + ( + "COPYUID 443 3:5 6:8", "\r", + .uidCopy(.init(destinationUIDValidity: 443, sourceUIDs: [3...5], destinationUIDs: [6...8])), #line + ), ("CORRUPTION", "\r", .corruption, #line), ("EXPIRED", "\r", .expired, #line), ("EXPUNGEISSUED", "\r", .expungeIssued, #line), @@ -89,8 +92,20 @@ extension GrammarParser_Response_Tests { ("METADATA NOPRIVATE", "\r", .metadataNoPrivate, #line), ("METADATA TOOMANY", "\r", .metadataTooMany, #line), ("MODIFIED 1", "\r", .modified(.set([1])), #line), - ("NAMESPACE NIL NIL NIL", "\r", .namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), #line), - (#"NAMESPACE (("Foo" NIL)) NIL (("Bar" NIL))"#, "\r", .namespace(.init(userNamespace: [.init(string: "Foo", responseExtensions: [:])], otherUserNamespace: [], sharedNamespace: [.init(string: "Bar", char: nil, responseExtensions: [:])])), #line), + ( + "NAMESPACE NIL NIL NIL", "\r", + .namespace(.init(userNamespace: [], otherUserNamespace: [], sharedNamespace: [])), #line + ), + ( + #"NAMESPACE (("Foo" NIL)) NIL (("Bar" NIL))"#, "\r", + .namespace( + .init( + userNamespace: [.init(string: "Foo", responseExtensions: [:])], + otherUserNamespace: [], + sharedNamespace: [.init(string: "Bar", char: nil, responseExtensions: [:])] + ) + ), #line + ), ("NOMODSEQ", "\r", .noModificationSequence, #line), ("NONEXISTENT", "\r", .nonExistent, #line), ("NOPERM", "\r", .noPermission, #line), @@ -98,12 +113,18 @@ extension GrammarParser_Response_Tests { ("OVERQUOTA", "\r", .overQuota, #line), ("PARSE", "\r", .parse, #line), ("PERMANENTFLAGS ()", "\r", .permanentFlags([]), #line), - ("PERMANENTFLAGS (\\Answered \\Seen \\*)", "\r", .permanentFlags([.flag(.answered), .flag(.seen), .wildcard]), #line), + ( + "PERMANENTFLAGS (\\Answered \\Seen \\*)", "\r", + .permanentFlags([.flag(.answered), .flag(.seen), .wildcard]), #line + ), ("PERMANENTFLAGS (\\Answered)", "\r", .permanentFlags([.flag(.answered)]), #line), ("PRIVACYREQUIRED", "\r", .privacyRequired, #line), ("READ-ONLY", "\r", .readOnly, #line), ("READ-WRITE", "\r", .readWrite, #line), - ("REFERRAL imap://localhost/", "\r", .referral(.init(server: .init(host: "localhost"), query: nil)), #line), + ( + "REFERRAL imap://localhost/", "\r", .referral(.init(server: .init(host: "localhost"), query: nil)), + #line + ), ("SERVERBUG", "\r", .serverBug, #line), ("SOMETHING", "\r", .other("SOMETHING", nil), #line), ("TRYCREATE", "\r", .tryCreate, #line), @@ -114,7 +135,10 @@ extension GrammarParser_Response_Tests { ("UNAVAILABLE", "\r", .unavailable, #line), ("UNSEEN 56", "\r", .unseen(56), #line), ("URLMECH INTERNAL INTERNAL", "\r", .urlMechanisms([.init(mechanism: .internal, base64: nil)]), #line), - ("URLMECH INTERNAL INTERNAL=YQ==", "\r", .urlMechanisms([.init(mechanism: .internal, base64: "a")]), #line), + ( + "URLMECH INTERNAL INTERNAL=YQ==", "\r", .urlMechanisms([.init(mechanism: .internal, base64: "a")]), + #line + ), ("URLMECH INTERNAL", "\r", .urlMechanisms([]), #line), ("USEATTR", "\r", .useAttribute, #line), ("some thing", "\r", .other("some", "thing"), #line), @@ -140,7 +164,7 @@ extension GrammarParser_Response_Tests { ("[UNSEEN 1]", "\r", .init(code: .unseen(1), text: ""), #line), ("[UNSEEN 2] ", "\r", .init(code: .unseen(2), text: ""), #line), ("[UNSEEN 2] some text", "\r", .init(code: .unseen(2), text: "some text"), #line), - ("[UIDVALIDITY 1561789793]", "\r", .init(code: .uidValidity(1561789793), text: ""), #line), + ("[UIDVALIDITY 1561789793]", "\r", .init(code: .uidValidity(1_561_789_793), text: ""), #line), ("[UIDNEXT 171]", "\r", .init(code: .uidNext(171), text: ""), #line), ], parserErrorInputs: [], diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Search+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Search+Tests.swift index 4fe6f4770..def0509a4 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Search+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Search+Tests.swift @@ -105,14 +105,20 @@ extension GrammarParser_Search_Tests { ("UNKEYWORD key2", "\r", .unkeyword(Flag.Keyword("key2")!), #line), ("NOT LARGER 1234", "\r", .not(.messageSizeLarger(1234)), #line), ("OR LARGER 6 SMALLER 4", "\r", .or(.messageSizeLarger(6), .messageSizeSmaller(4)), #line), - ("UID 2:4", "\r", .uid(.set(MessageIdentifierSetNonEmpty(set: MessageIdentifierSet(2 ... 4))!)), #line), + ( + "UID 2:4", "\r", .uid(.set(MessageIdentifierSetNonEmpty(set: MessageIdentifierSet(2...4))!)), + #line + ), ("UIDAFTER 33875", "\r", .uidAfter(.id(33_875)), #line), ("UIDAFTER $", "\r", .uidAfter(.lastCommand), #line), ("UIDBEFORE 44371", "\r", .uidBefore(.id(44_371)), #line), ("UIDBEFORE $", "\r", .uidBefore(.lastCommand), #line), - ("2:4", "\r", .sequenceNumbers(.set([2 ... 4])), #line), + ("2:4", "\r", .sequenceNumbers(.set([2...4])), #line), ("(LARGER 1)", "\r", .messageSizeLarger(1), #line), - ("(LARGER 1 SMALLER 5 KEYWORD hello)", "\r", .and([.messageSizeLarger(1), .messageSizeSmaller(5), .keyword(Flag.Keyword("hello")!)]), #line), + ( + "(LARGER 1 SMALLER 5 KEYWORD hello)", "\r", + .and([.messageSizeLarger(1), .messageSizeSmaller(5), .keyword(Flag.Keyword("hello")!)]), #line + ), ("YOUNGER 34", "\r", .younger(34), #line), ("OLDER 45", "\r", .older(45), #line), ("FILTER something", "\r", .filter("something"), #line), @@ -140,7 +146,7 @@ extension GrammarParser_Search_Tests { self.iterateTests( testFunction: GrammarParser().parseSearchReturnDataExtension, validInputs: [ - ("modifier 64", "\r", .init(key: "modifier", value: .sequence(.set([64]))), #line), + ("modifier 64", "\r", .init(key: "modifier", value: .sequence(.set([64]))), #line) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -165,10 +171,13 @@ extension GrammarParser_Search_Tests { ( "MODSEQ \"/flags/\\\\Answered\" priv \"/flags/\\\\Seen\" shared 4", " ", - .init(extensions: [ - .init(flag: .answered): .private, - .init(flag: .seen): .shared, - ], sequenceValue: 4), + .init( + extensions: [ + .init(flag: .answered): .private, + .init(flag: .seen): .shared, + ], + sequenceValue: 4 + ), #line ), ], @@ -185,7 +194,7 @@ extension GrammarParser_Search_Tests { self.iterateTests( testFunction: GrammarParser().parseSearchModificationSequenceExtension, validInputs: [ - (" \"/flags/\\\\Seen\" all", "", .init(key: .init(flag: .seen), value: .all), #line), + (" \"/flags/\\\\Seen\" all", "", .init(key: .init(flag: .seen), value: .all), #line) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -206,9 +215,9 @@ extension GrammarParser_Search_Tests { ("ALL 3,4,5", "\r", .all(.set([3, 4, 5])), #line), ("COUNT 4", "\r", .count(4), #line), ("MODSEQ 4", "\r", .modificationSequence(4), #line), - ("PARTIAL (1:10 108595)", "\r", .partial(.first(1 ... 10), [108595]), #line), - ("PARTIAL (-2:-20 20:24,108595)", "\r", .partial(.last(2 ... 20), [20 ... 24, 108595]), #line), - ("PARTIAL (1:10 NIL)", "\r", .partial(.first(1 ... 10), []), #line), + ("PARTIAL (1:10 108595)", "\r", .partial(.first(1...10), [108595]), #line), + ("PARTIAL (-2:-20 20:24,108595)", "\r", .partial(.last(2...20), [20...24, 108595]), #line), + ("PARTIAL (1:10 NIL)", "\r", .partial(.first(1...10), []), #line), ("modifier 5", "\r", .dataExtension(.init(key: "modifier", value: .sequence(.set([5])))), #line), ], parserErrorInputs: [], @@ -224,8 +233,8 @@ extension GrammarParser_Search_Tests { self.iterateTests( testFunction: GrammarParser().parseSearchReturnData_partial, validInputs: [ - ("PARTIAL (23500:24000 67,100:102)", "\r", .partial(.first(23_500 ... 24_000), [67, 100 ... 102]), #line), - ("PARTIAL (-55:-700 NIL)", "\r", .partial(.last(55 ... 700), []), #line), + ("PARTIAL (23500:24000 67,100:102)", "\r", .partial(.first(23_500...24_000), [67, 100...102]), #line), + ("PARTIAL (-55:-700 NIL)", "\r", .partial(.last(55...700), []), #line), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -255,8 +264,8 @@ extension GrammarParser_Search_Tests { ("SAVE", "\r", .save, #line), ("save", "\r", .save, #line), ("saVE", "\r", .save, #line), - ("PARTIAL 23500:24000", "\r", .partial(.first(23_500 ... 24_000)), #line), - ("partial -1:-100", "\r", .partial(.last(1 ... 100)), #line), + ("PARTIAL 23500:24000", "\r", .partial(.first(23_500...24_000)), #line), + ("partial -1:-100", "\r", .partial(.last(1...100)), #line), ("modifier", "\r", .optionExtension(.init(key: "modifier", value: nil)), #line), ], parserErrorInputs: [], @@ -274,12 +283,15 @@ extension GrammarParser_Search_Tests { validInputs: [ (" RETURN (ALL)", "\r", [.all], #line), (" RETURN (MIN MAX COUNT)", "\r", [.min, .max, .count], #line), - (" RETURN (m1 m2)", "\r", [ - .optionExtension(.init(key: "m1", value: nil)), - .optionExtension(.init(key: "m2", value: nil)), - ], #line), - (" RETURN (PARTIAL 23500:24000)", "\r", [.partial(.first(23_500 ... 24_000))], #line), - (" RETURN (MIN PARTIAL -1:-100 MAX)", "\r", [.min, .partial(.last(1 ... 100)), .max], #line), + ( + " RETURN (m1 m2)", "\r", + [ + .optionExtension(.init(key: "m1", value: nil)), + .optionExtension(.init(key: "m2", value: nil)), + ], #line + ), + (" RETURN (PARTIAL 23500:24000)", "\r", [.partial(.first(23_500...24_000))], #line), + (" RETURN (MIN PARTIAL -1:-100 MAX)", "\r", [.min, .partial(.last(1...100)), .max], #line), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -299,7 +311,7 @@ extension GrammarParser_Search_Tests { ], parserErrorInputs: [], incompleteMessageInputs: [ - ("modifier ", "", #line), + ("modifier ", "", #line) ] ) } @@ -312,10 +324,10 @@ extension GrammarParser_Search_Tests { self.iterateTests( testFunction: GrammarParser().parseSearchSortModificationSequence, validInputs: [ - ("(MODSEQ 123)", "\r", 123, #line), + ("(MODSEQ 123)", "\r", 123, #line) ], parserErrorInputs: [ - ("(MODSEQ a)", "", #line), + ("(MODSEQ a)", "", #line) ], incompleteMessageInputs: [ ("(MODSEQ ", "", #line), diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Sequence.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Sequence.swift index 8a76dc9f9..e15ee12f1 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Sequence.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+Sequence.swift @@ -26,10 +26,16 @@ extension GrammarParser_Sequence_Tests { testFunction: GrammarParser().parseMessageIdentifierSetOrLast, validInputs: [ ("765", " ", .set([765]), #line), - ("1,2:5,7,9:*", " ", .set([MessageIdentifierRange(1), MessageIdentifierRange(2 ... 5), MessageIdentifierRange(7), MessageIdentifierRange(9...)]), #line), + ( + "1,2:5,7,9:*", " ", + .set([ + MessageIdentifierRange(1), MessageIdentifierRange(2...5), + MessageIdentifierRange(7), MessageIdentifierRange(9...), + ]), #line + ), ("1:*", "\r", .set([.all]), #line), - ("1:2", "\r", .set([1 ... 2]), #line), - ("1:2,2:3,3:4", "\r", .set([1 ... 2, 2 ... 3, 3 ... 4]), #line), + ("1:2", "\r", .set([1...2]), #line), + ("1:2,2:3,3:4", "\r", .set([1...2, 2...3, 3...4]), #line), ("$", "\r", .lastCommand, #line), ], parserErrorInputs: [ @@ -57,8 +63,8 @@ extension GrammarParser_Sequence_Tests { ("1", " ", 1, #line), ("123", " ", 123, #line), ("12345", " ", 12345, #line), - ("1234567", " ", 1234567, #line), - ("123456789", " ", 123456789, #line), + ("1234567", " ", 1_234_567, #line), + ("123456789", " ", 123_456_789, #line), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -74,7 +80,7 @@ extension GrammarParser_Sequence_Tests { testFunction: GrammarParser().parseModificationSequenceValue, validInputs: [ ("0", " ", .zero, #line), - ("9223372036854775807", " ", 9223372036854775807, #line), + ("9223372036854775807", " ", 9_223_372_036_854_775_807, #line), ], parserErrorInputs: [ ("9223372036854775808", " ", #line), diff --git a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+UID+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+UID+Tests.swift index b104b3d31..8e6932cf6 100644 --- a/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+UID+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/Grammar/GrammarParser+UID+Tests.swift @@ -30,10 +30,10 @@ extension GrammarParser_UID_Tests { ("123", " ", 123, #line), ], parserErrorInputs: [ - ("0", " ", #line), + ("0", " ", #line) ], incompleteMessageInputs: [ - ("1", "", #line), + ("1", "", #line) ] ) } @@ -48,16 +48,16 @@ extension GrammarParser_UID_Tests { validInputs: [ ("*", "\r\n", MessageIdentifierRange(.max), #line), ("1:*", "\r\n", MessageIdentifierRange.all, #line), - ("12:34", "\r\n", MessageIdentifierRange(12 ... 34), #line), + ("12:34", "\r\n", MessageIdentifierRange(12...34), #line), ("12:*", "\r\n", MessageIdentifierRange(12 ... .max), #line), - ("1:34", "\r\n", MessageIdentifierRange((.min) ... 34), #line), + ("1:34", "\r\n", MessageIdentifierRange((.min)...34), #line), ], parserErrorInputs: [ ("!", " ", #line), ("a", " ", #line), ], incompleteMessageInputs: [ - ("1", "", #line), + ("1", "", #line) ] ) } @@ -71,19 +71,22 @@ extension GrammarParser_UID_Tests { testFunction: GrammarParser().parseUIDSet, validInputs: [ ("1234", "\r\n", MessageIdentifierSet(1234 as UID), #line), - ("12:34", "\r\n", MessageIdentifierSet(MessageIdentifierRange(12 ... 34)), #line), - ("1,2,34:56,78:910,11", "\r\n", MessageIdentifierSet([ - MessageIdentifierRange(1), - MessageIdentifierRange(2), - MessageIdentifierRange(34 ... 56), - MessageIdentifierRange(78 ... 910), - MessageIdentifierRange(11), - ]), #line), + ("12:34", "\r\n", MessageIdentifierSet(MessageIdentifierRange(12...34)), #line), + ( + "1,2,34:56,78:910,11", "\r\n", + MessageIdentifierSet([ + MessageIdentifierRange(1), + MessageIdentifierRange(2), + MessageIdentifierRange(34...56), + MessageIdentifierRange(78...910), + MessageIdentifierRange(11), + ]), #line + ), ("*", "\r\n", MessageIdentifierSet(MessageIdentifierRange(.max)), #line), ("1:*", "\r\n", .all, #line), ], parserErrorInputs: [ - ("a", " ", #line), + ("a", " ", #line) ], incompleteMessageInputs: [ ("1234", "", #line), diff --git a/Tests/NIOIMAPCoreTests/Parser/IMAPParserTests.swift b/Tests/NIOIMAPCoreTests/Parser/IMAPParserTests.swift index bd555dc4d..91040cd20 100644 --- a/Tests/NIOIMAPCoreTests/Parser/IMAPParserTests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/IMAPParserTests.swift @@ -32,18 +32,38 @@ protocol _ParserTestHelpers {} final class ParserUnitTests: XCTestCase, _ParserTestHelpers {} extension _ParserTestHelpers { - private func iterateTestInputs_generic(_ inputs: [(String, String, T, UInt)], file: StaticString = #filePath, testFunction: (inout ParseBuffer, StackTracker) throws -> T) { + private func iterateTestInputs_generic( + _ inputs: [(String, String, T, UInt)], + file: StaticString = #filePath, + testFunction: (inout ParseBuffer, StackTracker) throws -> T + ) { for (input, terminator, expected, line) in inputs { - TestUtilities.withParseBuffer(input, terminator: terminator, shouldRemainUnchanged: false, file: file, line: line) { (buffer) in + TestUtilities.withParseBuffer( + input, + terminator: terminator, + shouldRemainUnchanged: false, + file: file, + line: line + ) { (buffer) in let testValue = try testFunction(&buffer, .testTracker) XCTAssertEqual(testValue, expected, file: file, line: line) } } } - private func iterateInvalidTestInputs_ParserError_generic(_ inputs: [(String, String, UInt)], file: StaticString = #filePath, testFunction: (inout ParseBuffer, StackTracker) throws -> T) { + private func iterateInvalidTestInputs_ParserError_generic( + _ inputs: [(String, String, UInt)], + file: StaticString = #filePath, + testFunction: (inout ParseBuffer, StackTracker) throws -> T + ) { for (input, terminator, line) in inputs { - TestUtilities.withParseBuffer(input, terminator: terminator, shouldRemainUnchanged: true, file: file, line: line) { (buffer) in + TestUtilities.withParseBuffer( + input, + terminator: terminator, + shouldRemainUnchanged: true, + file: file, + line: line + ) { (buffer) in XCTAssertThrowsError(try testFunction(&buffer, .testTracker), file: file, line: line) { e in XCTAssertTrue(e is ParserError, "Expected ParserError, got \(e)", file: file, line: line) } @@ -51,27 +71,62 @@ extension _ParserTestHelpers { } } - private func iterateInvalidTestInputs_IncompleteMessage_generic(_ inputs: [(String, String, UInt)], file: StaticString = #filePath, testFunction: (inout ParseBuffer, StackTracker) throws -> T) { + private func iterateInvalidTestInputs_IncompleteMessage_generic( + _ inputs: [(String, String, UInt)], + file: StaticString = #filePath, + testFunction: (inout ParseBuffer, StackTracker) throws -> T + ) { for (input, terminator, line) in inputs { - TestUtilities.withParseBuffer(input, terminator: terminator, shouldRemainUnchanged: true, file: file, line: line) { (buffer) in + TestUtilities.withParseBuffer( + input, + terminator: terminator, + shouldRemainUnchanged: true, + file: file, + line: line + ) { (buffer) in XCTAssertThrowsError(try testFunction(&buffer, .testTracker), file: file, line: line) { e in - XCTAssertTrue(e is IncompleteMessage, "Expected IncompleteMessage, got \(e)", file: file, line: line) + XCTAssertTrue( + e is IncompleteMessage, + "Expected IncompleteMessage, got \(e)", + file: file, + line: line + ) } } } } - private func iterateTestInputs(_ inputs: [(String, String, UInt)], file: StaticString = #filePath, testFunction: (inout ParseBuffer, StackTracker) throws -> Void) { + private func iterateTestInputs( + _ inputs: [(String, String, UInt)], + file: StaticString = #filePath, + testFunction: (inout ParseBuffer, StackTracker) throws -> Void + ) { for (input, terminator, line) in inputs { - TestUtilities.withParseBuffer(input, terminator: terminator, shouldRemainUnchanged: false, file: file, line: line) { (buffer) in + TestUtilities.withParseBuffer( + input, + terminator: terminator, + shouldRemainUnchanged: false, + file: file, + line: line + ) { (buffer) in try testFunction(&buffer, .testTracker) } } } - private func iterateInvalidTestInputs_ParserError(_ inputs: [(String, String, UInt)], file: StaticString = #filePath, testFunction: (inout ParseBuffer, StackTracker) throws -> Void) { + private func iterateInvalidTestInputs_ParserError( + _ inputs: [(String, String, UInt)], + file: StaticString = #filePath, + testFunction: (inout ParseBuffer, StackTracker) throws -> Void + ) { for (input, terminator, line) in inputs { - TestUtilities.withParseBuffer(input, terminator: terminator, shouldRemainUnchanged: true, file: file, line: line) { (buffer) in + TestUtilities.withParseBuffer( + input, + terminator: terminator, + shouldRemainUnchanged: true, + file: file, + line: line + ) { (buffer) in XCTAssertThrowsError(try testFunction(&buffer, .testTracker), file: file, line: line) { e in XCTAssertTrue(e is ParserError, "Expected ParserError, got \(e)", file: file, line: line) } @@ -79,11 +134,26 @@ extension _ParserTestHelpers { } } - private func iterateInvalidTestInputs_IncompleteMessage(_ inputs: [(String, String, UInt)], file: StaticString = #filePath, testFunction: (inout ParseBuffer, StackTracker) throws -> Void) { + private func iterateInvalidTestInputs_IncompleteMessage( + _ inputs: [(String, String, UInt)], + file: StaticString = #filePath, + testFunction: (inout ParseBuffer, StackTracker) throws -> Void + ) { for (input, terminator, line) in inputs { - TestUtilities.withParseBuffer(input, terminator: terminator, shouldRemainUnchanged: true, file: file, line: line) { (buffer) in + TestUtilities.withParseBuffer( + input, + terminator: terminator, + shouldRemainUnchanged: true, + file: file, + line: line + ) { (buffer) in XCTAssertThrowsError(try testFunction(&buffer, .testTracker), file: file, line: line) { e in - XCTAssertTrue(e is IncompleteMessage, "Expected IncompleteMessage, got \(e)", file: file, line: line) + XCTAssertTrue( + e is IncompleteMessage, + "Expected IncompleteMessage, got \(e)", + file: file, + line: line + ) } } } @@ -103,7 +173,11 @@ extension _ParserTestHelpers { ) { self.iterateTestInputs_generic(validInputs, file: file, testFunction: testFunction) self.iterateInvalidTestInputs_ParserError_generic(parserErrorInputs, file: file, testFunction: testFunction) - self.iterateInvalidTestInputs_IncompleteMessage_generic(incompleteMessageInputs, file: file, testFunction: testFunction) + self.iterateInvalidTestInputs_IncompleteMessage_generic( + incompleteMessageInputs, + file: file, + testFunction: testFunction + ) } /// Convenience function to run a variety of happy and non-happy tests. @@ -143,9 +217,15 @@ extension ParserUnitTests { let c2_5 = try parser.parseCommandStream(buffer: &buffer) let c3 = try parser.parseCommandStream(buffer: &buffer) XCTAssertEqual(buffer.readableBytes, 0) - XCTAssertEqual(c1, SynchronizedCommand(.tagged(TaggedCommand(tag: "1", command: .noop)), numberOfSynchronisingLiterals: 1)) + XCTAssertEqual( + c1, + SynchronizedCommand(.tagged(TaggedCommand(tag: "1", command: .noop)), numberOfSynchronisingLiterals: 1) + ) XCTAssertEqual(c2_1, SynchronizedCommand(.append(.start(tag: "2", appendingTo: .inbox)))) - XCTAssertEqual(c2_2, SynchronizedCommand(.append(.beginMessage(message: .init(options: .none, data: .init(byteCount: 10)))))) + XCTAssertEqual( + c2_2, + SynchronizedCommand(.append(.beginMessage(message: .init(options: .none, data: .init(byteCount: 10))))) + ) XCTAssertEqual(c2_3, SynchronizedCommand(.append(.messageBytes("0123456789")))) XCTAssertEqual(c2_4, SynchronizedCommand(.append(.endMessage))) XCTAssertEqual(c2_5, SynchronizedCommand(.append(.finish))) @@ -156,13 +236,15 @@ extension ParserUnitTests { } func testCommandToStreamToCommand_catenateExampleOne() { - var buffer = ByteBuffer(string: #"1 NOOP\#r\#n"# + - #"A003 APPEND Drafts (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {42}\#r\#n"# + - #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME" "# + - #"URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1" TEXT {42}\#r\#n"# + - #"\#r\#n--------------030308070208000400050907\#r\#n"# + - #" URL "/Drafts;UIDVALIDITY=385759045/;UID=30" TEXT {44}\#r\#n"# + - #"\#r\#n--------------030308070208000400050907--\#r\#n)\#r\#n"#) + var buffer = ByteBuffer( + string: #"1 NOOP\#r\#n"# + + #"A003 APPEND Drafts (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER" TEXT {42}\#r\#n"# + + #"\#r\#n--------------030308070208000400050907\#r\#n URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME" "# + + #"URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1" TEXT {42}\#r\#n"# + + #"\#r\#n--------------030308070208000400050907\#r\#n"# + + #" URL "/Drafts;UIDVALIDITY=385759045/;UID=30" TEXT {44}\#r\#n"# + + #"\#r\#n--------------030308070208000400050907--\#r\#n)\#r\#n"# + ) var parser = CommandParser() do { @@ -185,21 +267,49 @@ extension ParserUnitTests { let c2_16 = try parser.parseCommandStream(buffer: &buffer) let c2_17 = try parser.parseCommandStream(buffer: &buffer) XCTAssertEqual(buffer.readableBytes, 0) - XCTAssertEqual(c1, SynchronizedCommand(.tagged(TaggedCommand(tag: "1", command: .noop)), numberOfSynchronisingLiterals: 3)) + XCTAssertEqual( + c1, + SynchronizedCommand(.tagged(TaggedCommand(tag: "1", command: .noop)), numberOfSynchronisingLiterals: 3) + ) XCTAssertEqual(c2_1, SynchronizedCommand(.append(.start(tag: "A003", appendingTo: MailboxName("Drafts"))))) - XCTAssertEqual(c2_2, SynchronizedCommand(.append(.beginCatenate(options: .init(flagList: [.seen, .draft, .keyword(.mdnSent)], extensions: [:]))))) - XCTAssertEqual(c2_3, SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")))) + XCTAssertEqual( + c2_2, + SynchronizedCommand( + .append( + .beginCatenate(options: .init(flagList: [.seen, .draft, .keyword(.mdnSent)], extensions: [:])) + ) + ) + ) + XCTAssertEqual( + c2_3, + SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER"))) + ) XCTAssertEqual(c2_4, SynchronizedCommand(.append(.catenateData(.begin(size: 42))))) - XCTAssertEqual(c2_5, SynchronizedCommand(.append(.catenateData(.bytes("\r\n--------------030308070208000400050907\r\n"))))) + XCTAssertEqual( + c2_5, + SynchronizedCommand(.append(.catenateData(.bytes("\r\n--------------030308070208000400050907\r\n")))) + ) XCTAssertEqual(c2_6, SynchronizedCommand(.append(.catenateData(.end)))) - XCTAssertEqual(c2_7, SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME")))) - XCTAssertEqual(c2_8, SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1")))) + XCTAssertEqual( + c2_7, + SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME"))) + ) + XCTAssertEqual( + c2_8, + SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1"))) + ) XCTAssertEqual(c2_9, SynchronizedCommand(.append(.catenateData(.begin(size: 42))))) - XCTAssertEqual(c2_10, SynchronizedCommand(.append(.catenateData(.bytes("\r\n--------------030308070208000400050907\r\n"))))) + XCTAssertEqual( + c2_10, + SynchronizedCommand(.append(.catenateData(.bytes("\r\n--------------030308070208000400050907\r\n")))) + ) XCTAssertEqual(c2_11, SynchronizedCommand(.append(.catenateData(.end)))) XCTAssertEqual(c2_12, SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=30")))) XCTAssertEqual(c2_13, SynchronizedCommand(.append(.catenateData(.begin(size: 44))))) - XCTAssertEqual(c2_14, SynchronizedCommand(.append(.catenateData(.bytes("\r\n--------------030308070208000400050907--\r\n"))))) + XCTAssertEqual( + c2_14, + SynchronizedCommand(.append(.catenateData(.bytes("\r\n--------------030308070208000400050907--\r\n")))) + ) XCTAssertEqual(c2_15, SynchronizedCommand(.append(.catenateData(.end)))) XCTAssertEqual(c2_16, SynchronizedCommand(.append(.endCatenate))) XCTAssertEqual(c2_17, SynchronizedCommand(.append(.finish))) @@ -209,7 +319,10 @@ extension ParserUnitTests { } func testCommandToStreamToCommand_catenateShortExample() { - var buffer = ByteBuffer(string: #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"#) + var buffer = ByteBuffer( + string: + #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"# + ) var parser = CommandParser() do { @@ -220,8 +333,18 @@ extension ParserUnitTests { let c2_5 = try parser.parseCommandStream(buffer: &buffer) XCTAssertEqual(buffer.readableBytes, 0) XCTAssertEqual(c2_1, SynchronizedCommand(.append(.start(tag: "A003", appendingTo: MailboxName("Drafts"))))) - XCTAssertEqual(c2_2, SynchronizedCommand(.append(.beginCatenate(options: .init(flagList: [.seen, .draft, .keyword(.mdnSent)], extensions: [:]))))) - XCTAssertEqual(c2_3, SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")))) + XCTAssertEqual( + c2_2, + SynchronizedCommand( + .append( + .beginCatenate(options: .init(flagList: [.seen, .draft, .keyword(.mdnSent)], extensions: [:])) + ) + ) + ) + XCTAssertEqual( + c2_3, + SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER"))) + ) XCTAssertEqual(c2_4, SynchronizedCommand(.append(.endCatenate))) XCTAssertEqual(c2_5, SynchronizedCommand(.append(.finish))) } catch { @@ -230,16 +353,22 @@ extension ParserUnitTests { } func testCatenate_failsToParseWithExtraSpace() { - var buffer = ByteBuffer(string: #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE ( URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"#) + var buffer = ByteBuffer( + string: + #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) CATENATE ( URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"# + ) var parser = CommandParser() - XCTAssertNoThrow(try parser.parseCommandStream(buffer: &buffer)) // .append(.start) - XCTAssertNoThrow(try parser.parseCommandStream(buffer: &buffer)) // .append(.beginCatenate) + XCTAssertNoThrow(try parser.parseCommandStream(buffer: &buffer)) // .append(.start) + XCTAssertNoThrow(try parser.parseCommandStream(buffer: &buffer)) // .append(.beginCatenate) XCTAssertThrowsError(try parser.parseCommandStream(buffer: &buffer)) } func testCommandToStreamToCommand_catenateAndOptions() { - var buffer = ByteBuffer(string: #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) EXTENSION (extdata) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"#) + var buffer = ByteBuffer( + string: + #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) EXTENSION (extdata) CATENATE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"# + ) var parser = CommandParser() do { @@ -250,8 +379,23 @@ extension ParserUnitTests { let c2_5 = try parser.parseCommandStream(buffer: &buffer) XCTAssertEqual(buffer.readableBytes, 0) XCTAssertEqual(c2_1, SynchronizedCommand(.append(.start(tag: "A003", appendingTo: MailboxName("Drafts"))))) - XCTAssertEqual(c2_2, SynchronizedCommand(.append(.beginCatenate(options: .init(flagList: [.seen, .draft, .keyword(.mdnSent)], extensions: ["EXTENSION": .comp(["extdata"])]))))) - XCTAssertEqual(c2_3, SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")))) + XCTAssertEqual( + c2_2, + SynchronizedCommand( + .append( + .beginCatenate( + options: .init( + flagList: [.seen, .draft, .keyword(.mdnSent)], + extensions: ["EXTENSION": .comp(["extdata"])] + ) + ) + ) + ) + ) + XCTAssertEqual( + c2_3, + SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER"))) + ) XCTAssertEqual(c2_4, SynchronizedCommand(.append(.endCatenate))) XCTAssertEqual(c2_5, SynchronizedCommand(.append(.finish))) } catch { @@ -260,7 +404,10 @@ extension ParserUnitTests { } func testCommandToStreamToCommand_catenateAndOptions_weirdCasing() { - var buffer = ByteBuffer(string: #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) EXTENSION (extdata) cAtEnAtE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"#) + var buffer = ByteBuffer( + string: + #"A003 APPEND "Drafts" (\Seen \Draft $MDNSent) EXTENSION (extdata) cAtEnAtE (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")\#r\#n"# + ) var parser = CommandParser() do { @@ -271,8 +418,23 @@ extension ParserUnitTests { let c2_5 = try parser.parseCommandStream(buffer: &buffer) XCTAssertEqual(buffer.readableBytes, 0) XCTAssertEqual(c2_1, SynchronizedCommand(.append(.start(tag: "A003", appendingTo: MailboxName("Drafts"))))) - XCTAssertEqual(c2_2, SynchronizedCommand(.append(.beginCatenate(options: .init(flagList: [.seen, .draft, .keyword(.mdnSent)], extensions: ["EXTENSION": .comp(["extdata"])]))))) - XCTAssertEqual(c2_3, SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER")))) + XCTAssertEqual( + c2_2, + SynchronizedCommand( + .append( + .beginCatenate( + options: .init( + flagList: [.seen, .draft, .keyword(.mdnSent)], + extensions: ["EXTENSION": .comp(["extdata"])] + ) + ) + ) + ) + ) + XCTAssertEqual( + c2_3, + SynchronizedCommand(.append(.catenateURL("/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER"))) + ) XCTAssertEqual(c2_4, SynchronizedCommand(.append(.endCatenate))) XCTAssertEqual(c2_5, SynchronizedCommand(.append(.finish))) } catch { @@ -331,7 +493,7 @@ extension ParserUnitTests { (#"("a" "b" "c" "d")"#, "", .init(personName: "a", sourceRoot: "b", mailbox: "c", host: "d"), #line), ], parserErrorInputs: [ - ("(NIL NIL NIL NIL ", "\r", #line), + ("(NIL NIL NIL NIL ", "\r", #line) ], incompleteMessageInputs: [ ("", "", #line), @@ -457,7 +619,16 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseAuthenticatedURL, validInputs: [ - ("imap://localhost/test/;UID=123", " ", .init(server: .init(host: "localhost"), messagePath: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123))), #line), + ( + "imap://localhost/test/;UID=123", " ", + .init( + server: .init(host: "localhost"), + messagePath: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ) + ), #line + ) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -475,9 +646,24 @@ extension ParserUnitTests { ( "imap://localhost/test/;UID=123;URLAUTH=anonymous:INTERNAL:01234567890123456789012345678901", " ", - .init(networkMessagePath: .init(server: .init(host: "localhost"), messagePath: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123))), authenticatedURL: .init(authenticatedURL: .init(access: .anonymous), verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "01234567890123456789012345678901")))), + .init( + networkMessagePath: .init( + server: .init(host: "localhost"), + messagePath: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ) + ), + authenticatedURL: .init( + authenticatedURL: .init(access: .anonymous), + verifier: .init( + urlAuthMechanism: .internal, + encodedAuthenticationURL: .init(data: "01234567890123456789012345678901") + ) + ) + ), #line - ), + ) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -495,9 +681,18 @@ extension ParserUnitTests { ( "imap://localhost/test/;UID=123;URLAUTH=anonymous", " ", - .init(authenticatedURL: .init(server: .init(host: "localhost"), messagePath: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123))), authenticatedURLRump: .init(access: .anonymous)), + .init( + authenticatedURL: .init( + server: .init(host: "localhost"), + messagePath: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ) + ), + authenticatedURLRump: .init(access: .anonymous) + ), #line - ), + ) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -649,7 +844,7 @@ extension ParserUnitTests { ("CHANGEDSINCE a", "", #line), ], incompleteMessageInputs: [ - ("CHANGEDSINCE 1", "", #line), + ("CHANGEDSINCE 1", "", #line) ] ) } @@ -670,7 +865,7 @@ extension ParserUnitTests { ("UNCHANGEDSINCE a", "", #line), ], incompleteMessageInputs: [ - ("UNCHANGEDSINCE 1", "", #line), + ("UNCHANGEDSINCE 1", "", #line) ] ) } @@ -685,7 +880,10 @@ extension ParserUnitTests { validInputs: [ ("+ OK\r\n", " ", .responseText(.init(code: nil, text: "OK")), #line), ("+ YQ==\r\n", " ", .data("a"), #line), - ("+ IDLE accepted, awaiting DONE command.\r\n", " ", .responseText(.init(code: nil, text: "IDLE accepted, awaiting DONE command.")), #line), + ( + "+ IDLE accepted, awaiting DONE command.\r\n", " ", + .responseText(.init(code: nil, text: "IDLE accepted, awaiting DONE command.")), #line + ), ("+ \r\n", " ", .responseText(.init(code: nil, text: "")), #line), ("+\r\n", " ", .responseText(.init(code: nil, text: "")), #line), ], @@ -725,13 +923,16 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseCreateParameters, validInputs: [ - (" (param1 param2)", "\r", [.labelled(.init(key: "param1", value: nil)), .labelled(.init(key: "param2", value: nil))], #line), + ( + " (param1 param2)", "\r", + [.labelled(.init(key: "param1", value: nil)), .labelled(.init(key: "param2", value: nil))], #line + ) ], parserErrorInputs: [ - (" (param1", "\r", #line), + (" (param1", "\r", #line) ], incompleteMessageInputs: [ - (" (param1", "", #line), + (" (param1", "", #line) ] ) } @@ -770,7 +971,10 @@ extension ParserUnitTests { for (input, line) in inputs { TestUtilities.withParseBuffer(input, terminator: " ") { (buffer) in - XCTAssertNoThrow(try GrammarParser().parseConditionalStoreParameter(buffer: &buffer, tracker: .testTracker), line: line) + XCTAssertNoThrow( + try GrammarParser().parseConditionalStoreParameter(buffer: &buffer, tracker: .testTracker), + line: line + ) } } } @@ -781,9 +985,13 @@ extension ParserUnitTests { extension ParserUnitTests { func testContinuationRequest_valid() { let inputs: [(String, String, ContinuationRequest, UInt)] = [ - ("+ Ready for additional command text\r\n", "", .responseText(.init(text: "Ready for additional command text")), #line), + ( + "+ Ready for additional command text\r\n", "", + .responseText(.init(text: "Ready for additional command text")), #line + ), ("+ \r\n", "", .responseText(.init(text: "")), #line), - ("+\r\n", "", .responseText(.init(text: "")), #line), // This is not standard conformant, but we’re allowing this. + // This is not standard conformant, but we’re allowing this. + ("+\r\n", "", .responseText(.init(text: "")), #line), ] self.iterateTests( testFunction: GrammarParser().parseContinuationRequest, @@ -864,10 +1072,8 @@ extension ParserUnitTests { ("=", " ", .empty, #line), ("YQ==", " ", .init("a"), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -882,10 +1088,8 @@ extension ParserUnitTests { (";AUTH=*", " ", .any, #line), (";AUTH=test", " ", .type(.init(authenticationType: "test")), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -898,12 +1102,15 @@ extension ParserUnitTests { testFunction: GrammarParser().parseAbsoluteMessagePath, validInputs: [ ("/", " ", .init(command: nil), #line), - ("/test", " ", .init(command: .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"))))), #line), - ], - parserErrorInputs: [ + ( + "/test", " ", + .init( + command: .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test")))) + ), #line + ), ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -915,14 +1122,39 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseURLCommand, validInputs: [ - ("test", " ", .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test")))), #line), - ("test/;UID=123", " ", .fetch(path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), authenticatedURL: nil), #line), - ("test/;UID=123;URLAUTH=anonymous:INTERNAL:01234567890123456789012345678901", " ", .fetch(path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), authenticatedURL: .init(authenticatedURL: .init(access: .anonymous), verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "01234567890123456789012345678901")))), #line), - ], - parserErrorInputs: [ + ( + "test", " ", .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test")))), + #line + ), + ( + "test/;UID=123", " ", + .fetch( + path: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ), + authenticatedURL: nil + ), #line + ), + ( + "test/;UID=123;URLAUTH=anonymous:INTERNAL:01234567890123456789012345678901", " ", + .fetch( + path: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ), + authenticatedURL: .init( + authenticatedURL: .init(access: .anonymous), + verifier: .init( + urlAuthMechanism: .internal, + encodedAuthenticationURL: .init(data: "01234567890123456789012345678901") + ) + ) + ), #line + ), ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -939,10 +1171,10 @@ extension ParserUnitTests { ("/;UID=123", " ", .init(uid: 123), #line), ], parserErrorInputs: [ - ("a", " ", #line), + ("a", " ", #line) ], incompleteMessageInputs: [ - ("/;UID=1", "", #line), + ("/;UID=1", "", #line) ] ) } @@ -960,10 +1192,10 @@ extension ParserUnitTests { (";UID=123", " ", .init(uid: 123), #line), ], parserErrorInputs: [ - ("a", " ", #line), + ("a", " ", #line) ], incompleteMessageInputs: [ - (";UID=1", "", #line), + (";UID=1", "", #line) ] ) } @@ -976,12 +1208,19 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseIURLAuth, validInputs: [ - (";URLAUTH=anonymous:INTERNAL:01234567890123456789012345678901", " ", .init(authenticatedURL: .init(access: .anonymous), verifier: .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "01234567890123456789012345678901"))), #line), - ], - parserErrorInputs: [ + ( + ";URLAUTH=anonymous:INTERNAL:01234567890123456789012345678901", " ", + .init( + authenticatedURL: .init(access: .anonymous), + verifier: .init( + urlAuthMechanism: .internal, + encodedAuthenticationURL: .init(data: "01234567890123456789012345678901") + ) + ), #line + ) ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -997,14 +1236,20 @@ extension ParserUnitTests { ( ";EXPIRE=1234-12-23T12:34:56;URLAUTH=anonymous", " ", - .init(expire: .init(dateTime: .init(date: .init(year: 1234, month: 12, day: 23), time: .init(hour: 12, minute: 34, second: 56))), access: .anonymous), + .init( + expire: .init( + dateTime: .init( + date: .init(year: 1234, month: 12, day: 23), + time: .init(hour: 12, minute: 34, second: 56) + ) + ), + access: .anonymous + ), #line ), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1016,12 +1261,16 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseAuthenticatedURLVerifier, validInputs: [ - (":INTERNAL:01234567890123456789012345678901", " ", .init(urlAuthMechanism: .internal, encodedAuthenticationURL: .init(data: "01234567890123456789012345678901")), #line), - ], - parserErrorInputs: [ + ( + ":INTERNAL:01234567890123456789012345678901", " ", + .init( + urlAuthMechanism: .internal, + encodedAuthenticationURL: .init(data: "01234567890123456789012345678901") + ), #line + ) ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1037,10 +1286,8 @@ extension ParserUnitTests { ("test", " ", .init(encodedUser: .init(data: "test"), authenticationMechanism: nil), #line), ("test;AUTH=*", " ", .init(encodedUser: .init(data: "test"), authenticationMechanism: .any), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1069,7 +1316,7 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEitemVendorTag, validInputs: [ - ("token-atom", " ", EItemVendorTag(token: "token", atom: "atom"), #line), + ("token-atom", " ", EItemVendorTag(token: "token", atom: "atom"), #line) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -1084,12 +1331,10 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEncodedAuthenticationType, validInputs: [ - ("hello%FF", " ", .init(authenticationType: "hello%FF"), #line), + ("hello%FF", " ", .init(authenticationType: "hello%FF"), #line) ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1101,12 +1346,10 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEncodedMailbox, validInputs: [ - ("hello%FF", " ", .init(mailbox: "hello%FF"), #line), - ], - parserErrorInputs: [ + ("hello%FF", " ", .init(mailbox: "hello%FF"), #line) ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1124,15 +1367,19 @@ extension ParserUnitTests { " ", .init( server: .init(host: "localhost"), - query: .fetch(path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), authenticatedURL: nil) + query: .fetch( + path: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ), + authenticatedURL: nil + ) ), #line ), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1144,12 +1391,10 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEncodedSearch, validInputs: [ - ("query%FF", " ", .init(query: "query%FF"), #line), + ("query%FF", " ", .init(query: "query%FF"), #line) ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1161,12 +1406,10 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEncodedSection, validInputs: [ - ("query%FF", " ", .init(section: "query%FF"), #line), - ], - parserErrorInputs: [ + ("query%FF", " ", .init(section: "query%FF"), #line) ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1178,12 +1421,10 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEncodedUser, validInputs: [ - ("query%FF", " ", .init(data: "query%FF"), #line), - ], - parserErrorInputs: [ + ("query%FF", " ", .init(data: "query%FF"), #line) ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1195,13 +1436,13 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEncodedURLAuth, validInputs: [ - ("0123456789abcdef01234567890abcde", "", .init(data: "0123456789abcdef01234567890abcde"), #line), + ("0123456789abcdef01234567890abcde", "", .init(data: "0123456789abcdef01234567890abcde"), #line) ], parserErrorInputs: [ - ("0123456789zbcdef01234567890abcde", "", #line), + ("0123456789zbcdef01234567890abcde", "", #line) ], incompleteMessageInputs: [ - ("0123456789", "", #line), + ("0123456789", "", #line) ] ) } @@ -1216,9 +1457,18 @@ extension ParserUnitTests { validInputs: [ ("ESEARCH", "\r", .init(correlator: nil, kind: .sequenceNumber, returnData: []), #line), ("ESEARCH UID", "\r", .init(correlator: nil, kind: .uid, returnData: []), #line), - ("ESEARCH (TAG \"col\") UID", "\r", .init(correlator: SearchCorrelator(tag: "col"), kind: .uid, returnData: []), #line), - ("ESEARCH (TAG \"col\") UID COUNT 2", "\r", .init(correlator: SearchCorrelator(tag: "col"), kind: .uid, returnData: [.count(2)]), #line), - ("ESEARCH (TAG \"col\") UID MIN 1 MAX 2", "\r", .init(correlator: SearchCorrelator(tag: "col"), kind: .uid, returnData: [.min(1), .max(2)]), #line), + ( + "ESEARCH (TAG \"col\") UID", "\r", + .init(correlator: SearchCorrelator(tag: "col"), kind: .uid, returnData: []), #line + ), + ( + "ESEARCH (TAG \"col\") UID COUNT 2", "\r", + .init(correlator: SearchCorrelator(tag: "col"), kind: .uid, returnData: [.count(2)]), #line + ), + ( + "ESEARCH (TAG \"col\") UID MIN 1 MAX 2", "\r", + .init(correlator: SearchCorrelator(tag: "col"), kind: .uid, returnData: [.min(1), .max(2)]), #line + ), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -1236,13 +1486,23 @@ extension ParserUnitTests { ( ";EXPIRE=1234-12-20T12:34:56", "\r", - Expire(dateTime: FullDateTime(date: FullDate(year: 1234, month: 12, day: 20), time: FullTime(hour: 12, minute: 34, second: 56))), + Expire( + dateTime: FullDateTime( + date: FullDate(year: 1234, month: 12, day: 20), + time: FullTime(hour: 12, minute: 34, second: 56) + ) + ), #line ), ( ";EXPIRE=1234-12-20t12:34:56", "\r", - Expire(dateTime: FullDateTime(date: FullDate(year: 1234, month: 12, day: 20), time: FullTime(hour: 12, minute: 34, second: 56))), + Expire( + dateTime: FullDateTime( + date: FullDate(year: 1234, month: 12, day: 20), + time: FullTime(hour: 12, minute: 34, second: 56) + ) + ), #line ), ], @@ -1280,12 +1540,10 @@ extension ParserUnitTests { " ", .init(date: .init(year: 1234, month: 12, day: 20), time: .init(hour: 11, minute: 22, second: 33)), #line - ), + ) ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1297,13 +1555,13 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseFullDate, validInputs: [ - ("1234-12-23", " ", .init(year: 1234, month: 12, day: 23), #line), + ("1234-12-23", " ", .init(year: 1234, month: 12, day: 23), #line) ], parserErrorInputs: [ - ("a", "", #line), + ("a", "", #line) ], incompleteMessageInputs: [ - ("1234", "", #line), + ("1234", "", #line) ] ) } @@ -1324,7 +1582,7 @@ extension ParserUnitTests { ("1234:56:12", "", #line), ], incompleteMessageInputs: [ - ("1234", "", #line), + ("1234", "", #line) ] ) } @@ -1369,12 +1627,11 @@ extension ParserUnitTests { ("()", " ", [], #line), ("(\\seen)", " ", [.seen], #line), ("(\\seen \\answered \\draft)", " ", [.seen, .answered, .draft], #line), - ("(\\seen \\answered \\draft )", " ", [.seen, .answered, .draft], #line), // iCloud sends a superfluous terminating space + // iCloud sends a superfluous terminating space + ("(\\seen \\answered \\draft )", " ", [.seen, .answered, .draft], #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1470,7 +1727,7 @@ extension ParserUnitTests { ("PARTIAL=a", " ", #line), ], incompleteMessageInputs: [ - ("/;PARTIAL=1", "", #line), + ("/;PARTIAL=1", "", #line) ] ) } @@ -1491,7 +1748,7 @@ extension ParserUnitTests { ("PARTIAL=a", " ", #line), ], incompleteMessageInputs: [ - (";PARTIAL=1", "", #line), + (";PARTIAL=1", "", #line) ] ) } @@ -1508,10 +1765,10 @@ extension ParserUnitTests { ("/;SECTION=abc", " ", URLMessageSection(encodedSection: .init(section: "abc")), #line), ], parserErrorInputs: [ - ("SECTION=a", " ", #line), + ("SECTION=a", " ", #line) ], incompleteMessageInputs: [ - ("/;SECTION=1", "", #line), + ("/;SECTION=1", "", #line) ] ) } @@ -1528,10 +1785,10 @@ extension ParserUnitTests { (";SECTION=abc", " ", URLMessageSection(encodedSection: .init(section: "abc")), #line), ], parserErrorInputs: [ - ("SECTION=a", " ", #line), + ("SECTION=a", " ", #line) ], incompleteMessageInputs: [ - (";SECTION=1", "", #line), + (";SECTION=1", "", #line) ] ) } @@ -1545,14 +1802,26 @@ extension ParserUnitTests { testFunction: GrammarParser().parseIMAPServer, validInputs: [ ("localhost", " ", .init(userAuthenticationMechanism: nil, host: "localhost", port: nil), #line), - (";AUTH=*@localhost", " ", .init(userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), host: "localhost", port: nil), #line), + ( + ";AUTH=*@localhost", " ", + .init( + userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), + host: "localhost", + port: nil + ), #line + ), ("localhost:1234", " ", .init(userAuthenticationMechanism: nil, host: "localhost", port: 1234), #line), - (";AUTH=*@localhost:1234", " ", .init(userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), host: "localhost", port: 1234), #line), - ], - parserErrorInputs: [ + ( + ";AUTH=*@localhost:1234", " ", + .init( + userAuthenticationMechanism: .init(encodedUser: nil, authenticationMechanism: .any), + host: "localhost", + port: 1234 + ), #line + ), ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1568,7 +1837,7 @@ extension ParserUnitTests { ("abc;UIDVALIDITY=123", " ", .init(encodeMailbox: .init(mailbox: "abc"), uidValidity: 123), #line), ], parserErrorInputs: [ - ("¢", " ", #line), + ("¢", " ", #line) ], incompleteMessageInputs: [ ("abc", "", #line), @@ -1591,15 +1860,19 @@ extension ParserUnitTests { " ", .init( server: .init(host: "localhost"), - query: .fetch(path: .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123)), authenticatedURL: nil) + query: .fetch( + path: .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123) + ), + authenticatedURL: nil + ) ), #line ), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1615,10 +1888,8 @@ extension ParserUnitTests { ("\"test\" INTERNAL", " ", .init(urlRump: "test", mechanism: .internal), #line), ("{4}\r\ntest INTERNAL", " ", .init(urlRump: "test", mechanism: .internal), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1633,10 +1904,8 @@ extension ParserUnitTests { ("url NIL", " ", .init(url: "url", data: nil), #line), ("url \"data\"", " ", .init(url: "url", data: "data"), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1648,14 +1917,21 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseRelativeIMAPURL, validInputs: [ - ("/test", " ", .absolutePath(.init(command: .messageList(.init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test")))))), #line), + ( + "/test", " ", + .absolutePath( + .init( + command: .messageList( + .init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"))) + ) + ) + ), #line + ), ("//localhost/", " ", .networkPath(.init(server: .init(host: "localhost"), query: nil)), #line), ("", " ", .empty, #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1667,13 +1943,20 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseEncodedSearchQuery, validInputs: [ - ("test", " ", .init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"), uidValidity: nil)), #line), - ("test?query", " ", .init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"), uidValidity: nil), encodedSearch: .init(query: "query")), #line), - ], - parserErrorInputs: [ + ( + "test", " ", + .init(mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"), uidValidity: nil)), #line + ), + ( + "test?query", " ", + .init( + mailboxUIDValidity: .init(encodeMailbox: .init(mailbox: "test"), uidValidity: nil), + encodedSearch: .init(query: "query") + ), #line + ), ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1700,7 +1983,10 @@ extension ParserUnitTests { ( ";SECTION=test/;PARTIAL=1.2", " ", - .sectionPartial(section: .init(encodedSection: .init(section: "test")), partial: .init(range: .init(offset: 1, length: 2))), + .sectionPartial( + section: .init(encodedSection: .init(section: "test")), + partial: .init(range: .init(offset: 1, length: 2)) + ), #line ), ( @@ -1712,50 +1998,80 @@ extension ParserUnitTests { ( ";UID=123/;SECTION=test", " ", - .uidSectionPartial(uid: .init(uid: 123), section: .init(encodedSection: .init(section: "test")), partial: nil), + .uidSectionPartial( + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "test")), + partial: nil + ), #line ), ( ";UID=123/;PARTIAL=1.2", " ", - .uidSectionPartial(uid: .init(uid: 123), section: nil, partial: .init(range: .init(offset: 1, length: 2))), + .uidSectionPartial( + uid: .init(uid: 123), + section: nil, + partial: .init(range: .init(offset: 1, length: 2)) + ), #line ), ( ";UID=123/;SECTION=test/;PARTIAL=1.2", " ", - .uidSectionPartial(uid: .init(uid: 123), section: .init(encodedSection: .init(section: "test")), partial: .init(range: .init(offset: 1, length: 2))), + .uidSectionPartial( + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "test")), + partial: .init(range: .init(offset: 1, length: 2)) + ), #line ), ( "test;UID=123", " ", - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: nil, partial: nil), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: nil, + partial: nil + ), #line ), ( "test;UID=123/;SECTION=section", " ", - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: .init(encodedSection: .init(section: "section")), partial: nil), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "section")), + partial: nil + ), #line ), ( "test;UID=123/;PARTIAL=1.2", " ", - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: nil, partial: .init(range: .init(offset: 1, length: 2))), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: nil, + partial: .init(range: .init(offset: 1, length: 2)) + ), #line ), ( "test;UID=123/;SECTION=section/;PARTIAL=1.2", " ", - .refUidSectionPartial(ref: .init(encodeMailbox: .init(mailbox: "test")), uid: .init(uid: 123), section: .init(encodedSection: .init(section: "section")), partial: .init(range: .init(offset: 1, length: 2))), + .refUidSectionPartial( + ref: .init(encodeMailbox: .init(mailbox: "test")), + uid: .init(uid: 123), + section: .init(encodedSection: .init(section: "section")), + partial: .init(range: .init(offset: 1, length: 2)) + ), #line ), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1770,38 +2086,61 @@ extension ParserUnitTests { ( "test/;UID=123", " ", - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: nil, range: nil), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: nil, + range: nil + ), #line ), ( "test/;UID=123/;SECTION=section", " ", - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: .init(encodedSection: .init(section: "section")), range: nil), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: .init(encodedSection: .init(section: "section")), + range: nil + ), #line ), ( "test/;UID=123/;PARTIAL=1.2", " ", - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: nil, range: .init(range: .init(offset: 1, length: 2))), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: nil, + range: .init(range: .init(offset: 1, length: 2)) + ), #line ), ( "test/;UID=123/;SECTION=section/;PARTIAL=1.2", " ", - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), iUID: .init(uid: 123), section: .init(encodedSection: .init(section: "section")), range: .init(range: .init(offset: 1, length: 2))), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test")), + iUID: .init(uid: 123), + section: .init(encodedSection: .init(section: "section")), + range: .init(range: .init(offset: 1, length: 2)) + ), #line ), ( "test/;UIDVALIDITY=123/;UID=123/;SECTION=section/;PARTIAL=1.2", " ", - .init(mailboxReference: .init(encodeMailbox: .init(mailbox: "test/"), uidValidity: 123), iUID: .init(uid: 123), section: .init(encodedSection: .init(section: "section")), range: .init(range: .init(offset: 1, length: 2))), + .init( + mailboxReference: .init(encodeMailbox: .init(mailbox: "test/"), uidValidity: 123), + iUID: .init(uid: 123), + section: .init(encodedSection: .init(section: "section")), + range: .init(range: .init(offset: 1, length: 2)) + ), #line ), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -1925,7 +2264,7 @@ extension ParserUnitTests { ("namespace", " ", .namespace, #line), ], parserErrorInputs: [ - ("something", " ", #line), + ("something", " ", #line) ], incompleteMessageInputs: [ ("", "", #line), @@ -1958,7 +2297,10 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseNamespaceResponse, validInputs: [ - ("NAMESPACE nil nil nil", " ", .init(userNamespace: [], otherUserNamespace: [], sharedNamespace: []), #line), + ( + "NAMESPACE nil nil nil", " ", .init(userNamespace: [], otherUserNamespace: [], sharedNamespace: []), + #line + ) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -1974,7 +2316,10 @@ extension ParserUnitTests { testFunction: GrammarParser().parseNamespaceResponseExtension, validInputs: [ (" \"str1\" (\"str2\")", " ", .init(key: "str1", value: ["str2"]), #line), - (" \"str1\" (\"str2\" \"str3\" \"str4\")", " ", .init(key: "str1", value: ["str2", "str3", "str4"]), #line), + ( + " \"str1\" (\"str2\" \"str3\" \"str4\")", " ", .init(key: "str1", value: ["str2", "str3", "str4"]), + #line + ), ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -1994,10 +2339,10 @@ extension ParserUnitTests { ("\r\n", "", #line), ], parserErrorInputs: [ - ("\\", " ", #line), + ("\\", " ", #line) ], incompleteMessageInputs: [ - ("", "", #line), + ("", "", #line) ] ) } @@ -2046,7 +2391,7 @@ extension ParserUnitTests { (#""a\"bc""#, "", #"a"bc"#, #line), ], parserErrorInputs: [ - (#""a\bc""#, "", #line), + (#""a\bc""#, "", #line) ], incompleteMessageInputs: [ ("\"", "", #line), @@ -2096,10 +2441,10 @@ extension ParserUnitTests { ("0", " ", 0, #line), ], parserErrorInputs: [ - ("abcd", " ", #line), + ("abcd", " ", #line) ], incompleteMessageInputs: [ - ("1234", "", #line), + ("1234", "", #line) ] ) } @@ -2121,7 +2466,7 @@ extension ParserUnitTests { ("abcd", " ", #line), ], incompleteMessageInputs: [ - ("1234", "", #line), + ("1234", "", #line) ] ) } @@ -2137,7 +2482,7 @@ extension ParserUnitTests { ("<0.1000000000>", " ", ClosedRange(uncheckedBounds: (0, 999_999_999)), #line), ("<0.4294967290>", " ", ClosedRange(uncheckedBounds: (0, 4_294_967_289)), #line), ("<1.2>", " ", ClosedRange(uncheckedBounds: (1, 2)), #line), - ("<4294967290.2>", " ", ClosedRange(uncheckedBounds: (4294967290, 4294967291)), #line), + ("<4294967290.2>", " ", ClosedRange(uncheckedBounds: (4_294_967_290, 4_294_967_291)), #line), ], parserErrorInputs: [ ("<0.0>", " ", #line), @@ -2169,7 +2514,7 @@ extension ParserUnitTests { ("1.2", " ", .init(offset: 1, length: 2), #line), ], parserErrorInputs: [ - ("a.1", " ", #line), + ("a.1", " ", #line) ], incompleteMessageInputs: [ ("1", "", #line), @@ -2256,10 +2601,10 @@ extension ParserUnitTests { ("1.2.3.4.5", "\r", [1, 2, 3, 4, 5], #line), ], parserErrorInputs: [ - ("", "\r", #line), + ("", "\r", #line) ], incompleteMessageInputs: [ - ("1.", "", #line), + ("1.", "", #line) ] ) } @@ -2277,7 +2622,7 @@ extension ParserUnitTests { ("1.2.3.HEADER", "\r", .init(part: [1, 2, 3], kind: .header), #line), ], parserErrorInputs: [ - ("MIME", "\r", #line), + ("MIME", "\r", #line) ], incompleteMessageInputs: [ ("", "", #line), @@ -2321,12 +2666,32 @@ extension ParserUnitTests { testFunction: GrammarParser().parseSelectParameter, validInputs: [ ("test 1", "\r", .basic(.init(key: "test", value: .sequence(.set([1])))), #line), - ("QRESYNC (1 1)", "\r", .qresync(.init(uidValidity: 1, modificationSequenceValue: 1, knownUIDs: nil, sequenceMatchData: nil)), #line), - ("QRESYNC (1 1 1:2)", "\r", .qresync(.init(uidValidity: 1, modificationSequenceValue: 1, knownUIDs: [1 ... 2], sequenceMatchData: nil)), #line), - ("QRESYNC (1 1 1:2 (1:* 1:*))", "\r", .qresync(.init(uidValidity: 1, modificationSequenceValue: 1, knownUIDs: [1 ... 2], sequenceMatchData: .init(knownSequenceSet: .set(.all), knownUidSet: .set(.all)))), #line), + ( + "QRESYNC (1 1)", "\r", + .qresync( + .init(uidValidity: 1, modificationSequenceValue: 1, knownUIDs: nil, sequenceMatchData: nil) + ), #line + ), + ( + "QRESYNC (1 1 1:2)", "\r", + .qresync( + .init(uidValidity: 1, modificationSequenceValue: 1, knownUIDs: [1...2], sequenceMatchData: nil) + ), #line + ), + ( + "QRESYNC (1 1 1:2 (1:* 1:*))", "\r", + .qresync( + .init( + uidValidity: 1, + modificationSequenceValue: 1, + knownUIDs: [1...2], + sequenceMatchData: .init(knownSequenceSet: .set(.all), knownUidSet: .set(.all)) + ) + ), #line + ), ], parserErrorInputs: [ - ("1", "\r", #line), + ("1", "\r", #line) ], incompleteMessageInputs: [ ("test ", "", #line), @@ -2375,10 +2740,25 @@ extension ParserUnitTests { testFunction: GrammarParser().parseMailboxStatus, validInputs: [ ("MESSAGES 1", ")", .init(messageCount: 1), #line), - ("MESSAGES 1 RECENT 2 UIDNEXT 3 UIDVALIDITY 4 UNSEEN 5 SIZE 6 HIGHESTMODSEQ 7", ")", .init(messageCount: 1, recentCount: 2, nextUID: 3, uidValidity: 4, unseenCount: 5, size: 6, highestModificationSequence: 7), #line), + ( + "MESSAGES 1 RECENT 2 UIDNEXT 3 UIDVALIDITY 4 UNSEEN 5 SIZE 6 HIGHESTMODSEQ 7", ")", + .init( + messageCount: 1, + recentCount: 2, + nextUID: 3, + uidValidity: 4, + unseenCount: 5, + size: 6, + highestModificationSequence: 7 + ), #line + ), ("APPENDLIMIT 257890", ")", .init(appendLimit: 257_890), #line), ("SIZE 81630", ")", .init(size: 81_630), #line), - ("UIDNEXT 95604 HIGHESTMODSEQ 35227 APPENDLIMIT 81818 UIDVALIDITY 33682", ")", .init(nextUID: 95604, uidValidity: 33682, highestModificationSequence: 35227, appendLimit: 81818), #line), + ( + "UIDNEXT 95604 HIGHESTMODSEQ 35227 APPENDLIMIT 81818 UIDVALIDITY 33682", ")", + .init(nextUID: 95604, uidValidity: 33682, highestModificationSequence: 35227, appendLimit: 81818), + #line + ), ], parserErrorInputs: [ ("MESSAGES UNSEEN 3 RECENT 4", "\r", #line), @@ -2404,7 +2784,7 @@ extension ParserUnitTests { ("test 1", " ", .other(.init(key: "test", value: .sequence(.set([1])))), #line), ], parserErrorInputs: [ - ("1", " ", #line), + ("1", " ", #line) ], incompleteMessageInputs: [ ("UNCHANGEDSINCE 1", "", #line), @@ -2425,7 +2805,7 @@ extension ParserUnitTests { ("-X-GM-LABELS (bar)", "\r", .gmailLabels(.remove(silent: false, gmailLabels: [.init("bar")])), #line), ], parserErrorInputs: [ - ("+SOMETHING \\answered", "\r", #line), + ("+SOMETHING \\answered", "\r", #line) ], incompleteMessageInputs: [ ("+", "", #line), @@ -2451,7 +2831,7 @@ extension ParserUnitTests { ("+FLAGS.SILENT \\answered \\seen", "\r", .add(silent: true, list: [.answered, .seen]), #line), ], parserErrorInputs: [ - ("FLAGS.SILEN \\answered", "\r", #line), + ("FLAGS.SILEN \\answered", "\r", #line) ], incompleteMessageInputs: [ ("+FLAGS ", "", #line), @@ -2470,12 +2850,19 @@ extension ParserUnitTests { testFunction: GrammarParser().parseStoreGmailLabels, validInputs: [ ("+X-GM-LABELS (foo)", "\r", .add(silent: false, gmailLabels: [.init("foo")]), #line), - ("-X-GM-LABELS (foo bar)", "\r", .remove(silent: false, gmailLabels: [.init("foo"), .init("bar")]), #line), - ("X-GM-LABELS (foo bar boo far)", "\r", .replace(silent: false, gmailLabels: [.init("foo"), .init("bar"), .init("boo"), .init("far")]), #line), + ( + "-X-GM-LABELS (foo bar)", "\r", .remove(silent: false, gmailLabels: [.init("foo"), .init("bar")]), + #line + ), + ( + "X-GM-LABELS (foo bar boo far)", "\r", + .replace(silent: false, gmailLabels: [.init("foo"), .init("bar"), .init("boo"), .init("far")]), + #line + ), ("X-GM-LABELS.SILENT (foo)", "\r", .replace(silent: true, gmailLabels: [.init("foo")]), #line), ], parserErrorInputs: [ - ("+X-GM-LABEL.SILEN (foo)", "\r", #line), + ("+X-GM-LABEL.SILEN (foo)", "\r", #line) ], incompleteMessageInputs: [ ("+X-GM-LABELS ", "", #line), @@ -2497,10 +2884,10 @@ extension ParserUnitTests { ("SUBScribe INBOX", "\r\n", .subscribe(.inbox), #line), ], parserErrorInputs: [ - ("SUBSCRIBE ", "\r", #line), + ("SUBSCRIBE ", "\r", #line) ], incompleteMessageInputs: [ - ("SUBSCRIBE ", "", #line), + ("SUBSCRIBE ", "", #line) ] ) } @@ -2515,13 +2902,16 @@ extension ParserUnitTests { validInputs: [ ("RENAME box1 box2", "\r", .rename(from: .init("box1"), to: .init("box2"), parameters: [:]), #line), ("rename box3 box4", "\r", .rename(from: .init("box3"), to: .init("box4"), parameters: [:]), #line), - ("RENAME box5 box6 (test)", "\r", .rename(from: .init("box5"), to: .init("box6"), parameters: ["test": nil]), #line), + ( + "RENAME box5 box6 (test)", "\r", + .rename(from: .init("box5"), to: .init("box6"), parameters: ["test": nil]), #line + ), ], parserErrorInputs: [ - ("RENAME box1 ", "\r", #line), + ("RENAME box1 ", "\r", #line) ], incompleteMessageInputs: [ - ("RENAME box1 ", "", #line), + ("RENAME box1 ", "", #line) ] ) } @@ -2560,10 +2950,10 @@ extension ParserUnitTests { ("abc", "+", "abc", #line), ], parserErrorInputs: [ - ("+", "", #line), + ("+", "", #line) ], incompleteMessageInputs: [ - ("", "", #line), + ("", "", #line) ] ) } @@ -2581,10 +2971,10 @@ extension ParserUnitTests { "", .init(tag: "15.16", state: .ok(.init(text: "Fetch completed (0.001 + 0.000 secs)."))), #line - ), + ) ], parserErrorInputs: [ - ("1+5.16 OK Fetch completed (0.001 \r\n", "", #line), + ("1+5.16 OK Fetch completed (0.001 \r\n", "", #line) ], incompleteMessageInputs: [ ("15.16 ", "", #line), @@ -2611,10 +3001,10 @@ extension ParserUnitTests { ("OK", "\n", .ok(.init(text: "")), #line), ], parserErrorInputs: [ - ("OOPS [ALERT] hello1", "\n", #line), + ("OOPS [ALERT] hello1", "\n", #line) ], incompleteMessageInputs: [ - ("OOPS", "", #line), + ("OOPS", "", #line) ] ) } @@ -2627,7 +3017,7 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseTaggedExtension, validInputs: [ - ("label 1", "\r\n", .init(key: "label", value: .sequence(.set([1]))), #line), + ("label 1", "\r\n", .init(key: "label", value: .sequence(.set([1]))), #line) ], parserErrorInputs: [], incompleteMessageInputs: [] @@ -2659,20 +3049,20 @@ extension ParserUnitTests { extension ParserUnitTests { func testParseText() { let invalid: Set = [UInt8(ascii: "\r"), .init(ascii: "\n"), 0] - let valid = Array(Set((UInt8.min ... UInt8.max)).subtracting(invalid).subtracting(128 ... UInt8.max)) + let valid = Array(Set((UInt8.min...UInt8.max)).subtracting(invalid).subtracting(128...UInt8.max)) let validString = String(decoding: valid, as: UTF8.self) self.iterateTests( testFunction: GrammarParser().parseText, validInputs: [ - (validString, "\r", ByteBuffer(string: validString), #line), + (validString, "\r", ByteBuffer(string: validString), #line) ], parserErrorInputs: [ ("\r", "", #line), ("\n", "", #line), - (String(decoding: (UInt8(128) ... UInt8.max), as: UTF8.self), " ", #line), + (String(decoding: (UInt8(128)...UInt8.max), as: UTF8.self), " ", #line), ], incompleteMessageInputs: [ - ("a", "", #line), + ("a", "", #line) ] ) } @@ -2691,10 +3081,10 @@ extension ParserUnitTests { ("%FF", "", [UInt8(ascii: "%"), UInt8(ascii: "F"), UInt8(ascii: "F")], #line), ], parserErrorInputs: [ - ("%GG", " ", #line), + ("%GG", " ", #line) ], incompleteMessageInputs: [ - ("%", "", #line), + ("%", "", #line) ] ) } @@ -2712,10 +3102,10 @@ extension ParserUnitTests { ("=", "", [UInt8(ascii: "=")], #line), ], parserErrorInputs: [ - ("£", " ", #line), + ("£", " ", #line) ], incompleteMessageInputs: [ - ("", "", #line), + ("", "", #line) ] ) } @@ -2733,10 +3123,8 @@ extension ParserUnitTests { ("submit+abc", " ", .submit(.init(data: "abc")), #line), ("user+abc", " ", .user(.init(data: "abc")), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -2754,10 +3142,10 @@ extension ParserUnitTests { ("/", "", [UInt8(ascii: "/")], #line), ], parserErrorInputs: [ - ("£", " ", #line), + ("£", " ", #line) ], incompleteMessageInputs: [ - ("", "", #line), + ("", "", #line) ] ) } @@ -2773,10 +3161,8 @@ extension ParserUnitTests { ("INTERNAL", " ", .internal, #line), ("abcdEFG0123456789", " ", .init("abcdEFG0123456789"), #line), ], - parserErrorInputs: [ - ], - incompleteMessageInputs: [ - ] + parserErrorInputs: [], + incompleteMessageInputs: [] ) } } @@ -2792,10 +3178,10 @@ extension ParserUnitTests { ("UNSUBScribe INBOX", "\r\n", .unsubscribe(.inbox), #line), ], parserErrorInputs: [ - ("UNSUBSCRIBE \r", " ", #line), + ("UNSUBSCRIBE \r", " ", #line) ], incompleteMessageInputs: [ - ("UNSUBSCRIBE", " ", #line), + ("UNSUBSCRIBE", " ", #line) ] ) } @@ -2822,10 +3208,10 @@ extension ParserUnitTests { ("NO", "\n", .no(.init(code: nil, text: "")), #line), ], parserErrorInputs: [ - ("OOPS [ALERT] hello1", "\n", #line), + ("OOPS [ALERT] hello1", "\n", #line) ], incompleteMessageInputs: [ - ("OOPS", "", #line), + ("OOPS", "", #line) ] ) } @@ -2844,7 +3230,7 @@ extension ParserUnitTests { ("\"test\"", " ", "test", #line), ], parserErrorInputs: [ - ("\\\\", "", #line), + ("\\\\", "", #line) ], incompleteMessageInputs: [ ("aaa", "", #line), @@ -2865,10 +3251,10 @@ extension ParserUnitTests { ("token", " ", "token", #line), ], parserErrorInputs: [ - ("1a", " ", #line), + ("1a", " ", #line) ], incompleteMessageInputs: [ - ("token", "", #line), + ("token", "", #line) ] ) } @@ -2881,7 +3267,7 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parse2Digit, validInputs: [ - ("12", " ", 12, #line), + ("12", " ", 12, #line) ], parserErrorInputs: [ ("ab", " ", #line), @@ -2902,7 +3288,7 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parse4Digit, validInputs: [ - ("1234", " ", 1234, #line), + ("1234", " ", 1234, #line) ], parserErrorInputs: [ ("abcd", " ", #line), @@ -2940,8 +3326,13 @@ extension ParserUnitTests { ( "SETQUOTA \"MASSIVE_POOL\" (STORAGE 512 BEANS 50000)", "\r", - Command.setQuota(QuotaRoot("MASSIVE_POOL"), [QuotaLimit(resourceName: "STORAGE", limit: 512), - QuotaLimit(resourceName: "BEANS", limit: 50000)]), + Command.setQuota( + QuotaRoot("MASSIVE_POOL"), + [ + QuotaLimit(resourceName: "STORAGE", limit: 512), + QuotaLimit(resourceName: "BEANS", limit: 50000), + ] + ), #line ), ( @@ -2970,7 +3361,7 @@ extension ParserUnitTests { ("GETQUOTA \"MASSIVE_POOL\"", "\r", Command.getQuota(QuotaRoot("MASSIVE_POOL")), #line), ], parserErrorInputs: [ - ("GETQUOTA", "\r", #line), + ("GETQUOTA", "\r", #line) ], incompleteMessageInputs: [] ) @@ -2984,7 +3375,7 @@ extension ParserUnitTests { ("GETQUOTAROOT Other", "\r", Command.getQuotaRoot(MailboxName("Other")), #line), ], parserErrorInputs: [ - ("GETQUOTAROOT", "\r", #line), + ("GETQUOTAROOT", "\r", #line) ], incompleteMessageInputs: [] ) @@ -2994,7 +3385,7 @@ extension ParserUnitTests { self.iterateTests( testFunction: GrammarParser().parseResponsePayload_quotaRoot, validInputs: [ - ("QUOTAROOT INBOX \"Root\"", "\r", .quotaRoot(.init("INBOX"), .init("Root")), #line), + ("QUOTAROOT INBOX \"Root\"", "\r", .quotaRoot(.init("INBOX"), .init("Root")), #line) ], parserErrorInputs: [ ("QUOTAROOT", "\r", #line), @@ -3015,8 +3406,13 @@ extension ParserUnitTests { ), ( "QUOTA \"Root\" (STORAGE 10 512 BEANS 50 100)", "\r", - .quota(.init("Root"), [QuotaResource(resourceName: "STORAGE", usage: 10, limit: 512), - QuotaResource(resourceName: "BEANS", usage: 50, limit: 100)]), + .quota( + .init("Root"), + [ + QuotaResource(resourceName: "STORAGE", usage: 10, limit: 512), + QuotaResource(resourceName: "BEANS", usage: 50, limit: 100), + ] + ), #line ), ( @@ -3064,7 +3460,7 @@ extension ParserUnitTests { ), ], parserErrorInputs: [ - ("()", "\r", #line), + ("()", "\r", #line) ], incompleteMessageInputs: [] ) @@ -3165,8 +3561,10 @@ extension ParserUnitTests { ), ( "IN (inboxes (name))", "\r", - ExtendedSearchSourceOptions(sourceMailbox: [.inboxes], - scopeOptions: ExtendedSearchScopeOptions(["name": nil])!), + ExtendedSearchSourceOptions( + sourceMailbox: [.inboxes], + scopeOptions: ExtendedSearchScopeOptions(["name": nil])! + ), #line ), ], @@ -3205,36 +3603,47 @@ extension ParserUnitTests { ), ( " IN (inboxes) ALL", "\r", - ExtendedSearchOptions(key: .all, sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])), + ExtendedSearchOptions( + key: .all, + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ), #line ), ( " IN (inboxes) CHARSET Alien ALL", "\r", - ExtendedSearchOptions(key: .all, - charset: "Alien", - sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])), + ExtendedSearchOptions( + key: .all, + charset: "Alien", + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ), #line ), ( " IN (inboxes) RETURN (MIN) ALL", "\r", - ExtendedSearchOptions(key: .all, - returnOptions: [.min], - sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])), + ExtendedSearchOptions( + key: .all, + returnOptions: [.min], + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ), #line ), ( " RETURN (MIN) CHARSET Alien ALL", "\r", - ExtendedSearchOptions(key: .all, - charset: "Alien", - returnOptions: [.min]), + ExtendedSearchOptions( + key: .all, + charset: "Alien", + returnOptions: [.min] + ), #line ), ( " IN (inboxes) RETURN (MIN) CHARSET Alien ALL", "\r", - ExtendedSearchOptions(key: .all, - charset: "Alien", - returnOptions: [.min], - sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes])), + ExtendedSearchOptions( + key: .all, + charset: "Alien", + returnOptions: [.min], + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ), #line ), ], diff --git a/Tests/NIOIMAPCoreTests/Parser/ParserLibraryTests.swift b/Tests/NIOIMAPCoreTests/Parser/ParserLibraryTests.swift index fddb0c463..5baf257b1 100644 --- a/Tests/NIOIMAPCoreTests/Parser/ParserLibraryTests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/ParserLibraryTests.swift @@ -24,34 +24,42 @@ final class ParserLibraryTests: XCTestCase {} extension ParserLibraryTests { func test_parseOptionalWorksForNothing() { var buffer = TestUtilities.makeParseBuffer(for: "") - XCTAssertThrowsError(try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - }) { error in + XCTAssertThrowsError( + try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + } + ) { error in XCTAssertTrue(error is IncompleteMessage) } } func test_parseOptionalWorks() { var buffer = TestUtilities.makeParseBuffer(for: "x") - XCTAssertNoThrow(try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - }) + XCTAssertNoThrow( + try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + } + ) } func test_parseOptionalWorksIfNotPresent() { var buffer = TestUtilities.makeParseBuffer(for: "y") - XCTAssertNoThrow(try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - }) + XCTAssertNoThrow( + try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + } + ) XCTAssertEqual(1, buffer.readableBytes) } func test_parseOptionalCorrectlyResetsForCompositesIfNotEnough() { var buffer = TestUtilities.makeParseBuffer(for: "x") - XCTAssertThrowsError(try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("y", buffer: &buffer, tracker: tracker) - }) { error in + XCTAssertThrowsError( + try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("y", buffer: &buffer, tracker: tracker) + } + ) { error in XCTAssertTrue(error is IncompleteMessage) } XCTAssertEqual(1, buffer.readableBytes) @@ -59,10 +67,12 @@ extension ParserLibraryTests { func test_parseOptionalCorrectlyResetsForCompositesIfNotMatching() { var buffer = TestUtilities.makeParseBuffer(for: "xz") - XCTAssertNoThrow(try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("y", buffer: &buffer, tracker: tracker) - }) + XCTAssertNoThrow( + try PL.parseOptional(buffer: &buffer, tracker: StackTracker.testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("y", buffer: &buffer, tracker: tracker) + } + ) XCTAssertEqual(2, buffer.readableBytes) } } @@ -73,24 +83,36 @@ extension ParserLibraryTests { func test_fixedStringCaseSensitively() { var buffer = TestUtilities.makeParseBuffer(for: "fooFooFOO") - XCTAssertNoThrow(try PL.parseFixedString("fooFooFOO", - caseSensitive: true, - buffer: &buffer, - tracker: .testTracker)) + XCTAssertNoThrow( + try PL.parseFixedString( + "fooFooFOO", + caseSensitive: true, + buffer: &buffer, + tracker: .testTracker + ) + ) buffer = TestUtilities.makeParseBuffer(for: "fooFooFOO") - XCTAssertThrowsError(try PL.parseFixedString("foofoofoo", - caseSensitive: true, - buffer: &buffer, - tracker: .testTracker)) { error in + XCTAssertThrowsError( + try PL.parseFixedString( + "foofoofoo", + caseSensitive: true, + buffer: &buffer, + tracker: .testTracker + ) + ) { error in XCTAssert(error is ParserError) } buffer = TestUtilities.makeParseBuffer(for: "foo") - XCTAssertThrowsError(try PL.parseFixedString("fooFooFOO", - caseSensitive: true, - buffer: &buffer, - tracker: .testTracker)) { error in + XCTAssertThrowsError( + try PL.parseFixedString( + "fooFooFOO", + caseSensitive: true, + buffer: &buffer, + tracker: .testTracker + ) + ) { error in XCTAssertTrue(error is IncompleteMessage) } } @@ -98,40 +120,60 @@ extension ParserLibraryTests { func test_fixedStringCaseInsensitively() { var buffer = TestUtilities.makeParseBuffer(for: "fooFooFOO") - XCTAssertNoThrow(try PL.parseFixedString("fooFooFOO", - buffer: &buffer, - tracker: .testTracker)) + XCTAssertNoThrow( + try PL.parseFixedString( + "fooFooFOO", + buffer: &buffer, + tracker: .testTracker + ) + ) buffer = TestUtilities.makeParseBuffer(for: "fooFooFOO") - XCTAssertNoThrow(try PL.parseFixedString("foofoofoo", - buffer: &buffer, - tracker: .testTracker)) + XCTAssertNoThrow( + try PL.parseFixedString( + "foofoofoo", + buffer: &buffer, + tracker: .testTracker + ) + ) buffer = TestUtilities.makeParseBuffer(for: "foo") - XCTAssertThrowsError(try PL.parseFixedString("fooFooFOO", - buffer: &buffer, - tracker: .testTracker)) { error in + XCTAssertThrowsError( + try PL.parseFixedString( + "fooFooFOO", + buffer: &buffer, + tracker: .testTracker + ) + ) { error in XCTAssertTrue(error is IncompleteMessage) } } func test_fixedStringNonASCII() { var buffer = TestUtilities.makeParseBuffer(for: "fooFooFOÖ") - XCTAssertThrowsError(try PL.parseFixedString("fooFooFOO", - caseSensitive: true, - buffer: &buffer, - tracker: .testTracker)) { error in + XCTAssertThrowsError( + try PL.parseFixedString( + "fooFooFOO", + caseSensitive: true, + buffer: &buffer, + tracker: .testTracker + ) + ) { error in XCTAssert(error is ParserError, "\(error)") } } func test_fixedStringWithLeadingSpaces() { var buffer = TestUtilities.makeParseBuffer(for: String(repeating: " ", count: 500) + "fooFooFOO") - XCTAssertNoThrow(try PL.parseFixedString("fooFooFOO", - caseSensitive: true, - allowLeadingSpaces: true, - buffer: &buffer, - tracker: .testTracker)) + XCTAssertNoThrow( + try PL.parseFixedString( + "fooFooFOO", + caseSensitive: true, + allowLeadingSpaces: true, + buffer: &buffer, + tracker: .testTracker + ) + ) } } @@ -140,21 +182,27 @@ extension ParserLibraryTests { extension ParserLibraryTests { func test_parseZeroOrMoreParsesNothingButThereIsData() { TestUtilities.withParseBuffer("", terminator: "xy") { buffer in - XCTAssertNoThrow(XCTAssertEqual([], - try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - return 1 - })) + XCTAssertNoThrow( + XCTAssertEqual( + [], + try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + return 1 + } + ) + ) } } func test_parseZeroOrMoreParsesNothingNoData() { TestUtilities.withParseBuffer("") { buffer in - XCTAssertThrowsError(try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - }) { error in + XCTAssertThrowsError( + try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + } + ) { error in XCTAssertTrue(error is IncompleteMessage) } } @@ -162,23 +210,31 @@ extension ParserLibraryTests { func test_parseZeroOrMoreParsesOneItemAndThereIsMore() { TestUtilities.withParseBuffer("xx", terminator: "xy") { buffer in - XCTAssertNoThrow(XCTAssertEqual([1], - try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - return 1 - })) + XCTAssertNoThrow( + XCTAssertEqual( + [1], + try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + return 1 + } + ) + ) } } func test_parseZeroOrMoreParsesTwoItemsAndThereIsMore() { TestUtilities.withParseBuffer("xxxx", terminator: "xy") { buffer in - XCTAssertNoThrow(XCTAssertEqual([1, 1], - try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - return 1 - })) + XCTAssertNoThrow( + XCTAssertEqual( + [1, 1], + try PL.parseZeroOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + return 1 + } + ) + ) } } } @@ -188,10 +244,12 @@ extension ParserLibraryTests { extension ParserLibraryTests { func test_parseOneOrMoreParsesNothingButThereIsData() { TestUtilities.withParseBuffer("", terminator: "xy") { buffer in - XCTAssertThrowsError(try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - }) { error in + XCTAssertThrowsError( + try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + } + ) { error in XCTAssert(error is ParserError) } } @@ -199,10 +257,12 @@ extension ParserLibraryTests { func test_parseOneOrMoreParsesNothingNoData() { TestUtilities.withParseBuffer("") { buffer in - XCTAssertThrowsError(try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - }) { error in + XCTAssertThrowsError( + try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + } + ) { error in XCTAssertTrue(error is IncompleteMessage) } } @@ -210,23 +270,31 @@ extension ParserLibraryTests { func test_parseOneOrMoreParsesOneItemAndThereIsMore() { TestUtilities.withParseBuffer("xx", terminator: "xy") { buffer in - XCTAssertNoThrow(XCTAssertEqual([1], - try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - return 1 - })) + XCTAssertNoThrow( + XCTAssertEqual( + [1], + try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + return 1 + } + ) + ) } } func test_parseOneOrMoreParsesTwoItemsAndThereIsMore() { TestUtilities.withParseBuffer("xxxx", terminator: "xy") { buffer in - XCTAssertNoThrow(XCTAssertEqual([1, 1], - try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) - return 1 - })) + XCTAssertNoThrow( + XCTAssertEqual( + [1, 1], + try PL.parseOneOrMore(buffer: &buffer, tracker: .testTracker) { buffer, tracker -> Int in + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + try PL.parseFixedString("x", buffer: &buffer, tracker: tracker) + return 1 + } + ) + ) } } } @@ -243,7 +311,7 @@ extension ParserLibraryTests { for (string, remaining, line) in inputs { var string = ParseBuffer(ByteBuffer(string: string)) let remaining = ParseBuffer(ByteBuffer(string: remaining)) - XCTAssertNoThrow(try PL.parseSpaces(buffer: &string, tracker: .makeNewDefaultLimitStackTracker), line: line) + XCTAssertNoThrow(try PL.parseSpaces(buffer: &string, tracker: .makeNewDefault), line: line) XCTAssertEqual(string, remaining, line: line) } } @@ -263,7 +331,13 @@ extension ParserLibraryTests { var string = ParseBuffer(ByteBuffer(string: string)) var id = UInt64(0) var actualConsumed = 0 - XCTAssertNoThrow((id, actualConsumed) = try PL.parseUnsignedInt64(buffer: &string, tracker: .makeNewDefaultLimitStackTracker), line: line) + XCTAssertNoThrow( + (id, actualConsumed) = try PL.parseUnsignedInt64( + buffer: &string, + tracker: .makeNewDefault + ), + line: line + ) XCTAssertEqual(actualConsumed, consumed, line: line) XCTAssertEqual(id, result, line: line) } @@ -293,8 +367,18 @@ extension ParserLibraryTests { extension ParserLibraryTests { func checkParseNewline(newline: String, line: UInt = #line) { var buffer = TestUtilities.makeParseBuffer(for: newline + "hello, world") - XCTAssertNoThrow(try ParserLibrary.parseNewline(buffer: &buffer, tracker: StackTracker.makeNewDefaultLimitStackTracker), line: line) - XCTAssertNoThrow(try ParserLibrary.parseFixedString("hello, world", buffer: &buffer, tracker: StackTracker.makeNewDefaultLimitStackTracker), line: line) + XCTAssertNoThrow( + try ParserLibrary.parseNewline(buffer: &buffer, tracker: StackTracker.makeNewDefault), + line: line + ) + XCTAssertNoThrow( + try ParserLibrary.parseFixedString( + "hello, world", + buffer: &buffer, + tracker: StackTracker.makeNewDefault + ), + line: line + ) } func testParseNewline() { @@ -313,12 +397,22 @@ extension ParserLibraryTests { func testParseNewlineRecursion() { var buffer = TestUtilities.makeParseBuffer(for: String(repeating: " ", count: 80) + "\r\nhello, world") - XCTAssertNoThrow(try ParserLibrary.parseNewline(buffer: &buffer, tracker: StackTracker(maximumParserStackDepth: 100))) - XCTAssertNoThrow(try ParserLibrary.parseFixedString("hello, world", buffer: &buffer, tracker: StackTracker.makeNewDefaultLimitStackTracker)) + XCTAssertNoThrow( + try ParserLibrary.parseNewline(buffer: &buffer, tracker: StackTracker(maximumParserStackDepth: 100)) + ) + XCTAssertNoThrow( + try ParserLibrary.parseFixedString( + "hello, world", + buffer: &buffer, + tracker: StackTracker.makeNewDefault + ) + ) } func testParseNewlineTooMuchRecursion() { var buffer = TestUtilities.makeParseBuffer(for: String(repeating: " ", count: 200) + "\r\nhello, world") - XCTAssertThrowsError(try ParserLibrary.parseNewline(buffer: &buffer, tracker: StackTracker(maximumParserStackDepth: 100))) + XCTAssertThrowsError( + try ParserLibrary.parseNewline(buffer: &buffer, tracker: StackTracker(maximumParserStackDepth: 100)) + ) } } diff --git a/Tests/NIOIMAPCoreTests/Parser/ResponseParser+Tests.swift b/Tests/NIOIMAPCoreTests/Parser/ResponseParser+Tests.swift index 6eca52b7c..aca7f6e38 100644 --- a/Tests/NIOIMAPCoreTests/Parser/ResponseParser+Tests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/ResponseParser+Tests.swift @@ -53,14 +53,20 @@ extension ResponseParser_Tests { // send some bytes to make sure it's worked buffer = "0123456789" - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.streamingBytes("0123456789")))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.streamingBytes("0123456789"))) + ) XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.streamingEnd))) } func testParseResponseStream() { let inputs: [(String, [ResponseOrContinuationRequest], UInt)] = [ ("+ OK Continue", [.continuationRequest(.responseText(.init(text: "OK Continue")))], #line), - ("1 OK NOOP Completed", [.response(.tagged(.init(tag: "1", state: .ok(.init(text: "NOOP Completed")))))], #line), + ( + "1 OK NOOP Completed", [.response(.tagged(.init(tag: "1", state: .ok(.init(text: "NOOP Completed")))))], + #line + ), ( "* 999 FETCH (FLAGS (\\Seen))", [ @@ -75,10 +81,78 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(12190))), .response( - .fetch(.simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 47)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 1772), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 40)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 2778), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: true))) + .fetch( + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 47)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 1772 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 40)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 2778 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: [ + "BOUNDARY": "Apple-Mail=_0D97185D-4FF1-42FE-9B8F-A0759D299015" + ], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) ), .response(.fetch(.finish)), ], @@ -89,9 +163,54 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(12194))), .response( - .fetch(.simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 50)), fields: .init(parameters: ["CHARSET": "UTF-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 3034), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "_____5C088583DDA30A778CEA0F5BFE2856D1"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: true))) + .fetch( + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 50)), + fields: .init( + parameters: ["CHARSET": "UTF-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 3034 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ], + mediaSubtype: .alternative, + extension: .init( + parameters: ["BOUNDARY": "_____5C088583DDA30A778CEA0F5BFE2856D1"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) ), .response(.fetch(.finish)), ], @@ -102,10 +221,80 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(12180))), .response( - .fetch(.simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 5)), fields: .init(parameters: ["CHARSET": "UTF-8"], id: nil, contentDescription: nil, encoding: .sevenBit, octetCount: 221), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 20)), fields: .init(parameters: ["CHARSET": "UTF-8"], id: nil, contentDescription: nil, encoding: .sevenBit, octetCount: 2075), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "--==_mimepart_5efddab8ca39a_6a343f841aacb93410876c", "CHARSET": "UTF-8"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: true))) + .fetch( + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 5)), + fields: .init( + parameters: ["CHARSET": "UTF-8"], + id: nil, + contentDescription: nil, + encoding: .sevenBit, + octetCount: 221 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 20)), + fields: .init( + parameters: ["CHARSET": "UTF-8"], + id: nil, + contentDescription: nil, + encoding: .sevenBit, + octetCount: 2075 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: [ + "BOUNDARY": + "--==_mimepart_5efddab8ca39a_6a343f841aacb93410876c", + "CHARSET": "UTF-8", + ], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) ), .response(.fetch(.finish)), ], @@ -116,10 +305,76 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(12182))), .response( - .fetch(.simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 4078)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 239844), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 4078)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 239844), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "===============8996999810533184102=="], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: true))) + .fetch( + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 4078)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 239844 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 4078)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 239844 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: ["BOUNDARY": "===============8996999810533184102=="], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) ), .response(.fetch(.finish)), ], @@ -130,7 +385,37 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(12183))), .response( - .fetch(.simpleAttribute(.body(.valid(.singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 603)), fields: .init(parameters: [:], id: nil, contentDescription: nil, encoding: .binary, octetCount: 28803), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: true))) + .fetch( + .simpleAttribute( + .body( + .valid( + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 603)), + fields: .init( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .binary, + octetCount: 28803 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) ), .response(.fetch(.finish)), ], @@ -141,7 +426,37 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(12184))), .response( - .fetch(.simpleAttribute(.body(.valid(.singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 30)), fields: .init(parameters: ["CHARSET": "utf-8"], id: "", contentDescription: nil, encoding: .base64, octetCount: 2340), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: ["EN-US"], location: .init(location: nil, extensions: []))))))), hasExtensionData: true))) + .fetch( + .simpleAttribute( + .body( + .valid( + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 30)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: "", + contentDescription: nil, + encoding: .base64, + octetCount: 2340 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: ["EN-US"], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) ), .response(.fetch(.finish)), ], @@ -153,18 +468,298 @@ extension ResponseParser_Tests { .response(.fetch(.start(12187))), .response( .fetch( - .simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 170)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 6990), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 274)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 18865), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .basic(.init(topLevel: .application, sub: .init("OCTET-STREAM"))), fields: .init(parameters: ["X-UNIX-MODE": "0644", "NAME": "Whiteboard on Webex.key"], id: nil, contentDescription: nil, encoding: .base64, octetCount: 4876604), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "ATTACHMENT", parameters: ["FILENAME": "Whiteboard on Webex.key"]), language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 17)), fields: .init(parameters: ["CHARSET": "us-ascii"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 1143), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .basic(.init(topLevel: .application, sub: .init("PDF"))), fields: .init(parameters: ["X-UNIX-MODE": "0644", "NAME": "Whiteboard on Webex.pdf"], id: nil, contentDescription: nil, encoding: .base64, octetCount: 1191444), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "INLINE", parameters: ["FILENAME": "Whiteboard on Webex.pdf"]), language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 32)), fields: .init(parameters: ["CHARSET": "us-ascii"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 2217), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .basic(.init(topLevel: .application, sub: .init("PDF"))), fields: .init(parameters: ["X-UNIX-MODE": "0666", "NAME": "Resume.pdf"], id: nil, contentDescription: nil, encoding: .base64, octetCount: 217550), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "INLINE", parameters: ["FILENAME": "Resume.pdf"]), language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 62)), fields: .init(parameters: ["CHARSET": "utf-8"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 4450), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .mixed, extension: .init(parameters: ["BOUNDARY": "Apple-Mail=_1B76125E-EB81-4B78-A023-B30D1F9070F2"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: [])))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "Apple-Mail=_2F0988E2-CA7E-4379-B088-7E556A97E21F"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: .init(location: nil, extensions: []))))))), hasExtensionData: true)) + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 170)), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 6990 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text( + .init(mediaSubtype: "HTML", lineCount: 274) + ), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 18865 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init( + location: nil, + extensions: [] + ) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init( + topLevel: .application, + sub: .init("OCTET-STREAM") + ) + ), + fields: .init( + parameters: [ + "X-UNIX-MODE": "0644", + "NAME": "Whiteboard on Webex.key", + ], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 4_876_604 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "ATTACHMENT", + parameters: [ + "FILENAME": + "Whiteboard on Webex.key" + ] + ), + language: .init( + languages: [], + location: .init( + location: nil, + extensions: [] + ) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text( + .init(mediaSubtype: "HTML", lineCount: 17) + ), + fields: .init( + parameters: ["CHARSET": "us-ascii"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 1143 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init( + location: nil, + extensions: [] + ) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init( + topLevel: .application, + sub: .init("PDF") + ) + ), + fields: .init( + parameters: [ + "X-UNIX-MODE": "0644", + "NAME": "Whiteboard on Webex.pdf", + ], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 1_191_444 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "INLINE", + parameters: [ + "FILENAME": + "Whiteboard on Webex.pdf" + ] + ), + language: .init( + languages: [], + location: .init( + location: nil, + extensions: [] + ) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text( + .init(mediaSubtype: "HTML", lineCount: 32) + ), + fields: .init( + parameters: ["CHARSET": "us-ascii"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 2217 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init( + location: nil, + extensions: [] + ) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init( + topLevel: .application, + sub: .init("PDF") + ) + ), + fields: .init( + parameters: [ + "X-UNIX-MODE": "0666", + "NAME": "Resume.pdf", + ], + id: nil, + contentDescription: nil, + encoding: .base64, + octetCount: 217550 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "INLINE", + parameters: [ + "FILENAME": "Resume.pdf" + ] + ), + language: .init( + languages: [], + location: .init( + location: nil, + extensions: [] + ) + ) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text( + .init(mediaSubtype: "HTML", lineCount: 62) + ), + fields: .init( + parameters: ["CHARSET": "utf-8"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 4450 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init( + location: nil, + extensions: [] + ) + ) + ) + ) + ) + ), + ], + mediaSubtype: .mixed, + extension: .init( + parameters: [ + "BOUNDARY": + "Apple-Mail=_1B76125E-EB81-4B78-A023-B30D1F9070F2" + ], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: [ + "BOUNDARY": "Apple-Mail=_2F0988E2-CA7E-4379-B088-7E556A97E21F" + ], + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: [], + location: .init(location: nil, extensions: []) + ) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) ) ), .response(.fetch(.finish)), @@ -175,9 +770,54 @@ extension ResponseParser_Tests { #"* 53 FETCH (BODYSTRUCTURE (("TEXT" "HTML" NIL NIL NIL "7BIT" 151 0 NIL NIL NIL) "MIXED" ("BOUNDARY" "----=rfsewr") NIL NIL))"#, [ .response(.fetch(.start(53))), - .response(.fetch(.simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 0)), fields: BodyStructure.Fields(parameters: [:], id: nil, contentDescription: nil, encoding: .sevenBit, octetCount: 151), extension: BodyStructure.Singlepart.Extension(digest: nil, dispositionAndLanguage: BodyStructure.DispositionAndLanguage(disposition: nil, language: BodyStructure.LanguageLocation(languages: [], location: nil))))), - ], mediaSubtype: .mixed, extension: .init(parameters: ["BOUNDARY": "----=rfsewr"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [], location: nil)))))), hasExtensionData: true)))), + .response( + .fetch( + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text(.init(mediaSubtype: "HTML", lineCount: 0)), + fields: BodyStructure.Fields( + parameters: [:], + id: nil, + contentDescription: nil, + encoding: .sevenBit, + octetCount: 151 + ), + extension: BodyStructure.Singlepart.Extension( + digest: nil, + dispositionAndLanguage: + BodyStructure.DispositionAndLanguage( + disposition: nil, + language: BodyStructure.LanguageLocation( + languages: [], + location: nil + ) + ) + ) + ) + ) + ], + mediaSubtype: .mixed, + extension: .init( + parameters: ["BOUNDARY": "----=rfsewr"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: [], location: nil) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) + ), .response(.fetch(.finish)), ], #line @@ -187,20 +827,163 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(433))), .response( - .fetch(.simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .multipart( - .init(parts: [ - .multipart( - .init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 20)), fields: .init(parameters: ["CHARSET": "ISO-8859-1"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 710), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: []))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 42)), fields: .init(parameters: ["CHARSET": "ISO-8859-1"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 4323), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "INLINE", parameters: [:]), language: .init(languages: []))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "4__=rtfgha"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [])))) + .fetch( + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .multipart( + .init( + parts: [ + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text( + .init( + mediaSubtype: "PLAIN", + lineCount: 20 + ) + ), + fields: .init( + parameters: [ + "CHARSET": "ISO-8859-1" + ], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 710 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init( + languages: []) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text( + .init( + mediaSubtype: "HTML", + lineCount: 42 + ) + ), + fields: .init( + parameters: [ + "CHARSET": "ISO-8859-1" + ], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 4323 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "INLINE", + parameters: [:] + ), + language: .init( + languages: []) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: ["BOUNDARY": "4__=rtfgha"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: []) + ) + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .image, sub: .init("JPEG")) + ), + fields: .init( + parameters: ["NAME": "bike.jpeg"], + id: "<2__=lgkfjr>", + contentDescription: nil, + encoding: .base64, + octetCount: 64 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "INLINE", + parameters: [ + "FILENAME": "bike.jpeg" + ] + ), + language: .init(languages: []) + ) + ) + ) + ), + ], + mediaSubtype: .related, + extension: .init( + parameters: ["BOUNDARY": "0__=rtfgaa"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: []) + ) + ) + ) + ), + .singlepart( + .init( + kind: .basic( + .init(topLevel: .application, sub: .init("PDF")) + ), + fields: .init( + parameters: ["NAME": "title.pdf"], + id: "<5__=jlgkfr>", + contentDescription: nil, + encoding: .base64, + octetCount: 333980 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "ATTACHMENT", + parameters: ["FILENAME": "list.pdf"] + ), + language: .init(languages: []) + ) + ) + ) + ), + ], + mediaSubtype: .mixed, + extension: .init( + parameters: ["BOUNDARY": "1__=tfgrhs"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: []) + ) + ) + ) + ) ), - .singlepart(.init(kind: .basic(.init(topLevel: .image, sub: .init("JPEG"))), fields: .init(parameters: ["NAME": "bike.jpeg"], id: "<2__=lgkfjr>", contentDescription: nil, encoding: .base64, octetCount: 64), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "INLINE", parameters: ["FILENAME": "bike.jpeg"]), language: .init(languages: []))))), - ], mediaSubtype: .related, extension: .init(parameters: ["BOUNDARY": "0__=rtfgaa"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [])))) - ), - .singlepart(.init(kind: .basic(.init(topLevel: .application, sub: .init("PDF"))), fields: .init(parameters: ["NAME": "title.pdf"], id: "<5__=jlgkfr>", contentDescription: nil, encoding: .base64, octetCount: 333980), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "ATTACHMENT", parameters: ["FILENAME": "list.pdf"]), language: .init(languages: []))))), - ], mediaSubtype: .mixed, extension: .init(parameters: ["BOUNDARY": "1__=tfgrhs"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [])))))), hasExtensionData: true)) + hasExtensionData: true + ) + ) ) ), .response(.fetch(.finish)), @@ -212,17 +995,110 @@ extension ResponseParser_Tests { [ .response(.fetch(.start(234))), .response( - .fetch(.simpleAttribute(.body(.valid(.multipart(.init(parts: [ - .multipart( - .init(parts: [ - .singlepart(.init(kind: .text(.init(mediaSubtype: "PLAIN", lineCount: 24)), fields: .init(parameters: ["CHARSET": "ISO-8859-1"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 410), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: nil, language: .init(languages: []))))), - .singlepart(.init(kind: .text(.init(mediaSubtype: "HTML", lineCount: 30)), fields: .init(parameters: ["CHARSET": "ISO-8859-1"], id: nil, contentDescription: nil, encoding: .quotedPrintable, octetCount: 1407), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "INLINE", parameters: [:]), language: .init(languages: []))))), - ], mediaSubtype: .alternative, extension: .init(parameters: ["BOUNDARY": "hqjksdm1__="], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [])))) - ), - .singlepart( - .init(kind: .basic(.init(topLevel: .image, sub: .init("PNG"))), fields: BodyStructure.Fields(parameters: ["NAME": "screenshot.png"], id: "<3__=f2fcxd>", contentDescription: nil, encoding: .base64, octetCount: 40655), extension: .init(digest: nil, dispositionAndLanguage: .init(disposition: .init(kind: "INLINE", parameters: ["FILENAME": "screenshot.png"]), language: .init(languages: [])))) - ), - ], mediaSubtype: .related, extension: .init(parameters: ["BOUNDARY": "5__=hsdqjkm"], dispositionAndLanguage: .init(disposition: nil, language: .init(languages: [])))))), hasExtensionData: true))) + .fetch( + .simpleAttribute( + .body( + .valid( + .multipart( + .init( + parts: [ + .multipart( + .init( + parts: [ + .singlepart( + .init( + kind: .text( + .init(mediaSubtype: "PLAIN", lineCount: 24) + ), + fields: .init( + parameters: ["CHARSET": "ISO-8859-1"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 410 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: []) + ) + ) + ) + ), + .singlepart( + .init( + kind: .text( + .init(mediaSubtype: "HTML", lineCount: 30) + ), + fields: .init( + parameters: ["CHARSET": "ISO-8859-1"], + id: nil, + contentDescription: nil, + encoding: .quotedPrintable, + octetCount: 1407 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "INLINE", + parameters: [:] + ), + language: .init(languages: []) + ) + ) + ) + ), + ], + mediaSubtype: .alternative, + extension: .init( + parameters: ["BOUNDARY": "hqjksdm1__="], + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: []) + ) + ) + ) + ), + .singlepart( + .init( + kind: .basic(.init(topLevel: .image, sub: .init("PNG"))), + fields: BodyStructure.Fields( + parameters: ["NAME": "screenshot.png"], + id: "<3__=f2fcxd>", + contentDescription: nil, + encoding: .base64, + octetCount: 40655 + ), + extension: .init( + digest: nil, + dispositionAndLanguage: .init( + disposition: .init( + kind: "INLINE", + parameters: ["FILENAME": "screenshot.png"] + ), + language: .init(languages: []) + ) + ) + ) + ), + ], + mediaSubtype: .related, + extension: .init( + parameters: ["BOUNDARY": "5__=hsdqjkm"], + dispositionAndLanguage: .init( + disposition: nil, + language: .init(languages: []) + ) + ) + ) + ) + ), + hasExtensionData: true + ) + ) + ) ), .response(.fetch(.finish)), ], @@ -232,7 +1108,7 @@ extension ResponseParser_Tests { "* 12183 FETCH (UID 2282556735 PREVIEW {3}\r\nabc FLAGS (\\Seen))", [ .response(.fetch(.start(12183))), - .response(.fetch(.simpleAttribute(.uid(2282556735)))), + .response(.fetch(.simpleAttribute(.uid(2_282_556_735)))), .response(.fetch(.simpleAttribute(.preview(.init("abc"))))), .response(.fetch(.simpleAttribute(.flags([.seen])))), .response(.fetch(.finish)), @@ -282,21 +1158,26 @@ extension ResponseParser_Tests { // IncompleteMessage error. This needs to _not_ fail the parsing, but instead // bubble up to parseResponseStream() which will then wait for more data. // - var buffer = ByteBuffer(string: #""" - * 61785 FETCH (UID 127139 BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 71399 1519 NIL NIL NIL NIL)(("TEXT" "HTML" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 659725 9831 NIL NIL NIL NIL)("IMAGE" "PNG" ("X-UNIX-MODE" "0666" "NAME" "H9eubHenuyTiQAAAABJRU5ErkJggg==.png") "<5079C210-D42C-49F4-A942-2BA779C88A96>" NIL "BASE64" 104028 NIL ("INLINE" ("FILENAME" "H9eubHenuyTiQAAAABJRU5ErkJggg==.png")) NIL NIL)("IMAGE" "PNG" ("X-UNIX-MODE" "0666" "NAME" "IAEJCABCUhAAhKQgAQkIAEJSEACEpCABCQgAQlIQAISkIAEJNAKAQMdW8HsSSQgAQlIQAISkIAEJCABCUhAAhKQgAQkIAEJSEACEpCABCQgAQlIQAISkIAEJCABCUhAA") "<23E8CC74-836D-4B45-8B1E-1CF023182729>" NIL "BASE64" 55168 NIL ("INLINE" ("FILENAME" {4138}\#r\#naaaaaaaa - """#) + var buffer = ByteBuffer( + string: #""" + * 61785 FETCH (UID 127139 BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 71399 1519 NIL NIL NIL NIL)(("TEXT" "HTML" ("CHARSET" "utf-8") NIL NIL "QUOTED-PRINTABLE" 659725 9831 NIL NIL NIL NIL)("IMAGE" "PNG" ("X-UNIX-MODE" "0666" "NAME" "H9eubHenuyTiQAAAABJRU5ErkJggg==.png") "<5079C210-D42C-49F4-A942-2BA779C88A96>" NIL "BASE64" 104028 NIL ("INLINE" ("FILENAME" "H9eubHenuyTiQAAAABJRU5ErkJggg==.png")) NIL NIL)("IMAGE" "PNG" ("X-UNIX-MODE" "0666" "NAME" "IAEJCABCUhAAhKQgAQkIAEJSEACEpCABCQgAQlIQAISkIAEJNAKAQMdW8HsSSQgAQlIQAISkIAEJCABCUhAAhKQgAQkIAEJSEACEpCABCQgAQlIQAISkIAEJCABCUhAA") "<23E8CC74-836D-4B45-8B1E-1CF023182729>" NIL "BASE64" 55168 NIL ("INLINE" ("FILENAME" {4138}\#r\#naaaaaaaa + """# + ) var parser = ResponseParser() - XCTAssertEqual(try { () -> [ResponseOrContinuationRequest] in - var results: [ResponseOrContinuationRequest] = [] - while buffer.readableBytes > 0 { - guard let resp = try parser.parseResponseStream(buffer: &buffer) else { break } - results.append(resp) - } - return results - }(), [ - .response(.fetch(.start(617_85))), - .response(.fetch(.simpleAttribute(.uid(127_139)))), - ]) + XCTAssertEqual( + try { () -> [ResponseOrContinuationRequest] in + var results: [ResponseOrContinuationRequest] = [] + while buffer.readableBytes > 0 { + guard let resp = try parser.parseResponseStream(buffer: &buffer) else { break } + results.append(resp) + } + return results + }(), + [ + .response(.fetch(.start(617_85))), + .response(.fetch(.simpleAttribute(.uid(127_139)))), + ] + ) } func testAttributeLimit_failOnStreaming() { @@ -305,9 +1186,15 @@ extension ResponseParser_Tests { // limit is 3, so let's parse the first 3 XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.start(999)))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.flags([.seen]))))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.simpleAttribute(.flags([.seen])))) + ) XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.uid(1))))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.rfc822Size(123))))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.simpleAttribute(.rfc822Size(123)))) + ) // the limit is 3, so the fourth should fail XCTAssertThrowsError(try parser.parseResponseStream(buffer: &buffer)) { e in @@ -321,9 +1208,15 @@ extension ResponseParser_Tests { // limit is 3, so let's parse the first 3 XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.start(999)))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.flags([.seen]))))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.simpleAttribute(.flags([.seen])))) + ) XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.uid(1))))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.rfc822Size(123))))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.simpleAttribute(.rfc822Size(123)))) + ) // the limit is 3, so the fourth should fail XCTAssertThrowsError(try parser.parseResponseStream(buffer: &buffer)) { e in @@ -335,7 +1228,10 @@ extension ResponseParser_Tests { var parser = ResponseParser(bufferLimit: 1000, bodySizeLimit: 10) var buffer: ByteBuffer = "* 999 FETCH (RFC822.TEXT {3}\r\n123 RFC822.HEADER {11}\r\n " XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.start(999)))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.streamingBegin(kind: .rfc822Text, byteCount: 3)))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.streamingBegin(kind: .rfc822Text, byteCount: 3))) + ) XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.streamingBytes("123")))) XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.streamingEnd))) @@ -348,7 +1244,10 @@ extension ResponseParser_Tests { var parser = ResponseParser(bufferLimit: 1000, bodySizeLimit: 10) var buffer: ByteBuffer = "* 999 FETCH (FLAGS (\\Seen))\r\n" XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.start(999)))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.flags([.seen]))))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.simpleAttribute(.flags([.seen])))) + ) } // The flag "seen" should be given to our cache closure @@ -363,7 +1262,10 @@ extension ResponseParser_Tests { var parser = ResponseParser(bufferLimit: 1000, bodySizeLimit: 10, parsedStringCache: testCache) var buffer: ByteBuffer = "* 999 FETCH (FLAGS (\\Seen))\r\n" XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.start(999)))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.simpleAttribute(.flags([.init("\\nees")]))))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.simpleAttribute(.flags([.init("\\nees")])))) + ) } // Even with a `literalSizeLimit` of 1 parsing a RFC822.TEXT should _not_ fail @@ -372,7 +1274,10 @@ extension ResponseParser_Tests { var parser = ResponseParser(bufferLimit: 1000, bodySizeLimit: 10, literalSizeLimit: 1) var buffer: ByteBuffer = "* 999 FETCH (RFC822.TEXT {3}\r\n123 RFC822.HEADER {11}\r\n " XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.start(999)))) - XCTAssertEqual(try parser.parseResponseStream(buffer: &buffer), .response(.fetch(.streamingBegin(kind: .rfc822Text, byteCount: 3)))) + XCTAssertEqual( + try parser.parseResponseStream(buffer: &buffer), + .response(.fetch(.streamingBegin(kind: .rfc822Text, byteCount: 3))) + ) } } diff --git a/Tests/NIOIMAPCoreTests/Parser/RoundtripTests.swift b/Tests/NIOIMAPCoreTests/Parser/RoundtripTests.swift index eda2ce1b2..36081d6bd 100644 --- a/Tests/NIOIMAPCoreTests/Parser/RoundtripTests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/RoundtripTests.swift @@ -76,9 +76,21 @@ final class RoundtripTests: XCTestCase { (.fetch(.set([.all]), .full, []), #line), (.fetch(.set([5678]), [.uid, .flags, .internalDate, .envelope], []), #line), (.fetch(.set([5678]), [.flags, .bodyStructure(extensions: true)], []), #line), - (.fetch(.set([5678]), [.flags, .bodySection(peek: false, .complete, 3 ... 4)], []), #line), - (.fetch(.set([5678]), [.flags, .bodySection(peek: false, .init(kind: .header), 3 ... 4)], []), #line), - (.fetch(.set([5678]), [.bodySection(peek: false, .init(part: [12, 34], kind: .headerFields(["some", "header"])), 3 ... 4)], []), #line), + (.fetch(.set([5678]), [.flags, .bodySection(peek: false, .complete, 3...4)], []), #line), + (.fetch(.set([5678]), [.flags, .bodySection(peek: false, .init(kind: .header), 3...4)], []), #line), + ( + .fetch( + .set([5678]), + [ + .bodySection( + peek: false, + .init(part: [12, 34], kind: .headerFields(["some", "header"])), + 3...4 + ) + ], + [] + ), #line + ), (.store(.set(.all), [], .flags(.remove(silent: true, list: [.answered, .deleted]))), #line), (.store(.set(.all), [], .flags(.add(silent: true, list: [.draft, .extension("\\some")]))), #line), @@ -93,7 +105,14 @@ final class RoundtripTests: XCTestCase { (.search(key: .or(.to("bar"), .unseen), charset: "UTF-8", returnOptions: [.min, .max]), #line), (.search(key: .and([.new, .deleted, .unseen]), charset: nil, returnOptions: [.min, .max]), #line), - (.extendedSearch(ExtendedSearchOptions(key: .all, sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]))), #line), + ( + .extendedSearch( + ExtendedSearchOptions( + key: .all, + sourceOptions: ExtendedSearchSourceOptions(sourceMailbox: [.inboxes]) + ) + ), #line + ), ] for (i, test) in tests.enumerated() { @@ -103,7 +122,8 @@ final class RoundtripTests: XCTestCase { let tag = "\(i + 1)" let command = TaggedCommand(tag: tag, command: commandType) encodeBuffer.writeCommand(command) - encodeBuffer.buffer.writeString("\r\n") // required for commands that might terminate with a literal (e.g. append) + // required for commands that might terminate with a literal (e.g. append) + encodeBuffer.buffer.writeString("\r\n") var buffer = ByteBufferAllocator().buffer(capacity: 128) while true { let next = encodeBuffer.buffer.nextChunk() diff --git a/Tests/NIOIMAPCoreTests/Parser/SynchronizingLiteralParserTests.swift b/Tests/NIOIMAPCoreTests/Parser/SynchronizingLiteralParserTests.swift index f51f50ae0..cfc41bbaa 100644 --- a/Tests/NIOIMAPCoreTests/Parser/SynchronizingLiteralParserTests.swift +++ b/Tests/NIOIMAPCoreTests/Parser/SynchronizingLiteralParserTests.swift @@ -120,12 +120,15 @@ final class SynchronizingLiteralParserTests: XCTestCase { self.indicateConsume("\r\n") self.feed("") self.indicateConsume("FOO {1}\r\nx y\r\n") - self.assertMultipleParses([ - "LOGIN {1}\r\nA {1}\r\nB\r\nFOO {1}\r\nx y\r\n", - "B\r\nFOO {1}\r\nx y\r\n", - "\r\nFOO {1}\r\nx y\r\n", - "FOO {1}\r\nx y\r\n", - ], continuationsNecessary: 3) + self.assertMultipleParses( + [ + "LOGIN {1}\r\nA {1}\r\nB\r\nFOO {1}\r\nx y\r\n", + "B\r\nFOO {1}\r\nx y\r\n", + "\r\nFOO {1}\r\nx y\r\n", + "FOO {1}\r\nx y\r\n", + ], + continuationsNecessary: 3 + ) } func testDripFeedWorks() { @@ -149,21 +152,24 @@ final class SynchronizingLiteralParserTests: XCTestCase { self.indicateConsume("FOO {5}\n{0}\r\n\n") self.feed("") - self.assertMultipleParses([ - "", - "LOGIN {1}\r\n", - "LOGIN {1}\r\n\n", - "LOGIN {1}\r\n\n\n", - "LOGIN {1}\r\n\n\n", - "LOGIN {1}\r\n\n\n", - "LOGIN {1}\r\n\n\nLOGIN {1}\r\n {2}\n\r\n\nFOO {5}\n{0}\r\n\n", - "LOGIN {1}\r\n {2}\n\r\n\nFOO {5}\n{0}\r\n\n", - " {2}\n\r\n\nFOO {5}\n{0}\r\n\n", - "{2}\n\r\n\nFOO {5}\n{0}\r\n\n", - "\n\nFOO {5}\n{0}\r\n\n", - "FOO {5}\n{0}\r\n\n", - "", - ], continuationsNecessary: 4) + self.assertMultipleParses( + [ + "", + "LOGIN {1}\r\n", + "LOGIN {1}\r\n\n", + "LOGIN {1}\r\n\n\n", + "LOGIN {1}\r\n\n\n", + "LOGIN {1}\r\n\n\n", + "LOGIN {1}\r\n\n\nLOGIN {1}\r\n {2}\n\r\n\nFOO {5}\n{0}\r\n\n", + "LOGIN {1}\r\n {2}\n\r\n\nFOO {5}\n{0}\r\n\n", + " {2}\n\r\n\nFOO {5}\n{0}\r\n\n", + "{2}\n\r\n\nFOO {5}\n{0}\r\n\n", + "\n\nFOO {5}\n{0}\r\n\n", + "FOO {5}\n{0}\r\n\n", + "", + ], + continuationsNecessary: 4 + ) } func testAppendFollowedByHalfCommand() { @@ -174,10 +180,12 @@ final class SynchronizingLiteralParserTests: XCTestCase { self.feed("") self.feed("t") - self.assertMultipleParses(["tag APPEND box (\\Seen) {1+}\r\na\r\n", - "a\r\n", - "\r\n", - "\r\n"]) + self.assertMultipleParses([ + "tag APPEND box (\\Seen) {1+}\r\na\r\n", + "a\r\n", + "\r\n", + "\r\n", + ]) } override func setUp() { @@ -201,7 +209,9 @@ final class SynchronizingLiteralParserTests: XCTestCase { private func feed(_ string: String) { let buffer = self.stringBuffer(string) self.accumulator.writeBytes(buffer.readableBytesView) - XCTAssertNoThrow(self.parses.append(try self.parser.parseContinuationsNecessary(self.bufferWithGarbage(self.accumulator)))) + XCTAssertNoThrow( + self.parses.append(try self.parser.parseContinuationsNecessary(self.bufferWithGarbage(self.accumulator))) + ) } private func indicateConsume(_ string: String) { @@ -210,10 +220,12 @@ final class SynchronizingLiteralParserTests: XCTestCase { self.parser.consumed(string.utf8.count) } - private func assertMultipleParses(_ expectedStrings: [String], continuationsNecessary: Int = 0, - file: StaticString = (#filePath), - line: UInt = #line) - { + private func assertMultipleParses( + _ expectedStrings: [String], + continuationsNecessary: Int = 0, + file: StaticString = (#filePath), + line: UInt = #line + ) { guard expectedStrings.count == self.parses.count else { XCTFail("Unexpected number of parses: \(self.parses.count)", file: file, line: line) return @@ -227,26 +239,37 @@ final class SynchronizingLiteralParserTests: XCTestCase { let parse = self.parses[expected.offset] let expectedUTF8 = Array(expected.element.utf8) let actual = Array(allBytes.readableBytesView.prefix(parse.maximumValidBytes)) - XCTAssertEqual(expectedUTF8, actual, - "parse \(expected.0): \(String(decoding: expectedUTF8, as: UTF8.self)) != \(String(decoding: actual, as: UTF8.self))", - file: file, line: line) + XCTAssertEqual( + expectedUTF8, + actual, + "parse \(expected.0): \(String(decoding: expectedUTF8, as: UTF8.self)) != \(String(decoding: actual, as: UTF8.self))", + file: file, + line: line + ) XCTAssertGreaterThanOrEqual(parse.synchronizingLiteralCount, 0) continuations += parse.synchronizingLiteralCount - let newReader = self.consumptions.filter { - $0.numberOfPriorParses <= expected.offset + 1 - }.map(\.consumption).reduce(0, +) + initialAllByteReader + let newReader = + self.consumptions.filter { + $0.numberOfPriorParses <= expected.offset + 1 + }.map(\.consumption).reduce(0, +) + initialAllByteReader allBytes.moveReaderIndex(to: newReader) } - XCTAssertEqual(continuationsNecessary, continuations, - "wrong number of continuations", - file: file, line: line) + XCTAssertEqual( + continuationsNecessary, + continuations, + "wrong number of continuations", + file: file, + line: line + ) } - private func assertOneParse(_ string: String, continuationsNecessary: Int = 0, - file: StaticString = (#filePath), - line: UInt = #line) - { + private func assertOneParse( + _ string: String, + continuationsNecessary: Int = 0, + file: StaticString = (#filePath), + line: UInt = #line + ) { XCTAssertEqual(1, self.parses.count) guard let parse = self.parses.first else { XCTFail("no parses found", file: file, line: line) @@ -254,11 +277,19 @@ final class SynchronizingLiteralParserTests: XCTestCase { } let expected = Array(string.utf8) let actual = Array(self.accumulator.readableBytesView.prefix(parse.maximumValidBytes)) - XCTAssertEqual(expected, actual, - "\(String(decoding: expected, as: UTF8.self)) != \(String(decoding: actual, as: UTF8.self))", - file: file, line: line) - XCTAssertEqual(continuationsNecessary, parse.synchronizingLiteralCount, - file: file, line: line) + XCTAssertEqual( + expected, + actual, + "\(String(decoding: expected, as: UTF8.self)) != \(String(decoding: actual, as: UTF8.self))", + file: file, + line: line + ) + XCTAssertEqual( + continuationsNecessary, + parse.synchronizingLiteralCount, + file: file, + line: line + ) } private func stringBuffer(_ string: String) -> ByteBuffer { @@ -269,7 +300,7 @@ final class SynchronizingLiteralParserTests: XCTestCase { private func bufferWithGarbage(_ buffer: ByteBuffer) -> ByteBuffer { var buffer = buffer - let garbageByteCount = (0 ..< 32).randomElement() ?? 0 + let garbageByteCount = (0..<32).randomElement() ?? 0 var newBuffer = ByteBufferAllocator().buffer(capacity: garbageByteCount + buffer.readableBytes) newBuffer.writeString(String(repeating: "X", count: garbageByteCount)) newBuffer.moveReaderIndex(forwardBy: garbageByteCount) diff --git a/Tests/NIOIMAPCoreTests/Parser/UInt8+ParseTypeMembership.swift b/Tests/NIOIMAPCoreTests/Parser/UInt8+ParseTypeMembership.swift index 95fc77a70..780c3d8ad 100644 --- a/Tests/NIOIMAPCoreTests/Parser/UInt8+ParseTypeMembership.swift +++ b/Tests/NIOIMAPCoreTests/Parser/UInt8+ParseTypeMembership.swift @@ -16,7 +16,7 @@ import XCTest final class UInt8ParseTypeMembershipTests: XCTestCase { - let allChars = Set(UInt8.min ... UInt8.max) + let allChars = Set(UInt8.min...UInt8.max) } // MARK: - test isCR @@ -80,11 +80,11 @@ extension UInt8ParseTypeMembershipTests { func testAtomSpecial() { var valid: Set = [ UInt8(ascii: "("), UInt8(ascii: ")"), UInt8(ascii: " "), UInt8(ascii: "{"), - UInt8(ascii: "]"), // ResponseSpecial - UInt8(ascii: "%"), UInt8(ascii: "*"), // ListWildcard - UInt8(ascii: "\""), UInt8(ascii: "\\"), // QuotedSpecial + UInt8(ascii: "]"), // ResponseSpecial + UInt8(ascii: "%"), UInt8(ascii: "*"), // ListWildcard + UInt8(ascii: "\""), UInt8(ascii: "\\"), // QuotedSpecial ] - valid = valid.union(0 ... 31) + valid = valid.union(0...31) self.allChars.forEach { char in if valid.contains(char) { XCTAssertTrue(char.isAtomSpecial) @@ -101,7 +101,7 @@ extension UInt8ParseTypeMembershipTests { // thanks Johannes func testTextChar() { let invalid: Set = [UInt8(ascii: "\r"), .init(ascii: "\n"), 0] - let valid = self.allChars.subtracting(invalid).subtracting(128 ... UInt8.max) + let valid = self.allChars.subtracting(invalid).subtracting(128...UInt8.max) XCTAssertTrue(valid.allSatisfy(\.isTextChar)) XCTAssertTrue(invalid.allSatisfy { !$0.isTextChar }) } @@ -112,9 +112,9 @@ extension UInt8ParseTypeMembershipTests { extension UInt8ParseTypeMembershipTests { func testHexCharacter() { var valid = Set() - valid = valid.union(UInt8(ascii: "0") ... UInt8(ascii: "9")) - valid = valid.union(UInt8(ascii: "a") ... UInt8(ascii: "f")) - valid = valid.union(UInt8(ascii: "A") ... UInt8(ascii: "F")) + valid = valid.union(UInt8(ascii: "0")...UInt8(ascii: "9")) + valid = valid.union(UInt8(ascii: "a")...UInt8(ascii: "f")) + valid = valid.union(UInt8(ascii: "A")...UInt8(ascii: "F")) let invalid = self.allChars.subtracting(valid) XCTAssertTrue(valid.allSatisfy(\.isHexCharacter)) @@ -127,9 +127,9 @@ extension UInt8ParseTypeMembershipTests { extension UInt8ParseTypeMembershipTests { func testBase64Character() { var valid = Set() - valid = valid.union(UInt8(ascii: "0") ... UInt8(ascii: "9")) - valid = valid.union(UInt8(ascii: "a") ... UInt8(ascii: "z")) - valid = valid.union(UInt8(ascii: "A") ... UInt8(ascii: "Z")) + valid = valid.union(UInt8(ascii: "0")...UInt8(ascii: "9")) + valid = valid.union(UInt8(ascii: "a")...UInt8(ascii: "z")) + valid = valid.union(UInt8(ascii: "A")...UInt8(ascii: "Z")) valid = valid.union([UInt8(ascii: "+"), UInt8(ascii: "/")]) let invalid = self.allChars.subtracting(valid) diff --git a/Tests/NIOIMAPCoreTests/PipeliningTests.swift b/Tests/NIOIMAPCoreTests/PipeliningTests.swift index 5a972d943..3bd5b84a2 100644 --- a/Tests/NIOIMAPCoreTests/PipeliningTests.swift +++ b/Tests/NIOIMAPCoreTests/PipeliningTests.swift @@ -25,11 +25,16 @@ extension MailboxPatterns { } extension RumpURLAndMechanism { - fileprivate static let joe = RumpURLAndMechanism(urlRump: "imap://joe@example.com/INBOX/;uid=20/;section=1.2", mechanism: .internal) + fileprivate static let joe = RumpURLAndMechanism( + urlRump: "imap://joe@example.com/INBOX/;uid=20/;section=1.2", + mechanism: .internal + ) } extension ByteBuffer { - fileprivate static let joeURLFetch = ByteBuffer("imap://joe@example.com/INBOX/;uid=20/;section=1.2;urlauth=submit+fred:internal:91354a473744909de610943775f92038") + fileprivate static let joeURLFetch = ByteBuffer( + "imap://joe@example.com/INBOX/;uid=20/;section=1.2;urlauth=submit+fred:internal:91354a473744909de610943775f92038" + ) } extension SearchKey { @@ -124,9 +129,9 @@ extension SearchKey { extension MessageIdentifierSetNonEmpty where IdentifierType == UID { fileprivate static var arbitrarySets: [Self] { [ - [100 ... 200], - [IdentifierType.min ... IdentifierType.min], - [43_195 ... 43_195], + [100...200], + [IdentifierType.min...IdentifierType.min], + [43_195...43_195], .all, ] } @@ -139,45 +144,95 @@ extension PipeliningRequirement { .noUIDBasedCommandRunning, .noFlagChanges(.all), .noFlagChanges([1]), - .noFlagChanges([55 ... 1000]), + .noFlagChanges([55...1000]), .noFlagReads(.all), .noFlagReads([1]), - .noFlagReads([55 ... 1000]), + .noFlagReads([55...1000]), ] } // MARK: - -private func AssertCanStart(_ requirements: Set, whileRunning behavior: Set, file: StaticString = #filePath, line: UInt = #line) { +private func AssertCanStart( + _ requirements: Set, + whileRunning behavior: Set, + file: StaticString = #filePath, + line: UInt = #line +) { XCTAssert(behavior.satisfies(requirements), file: file, line: line) } -private func AssertCanNotStart(_ requirements: Set, whileRunning behavior: Set, file: StaticString = #filePath, line: UInt = #line) { +private func AssertCanNotStart( + _ requirements: Set, + whileRunning behavior: Set, + file: StaticString = #filePath, + line: UInt = #line +) { XCTAssertFalse(behavior.satisfies(requirements), file: file, line: line) } -private func AssertFalse(commands: [(UInt, Command)], require requirement: PipeliningRequirement, _ message: @autoclosure () -> String = "", file: StaticString = #filePath) { +private func AssertFalse( + commands: [(UInt, Command)], + require requirement: PipeliningRequirement, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath +) { commands.forEach { line, command in - XCTAssertFalse(command.pipeliningRequirements.contains(requirement), "Should not require \(requirement). \(message())", file: file, line: line) + XCTAssertFalse( + command.pipeliningRequirements.contains(requirement), + "Should not require \(requirement). \(message())", + file: file, + line: line + ) } } -private func Assert(commands: [(UInt, Command)], require requirement: PipeliningRequirement, _ message: @autoclosure () -> String = "", file: StaticString = #filePath) { +private func Assert( + commands: [(UInt, Command)], + require requirement: PipeliningRequirement, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath +) { commands.forEach { line, command in let r = command.pipeliningRequirements - XCTAssert(r.contains(requirement), "Should require \(requirement). Did: \(r). \(message())", file: file, line: line) + XCTAssert( + r.contains(requirement), + "Should require \(requirement). Did: \(r). \(message())", + file: file, + line: line + ) } } -private func AssertFalse(commands: [(UInt, Command)], haveBehavior behavior: PipeliningBehavior, _ message: @autoclosure () -> String = "", file: StaticString = #filePath) { +private func AssertFalse( + commands: [(UInt, Command)], + haveBehavior behavior: PipeliningBehavior, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath +) { commands.forEach { line, command in - XCTAssertFalse(command.pipeliningBehavior.contains(behavior), "Should not have \(behavior) behavior. \(message())", file: file, line: line) + XCTAssertFalse( + command.pipeliningBehavior.contains(behavior), + "Should not have \(behavior) behavior. \(message())", + file: file, + line: line + ) } } -private func Assert(commands: [(UInt, Command)], haveBehavior behavior: PipeliningBehavior, _ message: @autoclosure () -> String = "", file: StaticString = #filePath) { +private func Assert( + commands: [(UInt, Command)], + haveBehavior behavior: PipeliningBehavior, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath +) { commands.forEach { line, command in - XCTAssert(command.pipeliningBehavior.contains(behavior), "Should have \(behavior) behavior. \(message())", file: file, line: line) + XCTAssert( + command.pipeliningBehavior.contains(behavior), + "Should have \(behavior) behavior. \(message())", + file: file, + line: line + ) } } @@ -190,164 +245,288 @@ final class PipeliningTests: XCTestCase {} extension PipeliningTests { func testCanStartEmptyBehavior() { // When the running command doesn’t have any requirements, anything can run: - AssertCanStart([], - whileRunning: []) - AssertCanStart([.noMailboxCommandsRunning], - whileRunning: []) - AssertCanStart([.noUntaggedExpungeResponse], - whileRunning: []) - AssertCanStart([.noUIDBasedCommandRunning], - whileRunning: []) - AssertCanStart([.noFlagChangesToAnyMessage], - whileRunning: []) - AssertCanStart([.noFlagReadsFromAnyMessage], - whileRunning: []) - AssertCanStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: []) + AssertCanStart( + [], + whileRunning: [] + ) + AssertCanStart( + [.noMailboxCommandsRunning], + whileRunning: [] + ) + AssertCanStart( + [.noUntaggedExpungeResponse], + whileRunning: [] + ) + AssertCanStart( + [.noUIDBasedCommandRunning], + whileRunning: [] + ) + AssertCanStart( + [.noFlagChangesToAnyMessage], + whileRunning: [] + ) + AssertCanStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [] + ) + AssertCanStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [] + ) } func testCanStartChangesMailboxSelectionBehavior() { - AssertCanStart([], - whileRunning: [.changesMailboxSelection]) + AssertCanStart( + [], + whileRunning: [.changesMailboxSelection] + ) // Don’t start a command that depends on the selected state // while changing the selected state. - AssertCanNotStart([.noMailboxCommandsRunning], - whileRunning: [.changesMailboxSelection]) - AssertCanStart([.noUntaggedExpungeResponse], - whileRunning: [.changesMailboxSelection]) - AssertCanStart([.noUIDBasedCommandRunning], - whileRunning: [.changesMailboxSelection]) - AssertCanStart([.noFlagChangesToAnyMessage], - whileRunning: [.changesMailboxSelection]) - AssertCanStart([.noFlagReadsFromAnyMessage], - whileRunning: [.changesMailboxSelection]) - - AssertCanNotStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: [.changesMailboxSelection]) + AssertCanNotStart( + [.noMailboxCommandsRunning], + whileRunning: [.changesMailboxSelection] + ) + AssertCanStart( + [.noUntaggedExpungeResponse], + whileRunning: [.changesMailboxSelection] + ) + AssertCanStart( + [.noUIDBasedCommandRunning], + whileRunning: [.changesMailboxSelection] + ) + AssertCanStart( + [.noFlagChangesToAnyMessage], + whileRunning: [.changesMailboxSelection] + ) + AssertCanStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [.changesMailboxSelection] + ) + + AssertCanNotStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [.changesMailboxSelection] + ) } func testCanStartDependsOnMailboxSelectionBehavior() { - AssertCanStart([], - whileRunning: [.dependsOnMailboxSelection]) + AssertCanStart( + [], + whileRunning: [.dependsOnMailboxSelection] + ) // Don’t start a command that requires no mailbox-specific commands, // while mailbox-specific commands are running. E.g. don’t change the // mailbox while running a FETCH. - AssertCanNotStart([.noMailboxCommandsRunning], - whileRunning: [.dependsOnMailboxSelection]) - AssertCanStart([.noUntaggedExpungeResponse], - whileRunning: [.dependsOnMailboxSelection]) - AssertCanStart([.noUIDBasedCommandRunning], - whileRunning: [.dependsOnMailboxSelection]) - AssertCanStart([.noFlagChangesToAnyMessage], - whileRunning: [.dependsOnMailboxSelection]) - AssertCanStart([.noFlagReadsFromAnyMessage], - whileRunning: [.dependsOnMailboxSelection]) - - AssertCanNotStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: [.dependsOnMailboxSelection]) + AssertCanNotStart( + [.noMailboxCommandsRunning], + whileRunning: [.dependsOnMailboxSelection] + ) + AssertCanStart( + [.noUntaggedExpungeResponse], + whileRunning: [.dependsOnMailboxSelection] + ) + AssertCanStart( + [.noUIDBasedCommandRunning], + whileRunning: [.dependsOnMailboxSelection] + ) + AssertCanStart( + [.noFlagChangesToAnyMessage], + whileRunning: [.dependsOnMailboxSelection] + ) + AssertCanStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [.dependsOnMailboxSelection] + ) + + AssertCanNotStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [.dependsOnMailboxSelection] + ) } func testCanStartMayTriggerUntaggedExpungeBehavior() { - AssertCanStart([], - whileRunning: [.mayTriggerUntaggedExpunge]) - AssertCanStart([.noMailboxCommandsRunning], - whileRunning: [.mayTriggerUntaggedExpunge]) + AssertCanStart( + [], + whileRunning: [.mayTriggerUntaggedExpunge] + ) + AssertCanStart( + [.noMailboxCommandsRunning], + whileRunning: [.mayTriggerUntaggedExpunge] + ) // If a command may trigger “untagged EXPUNGE”, don’t start a command // that requires this not to happen. - AssertCanNotStart([.noUntaggedExpungeResponse], - whileRunning: [.mayTriggerUntaggedExpunge]) - AssertCanStart([.noUIDBasedCommandRunning], - whileRunning: [.mayTriggerUntaggedExpunge]) - AssertCanStart([.noFlagChangesToAnyMessage], - whileRunning: [.mayTriggerUntaggedExpunge]) - AssertCanStart([.noFlagReadsFromAnyMessage], - whileRunning: [.mayTriggerUntaggedExpunge]) - - AssertCanNotStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: [.mayTriggerUntaggedExpunge]) + AssertCanNotStart( + [.noUntaggedExpungeResponse], + whileRunning: [.mayTriggerUntaggedExpunge] + ) + AssertCanStart( + [.noUIDBasedCommandRunning], + whileRunning: [.mayTriggerUntaggedExpunge] + ) + AssertCanStart( + [.noFlagChangesToAnyMessage], + whileRunning: [.mayTriggerUntaggedExpunge] + ) + AssertCanStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [.mayTriggerUntaggedExpunge] + ) + + AssertCanNotStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [.mayTriggerUntaggedExpunge] + ) } func testCanStartIsUIDBasedBehavior() { - AssertCanStart([], - whileRunning: [.isUIDBased]) - AssertCanStart([.noMailboxCommandsRunning], - whileRunning: [.isUIDBased]) - AssertCanStart([.noUntaggedExpungeResponse], - whileRunning: [.isUIDBased]) - AssertCanNotStart([.noUIDBasedCommandRunning], - whileRunning: [.isUIDBased]) - AssertCanStart([.noFlagChangesToAnyMessage], - whileRunning: [.isUIDBased]) - AssertCanStart([.noFlagReadsFromAnyMessage], - whileRunning: [.isUIDBased]) - AssertCanNotStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: [.isUIDBased]) + AssertCanStart( + [], + whileRunning: [.isUIDBased] + ) + AssertCanStart( + [.noMailboxCommandsRunning], + whileRunning: [.isUIDBased] + ) + AssertCanStart( + [.noUntaggedExpungeResponse], + whileRunning: [.isUIDBased] + ) + AssertCanNotStart( + [.noUIDBasedCommandRunning], + whileRunning: [.isUIDBased] + ) + AssertCanStart( + [.noFlagChangesToAnyMessage], + whileRunning: [.isUIDBased] + ) + AssertCanStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [.isUIDBased] + ) + AssertCanNotStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [.isUIDBased] + ) } func testCanStartChangesFlagsBehavior() { - AssertCanStart([], - whileRunning: [.changesFlagsOnAnyMessage]) - AssertCanStart([.noMailboxCommandsRunning], - whileRunning: [.changesFlagsOnAnyMessage]) - AssertCanStart([.noUntaggedExpungeResponse], - whileRunning: [.changesFlagsOnAnyMessage]) - AssertCanStart([.noUIDBasedCommandRunning], - whileRunning: [.changesFlagsOnAnyMessage]) - AssertCanNotStart([.noFlagChangesToAnyMessage], - whileRunning: [.changesFlagsOnAnyMessage]) - AssertCanStart([.noFlagReadsFromAnyMessage], - whileRunning: [.changesFlagsOnAnyMessage]) - AssertCanNotStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: [.changesFlagsOnAnyMessage]) - - AssertCanStart([.noFlagChanges([200 ... 300])], - whileRunning: [.changesFlags([1 ... 100]), .changesFlags([400 ... 500])]) - AssertCanNotStart([.noFlagChanges([200 ... 300])], - whileRunning: [.changesFlags([100 ... 200])]) - AssertCanStart([.noFlagChanges([200 ... 300])], - whileRunning: [.readsFlags([100 ... 200])]) + AssertCanStart( + [], + whileRunning: [.changesFlagsOnAnyMessage] + ) + AssertCanStart( + [.noMailboxCommandsRunning], + whileRunning: [.changesFlagsOnAnyMessage] + ) + AssertCanStart( + [.noUntaggedExpungeResponse], + whileRunning: [.changesFlagsOnAnyMessage] + ) + AssertCanStart( + [.noUIDBasedCommandRunning], + whileRunning: [.changesFlagsOnAnyMessage] + ) + AssertCanNotStart( + [.noFlagChangesToAnyMessage], + whileRunning: [.changesFlagsOnAnyMessage] + ) + AssertCanStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [.changesFlagsOnAnyMessage] + ) + AssertCanNotStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [.changesFlagsOnAnyMessage] + ) + + AssertCanStart( + [.noFlagChanges([200...300])], + whileRunning: [.changesFlags([1...100]), .changesFlags([400...500])] + ) + AssertCanNotStart( + [.noFlagChanges([200...300])], + whileRunning: [.changesFlags([100...200])] + ) + AssertCanStart( + [.noFlagChanges([200...300])], + whileRunning: [.readsFlags([100...200])] + ) } func testCanStartReadsFlagsBehavior() { - AssertCanStart([], - whileRunning: [.readsFlagsFromAnyMessage]) - AssertCanStart([.noMailboxCommandsRunning], - whileRunning: [.readsFlagsFromAnyMessage]) - AssertCanStart([.noUntaggedExpungeResponse], - whileRunning: [.readsFlagsFromAnyMessage]) - AssertCanStart([.noUIDBasedCommandRunning], - whileRunning: [.readsFlagsFromAnyMessage]) - AssertCanStart([.noFlagChangesToAnyMessage], - whileRunning: [.readsFlagsFromAnyMessage]) - AssertCanNotStart([.noFlagReadsFromAnyMessage], - whileRunning: [.readsFlagsFromAnyMessage]) - AssertCanNotStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: [.readsFlagsFromAnyMessage]) - - AssertCanStart([.noFlagReads([200 ... 300])], - whileRunning: [.readsFlags([1 ... 100]), .readsFlags([400 ... 500])]) - AssertCanNotStart([.noFlagReads([200 ... 300])], - whileRunning: [.readsFlags([100 ... 200])]) - AssertCanStart([.noFlagReads([200 ... 300])], - whileRunning: [.changesFlags([100 ... 200])]) + AssertCanStart( + [], + whileRunning: [.readsFlagsFromAnyMessage] + ) + AssertCanStart( + [.noMailboxCommandsRunning], + whileRunning: [.readsFlagsFromAnyMessage] + ) + AssertCanStart( + [.noUntaggedExpungeResponse], + whileRunning: [.readsFlagsFromAnyMessage] + ) + AssertCanStart( + [.noUIDBasedCommandRunning], + whileRunning: [.readsFlagsFromAnyMessage] + ) + AssertCanStart( + [.noFlagChangesToAnyMessage], + whileRunning: [.readsFlagsFromAnyMessage] + ) + AssertCanNotStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [.readsFlagsFromAnyMessage] + ) + AssertCanNotStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [.readsFlagsFromAnyMessage] + ) + + AssertCanStart( + [.noFlagReads([200...300])], + whileRunning: [.readsFlags([1...100]), .readsFlags([400...500])] + ) + AssertCanNotStart( + [.noFlagReads([200...300])], + whileRunning: [.readsFlags([100...200])] + ) + AssertCanStart( + [.noFlagReads([200...300])], + whileRunning: [.changesFlags([100...200])] + ) } func testCanStartBarrierBehavior() { // Nothing can be started, while a barrier is running. - AssertCanNotStart([], - whileRunning: [.barrier]) - AssertCanNotStart([.noMailboxCommandsRunning], - whileRunning: [.barrier]) - AssertCanNotStart([.noUntaggedExpungeResponse], - whileRunning: [.barrier]) - AssertCanNotStart([.noUIDBasedCommandRunning], - whileRunning: [.barrier]) - AssertCanNotStart([.noFlagChangesToAnyMessage], - whileRunning: [.barrier]) - AssertCanNotStart([.noFlagReadsFromAnyMessage], - whileRunning: [.barrier]) - AssertCanNotStart(Set(PipeliningRequirement.arbitraryRequirements), - whileRunning: [.barrier]) + AssertCanNotStart( + [], + whileRunning: [.barrier] + ) + AssertCanNotStart( + [.noMailboxCommandsRunning], + whileRunning: [.barrier] + ) + AssertCanNotStart( + [.noUntaggedExpungeResponse], + whileRunning: [.barrier] + ) + AssertCanNotStart( + [.noUIDBasedCommandRunning], + whileRunning: [.barrier] + ) + AssertCanNotStart( + [.noFlagChangesToAnyMessage], + whileRunning: [.barrier] + ) + AssertCanNotStart( + [.noFlagReadsFromAnyMessage], + whileRunning: [.barrier] + ) + AssertCanNotStart( + Set(PipeliningRequirement.arbitraryRequirements), + whileRunning: [.barrier] + ) } } @@ -357,82 +536,94 @@ extension PipeliningTests { func testAppend() { let append = CommandStreamPart.append(.start(tag: "A1", appendingTo: .food)) XCTAssertEqual(append.pipeliningRequirements, []) - XCTAssertEqual(append.pipeliningBehavior, [ - .mayTriggerUntaggedExpunge, - ]) + XCTAssertEqual( + append.pipeliningBehavior, + [ + .mayTriggerUntaggedExpunge + ] + ) } func testCatenatePart() { // CATEANTE may reference other messages by UID: let append = CommandStreamPart.append(.catenateURL(.joeURLFetch)) XCTAssertEqual(append.pipeliningRequirements, []) - XCTAssertEqual(append.pipeliningBehavior, [ - .isUIDBased, - ]) + XCTAssertEqual( + append.pipeliningBehavior, + [ + .isUIDBased + ] + ) } func testCommandRequires_noMailboxCommandsRunning() { // Which commands have the requirements that: // > No command that depend on the _Selected State_ must be running. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - - (#line, .create(.food, [])), - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - - (#line, .status(.food, [.messageCount])), - (#line, .copy(.set([1]), .food)), - (#line, .uidCopy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - (#line, .check), - (#line, .expunge), - (#line, .uidExpunge(.set([1]))), - (#line, .search(key: .all, charset: nil, returnOptions: [.all])), - (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .enable([.condStore])), - - (#line, .idleStart), - (#line, .id([:])), - (#line, .namespace), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - ], require: .noMailboxCommandsRunning) - - Assert(commands: [ - (#line, .examine(.food)), - (#line, .select(.food)), - (#line, .unselect), - (#line, .close), - ], require: .noMailboxCommandsRunning) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + + (#line, .create(.food, [])), + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + + (#line, .status(.food, [.messageCount])), + (#line, .copy(.set([1]), .food)), + (#line, .uidCopy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + (#line, .check), + (#line, .expunge), + (#line, .uidExpunge(.set([1]))), + (#line, .search(key: .all, charset: nil, returnOptions: [.all])), + (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .enable([.condStore])), + + (#line, .idleStart), + (#line, .id([:])), + (#line, .namespace), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + ], + require: .noMailboxCommandsRunning + ) + + Assert( + commands: [ + (#line, .examine(.food)), + (#line, .select(.food)), + (#line, .unselect), + (#line, .close), + ], + require: .noMailboxCommandsRunning + ) } func testCommandRequires_noUntaggedExpungeResponse() { @@ -440,77 +631,91 @@ extension PipeliningTests { // > No command besides `FETCH`, `STORE`, and `SEARCH` is running. // This is a requirement for all sequence number based commands. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .enable([.condStore])), - (#line, .unselect), - (#line, .idleStart), - - (#line, .id([:])), - (#line, .namespace), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - - (#line, .select(.food)), - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .status(.food, [.messageCount])), - (#line, .check), - (#line, .close), - (#line, .expunge), - (#line, .uidExpunge(.set([1]))), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - ], require: .noUntaggedExpungeResponse) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .enable([.condStore])), + (#line, .unselect), + (#line, .idleStart), + + (#line, .id([:])), + (#line, .namespace), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + + (#line, .select(.food)), + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .status(.food, [.messageCount])), + (#line, .check), + (#line, .close), + (#line, .expunge), + (#line, .uidExpunge(.set([1]))), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + ], + require: .noUntaggedExpungeResponse + ) // All commands that reference messages by sequence numbers have this requirement: - Assert(commands: [ - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - ], require: .noUntaggedExpungeResponse) + Assert( + commands: [ + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + ], + require: .noUntaggedExpungeResponse + ) // SEARCH, ESEARCH, and UID SEARCH // only have this requirement if a search key references sequence numbers. SearchKey.keysWithoutSequenceNumber.forEach { key in - AssertFalse(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], require: .noUntaggedExpungeResponse, "key: \(key)") + AssertFalse( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + require: .noUntaggedExpungeResponse, + "key: \(key)" + ) } SearchKey.keysWithSequenceNumber.forEach { key in - Assert(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], require: .noUntaggedExpungeResponse, "key: \(key)") + Assert( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + require: .noUntaggedExpungeResponse, + "key: \(key)" + ) } } @@ -518,87 +723,101 @@ extension PipeliningTests { // Which commands have the requirements that: // > No command is running that uses UIDs to specify messages. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .enable([.condStore])), - (#line, .unselect), - (#line, .idleStart), - - (#line, .id([:])), - (#line, .namespace), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - - (#line, .select(.food)), - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .status(.food, [.messageCount])), - (#line, .check), - (#line, .close), - (#line, .expunge), - (#line, .uidExpunge(.set([1]))), - (#line, .extendedSearch(ExtendedSearchOptions(key: .bcc("foo")))), - (#line, .extendedSearch(ExtendedSearchOptions(key: .uid(.set([1]))))), - (#line, .search(key: .bcc("foo"), charset: nil, returnOptions: [])), - (#line, .search(key: .uid(.set([1])), charset: nil, returnOptions: [])), - (#line, .uidSearch(key: .bcc("foo"), charset: nil, returnOptions: [])), - (#line, .uidSearch(key: .uid(.set([1])), charset: nil, returnOptions: [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - ], require: .noUIDBasedCommandRunning) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .enable([.condStore])), + (#line, .unselect), + (#line, .idleStart), + + (#line, .id([:])), + (#line, .namespace), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + + (#line, .select(.food)), + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .status(.food, [.messageCount])), + (#line, .check), + (#line, .close), + (#line, .expunge), + (#line, .uidExpunge(.set([1]))), + (#line, .extendedSearch(ExtendedSearchOptions(key: .bcc("foo")))), + (#line, .extendedSearch(ExtendedSearchOptions(key: .uid(.set([1]))))), + (#line, .search(key: .bcc("foo"), charset: nil, returnOptions: [])), + (#line, .search(key: .uid(.set([1])), charset: nil, returnOptions: [])), + (#line, .uidSearch(key: .bcc("foo"), charset: nil, returnOptions: [])), + (#line, .uidSearch(key: .uid(.set([1])), charset: nil, returnOptions: [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + ], + require: .noUIDBasedCommandRunning + ) // All commands that reference messages by sequence numbers have this requirement: - Assert(commands: [ - (#line, .uidSearch(key: .sequenceNumbers(.set([1])), charset: nil, returnOptions: [])), - (#line, .search(key: .sequenceNumbers(.set([1])), charset: nil, returnOptions: [.all])), - (#line, .extendedSearch(ExtendedSearchOptions(key: .sequenceNumbers(.set([1]))))), - - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - ], require: .noUIDBasedCommandRunning) + Assert( + commands: [ + (#line, .uidSearch(key: .sequenceNumbers(.set([1])), charset: nil, returnOptions: [])), + (#line, .search(key: .sequenceNumbers(.set([1])), charset: nil, returnOptions: [.all])), + (#line, .extendedSearch(ExtendedSearchOptions(key: .sequenceNumbers(.set([1]))))), + + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + ], + require: .noUIDBasedCommandRunning + ) // SEARCH, ESEARCH, and UID SEARCH // only have this requirement if a search key references sequence numbers. SearchKey.keysWithoutSequenceNumber.forEach { key in - AssertFalse(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], require: .noUIDBasedCommandRunning, "key: \(key)") + AssertFalse( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + require: .noUIDBasedCommandRunning, + "key: \(key)" + ) } SearchKey.keysWithSequenceNumber.forEach { key in - Assert(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], require: .noUIDBasedCommandRunning, "key: \(key)") + Assert( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + require: .noUIDBasedCommandRunning, + "key: \(key)" + ) } } @@ -607,93 +826,111 @@ extension PipeliningTests { // > No STORE command is running. // (i.e. no flags are being changed) - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - - (#line, .logout), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .enable([.condStore])), - (#line, .unselect), - (#line, .idleStart), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .id([:])), - (#line, .namespace), - (#line, .uidExpunge(.set([1]))), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - - (#line, .select(.food)), - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .status(.food, [.messageCount])), - (#line, .check), - (#line, .close), - (#line, .expunge), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - // STORE is ok if SILENT is set: - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - ], require: .noFlagChangesToAnyMessage) - - Assert(commands: [ - // FETCH that return flags: - (#line, .fetch(.set([1]), [.envelope, .uid, .flags], [])), - (#line, .fetch(.set([1]), [.uid, .flags], [])), - (#line, .uidFetch(.lastCommand, [.envelope, .uid, .flags], [])), - // STORE without SILENT will also return flags: - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - ], require: .noFlagChangesToAnyMessage) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + + (#line, .logout), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .enable([.condStore])), + (#line, .unselect), + (#line, .idleStart), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .id([:])), + (#line, .namespace), + (#line, .uidExpunge(.set([1]))), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + + (#line, .select(.food)), + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .status(.food, [.messageCount])), + (#line, .check), + (#line, .close), + (#line, .expunge), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + // STORE is ok if SILENT is set: + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + ], + require: .noFlagChangesToAnyMessage + ) + + Assert( + commands: [ + // FETCH that return flags: + (#line, .fetch(.set([1]), [.envelope, .uid, .flags], [])), + (#line, .fetch(.set([1]), [.uid, .flags], [])), + (#line, .uidFetch(.lastCommand, [.envelope, .uid, .flags], [])), + // STORE without SILENT will also return flags: + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + ], + require: .noFlagChangesToAnyMessage + ) MessageIdentifierSetNonEmpty.arbitrarySets.forEach { uids in - Assert(commands: [ - // UID FETCH that return flags: - (#line, .uidFetch(.set(uids), [.envelope, .uid, .flags], [])), - (#line, .uidFetch(.set(uids), [.uid, .flags], [])), - // UID STORE without SILENT will also return flags: - (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), - ], require: .noFlagChanges(uids), "uids: \(uids)") + Assert( + commands: [ + // UID FETCH that return flags: + (#line, .uidFetch(.set(uids), [.envelope, .uid, .flags], [])), + (#line, .uidFetch(.set(uids), [.uid, .flags], [])), + // UID STORE without SILENT will also return flags: + (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), + ], + require: .noFlagChanges(uids), + "uids: \(uids)" + ) } // SEARCH, ESEARCH, and UID SEARCH have this requirement only if they // reference flags: SearchKey.keysWithoutFlags.forEach { key in - AssertFalse(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], require: .noFlagChangesToAnyMessage, "key: \(key)") + AssertFalse( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + require: .noFlagChangesToAnyMessage, + "key: \(key)" + ) } SearchKey.keysWithFlags.forEach { key in - Assert(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], require: .noFlagChangesToAnyMessage, "key: \(key)") + Assert( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + require: .noFlagChangesToAnyMessage, + "key: \(key)" + ) } } @@ -701,79 +938,93 @@ extension PipeliningTests { // Which commands have the requirements that: /// > No command is running that retrieves flags. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .enable([.condStore])), - (#line, .unselect), - (#line, .idleStart), - (#line, .id([:])), - (#line, .namespace), - (#line, .uidExpunge(.set([1]))), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - - (#line, .select(.food)), - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .status(.food, [.messageCount])), - (#line, .check), - (#line, .close), - (#line, .expunge), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .fetch(.set([1]), [.envelope, .uid, .flags], [])), - (#line, .fetch(.set([1]), [.uid, .flags], [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid, .flags], [])), - (#line, .uidFetch(.set([1]), [.uid, .flags], [])), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - ], require: .noFlagReadsFromAnyMessage) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .enable([.condStore])), + (#line, .unselect), + (#line, .idleStart), + (#line, .id([:])), + (#line, .namespace), + (#line, .uidExpunge(.set([1]))), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + + (#line, .select(.food)), + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .status(.food, [.messageCount])), + (#line, .check), + (#line, .close), + (#line, .expunge), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .fetch(.set([1]), [.envelope, .uid, .flags], [])), + (#line, .fetch(.set([1]), [.uid, .flags], [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidFetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid, .flags], [])), + (#line, .uidFetch(.set([1]), [.uid, .flags], [])), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + ], + require: .noFlagReadsFromAnyMessage + ) // STORE / UID STORE are the only ones with this requirement: - Assert(commands: [ - (#line, .uidStore(.lastCommand, [], .flags(.add(silent: false, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - ], require: .noFlagReadsFromAnyMessage) + Assert( + commands: [ + (#line, .uidStore(.lastCommand, [], .flags(.add(silent: false, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + ], + require: .noFlagReadsFromAnyMessage + ) MessageIdentifierSetNonEmpty.arbitrarySets.forEach { uids in - Assert(commands: [ - (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .uidStore(.set(uids), [], .flags(.add(silent: true, list: [.answered])))), - ], require: .noFlagReads(uids), "uids: \(uids)") + Assert( + commands: [ + (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .uidStore(.set(uids), [], .flags(.add(silent: true, list: [.answered])))), + ], + require: .noFlagReads(uids), + "uids: \(uids)" + ) } // SEARCH, ESEARCH, and UID SEARCH never have this requirement. SearchKey.arbitraryKeys.forEach { key in - AssertFalse(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], require: .noFlagReadsFromAnyMessage, "key: \(key)") + AssertFalse( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + require: .noFlagReadsFromAnyMessage, + "key: \(key)" + ) } } } @@ -784,489 +1035,557 @@ extension PipeliningTests { func testCommandBehavior_changesMailboxSelection() { /// Commands that change the _mailbox selection_. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .enable([.condStore])), - (#line, .idleStart), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .id([:])), - (#line, .namespace), - (#line, .uidExpunge(.set([1]))), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .create(.food, [])), - (#line, .status(.food, [.messageCount])), - (#line, .check), - (#line, .expunge), - (#line, .search(key: .all, charset: nil, returnOptions: [.all])), - (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - ], haveBehavior: .changesMailboxSelection) - - Assert(commands: [ - (#line, .select(.food)), - (#line, .examine(.food)), - (#line, .unselect), - (#line, .close), - ], haveBehavior: .changesMailboxSelection) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .enable([.condStore])), + (#line, .idleStart), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .id([:])), + (#line, .namespace), + (#line, .uidExpunge(.set([1]))), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .create(.food, [])), + (#line, .status(.food, [.messageCount])), + (#line, .check), + (#line, .expunge), + (#line, .search(key: .all, charset: nil, returnOptions: [.all])), + (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + ], + haveBehavior: .changesMailboxSelection + ) + + Assert( + commands: [ + (#line, .select(.food)), + (#line, .examine(.food)), + (#line, .unselect), + (#line, .close), + ], + haveBehavior: .changesMailboxSelection + ) } func testCommandBehavior_dependsOnMailboxSelection() { /// Commands that depend on the _mailbox selection_. - AssertFalse(commands: [ - (#line, .capability), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - (#line, .enable([.condStore])), - (#line, .id([:])), - (#line, .namespace), - - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .status(.food, [.messageCount])), - - (#line, .close), - (#line, .select(.food)), - (#line, .unselect), - - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - ], haveBehavior: .dependsOnMailboxSelection) - - Assert(commands: [ - (#line, .noop), - (#line, .check), - (#line, .uidExpunge(.set([1]))), - (#line, .expunge), - (#line, .search(key: .all, charset: nil, returnOptions: [.all])), - (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .idleStart), - ], haveBehavior: .dependsOnMailboxSelection) + AssertFalse( + commands: [ + (#line, .capability), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + (#line, .enable([.condStore])), + (#line, .id([:])), + (#line, .namespace), + + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .status(.food, [.messageCount])), + + (#line, .close), + (#line, .select(.food)), + (#line, .unselect), + + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + ], + haveBehavior: .dependsOnMailboxSelection + ) + + Assert( + commands: [ + (#line, .noop), + (#line, .check), + (#line, .uidExpunge(.set([1]))), + (#line, .expunge), + (#line, .search(key: .all, charset: nil, returnOptions: [.all])), + (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .idleStart), + ], + haveBehavior: .dependsOnMailboxSelection + ) } func testCommandBehavior_mayTriggerUntaggedExpunge() { // All commands, except for FETCH, STORE, and SEARCH can // trigger an untagged EXPUNGE. - AssertFalse(commands: [ - // FETCH, STORE, and SEARCH - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .search(key: .all, charset: nil, returnOptions: [.all])), - (#line, .search(key: .answered, charset: nil, returnOptions: [.all])), - // Does not make sense for these: - (#line, .login(username: "user", password: "password")), - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .logout), - ], haveBehavior: .mayTriggerUntaggedExpunge) - - Assert(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .status(.food, [.messageCount])), - (#line, .close), - (#line, .select(.food)), - - (#line, .check), - (#line, .expunge), - (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .enable([.condStore])), - (#line, .unselect), - (#line, .idleStart), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .id([:])), - (#line, .namespace), - (#line, .uidExpunge(.set([1]))), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - ], haveBehavior: .mayTriggerUntaggedExpunge) + AssertFalse( + commands: [ + // FETCH, STORE, and SEARCH + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .search(key: .all, charset: nil, returnOptions: [.all])), + (#line, .search(key: .answered, charset: nil, returnOptions: [.all])), + // Does not make sense for these: + (#line, .login(username: "user", password: "password")), + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .logout), + ], + haveBehavior: .mayTriggerUntaggedExpunge + ) + + Assert( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .status(.food, [.messageCount])), + (#line, .close), + (#line, .select(.food)), + + (#line, .check), + (#line, .expunge), + (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .enable([.condStore])), + (#line, .unselect), + (#line, .idleStart), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .id([:])), + (#line, .namespace), + (#line, .uidExpunge(.set([1]))), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + ], + haveBehavior: .mayTriggerUntaggedExpunge + ) } func testCommandBehavior_isUIDBased() { /// Commands that use UIDs to specify messages. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - (#line, .enable([.condStore])), - (#line, .id([:])), - (#line, .namespace), - - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .status(.food, [.messageCount])), - (#line, .close), - (#line, .select(.food)), - (#line, .unselect), - (#line, .idleStart), - - (#line, .check), - (#line, .expunge), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - ], haveBehavior: .isUIDBased) - - Assert(commands: [ - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - (#line, .uidExpunge(.set([1]))), - // `GENURLAUTH` and `URLFETCH` (indirectly) reference UIDs: - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - ], haveBehavior: .isUIDBased) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + (#line, .enable([.condStore])), + (#line, .id([:])), + (#line, .namespace), + + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .status(.food, [.messageCount])), + (#line, .close), + (#line, .select(.food)), + (#line, .unselect), + (#line, .idleStart), + + (#line, .check), + (#line, .expunge), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + ], + haveBehavior: .isUIDBased + ) + + Assert( + commands: [ + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + (#line, .uidExpunge(.set([1]))), + // `GENURLAUTH` and `URLFETCH` (indirectly) reference UIDs: + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + ], + haveBehavior: .isUIDBased + ) SearchKey.keysWithoutUID.forEach { key in - AssertFalse(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - ], haveBehavior: .isUIDBased, "key: \(key)") + AssertFalse( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])) + ], + haveBehavior: .isUIDBased, + "key: \(key)" + ) // UID SEARCH has this behavior even if the key does not // reference UIDs: - Assert(commands: [ - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - ], haveBehavior: .isUIDBased, "key: \(key)") + Assert( + commands: [ + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + ], + haveBehavior: .isUIDBased, + "key: \(key)" + ) } SearchKey.keysWithUID.forEach { key in - Assert(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], haveBehavior: .isUIDBased, "key: \(key)") + Assert( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + haveBehavior: .isUIDBased, + "key: \(key)" + ) } } func testCommandBehavior_changesFlags() { /// Commands that change flags on messages. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - (#line, .enable([.condStore])), - (#line, .id([:])), - (#line, .namespace), - - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .status(.food, [.messageCount])), - (#line, .close), - (#line, .select(.food)), - (#line, .unselect), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .idleStart), - - (#line, .check), - (#line, .expunge), - (#line, .uidExpunge(.set([1]))), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .search(key: .all, charset: nil, returnOptions: [.all])), - (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - ], haveBehavior: .changesFlagsOnAnyMessage) - - Assert(commands: [ - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .uidStore(.lastCommand, [], .flags(.add(silent: true, list: [.answered])))), - ], haveBehavior: .changesFlagsOnAnyMessage) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + (#line, .enable([.condStore])), + (#line, .id([:])), + (#line, .namespace), + + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .status(.food, [.messageCount])), + (#line, .close), + (#line, .select(.food)), + (#line, .unselect), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .idleStart), + + (#line, .check), + (#line, .expunge), + (#line, .uidExpunge(.set([1]))), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .search(key: .all, charset: nil, returnOptions: [.all])), + (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + ], + haveBehavior: .changesFlagsOnAnyMessage + ) + + Assert( + commands: [ + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .uidStore(.lastCommand, [], .flags(.add(silent: true, list: [.answered])))), + ], + haveBehavior: .changesFlagsOnAnyMessage + ) MessageIdentifierSetNonEmpty.arbitrarySets.forEach { uids in - Assert(commands: [ - (#line, .uidStore(.set(uids), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), - ], haveBehavior: .changesFlags(uids)) + Assert( + commands: [ + (#line, .uidStore(.set(uids), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), + ], + haveBehavior: .changesFlags(uids) + ) } } func testCommandBehavior_readsFlags() { /// Command that are querying / reading flags. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .login(username: "user", password: "password")), - (#line, .logout), - (#line, .id([:])), - (#line, .namespace), - (#line, .idleStart), - (#line, .enable([.condStore])), - - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .status(.food, [.messageCount])), - (#line, .close), - (#line, .select(.food)), - (#line, .unselect), - (#line, .check), - (#line, .expunge), - (#line, .uidExpunge(.set([1]))), - (#line, .uidCopy(.set([1]), .food)), - (#line, .copy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .subscribe(.food)), - (#line, .unsubscribe(.food)), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - - // STORE is ok as long as it is SILENT - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - - // FETCH is ok as long as it’s not fetching flags: - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - ], haveBehavior: .readsFlagsFromAnyMessage) - - Assert(commands: [ - (#line, .fetch(.set([1]), [.envelope, .uid, .flags], [])), - (#line, .fetch(.set([1]), [.uid, .flags], [])), - // This will also return flags: - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .uidStore(.lastCommand, [], .flags(.add(silent: false, list: [.answered])))), - ], haveBehavior: .readsFlagsFromAnyMessage) - MessageIdentifierSetNonEmpty.arbitrarySets.forEach { uids in - Assert(commands: [ - (#line, .uidFetch(.set(uids), [.envelope, .uid, .flags], [])), - (#line, .uidFetch(.set(uids), [.uid, .flags], [])), + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .login(username: "user", password: "password")), + (#line, .logout), + (#line, .id([:])), + (#line, .namespace), + (#line, .idleStart), + (#line, .enable([.condStore])), + + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .status(.food, [.messageCount])), + (#line, .close), + (#line, .select(.food)), + (#line, .unselect), + (#line, .check), + (#line, .expunge), + (#line, .uidExpunge(.set([1]))), + (#line, .uidCopy(.set([1]), .food)), + (#line, .copy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .subscribe(.food)), + (#line, .unsubscribe(.food)), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + + // STORE is ok as long as it is SILENT + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + + // FETCH is ok as long as it’s not fetching flags: + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + ], + haveBehavior: .readsFlagsFromAnyMessage + ) + + Assert( + commands: [ + (#line, .fetch(.set([1]), [.envelope, .uid, .flags], [])), + (#line, .fetch(.set([1]), [.uid, .flags], [])), // This will also return flags: - (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), - ], haveBehavior: .readsFlags(uids)) + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .uidStore(.lastCommand, [], .flags(.add(silent: false, list: [.answered])))), + ], + haveBehavior: .readsFlagsFromAnyMessage + ) + MessageIdentifierSetNonEmpty.arbitrarySets.forEach { uids in + Assert( + commands: [ + (#line, .uidFetch(.set(uids), [.envelope, .uid, .flags], [])), + (#line, .uidFetch(.set(uids), [.uid, .flags], [])), + // This will also return flags: + (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .uidStore(.set(uids), [], .flags(.add(silent: false, list: [.answered])))), + ], + haveBehavior: .readsFlags(uids) + ) } // SEARCH, ESEARCH, and UID SEARCH have this behavior only if they // reference flags: SearchKey.keysWithoutFlags.forEach { key in - AssertFalse(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], haveBehavior: .readsFlagsFromAnyMessage, "key: \(key)") + AssertFalse( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + haveBehavior: .readsFlagsFromAnyMessage, + "key: \(key)" + ) } SearchKey.keysWithFlags.forEach { key in - Assert(commands: [ - (#line, .search(key: key, charset: nil, returnOptions: [])), - (#line, .extendedSearch(ExtendedSearchOptions(key: key))), - (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), - ], haveBehavior: .readsFlagsFromAnyMessage, "key: \(key)") + Assert( + commands: [ + (#line, .search(key: key, charset: nil, returnOptions: [])), + (#line, .extendedSearch(ExtendedSearchOptions(key: key))), + (#line, .uidSearch(key: key, charset: nil, returnOptions: [])), + ], + haveBehavior: .readsFlagsFromAnyMessage, + "key: \(key)" + ) } } func testCommandBehavior_barrier() { /// No additional commands may be sent until these commands complete. - AssertFalse(commands: [ - (#line, .capability), - (#line, .noop), - - (#line, .login(username: "user", password: "password")), - - (#line, .delete(.food)), - (#line, .rename(from: .food, to: .food, parameters: [:])), - (#line, .examine(.food)), - (#line, .create(.food, [])), - (#line, .list(nil, reference: .food, .food, [])), - (#line, .listIndependent([], reference: .food, .food, [])), - (#line, .lsub(reference: .food, pattern: "Food")), - (#line, .unsubscribe(.food)), - (#line, .subscribe(.food)), - (#line, .status(.food, [.messageCount])), - (#line, .close), - (#line, .select(.food)), - (#line, .unselect), - (#line, .enable([.condStore])), - (#line, .id([:])), - (#line, .namespace), - - (#line, .check), - (#line, .expunge), - (#line, .fetch(.set([1]), [.envelope, .uid], [])), - (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), - (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), - (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), - (#line, .uidCopy(.set([1]), .food)), - (#line, .uidMove(.set([1]), .food)), - (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .copy(.set([1]), .food)), - (#line, .move(.set([1]), .food)), - (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), - (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), - (#line, .search(key: .all, charset: nil, returnOptions: [.all])), - (#line, .uidExpunge(.set([1]))), - (#line, .getQuota(QuotaRoot("foo"))), - (#line, .getQuotaRoot(.food)), - (#line, .setQuota(QuotaRoot("foo"), [])), - (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), - (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), - (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), - (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), - (#line, .generateAuthorizedURL([.joe])), - (#line, .urlFetch([.joeURLFetch])), - ], haveBehavior: .barrier) - - Assert(commands: [ - (#line, .startTLS), - (#line, .authenticate(mechanism: .plain, initialResponse: nil)), - (#line, .compress(.deflate)), - (#line, .logout), - (#line, .idleStart), - ], haveBehavior: .barrier) + AssertFalse( + commands: [ + (#line, .capability), + (#line, .noop), + + (#line, .login(username: "user", password: "password")), + + (#line, .delete(.food)), + (#line, .rename(from: .food, to: .food, parameters: [:])), + (#line, .examine(.food)), + (#line, .create(.food, [])), + (#line, .list(nil, reference: .food, .food, [])), + (#line, .listIndependent([], reference: .food, .food, [])), + (#line, .lsub(reference: .food, pattern: "Food")), + (#line, .unsubscribe(.food)), + (#line, .subscribe(.food)), + (#line, .status(.food, [.messageCount])), + (#line, .close), + (#line, .select(.food)), + (#line, .unselect), + (#line, .enable([.condStore])), + (#line, .id([:])), + (#line, .namespace), + + (#line, .check), + (#line, .expunge), + (#line, .fetch(.set([1]), [.envelope, .uid], [])), + (#line, .fetch(.set([1]), [.bodyStructure(extensions: false)], [])), + (#line, .uidSearch(key: .all, charset: nil, returnOptions: [])), + (#line, .uidFetch(.set([1]), [.envelope, .uid], [])), + (#line, .uidCopy(.set([1]), .food)), + (#line, .uidMove(.set([1]), .food)), + (#line, .uidStore(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .copy(.set([1]), .food)), + (#line, .move(.set([1]), .food)), + (#line, .store(.set([1]), [], .flags(.add(silent: true, list: [.answered])))), + (#line, .store(.set([1]), [], .flags(.add(silent: false, list: [.answered])))), + (#line, .search(key: .all, charset: nil, returnOptions: [.all])), + (#line, .uidExpunge(.set([1]))), + (#line, .getQuota(QuotaRoot("foo"))), + (#line, .getQuotaRoot(.food)), + (#line, .setQuota(QuotaRoot("foo"), [])), + (#line, .getMetadata(options: [], mailbox: .food, entries: ["/shared/comment"])), + (#line, .setMetadata(mailbox: .food, entries: ["/shared/comment": nil])), + (#line, .extendedSearch(ExtendedSearchOptions(key: .all))), + (#line, .resetKey(mailbox: nil, mechanisms: [.internal])), + (#line, .generateAuthorizedURL([.joe])), + (#line, .urlFetch([.joeURLFetch])), + ], + haveBehavior: .barrier + ) + + Assert( + commands: [ + (#line, .startTLS), + (#line, .authenticate(mechanism: .plain, initialResponse: nil)), + (#line, .compress(.deflate)), + (#line, .logout), + (#line, .idleStart), + ], + haveBehavior: .barrier + ) } } diff --git a/Tests/NIOIMAPCoreTests/ResponseStreamingTests.swift b/Tests/NIOIMAPCoreTests/ResponseStreamingTests.swift index 2756efd80..749b6489b 100644 --- a/Tests/NIOIMAPCoreTests/ResponseStreamingTests.swift +++ b/Tests/NIOIMAPCoreTests/ResponseStreamingTests.swift @@ -39,135 +39,209 @@ extension ResponseStreamingTests { } func testBodyStreaming() { - self.AssertFetchResponses("* 1 FETCH (BODY[TEXT]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", [ - (.fetch(.start(1)), #line), - (.fetch(.streamingBegin(kind: .body(section: .text, offset: 4), byteCount: 3)), #line), - (.fetch(.streamingBytes("abc")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 2 UIDFETCH (FLAGS (\\deleted) BODY[TEXT] {3}\r\ndef)\r\n", [ - (.fetch(.startUID(2)), #line), - (.fetch(.simpleAttribute(.flags([.deleted]))), #line), - (.fetch(.streamingBegin(kind: .body(section: .text, offset: nil), byteCount: 3)), #line), - (.fetch(.streamingBytes("def")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 3 FETCH (BODY[TEXT] {3}\r\nghi)\r\n", [ - (.fetch(.start(3)), #line), - (.fetch(.streamingBegin(kind: .body(section: .text, offset: nil), byteCount: 3)), #line), - (.fetch(.streamingBytes("ghi")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 3 FETCH (BODY[TEXT] {3}\r\nghi)\r\n", [ - (.fetch(.start(3)), #line), - (.fetch(.streamingBegin(kind: .body(section: .text, offset: nil), byteCount: 3)), #line), - (.fetch(.streamingBytes("ghi")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 3 FETCH (BODY[5.2.MIME] NIL)\r\n", [ - (.fetch(.start(3)), #line), - (.fetch(.simpleAttribute(.nilBody(.body(section: .init(part: [5, 2], kind: .MIMEHeader), offset: nil)))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 3 FETCH (BODY[3] NIL UID 456)\r\n", [ - (.fetch(.start(3)), #line), - (.fetch(.simpleAttribute(.nilBody(.body(section: .init(part: [3]), offset: nil)))), #line), - (.fetch(.simpleAttribute(.uid(456))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 3 FETCH (BINARY[4] {3}\r\nghi)\r\n", [ - (.fetch(.start(3)), #line), - (.fetch(.streamingBegin(kind: .binary(section: [4], offset: nil), byteCount: 3)), #line), - (.fetch(.streamingBytes("ghi")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 3 FETCH (BINARY[4] NIL)\r\n", [ - (.fetch(.start(3)), #line), - (.fetch(.simpleAttribute(.nilBody(.binary(section: [4], offset: nil)))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 4 FETCH (BODY[4.TEXT]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", [ - (.fetch(.start(4)), #line), - (.fetch(.streamingBegin(kind: .body(section: .init(part: [4], kind: .text), offset: 4), byteCount: 3)), #line), - (.fetch(.streamingBytes("abc")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 5 FETCH (BODY[5.TEXT]<4> \"asdf\" FLAGS (\\seen \\answered))\r\n", [ - (.fetch(.start(5)), #line), - (.fetch(.streamingBegin(kind: .body(section: .init(part: [5], kind: .text), offset: 4), byteCount: 4)), #line), - (.fetch(.streamingBytes("asdf")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 6 FETCH (BODY[5.2]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", [ - (.fetch(.start(6)), #line), - (.fetch(.streamingBegin(kind: .body(section: .init(part: [5, 2], kind: .complete), offset: 4), byteCount: 3)), #line), - (.fetch(.streamingBytes("abc")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 7 FETCH (BODY[5.2.HEADER]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", [ - (.fetch(.start(7)), #line), - (.fetch(.streamingBegin(kind: .body(section: .init(part: [5, 2], kind: .header), offset: 4), byteCount: 3)), #line), - (.fetch(.streamingBytes("abc")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 8 FETCH (RFC822.TEXT {3}\r\nabc)\r\n", [ - (.fetch(.start(8)), #line), - (.fetch(.streamingBegin(kind: .rfc822Text, byteCount: 3)), #line), - (.fetch(.streamingBytes("abc")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 9 FETCH (RFC822.HEADER {3}\r\nabc)\r\n", [ - (.fetch(.start(9)), #line), - (.fetch(.streamingBegin(kind: .rfc822Header, byteCount: 3)), #line), - (.fetch(.streamingBytes("abc")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) + self.AssertFetchResponses( + "* 1 FETCH (BODY[TEXT]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", + [ + (.fetch(.start(1)), #line), + (.fetch(.streamingBegin(kind: .body(section: .text, offset: 4), byteCount: 3)), #line), + (.fetch(.streamingBytes("abc")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 2 UIDFETCH (FLAGS (\\deleted) BODY[TEXT] {3}\r\ndef)\r\n", + [ + (.fetch(.startUID(2)), #line), + (.fetch(.simpleAttribute(.flags([.deleted]))), #line), + (.fetch(.streamingBegin(kind: .body(section: .text, offset: nil), byteCount: 3)), #line), + (.fetch(.streamingBytes("def")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 3 FETCH (BODY[TEXT] {3}\r\nghi)\r\n", + [ + (.fetch(.start(3)), #line), + (.fetch(.streamingBegin(kind: .body(section: .text, offset: nil), byteCount: 3)), #line), + (.fetch(.streamingBytes("ghi")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 3 FETCH (BODY[TEXT] {3}\r\nghi)\r\n", + [ + (.fetch(.start(3)), #line), + (.fetch(.streamingBegin(kind: .body(section: .text, offset: nil), byteCount: 3)), #line), + (.fetch(.streamingBytes("ghi")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 3 FETCH (BODY[5.2.MIME] NIL)\r\n", + [ + (.fetch(.start(3)), #line), + ( + .fetch( + .simpleAttribute(.nilBody(.body(section: .init(part: [5, 2], kind: .MIMEHeader), offset: nil))) + ), #line + ), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 3 FETCH (BODY[3] NIL UID 456)\r\n", + [ + (.fetch(.start(3)), #line), + (.fetch(.simpleAttribute(.nilBody(.body(section: .init(part: [3]), offset: nil)))), #line), + (.fetch(.simpleAttribute(.uid(456))), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 3 FETCH (BINARY[4] {3}\r\nghi)\r\n", + [ + (.fetch(.start(3)), #line), + (.fetch(.streamingBegin(kind: .binary(section: [4], offset: nil), byteCount: 3)), #line), + (.fetch(.streamingBytes("ghi")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 3 FETCH (BINARY[4] NIL)\r\n", + [ + (.fetch(.start(3)), #line), + (.fetch(.simpleAttribute(.nilBody(.binary(section: [4], offset: nil)))), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 4 FETCH (BODY[4.TEXT]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", + [ + (.fetch(.start(4)), #line), + ( + .fetch( + .streamingBegin(kind: .body(section: .init(part: [4], kind: .text), offset: 4), byteCount: 3) + ), #line + ), + (.fetch(.streamingBytes("abc")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 5 FETCH (BODY[5.TEXT]<4> \"asdf\" FLAGS (\\seen \\answered))\r\n", + [ + (.fetch(.start(5)), #line), + ( + .fetch( + .streamingBegin(kind: .body(section: .init(part: [5], kind: .text), offset: 4), byteCount: 4) + ), #line + ), + (.fetch(.streamingBytes("asdf")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 6 FETCH (BODY[5.2]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", + [ + (.fetch(.start(6)), #line), + ( + .fetch( + .streamingBegin( + kind: .body(section: .init(part: [5, 2], kind: .complete), offset: 4), + byteCount: 3 + ) + ), #line + ), + (.fetch(.streamingBytes("abc")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 7 FETCH (BODY[5.2.HEADER]<4> {3}\r\nabc FLAGS (\\seen \\answered))\r\n", + [ + (.fetch(.start(7)), #line), + ( + .fetch( + .streamingBegin( + kind: .body(section: .init(part: [5, 2], kind: .header), offset: 4), + byteCount: 3 + ) + ), #line + ), + (.fetch(.streamingBytes("abc")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.simpleAttribute(.flags([.seen, .answered]))), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 8 FETCH (RFC822.TEXT {3}\r\nabc)\r\n", + [ + (.fetch(.start(8)), #line), + (.fetch(.streamingBegin(kind: .rfc822Text, byteCount: 3)), #line), + (.fetch(.streamingBytes("abc")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 9 FETCH (RFC822.HEADER {3}\r\nabc)\r\n", + [ + (.fetch(.start(9)), #line), + (.fetch(.streamingBegin(kind: .rfc822Header, byteCount: 3)), #line), + (.fetch(.streamingBytes("abc")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) } func testBinaryStreaming() { - self.AssertFetchResponses("* 1 FETCH (BINARY[] {4}\r\n1234)\r\n", [ - (.fetch(.start(1)), #line), - (.fetch(.streamingBegin(kind: .binary(section: [], offset: nil), byteCount: 4)), #line), - (.fetch(.streamingBytes("1234")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) - - self.AssertFetchResponses("* 2 UIDFETCH (BINARY[1.2]<77> {4}\r\n1234)\r\n", [ - (.fetch(.startUID(2)), #line), - (.fetch(.streamingBegin(kind: .binary(section: [1, 2], offset: 77), byteCount: 4)), #line), - (.fetch(.streamingBytes("1234")), #line), - (.fetch(.streamingEnd), #line), - (.fetch(.finish), #line), - ]) + self.AssertFetchResponses( + "* 1 FETCH (BINARY[] {4}\r\n1234)\r\n", + [ + (.fetch(.start(1)), #line), + (.fetch(.streamingBegin(kind: .binary(section: [], offset: nil), byteCount: 4)), #line), + (.fetch(.streamingBytes("1234")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) + + self.AssertFetchResponses( + "* 2 UIDFETCH (BINARY[1.2]<77> {4}\r\n1234)\r\n", + [ + (.fetch(.startUID(2)), #line), + (.fetch(.streamingBegin(kind: .binary(section: [1, 2], offset: 77), byteCount: 4)), #line), + (.fetch(.streamingBytes("1234")), #line), + (.fetch(.streamingEnd), #line), + (.fetch(.finish), #line), + ] + ) } } diff --git a/Tests/NIOIMAPCoreTests/String+ByteBuffer+Tests.swift b/Tests/NIOIMAPCoreTests/String+ByteBuffer+Tests.swift index 1705f195f..297e2c157 100644 --- a/Tests/NIOIMAPCoreTests/String+ByteBuffer+Tests.swift +++ b/Tests/NIOIMAPCoreTests/String+ByteBuffer+Tests.swift @@ -51,6 +51,6 @@ extension String_ByteBuffer_Tests { func testInitBestEffortDecodingInvalid() { let test2: [UInt8] = [0x41, 0xFF, 0x42] - XCTAssertEqual(String(bestEffortDecodingUTF8Bytes: test2), "AB") // we've removed the invalid middle byte + XCTAssertEqual(String(bestEffortDecodingUTF8Bytes: test2), "AB") // we've removed the invalid middle byte } } diff --git a/Tests/NIOIMAPCoreTests/TestUtilities.swift b/Tests/NIOIMAPCoreTests/TestUtilities.swift index e6b3360a7..7d07188a5 100644 --- a/Tests/NIOIMAPCoreTests/TestUtilities.swift +++ b/Tests/NIOIMAPCoreTests/TestUtilities.swift @@ -27,11 +27,14 @@ extension TestUtilities { return ParseBuffer(buffer) } - static func withParseBuffer(_ string: String, - terminator: String = "", - shouldRemainUnchanged: Bool = false, - file: StaticString = (#filePath), line: UInt = #line, _ body: (inout ParseBuffer) throws -> Void) - { + static func withParseBuffer( + _ string: String, + terminator: String = "", + shouldRemainUnchanged: Bool = false, + file: StaticString = (#filePath), + line: UInt = #line, + _ body: (inout ParseBuffer) throws -> Void + ) { var inputBuffer = ByteBufferAllocator().buffer(capacity: string.utf8.count + terminator.utf8.count + 10) inputBuffer.writeString("hello") inputBuffer.moveReaderIndex(forwardBy: 5) @@ -40,16 +43,22 @@ extension TestUtilities { inputBuffer.writeString("hallo") inputBuffer.moveWriterIndex(to: inputBuffer.writerIndex - 5) - let expected = inputBuffer.getSlice(at: inputBuffer.readerIndex + string.utf8.count, length: terminator.utf8.count)! + let expected = inputBuffer.getSlice( + at: inputBuffer.readerIndex + string.utf8.count, + length: terminator.utf8.count + )! let beforeRunningBody = inputBuffer var parseBuffer = ParseBuffer(inputBuffer) defer { let expectedString = String(buffer: expected) - let remaining = (try? PL.parseBytes(buffer: &parseBuffer, - tracker: .makeNewDefaultLimitStackTracker, - upTo: .max)) ?? ByteBuffer() + let remaining = + (try? PL.parseBytes( + buffer: &parseBuffer, + tracker: .makeNewDefault, + upTo: .max + )) ?? ByteBuffer() let remainingString = String(buffer: remaining) if shouldRemainUnchanged { XCTAssertEqual(String(buffer: beforeRunningBody), remainingString, file: file, line: line) diff --git a/Tests/NIOIMAPTests/B2MV+Tests.swift b/Tests/NIOIMAPTests/B2MV+Tests.swift index 0cb260682..3cb1aa2f9 100644 --- a/Tests/NIOIMAPTests/B2MV+Tests.swift +++ b/Tests/NIOIMAPTests/B2MV+Tests.swift @@ -50,9 +50,15 @@ extension B2MV_Tests { // MARK: Login - (#"tag LOGIN "foo" "bar""#, [.tagged(.init(tag: "tag", command: .login(username: "foo", password: "bar")))]), + ( + #"tag LOGIN "foo" "bar""#, + [.tagged(.init(tag: "tag", command: .login(username: "foo", password: "bar")))] + ), ("tag LOGIN \"\" {0+}\n", [.tagged(.init(tag: "tag", command: .login(username: "", password: "")))]), - (#"tag LOGIN "foo" "bar""#, [.tagged(.init(tag: "tag", command: .login(username: "foo", password: "bar")))]), + ( + #"tag LOGIN "foo" "bar""#, + [.tagged(.init(tag: "tag", command: .login(username: "foo", password: "bar")))] + ), (#"tag LOGIN foo bar"#, [.tagged(.init(tag: "tag", command: .login(username: "foo", password: "bar")))]), // MARK: Select @@ -60,23 +66,96 @@ extension B2MV_Tests { ("tag SELECT box1", [.tagged(.init(tag: "tag", command: .select(.init("box1"), [])))]), ("tag SELECT \"box2\"", [.tagged(.init(tag: "tag", command: .select(.init("box2"), [])))]), ("tag SELECT {4+}\nbox3", [.tagged(.init(tag: "tag", command: .select(.init("box3"), [])))]), - ("tag SELECT box4 (k1 1 k2 2)", [.tagged(.init(tag: "tag", command: .select(.init("box4"), [.basic(.init(key: "k1", value: .sequence(.set([1])))), .basic(.init(key: "k2", value: .sequence(.set([2]))))])))]), + ( + "tag SELECT box4 (k1 1 k2 2)", + [ + .tagged( + .init( + tag: "tag", + command: .select( + .init("box4"), + [ + .basic(.init(key: "k1", value: .sequence(.set([1])))), + .basic(.init(key: "k2", value: .sequence(.set([2])))), + ] + ) + ) + ) + ] + ), // MARK: Examine ("tag EXAMINE box1", [.tagged(.init(tag: "tag", command: .examine(.init("box1"), [])))]), ("tag EXAMINE \"box2\"", [.tagged(.init(tag: "tag", command: .examine(.init("box2"), [])))]), ("tag EXAMINE {4+}\nbox3", [.tagged(.init(tag: "tag", command: .examine(.init("box3"), [])))]), - ("tag EXAMINE box4 (k3 1 k4 2)", [.tagged(.init(tag: "tag", command: .examine(.init("box4"), [.basic(.init(key: "k3", value: .sequence(.set([1])))), .basic(.init(key: "k4", value: .sequence(.set([2]))))])))]), - ("tag EXAMINE box4 (QRESYNC (67890007 20050715194045000 41,43:211,214:541))", [.tagged(.init(tag: "tag", command: .examine(.init("box4"), [.qresync(QResyncParameter(uidValidity: 67890007, modificationSequenceValue: 20050715194045000, knownUIDs: [41, 43 ... 211, 214 ... 541], sequenceMatchData: nil))])))]), - ("tag EXAMINE box4 (CONDSTORE)", [.tagged(.init(tag: "tag", command: .examine(.init("box4"), [.condStore])))]), + ( + "tag EXAMINE box4 (k3 1 k4 2)", + [ + .tagged( + .init( + tag: "tag", + command: .examine( + .init("box4"), + [ + .basic(.init(key: "k3", value: .sequence(.set([1])))), + .basic(.init(key: "k4", value: .sequence(.set([2])))), + ] + ) + ) + ) + ] + ), + ( + "tag EXAMINE box4 (QRESYNC (67890007 20050715194045000 41,43:211,214:541))", + [ + .tagged( + .init( + tag: "tag", + command: .examine( + .init("box4"), + [ + .qresync( + QResyncParameter( + uidValidity: 67_890_007, + modificationSequenceValue: 20_050_715_194_045_000, + knownUIDs: [41, 43...211, 214...541], + sequenceMatchData: nil + ) + ) + ] + ) + ) + ) + ] + ), + ( + "tag EXAMINE box4 (CONDSTORE)", + [.tagged(.init(tag: "tag", command: .examine(.init("box4"), [.condStore])))] + ), // MARK: Create ("tag CREATE newBox1", [.tagged(.init(tag: "tag", command: .create(.init("newBox1"), [])))]), ("tag CREATE \"newBox2\"", [.tagged(.init(tag: "tag", command: .create(.init("newBox2"), [])))]), ("tag CREATE {7+}\nnewBox3", [.tagged(.init(tag: "tag", command: .create(.init("newBox3"), [])))]), - ("tag CREATE newBox4 (k5 5 k6 6)", [.tagged(.init(tag: "tag", command: .create(.init("newBox4"), [.labelled(.init(key: "k5", value: .sequence(.set([5])))), .labelled(.init(key: "k6", value: .sequence(.set([6]))))])))]), + ( + "tag CREATE newBox4 (k5 5 k6 6)", + [ + .tagged( + .init( + tag: "tag", + command: .create( + .init("newBox4"), + [ + .labelled(.init(key: "k5", value: .sequence(.set([5])))), + .labelled(.init(key: "k6", value: .sequence(.set([6])))), + ] + ) + ) + ) + ] + ), // MARK: Delete @@ -86,9 +165,32 @@ extension B2MV_Tests { // MARK: Rename - (#"tag RENAME "foo" "bar""#, [.tagged(TaggedCommand(tag: "tag", command: .rename(from: MailboxName("foo"), to: MailboxName("bar"), parameters: [:])))]), - (#"tag RENAME InBoX "inBOX""#, [.tagged(TaggedCommand(tag: "tag", command: .rename(from: .inbox, to: .inbox, parameters: [:])))]), - ("tag RENAME {1+}\n1 {1+}\n2", [.tagged(TaggedCommand(tag: "tag", command: .rename(from: MailboxName("1"), to: MailboxName("2"), parameters: [:])))]), + ( + #"tag RENAME "foo" "bar""#, + [ + .tagged( + TaggedCommand( + tag: "tag", + command: .rename(from: MailboxName("foo"), to: MailboxName("bar"), parameters: [:]) + ) + ) + ] + ), + ( + #"tag RENAME InBoX "inBOX""#, + [.tagged(TaggedCommand(tag: "tag", command: .rename(from: .inbox, to: .inbox, parameters: [:])))] + ), + ( + "tag RENAME {1+}\n1 {1+}\n2", + [ + .tagged( + TaggedCommand( + tag: "tag", + command: .rename(from: MailboxName("1"), to: MailboxName("2"), parameters: [:]) + ) + ) + ] + ), // MARK: Subscribe @@ -113,7 +215,10 @@ extension B2MV_Tests { // MARK: List ("tag LIST INBOX \"\"", [.tagged(.init(tag: "tag", command: .list(nil, reference: .inbox, .mailbox(""))))]), - ("tag LIST /Mail/ %", [.tagged(.init(tag: "tag", command: .list(nil, reference: .init("/Mail/"), .mailbox("%"))))]), + ( + "tag LIST /Mail/ %", + [.tagged(.init(tag: "tag", command: .list(nil, reference: .init("/Mail/"), .mailbox("%"))))] + ), // MARK: LSUB @@ -122,17 +227,30 @@ extension B2MV_Tests { // MARK: Status ("tag STATUS INBOX (MESSAGES)", [.tagged(.init(tag: "tag", command: .status(.inbox, [.messageCount])))]), - ("tag STATUS INBOX (MESSAGES RECENT UIDNEXT)", [.tagged(.init(tag: "tag", command: .status(.inbox, [.messageCount, .recentCount, .uidNext])))]), + ( + "tag STATUS INBOX (MESSAGES RECENT UIDNEXT)", + [.tagged(.init(tag: "tag", command: .status(.inbox, [.messageCount, .recentCount, .uidNext])))] + ), // MARK: Append - ("tag APPEND box (\\Seen) {1+}\na", [ - .append(.start(tag: "tag", appendingTo: .init("box"))), - .append(.beginMessage(message: .init(options: .init(flagList: [.seen], extensions: [:]), data: .init(byteCount: 1)))), - .append(.messageBytes("a")), - .append(.endMessage), - .append(.finish), - ]), + ( + "tag APPEND box (\\Seen) {1+}\na", + [ + .append(.start(tag: "tag", appendingTo: .init("box"))), + .append( + .beginMessage( + message: .init( + options: .init(flagList: [.seen], extensions: [:]), + data: .init(byteCount: 1) + ) + ) + ), + .append(.messageBytes("a")), + .append(.endMessage), + .append(.finish), + ] + ), ] let input = inoutPairs.map { ($0.0 + "\n", $0.1.map { SynchronizedCommand($0) }) } @@ -179,34 +297,85 @@ extension B2MV_Tests { // MARK: State responses ("* OK Server ready", [.untagged(.conditionalState(.ok(.init(code: nil, text: "Server ready"))))]), - ("* OK [ALERT] Server ready", [.untagged(.conditionalState(.ok(.init(code: .alert, text: "Server ready"))))]), + ( + "* OK [ALERT] Server ready", + [.untagged(.conditionalState(.ok(.init(code: .alert, text: "Server ready"))))] + ), ("* NO Disk full", [.untagged(.conditionalState(.no(.init(code: nil, text: "Disk full"))))]), - ("* NO [READ-ONLY] Disk full", [.untagged(.conditionalState(.no(.init(code: .readOnly, text: "Disk full"))))]), + ( + "* NO [READ-ONLY] Disk full", + [.untagged(.conditionalState(.no(.init(code: .readOnly, text: "Disk full"))))] + ), ("* BAD horrible", [.untagged(.conditionalState(.bad(.init(code: nil, text: "horrible"))))]), - ("* BAD [BADCHARSET (utf123)] horrible", [.untagged(.conditionalState(.bad(.init(code: .badCharset(["utf123"]), text: "horrible"))))]), + ( + "* BAD [BADCHARSET (utf123)] horrible", + [.untagged(.conditionalState(.bad(.init(code: .badCharset(["utf123"]), text: "horrible"))))] + ), // MARK: Bye ("* BYE logging off", [.untagged(.conditionalState(.bye(.init(code: nil, text: "logging off"))))]), - ("* BYE [ALERT] logging off", [.untagged(.conditionalState(.bye(.init(code: .alert, text: "logging off"))))]), + ( + "* BYE [ALERT] logging off", + [.untagged(.conditionalState(.bye(.init(code: .alert, text: "logging off"))))] + ), // MARK: Capability - ("* CAPABILITY IMAP4rev1 CHILDREN CONDSTORE", [.untagged(.capabilityData([.imap4rev1, .children, .condStore]))]), + ( + "* CAPABILITY IMAP4rev1 CHILDREN CONDSTORE", + [.untagged(.capabilityData([.imap4rev1, .children, .condStore]))] + ), // With trailing space: - ("* CAPABILITY IMAP4rev1 CHILDREN CONDSTORE ", [.untagged(.capabilityData([.imap4rev1, .children, .condStore]))]), + ( + "* CAPABILITY IMAP4rev1 CHILDREN CONDSTORE ", + [.untagged(.capabilityData([.imap4rev1, .children, .condStore]))] + ), // MARK: LIST - ("* LIST (\\noselect) \"/\" ~/Mail/foo", [.untagged(.mailboxData(.list(.init(attributes: [.noSelect], path: try! .init(name: .init("~/Mail/foo"), pathSeparator: "/"), extensions: [:]))))]), + ( + "* LIST (\\noselect) \"/\" ~/Mail/foo", + [ + .untagged( + .mailboxData( + .list( + .init( + attributes: [.noSelect], + path: try! .init(name: .init("~/Mail/foo"), pathSeparator: "/"), + extensions: [:] + ) + ) + ) + ) + ] + ), // MARK: LSUB - ("* LSUB (\\noselect) \"/\" ~/Mail/foo", [.untagged(.mailboxData(.lsub(.init(attributes: [.noSelect], path: try! .init(name: .init("~/Mail/foo"), pathSeparator: "/"), extensions: [:]))))]), + ( + "* LSUB (\\noselect) \"/\" ~/Mail/foo", + [ + .untagged( + .mailboxData( + .lsub( + .init( + attributes: [.noSelect], + path: try! .init(name: .init("~/Mail/foo"), pathSeparator: "/"), + extensions: [:] + ) + ) + ) + ) + ] + ), // MARK: Status - ("* STATUS INBOX (MESSAGES 231 UIDNEXT 44292)", [.untagged(.mailboxData(.status(.inbox, .init(messageCount: 231, nextUID: 44292))))]), + ( + "* STATUS INBOX (MESSAGES 231 UIDNEXT 44292)", + [.untagged(.mailboxData(.status(.inbox, .init(messageCount: 231, nextUID: 44292))))] + ), // MARK: Flags @@ -243,8 +412,14 @@ extension B2MV_Tests { // MARK: Tagged ("tag OK Complete", [.tagged(.init(tag: "tag", state: .ok(.init(code: nil, text: "Complete"))))]), - ("tag NO [ALERT] Complete", [.tagged(.init(tag: "tag", state: .no(.init(code: .alert, text: "Complete"))))]), - ("tag BAD [PARSE] Complete", [.tagged(.init(tag: "tag", state: .bad(.init(code: .parse, text: "Complete"))))]), + ( + "tag NO [ALERT] Complete", + [.tagged(.init(tag: "tag", state: .no(.init(code: .alert, text: "Complete"))))] + ), + ( + "tag BAD [PARSE] Complete", + [.tagged(.init(tag: "tag", state: .bad(.init(code: .parse, text: "Complete"))))] + ), ] let inputs = inoutPairs.map { ($0.0 + "\n", $0.1.map { ResponseOrContinuationRequest.response($0) }) } diff --git a/Tests/NIOIMAPTests/Client/AppendStateMachineTests.swift b/Tests/NIOIMAPTests/Client/AppendStateMachineTests.swift index 5f3d70fc3..032fd2a67 100644 --- a/Tests/NIOIMAPTests/Client/AppendStateMachineTests.swift +++ b/Tests/NIOIMAPTests/Client/AppendStateMachineTests.swift @@ -25,7 +25,11 @@ class AppendStateMachineTests: XCTestCase { func testNormalWorkflow() { // append a message - XCTAssertTrue(self.stateMachine.sendCommand(.append(.beginMessage(message: .init(options: .init(), data: .init(byteCount: 10)))))) + XCTAssertTrue( + self.stateMachine.sendCommand( + .append(.beginMessage(message: .init(options: .init(), data: .init(byteCount: 10)))) + ) + ) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.data("req"))) XCTAssertFalse(self.stateMachine.sendCommand(.append(.messageBytes("12345")))) XCTAssertFalse(self.stateMachine.sendCommand(.append(.messageBytes("67890")))) @@ -44,8 +48,13 @@ class AppendStateMachineTests: XCTestCase { XCTAssertFalse(self.stateMachine.sendCommand(.append(.endCatenate))) XCTAssertFalse(self.stateMachine.sendCommand(.append(.finish))) - XCTAssertNoThrow(XCTAssertEqual( - try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK"))))), true - )) + XCTAssertNoThrow( + XCTAssertEqual( + try self.stateMachine.receiveResponse( + .tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK")))) + ), + true + ) + ) } } diff --git a/Tests/NIOIMAPTests/Client/AuthenticationStateMachineTests.swift b/Tests/NIOIMAPTests/Client/AuthenticationStateMachineTests.swift index 304de9c73..848d92697 100644 --- a/Tests/NIOIMAPTests/Client/AuthenticationStateMachineTests.swift +++ b/Tests/NIOIMAPTests/Client/AuthenticationStateMachineTests.swift @@ -30,7 +30,9 @@ class AuthenticationStateMachineTests: XCTestCase { XCTAssertEqual(stateMachine.state, .waitingForServer) // finish - XCTAssertNoThrow(try stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK")))))) + XCTAssertNoThrow( + try stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK"))))) + ) XCTAssertEqual(stateMachine.state, .finished) } @@ -44,7 +46,9 @@ class AuthenticationStateMachineTests: XCTestCase { XCTAssertEqual(stateMachine.state, .waitingForServer) // finish - XCTAssertNoThrow(try stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK")))))) + XCTAssertNoThrow( + try stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK"))))) + ) XCTAssertEqual(stateMachine.state, .finished) } diff --git a/Tests/NIOIMAPTests/Coders/ClientStateMachineTests.swift b/Tests/NIOIMAPTests/Coders/ClientStateMachineTests.swift index b1d467024..c67ba5618 100644 --- a/Tests/NIOIMAPTests/Coders/ClientStateMachineTests.swift +++ b/Tests/NIOIMAPTests/Coders/ClientStateMachineTests.swift @@ -28,32 +28,54 @@ class ClientStateMachineTests: XCTestCase { func testNormalWorkflow() { // NOOP XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A1", command: .noop)))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK"))))) + ) // LOGIN one continuation - XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A3", command: .login(username: "\\", password: "hey"))))) + XCTAssertNoThrow( + try self.stateMachine.sendCommand( + .tagged(.init(tag: "A3", command: .login(username: "\\", password: "hey"))) + ) + ) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.data("OK"))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A3", state: .no(.init(text: "Invalid")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A3", state: .no(.init(text: "Invalid"))))) + ) // LOGIN two continuations - XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A3", command: .login(username: "\\", password: "\\"))))) + XCTAssertNoThrow( + try self.stateMachine.sendCommand( + .tagged(.init(tag: "A3", command: .login(username: "\\", password: "\\"))) + ) + ) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.data("OK"))) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.data("OK"))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A3", state: .no(.init(text: "Invalid")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A3", state: .no(.init(text: "Invalid"))))) + ) // IDLE XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A2", command: .idleStart)))) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.responseText(.init(text: "OK")))) XCTAssertNoThrow(try self.stateMachine.sendCommand(.idleDone)) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(text: "OK"))))) + ) // SELECT XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A5", command: .select(.inbox, []))))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A5", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A5", state: .ok(.init(text: "OK"))))) + ) // APPEND XCTAssertNoThrow(try self.stateMachine.sendCommand(.append(.start(tag: "A4", appendingTo: .inbox)))) - XCTAssertNoThrow(try self.stateMachine.sendCommand(.append(.beginMessage(message: .init(options: .init(), data: .init(byteCount: 10)))))) + XCTAssertNoThrow( + try self.stateMachine.sendCommand( + .append(.beginMessage(message: .init(options: .init(), data: .init(byteCount: 10)))) + ) + ) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.data("OK"))) XCTAssertNoThrow(try self.stateMachine.sendCommand(.append(.messageBytes("0123456789")))) XCTAssertNoThrow(try self.stateMachine.sendCommand(.append(.endMessage))) @@ -81,7 +103,9 @@ class ClientStateMachineTests: XCTestCase { XCTAssertEqual(action, .sendChunks([.init(bytes: "\\\r\n", promise: nil, shouldSucceedPromise: true)])) // this time we expect a tagged response, so let's send one - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK"))))) + ) } func testChunkingMultipleCommands() { @@ -100,10 +124,13 @@ class ClientStateMachineTests: XCTestCase { var result3: ClientStateMachine.ContinuationRequestAction! XCTAssertNoThrow(result3 = try self.stateMachine.receiveContinuationRequest(.data("OK"))) - XCTAssertEqual(result3, .sendChunks([ - .init(bytes: "\\ \"pass\"\r\n", promise: nil, shouldSucceedPromise: true), - .init(bytes: "A2 NOOP\r\n", promise: nil, shouldSucceedPromise: true), - ])) + XCTAssertEqual( + result3, + .sendChunks([ + .init(bytes: "\\ \"pass\"\r\n", promise: nil, shouldSucceedPromise: true), + .init(bytes: "A2 NOOP\r\n", promise: nil, shouldSucceedPromise: true), + ]) + ) } func testMultipleCommandsCanRunConcurrently() { @@ -111,10 +138,18 @@ class ClientStateMachineTests: XCTestCase { XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A2", command: .noop)))) XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A3", command: .noop)))) XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A4", command: .noop)))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(text: "OK")))))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A4", state: .ok(.init(text: "OK")))))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK")))))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A3", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(text: "OK"))))) + ) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A4", state: .ok(.init(text: "OK"))))) + ) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK"))))) + ) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A3", state: .ok(.init(text: "OK"))))) + ) } func testDuplicateTagThrows() { @@ -131,7 +166,9 @@ extension ClientStateMachineTests { func testIdleWorkflow_normal() { // set up the state machine, show we can send a command XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A1", command: .noop)))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK"))))) + ) // 1. start idle // 2. server confirms idle @@ -150,7 +187,9 @@ extension ClientStateMachineTests { XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.responseText(.init(text: "IDLE started")))) // machine is idle, so sending a different command should throw - XCTAssertThrowsError(try self.stateMachine.receiveContinuationRequest(.responseText(.init(text: "IDLE started")))) { e in + XCTAssertThrowsError( + try self.stateMachine.receiveContinuationRequest(.responseText(.init(text: "IDLE started"))) + ) { e in XCTAssertTrue(e is UnexpectedContinuationRequest) } } @@ -162,18 +201,26 @@ extension ClientStateMachineTests { func testAuthenticationWorkflow_normal() { // set up the state machine, show we can send a command XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A1", command: .noop)))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK"))))) + ) // 1. start authenticating // 2. a couple of challenges back and forth - XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A2", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))))) + XCTAssertNoThrow( + try self.stateMachine.sendCommand( + .tagged(.init(tag: "A2", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))) + ) + ) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.data("c1"))) XCTAssertNoThrow(try self.stateMachine.sendCommand(.continuationResponse("r1"))) XCTAssertNoThrow(try self.stateMachine.receiveContinuationRequest(.data("c2"))) XCTAssertNoThrow(try self.stateMachine.sendCommand(.continuationResponse("r2"))) // finish authenticating - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(code: nil, text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(code: nil, text: "OK"))))) + ) // state machine should have reset, so we can send a normal command again XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A3", command: .noop)))) @@ -182,12 +229,20 @@ extension ClientStateMachineTests { func testAuthenticationWorkflow_normal_noChallenges() { // set up the state machine, show we can send a command XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A1", command: .noop)))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(text: "OK"))))) + ) // 1. start authenticating // 2. server immediately confirms - XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A2", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))))) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(code: nil, text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.sendCommand( + .tagged(.init(tag: "A2", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))) + ) + ) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A2", state: .ok(.init(code: nil, text: "OK"))))) + ) // state machine should have reset, so we can send a normal command again XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A3", command: .noop)))) @@ -195,7 +250,11 @@ extension ClientStateMachineTests { func testAuthenticationWorkflow_untagged() { // set up the state machine to authenticate - XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A1", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))))) + XCTAssertNoThrow( + try self.stateMachine.sendCommand( + .tagged(.init(tag: "A1", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))) + ) + ) // machine is authenticating, so sending an untagged response should be ignored XCTAssertNoThrow(try self.stateMachine.receiveResponse(.untagged(.enableData([.metadata])))) @@ -203,7 +262,11 @@ extension ClientStateMachineTests { func testAuthenticationWorkflow_unexpectedResponse() { // set up the state machine to authenticate - XCTAssertNoThrow(try self.stateMachine.sendCommand(.tagged(.init(tag: "A1", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))))) + XCTAssertNoThrow( + try self.stateMachine.sendCommand( + .tagged(.init(tag: "A1", command: .authenticate(mechanism: .gssAPI, initialResponse: nil))) + ) + ) // machine is authenticating, so sending idle started should throw XCTAssertThrowsError(try self.stateMachine.receiveResponse(.idleStarted)) { e in @@ -238,12 +301,16 @@ extension ClientStateMachineTests { // append a message self.assert( .init(bytes: " {10}\r\n", promise: nil, shouldSucceedPromise: true), - try self.stateMachine.sendCommand(.append(.beginMessage(message: .init(options: .init(), data: .init(byteCount: 10))))) + try self.stateMachine.sendCommand( + .append(.beginMessage(message: .init(options: .init(), data: .init(byteCount: 10)))) + ) + ) + XCTAssertNoThrow( + XCTAssertEqual( + try self.stateMachine.receiveContinuationRequest(.data("ready2")), + .sendChunks([]) + ) ) - XCTAssertNoThrow(XCTAssertEqual( - try self.stateMachine.receiveContinuationRequest(.data("ready2")), - .sendChunks([]) - )) self.assert( .init(bytes: "01234", promise: nil, shouldSucceedPromise: true), try self.stateMachine.sendCommand(.append(.messageBytes("01234"))) @@ -297,7 +364,9 @@ extension ClientStateMachineTests { .init(bytes: "\r\n", promise: nil, shouldSucceedPromise: true), try self.stateMachine.sendCommand(.append(.finish)) ) - XCTAssertNoThrow(try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK")))))) + XCTAssertNoThrow( + try self.stateMachine.receiveResponse(.tagged(.init(tag: "A1", state: .ok(.init(code: nil, text: "OK"))))) + ) self.assert( .init(bytes: "A2 NOOP\r\n", promise: nil, shouldSucceedPromise: true), try self.stateMachine.sendCommand(.tagged(.init(tag: "A2", command: .noop))) @@ -309,7 +378,11 @@ extension ClientStateMachineTests { XCTAssertNoThrow(result = try self.stateMachine.sendCommand(.append(.start(tag: "A1", appendingTo: .inbox)))) XCTAssertEqual(result, .init(bytes: "A1 APPEND \"INBOX\"", promise: nil, shouldSucceedPromise: true)) - XCTAssertNoThrow(result = try self.stateMachine.sendCommand(.append(.beginMessage(message: .init(options: .none, data: .init(byteCount: 5)))))) + XCTAssertNoThrow( + result = try self.stateMachine.sendCommand( + .append(.beginMessage(message: .init(options: .none, data: .init(byteCount: 5)))) + ) + ) XCTAssertEqual(result, .init(bytes: " {5}\r\n", promise: nil, shouldSucceedPromise: true)) XCTAssertNoThrow(result = try self.stateMachine.sendCommand(.append(.messageBytes("0")))) @@ -331,15 +404,18 @@ extension ClientStateMachineTests { var resultAction: ClientStateMachine.ContinuationRequestAction! XCTAssertNoThrow(resultAction = try self.stateMachine.receiveContinuationRequest(.data("OK"))) - XCTAssertEqual(resultAction, .sendChunks([ - .init(bytes: "0", promise: nil, shouldSucceedPromise: true), - .init(bytes: "1", promise: nil, shouldSucceedPromise: true), - .init(bytes: "2", promise: nil, shouldSucceedPromise: true), - .init(bytes: "3", promise: nil, shouldSucceedPromise: true), - .init(bytes: "4", promise: nil, shouldSucceedPromise: true), - .init(bytes: "", promise: nil, shouldSucceedPromise: true), - .init(bytes: "\r\n", promise: nil, shouldSucceedPromise: true), - ])) + XCTAssertEqual( + resultAction, + .sendChunks([ + .init(bytes: "0", promise: nil, shouldSucceedPromise: true), + .init(bytes: "1", promise: nil, shouldSucceedPromise: true), + .init(bytes: "2", promise: nil, shouldSucceedPromise: true), + .init(bytes: "3", promise: nil, shouldSucceedPromise: true), + .init(bytes: "4", promise: nil, shouldSucceedPromise: true), + .init(bytes: "", promise: nil, shouldSucceedPromise: true), + .init(bytes: "\r\n", promise: nil, shouldSucceedPromise: true), + ]) + ) } func testSendingAnAuthencationChallengeWhenUnexpectedThrows() { diff --git a/Tests/NIOIMAPTests/Coders/IMAPClientHandlerTests.swift b/Tests/NIOIMAPTests/Coders/IMAPClientHandlerTests.swift index 2d46a416b..262175808 100644 --- a/Tests/NIOIMAPTests/Coders/IMAPClientHandlerTests.swift +++ b/Tests/NIOIMAPTests/Coders/IMAPClientHandlerTests.swift @@ -27,26 +27,45 @@ class IMAPClientHandlerTests: XCTestCase { self.writeOutbound(.tagged(.init(tag: "a", command: .login(username: "foo", password: "bar")))) self.assertOutboundString("a LOGIN \"foo\" \"bar\"\r\n") self.writeInbound("a OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "a", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "a", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) } func testReferralURLResponse() { let expectedResponse = Response.tagged( - TaggedResponse(tag: "A1", - state: .ok(ResponseText(code: - .referral(IMAPURL(server: IMAPServer(userAuthenticationMechanism: nil, host: "hostname", port: nil), - query: URLCommand.fetch( - path: MessagePath( - mailboxReference: MailboxUIDValidity(encodeMailbox: EncodedMailbox(mailbox: "foo/bar"), - uidValidity: nil), - iUID: IUID(uid: 1234), - section: nil, - range: nil - ), - authenticatedURL: nil - ))), - text: "")))) + TaggedResponse( + tag: "A1", + state: .ok( + ResponseText( + code: + .referral( + IMAPURL( + server: IMAPServer(userAuthenticationMechanism: nil, host: "hostname", port: nil), + query: URLCommand.fetch( + path: MessagePath( + mailboxReference: MailboxUIDValidity( + encodeMailbox: EncodedMailbox(mailbox: "foo/bar"), + uidValidity: nil + ), + iUID: IUID(uid: 1234), + section: nil, + range: nil + ), + authenticatedURL: nil + ) + ) + ), + text: "" + ) + ) + ) + ) self.writeOutbound(.tagged(.init(tag: "A1", command: .login(username: "foo", password: "bar")))) self.assertOutboundString("A1 LOGIN \"foo\" \"bar\"\r\n") self.writeInbound("A1 OK [REFERRAL imap://hostname/foo/bar/;UID=1234]\r\n") @@ -54,26 +73,48 @@ class IMAPClientHandlerTests: XCTestCase { } func testCommandThatNeedsToWaitForContinuationRequest() { - let f = self.writeOutbound(CommandStreamPart.tagged(TaggedCommand(tag: "x", - command: .rename(from: .init("\\"), - to: .init("to"), - parameters: [:]))), - wait: false) + let f = self.writeOutbound( + CommandStreamPart.tagged( + TaggedCommand( + tag: "x", + command: .rename( + from: .init("\\"), + to: .init("to"), + parameters: [:] + ) + ) + ), + wait: false + ) self.assertOutboundString("x RENAME {1}\r\n") self.writeInbound("+ OK\r\n") self.assertOutboundString("\\ \"to\"\r\n") XCTAssertNoThrow(try f.wait()) self.writeInbound("x OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "x", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "x", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) } func testCommandThatNeedsToWaitForTwoContinuationRequest() { - let f = self.writeOutbound(CommandStreamPart.tagged(TaggedCommand(tag: "x", - command: .rename(from: .init("\\"), - to: .init("\""), - parameters: [:]))), - wait: false) + let f = self.writeOutbound( + CommandStreamPart.tagged( + TaggedCommand( + tag: "x", + command: .rename( + from: .init("\\"), + to: .init("\""), + parameters: [:] + ) + ) + ), + wait: false + ) self.assertOutboundString("x RENAME {1}\r\n") self.writeInbound("+ OK\r\n") self.assertOutboundString("\\ {1}\r\n") @@ -81,21 +122,43 @@ class IMAPClientHandlerTests: XCTestCase { self.assertOutboundString("\"\r\n") XCTAssertNoThrow(try f.wait()) self.writeInbound("x OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "x", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "x", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) } func testTwoContReqCommandsEnqueued() { - let f1 = self.writeOutbound(CommandStreamPart.tagged(TaggedCommand(tag: "x", - command: .rename(from: .init("\\"), - to: .init("to"), - parameters: [:]))), - wait: false) - let f2 = self.writeOutbound(CommandStreamPart.tagged(TaggedCommand(tag: "y", - command: .rename(from: .init("from"), - to: .init("\\"), - parameters: [:]))), - wait: false) + let f1 = self.writeOutbound( + CommandStreamPart.tagged( + TaggedCommand( + tag: "x", + command: .rename( + from: .init("\\"), + to: .init("to"), + parameters: [:] + ) + ) + ), + wait: false + ) + let f2 = self.writeOutbound( + CommandStreamPart.tagged( + TaggedCommand( + tag: "y", + command: .rename( + from: .init("from"), + to: .init("\\"), + parameters: [:] + ) + ) + ), + wait: false + ) self.assertOutboundString("x RENAME {1}\r\n") self.writeInbound("+ OK\r\n") XCTAssertNoThrow(try f1.wait()) @@ -105,11 +168,23 @@ class IMAPClientHandlerTests: XCTestCase { XCTAssertNoThrow(try f2.wait()) self.assertOutboundString("\\\r\n") self.writeInbound("x OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "x", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "x", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) self.writeInbound("y OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "y", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "y", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) } // This makes sure that we successfully switch from responding to continuation @@ -143,30 +218,68 @@ class IMAPClientHandlerTests: XCTestCase { XCTAssertNoThrow(try f5.wait()) self.writeInbound("1 OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "1", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "1", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) self.writeInbound("2 OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "2", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "2", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) self.writeInbound("3 OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "3", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "3", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) self.writeInbound("4 OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "4", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "4", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) self.writeInbound("5 OK ok\r\n") - self.assertInbound(.tagged(.init(tag: "5", - state: .ok(.init(code: nil, text: "ok"))))) + self.assertInbound( + .tagged( + .init( + tag: "5", + state: .ok(.init(code: nil, text: "ok")) + ) + ) + ) } func testUnexpectedContinuationRequest() { - self.writeOutbound(CommandStreamPart.tagged(TaggedCommand(tag: "x", - command: .rename(from: .init("from"), - to: .init("to"), - parameters: [:])))) + self.writeOutbound( + CommandStreamPart.tagged( + TaggedCommand( + tag: "x", + command: .rename( + from: .init("from"), + to: .init("to"), + parameters: [:] + ) + ) + ) + ) self.assertOutboundString("x RENAME \"from\" \"to\"\r\n") XCTAssertThrowsError(try self.channel.writeInbound(self.buffer(string: "+ OK\r\n+ OK\r\n"))) { error in XCTAssertTrue(error is UnexpectedContinuationRequest, "Error is \(error)") @@ -184,54 +297,100 @@ class IMAPClientHandlerTests: XCTestCase { self.assertInbound(.authenticationChallenge(ByteBuffer())) // client responds - let responseBytes1 = "YIIB+wYJKoZIhvcSAQICAQBuggHqMIIB5qADAgEFoQMCAQ6iBwMFACAAAACjggEmYYIBIjCCAR6gAwIBBaESGxB1Lndhc2hpbmd0b24uZWR1oi0wK6ADAgEDoSQwIhsEaW1hcBsac2hpdmFtcy5jYWMud2FzaGluZ3Rvbi5lZHWjgdMwgdCgAwIBAaEDAgEDooHDBIHAcS1GSa5b+fXnPZNmXB9SjL8Ollj2SKyb+3S0iXMljen/jNkpJXAleKTz6BQPzj8duz8EtoOuNfKgweViyn/9B9bccy1uuAE2HI0yC/PHXNNU9ZrBziJ8Lm0tTNc98kUpjXnHZhsMcz5Mx2GR6dGknbI0iaGcRerMUsWOuBmKKKRmVMMdR9T3EZdpqsBd7jZCNMWotjhivd5zovQlFqQ2Wjc2+y46vKP/iXxWIuQJuDiisyXF0Y8+5GTpALpHDc1/pIGmMIGjoAMCAQGigZsEgZg2on5mSuxoDHEA1w9bcW9nFdFxDKpdrQhVGVRDIzcCMCTzvUboqb5KjY1NJKJsfjRQiBYBdENKfzK+g5DlV8nrw81uOcP8NOQCLR5XkoMHC0Dr/80ziQzbNqhxO6652Npft0LQwJvenwDI13YxpwOdMXzkWZN/XrEqOWp6GCgXTBvCyLWLlWnbaUkZdEYbKHBPjd8t/1x5Yg==" - self.writeOutbound(.continuationResponse(ByteBuffer(bytes: [ - 0x60, 0x82, 0x01, 0xFB, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x02, 0x01, 0x00, - 0x6E, 0x82, 0x01, 0xEA, 0x30, 0x82, 0x01, 0xE6, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x03, 0x02, 0x01, - 0x0E, 0xA2, 0x07, 0x03, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA3, 0x82, 0x01, 0x26, 0x61, 0x82, 0x01, - 0x22, 0x30, 0x82, 0x01, 0x1E, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x12, 0x1B, 0x10, 0x75, 0x2E, 0x77, - 0x61, 0x73, 0x68, 0x69, 0x6E, 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA2, 0x2D, 0x30, 0x2B, - 0xA0, 0x03, 0x02, 0x01, 0x03, 0xA1, 0x24, 0x30, 0x22, 0x1B, 0x04, 0x69, 0x6D, 0x61, 0x70, 0x1B, 0x1A, - 0x73, 0x68, 0x69, 0x76, 0x61, 0x6D, 0x73, 0x2E, 0x63, 0x61, 0x63, 0x2E, 0x77, 0x61, 0x73, 0x68, 0x69, - 0x6E, 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA3, 0x81, 0xD3, 0x30, 0x81, 0xD0, 0xA0, 0x03, - 0x02, 0x01, 0x01, 0xA1, 0x03, 0x02, 0x01, 0x03, 0xA2, 0x81, 0xC3, 0x04, 0x81, 0xC0, 0x71, 0x2D, 0x46, - 0x49, 0xAE, 0x5B, 0xF9, 0xF5, 0xE7, 0x3D, 0x93, 0x66, 0x5C, 0x1F, 0x52, 0x8C, 0xBF, 0x0E, 0x96, 0x58, - 0xF6, 0x48, 0xAC, 0x9B, 0xFB, 0x74, 0xB4, 0x89, 0x73, 0x25, 0x8D, 0xE9, 0xFF, 0x8C, 0xD9, 0x29, 0x25, - 0x70, 0x25, 0x78, 0xA4, 0xF3, 0xE8, 0x14, 0x0F, 0xCE, 0x3F, 0x1D, 0xBB, 0x3F, 0x04, 0xB6, 0x83, 0xAE, - 0x35, 0xF2, 0xA0, 0xC1, 0xE5, 0x62, 0xCA, 0x7F, 0xFD, 0x07, 0xD6, 0xDC, 0x73, 0x2D, 0x6E, 0xB8, 0x01, - 0x36, 0x1C, 0x8D, 0x32, 0x0B, 0xF3, 0xC7, 0x5C, 0xD3, 0x54, 0xF5, 0x9A, 0xC1, 0xCE, 0x22, 0x7C, 0x2E, - 0x6D, 0x2D, 0x4C, 0xD7, 0x3D, 0xF2, 0x45, 0x29, 0x8D, 0x79, 0xC7, 0x66, 0x1B, 0x0C, 0x73, 0x3E, 0x4C, - 0xC7, 0x61, 0x91, 0xE9, 0xD1, 0xA4, 0x9D, 0xB2, 0x34, 0x89, 0xA1, 0x9C, 0x45, 0xEA, 0xCC, 0x52, 0xC5, - 0x8E, 0xB8, 0x19, 0x8A, 0x28, 0xA4, 0x66, 0x54, 0xC3, 0x1D, 0x47, 0xD4, 0xF7, 0x11, 0x97, 0x69, 0xAA, - 0xC0, 0x5D, 0xEE, 0x36, 0x42, 0x34, 0xC5, 0xA8, 0xB6, 0x38, 0x62, 0xBD, 0xDE, 0x73, 0xA2, 0xF4, 0x25, - 0x16, 0xA4, 0x36, 0x5A, 0x37, 0x36, 0xFB, 0x2E, 0x3A, 0xBC, 0xA3, 0xFF, 0x89, 0x7C, 0x56, 0x22, 0xE4, - 0x09, 0xB8, 0x38, 0xA2, 0xB3, 0x25, 0xC5, 0xD1, 0x8F, 0x3E, 0xE4, 0x64, 0xE9, 0x00, 0xBA, 0x47, 0x0D, - 0xCD, 0x7F, 0xA4, 0x81, 0xA6, 0x30, 0x81, 0xA3, 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA2, 0x81, 0x9B, 0x04, - 0x81, 0x98, 0x36, 0xA2, 0x7E, 0x66, 0x4A, 0xEC, 0x68, 0x0C, 0x71, 0x00, 0xD7, 0x0F, 0x5B, 0x71, 0x6F, - 0x67, 0x15, 0xD1, 0x71, 0x0C, 0xAA, 0x5D, 0xAD, 0x08, 0x55, 0x19, 0x54, 0x43, 0x23, 0x37, 0x02, 0x30, - 0x24, 0xF3, 0xBD, 0x46, 0xE8, 0xA9, 0xBE, 0x4A, 0x8D, 0x8D, 0x4D, 0x24, 0xA2, 0x6C, 0x7E, 0x34, 0x50, - 0x88, 0x16, 0x01, 0x74, 0x43, 0x4A, 0x7F, 0x32, 0xBE, 0x83, 0x90, 0xE5, 0x57, 0xC9, 0xEB, 0xC3, 0xCD, - 0x6E, 0x39, 0xC3, 0xFC, 0x34, 0xE4, 0x02, 0x2D, 0x1E, 0x57, 0x92, 0x83, 0x07, 0x0B, 0x40, 0xEB, 0xFF, - 0xCD, 0x33, 0x89, 0x0C, 0xDB, 0x36, 0xA8, 0x71, 0x3B, 0xAE, 0xB9, 0xD8, 0xDA, 0x5F, 0xB7, 0x42, 0xD0, - 0xC0, 0x9B, 0xDE, 0x9F, 0x00, 0xC8, 0xD7, 0x76, 0x31, 0xA7, 0x03, 0x9D, 0x31, 0x7C, 0xE4, 0x59, 0x93, - 0x7F, 0x5E, 0xB1, 0x2A, 0x39, 0x6A, 0x7A, 0x18, 0x28, 0x17, 0x4C, 0x1B, 0xC2, 0xC8, 0xB5, 0x8B, 0x95, - 0x69, 0xDB, 0x69, 0x49, 0x19, 0x74, 0x46, 0x1B, 0x28, 0x70, 0x4F, 0x8D, 0xDF, 0x2D, 0xFF, 0x5C, 0x79, - 0x62, - ]))) + let responseBytes1 = + "YIIB+wYJKoZIhvcSAQICAQBuggHqMIIB5qADAgEFoQMCAQ6iBwMFACAAAACjggEmYYIBIjCCAR6gAwIBBaESGxB1Lndhc2hpbmd0b24uZWR1oi0wK6ADAgEDoSQwIhsEaW1hcBsac2hpdmFtcy5jYWMud2FzaGluZ3Rvbi5lZHWjgdMwgdCgAwIBAaEDAgEDooHDBIHAcS1GSa5b+fXnPZNmXB9SjL8Ollj2SKyb+3S0iXMljen/jNkpJXAleKTz6BQPzj8duz8EtoOuNfKgweViyn/9B9bccy1uuAE2HI0yC/PHXNNU9ZrBziJ8Lm0tTNc98kUpjXnHZhsMcz5Mx2GR6dGknbI0iaGcRerMUsWOuBmKKKRmVMMdR9T3EZdpqsBd7jZCNMWotjhivd5zovQlFqQ2Wjc2+y46vKP/iXxWIuQJuDiisyXF0Y8+5GTpALpHDc1/pIGmMIGjoAMCAQGigZsEgZg2on5mSuxoDHEA1w9bcW9nFdFxDKpdrQhVGVRDIzcCMCTzvUboqb5KjY1NJKJsfjRQiBYBdENKfzK+g5DlV8nrw81uOcP8NOQCLR5XkoMHC0Dr/80ziQzbNqhxO6652Npft0LQwJvenwDI13YxpwOdMXzkWZN/XrEqOWp6GCgXTBvCyLWLlWnbaUkZdEYbKHBPjd8t/1x5Yg==" + self.writeOutbound( + .continuationResponse( + ByteBuffer(bytes: [ + 0x60, 0x82, 0x01, 0xFB, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x02, 0x01, + 0x00, + 0x6E, 0x82, 0x01, 0xEA, 0x30, 0x82, 0x01, 0xE6, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x03, 0x02, + 0x01, + 0x0E, 0xA2, 0x07, 0x03, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00, 0xA3, 0x82, 0x01, 0x26, 0x61, 0x82, + 0x01, + 0x22, 0x30, 0x82, 0x01, 0x1E, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x12, 0x1B, 0x10, 0x75, 0x2E, + 0x77, + 0x61, 0x73, 0x68, 0x69, 0x6E, 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA2, 0x2D, 0x30, + 0x2B, + 0xA0, 0x03, 0x02, 0x01, 0x03, 0xA1, 0x24, 0x30, 0x22, 0x1B, 0x04, 0x69, 0x6D, 0x61, 0x70, 0x1B, + 0x1A, + 0x73, 0x68, 0x69, 0x76, 0x61, 0x6D, 0x73, 0x2E, 0x63, 0x61, 0x63, 0x2E, 0x77, 0x61, 0x73, 0x68, + 0x69, + 0x6E, 0x67, 0x74, 0x6F, 0x6E, 0x2E, 0x65, 0x64, 0x75, 0xA3, 0x81, 0xD3, 0x30, 0x81, 0xD0, 0xA0, + 0x03, + 0x02, 0x01, 0x01, 0xA1, 0x03, 0x02, 0x01, 0x03, 0xA2, 0x81, 0xC3, 0x04, 0x81, 0xC0, 0x71, 0x2D, + 0x46, + 0x49, 0xAE, 0x5B, 0xF9, 0xF5, 0xE7, 0x3D, 0x93, 0x66, 0x5C, 0x1F, 0x52, 0x8C, 0xBF, 0x0E, 0x96, + 0x58, + 0xF6, 0x48, 0xAC, 0x9B, 0xFB, 0x74, 0xB4, 0x89, 0x73, 0x25, 0x8D, 0xE9, 0xFF, 0x8C, 0xD9, 0x29, + 0x25, + 0x70, 0x25, 0x78, 0xA4, 0xF3, 0xE8, 0x14, 0x0F, 0xCE, 0x3F, 0x1D, 0xBB, 0x3F, 0x04, 0xB6, 0x83, + 0xAE, + 0x35, 0xF2, 0xA0, 0xC1, 0xE5, 0x62, 0xCA, 0x7F, 0xFD, 0x07, 0xD6, 0xDC, 0x73, 0x2D, 0x6E, 0xB8, + 0x01, + 0x36, 0x1C, 0x8D, 0x32, 0x0B, 0xF3, 0xC7, 0x5C, 0xD3, 0x54, 0xF5, 0x9A, 0xC1, 0xCE, 0x22, 0x7C, + 0x2E, + 0x6D, 0x2D, 0x4C, 0xD7, 0x3D, 0xF2, 0x45, 0x29, 0x8D, 0x79, 0xC7, 0x66, 0x1B, 0x0C, 0x73, 0x3E, + 0x4C, + 0xC7, 0x61, 0x91, 0xE9, 0xD1, 0xA4, 0x9D, 0xB2, 0x34, 0x89, 0xA1, 0x9C, 0x45, 0xEA, 0xCC, 0x52, + 0xC5, + 0x8E, 0xB8, 0x19, 0x8A, 0x28, 0xA4, 0x66, 0x54, 0xC3, 0x1D, 0x47, 0xD4, 0xF7, 0x11, 0x97, 0x69, + 0xAA, + 0xC0, 0x5D, 0xEE, 0x36, 0x42, 0x34, 0xC5, 0xA8, 0xB6, 0x38, 0x62, 0xBD, 0xDE, 0x73, 0xA2, 0xF4, + 0x25, + 0x16, 0xA4, 0x36, 0x5A, 0x37, 0x36, 0xFB, 0x2E, 0x3A, 0xBC, 0xA3, 0xFF, 0x89, 0x7C, 0x56, 0x22, + 0xE4, + 0x09, 0xB8, 0x38, 0xA2, 0xB3, 0x25, 0xC5, 0xD1, 0x8F, 0x3E, 0xE4, 0x64, 0xE9, 0x00, 0xBA, 0x47, + 0x0D, + 0xCD, 0x7F, 0xA4, 0x81, 0xA6, 0x30, 0x81, 0xA3, 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA2, 0x81, 0x9B, + 0x04, + 0x81, 0x98, 0x36, 0xA2, 0x7E, 0x66, 0x4A, 0xEC, 0x68, 0x0C, 0x71, 0x00, 0xD7, 0x0F, 0x5B, 0x71, + 0x6F, + 0x67, 0x15, 0xD1, 0x71, 0x0C, 0xAA, 0x5D, 0xAD, 0x08, 0x55, 0x19, 0x54, 0x43, 0x23, 0x37, 0x02, + 0x30, + 0x24, 0xF3, 0xBD, 0x46, 0xE8, 0xA9, 0xBE, 0x4A, 0x8D, 0x8D, 0x4D, 0x24, 0xA2, 0x6C, 0x7E, 0x34, + 0x50, + 0x88, 0x16, 0x01, 0x74, 0x43, 0x4A, 0x7F, 0x32, 0xBE, 0x83, 0x90, 0xE5, 0x57, 0xC9, 0xEB, 0xC3, + 0xCD, + 0x6E, 0x39, 0xC3, 0xFC, 0x34, 0xE4, 0x02, 0x2D, 0x1E, 0x57, 0x92, 0x83, 0x07, 0x0B, 0x40, 0xEB, + 0xFF, + 0xCD, 0x33, 0x89, 0x0C, 0xDB, 0x36, 0xA8, 0x71, 0x3B, 0xAE, 0xB9, 0xD8, 0xDA, 0x5F, 0xB7, 0x42, + 0xD0, + 0xC0, 0x9B, 0xDE, 0x9F, 0x00, 0xC8, 0xD7, 0x76, 0x31, 0xA7, 0x03, 0x9D, 0x31, 0x7C, 0xE4, 0x59, + 0x93, + 0x7F, 0x5E, 0xB1, 0x2A, 0x39, 0x6A, 0x7A, 0x18, 0x28, 0x17, 0x4C, 0x1B, 0xC2, 0xC8, 0xB5, 0x8B, + 0x95, + 0x69, 0xDB, 0x69, 0x49, 0x19, 0x74, 0x46, 0x1B, 0x28, 0x70, 0x4F, 0x8D, 0xDF, 0x2D, 0xFF, 0x5C, + 0x79, + 0x62, + ]) + ) + ) self.assertOutboundString("\(responseBytes1)\r\n") // server challenge 2 - let challengeBytes2 = "YGgGCSqGSIb3EgECAgIAb1kwV6ADAgEFoQMCAQ+iSzBJoAMCAQGiQgRAtHTEuOP2BXb9sBYFR4SJlDZxmg39IxmRBOhXRKdDA0uHTCOT9Bq3OsUTXUlk0CsFLoa8j+gvGDlgHuqzWHPSQg==" + let challengeBytes2 = + "YGgGCSqGSIb3EgECAgIAb1kwV6ADAgEFoQMCAQ+iSzBJoAMCAQGiQgRAtHTEuOP2BXb9sBYFR4SJlDZxmg39IxmRBOhXRKdDA0uHTCOT9Bq3OsUTXUlk0CsFLoa8j+gvGDlgHuqzWHPSQg==" self.writeInbound("+ \(challengeBytes2)\r\n") - self.assertInbound(.authenticationChallenge(ByteBuffer(bytes: [ - 0x60, 0x68, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x02, 0x02, 0x00, 0x6F, 0x59, - 0x30, 0x57, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x03, 0x02, 0x01, 0x0F, 0xA2, 0x4B, 0x30, 0x49, 0xA0, - 0x03, 0x02, 0x01, 0x01, 0xA2, 0x42, 0x04, 0x40, 0xB4, 0x74, 0xC4, 0xB8, 0xE3, 0xF6, 0x05, 0x76, 0xFD, - 0xB0, 0x16, 0x05, 0x47, 0x84, 0x89, 0x94, 0x36, 0x71, 0x9A, 0x0D, 0xFD, 0x23, 0x19, 0x91, 0x04, 0xE8, - 0x57, 0x44, 0xA7, 0x43, 0x03, 0x4B, 0x87, 0x4C, 0x23, 0x93, 0xF4, 0x1A, 0xB7, 0x3A, 0xC5, 0x13, 0x5D, - 0x49, 0x64, 0xD0, 0x2B, 0x05, 0x2E, 0x86, 0xBC, 0x8F, 0xE8, 0x2F, 0x18, 0x39, 0x60, 0x1E, 0xEA, 0xB3, - 0x58, 0x73, 0xD2, 0x42, - ]))) + self.assertInbound( + .authenticationChallenge( + ByteBuffer(bytes: [ + 0x60, 0x68, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x02, 0x02, 0x00, 0x6F, + 0x59, + 0x30, 0x57, 0xA0, 0x03, 0x02, 0x01, 0x05, 0xA1, 0x03, 0x02, 0x01, 0x0F, 0xA2, 0x4B, 0x30, 0x49, + 0xA0, + 0x03, 0x02, 0x01, 0x01, 0xA2, 0x42, 0x04, 0x40, 0xB4, 0x74, 0xC4, 0xB8, 0xE3, 0xF6, 0x05, 0x76, + 0xFD, + 0xB0, 0x16, 0x05, 0x47, 0x84, 0x89, 0x94, 0x36, 0x71, 0x9A, 0x0D, 0xFD, 0x23, 0x19, 0x91, 0x04, + 0xE8, + 0x57, 0x44, 0xA7, 0x43, 0x03, 0x4B, 0x87, 0x4C, 0x23, 0x93, 0xF4, 0x1A, 0xB7, 0x3A, 0xC5, 0x13, + 0x5D, + 0x49, 0x64, 0xD0, 0x2B, 0x05, 0x2E, 0x86, 0xBC, 0x8F, 0xE8, 0x2F, 0x18, 0x39, 0x60, 0x1E, 0xEA, + 0xB3, + 0x58, 0x73, 0xD2, 0x42, + ]) + ) + ) // client responds self.writeOutbound(.continuationResponse("")) @@ -256,7 +415,10 @@ class IMAPClientHandlerTests: XCTestCase { self.channel = EmbeddedChannel(handler: self.clientHandler, loop: .init()) self.channel.pipeline.fireChannelActive() - let f1 = self.writeOutbound(.tagged(.init(tag: "A1", command: .login(username: "\\", password: "\\"))), wait: false) + let f1 = self.writeOutbound( + .tagged(.init(tag: "A1", command: .login(username: "\\", password: "\\"))), + wait: false + ) self.assertOutboundString("A1 LOGIN {1}\r\n") self.writeInbound("+ OK\r\n") self.assertOutboundString("\\ {1}\r\n") @@ -279,7 +441,10 @@ class IMAPClientHandlerTests: XCTestCase { XCTAssertNoThrow(try f2.wait()) // now we should have literal+ turned on - let f3 = self.writeOutbound(.tagged(.init(tag: "A3", command: .login(username: "\\", password: "\\"))), wait: false) + let f3 = self.writeOutbound( + .tagged(.init(tag: "A3", command: .login(username: "\\", password: "\\"))), + wait: false + ) self.assertOutboundString("A3 LOGIN {1+}\r\n\\ {1+}\r\n\\\r\n") XCTAssertNoThrow(try f3.wait()) } @@ -298,7 +463,11 @@ class IMAPClientHandlerTests: XCTestCase { var expectation2: EventLoopPromise var expectation3: EventLoopPromise - init(expectation1: EventLoopPromise, expectation2: EventLoopPromise, expectation3: EventLoopPromise) { + init( + expectation1: EventLoopPromise, + expectation2: EventLoopPromise, + expectation3: EventLoopPromise + ) { self.expectation1 = expectation1 self.expectation2 = expectation2 self.expectation3 = expectation3 @@ -319,11 +488,13 @@ class IMAPClientHandlerTests: XCTestCase { } } - try! self.channel.pipeline.addHandler(UserEventHandler( - expectation1: eventExpectation1, - expectation2: eventExpectation2, - expectation3: eventExpectation3 - )).wait() + try! self.channel.pipeline.addHandler( + UserEventHandler( + expectation1: eventExpectation1, + expectation2: eventExpectation2, + expectation3: eventExpectation3 + ) + ).wait() // confirm it works for literals self.writeOutbound(.tagged(.init(tag: "A1", command: .login(username: "\\", password: "\\"))), wait: false) @@ -351,7 +522,10 @@ class IMAPClientHandlerTests: XCTestCase { func testPromisesAreFailedOnChannelClose() { // p1 will be the active promise - let p1 = self.writeOutbound(.tagged(.init(tag: "A1", command: .login(username: "\\", password: "\\"))), wait: false) + let p1 = self.writeOutbound( + .tagged(.init(tag: "A1", command: .login(username: "\\", password: "\\"))), + wait: false + ) // p2 is a promise loaded from the queue let p2 = self.writeOutbound(.tagged(.init(tag: "A2", command: .noop)), wait: false) @@ -386,7 +560,8 @@ class IMAPClientHandlerTests: XCTestCase { } } - func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { + func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) + { XCTAssert(event is MyOutboundEvent) let data = self.wrapInboundOut(ByteBuffer(string: "A1 OK NOOP complete\r\n")) context.fireChannelRead(data) @@ -414,62 +589,64 @@ class IMAPClientHandlerTests: XCTestCase { } } - XCTAssertNoThrow(try self.channel.pipeline.addHandlers([ - PreTestHandler(), - IMAPClientHandler(), - PostTestHandler(), - ]).wait()) + XCTAssertNoThrow( + try self.channel.pipeline.addHandlers([ + PreTestHandler(), + IMAPClientHandler(), + PostTestHandler(), + ]).wait() + ) self.writeOutbound(.tagged(.init(tag: "A1", command: .authenticate(mechanism: .gssAPI, initialResponse: nil)))) } -// func testProtectAgainstReentrancyWithContinuation() { -// struct MyOutboundEvent {} -// -// class PreTestHandler: ChannelDuplexHandler { -// typealias InboundIn = ByteBuffer -// typealias InboundOut = ByteBuffer -// typealias OutboundIn = ByteBuffer -// -// func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { -// let data = self.wrapInboundOut(ByteBuffer(string: "+ \r\n")) -// context.fireChannelRead(data) -// promise?.succeed(()) -// } -// -// func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { -// XCTAssert(event is MyOutboundEvent) -// let data = self.wrapInboundOut(ByteBuffer(string: "A1 OK NOOP complete\r\n")) -// context.fireChannelRead(data) -// promise?.succeed(()) -// } -// } -// -// class PostTestHandler: ChannelDuplexHandler { -// typealias InboundIn = Response -// typealias OutboundIn = Response -// -// var callCount = 0 -// -// func channelRead(context: ChannelHandlerContext, data: NIOAny) { -// self.callCount += 1 -// if self.callCount < 3 { -// context.triggerUserOutboundEvent(MyOutboundEvent(), promise: nil) -// } -// } -// -// func errorCaught(context: ChannelHandlerContext, error: Error) { -// XCTFail("Unexpected error \(error)") -// } -// } -// -// XCTAssertNoThrow(try self.channel.pipeline.addHandlers([ -// PreTestHandler(), -// IMAPClientHandler(), -// PostTestHandler(), -// ]).wait()) -// self.writeOutbound(.command(.init(tag: "A1", command: .create(.init("\\"), []))), wait: false) -// self.writeOutbound(.command(.init(tag: "A2", command: .noop)), wait: true) -// } + // func testProtectAgainstReentrancyWithContinuation() { + // struct MyOutboundEvent {} + // + // class PreTestHandler: ChannelDuplexHandler { + // typealias InboundIn = ByteBuffer + // typealias InboundOut = ByteBuffer + // typealias OutboundIn = ByteBuffer + // + // func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { + // let data = self.wrapInboundOut(ByteBuffer(string: "+ \r\n")) + // context.fireChannelRead(data) + // promise?.succeed(()) + // } + // + // func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { + // XCTAssert(event is MyOutboundEvent) + // let data = self.wrapInboundOut(ByteBuffer(string: "A1 OK NOOP complete\r\n")) + // context.fireChannelRead(data) + // promise?.succeed(()) + // } + // } + // + // class PostTestHandler: ChannelDuplexHandler { + // typealias InboundIn = Response + // typealias OutboundIn = Response + // + // var callCount = 0 + // + // func channelRead(context: ChannelHandlerContext, data: NIOAny) { + // self.callCount += 1 + // if self.callCount < 3 { + // context.triggerUserOutboundEvent(MyOutboundEvent(), promise: nil) + // } + // } + // + // func errorCaught(context: ChannelHandlerContext, error: Error) { + // XCTFail("Unexpected error \(error)") + // } + // } + // + // XCTAssertNoThrow(try self.channel.pipeline.addHandlers([ + // PreTestHandler(), + // IMAPClientHandler(), + // PostTestHandler(), + // ]).wait()) + // self.writeOutbound(.command(.init(tag: "A1", command: .create(.init("\\"), []))), wait: false) + // self.writeOutbound(.command(.init(tag: "A2", command: .noop)), wait: true) + // } func testWriteCascadesPromiseFailure() { struct TestError: Error {} @@ -485,7 +662,8 @@ class IMAPClientHandlerTests: XCTestCase { // writing a command that has a continuation var didComplete = NIOLoopBound(false, eventLoop: self.channel.eventLoop) - self.writeOutbound(.tagged(.init(tag: "A1", command: .create(.init("\\"), []))), wait: false).whenFailure { error in + self.writeOutbound(.tagged(.init(tag: "A1", command: .create(.init("\\"), []))), wait: false).whenFailure { + error in XCTAssertTrue(error is TestError) didComplete.value = true } @@ -511,7 +689,11 @@ class IMAPClientHandlerTests: XCTestCase { try! self.channel.pipeline.addHandler(testHandler, position: .first).wait() // writing a command that has a continuation - let future = self.channel.writeAndFlush(CommandStreamPart.tagged(.init(tag: "A1", command: .rename(from: .init("\\"), to: .init("\\"), parameters: [:])))) + let future = self.channel.writeAndFlush( + CommandStreamPart.tagged( + .init(tag: "A1", command: .rename(from: .init("\\"), to: .init("\\"), parameters: [:])) + ) + ) self.assertOutboundString("A1 RENAME {1}\r\n") testHandler.failNextWrite = true @@ -529,7 +711,10 @@ class IMAPClientHandlerTests: XCTestCase { self.writeOutbound(.append(.start(tag: "A1", appendingTo: .inbox))) self.assertOutboundString("A1 APPEND \"INBOX\"") - let literalPromise = self.writeOutbound(.append(.beginMessage(message: .init(options: .none, data: .init(byteCount: 5)))), wait: false) + let literalPromise = self.writeOutbound( + .append(.beginMessage(message: .init(options: .none, data: .init(byteCount: 5)))), + wait: false + ) self.assertOutboundString(" {5}\r\n") XCTAssertNoThrow(try literalPromise.wait()) @@ -572,7 +757,10 @@ class IMAPClientHandlerTests: XCTestCase { // We need to get into a state where we are // expecting a continuation, but actually // receive a response. - let promise = self.writeOutbound(.tagged(.init(tag: "A1", command: .login(username: "\\", password: "test"))), wait: false) + let promise = self.writeOutbound( + .tagged(.init(tag: "A1", command: .login(username: "\\", password: "test"))), + wait: false + ) self.assertOutboundBuffer("A1 LOGIN {1}\r\n") XCTAssertThrowsError(try self.channel.writeInbound(ByteBuffer("A1 OK\r\n"))) XCTAssertThrowsError(try promise.wait()) @@ -636,7 +824,11 @@ extension IMAPClientHandlerTests { } @discardableResult - private func writeOutbound(_ command: CommandStreamPart, wait: Bool = true, line: UInt = #line) -> EventLoopFuture { + private func writeOutbound( + _ command: CommandStreamPart, + wait: Bool = true, + line: UInt = #line + ) -> EventLoopFuture { let result = self.channel.writeAndFlush(command) if wait { XCTAssertNoThrow(try result.wait(), line: line) diff --git a/Tests/NIOIMAPTests/CommandDecoder+Tests.swift b/Tests/NIOIMAPTests/CommandDecoder+Tests.swift index 249a5cfd8..fab0f488f 100644 --- a/Tests/NIOIMAPTests/CommandDecoder+Tests.swift +++ b/Tests/NIOIMAPTests/CommandDecoder+Tests.swift @@ -31,7 +31,13 @@ extension CommandDecoder_Tests { let output: [(CommandStreamPart, UInt)] = [ (.append(.start(tag: "tag", appendingTo: .init("box"))), #line), - (.append(.beginMessage(message: .init(options: .init(flagList: [.seen], extensions: [:]), data: .init(byteCount: 1)))), #line), + ( + .append( + .beginMessage( + message: .init(options: .init(flagList: [.seen], extensions: [:]), data: .init(byteCount: 1)) + ) + ), #line + ), (.append(.messageBytes("a")), #line), (.append(.endMessage), #line), (.append(.finish), #line), @@ -41,7 +47,8 @@ extension CommandDecoder_Tests { XCTAssertNoThrow( XCTAssertEqual( try channel.readInbound(as: SynchronizedCommand.self), - SynchronizedCommand(expected), line: line + SynchronizedCommand(expected), + line: line ), line: line ) diff --git a/Tests/NIOIMAPTests/CommandEncoder+Tests.swift b/Tests/NIOIMAPTests/CommandEncoder+Tests.swift index 5891a9b12..dee022594 100644 --- a/Tests/NIOIMAPTests/CommandEncoder+Tests.swift +++ b/Tests/NIOIMAPTests/CommandEncoder+Tests.swift @@ -36,62 +36,160 @@ extension CommandEncoder_Tests { // SEARCH // We only want to include "CHARSET" if there’s a string in the search key / query. - (.tagged(.init(tag: "A1", command: .search(key: .all, charset: "UTF-8"))), - #"A1 SEARCH ALL\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .draft, charset: "UTF-8"))), - #"A1 SEARCH DRAFT\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .uid(.set([2 ... 80])), charset: "UTF-8"))), - #"A1 SEARCH UID 2:80\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .sequenceNumbers(.set([2 ... 80])), charset: "UTF-8"))), - #"A1 SEARCH 2:80\#r\#n"#, #line), + ( + .tagged(.init(tag: "A1", command: .search(key: .all, charset: "UTF-8"))), + #"A1 SEARCH ALL\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .draft, charset: "UTF-8"))), + #"A1 SEARCH DRAFT\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .uid(.set([2...80])), charset: "UTF-8"))), + #"A1 SEARCH UID 2:80\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .sequenceNumbers(.set([2...80])), charset: "UTF-8"))), + #"A1 SEARCH 2:80\#r\#n"#, #line + ), - (.tagged(.init(tag: "A1", command: .search(key: .and([.draft, .to("foo")]), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 DRAFT TO "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .or(.draft, .to("foo")), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 OR DRAFT TO "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .not(.to("foo")), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 NOT TO "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .bcc("foo"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 BCC "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .body("foo"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 BODY "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .cc("foo"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 CC "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .from("foo"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 FROM "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .subject("foo"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 SUBJECT "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .text("foo"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 TEXT "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .to("foo"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 TO "foo"\#r\#n"#, #line), - (.tagged(.init(tag: "A1", command: .search(key: .header("foo", "bar"), charset: "UTF-8"))), - #"A1 SEARCH CHARSET UTF-8 HEADER "foo" "bar"\#r\#n"#, #line), + ( + .tagged(.init(tag: "A1", command: .search(key: .and([.draft, .to("foo")]), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 DRAFT TO "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .or(.draft, .to("foo")), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 OR DRAFT TO "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .not(.to("foo")), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 NOT TO "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .bcc("foo"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 BCC "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .body("foo"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 BODY "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .cc("foo"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 CC "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .from("foo"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 FROM "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .subject("foo"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 SUBJECT "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .text("foo"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 TEXT "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .to("foo"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 TO "foo"\#r\#n"#, #line + ), + ( + .tagged(.init(tag: "A1", command: .search(key: .header("foo", "bar"), charset: "UTF-8"))), + #"A1 SEARCH CHARSET UTF-8 HEADER "foo" "bar"\#r\#n"#, #line + ), ] for (command, expected, line) in inputs { var buffer = ByteBuffer() let encoder = CommandEncoder(loggingMode: false) encoder.encode(data: command, out: &buffer) - XCTAssertEqual(expected, buffer, "\(String(buffer: expected)) is not equal to \(String(buffer: buffer))", line: line) + XCTAssertEqual( + expected, + buffer, + "\(String(buffer: expected)) is not equal to \(String(buffer: buffer))", + line: line + ) } } func testEncodingLoggingMode() { let inputs: [(CommandStreamPart, ByteBuffer, UInt)] = [ // LOGIN / AUTHENTICATE - (.tagged(.init(tag: "3", command: .login(username: "username", password: "\\pass"))), "3 LOGIN \"∅\" {5+}\r\n∅\r\n", #line), - (.tagged(.init(tag: "B23", command: .authenticate(mechanism: AuthenticationMechanism.plain, initialResponse: .init(ByteBuffer(string: "foobar"))))), "B23 AUTHENTICATE PLAIN ∅\r\n", #line), + ( + .tagged(.init(tag: "3", command: .login(username: "username", password: "\\pass"))), + "3 LOGIN \"∅\" {5+}\r\n∅\r\n", #line + ), + ( + .tagged( + .init( + tag: "B23", + command: .authenticate( + mechanism: AuthenticationMechanism.plain, + initialResponse: .init(ByteBuffer(string: "foobar")) + ) + ) + ), "B23 AUTHENTICATE PLAIN ∅\r\n", #line + ), (.tagged(.init(tag: "1", command: .noop)), "1 NOOP\r\n", #line), (.idleDone, "DONE\r\n", #line), - (.tagged(.init(tag: "4", command: .rename(from: .inbox, to: .init("test"), parameters: [:]))), "4 RENAME \"∅\" \"∅\"\r\n", #line), - (.tagged(.init(tag: "AB", command: .store(.set([42]), [.unchangedSince(.init(modificationSequence: .init(361_656)))], .flags(.add(silent: false, list: [.answered, .draft]))))), #"AB STORE 42 (UNCHANGEDSINCE 361656) +FLAGS (\Answered \Draft)\#r\#n"#, #line), - (.tagged(.init(tag: "AB", command: .store(.set([42]), [.unchangedSince(.init(modificationSequence: .init(361_656)))], .gmailLabels(.remove(silent: false, gmailLabels: [GmailLabel(ByteBuffer(string: "foobar"))]))))), #"AB STORE 42 (UNCHANGEDSINCE 361656) -X-GM-LABELS ("∅")\#r\#n"#, #line), + ( + .tagged(.init(tag: "4", command: .rename(from: .inbox, to: .init("test"), parameters: [:]))), + "4 RENAME \"∅\" \"∅\"\r\n", #line + ), + ( + .tagged( + .init( + tag: "AB", + command: .store( + .set([42]), + [.unchangedSince(.init(modificationSequence: .init(361_656)))], + .flags(.add(silent: false, list: [.answered, .draft])) + ) + ) + ), #"AB STORE 42 (UNCHANGEDSINCE 361656) +FLAGS (\Answered \Draft)\#r\#n"#, #line + ), + ( + .tagged( + .init( + tag: "AB", + command: .store( + .set([42]), + [.unchangedSince(.init(modificationSequence: .init(361_656)))], + .gmailLabels( + .remove(silent: false, gmailLabels: [GmailLabel(ByteBuffer(string: "foobar"))]) + ) + ) + ) + ), #"AB STORE 42 (UNCHANGEDSINCE 361656) -X-GM-LABELS ("∅")\#r\#n"#, #line + ), // APPEND (.append(.start(tag: "2", appendingTo: .inbox)), #"2 APPEND "∅""#, #line), - (.append(.beginMessage(message: AppendMessage(options: AppendOptions(flagList: [.answered], internalDate: ServerMessageDate(.init(year: 2022, month: 1, day: 14, hour: 13, minute: 54, second: 22, timeZoneMinutes: -120)!), extensions: [:]), data: AppendData(byteCount: 30_531)))), #" (\Answered) "14-Jan-2022 13:54:22 -0200" {30531+}\#r\#n"#, #line), + ( + .append( + .beginMessage( + message: AppendMessage( + options: AppendOptions( + flagList: [.answered], + internalDate: ServerMessageDate( + .init( + year: 2022, + month: 1, + day: 14, + hour: 13, + minute: 54, + second: 22, + timeZoneMinutes: -120 + )! + ), + extensions: [:] + ), + data: AppendData(byteCount: 30_531) + ) + ) + ), #" (\Answered) "14-Jan-2022 13:54:22 -0200" {30531+}\#r\#n"#, #line + ), (.append(.messageBytes(ByteBuffer(string: "foobar"))), "", #line), (.append(.endMessage), "∅", #line), (.append(.finish), "\r\n", #line), @@ -102,7 +200,12 @@ extension CommandEncoder_Tests { let encoder = CommandEncoder(loggingMode: true) encoder.capabilities.append(.literalPlus) encoder.encode(data: command, out: &buffer) - XCTAssertEqual(expected, buffer, "'\(String(buffer: expected))' is not equal to '\(String(buffer: buffer))'", line: line) + XCTAssertEqual( + expected, + buffer, + "'\(String(buffer: expected))' is not equal to '\(String(buffer: buffer))'", + line: line + ) } } } diff --git a/Tests/NIOIMAPTests/FramingParserTests.swift b/Tests/NIOIMAPTests/FramingParserTests.swift index ebadc6544..cfb389766 100644 --- a/Tests/NIOIMAPTests/FramingParserTests.swift +++ b/Tests/NIOIMAPTests/FramingParserTests.swift @@ -60,7 +60,10 @@ extension FramingParserTests { func testCommandWithQuotedContinuationLikeAndEscapedQuote_2() { var buffer: ByteBuffer = #"A1 LOGIN "a\"a\\a{2}bb" "bar"\#r\#n"# - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete(#"A1 LOGIN "a\"a\\a{2}bb" "bar"\#r\#n"#)]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete(#"A1 LOGIN "a\"a\\a{2}bb" "bar"\#r\#n"#)] + ) } // Shows that we don't need a CR to complete a frame @@ -99,7 +102,10 @@ extension FramingParserTests { func testSimpleCommandTimes2() { var buffer: ByteBuffer = "A1 NOOP\r\nA2 NOOP\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 NOOP\r\n"), .complete("A2 NOOP\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 NOOP\r\n"), .complete("A2 NOOP\r\n")] + ) } // Note that we don't jump the gun when we see a \r, we wait until @@ -138,17 +144,26 @@ extension FramingParserTests { // Remember that the framing parser is just there to look for frames. func testParsingLiteral() { var buffer: ByteBuffer = "A1 LOGIN {3}\r\nhey\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 LOGIN {3}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 LOGIN {3}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingLiteralNoLF() { var buffer: ByteBuffer = "A1 LOGIN {3}\rhey\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 LOGIN {3}\r"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 LOGIN {3}\r"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingLiteralNoCR() { var buffer: ByteBuffer = "A1 LOGIN {3}\nhey\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 LOGIN {3}\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 LOGIN {3}\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingLiteralNoCRLF() { @@ -159,37 +174,58 @@ extension FramingParserTests { func testParsingQuotedFollowedByLiteral() { var buffer: ByteBuffer = #"A1 LOGIN "foobar" {3}\#r\#nhey\#r\#n"# - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete(#"A1 LOGIN "foobar" {3}\#r\#n"#), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete(#"A1 LOGIN "foobar" {3}\#r\#n"#), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingQuotedWithEscapedQuoteFollowedByLiteral() { var buffer: ByteBuffer = #"A1 LOGIN "foo\"bar" {3}\#r\#nhey\#r\#n"# - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete(#"A1 LOGIN "foo\"bar" {3}\#r\#n"#), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete(#"A1 LOGIN "foo\"bar" {3}\#r\#n"#), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingBinaryLiteral() { var buffer: ByteBuffer = "A1 LOGIN {~3}\r\nhey\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 LOGIN {~3}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 LOGIN {~3}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingLiteralPlus() { var buffer: ByteBuffer = "A1 LOGIN {3+}\r\nhey\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 LOGIN {3+}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 LOGIN {3+}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingLiteralIntegerOverflow() { var buffer: ByteBuffer = "A1 LOGIN {99999999999999999999999999999999999999999999999999999999999999" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.invalid("A1 LOGIN {999999999999999999999"), .incomplete(2)]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.invalid("A1 LOGIN {999999999999999999999"), .incomplete(2)] + ) } func testParsingLiteralMinus() { var buffer: ByteBuffer = "A1 LOGIN {3-}\r\nhey\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 LOGIN {3-}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 LOGIN {3-}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } func testParsingBinaryLiteralPlus() { var buffer: ByteBuffer = "A1 LOGIN {~3+}\r\nhey\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.complete("A1 LOGIN {~3+}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.complete("A1 LOGIN {~3+}\r\n"), .insideLiteral("hey", remainingBytes: 0), .complete("\r\n")] + ) } // full command "A1 LOGIN {3}\r\n123 test\r\n @@ -264,6 +300,9 @@ extension FramingParserTests { func testShowWeCanSkipPastInvalidFrames() { var buffer: ByteBuffer = "A1 LOGIN {a\r\nA1 NOOP\r\n" - XCTAssertEqual(try self.parser.appendAndFrameBuffer(&buffer), [.invalid("A1 LOGIN {a"), .complete("\r\n"), .complete("A1 NOOP\r\n")]) + XCTAssertEqual( + try self.parser.appendAndFrameBuffer(&buffer), + [.invalid("A1 LOGIN {a"), .complete("\r\n"), .complete("A1 NOOP\r\n")] + ) } } diff --git a/Tests/NIOIMAPTests/IntegrationTests.swift b/Tests/NIOIMAPTests/IntegrationTests.swift index cb077704f..05673427e 100644 --- a/Tests/NIOIMAPTests/IntegrationTests.swift +++ b/Tests/NIOIMAPTests/IntegrationTests.swift @@ -58,26 +58,30 @@ final class ParserIntegrationTests: XCTestCase { let collectionDonePromise = group.next().makePromise(of: [CommandStreamPart].self) var server: Channel? - XCTAssertNoThrow(server = try ServerBootstrap(group: group) - .serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) - .childChannelInitializer { channel in - channel.pipeline.addHandlers( - ByteToMessageHandler(FrameDecoder()), - IMAPServerHandler(), - CollectEverythingHandler(collectionDonePromise: collectionDonePromise) - ) - } - .bind(to: .init(ipAddress: "127.0.0.1", port: 0)) - .wait()) + XCTAssertNoThrow( + server = try ServerBootstrap(group: group) + .serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) + .childChannelInitializer { channel in + channel.pipeline.addHandlers( + ByteToMessageHandler(FrameDecoder()), + IMAPServerHandler(), + CollectEverythingHandler(collectionDonePromise: collectionDonePromise) + ) + } + .bind(to: .init(ipAddress: "127.0.0.1", port: 0)) + .wait() + ) XCTAssertNotNil(server) defer { XCTAssertNoThrow(try server?.close().wait()) } var maybeClient: Channel? - XCTAssertNoThrow(maybeClient = try ClientBootstrap(group: group) - .connect(to: server?.localAddress ?? SocketAddress(unixDomainSocketPath: "should fail")) - .wait()) + XCTAssertNoThrow( + maybeClient = try ClientBootstrap(group: group) + .connect(to: server?.localAddress ?? SocketAddress(unixDomainSocketPath: "should fail")) + .wait() + ) guard let client = maybeClient else { XCTFail("couldn't connect client") return diff --git a/Tests/NIOIMAPTests/ParserStressTests.swift b/Tests/NIOIMAPTests/ParserStressTests.swift index d2a22418e..a8d554408 100644 --- a/Tests/NIOIMAPTests/ParserStressTests.swift +++ b/Tests/NIOIMAPTests/ParserStressTests.swift @@ -59,7 +59,7 @@ final class ParserStressTests: XCTestCase { func testArbitraryNumberOfFlags() { var longBuffer = self.channel.allocator.buffer(capacity: 90_000) longBuffer.writeString("STORE 1, ") - for i in 2 ..< 20_000 { + for i in 2..<20_000 { longBuffer.writeString("\(i), ") } @@ -72,11 +72,11 @@ final class ParserStressTests: XCTestCase { func testPreventInfiniteRecursion() { var longBuffer = self.channel.allocator.buffer(capacity: 80_000) longBuffer.writeString("tag SEARCH (") - for _ in 0 ..< 3_000 { + for _ in 0..<3_000 { longBuffer.writeString(#"ALL ANSWERED BCC CC ("#) } - for _ in 0 ..< 3_000 { - longBuffer.writeString(")") // close the recursive brackets + for _ in 0..<3_000 { + longBuffer.writeString(")") // close the recursive brackets } longBuffer.writeString(")\r\n") @@ -100,15 +100,17 @@ final class ParserStressTests: XCTestCase { func testManyShortCommands() { var longBuffer = self.channel.allocator.buffer(capacity: 80_000) - for _ in 1 ... 1_000 { + for _ in 1...1_000 { longBuffer.writeString("1 NOOP\r\n") } XCTAssertNoThrow(try self.channel.writeInbound(longBuffer)) - for _ in 1 ... 1_000 { - XCTAssertNoThrow(XCTAssertEqual( - CommandStreamPart.tagged(.init(tag: "1", command: .noop)), - try self.channel.readInbound(as: CommandStreamPart.self) - )) + for _ in 1...1_000 { + XCTAssertNoThrow( + XCTAssertEqual( + CommandStreamPart.tagged(.init(tag: "1", command: .noop)), + try self.channel.readInbound(as: CommandStreamPart.self) + ) + ) } } } diff --git a/Tests/NIOIMAPTests/RealWorldTests.swift b/Tests/NIOIMAPTests/RealWorldTests.swift index 8d74c00a6..08c13747e 100644 --- a/Tests/NIOIMAPTests/RealWorldTests.swift +++ b/Tests/NIOIMAPTests/RealWorldTests.swift @@ -26,13 +26,13 @@ final class RealWorldTests: XCTestCase {} extension RealWorldTests { func test_realWorldTest() { let input = """ - * 1 FETCH (UID 54 RFC822.SIZE 40639) - * 2 FETCH (UID 55 RFC822.SIZE 27984) - * 3 FETCH (UID 56 RFC822.SIZE 34007) - 15.16 OK Fetch completed (0.001 + 0.000 secs). - tag OK [REFERRAL imap://hostname/foo/bar/;UID=1234] + * 1 FETCH (UID 54 RFC822.SIZE 40639) + * 2 FETCH (UID 55 RFC822.SIZE 27984) + * 3 FETCH (UID 56 RFC822.SIZE 34007) + 15.16 OK Fetch completed (0.001 + 0.000 secs). + tag OK [REFERRAL imap://hostname/foo/bar/;UID=1234] - """ + """ let inoutPairs: [(String, [ResponseOrContinuationRequest])] = [ ( @@ -50,24 +50,50 @@ extension RealWorldTests { .response(.fetch(.simpleAttribute(.uid(56)))), .response(.fetch(.simpleAttribute(.rfc822Size(34007)))), .response(.fetch(.finish)), - .response(.tagged(.init(tag: "15.16", state: .ok(.init(code: nil, text: "Fetch completed (0.001 + 0.000 secs)."))))), - .response(.tagged( - TaggedResponse(tag: "tag", - state: .ok(ResponseText(code: - .referral(IMAPURL(server: IMAPServer(userAuthenticationMechanism: nil, host: "hostname", port: nil), - query: URLCommand.fetch( - path: MessagePath( - mailboxReference: MailboxUIDValidity(encodeMailbox: EncodedMailbox(mailbox: "foo/bar"), - uidValidity: nil), - iUID: IUID(uid: 1234), - section: nil, - range: nil - ), - authenticatedURL: nil - ))), - text: ""))))), + .response( + .tagged( + .init( + tag: "15.16", + state: .ok(.init(code: nil, text: "Fetch completed (0.001 + 0.000 secs).")) + ) + ) + ), + .response( + .tagged( + TaggedResponse( + tag: "tag", + state: .ok( + ResponseText( + code: + .referral( + IMAPURL( + server: IMAPServer( + userAuthenticationMechanism: nil, + host: "hostname", + port: nil + ), + query: URLCommand.fetch( + path: MessagePath( + mailboxReference: MailboxUIDValidity( + encodeMailbox: EncodedMailbox(mailbox: "foo/bar"), + uidValidity: nil + ), + iUID: IUID(uid: 1234), + section: nil, + range: nil + ), + authenticatedURL: nil + ) + ) + ), + text: "" + ) + ) + ) + ) + ), ] - ), + ) ] do { diff --git a/Tests/NIOIMAPTests/TestUtilities.swift b/Tests/NIOIMAPTests/TestUtilities.swift index d178c40ae..3d851c56f 100644 --- a/Tests/NIOIMAPTests/TestUtilities.swift +++ b/Tests/NIOIMAPTests/TestUtilities.swift @@ -40,11 +40,14 @@ extension TestUtilities { return buffer } - static func withBuffer(_ string: String, - terminator: String = "", - shouldRemainUnchanged: Bool = false, - file: StaticString = (#filePath), line: UInt = #line, _ body: (inout ByteBuffer) throws -> Void) - { + static func withBuffer( + _ string: String, + terminator: String = "", + shouldRemainUnchanged: Bool = false, + file: StaticString = (#filePath), + line: UInt = #line, + _ body: (inout ByteBuffer) throws -> Void + ) { var inputBuffer = ByteBufferAllocator().buffer(capacity: string.utf8.count + terminator.utf8.count + 10) inputBuffer.writeString("hello") inputBuffer.moveReaderIndex(forwardBy: 5) @@ -53,15 +56,21 @@ extension TestUtilities { inputBuffer.writeString("hallo") inputBuffer.moveWriterIndex(to: inputBuffer.writerIndex - 5) - let expected = inputBuffer.getSlice(at: inputBuffer.readerIndex + string.utf8.count, length: terminator.utf8.count)! + let expected = inputBuffer.getSlice( + at: inputBuffer.readerIndex + string.utf8.count, + length: terminator.utf8.count + )! let beforeRunningBody = inputBuffer var parseBuffer = ParseBuffer(inputBuffer) defer { - let remaining = (try? PL.parseBytes(buffer: &parseBuffer, - tracker: .makeNewDefaultLimitStackTracker, - upTo: .max)) ?? ByteBuffer() + let remaining = + (try? PL.parseBytes( + buffer: &parseBuffer, + tracker: .makeNewDefault, + upTo: .max + )) ?? ByteBuffer() let expectedString = String(buffer: expected) let remainingString = String(buffer: remaining) if shouldRemainUnchanged { diff --git a/dev/process_grammar.sh b/dev/process_grammar.sh index 07bf26efa..b70a1fb37 100755 --- a/dev/process_grammar.sh +++ b/dev/process_grammar.sh @@ -29,7 +29,7 @@ function functionalise() { continue fi if $uppercase_next; then - echo -n "${string:$i:1}" | tr a-z A-Z + echo -n "${string:$i:1}" | tr '[:upper:]' '[:lower:]' uppercase_next=false else echo -n "${string:$i:1}" @@ -50,9 +50,12 @@ function output_last() { all_types+=( "$swift_id" ) returnType=$swift_id - returnType=$( echo $swift_id | sed "s/Addr/Address\./g" ) - returnType=$( echo $swift_id | sed "s/Env/Envelope\./g" ) - returnType=$( echo $swift_id | sed "s/Fld/Field\./g" ) + # shellcheck disable=SC2001 + returnType=$( echo "$swift_id" | sed "s/Addr/Address\./g" ) + # shellcheck disable=SC2001 + returnType=$( echo "$swift_id" | sed "s/Env/Envelope\./g" ) + # shellcheck disable=SC2001 + returnType=$( echo "$swift_id" | sed "s/Fld/Field\./g" ) echo "func parse$swift_id(buffer: inout ParseBuffer, tracker: StackTracker) throws -> NIOIMAO.$returnType {" echo " return try PL.composite(buffer: &buffer, tracker: tracker) { buffer, tracker -> $swift_id in" diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index c6103a350..000000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG swift_version=5.6 -ARG ubuntu_version=focal -ARG base_image=swift:$swift_version-$ubuntu_version -FROM $base_image -# needed to do again after FROM due to docker limitation -ARG swift_version -ARG ubuntu_version - -# set as UTF-8 -RUN apt-get update && apt-get install -y locales locales-all -ENV LC_ALL en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US.UTF-8 - -# dependencies -RUN apt-get update && apt-get install -y wget -RUN apt-get update && apt-get install -y lsof dnsutils netcat-openbsd net-tools curl jq # used by integration tests - -# ruby -RUN apt-get update && apt-get install -y ruby ruby-dev libsqlite3-dev build-essential - -# tools -RUN mkdir -p $HOME/.tools -RUN echo 'export PATH="$HOME/.tools:$PATH"' >> $HOME/.profile - -# swiftformat (until part of the toolchain) - -ARG swiftformat_version=0.49.17 -RUN git clone --branch $swiftformat_version --depth 1 https://github.com/nicklockwood/SwiftFormat $HOME/.tools/swift-format -RUN cd $HOME/.tools/swift-format && swift build -c release -RUN ln -s $HOME/.tools/swift-format/.build/release/swiftformat $HOME/.tools/swiftformat diff --git a/docker/docker-compose.2004.57.yaml b/docker/docker-compose.2004.57.yaml deleted file mode 100644 index c46ddd653..000000000 --- a/docker/docker-compose.2004.57.yaml +++ /dev/null @@ -1,22 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-nio-imap:20.04-5.7 - build: - args: - ubuntu_version: "focal" - swift_version: "5.7" - - test: - image: swift-nio-imap:20.04-5.7 - - shell: - image: swift-nio-imap:20.04-5.7 - - performance-test: - image: swift-nio-imap:20.04-5.7 - - soundness: - image: swift-nio-imap:20.04-5.7 diff --git a/docker/docker-compose.2204.58.yaml b/docker/docker-compose.2204.58.yaml deleted file mode 100644 index 1c686476e..000000000 --- a/docker/docker-compose.2204.58.yaml +++ /dev/null @@ -1,22 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-nio-imap:22.04-5.8 - build: - args: - ubuntu_version: "jammy" - swift_version: "5.8" - - test: - image: swift-nio-imap:22.04-5.8 - - shell: - image: swift-nio-imap:22.04-5.8 - - performance-test: - image: swift-nio-imap:22.04-5.8 - - soundness: - image: swift-nio-imap:22.04-5.8 diff --git a/docker/docker-compose.2204.59.yaml b/docker/docker-compose.2204.59.yaml deleted file mode 100644 index 05cccf7b0..000000000 --- a/docker/docker-compose.2204.59.yaml +++ /dev/null @@ -1,21 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-nio-imap:22.04-5.9 - build: - args: - base_image: "swiftlang/swift:nightly-5.9-jammy" - - test: - image: swift-nio-imap:22.04-5.9 - - shell: - image: swift-nio-imap:22.04-5.9 - - performance-test: - image: swift-nio-imap:22.04-5.9 - - soundness: - image: swift-nio-imap:22.04-5.9 diff --git a/docker/docker-compose.2204.main.yaml b/docker/docker-compose.2204.main.yaml deleted file mode 100644 index 4ffc51360..000000000 --- a/docker/docker-compose.2204.main.yaml +++ /dev/null @@ -1,21 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-nio-imap:22.04-main - build: - args: - base_image: "swiftlang/swift:nightly-main-jammy" - - test: - image: swift-nio-imap:22.04-main - - shell: - image: swift-nio-imap:22.04-main - - performance-test: - image: swift-nio-imap:22.04-main - - soundness: - image: swift-nio-imap:22.04-main diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml deleted file mode 100644 index 19fd9e06e..000000000 --- a/docker/docker-compose.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# this file is not designed to be run directly -# instead, use the docker-compose.. files -# eg docker-compose -f docker/docker-compose.yaml -f docker/docker-compose.1604.41.yaml run test -version: "3" - -services: - - runtime-setup: - image: swift-nio-imap:default - build: - context: . - dockerfile: Dockerfile - - common: &common - image: swift-nio-imap:default - depends_on: [runtime-setup] - volumes: - - ~/.ssh:/root/.ssh - - ..:/code:z - working_dir: /code - cap_drop: - - CAP_NET_RAW - - CAP_NET_BIND_SERVICE - - soundness: - <<: *common - command: /bin/bash -xcl "./scripts/soundness.sh" - - test: - <<: *common - command: /bin/bash -xcl "swift test -Xswiftc -warnings-as-errors --enable-test-discovery $${SANITIZER_ARG-}" - - performance-test: - <<: *common - command: /bin/bash -xcl "swift build -c release -Xswiftc -Xllvm -Xswiftc -align-all-functions=5 -Xswiftc -Xllvm -Xswiftc -align-all-blocks=5 && ./.build/release/NIOIMAPPerformanceTester" - - # util - - shell: - <<: *common - entrypoint: /bin/bash diff --git a/scripts/analyze_performance_results.rb b/scripts/analyze_performance_results.rb deleted file mode 100755 index 4e89f8523..000000000 --- a/scripts/analyze_performance_results.rb +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env ruby -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## -require 'optparse' - -METRIC="min" # used for comparison - -module Enumerable - def sum - return self.inject(0){|accum, i| accum + i } - end - - def mean - return self.sum / self.length.to_f - end - - def sample_variance - m = self.mean - sum = self.inject(0){|accum, i| accum + (i - m) ** 2 } - return sum / (self.length - 1).to_f - end - - def standard_deviation - return Math.sqrt(self.sample_variance) - end -end - -def parse_results(file) - results = {} - File.open(file, "r") do |f| - f.each_line do |line| - parts = line.split(':').collect(&:strip) - throw "invalid data format" unless parts.length == 3 - key = parts[1] - values = parts[2].split(',').collect(&:strip).map(&:to_f) - results[key] = {} - results[key]["values"] = values - results[key]["max"] = values.max - results[key]["min"] = values.min - results[key]["mean"] = values.mean - results[key]["std"] = values.standard_deviation - end - end - results -end - -def compare_results(current, previous) - results = {} - current.keys.each do |key| - results[key] = {} - results[key]["previous"] = previous[key] || { ::METRIC => "n/a" } - results[key]["current"] = current[key] - if previous[key] - current_value = current[key][::METRIC] - previous_value = previous[key][::METRIC] - delta = current_value - previous_value - results[key]["delta"] = delta - results[key]["winner"] = current_value <= previous_value ? "current" : "previous" - results[key]["diff"] = (delta / previous_value * 100).to_i - else - results[key]["winner"] = "n/a" - results[key]["diff"] = "n/a" - end - end - results -end - -def print_results_markdown(results) - columns = ["min", "max", "mean", "std"] - puts "| name | #{columns.join(" | ")} |" - puts "|#{Array.new(columns.size+1, '--').join("|")}|" - results.keys.each do |key| - print "| #{key}" - columns.each do |column| - print " | #{results[key][column]}" - end - puts " |\n" - end -end - -def print_results_html(results) - columns = ["min", "max", "mean", "std"] - puts "" - puts "" - results.keys.each do |key| - puts "" - puts "" - columns.each do |column| - puts "" - end - puts "" - end - puts "
name#{columns.join("")}
#{key}#{results[key][column]}
" -end - -def print_results_csv(results) - puts results.keys.join(",") - puts results.keys.map{ |key| results[key][::METRIC] }.join(",") -end - -def print_comparison_markdown(results) - puts "| name | current | previous | winner | diff |" - puts "|#{Array.new(5, '--').join("|")}|" - results.keys.each do |key| - puts "| #{key} | #{results[key]["current"][::METRIC]} | #{results[key]["previous"][::METRIC]} | #{results[key]["winner"]} | #{results[key]["diff"]}% |" - end -end - -def print_comparison_html(results) - puts "" - puts " - - - - - - " - results.keys.each do |key| - puts " - - - - - - " - end - puts "
namecurrentpreviouswinnerdiff
#{key}#{results[key]["current"][::METRIC]}#{results[key]["previous"][::METRIC]}#{results[key]["winner"]}#{results[key]["diff"]}%
" -end - - -ARGV << '-h' if ARGV.empty? - -options = {} -OptionParser.new do |opt| - opt.on('-f', '--file file', 'file to process') { |o| options[:file] = o } - opt.on('-p', '--previous previous', 'previous file to process') { |o| options[:previous] = o } - opt.on('-o', '--output output', 'output format') { |o| options[:output] = o } - opt.on_tail("-h", "--help", "show this message") do - puts opt - end -end.parse! - -if options.has_key?(:file) && options.has_key?(:previous) - current = parse_results(options[:file]) - previous = parse_results(options[:previous]) - results = compare_results(current, previous) - - case options[:output] - when "html" - print_comparison_html(results) - when "markdown", nil - print_comparison_markdown(results) - else - throw "invalid output format #{options[:output]}" - end - -elsif options.has_key?(:file) - results = parse_results(options[:file]) - case options[:output] - when "csv" - print_results_csv(results) - when "html", nil - print_results_html(results) - when "markdown", nil - print_results_markdown(results) - else - throw "invalid output format #{options[:output]}" - end - -else - throw "invalid arguemnts" -end diff --git a/scripts/check_no_api_breakages.sh b/scripts/check_no_api_breakages.sh deleted file mode 100755 index a16cb90bc..000000000 --- a/scripts/check_no_api_breakages.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -set -eu - -# repodir -function all_modules() { - local repodir="$1" - ( - set -eu - cd "$repodir" - swift package dump-package | jq '.products | - map(select(.type | has("library") )) | - map(.name) | .[]' | tr -d '"' - ) -} - -# repodir tag output -function build_and_do() { - local repodir=$1 - local tag=$2 - local output=$3 - - ( - cd "$repodir" - git checkout -q "$tag" - swift build - while read -r module; do - swift api-digester -sdk "$sdk" -dump-sdk -module "$module" \ - -o "$output/$module.json" -I "$repodir/.build/debug" - done < <(all_modules "$repodir") - ) -} - -function usage() { - echo >&2 "Usage: $0 REPO-GITHUB-URL NEW-VERSION OLD-VERSIONS..." - echo >&2 - echo >&2 "This script requires a Swift 5.1+ toolchain." - echo >&2 - echo >&2 "Examples:" - echo >&2 - echo >&2 "Check between master and tag 0.0.0 of swift-nio:" - echo >&2 " $0 https://github.com/apple/swift-nio-imap master 0.0.0" - echo >&2 - echo >&2 "Check between HEAD and commit 64cf63d7 using the provided toolchain:" - echo >&2 " xcrun --toolchain org.swift.5120190702a $0 ../some-local-repo HEAD 64cf63d7" -} - -if [[ $# -lt 3 ]]; then - usage - exit 1 -fi - -sdk=/ -if [[ "$(uname -s)" == Darwin ]]; then - sdk=$(xcrun --show-sdk-path) -fi - -hash jq 2> /dev/null || { echo >&2 "ERROR: jq must be installed"; exit 1; } -tmpdir=$(mktemp -d /tmp/.check-api_XXXXXX) -repo_url=$1 -new_tag=$2 -shift 2 - -repodir="$tmpdir/repo" -git clone "$repo_url" "$repodir" -git -C "$repodir" fetch -q origin '+refs/pull/*:refs/remotes/origin/pr/*' -errors=0 - -for old_tag in "$@"; do - mkdir "$tmpdir/api-old" - mkdir "$tmpdir/api-new" - - echo "Checking public API breakages from $old_tag to $new_tag" - - build_and_do "$repodir" "$new_tag" "$tmpdir/api-new/" - build_and_do "$repodir" "$old_tag" "$tmpdir/api-old/" - - for f in "$tmpdir/api-new"/*; do - f=$(basename "$f") - report="$tmpdir/$f.report" - if [[ ! -f "$tmpdir/api-old/$f" ]]; then - echo "NOTICE: NEW MODULE $f" - continue - fi - - echo -n "Checking $f... " - swift api-digester -sdk "$sdk" -diagnose-sdk \ - --input-paths "$tmpdir/api-old/$f" -input-paths "$tmpdir/api-new/$f" 2>&1 \ - > "$report" 2>&1 - - if ! shasum "$report" | grep -q cefc4ee5bb7bcdb7cb5a7747efa178dab3c794d5; then - echo ERROR - echo >&2 "==============================" - echo >&2 "ERROR: public API change in $f" - echo >&2 "==============================" - cat >&2 "$report" - errors=$(( errors + 1 )) - else - echo OK - fi - done - rm -rf "$tmpdir/api-new" "$tmpdir/api-old" -done - -if [[ "$errors" == 0 ]]; then - echo "OK, all seems good" -fi -echo done -exit "$errors" diff --git a/scripts/generate_contributors_list.sh b/scripts/generate_contributors_list.sh deleted file mode 100755 index 8ef4f110f..000000000 --- a/scripts/generate_contributors_list.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -set -eu -here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -contributors=$( cd "$here"/.. && git shortlog -es | cut -f2 | sed 's/^/- /' ) - -cat > "$here/../CONTRIBUTORS.txt" <<- EOF - For the purpose of tracking copyright, this is the list of individuals and - organizations who have contributed source code to swift-nio-email. - - For employees of an organization/company where the copyright of work done - by employees of that company is held by the company itself, only the company - needs to be listed here. - - ## COPYRIGHT HOLDERS - - - Apple Inc. (all contributors with '@apple.com') - - ### Contributors - - $contributors - - **Updating this list** - - Please do not edit this file manually. It is generated using \`./scripts/generate_contributors_list.sh\`. If a name is misspelled or appearing multiple times: add an entry in \`./.mailmap\` -EOF diff --git a/scripts/generate_docs.sh b/scripts/generate_docs.sh deleted file mode 100755 index 85b9355ac..000000000 --- a/scripts/generate_docs.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -set -ex - -my_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -root_path="$my_path/.." -version=$(git describe --abbrev=0 --tags || echo "0.0.0") -modules=( NIOIMAP ) - - -if [[ "$(uname -s)" == "Linux" ]]; then - # build code if required - if [[ ! -d "$root_path/.build/x86_64-unknown-linux" ]]; then - swift build - fi - # setup source-kitten if required - source_kitten_source_path="$root_path/.SourceKitten" - if [[ ! -d "$source_kitten_source_path" ]]; then - git clone https://github.com/jpsim/SourceKitten.git "$source_kitten_source_path" - fi - source_kitten_path="$source_kitten_source_path/.build/x86_64-unknown-linux/debug" - if [[ ! -d "$source_kitten_path" ]]; then - rm -rf "$source_kitten_source_path/.swift-version" - cd "$source_kitten_source_path" && swift build && cd "$root_path" - fi - # generate - mkdir -p "$root_path/.build/sourcekitten" - for module in "${modules[@]}"; do - if [[ ! -f "$root_path/.build/sourcekitten/$module.json" ]]; then - "$source_kitten_path/sourcekitten" doc --spm-module $module > "$root_path/.build/sourcekitten/$module.json" - fi - done -fi - -[[ -d docs/$version ]] || mkdir -p docs/$version -[[ -d swift-nio-imap.xcodeproj ]] || swift package generate-xcodeproj - -# run jazzy -if ! command -v jazzy > /dev/null; then - gem install jazzy --no-ri --no-rdoc -fi - -jazzy_dir="$root_path/.build/jazzy" -rm -rf "$jazzy_dir" -mkdir -p "$jazzy_dir" - -module_switcher="$jazzy_dir/README.md" -jazzy_args=(--clean - --author 'SwiftNIO Team' - --readme "$module_switcher" - --author_url https://github.com/apple/swift-nio-imap - --github_url https://github.com/apple/swift-nio-imap - --theme fullwidth - --github-file-prefix https://github.com/apple/swift-nio-imap/tree/$version - --xcodebuild-arguments -scheme,swift-nio-imap-Package) -cat > "$module_switcher" <<"EOF" -# SwiftNIO Docs -SwiftNIO contains multiple modules: -EOF - -for module in "${modules[@]}"; do - echo " - [$module](../$module/index.html)" >> "$module_switcher" -done - -cat >> "$module_switcher" <<"EOF" ---- -For the API documentation of the other repositories in the SwiftNIO family check: -- [`swift-nio` API docs](https://apple.github.io/swift-nio/docs/current/NIO/index.html) -- [`swift-nio-ssl` API docs](https://apple.github.io/swift-nio-ssl/docs/current/NIOSSL/index.html) -EOF - -for module in "${modules[@]}"; do - args=("${jazzy_args[@]}" --output "$jazzy_dir/docs/$version/$module" --docset-path "$jazzy_dir/docset/$version/$module" - --module "$module" --module-version $version - --root-url "https://apple.github.io/swift-nio-imap/docs/$version/$module/") - if [[ -f "$root_path/.build/sourcekitten/$module.json" ]]; then - args+=(--sourcekitten-sourcefile "$root_path/.build/sourcekitten/$module.json") - fi - jazzy "${args[@]}" -done - -# push to github pages -if [[ $PUSH == true ]]; then - BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) - GIT_AUTHOR=$(git --no-pager show -s --format='%an <%ae>' HEAD) - git fetch origin +gh-pages:gh-pages - git checkout gh-pages - rm -rf "docs/$version" - rm -rf "docs/current" - cp -r "$jazzy_dir/docs/$version" docs/ - cp -r "docs/$version" docs/current - git add --all docs - echo '' > index.html - git add index.html - touch .nojekyll - git add .nojekyll - changes=$(git diff-index --name-only HEAD) - if [[ -n "$changes" ]]; then - echo -e "changes detected\n$changes" - git commit --author="$GIT_AUTHOR" -m "publish $version docs" - git push origin gh-pages - else - echo "no changes detected" - fi - git checkout -f $BRANCH_NAME -fi diff --git a/scripts/soundness.sh b/scripts/soundness.sh deleted file mode 100755 index 2d6ded01e..000000000 --- a/scripts/soundness.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2022 Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## - -set -eu -here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -function replace_acceptable_years() { - # this needs to replace all acceptable forms with 'YEARS' - sed -e 's/20[12][7890123]-20[12][890123]/YEARS/' -e 's/20[12][890123]/YEARS/' -} - -printf "=> Checking for unacceptable language... " -# This greps for unacceptable terminology. The square bracket[s] are so that -# "git grep" doesn't find the lines that greps :). -unacceptable_terms=( - -e blacklis[t] - -e whitelis[t] - -e slav[e] - -e sanit[y] -) -if git grep --color=never -i "${unacceptable_terms[@]}" > /dev/null; then - printf "\033[0;31mUnacceptable language found.\033[0m\n" - git grep -i "${unacceptable_terms[@]}" - exit 1 -fi -printf "\033[0;32mokay.\033[0m\n" - -printf "=> Checking format... " -FIRST_OUT="$(git status --porcelain)" -swiftformat . > /dev/null 2>&1 -SECOND_OUT="$(git status --porcelain)" -if [[ "$FIRST_OUT" != "$SECOND_OUT" ]]; then - printf "\033[0;31mformatting issues!\033[0m\n" - git --no-pager diff - exit 1 -else - printf "\033[0;32mokay.\033[0m\n" -fi - -tmp=$(mktemp /tmp/.swift-nio-imap-soundness_XXXXXX) - -printf "=> Checking license headers\n" -for language in swift-or-c bash; do - printf " * $language... " - declare -a matching_files - declare -a exceptions - expections=( ) - matching_files=( -name '*' ) - case "$language" in - swift-or-c) - exceptions=( -name Package.swift -o -name Base64.swift -o -name DecodingError.swift -o -name Chromium.swift ) - matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' ) - cat > "$tmp" <<"EOF" -//===----------------------------------------------------------------------===// -// -// This source file is part of the SwiftNIO open source project -// -// Copyright (c) YEARS Apple Inc. and the SwiftNIO project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of SwiftNIO project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -EOF - ;; - bash) - matching_files=( -name '*.sh' ) - cat > "$tmp" <<"EOF" -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) YEARS Apple Inc. and the SwiftNIO project authors -## Licensed under Apache License v2.0 -## -## See LICENSE.txt for license information -## See CONTRIBUTORS.txt for the list of SwiftNIO project authors -## -## SPDX-License-Identifier: Apache-2.0 -## -##===----------------------------------------------------------------------===## -EOF - ;; - *) - echo >&2 "ERROR: unknown language '$language'" - ;; - esac - - expected_lines=$(cat "$tmp" | wc -l) - expected_sha=$(cat "$tmp" | shasum) - - ( - cd "$here/.." - find . \ - \( \! -path './.build/*' -a \ - \( "${matching_files[@]}" \) -a \ - \( \! \( "${exceptions[@]}" \) \) \) | while read line; do - if [[ "$(cat "$line" | replace_acceptable_years | head -n $expected_lines | shasum)" != "$expected_sha" ]]; then - printf "\033[0;31mmissing headers in file '$line'!\033[0m\n" - diff -u <(cat "$line" | replace_acceptable_years | head -n $expected_lines) "$tmp" - exit 1 - fi - done - printf "\033[0;32mokay.\033[0m\n" - ) -done - -rm "$tmp"