├── .editorconfig ├── .gitattributes ├── .github ├── actions │ └── dotnet │ │ └── action.yml ├── dependabot.yml ├── release.yml └── workflows │ ├── build.yml │ ├── changelog.config │ ├── changelog.yml │ ├── combine-prs.yml │ ├── dotnet-env.yml │ ├── dotnet-file.yml │ ├── includes.yml │ ├── publish.yml │ └── triage.yml ├── .gitignore ├── .netconfig ├── Directory.Build.rsp ├── RxFree.sln ├── _config.yml ├── assets ├── css │ └── style.scss └── img │ ├── icon.png │ └── icon.svg ├── changelog.md ├── license.txt ├── profile └── readme.md ├── readme.md └── src ├── Directory.Build.props ├── Directory.Build.targets ├── RxFree.SourceTests.Legacy └── RxFree.SourceTests.Legacy.csproj ├── RxFree.SourceTests.Nullable └── RxFree.SourceTests.Nullable.csproj ├── RxFree.SourceTests └── RxFree.SourceTests.csproj ├── RxFree.Tests ├── DisposableTests.cs ├── LinqTests.cs ├── RxFree.Tests.csproj └── SubjectTests.cs ├── RxFree ├── RxFree.csproj ├── build │ ├── Legacy │ │ ├── AnonymousObserver.cs │ │ ├── CompositeDisposable.cs │ │ ├── Disposable.cs │ │ ├── ObservableExtensions.OfType.cs │ │ ├── ObservableExtensions.Select.cs │ │ ├── ObservableExtensions.Where.cs │ │ ├── ObservableExtensions.cs │ │ ├── StableCompositeDisposable.cs │ │ └── Subject.cs │ ├── NotNull │ │ ├── AnonymousObserver.cs │ │ ├── CompositeDisposable.cs │ │ ├── Disposable.cs │ │ ├── ObservableExtensions.OfType.cs │ │ ├── ObservableExtensions.Select.cs │ │ ├── ObservableExtensions.Where.cs │ │ ├── ObservableExtensions.cs │ │ ├── StableCompositeDisposable.cs │ │ └── Subject.cs │ ├── Nullable │ │ ├── AnonymousObserver.cs │ │ ├── CompositeDisposable.cs │ │ ├── Disposable.cs │ │ ├── ObservableExtensions.OfType.cs │ │ ├── ObservableExtensions.Select.cs │ │ ├── ObservableExtensions.Where.cs │ │ ├── ObservableExtensions.cs │ │ ├── StableCompositeDisposable.cs │ │ └── Subject.cs │ ├── RxFree.props │ └── RxFree.targets └── readme.md ├── icon.png ├── kzu.snk └── nuget.config /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome:http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Don't use tabs for indentation. 7 | [*] 8 | indent_style = space 9 | # (Please don't specify an indent_size here; that has too many unintended consequences.) 10 | 11 | # Code files 12 | [*.{cs,csx,vb,vbx}] 13 | indent_size = 4 14 | 15 | # Xml project files 16 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,msbuildproj,props,targets}] 17 | indent_size = 2 18 | 19 | # Xml config files 20 | [*.{ruleset,config,nuspec,resx,vsixmanifest,vsct}] 21 | indent_size = 2 22 | 23 | # YAML files 24 | [*.{yaml,yml}] 25 | indent_size = 2 26 | 27 | # JSON files 28 | [*.json] 29 | indent_size = 2 30 | 31 | # Dotnet code style settings: 32 | [*.{cs,vb}] 33 | # Sort using and Import directives with System.* appearing first 34 | dotnet_sort_system_directives_first = true 35 | # Avoid "this." and "Me." if not necessary 36 | dotnet_style_qualification_for_field = false:suggestion 37 | dotnet_style_qualification_for_property = false:suggestion 38 | dotnet_style_qualification_for_method = false:suggestion 39 | dotnet_style_qualification_for_event = false:suggestion 40 | 41 | # Use language keywords instead of framework type names for type references 42 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 43 | dotnet_style_predefined_type_for_member_access = true:suggestion 44 | 45 | # Suggest more modern language features when available 46 | dotnet_style_object_initializer = true:suggestion 47 | dotnet_style_collection_initializer = true:suggestion 48 | dotnet_style_coalesce_expression = true:suggestion 49 | dotnet_style_null_propagation = true:suggestion 50 | dotnet_style_explicit_tuple_names = true:suggestion 51 | 52 | # CSharp code style settings: 53 | 54 | # IDE0040: Add accessibility modifiers 55 | dotnet_style_require_accessibility_modifiers = omit_if_default:error 56 | 57 | # IDE0040: Add accessibility modifiers 58 | dotnet_diagnostic.IDE0040.severity = error 59 | 60 | [*.cs] 61 | # Top-level files are definitely OK 62 | csharp_using_directive_placement = outside_namespace:silent 63 | csharp_style_namespace_declarations = block_scoped:silent 64 | csharp_prefer_simple_using_statement = true:suggestion 65 | csharp_prefer_braces = true:silent 66 | 67 | # Prefer "var" everywhere 68 | csharp_style_var_for_built_in_types = true:suggestion 69 | csharp_style_var_when_type_is_apparent = true:suggestion 70 | csharp_style_var_elsewhere = true:suggestion 71 | 72 | # Prefer method-like constructs to have an expression-body 73 | csharp_style_expression_bodied_methods = true:none 74 | csharp_style_expression_bodied_constructors = true:none 75 | csharp_style_expression_bodied_operators = true:none 76 | 77 | # Prefer property-like constructs to have an expression-body 78 | csharp_style_expression_bodied_properties = true:none 79 | csharp_style_expression_bodied_indexers = true:none 80 | csharp_style_expression_bodied_accessors = true:none 81 | 82 | # Suggest more modern language features when available 83 | csharp_style_pattern_matching_over_is_with_cast_check = true:error 84 | csharp_style_pattern_matching_over_as_with_null_check = true:error 85 | csharp_style_inlined_variable_declaration = true:suggestion 86 | csharp_style_throw_expression = true:suggestion 87 | csharp_style_conditional_delegate_call = true:suggestion 88 | 89 | # Newline settings 90 | csharp_new_line_before_open_brace = all 91 | csharp_new_line_before_else = true 92 | csharp_new_line_before_catch = true 93 | csharp_new_line_before_finally = true 94 | csharp_new_line_before_members_in_object_initializers = true 95 | csharp_new_line_before_members_in_anonymous_types = true 96 | 97 | # Test settings 98 | [**/*Tests*/**{.cs,.vb}] 99 | # xUnit1013: Public method should be marked as test. Allows using records as test classes 100 | dotnet_diagnostic.xUnit1013.severity = none 101 | 102 | # CS9113: Parameter is unread (usually, ITestOutputHelper) 103 | dotnet_diagnostic.CS9113.severity = none 104 | 105 | # Default severity for analyzer diagnostics with category 'Style' 106 | dotnet_analyzer_diagnostic.category-Style.severity = none 107 | 108 | # VSTHRD200: Use "Async" suffix for async methods 109 | dotnet_diagnostic.VSTHRD200.severity = none 110 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # normalize by default 2 | * text=auto encoding=UTF-8 3 | *.sh text eol=lf 4 | *.sbn eol=lf 5 | 6 | # These are windows specific files which we may as well ensure are 7 | # always crlf on checkout 8 | *.bat text eol=crlf 9 | *.cmd text eol=crlf 10 | -------------------------------------------------------------------------------- /.github/actions/dotnet/action.yml: -------------------------------------------------------------------------------- 1 | name: ⚙ dotnet 2 | description: Configures dotnet if the repo/org defines the DOTNET custom property 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: 🔎 dotnet 8 | id: dotnet 9 | shell: bash 10 | run: | 11 | VERSIONS=$(gh api repos/${{ github.repository }}/properties/values | jq -r '.[] | select(.property_name == "DOTNET") | .value') 12 | # Remove extra whitespace from VERSIONS 13 | VERSIONS=$(echo "$VERSIONS" | tr -s ' ' | tr -d ' ') 14 | # Convert comma-separated to newline-separated 15 | NEWLINE_VERSIONS=$(echo "$VERSIONS" | tr ',' '\n') 16 | # Validate versions 17 | while IFS= read -r version; do 18 | if ! [[ $version =~ ^[0-9]+(\.[0-9]+(\.[0-9]+)?)?(\.x)?$ ]]; then 19 | echo "Error: Invalid version format: $version" 20 | exit 1 21 | fi 22 | done <<< "$NEWLINE_VERSIONS" 23 | # Write multiline output to $GITHUB_OUTPUT 24 | { 25 | echo 'versions<> $GITHUB_OUTPUT 29 | 30 | - name: ⚙ dotnet 31 | if: steps.dotnet.outputs.versions != '' 32 | uses: actions/setup-dotnet@v4 33 | with: 34 | dotnet-version: | 35 | ${{ steps.dotnet.outputs.versions }} 36 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: nuget 7 | directory: / 8 | schedule: 9 | interval: daily 10 | groups: 11 | Azure: 12 | patterns: 13 | - "Azure*" 14 | - "Microsoft.Azure*" 15 | Identity: 16 | patterns: 17 | - "System.IdentityModel*" 18 | - "Microsoft.IdentityModel*" 19 | System: 20 | patterns: 21 | - "System*" 22 | exclude-patterns: 23 | - "System.IdentityModel*" 24 | Extensions: 25 | patterns: 26 | - "Microsoft.Extensions*" 27 | exclude-patterns: 28 | - "Microsoft.Extensions.AI*" 29 | ExtensionsAI: 30 | patterns: 31 | - "Microsoft.Extensions.AI*" 32 | Web: 33 | patterns: 34 | - "Microsoft.AspNetCore*" 35 | Tests: 36 | patterns: 37 | - "Microsoft.NET.Test*" 38 | - "xunit*" 39 | - "coverlet*" 40 | ThisAssembly: 41 | patterns: 42 | - "ThisAssembly*" 43 | ProtoBuf: 44 | patterns: 45 | - "protobuf-*" 46 | Spectre: 47 | patterns: 48 | - "Spectre.Console*" 49 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - bydesign 5 | - dependencies 6 | - duplicate 7 | - question 8 | - invalid 9 | - wontfix 10 | - need info 11 | - techdebt 12 | authors: 13 | - devlooped-bot 14 | - dependabot 15 | - github-actions 16 | categories: 17 | - title: ✨ Implemented enhancements 18 | labels: 19 | - enhancement 20 | - title: 🐛 Fixed bugs 21 | labels: 22 | - bug 23 | - title: 📝 Documentation updates 24 | labels: 25 | - docs 26 | - documentation 27 | - title: 🔨 Other 28 | labels: 29 | - '*' 30 | exclude: 31 | labels: 32 | - dependencies 33 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Builds and runs tests in all three supported OSes 2 | # Pushes CI feed if secrets.SLEET_CONNECTION is provided 3 | 4 | name: build 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | configuration: 9 | type: choice 10 | description: Configuration 11 | options: 12 | - Release 13 | - Debug 14 | push: 15 | branches: [ main, dev, 'dev/*', 'feature/*', 'rel/*' ] 16 | paths-ignore: 17 | - changelog.md 18 | - readme.md 19 | pull_request: 20 | types: [opened, synchronize, reopened] 21 | 22 | env: 23 | DOTNET_NOLOGO: true 24 | PackOnBuild: true 25 | GeneratePackageOnBuild: true 26 | VersionPrefix: 42.42.${{ github.run_number }} 27 | VersionLabel: ${{ github.ref }} 28 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 29 | MSBUILDTERMINALLOGGER: auto 30 | Configuration: ${{ github.event.inputs.configuration || 'Release' }} 31 | SLEET_FEED_URL: ${{ vars.SLEET_FEED_URL }} 32 | 33 | defaults: 34 | run: 35 | shell: bash 36 | 37 | jobs: 38 | os-matrix: 39 | runs-on: ubuntu-latest 40 | outputs: 41 | matrix: ${{ steps.lookup.outputs.matrix }} 42 | steps: 43 | - name: 🤘 checkout 44 | uses: actions/checkout@v4 45 | 46 | - name: 🔎 lookup 47 | id: lookup 48 | shell: pwsh 49 | run: | 50 | $path = './.github/workflows/os-matrix.json' 51 | $os = if (test-path $path) { cat $path } else { '["ubuntu-latest"]' } 52 | echo "matrix=$os" >> $env:GITHUB_OUTPUT 53 | 54 | build: 55 | needs: os-matrix 56 | name: build-${{ matrix.os }} 57 | runs-on: ${{ matrix.os }} 58 | strategy: 59 | matrix: 60 | os: ${{ fromJSON(needs.os-matrix.outputs.matrix) }} 61 | steps: 62 | - name: 🤘 checkout 63 | uses: actions/checkout@v4 64 | with: 65 | submodules: recursive 66 | fetch-depth: 0 67 | 68 | - name: ⚙ dotnet 69 | uses: devlooped/actions-dotnet-env@v1 70 | 71 | - name: 🙏 build 72 | run: dotnet build -m:1 -bl:build.binlog 73 | 74 | - name: 🧪 test 75 | run: | 76 | dotnet tool update -g dotnet-retest 77 | dotnet retest -- --no-build 78 | 79 | - name: 🐛 logs 80 | uses: actions/upload-artifact@v4 81 | if: runner.debug && always() 82 | with: 83 | name: logs 84 | path: '*.binlog' 85 | 86 | - name: 🚀 sleet 87 | env: 88 | SLEET_CONNECTION: ${{ secrets.SLEET_CONNECTION }} 89 | if: env.SLEET_CONNECTION != '' 90 | run: | 91 | dotnet tool update sleet -g --allow-downgrade --version $(curl -s --compressed ${{ vars.SLEET_FEED_URL }} | jq '.["sleet:version"]' -r) 92 | sleet push bin --config none -f --verbose -p "SLEET_FEED_CONTAINER=nuget" -p "SLEET_FEED_CONNECTIONSTRING=${{ secrets.SLEET_CONNECTION }}" -p "SLEET_FEED_TYPE=azure" || echo "No packages found" 93 | 94 | dotnet-format: 95 | runs-on: ubuntu-latest 96 | steps: 97 | - name: 🤘 checkout 98 | uses: actions/checkout@v4 99 | with: 100 | submodules: recursive 101 | fetch-depth: 0 102 | 103 | - name: ⚙ dotnet 104 | uses: devlooped/actions-dotnet-env@v1 105 | 106 | - name: ✓ ensure format 107 | run: | 108 | dotnet format whitespace --verify-no-changes -v:diag --exclude ~/.nuget 109 | dotnet format style --verify-no-changes -v:diag --exclude ~/.nuget 110 | -------------------------------------------------------------------------------- /.github/workflows/changelog.config: -------------------------------------------------------------------------------- 1 | usernames-as-github-logins=true 2 | issues_wo_labels=true 3 | pr_wo_labels=true 4 | exclude-labels=bydesign,dependencies,duplicate,discussion,question,invalid,wontfix,need info,docs 5 | enhancement-label=:sparkles: Implemented enhancements: 6 | bugs-label=:bug: Fixed bugs: 7 | issues-label=:hammer: Other: 8 | pr-label=:twisted_rightwards_arrows: Merged: 9 | unreleased=false 10 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: changelog 2 | on: 3 | workflow_dispatch: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | changelog: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: 🤖 defaults 12 | uses: devlooped/actions-bot@v1 13 | with: 14 | name: ${{ secrets.BOT_NAME }} 15 | email: ${{ secrets.BOT_EMAIL }} 16 | gh_token: ${{ secrets.GH_TOKEN }} 17 | github_token: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | - name: 🤘 checkout 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | ref: main 24 | token: ${{ env.GH_TOKEN }} 25 | 26 | - name: ⚙ ruby 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: 3.0.3 30 | 31 | - name: ⚙ changelog 32 | run: | 33 | gem install github_changelog_generator 34 | github_changelog_generator --user ${GITHUB_REPOSITORY%/*} --project ${GITHUB_REPOSITORY##*/} --token $GH_TOKEN --o changelog.md --config-file .github/workflows/changelog.config 35 | 36 | - name: 🚀 changelog 37 | run: | 38 | git add changelog.md 39 | (git commit -m "🖉 Update changelog with ${GITHUB_REF#refs/*/}" && git push) || echo "Done" -------------------------------------------------------------------------------- /.github/workflows/combine-prs.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/hrvey/combine-prs-workflow 2 | # Tweaks: regex support for branch 3 | 4 | name: '⛙ combine-prs' 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | branchExpression: 10 | description: 'Regular expression to match against PR branches to find combinable PRs' 11 | required: true 12 | default: 'dependabot' 13 | mustBeGreen: 14 | description: 'Only combine PRs that are green (status is success)' 15 | required: true 16 | default: true 17 | combineTitle: 18 | description: 'Title of the combined PR' 19 | required: true 20 | default: '⬆️ Bump dependencies' 21 | combineBranchName: 22 | description: 'Name of the branch to combine PRs into' 23 | required: true 24 | default: 'combine-prs' 25 | ignoreLabel: 26 | description: 'Exclude PRs with this label' 27 | required: true 28 | default: 'nocombine' 29 | 30 | jobs: 31 | combine-prs: 32 | name: ${{ github.event.inputs.combineBranchName }} 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/github-script@v6 36 | with: 37 | github-token: ${{secrets.GITHUB_TOKEN}} 38 | script: | 39 | const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', { 40 | owner: context.repo.owner, 41 | repo: context.repo.repo 42 | }); 43 | const branchRegExp = new RegExp(`${{github.event.inputs.branchExpression}}`); 44 | let branchesAndPRStrings = []; 45 | let baseBranch = null; 46 | let baseBranchSHA = null; 47 | for (const pull of pulls) { 48 | const branch = pull['head']['ref']; 49 | console.log('Pull for branch: ' + branch); 50 | if (branchRegExp.test(branch)) { 51 | console.log('Branch matched: ' + branch); 52 | let statusOK = true; 53 | if(${{ github.event.inputs.mustBeGreen }}) { 54 | console.log('Checking green status: ' + branch); 55 | const stateQuery = `query($owner: String!, $repo: String!, $pull_number: Int!) { 56 | repository(owner: $owner, name: $repo) { 57 | pullRequest(number:$pull_number) { 58 | commits(last: 1) { 59 | nodes { 60 | commit { 61 | statusCheckRollup { 62 | state 63 | } 64 | } 65 | } 66 | } 67 | } 68 | } 69 | }` 70 | const vars = { 71 | owner: context.repo.owner, 72 | repo: context.repo.repo, 73 | pull_number: pull['number'] 74 | }; 75 | const result = await github.graphql(stateQuery, vars); 76 | const [{ commit }] = result.repository.pullRequest.commits.nodes; 77 | const state = commit.statusCheckRollup.state 78 | console.log('Validating status: ' + state); 79 | if(state != 'SUCCESS') { 80 | console.log('Discarding ' + branch + ' with status ' + state); 81 | statusOK = false; 82 | } 83 | } 84 | console.log('Checking labels: ' + branch); 85 | const labels = pull['labels']; 86 | for(const label of labels) { 87 | const labelName = label['name']; 88 | console.log('Checking label: ' + labelName); 89 | if(labelName == '${{ github.event.inputs.ignoreLabel }}') { 90 | console.log('Discarding ' + branch + ' with label ' + labelName); 91 | statusOK = false; 92 | } 93 | } 94 | if (statusOK) { 95 | console.log('Adding branch to array: ' + branch); 96 | const prString = '#' + pull['number'] + ' ' + pull['title']; 97 | branchesAndPRStrings.push({ branch, prString }); 98 | baseBranch = pull['base']['ref']; 99 | baseBranchSHA = pull['base']['sha']; 100 | } 101 | } 102 | } 103 | if (branchesAndPRStrings.length == 0) { 104 | core.setFailed('No PRs/branches matched criteria'); 105 | return; 106 | } 107 | if (branchesAndPRStrings.length == 1) { 108 | core.setFailed('Only one PR/branch matched criteria'); 109 | return; 110 | } 111 | 112 | try { 113 | await github.rest.git.createRef({ 114 | owner: context.repo.owner, 115 | repo: context.repo.repo, 116 | ref: 'refs/heads/' + '${{ github.event.inputs.combineBranchName }}', 117 | sha: baseBranchSHA 118 | }); 119 | } catch (error) { 120 | console.log(error); 121 | core.setFailed('Failed to create combined branch - maybe a branch by that name already exists?'); 122 | return; 123 | } 124 | 125 | let combinedPRs = []; 126 | let mergeFailedPRs = []; 127 | for(const { branch, prString } of branchesAndPRStrings) { 128 | try { 129 | await github.rest.repos.merge({ 130 | owner: context.repo.owner, 131 | repo: context.repo.repo, 132 | base: '${{ github.event.inputs.combineBranchName }}', 133 | head: branch, 134 | }); 135 | console.log('Merged branch ' + branch); 136 | combinedPRs.push(prString); 137 | } catch (error) { 138 | console.log('Failed to merge branch ' + branch); 139 | mergeFailedPRs.push(prString); 140 | } 141 | } 142 | 143 | console.log('Creating combined PR'); 144 | const combinedPRsString = combinedPRs.join('\n'); 145 | let body = '⛙ Combined PRs:\n' + combinedPRsString; 146 | if(mergeFailedPRs.length > 0) { 147 | const mergeFailedPRsString = mergeFailedPRs.join('\n'); 148 | body += '\n\n⚠️ The following PRs were left out due to merge conflicts:\n' + mergeFailedPRsString 149 | } 150 | await github.rest.pulls.create({ 151 | owner: context.repo.owner, 152 | repo: context.repo.repo, 153 | title: '⛙ ${{github.event.inputs.combineTitle}}', 154 | head: '${{ github.event.inputs.combineBranchName }}', 155 | base: baseBranch, 156 | body: body 157 | }); 158 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-env.yml: -------------------------------------------------------------------------------- 1 | name: dotnet-env 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '**/*.*proj' 9 | 10 | jobs: 11 | which-dotnet: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | pull-requests: write 16 | 17 | steps: 18 | - name: 🤖 defaults 19 | uses: devlooped/actions-bot@v1 20 | with: 21 | name: ${{ secrets.BOT_NAME }} 22 | email: ${{ secrets.BOT_EMAIL }} 23 | gh_token: ${{ secrets.GH_TOKEN }} 24 | github_token: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: 🤘 checkout 27 | uses: actions/checkout@v4 28 | with: 29 | token: ${{ env.GH_TOKEN }} 30 | 31 | - name: 🤌 dotnet 32 | uses: devlooped/actions-which-dotnet@v1 33 | 34 | - name: ✍ pull request 35 | uses: peter-evans/create-pull-request@v7 36 | with: 37 | base: main 38 | branch: which-dotnet 39 | delete-branch: true 40 | labels: dependencies 41 | title: "⚙ Update dotnet versions" 42 | body: "Update dotnet versions" 43 | commit-message: "Update dotnet versions" 44 | token: ${{ env.GH_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/dotnet-file.yml: -------------------------------------------------------------------------------- 1 | # Synchronizes .netconfig-configured files with dotnet-file 2 | name: dotnet-file 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | push: 8 | branches: [ 'dotnet-file' ] 9 | 10 | env: 11 | DOTNET_NOLOGO: true 12 | 13 | jobs: 14 | run: 15 | permissions: 16 | contents: write 17 | uses: devlooped/oss/.github/workflows/dotnet-file-core.yml@main 18 | secrets: 19 | BOT_NAME: ${{ secrets.BOT_NAME }} 20 | BOT_EMAIL: ${{ secrets.BOT_EMAIL }} 21 | GH_TOKEN: ${{ secrets.GH_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/includes.yml: -------------------------------------------------------------------------------- 1 | name: +Mᐁ includes 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - 'main' 7 | paths: 8 | - '**.md' 9 | - '!changelog.md' 10 | 11 | jobs: 12 | includes: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | pull-requests: write 17 | steps: 18 | - name: 🤖 defaults 19 | uses: devlooped/actions-bot@v1 20 | with: 21 | name: ${{ secrets.BOT_NAME }} 22 | email: ${{ secrets.BOT_EMAIL }} 23 | gh_token: ${{ secrets.GH_TOKEN }} 24 | github_token: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: 🤘 checkout 27 | uses: actions/checkout@v4 28 | with: 29 | token: ${{ env.GH_TOKEN }} 30 | 31 | - name: +Mᐁ includes 32 | uses: devlooped/actions-includes@v1 33 | 34 | - name: ✍ pull request 35 | uses: peter-evans/create-pull-request@v6 36 | with: 37 | add-paths: '**.md' 38 | base: main 39 | branch: markdown-includes 40 | delete-branch: true 41 | labels: docs 42 | author: ${{ env.BOT_AUTHOR }} 43 | committer: ${{ env.BOT_AUTHOR }} 44 | commit-message: +Mᐁ includes 45 | title: +Mᐁ includes 46 | body: +Mᐁ includes 47 | token: ${{ env.GH_TOKEN }} 48 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # Builds a final release version and pushes to nuget.org 2 | # whenever a release is published. 3 | # Requires: secrets.NUGET_API_KEY 4 | 5 | name: publish 6 | on: 7 | release: 8 | types: [prereleased, released] 9 | 10 | env: 11 | DOTNET_NOLOGO: true 12 | Configuration: Release 13 | PackOnBuild: true 14 | GeneratePackageOnBuild: true 15 | VersionLabel: ${{ github.ref }} 16 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 17 | MSBUILDTERMINALLOGGER: auto 18 | SLEET_FEED_URL: https://api.nuget.org/v3/index.json 19 | 20 | jobs: 21 | publish: 22 | runs-on: ${{ vars.PUBLISH_AGENT || 'ubuntu-latest' }} 23 | steps: 24 | - name: 🤘 checkout 25 | uses: actions/checkout@v4 26 | with: 27 | submodules: recursive 28 | fetch-depth: 0 29 | 30 | - name: ⚙ dotnet 31 | uses: devlooped/actions-dotnet-env@v1 32 | 33 | - name: 🙏 build 34 | run: dotnet build -m:1 -bl:build.binlog 35 | 36 | - name: 🧪 test 37 | run: | 38 | dotnet tool update -g dotnet-retest 39 | dotnet retest -- --no-build 40 | 41 | - name: 🐛 logs 42 | uses: actions/upload-artifact@v4 43 | if: runner.debug && always() 44 | with: 45 | name: logs 46 | path: '*.binlog' 47 | 48 | - name: 🚀 nuget 49 | env: 50 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 51 | if: ${{ env.NUGET_API_KEY != '' && github.event.action != 'prereleased' }} 52 | working-directory: bin 53 | run: dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}} --skip-duplicate 54 | 55 | - name: 🚀 sleet 56 | env: 57 | SLEET_CONNECTION: ${{ secrets.SLEET_CONNECTION }} 58 | if: env.SLEET_CONNECTION != '' 59 | run: | 60 | dotnet tool update sleet -g --allow-downgrade --version $(curl -s --compressed ${{ vars.SLEET_FEED_URL }} | jq '.["sleet:version"]' -r) 61 | sleet push bin --config none -f --verbose -p "SLEET_FEED_CONTAINER=nuget" -p "SLEET_FEED_CONNECTIONSTRING=${{ secrets.SLEET_CONNECTION }}" -p "SLEET_FEED_TYPE=azure" || echo "No packages found" 62 | -------------------------------------------------------------------------------- /.github/workflows/triage.yml: -------------------------------------------------------------------------------- 1 | name: 'triage' 2 | on: 3 | schedule: 4 | - cron: '42 0 * * *' 5 | 6 | workflow_dispatch: 7 | # Manual triggering through the GitHub UI, API, or CLI 8 | inputs: 9 | daysBeforeClose: 10 | description: "Days before closing stale or need info issues" 11 | required: true 12 | default: "30" 13 | daysBeforeStale: 14 | description: "Days before labeling stale" 15 | required: true 16 | default: "180" 17 | daysSinceClose: 18 | description: "Days since close to lock" 19 | required: true 20 | default: "30" 21 | daysSinceUpdate: 22 | description: "Days since update to lock" 23 | required: true 24 | default: "30" 25 | 26 | permissions: 27 | actions: write # For managing the operation state cache 28 | issues: write 29 | contents: read 30 | 31 | jobs: 32 | stale: 33 | # Do not run on forks 34 | if: github.repository_owner == 'devlooped' 35 | runs-on: ubuntu-latest 36 | steps: 37 | - name: ⌛ rate 38 | shell: pwsh 39 | if: github.event_name != 'workflow_dispatch' 40 | env: 41 | GH_TOKEN: ${{ secrets.DEVLOOPED_TOKEN }} 42 | run: | 43 | # add random sleep since we run on fixed schedule 44 | $wait = get-random -max 180 45 | echo "Waiting random $wait seconds to start" 46 | sleep $wait 47 | # get currently authenticated user rate limit info 48 | $rate = gh api rate_limit | convertfrom-json | select -expandproperty rate 49 | # if we don't have at least 100 requests left, wait until reset 50 | if ($rate.remaining -lt 100) { 51 | $wait = ($rate.reset - (Get-Date (Get-Date).ToUniversalTime() -UFormat %s)) 52 | echo "Rate limit remaining is $($rate.remaining), waiting for $($wait / 1000) seconds to reset" 53 | sleep $wait 54 | $rate = gh api rate_limit | convertfrom-json | select -expandproperty rate 55 | echo "Rate limit has reset to $($rate.remaining) requests" 56 | } 57 | 58 | - name: ✏️ stale labeler 59 | # pending merge: https://github.com/actions/stale/pull/1176 60 | uses: kzu/stale@c8450312ba97b204bf37545cb249742144d6ca69 61 | with: 62 | ascending: true # Process the oldest issues first 63 | stale-issue-label: 'stale' 64 | stale-issue-message: | 65 | Due to lack of recent activity, this issue has been labeled as 'stale'. 66 | It will be closed if no further activity occurs within ${{ fromJson(inputs.daysBeforeClose || 30 ) }} more days. 67 | Any new comment will remove the label. 68 | close-issue-message: | 69 | This issue will now be closed since it has been labeled 'stale' without activity for ${{ fromJson(inputs.daysBeforeClose || 30 ) }} days. 70 | days-before-stale: ${{ fromJson(inputs.daysBeforeStale || 180) }} 71 | days-before-close: ${{ fromJson(inputs.daysBeforeClose || 30 ) }} 72 | days-before-pr-close: -1 # Do not close PRs labeled as 'stale' 73 | exempt-all-milestones: true 74 | exempt-all-assignees: true 75 | exempt-issue-labels: priority,sponsor,backed 76 | exempt-authors: kzu 77 | 78 | - name: 🤘 checkout actions 79 | uses: actions/checkout@v4 80 | with: 81 | repository: 'microsoft/vscode-github-triage-actions' 82 | ref: v42 83 | 84 | - name: ⚙ install actions 85 | run: npm install --production 86 | 87 | - name: 🔒 issues locker 88 | uses: ./locker 89 | with: 90 | token: ${{ secrets.DEVLOOPED_TOKEN }} 91 | ignoredLabel: priority 92 | daysSinceClose: ${{ fromJson(inputs.daysSinceClose || 30) }} 93 | daysSinceUpdate: ${{ fromJson(inputs.daysSinceUpdate || 30) }} 94 | 95 | - name: 🔒 need info closer 96 | uses: ./needs-more-info-closer 97 | with: 98 | token: ${{ secrets.DEVLOOPED_TOKEN }} 99 | label: 'need info' 100 | closeDays: ${{ fromJson(inputs.daysBeforeClose || 30) }} 101 | closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity.\n\nHappy Coding!" 102 | pingDays: 80 103 | pingComment: "Hey @${assignee}, this issue might need further attention.\n\n@${author}, you can help us out by closing this issue if the problem no longer exists, or adding more information." -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | artifacts 4 | pack 5 | TestResults 6 | results 7 | BenchmarkDotNet.Artifacts 8 | /app 9 | .vs 10 | .vscode 11 | .genaiscript 12 | .idea 13 | local.settings.json 14 | 15 | *.suo 16 | *.sdf 17 | *.userprefs 18 | *.user 19 | *.nupkg 20 | *.metaproj 21 | *.tmp 22 | *.log 23 | *.cache 24 | *.binlog 25 | *.zip 26 | __azurite*.* 27 | __*__ 28 | 29 | .nuget 30 | *.lock.json 31 | *.nuget.props 32 | *.nuget.targets 33 | 34 | node_modules 35 | _site 36 | .jekyll-metadata 37 | .jekyll-cache 38 | .sass-cache 39 | Gemfile.lock 40 | package-lock.json 41 | -------------------------------------------------------------------------------- /.netconfig: -------------------------------------------------------------------------------- 1 | [file] 2 | url = https://github.com/devlooped/oss 3 | [file ".netconfig"] 4 | url = https://github.com/devlooped/oss/blob/main/.netconfig 5 | skip 6 | [file "readme.md"] 7 | url = https://github.com/devlooped/oss/blob/main/readme.md 8 | skip 9 | [file "src/icon.png"] 10 | url = https://github.com/devlooped/oss/blob/main/src/icon.png 11 | skip 12 | [file "SponsorLink.sln"] 13 | url = https://github.com/devlooped/oss/blob/main/SponsorLink.sln 14 | skip 15 | [file ".github/ISSUE_TEMPLATE/config.yml"] 16 | url = https://github.com/devlooped/oss/blob/main/.github/ISSUE_TEMPLATE/config.yml 17 | skip 18 | [file ".github/workflows/release-artifacts.yml"] 19 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/release-artifacts.yml 20 | skip 21 | [file "assets/images/sponsors.svg"] 22 | url = https://github.com/devlooped/oss/blob/main/assets/images/sponsors.svg 23 | skip 24 | [file "assets/images/sponsors.png"] 25 | url = https://github.com/devlooped/oss/blob/main/assets/images/sponsors.png 26 | skip 27 | [file ".editorconfig"] 28 | url = https://github.com/devlooped/oss/blob/main/.editorconfig 29 | sha = c779d3d4e468358106dea03e93ba2cd35bb01ecb 30 | etag = 7298c6450967975a8782b5c74f3071e1910fc59686e48f9c9d5cd7c68213cf59 31 | weak 32 | [file ".gitattributes"] 33 | url = https://github.com/devlooped/oss/blob/main/.gitattributes 34 | sha = 4a9aa321c4982b83c185cf8dffed181ff84667d5 35 | etag = 09cad18280ed04b67f7f87591e5481510df04d44c3403231b8af885664d8fd58 36 | weak 37 | [file ".github/dependabot.yml"] 38 | url = https://github.com/devlooped/oss/blob/main/.github/dependabot.yml 39 | sha = e733294084fb3e75d517a2e961e87df8faae7dc6 40 | etag = 3bf8d9214a15c049ca5cfe80d212a8cbe4753b8a638a9804ef73d34c7def9618 41 | weak 42 | [file ".github/workflows/build.yml"] 43 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/build.yml 44 | sha = 56c2b8532c2f86235a0f5bd00ba6eba126f199cf 45 | etag = bf99c19427f4372ecfe38ec56aa8c411058684fb717da5661f17ac00388b3602 46 | weak 47 | [file ".github/workflows/changelog.yml"] 48 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/changelog.yml 49 | sha = 5fb172362c767bef7c36478f1a6bdc264723f8f9 50 | etag = ad1efa56d6024ee1add2bcda81a7e4e38d0e9069473c6ff70374d5ce06af1f5a 51 | weak 52 | [file ".github/workflows/dotnet-file.yml"] 53 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-file.yml 54 | sha = 8fa147d4799d73819040736c399d0b1db2c2d86c 55 | etag = 1ca805a23656e99c03f9d478dba8ccef6e571f5de2ac0e9bb7e3c5216c99a694 56 | weak 57 | [file ".github/workflows/publish.yml"] 58 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/publish.yml 59 | sha = 56c2b8532c2f86235a0f5bd00ba6eba126f199cf 60 | etag = 2ef43521627aa3a91dd55bdc2856ec0c6a93b42485d4fe9d6b181f9ee42c8e18 61 | weak 62 | [file ".gitignore"] 63 | url = https://github.com/devlooped/oss/blob/main/.gitignore 64 | sha = e0be248fff1d39133345283b8227372b36574b75 65 | etag = c449ec6f76803e1891357ca2b8b4fcb5b2e5deeff8311622fd92ca9fbf1e6575 66 | weak 67 | [file "Directory.Build.rsp"] 68 | url = https://github.com/devlooped/oss/blob/main/Directory.Build.rsp 69 | sha = 0f7f7f7e8a29de9b535676f75fe7c67e629a5e8c 70 | etag = 0ccae83fc51f400bfd7058170bfec7aba11455e24a46a0d7e6a358da6486e255 71 | weak 72 | [file "_config.yml"] 73 | url = https://github.com/devlooped/oss/blob/main/_config.yml 74 | sha = 68b409c486842062e0de0e5b11e6fdb7cd12d6e2 75 | etag = d608aa0ddaedc2d8a87260f50756e8d8314964ad4671b76bd085bcb458757010 76 | weak 77 | [file "assets/css/style.scss"] 78 | url = https://github.com/devlooped/oss/blob/main/assets/css/style.scss 79 | sha = 9db26e2710b084d219d6355339d822f159bf5780 80 | etag = f710d8919abfd5a8d00050b74ba7d0bb05c6d02e40842a3012eb96555c208504 81 | weak 82 | [file "license.txt"] 83 | url = https://github.com/devlooped/oss/blob/main/license.txt 84 | sha = 0683ee777d7d878d4bf013d7deea352685135a05 85 | etag = 2c6335b37e4ae05eea7c01f5d0c9d82b49c488f868a8b5ba7bff7c6ff01f3994 86 | weak 87 | [file "src/Directory.Build.props"] 88 | url = https://github.com/devlooped/oss/blob/main/src/Directory.Build.props 89 | sha = 81d972fd0760c244d134dae7f4b17d6c43cb004a 90 | etag = 1368697c1521e465a1dea88b93787b1c7def441c37d62afc903fb8d07179e4f6 91 | weak 92 | [file "src/Directory.Build.targets"] 93 | url = https://github.com/devlooped/oss/blob/main/src/Directory.Build.targets 94 | sha = a8b208093599263b7f2d1fe3854634c588ea5199 95 | etag = 19087699f05396205e6b050d999a43b175bd242f6e8fac86f6df936310178b03 96 | weak 97 | [file "src/kzu.snk"] 98 | url = https://github.com/devlooped/oss/blob/main/src/kzu.snk 99 | sha = 0683ee777d7d878d4bf013d7deea352685135a05 100 | etag = b8d789b5b6bea017cdcc8badcea888ad78de3e34298efca922054e9fb0e7b6b9 101 | weak 102 | [file ".github/workflows/includes.yml"] 103 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/includes.yml 104 | sha = 85829f2510f335f4a411867f3dbaaa116c3ab3de 105 | etag = 086f6b6316cc6ea7089c0dcc6980be519e6ed6e6201e65042ef41b82634ec0ee 106 | weak 107 | [file ".github/workflows/combine-prs.yml"] 108 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/combine-prs.yml 109 | sha = c1610886eba42cb250e3894aed40c0a258cd383d 110 | etag = 598ee294649a44d4c5d5934416c01183597d08aa7db7938453fd2bbf52a4e64d 111 | weak 112 | [file ".github/release.yml"] 113 | url = https://github.com/devlooped/oss/blob/main/.github/release.yml 114 | sha = 0c23e24704625cf75b2cb1fdc566cef7e20af313 115 | etag = 310df162242c95ed19ed12e3c96a65f77e558b46dced676ad5255eb12caafe75 116 | weak 117 | [file ".github/workflows/changelog.config"] 118 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/changelog.config 119 | sha = 08d83cb510732f861416760d37702f9f55bd7f9e 120 | etag = 556a28914eeeae78ca924b1105726cdaa211af365671831887aec81f5f4301b4 121 | weak 122 | [file ".github/workflows/dotnet-file-core.yml"] 123 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-file-core.yml 124 | skip 125 | [file ".github/workflows/triage.yml"] 126 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/triage.yml 127 | sha = 33000c0c4ab4eb4e0e142fa54515b811a189d55c 128 | etag = 013a47739e348f06891f37c45164478cca149854e6cd5c5158e6f073f852b61a 129 | weak 130 | [file "src/nuget.config"] 131 | url = https://github.com/devlooped/oss/blob/main/src/nuget.config 132 | sha = 032439dbf180fca0539a5bd3a019f18ab3484b76 133 | etag = da7c0104131bd474b52fc9bc9f9bda6470e24ae38d4fb9f5c4f719bc01370ab5 134 | weak 135 | [file ".github/actions/dotnet/action.yml"] 136 | url = https://github.com/devlooped/oss/blob/main/.github/actions/dotnet/action.yml 137 | sha = f2b690ce307acb76c5b8d7faec1a5b971a93653e 138 | etag = 27ea11baa2397b3ec9e643a935832da97719c4e44215cfd135c49cad4c29373f 139 | weak 140 | [file ".github/workflows/dotnet-env.yml"] 141 | url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-env.yml 142 | sha = 77e83f238196d2723640abef0c7b6f43994f9747 143 | etag = fcb9759a96966df40dcd24906fd328ddec05953b7e747a6bb8d0d1e4c3865274 144 | weak 145 | -------------------------------------------------------------------------------- /Directory.Build.rsp: -------------------------------------------------------------------------------- 1 | # See https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-response-files 2 | -nr:false 3 | -m:1 4 | -v:m 5 | -clp:Summary;ForceNoAlign 6 | -------------------------------------------------------------------------------- /RxFree.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31710.8 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RxFree", "src\RxFree\RxFree.csproj", "{EFE7EFE0-5AC3-4C16-B56F-65ECDB937F97}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RxFree.Tests", "src\RxFree.Tests\RxFree.Tests.csproj", "{9C1BA247-9164-4A60-82F7-24364A353E1E}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RxFree.SourceTests", "src\RxFree.SourceTests\RxFree.SourceTests.csproj", "{BA6410EE-DECB-435B-AD4F-DA8A78AA9D85}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{768BF814-BBED-4362-A745-30F79160EA40}" 13 | ProjectSection(SolutionItems) = preProject 14 | .editorconfig = .editorconfig 15 | readme.md = readme.md 16 | EndProjectSection 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RxFree.SourceTests.Legacy", "src\RxFree.SourceTests.Legacy\RxFree.SourceTests.Legacy.csproj", "{774752A8-CFFA-417D-8A9C-503129D4C944}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RxFree.SourceTests.Nullable", "src\RxFree.SourceTests.Nullable\RxFree.SourceTests.Nullable.csproj", "{27252A70-1166-4B07-BA68-882E6F5F1DE9}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {EFE7EFE0-5AC3-4C16-B56F-65ECDB937F97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {EFE7EFE0-5AC3-4C16-B56F-65ECDB937F97}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {EFE7EFE0-5AC3-4C16-B56F-65ECDB937F97}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {EFE7EFE0-5AC3-4C16-B56F-65ECDB937F97}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {9C1BA247-9164-4A60-82F7-24364A353E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {9C1BA247-9164-4A60-82F7-24364A353E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {9C1BA247-9164-4A60-82F7-24364A353E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {9C1BA247-9164-4A60-82F7-24364A353E1E}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {BA6410EE-DECB-435B-AD4F-DA8A78AA9D85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {BA6410EE-DECB-435B-AD4F-DA8A78AA9D85}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {BA6410EE-DECB-435B-AD4F-DA8A78AA9D85}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {BA6410EE-DECB-435B-AD4F-DA8A78AA9D85}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {774752A8-CFFA-417D-8A9C-503129D4C944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {774752A8-CFFA-417D-8A9C-503129D4C944}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {774752A8-CFFA-417D-8A9C-503129D4C944}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {774752A8-CFFA-417D-8A9C-503129D4C944}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {27252A70-1166-4B07-BA68-882E6F5F1DE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {27252A70-1166-4B07-BA68-882E6F5F1DE9}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {27252A70-1166-4B07-BA68-882E6F5F1DE9}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {27252A70-1166-4B07-BA68-882E6F5F1DE9}.Release|Any CPU.Build.0 = Release|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {42975E35-A694-4E45-862E-B2D58AB474F5} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate 2 | 3 | exclude: [ 'src/', '*.sln', '*.slnx', 'Gemfile*', '*.rsp' ] 4 | -------------------------------------------------------------------------------- /assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "jekyll-theme-slate"; 5 | 6 | .inner { 7 | max-width: 960px; 8 | } 9 | 10 | pre, code { 11 | background-color: unset; 12 | font-size: unset; 13 | } 14 | 15 | code { 16 | font-size: 0.80em; 17 | } 18 | 19 | h1 > img { 20 | border: unset; 21 | box-shadow: unset; 22 | vertical-align: middle; 23 | -moz-box-shadow: unset; 24 | -o-box-shadow: unset; 25 | -ms-box-shadow: unset; 26 | } 27 | -------------------------------------------------------------------------------- /assets/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlooped/RxFree/8c964e3ef83fff4458948c36d79a6e5b7f79487b/assets/img/icon.png -------------------------------------------------------------------------------- /assets/img/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | ReactiveX 3 | 4 | 5 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v1.1.3](https://github.com/devlooped/RxFree/tree/v1.1.3) (2024-01-24) 4 | 5 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.1.2...v1.1.3) 6 | 7 | :twisted_rightwards_arrows: Merged: 8 | 9 | - ⛙ ⬆️ Bump dependencies [\#37](https://github.com/devlooped/RxFree/pull/37) (@github-actions[bot]) 10 | 11 | ## [v1.1.2](https://github.com/devlooped/RxFree/tree/v1.1.2) (2022-08-08) 12 | 13 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.1.1...v1.1.2) 14 | 15 | :twisted_rightwards_arrows: Merged: 16 | 17 | - +M▼ includes [\#22](https://github.com/devlooped/RxFree/pull/22) (@github-actions[bot]) 18 | 19 | ## [v1.1.1](https://github.com/devlooped/RxFree/tree/v1.1.1) (2022-07-26) 20 | 21 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.1.0...v1.1.1) 22 | 23 | ## [v1.1.0](https://github.com/devlooped/RxFree/tree/v1.1.0) (2021-10-28) 24 | 25 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.0.5...v1.1.0) 26 | 27 | :sparkles: Implemented enhancements: 28 | 29 | - Match System.Reactive namespaces [\#9](https://github.com/devlooped/RxFree/issues/9) 30 | 31 | :twisted_rightwards_arrows: Merged: 32 | 33 | - Match System.Reactive namespaces [\#11](https://github.com/devlooped/RxFree/pull/11) (@kzu) 34 | 35 | ## [v1.0.5](https://github.com/devlooped/RxFree/tree/v1.0.5) (2021-09-27) 36 | 37 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.0.4...v1.0.5) 38 | 39 | :sparkles: Implemented enhancements: 40 | 41 | - Expose AnonymousObserver [\#8](https://github.com/devlooped/RxFree/issues/8) 42 | 43 | ## [v1.0.4](https://github.com/devlooped/RxFree/tree/v1.0.4) (2021-09-22) 44 | 45 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.0.3...v1.0.4) 46 | 47 | :sparkles: Implemented enhancements: 48 | 49 | - Make all source-provided classes partial so the consumer can decide visibility [\#6](https://github.com/devlooped/RxFree/issues/6) 50 | 51 | ## [v1.0.3](https://github.com/devlooped/RxFree/tree/v1.0.3) (2021-09-22) 52 | 53 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.0.0...v1.0.3) 54 | 55 | :sparkles: Implemented enhancements: 56 | 57 | - Mark package as a development dependency [\#5](https://github.com/devlooped/RxFree/issues/5) 58 | - Add \#nullable enable context to source files as suggested by analyzer [\#4](https://github.com/devlooped/RxFree/issues/4) 59 | 60 | ## [v1.0.0](https://github.com/devlooped/RxFree/tree/v1.0.0) (2021-09-22) 61 | 62 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.0.0-rc.13...v1.0.0) 63 | 64 | :sparkles: Implemented enhancements: 65 | 66 | - Ensure we provide source repository information in package [\#3](https://github.com/devlooped/RxFree/issues/3) 67 | - Add \ as first line to avoid analysis [\#2](https://github.com/devlooped/RxFree/issues/2) 68 | 69 | ## [v1.0.0-rc.13](https://github.com/devlooped/RxFree/tree/v1.0.0-rc.13) (2021-09-22) 70 | 71 | [Full Changelog](https://github.com/devlooped/RxFree/compare/v1.0.0-rc.12...v1.0.0-rc.13) 72 | 73 | ## [v1.0.0-rc.12](https://github.com/devlooped/RxFree/tree/v1.0.0-rc.12) (2021-09-21) 74 | 75 | [Full Changelog](https://github.com/devlooped/RxFree/compare/0317ddb89fe039443e3e819611159ad2c39357d0...v1.0.0-rc.12) 76 | 77 | 78 | 79 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 80 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Daniel Cazzulino and Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /profile/readme.md: -------------------------------------------------------------------------------- 1 |

Hi 👋, I'm kzu

2 |

sounds like 'kah-zu' rather than 'kzoo' :)

3 |

A hardcode .NET/C# developer from Argentina

4 | 5 | [![X](https://img.shields.io/badge/X-@kzu-blue?logo=twitter&logoColor=1D9BF0&labelColor=black)](https://x.com/kzu) 6 | [![Discord](https://img.shields.io/badge/discord-danielkzu-7289DA.svg?logo=discord&logoColor=white)](https://discord.gg/8PtpGdu) 7 | [![LinkedIn](https://img.shields.io/badge/LinkedIn-danielkzu-0A66C2.svg?logo=linkedin&logoColor=0A66C2&labelColor=silver)]([https://discord.gg/8PtpGdu](https://linkedin.com/in/danielcazzulino)) 8 | 9 | This is my sponsor account so you can contribute to my ongoing opensource work, no minimum amount required! 10 | 11 | [![Sponsor me 💜](https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor.png#gh-light-mode-only "Sponsor me 💜")](https://github.com/sponsors/devlooped#gh-light-mode-only) 12 | [![Sponsor me 💜](https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor-dark.png#gh-dark-mode-only "Sponsor me 💜")](https://github.com/sponsors/devlooped#gh-dark-mode-only) 13 | 14 | Some fancy stats about my favorite hobbie (coding on GitHub, of course!): 15 | 16 |

17 | 18 | 21 | 24 | 25 | 26 |

27 | 28 |

29 | 30 | 33 | 36 | 37 | 38 |

39 | 40 |

41 | 42 | 45 | 48 | 49 | 50 |

51 |

52 | 53 | And since I'm a total fan of [Shields.io](https://shields.io/) badges, some additional GitHub stats in badge format: 54 | 55 | ![GitHub Sponsors](https://img.shields.io/github/sponsors/devlooped?color=C65197&label=@devlooped%20sponsors) 56 | ![GitHub followers](https://img.shields.io/github/followers/kzu?logo=GitHub&label=@kzu%20followers) 57 | ![GitHub stars](https://img.shields.io/github/stars/kzu?logo=GitHub&label=@kzu%20stars&affiliations=OWNER,COLLABORATOR&color=FFC83D) 58 | [![GitHub followers](https://img.shields.io/github/followers/devlooped?logo=GitHub&label=@devlooped%20followers)](https://github.com/devlooped) 59 | [![GitHub Org's stars](https://img.shields.io/github/stars/devlooped?logo=GitHub&label=@devlooped%20stars)](https://github.com/devlooped) 60 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![Icon](https://raw.githubusercontent.com/devlooped/RxFree/main/assets/img/icon.png) RxFree 2 | ============ 3 | 4 | [![Version](https://img.shields.io/nuget/v/RxFree.svg?color=royalblue)](https://www.nuget.org/packages/RxFree) 5 | [![Downloads](https://img.shields.io/nuget/dt/RxFree.svg?color=darkmagenta)](https://www.nuget.org/packages/RxFree) 6 | [![License](https://img.shields.io/github/license/devlooped/RxFree.svg?color=blue)](https://github.com/devlooped/RxFree/blob/main/license.txt) 7 | [![Build](https://github.com/devlooped/RxFree/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/devlooped/RxFree/actions) 8 | 9 | 10 | An ultra-lightweight Rx source-only (C#) nuget to avoid depending on the full 11 | [System.Reactive](https://www.nuget.org/packages/System.Reactive) for `IObservable` 12 | producers. 13 | 14 | 100% dependency-free (source-based) support for library authors exposing IObservable<T> leveraging 15 | Subject<T>, CompositeDisposable, IObservable<T>.Subscribe extension method overloads, 16 | IObservable<T>.Select/Where/OfType LINQ operators, and others. 17 | 18 | # Usage 19 | 20 | All of the documentation and samples for `Subject` and the provided extension methods 21 | (i.e. `Subscribe` overloads) that are officially available for `System.Reactive` apply to 22 | this project as well, since the implementations are heavily based on it (taking them to 23 | the bare essentials for source-only inclusion, with `Subject` being pretty much exactly 24 | the same). 25 | For example: [Using Subjects](https://docs.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh242970(v=vs.103)). 26 | 27 | ```csharp 28 | using System; 29 | using System.Reactive.Subjects; 30 | 31 | var subject = new Subject(); 32 | 33 | subject.Subscribe(x => Console.WriteLine($"Got raw value {x}")); 34 | 35 | subject.Where(x => int.TryParse(x, out _)) 36 | .Select(x => int.Parse(x)) 37 | .Subscribe(x => Console.WriteLine($"Got number {x} (squared is {x * x})")); 38 | 39 | subject.Where(x => bool.TryParse(x, out var value) && value) 40 | .Subscribe(x => Console.WriteLine($"Got a boolean True")); 41 | 42 | while (Console.ReadLine() is var line && !string.IsNullOrEmpty(line)) 43 | subject.OnNext(line); 44 | ``` 45 | 46 | This package is a drop-in replacement for `System.Reactive` if you are only using the 47 | most common subset of features in it that are also provided in this project. 48 | 49 | # Why 50 | 51 | For the most part, a producer needs the `Subject` (read more about 52 | [using subjects](https://docs.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh242970(v=vs.103))) 53 | and maybe the `ObservableExtensions` that provide `Subscribe` overloads to provide 54 | lambdas instead of an `IObserver`. Taking the somewhat large and heavy dependency 55 | on the full [System.Reactive](https://www.nuget.org/packages/System.Reactive) to consume 56 | just the basics a reusable library needs is overkill in most cases. 57 | 58 | In addition to `Subject`, typical activities of a producer invole handling 59 | disposables and potentially filtering/querying/converting other observables they 60 | consume themselves in turn. For that purpose, the following simple features are 61 | included too: 62 | 63 | - `Subject`: for producing observable sequences 64 | - `Disposable.Empty` and `Disposable.Create(Action)` 65 | - `CompositeDisposable`: allows disposing subscriptions as a group 66 | - Extension methods for `IObservable`: 67 | * `Subscribe` overloads receiving delegates for onNext, onError and onCompleted 68 | * `Select`/`Where`/`OfType` LINQ operators 69 | 70 | This is what this project provides at the moment, in source form, in your project, as internal 71 | classes for your own implementation usage, with no external dependencies. They are not even 72 | visible in the project since the package provides them automatically to the compiler, compiled 73 | into your own assembly, and which you can fully debug as any other code in your project. 74 | 75 | All types are included as `partial` classes, so you can easily add your own code to 76 | them, and make them public if you need to. 77 | 78 | 79 | 80 | # Dogfooding 81 | 82 | [![CI Version](https://img.shields.io/endpoint?url=https://shields.kzu.app/vpre/RxFree/main&label=nuget.ci&color=brightgreen)](https://pkg.kzu.app/index.json) 83 | [![Build](https://github.com/devlooped/RxFree/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/devlooped/RxFree/actions) 84 | 85 | We also produce CI packages from branches and pull requests so you can dogfood builds as quickly as they are produced. 86 | 87 | The CI feed is `https://pkg.kzu.app/index.json`. 88 | 89 | The versioning scheme for packages is: 90 | 91 | - PR builds: *42.42.42-pr*`[NUMBER]` 92 | - Branch builds: *42.42.42-*`[BRANCH]`.`[COMMITS]` 93 | 94 | 95 | # Sponsors 96 | 97 | 98 | [![Clarius Org](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/clarius.png "Clarius Org")](https://github.com/clarius) 99 | [![MFB Technologies, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MFB-Technologies-Inc.png "MFB Technologies, Inc.")](https://github.com/MFB-Technologies-Inc) 100 | [![Torutek](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/torutek-gh.png "Torutek")](https://github.com/torutek-gh) 101 | [![DRIVE.NET, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/drivenet.png "DRIVE.NET, Inc.")](https://github.com/drivenet) 102 | [![Keith Pickford](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Keflon.png "Keith Pickford")](https://github.com/Keflon) 103 | [![Thomas Bolon](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/tbolon.png "Thomas Bolon")](https://github.com/tbolon) 104 | [![Kori Francis](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/kfrancis.png "Kori Francis")](https://github.com/kfrancis) 105 | [![Toni Wenzel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/twenzel.png "Toni Wenzel")](https://github.com/twenzel) 106 | [![Uno Platform](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/unoplatform.png "Uno Platform")](https://github.com/unoplatform) 107 | [![Reuben Swartz](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/rbnswartz.png "Reuben Swartz")](https://github.com/rbnswartz) 108 | [![Jacob Foshee](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jfoshee.png "Jacob Foshee")](https://github.com/jfoshee) 109 | [![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Mrxx99.png "")](https://github.com/Mrxx99) 110 | [![Eric Johnson](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/eajhnsn1.png "Eric Johnson")](https://github.com/eajhnsn1) 111 | [![David JENNI](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/davidjenni.png "David JENNI")](https://github.com/davidjenni) 112 | [![Jonathan ](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Jonathan-Hickey.png "Jonathan ")](https://github.com/Jonathan-Hickey) 113 | [![Charley Wu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/akunzai.png "Charley Wu")](https://github.com/akunzai) 114 | [![Ken Bonny](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/KenBonny.png "Ken Bonny")](https://github.com/KenBonny) 115 | [![Simon Cropp](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/SimonCropp.png "Simon Cropp")](https://github.com/SimonCropp) 116 | [![agileworks-eu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/agileworks-eu.png "agileworks-eu")](https://github.com/agileworks-eu) 117 | [![sorahex](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/sorahex.png "sorahex")](https://github.com/sorahex) 118 | [![Zheyu Shen](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/arsdragonfly.png "Zheyu Shen")](https://github.com/arsdragonfly) 119 | [![Vezel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/vezel-dev.png "Vezel")](https://github.com/vezel-dev) 120 | [![ChilliCream](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/ChilliCream.png "ChilliCream")](https://github.com/ChilliCream) 121 | [![4OTC](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/4OTC.png "4OTC")](https://github.com/4OTC) 122 | [![Vincent Limo](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/v-limo.png "Vincent Limo")](https://github.com/v-limo) 123 | [![Jordan S. Jones](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jordansjones.png "Jordan S. Jones")](https://github.com/jordansjones) 124 | [![domischell](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/DominicSchell.png "domischell")](https://github.com/DominicSchell) 125 | [![Justin Wendlandt](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jwendl.png "Justin Wendlandt")](https://github.com/jwendl) 126 | [![Adrian Alonso](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/adalon.png "Adrian Alonso")](https://github.com/adalon) 127 | [![Michael Hagedorn](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Eule02.png "Michael Hagedorn")](https://github.com/Eule02) 128 | [![Matt Frear](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/mattfrear.png "Matt Frear")](https://github.com/mattfrear) 129 | 130 | 131 | 132 | 133 | [![Sponsor this project](https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor.png "Sponsor this project")](https://github.com/sponsors/devlooped) 134 |   135 | 136 | [Learn more about GitHub Sponsors](https://github.com/sponsors) 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | true 14 | 15 | 16 | 17 | 18 | $(CI) 19 | 20 | 21 | 22 | Daniel Cazzulino 23 | Copyright (C) Daniel Cazzulino and Contributors. All rights reserved. 24 | false 25 | MIT 26 | 27 | 28 | icon.png 29 | readme.md 30 | 31 | icon.png 32 | readme.md 33 | 34 | true 35 | true 36 | 37 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\bin')) 38 | 39 | 40 | true 41 | true 42 | 43 | 44 | true 45 | 46 | 47 | 48 | Release 49 | Latest 50 | 51 | 52 | false 53 | 54 | embedded 55 | true 56 | enable 57 | 58 | strict 59 | 60 | 61 | $(MSBuildProjectName) 62 | $(MSBuildProjectName.IndexOf('.')) 63 | $(MSBuildProjectName.Substring(0, $(RootNamespaceDot))) 64 | 65 | 66 | $(DefaultItemExcludes);*.binlog;*.zip;*.rsp;*.items;**/TestResults/**/*.* 67 | 68 | true 69 | true 70 | true 71 | true 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | NU5105;$(NoWarn) 81 | 82 | true 83 | 84 | 85 | true 86 | 87 | 88 | LatestMinor 89 | 90 | 91 | 92 | 93 | $(MSBuildThisFileDirectory)kzu.snk 94 | 100 | 002400000480000094000000060200000024000052534131000400000100010051155fd0ee280be78d81cc979423f1129ec5dd28edce9cd94fd679890639cad54c121ebdb606f8659659cd313d3b3db7fa41e2271158dd602bb0039a142717117fa1f63d93a2d288a1c2f920ec05c4858d344a45d48ebd31c1368ab783596b382b611d8c92f9c1b3d338296aa21b12f3bc9f34de87756100c172c52a24bad2db 101 | 00352124762f2aa5 102 | true 103 | 104 | 105 | 106 | 114 | 42.42.42 115 | 116 | 117 | 118 | <_VersionLabel>$(VersionLabel.Replace('refs/heads/', '')) 119 | <_VersionLabel>$(_VersionLabel.Replace('refs/tags/v', '')) 120 | 121 | 122 | <_VersionLabel Condition="$(_VersionLabel.Contains('refs/pull/'))">$(VersionLabel.TrimEnd('.0123456789')) 123 | 124 | <_VersionLabel>$(_VersionLabel.Replace('refs/pull/', 'pr')) 125 | 126 | <_VersionLabel>$(_VersionLabel.Replace('/merge', '')) 127 | 128 | <_VersionLabel>$(_VersionLabel.Replace('/', '-')) 129 | 130 | <_VersionLabel>$(_VersionLabel.Replace('_', '-')) 131 | 132 | 133 | $(_VersionLabel) 134 | 135 | $(_VersionLabel) 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 164 | 165 | 1.0.0 166 | $(VersionPrefix)-$(VersionSuffix) 167 | $(VersionPrefix) 168 | 169 | 170 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CI;$(DefineConstants) 6 | 7 | 8 | 9 | 10 | false 11 | false 12 | true 13 | 14 | 15 | 16 | true 17 | true 18 | 19 | 20 | 21 | 31 | false 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 45 | 49 | 50 | 51 | 55 | 56 | 60 | 61 | 62 | 64 | 65 | 1.0.0 66 | $(VersionPrefix)-$(VersionSuffix) 67 | $(VersionPrefix) 68 | 69 | 70 | 71 | 72 | $(PackFolder) 73 | $(PackFolderPath.Replace('\$(TargetFramework)', '')) 74 | $(IntermediateOutputPath)$(PackFolderPath)\ 75 | $(OutputPath)$(PackFolderPath)\ 76 | $(OutputPath) 77 | 78 | 79 | 80 | 81 | pr$(GITHUB_REF.Replace('refs/pull/', '').Replace('/merge', '')) 82 | $(GITHUB_REF.Replace('refs/heads/', '').Replace('refs/tags/', '')) 83 | 84 | $(BUILD_SOURCEBRANCH.Replace('refs/heads/', '').Replace('refs/tags/', '')) 85 | 86 | pr$(APPVEYOR_PULL_REQUEST_NUMBER) 87 | $(APPVEYOR_REPO_TAG_NAME) 88 | $(APPVEYOR_REPO_BRANCH) 89 | 90 | $(TEAMCITY_BUILD_BRANCH) 91 | 92 | pr$(TRAVIS_PULL_REQUEST) 93 | $(TRAVIS_BRANCH) 94 | 95 | pr$(CIRCLE_PR_NUMBER) 96 | $(CIRCLE_TAG) 97 | $(CIRCLE_BRANCH) 98 | 99 | $(CI_COMMIT_TAG) 100 | pr$(CI_MERGE_REQUEST_IID) 101 | pr$(CI_EXTERNAL_PULL_REQUEST_IID) 102 | $(CI_COMMIT_BRANCH) 103 | 104 | pr$(BUDDY_EXECUTION_PULL_REQUEST_NO) 105 | $(BUDDY_EXECUTION_TAG) 106 | $(BUDDY_EXECUTION_BRANCH) 107 | 108 | 109 | 110 | 111 | CoreResGen;$(CoreCompileDependsOn) 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | $(IntermediateOutputPath)\$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.'))%(Filename).g$(DefaultLanguageSourceExtension) 121 | $(Language) 122 | $(RootNamespace) 123 | $(RootNamespace).$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.').TrimEnd('.')) 124 | %(Filename) 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 148 | 149 | 150 | 151 | $(PrivateRepositoryUrl) 152 | 153 | 154 | 155 | $(SourceRevisionId) 156 | $(SourceRevisionId.Substring(0, 9)) 157 | 158 | $(RepositorySha) 159 | 160 | 161 | 162 | 163 | <_GitSourceRoot Include="@(SourceRoot -> WithMetadataValue('SourceControl', 'git'))" /> 164 | 165 | 166 | 167 | @(_GitSourceRoot) 168 | 169 | 170 | 171 | 172 | 177 | 178 | $(RepositoryUrl) 179 | $(Description) 180 | $(RepositoryUrl)/blob/main/changelog.md 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /src/RxFree.SourceTests.Legacy/RxFree.SourceTests.Legacy.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | net8.0 6 | disable 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/RxFree.SourceTests.Nullable/RxFree.SourceTests.Nullable.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | net8.0 6 | enable 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/RxFree.SourceTests/RxFree.SourceTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | net8.0 6 | enable 7 | latest 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/RxFree.Tests/DisposableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Disposables; 2 | using Xunit; 3 | 4 | namespace RxFree.Tests 5 | { 6 | public class DisposableTests 7 | { 8 | [Fact] 9 | public void DisposeOnce() 10 | { 11 | var disposed = 0; 12 | var disposable = Disposable.Create(() => disposed++); 13 | 14 | disposable.Dispose(); 15 | 16 | Assert.Equal(1, disposed); 17 | 18 | disposable.Dispose(); 19 | 20 | Assert.Equal(1, disposed); 21 | } 22 | 23 | [Fact] 24 | public void EmptyDisposable() 25 | { 26 | var disposable = Disposable.Empty; 27 | disposable.Dispose(); 28 | disposable.Dispose(); 29 | disposable.Dispose(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/RxFree.Tests/LinqTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Reactive.Linq; 3 | using System.Reactive.Subjects; 4 | using Xunit; 5 | 6 | namespace System 7 | { 8 | public class LinqTests 9 | { 10 | [Fact] 11 | public void OfType() 12 | { 13 | var subject = new Subject(); 14 | 15 | var b = 0; 16 | var c = 0; 17 | 18 | subject.OfType().Subscribe(_ => b++); 19 | var subs = subject.OfType().Subscribe(_ => c++); 20 | 21 | subject.OnNext(new A()); 22 | subject.OnNext(new B()); 23 | subject.OnNext(new C()); 24 | subject.OnNext(new C()); 25 | 26 | Assert.Equal(1, b); 27 | Assert.Equal(2, c); 28 | 29 | subs.Dispose(); 30 | 31 | subject.OnNext(new C()); 32 | 33 | Assert.Equal(1, b); 34 | Assert.Equal(2, c); 35 | } 36 | 37 | [Fact] 38 | public void Select() 39 | { 40 | var subject = new Subject(); 41 | 42 | var count = 0; 43 | 44 | subject.Select(x => new B()).Subscribe(_ => count++); 45 | 46 | subject.OnNext(new A()); 47 | subject.OnNext(new C()); 48 | 49 | Assert.Equal(2, count); 50 | } 51 | 52 | [Fact] 53 | public void Where() 54 | { 55 | var subject = new Subject(); 56 | 57 | var count = 0; 58 | 59 | subject.Where(a => a.Id?.StartsWith("a") == true).Subscribe(_ => count++); 60 | 61 | subject.OnNext(new A()); 62 | subject.OnNext(new A { Id = "asdf" }); 63 | subject.OnNext(new A { Id = "sdf" }); 64 | subject.OnNext(new A { Id = "a" }); 65 | 66 | Assert.Equal(2, count); 67 | } 68 | 69 | #pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 70 | class A { public string Id { get; set; } } 71 | #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 72 | class B : A { } 73 | class C : A { } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/RxFree.Tests/RxFree.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | all 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/RxFree.Tests/SubjectTests.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | using Xunit; 3 | 4 | namespace System 5 | { 6 | public class SubjectTests 7 | { 8 | [Fact] 9 | public void OnNext() 10 | { 11 | var subject = new Subject(); 12 | var value = 0; 13 | 14 | var subscription = subject.Subscribe(i => value += i); 15 | 16 | subject.OnNext(1); 17 | Assert.Equal(1, value); 18 | 19 | subject.OnNext(1); 20 | Assert.Equal(2, value); 21 | 22 | subscription.Dispose(); 23 | 24 | subject.OnNext(1); 25 | Assert.Equal(2, value); 26 | } 27 | 28 | [Fact] 29 | public void OnNextDisposed() 30 | { 31 | var subject = new Subject(); 32 | 33 | subject.Dispose(); 34 | 35 | Assert.Throws(() => subject.OnNext(1)); 36 | } 37 | 38 | [Fact] 39 | public void OnNextDisposedSubscriber() 40 | { 41 | var subject = new Subject(); 42 | var value = 0; 43 | 44 | subject.Subscribe(i => value += i).Dispose(); 45 | 46 | subject.OnNext(1); 47 | 48 | Assert.Equal(0, value); 49 | } 50 | 51 | [Fact] 52 | public void OnCompleted() 53 | { 54 | var subject = new Subject(); 55 | var completed = false; 56 | 57 | var subscription = subject.Subscribe(_ => { }, () => completed = true); 58 | 59 | subject.OnCompleted(); 60 | 61 | Assert.True(completed); 62 | } 63 | 64 | [Fact] 65 | public void OnCompletedNoOp() 66 | { 67 | var subject = new Subject(); 68 | 69 | var subscription = subject.Subscribe(_ => { }); 70 | 71 | subject.OnCompleted(); 72 | } 73 | 74 | [Fact] 75 | public void OnCompletedOnce() 76 | { 77 | var subject = new Subject(); 78 | var completed = 0; 79 | 80 | var subscription = subject.Subscribe(_ => { }, () => completed++); 81 | 82 | subject.OnCompleted(); 83 | 84 | Assert.Equal(1, completed); 85 | 86 | subject.OnCompleted(); 87 | 88 | Assert.Equal(1, completed); 89 | } 90 | 91 | [Fact] 92 | public void OnCompletedDisposed() 93 | { 94 | var subject = new Subject(); 95 | 96 | subject.Dispose(); 97 | 98 | Assert.Throws(() => subject.OnCompleted()); 99 | } 100 | 101 | [Fact] 102 | public void OnCompletedDisposedSubscriber() 103 | { 104 | var subject = new Subject(); 105 | var completed = false; 106 | 107 | subject.Subscribe(_ => { }, () => completed = true).Dispose(); 108 | 109 | subject.OnCompleted(); 110 | 111 | Assert.False(completed); 112 | } 113 | 114 | [Fact] 115 | public void OnError() 116 | { 117 | var subject = new Subject(); 118 | var error = false; 119 | 120 | var subscription = subject.Subscribe(_ => { }, e => error = true); 121 | 122 | subject.OnError(new Exception()); 123 | 124 | Assert.True(error); 125 | } 126 | 127 | [Fact] 128 | public void OnErrorOnce() 129 | { 130 | var subject = new Subject(); 131 | var errors = 0; 132 | 133 | var subscription = subject.Subscribe(_ => { }, e => errors++); 134 | 135 | subject.OnError(new Exception()); 136 | 137 | Assert.Equal(1, errors); 138 | 139 | subject.OnError(new Exception()); 140 | 141 | Assert.Equal(1, errors); 142 | } 143 | 144 | [Fact] 145 | public void OnErrorDisposed() 146 | { 147 | var subject = new Subject(); 148 | 149 | subject.Dispose(); 150 | 151 | Assert.Throws(() => subject.OnError(new Exception())); 152 | } 153 | 154 | [Fact] 155 | public void OnErrorDisposedSubscriber() 156 | { 157 | var subject = new Subject(); 158 | var error = false; 159 | 160 | subject.Subscribe(_ => { }, e => error = true).Dispose(); 161 | 162 | subject.OnError(new Exception()); 163 | 164 | Assert.False(error); 165 | } 166 | 167 | [Fact] 168 | public void OnErrorRethrowsByDefault() 169 | { 170 | var subject = new Subject(); 171 | 172 | var subs = subject.Subscribe(_ => { }); 173 | 174 | Assert.Throws(() => subject.OnError(new ArgumentException())); 175 | } 176 | 177 | [Fact] 178 | public void OnErrorNullThrows() 179 | { 180 | #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. 181 | Assert.Throws(() => new Subject().OnError(null)); 182 | #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. 183 | } 184 | 185 | 186 | [Fact] 187 | public void SubscribeNullThrows() 188 | { 189 | #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. 190 | Assert.Throws(() => new Subject().Subscribe(null)); 191 | #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. 192 | } 193 | 194 | [Fact] 195 | public void SubscribeDisposedThrows() 196 | { 197 | var subject = new Subject(); 198 | 199 | subject.Dispose(); 200 | 201 | Assert.Throws(() => subject.Subscribe(_ => { })); 202 | } 203 | 204 | [Fact] 205 | public void SubscribeOnCompleted() 206 | { 207 | var subject = new Subject(); 208 | subject.OnCompleted(); 209 | var completed = false; 210 | 211 | subject.Subscribe(_ => { }, () => completed = true).Dispose(); 212 | 213 | Assert.True(completed); 214 | } 215 | 216 | [Fact] 217 | public void SubscribeOnError() 218 | { 219 | var subject = new Subject(); 220 | subject.OnError(new Exception()); 221 | var error = false; 222 | 223 | subject.Subscribe(_ => { }, e => error = true); 224 | 225 | Assert.True(error); 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/RxFree/RxFree.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | 7 | 8 | 9 | RxFree 10 | An ultra-lightweight Rx source-only (C#) nuget to avoid depending on the full 11 | System.Reactive package for `IObservable<T>` producers. 12 | 13 | https://clarius.org/RxFree/ 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/AnonymousObserver.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.ExceptionServices; 2 | 3 | namespace System.Reactive 4 | { 5 | /// 6 | /// Create an instance from delegate-based implementations 7 | /// of the On* methods. 8 | /// 9 | /// The type of the elements in the sequence. 10 | partial class AnonymousObserver : IObserver 11 | { 12 | static readonly Action rethrow = e => ExceptionDispatchInfo.Capture(e).Throw(); 13 | static readonly Action nop = () => { }; 14 | 15 | readonly Action onNext; 16 | readonly Action onError; 17 | readonly Action onCompleted; 18 | 19 | /// 20 | /// Creates the observable providing just the action. 21 | /// 22 | public AnonymousObserver(Action onNext) 23 | : this(onNext, rethrow, nop) { } 24 | 25 | /// 26 | /// Creates the observable providing both the and 27 | /// actions. 28 | /// 29 | public AnonymousObserver(Action onNext, Action onError) 30 | : this(onNext, onError, nop) { } 31 | 32 | /// 33 | /// Creates the observable providing both the and 34 | /// actions. 35 | /// 36 | public AnonymousObserver(Action onNext, Action onCompleted) 37 | : this(onNext, rethrow, onCompleted) { } 38 | 39 | /// 40 | /// Creates the observable providing all three , 41 | /// and actions. 42 | /// 43 | public AnonymousObserver(Action onNext, Action onError, Action onCompleted) 44 | { 45 | this.onNext = onNext; 46 | this.onError = onError; 47 | this.onCompleted = onCompleted; 48 | } 49 | 50 | /// 51 | /// Calls the action implementing . 52 | /// 53 | public void OnCompleted() => onCompleted(); 54 | 55 | /// 56 | /// Calls the action implementing . 57 | /// 58 | public void OnError(Exception error) => onError(error); 59 | 60 | /// 61 | /// Calls the action implementing . 62 | /// 63 | public void OnNext(T value) => onNext(value); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/CompositeDisposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.CodeDom.Compiler; 3 | using System.Collections.Concurrent; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | using System.Threading; 7 | 8 | namespace System.Reactive.Disposables 9 | { 10 | /// 11 | /// Represents a group of disposable resources that are disposed together. 12 | /// 13 | [GeneratedCode("RxFree", "*")] 14 | [CompilerGenerated] 15 | [ExcludeFromCodeCoverage] 16 | partial class CompositeDisposable : IDisposable 17 | { 18 | bool disposed; 19 | readonly ConcurrentBag disposables; 20 | 21 | /// 22 | /// Initializes a new instance of the class from a group of disposables. 23 | /// 24 | /// Disposables that will be disposed together. 25 | /// is . 26 | public CompositeDisposable(params IDisposable[] disposables) 27 | { 28 | this.disposables = new ConcurrentBag(disposables) ?? throw new ArgumentNullException(nameof(disposables)); 29 | } 30 | 31 | /// 32 | /// Gets a value that indicates whether the object is disposed. 33 | /// 34 | public bool IsDisposed => disposed; 35 | 36 | /// 37 | /// Adds a disposable to the or disposes the disposable if the is disposed. 38 | /// 39 | /// Disposable to add. 40 | public void Add(IDisposable disposable) 41 | { 42 | if (disposed) 43 | disposable?.Dispose(); 44 | else 45 | disposables.Add(disposable); 46 | } 47 | 48 | /// 49 | /// Disposes all disposables in the group and removes them from the group. 50 | /// 51 | public void Dispose() 52 | { 53 | if (disposed) 54 | return; 55 | 56 | disposed = true; 57 | while (disposables.TryTake(out var disposable)) 58 | { 59 | disposable?.Dispose(); 60 | } 61 | } 62 | 63 | /// 64 | /// Creates a new group of disposable resources that are disposed together. 65 | /// 66 | /// Disposable resources to add to the group. 67 | /// Group of disposable resources that are disposed together. 68 | public static IDisposable Create(params IDisposable[] disposables) => new DisposableArray(disposables); 69 | 70 | [ExcludeFromCodeCoverage] 71 | sealed class DisposableArray : IDisposable 72 | { 73 | IDisposable[] disposables; 74 | 75 | public DisposableArray(IDisposable[] disposables) 76 | { 77 | Volatile.Write(ref this.disposables, disposables ?? throw new ArgumentNullException(nameof(disposables))); 78 | } 79 | 80 | public void Dispose() 81 | { 82 | var old = Interlocked.Exchange(ref disposables, null); 83 | if (old != null) 84 | { 85 | foreach (var disposable in old) 86 | { 87 | disposable?.Dispose(); 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/Disposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Threading; 4 | 5 | namespace System.Reactive.Disposables 6 | { 7 | /// 8 | /// Provides a set of static methods for creating objects. 9 | /// 10 | [ExcludeFromCodeCoverage] 11 | static partial class Disposable 12 | { 13 | /// 14 | /// Gets the disposable that does nothing when disposed. 15 | /// 16 | public static IDisposable Empty { get; } = new EmptyDisposable(); 17 | 18 | /// 19 | /// Creates a disposable object that invokes the specified action when disposed. 20 | /// 21 | /// Action to run during the first call to . The action is guaranteed to be run at most once. 22 | /// The disposable object that runs the given action upon disposal. 23 | /// is null. 24 | public static IDisposable Create(Action dispose) => 25 | new AnonymousDisposable(dispose ?? throw new ArgumentNullException(nameof(dispose))); 26 | 27 | sealed class EmptyDisposable : IDisposable 28 | { 29 | public void Dispose() { } 30 | } 31 | 32 | /// 33 | /// Represents an Action-based disposable. 34 | /// 35 | internal sealed class AnonymousDisposable : IDisposable 36 | { 37 | volatile Action dispose; 38 | 39 | /// 40 | /// Constructs a new disposable with the given action used for disposal. 41 | /// 42 | /// Disposal action which will be run upon calling Dispose. 43 | public AnonymousDisposable(Action dispose) => 44 | this.dispose = dispose; 45 | 46 | /// 47 | /// Calls the disposal action if and only if the current instance hasn't been disposed yet. 48 | /// 49 | public void Dispose() => 50 | Interlocked.Exchange(ref dispose, null)?.Invoke(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/ObservableExtensions.OfType.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | static partial class ObservableExtensions 6 | { 7 | /// 8 | /// Filters the elements of an observable sequence based on the specified type. 9 | /// 10 | /// The type to filter the elements in the source sequence on. 11 | /// The sequence that contains the elements to be filtered. 12 | /// is null. 13 | public static IObservable OfType(this IObservable source) 14 | => new OfTypeSubject(source ?? throw new ArgumentNullException(nameof(source))); 15 | 16 | class OfTypeSubject : Subject 17 | { 18 | IDisposable subscription; 19 | 20 | public OfTypeSubject(IObservable source) 21 | { 22 | subscription = source.Subscribe( 23 | next => 24 | { 25 | if (next is T result) 26 | OnNext(result); 27 | }, 28 | OnError, 29 | OnCompleted); 30 | } 31 | 32 | public override void Dispose() 33 | { 34 | base.Dispose(); 35 | subscription?.Dispose(); 36 | subscription = null; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/ObservableExtensions.Select.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | partial class ObservableExtensions 6 | { 7 | /// 8 | /// Projects each element of a sequence into a new form. 9 | /// 10 | /// The type of the elements in the source sequence. 11 | /// The type of the elements in the result sequence, obtained by running the selector function for each element in the source sequence. 12 | /// A sequence of elements to invoke a transform function on. 13 | /// A transform function to apply to each source element. 14 | /// An sequence whose elements are the result of invoking the transform function on each element of source. 15 | /// or is null. 16 | public static IObservable Select(this IObservable source, Func selector) 17 | => new SelectorSubject(source ?? throw new ArgumentNullException(nameof(source)), selector ?? throw new ArgumentNullException(nameof(selector))); 18 | 19 | class SelectorSubject : Subject 20 | { 21 | IDisposable subscription; 22 | Func selector; 23 | 24 | public SelectorSubject(IObservable source, Func selector) 25 | { 26 | this.selector = selector; 27 | subscription = source.Subscribe( 28 | next => OnNext(this.selector(next)), 29 | OnError, 30 | OnCompleted); 31 | } 32 | 33 | public override void Dispose() 34 | { 35 | base.Dispose(); 36 | subscription?.Dispose(); 37 | subscription = null; 38 | selector = null; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/ObservableExtensions.Where.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | partial class ObservableExtensions 6 | { 7 | /// 8 | /// Filters the elements of an observable sequence based on the specified type. 9 | /// 10 | /// The type to filter the elements in the source sequence on. 11 | /// The sequence that contains the elements to be filtered. 12 | /// Predicate to apply to elements in for filtering. 13 | /// is null. 14 | public static IObservable Where(this IObservable source, Func predicate) 15 | => new WhereSubject(source ?? throw new ArgumentNullException(nameof(source)), predicate ?? throw new ArgumentNullException(nameof(predicate))); 16 | 17 | class WhereSubject : Subject 18 | { 19 | IDisposable subscription; 20 | Func predicate; 21 | 22 | public WhereSubject(IObservable source, Func predicate) 23 | { 24 | this.predicate = predicate; 25 | subscription = source.Subscribe( 26 | next => 27 | { 28 | if (this.predicate(next)) 29 | OnNext(next); 30 | }, 31 | OnError, 32 | OnCompleted); 33 | } 34 | 35 | public override void Dispose() 36 | { 37 | base.Dispose(); 38 | subscription?.Dispose(); 39 | subscription = null; 40 | predicate = null; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/ObservableExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.CodeDom.Compiler; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Reactive; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.ExceptionServices; 7 | 8 | namespace System 9 | { 10 | /// 11 | /// Provides a set of static methods for subscribing delegates to observables. 12 | /// 13 | [GeneratedCode("RxFree", "*")] 14 | [CompilerGenerated] 15 | [ExcludeFromCodeCoverage] 16 | static partial class ObservableExtensions 17 | { 18 | static readonly Action rethrow = e => ExceptionDispatchInfo.Capture(e).Throw(); 19 | static readonly Action nop = () => { }; 20 | 21 | /// 22 | /// Subscribes to the observable providing just the delegate. 23 | /// 24 | public static IDisposable Subscribe(this IObservable source, Action onNext) 25 | => Subscribe(source, onNext, rethrow, nop); 26 | 27 | /// 28 | /// Subscribes to the observable providing both the and 29 | /// delegates. 30 | /// 31 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onError) 32 | => Subscribe(source, onNext, onError, nop); 33 | 34 | /// 35 | /// Subscribes to the observable providing both the and 36 | /// delegates. 37 | /// 38 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onCompleted) 39 | => Subscribe(source, onNext, rethrow, onCompleted); 40 | 41 | /// 42 | /// Subscribes to the observable providing all three , 43 | /// and delegates. 44 | /// 45 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onError, Action onCompleted) 46 | => source.Subscribe(new AnonymousObserver(onNext, onError, onCompleted)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/StableCompositeDisposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.ComponentModel; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace System.Reactive.Disposables 6 | { 7 | /// 8 | /// For compatibility with Rx 9 | /// 10 | [EditorBrowsable(EditorBrowsableState.Never)] 11 | [ExcludeFromCodeCoverage] 12 | partial class StableCompositeDisposable 13 | { 14 | /// 15 | /// For compatibility with Rx 16 | /// 17 | [EditorBrowsable(EditorBrowsableState.Never)] 18 | public static IDisposable Create(params IDisposable[] disposables) => CompositeDisposable.Create(disposables); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/RxFree/build/Legacy/Subject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the .NET Foundation under one or more agreements. 3 | // The .NET Foundation licenses this file to you under the MIT License. 4 | 5 | // https://github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/System.Reactive/Subjects/Subject.cs 6 | // https://github.com/dotnet/reactive/blob/main/LICENSE 7 | 8 | using System.CodeDom.Compiler; 9 | using System.Diagnostics.CodeAnalysis; 10 | using System.Runtime.CompilerServices; 11 | using System.Threading; 12 | 13 | namespace System.Reactive.Subjects 14 | { 15 | /// 16 | /// Represents an object that is both an observable sequence as well as an observer. 17 | /// Each notification is broadcasted to all subscribed observers. 18 | /// 19 | /// The type of the elements processed by the subject. 20 | [GeneratedCode("RxFree", "*")] 21 | [CompilerGenerated] 22 | [ExcludeFromCodeCoverage] 23 | partial class Subject : IObserver, IObservable, IDisposable 24 | { 25 | static readonly SubjectDisposable[] Terminated = new SubjectDisposable[0]; 26 | static readonly SubjectDisposable[] Disposed = new SubjectDisposable[0]; 27 | 28 | SubjectDisposable[] observers; 29 | Exception exception; 30 | 31 | /// 32 | /// Creates a subject. 33 | /// 34 | public Subject() => observers = Array.Empty(); 35 | 36 | /// 37 | /// Indicates whether the subject has observers subscribed to it. 38 | /// 39 | public virtual bool HasObservers => Volatile.Read(ref observers).Length != 0; 40 | 41 | /// 42 | /// Indicates whether the subject has been disposed. 43 | /// 44 | public virtual bool IsDisposed => Volatile.Read(ref this.observers) == Disposed; 45 | 46 | static void ThrowDisposed() => throw new ObjectDisposedException(string.Empty); 47 | 48 | /// 49 | /// Notifies all subscribed observers about the end of the sequence. 50 | /// 51 | public virtual void OnCompleted() 52 | { 53 | for (; ; ) 54 | { 55 | var observers = Volatile.Read(ref this.observers); 56 | if (observers == Disposed) 57 | { 58 | exception = null; 59 | ThrowDisposed(); 60 | break; 61 | } 62 | if (observers == Terminated) 63 | { 64 | break; 65 | } 66 | if (Interlocked.CompareExchange(ref this.observers, Terminated, observers) == observers) 67 | { 68 | foreach (var observer in observers) 69 | { 70 | observer.Observer?.OnCompleted(); 71 | } 72 | break; 73 | } 74 | } 75 | } 76 | 77 | /// 78 | /// Notifies all subscribed observers about the specified exception. 79 | /// 80 | /// The exception to send to all currently subscribed observers. 81 | /// is null. 82 | public virtual void OnError(Exception error) 83 | { 84 | if (error == null) 85 | { 86 | throw new ArgumentNullException(nameof(error)); 87 | } 88 | 89 | for (; ; ) 90 | { 91 | var observers = Volatile.Read(ref this.observers); 92 | if (observers == Disposed) 93 | { 94 | exception = null; 95 | ThrowDisposed(); 96 | break; 97 | } 98 | if (observers == Terminated) 99 | { 100 | break; 101 | } 102 | exception = error; 103 | if (Interlocked.CompareExchange(ref this.observers, Terminated, observers) == observers) 104 | { 105 | foreach (var observer in observers) 106 | { 107 | observer.Observer?.OnError(error); 108 | } 109 | break; 110 | } 111 | } 112 | } 113 | 114 | /// 115 | /// Notifies all subscribed observers about the arrival of the specified element in the sequence. 116 | /// 117 | /// The value to send to all currently subscribed observers. 118 | public virtual void OnNext(T value) 119 | { 120 | var observers = Volatile.Read(ref this.observers); 121 | if (observers == Disposed) 122 | { 123 | exception = null; 124 | ThrowDisposed(); 125 | return; 126 | } 127 | foreach (var observer in observers) 128 | { 129 | observer.Observer?.OnNext(value); 130 | } 131 | } 132 | 133 | /// 134 | /// Subscribes an observer to the subject. 135 | /// 136 | /// Observer to subscribe to the subject. 137 | /// Disposable object that can be used to unsubscribe the observer from the subject. 138 | /// is null. 139 | public virtual IDisposable Subscribe(IObserver observer) 140 | { 141 | if (observer == null) 142 | { 143 | throw new ArgumentNullException(nameof(observer)); 144 | } 145 | 146 | var disposable = default(SubjectDisposable); 147 | for (; ; ) 148 | { 149 | var observers = Volatile.Read(ref this.observers); 150 | if (observers == Disposed) 151 | { 152 | exception = null; 153 | ThrowDisposed(); 154 | break; 155 | } 156 | if (observers == Terminated) 157 | { 158 | var ex = exception; 159 | if (ex != null) 160 | { 161 | observer.OnError(ex); 162 | } 163 | else 164 | { 165 | observer.OnCompleted(); 166 | } 167 | break; 168 | } 169 | 170 | disposable ??= new SubjectDisposable(this, observer); 171 | 172 | var n = observers.Length; 173 | var b = new SubjectDisposable[n + 1]; 174 | 175 | Array.Copy(observers, 0, b, 0, n); 176 | 177 | b[n] = disposable; 178 | if (Interlocked.CompareExchange(ref this.observers, b, observers) == observers) 179 | { 180 | return disposable; 181 | } 182 | } 183 | 184 | return Disposable.Empty; 185 | } 186 | 187 | void Unsubscribe(SubjectDisposable observer) 188 | { 189 | for (; ; ) 190 | { 191 | var a = Volatile.Read(ref observers); 192 | var n = a.Length; 193 | if (n == 0) 194 | { 195 | break; 196 | } 197 | 198 | var j = Array.IndexOf(a, observer); 199 | 200 | if (j < 0) 201 | { 202 | break; 203 | } 204 | 205 | SubjectDisposable[] b; 206 | 207 | if (n == 1) 208 | { 209 | b = Array.Empty(); 210 | } 211 | else 212 | { 213 | b = new SubjectDisposable[n - 1]; 214 | Array.Copy(a, 0, b, 0, j); 215 | Array.Copy(a, j + 1, b, j, n - j - 1); 216 | } 217 | if (Interlocked.CompareExchange(ref observers, b, a) == a) 218 | { 219 | break; 220 | } 221 | } 222 | } 223 | 224 | class Disposable : IDisposable 225 | { 226 | public static IDisposable Empty { get; } = new Disposable(); 227 | 228 | Disposable() { } 229 | 230 | public void Dispose() { } 231 | } 232 | 233 | sealed class SubjectDisposable : IDisposable 234 | { 235 | Subject subject; 236 | volatile IObserver observer; 237 | 238 | public SubjectDisposable(Subject subject, IObserver observer) 239 | { 240 | this.subject = subject; 241 | this.observer = observer; 242 | } 243 | 244 | public IObserver Observer => observer; 245 | 246 | public void Dispose() 247 | { 248 | var observer = Interlocked.Exchange(ref this.observer, null); 249 | if (observer == null) 250 | { 251 | return; 252 | } 253 | 254 | subject.Unsubscribe(this); 255 | subject = null; 256 | } 257 | } 258 | 259 | /// 260 | /// Releases all resources used by the current instance of the class and unsubscribes all observers. 261 | /// 262 | public virtual void Dispose() 263 | { 264 | Interlocked.Exchange(ref observers, Disposed); 265 | exception = null; 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/AnonymousObserver.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.ExceptionServices; 2 | 3 | namespace System.Reactive 4 | { 5 | /// 6 | /// Create an instance from delegate-based implementations 7 | /// of the On* methods. 8 | /// 9 | /// The type of the elements in the sequence. 10 | partial class AnonymousObserver : IObserver 11 | { 12 | static readonly Action rethrow = e => ExceptionDispatchInfo.Capture(e).Throw(); 13 | static readonly Action nop = () => { }; 14 | 15 | readonly Action onNext; 16 | readonly Action onError; 17 | readonly Action onCompleted; 18 | 19 | /// 20 | /// Creates the observable providing just the action. 21 | /// 22 | public AnonymousObserver(Action onNext) 23 | : this(onNext, rethrow, nop) { } 24 | 25 | /// 26 | /// Creates the observable providing both the and 27 | /// actions. 28 | /// 29 | public AnonymousObserver(Action onNext, Action onError) 30 | : this(onNext, onError, nop) { } 31 | 32 | /// 33 | /// Creates the observable providing both the and 34 | /// actions. 35 | /// 36 | public AnonymousObserver(Action onNext, Action onCompleted) 37 | : this(onNext, rethrow, onCompleted) { } 38 | 39 | /// 40 | /// Creates the observable providing all three , 41 | /// and actions. 42 | /// 43 | public AnonymousObserver(Action onNext, Action onError, Action onCompleted) 44 | { 45 | this.onNext = onNext; 46 | this.onError = onError; 47 | this.onCompleted = onCompleted; 48 | } 49 | 50 | /// 51 | /// Calls the action implementing . 52 | /// 53 | public void OnCompleted() => onCompleted(); 54 | 55 | /// 56 | /// Calls the action implementing . 57 | /// 58 | public void OnError(Exception error) => onError(error); 59 | 60 | /// 61 | /// Calls the action implementing . 62 | /// 63 | public void OnNext(T value) => onNext(value); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/CompositeDisposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.CodeDom.Compiler; 4 | using System.Collections.Concurrent; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | 9 | namespace System.Reactive.Disposables 10 | { 11 | /// 12 | /// Represents a group of disposable resources that are disposed together. 13 | /// 14 | [GeneratedCode("RxFree", "*")] 15 | [CompilerGenerated] 16 | [ExcludeFromCodeCoverage] 17 | partial class CompositeDisposable : IDisposable 18 | { 19 | bool disposed; 20 | readonly ConcurrentBag disposables; 21 | 22 | /// 23 | /// Initializes a new instance of the class from a group of disposables. 24 | /// 25 | /// Disposables that will be disposed together. 26 | /// is . 27 | public CompositeDisposable(params IDisposable[] disposables) 28 | { 29 | this.disposables = new ConcurrentBag(disposables) ?? throw new ArgumentNullException(nameof(disposables)); 30 | } 31 | 32 | /// 33 | /// Gets a value that indicates whether the object is disposed. 34 | /// 35 | public bool IsDisposed => disposed; 36 | 37 | /// 38 | /// Adds a disposable to the or disposes the disposable if the is disposed. 39 | /// 40 | /// Disposable to add. 41 | public void Add(IDisposable disposable) 42 | { 43 | if (disposed) 44 | disposable?.Dispose(); 45 | else 46 | disposables.Add(disposable); 47 | } 48 | 49 | /// 50 | /// Disposes all disposables in the group and removes them from the group. 51 | /// 52 | public void Dispose() 53 | { 54 | if (disposed) 55 | return; 56 | 57 | disposed = true; 58 | while (disposables.TryTake(out var disposable)) 59 | { 60 | disposable?.Dispose(); 61 | } 62 | } 63 | 64 | /// 65 | /// Creates a new group of disposable resources that are disposed together. 66 | /// 67 | /// Disposable resources to add to the group. 68 | /// Group of disposable resources that are disposed together. 69 | public static IDisposable Create(params IDisposable[] disposables) => new DisposableArray(disposables); 70 | 71 | [ExcludeFromCodeCoverage] 72 | sealed class DisposableArray : IDisposable 73 | { 74 | IDisposable[]? disposables; 75 | 76 | public DisposableArray(IDisposable[] disposables) 77 | { 78 | Volatile.Write(ref this.disposables, disposables ?? throw new ArgumentNullException(nameof(disposables))); 79 | } 80 | 81 | public void Dispose() 82 | { 83 | var old = Interlocked.Exchange(ref disposables, null); 84 | if (old != null) 85 | { 86 | foreach (var disposable in old) 87 | { 88 | disposable?.Dispose(); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/Disposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Threading; 5 | 6 | namespace System.Reactive.Disposables 7 | { 8 | /// 9 | /// Provides a set of static methods for creating objects. 10 | /// 11 | [ExcludeFromCodeCoverage] 12 | static partial class Disposable 13 | { 14 | /// 15 | /// Gets the disposable that does nothing when disposed. 16 | /// 17 | public static IDisposable Empty { get; } = new EmptyDisposable(); 18 | 19 | /// 20 | /// Creates a disposable object that invokes the specified action when disposed. 21 | /// 22 | /// Action to run during the first call to . The action is guaranteed to be run at most once. 23 | /// The disposable object that runs the given action upon disposal. 24 | /// is null. 25 | public static IDisposable Create(Action dispose) => 26 | new AnonymousDisposable(dispose ?? throw new ArgumentNullException(nameof(dispose))); 27 | 28 | sealed class EmptyDisposable : IDisposable 29 | { 30 | public void Dispose() { } 31 | } 32 | 33 | /// 34 | /// Represents an Action-based disposable. 35 | /// 36 | internal sealed class AnonymousDisposable : IDisposable 37 | { 38 | volatile Action? dispose; 39 | 40 | /// 41 | /// Constructs a new disposable with the given action used for disposal. 42 | /// 43 | /// Disposal action which will be run upon calling Dispose. 44 | public AnonymousDisposable(Action dispose) => 45 | this.dispose = dispose; 46 | 47 | /// 48 | /// Calls the disposal action if and only if the current instance hasn't been disposed yet. 49 | /// 50 | public void Dispose() => 51 | Interlocked.Exchange(ref dispose, null)?.Invoke(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/ObservableExtensions.OfType.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | static partial class ObservableExtensions 6 | { 7 | /// 8 | /// Filters the elements of an observable sequence based on the specified type. 9 | /// 10 | /// The type to filter the elements in the source sequence on. 11 | /// The sequence that contains the elements to be filtered. 12 | /// is null. 13 | public static IObservable OfType(this IObservable source) where T : notnull 14 | => new OfTypeSubject(source ?? throw new ArgumentNullException(nameof(source))); 15 | 16 | class OfTypeSubject : Subject where T : notnull 17 | { 18 | IDisposable? subscription; 19 | 20 | public OfTypeSubject(IObservable source) 21 | { 22 | subscription = source.Subscribe( 23 | next => 24 | { 25 | if (next is T result) 26 | OnNext(result); 27 | }, 28 | OnError, 29 | OnCompleted); 30 | } 31 | 32 | public override void Dispose() 33 | { 34 | base.Dispose(); 35 | subscription?.Dispose(); 36 | subscription = null; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/ObservableExtensions.Select.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | partial class ObservableExtensions 6 | { 7 | /// 8 | /// Projects each element of a sequence into a new form. 9 | /// 10 | /// The type of the elements in the source sequence. 11 | /// The type of the elements in the result sequence, obtained by running the selector function for each element in the source sequence. 12 | /// A sequence of elements to invoke a transform function on. 13 | /// A transform function to apply to each source element. 14 | /// An sequence whose elements are the result of invoking the transform function on each element of source. 15 | /// or is null. 16 | public static IObservable Select(this IObservable source, Func selector) 17 | where TSource : notnull 18 | where TResult : notnull 19 | => new SelectorSubject(source ?? throw new ArgumentNullException(nameof(source)), selector ?? throw new ArgumentNullException(nameof(selector))); 20 | 21 | class SelectorSubject : Subject 22 | where TSource : notnull 23 | where TResult : notnull 24 | { 25 | IDisposable? subscription; 26 | Func? selector; 27 | 28 | public SelectorSubject(IObservable source, Func selector) 29 | { 30 | this.selector = selector; 31 | subscription = source.Subscribe( 32 | next => OnNext(this.selector(next)), 33 | OnError, 34 | OnCompleted); 35 | } 36 | 37 | public override void Dispose() 38 | { 39 | base.Dispose(); 40 | subscription?.Dispose(); 41 | subscription = null; 42 | selector = null; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/ObservableExtensions.Where.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | partial class ObservableExtensions 6 | { 7 | /// 8 | /// Filters the elements of an observable sequence based on the specified type. 9 | /// 10 | /// The type to filter the elements in the source sequence on. 11 | /// The sequence that contains the elements to be filtered. 12 | /// Predicate to apply to elements in for filtering. 13 | /// is null. 14 | public static IObservable Where(this IObservable source, Func predicate) where T : notnull 15 | => new WhereSubject(source ?? throw new ArgumentNullException(nameof(source)), predicate ?? throw new ArgumentNullException(nameof(predicate))); 16 | 17 | class WhereSubject : Subject where T : notnull 18 | { 19 | IDisposable? subscription; 20 | Func? predicate; 21 | 22 | public WhereSubject(IObservable source, Func predicate) 23 | { 24 | this.predicate = predicate; 25 | subscription = source.Subscribe( 26 | next => 27 | { 28 | if (this.predicate(next)) 29 | OnNext(next); 30 | }, 31 | OnError, 32 | OnCompleted); 33 | } 34 | 35 | public override void Dispose() 36 | { 37 | base.Dispose(); 38 | subscription?.Dispose(); 39 | subscription = null; 40 | predicate = null; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/ObservableExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.CodeDom.Compiler; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Reactive; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.ExceptionServices; 8 | 9 | namespace System 10 | { 11 | /// 12 | /// Provides a set of static methods for subscribing delegates to observables. 13 | /// 14 | [GeneratedCode("RxFree", "*")] 15 | [CompilerGenerated] 16 | [ExcludeFromCodeCoverage] 17 | static partial class ObservableExtensions 18 | { 19 | static readonly Action rethrow = e => ExceptionDispatchInfo.Capture(e).Throw(); 20 | static readonly Action nop = () => { }; 21 | 22 | /// 23 | /// Subscribes to the observable providing just the delegate. 24 | /// 25 | public static IDisposable Subscribe(this IObservable source, Action onNext) 26 | => Subscribe(source, onNext, rethrow, nop); 27 | 28 | /// 29 | /// Subscribes to the observable providing both the and 30 | /// delegates. 31 | /// 32 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onError) 33 | => Subscribe(source, onNext, onError, nop); 34 | 35 | /// 36 | /// Subscribes to the observable providing both the and 37 | /// delegates. 38 | /// 39 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onCompleted) 40 | => Subscribe(source, onNext, rethrow, onCompleted); 41 | 42 | /// 43 | /// Subscribes to the observable providing all three , 44 | /// and delegates. 45 | /// 46 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onError, Action onCompleted) 47 | => source.Subscribe(new AnonymousObserver(onNext, onError, onCompleted)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/StableCompositeDisposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.ComponentModel; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace System.Reactive.Disposables 7 | { 8 | /// 9 | /// For compatibility with Rx 10 | /// 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | [ExcludeFromCodeCoverage] 13 | partial class StableCompositeDisposable 14 | { 15 | /// 16 | /// For compatibility with Rx 17 | /// 18 | [EditorBrowsable(EditorBrowsableState.Never)] 19 | public static IDisposable Create(params IDisposable[] disposables) => CompositeDisposable.Create(disposables); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/RxFree/build/NotNull/Subject.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | // Licensed to the .NET Foundation under one or more agreements. 4 | // The .NET Foundation licenses this file to you under the MIT License. 5 | 6 | // https://github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/System.Reactive/Subjects/Subject.cs 7 | // https://github.com/dotnet/reactive/blob/main/LICENSE 8 | 9 | using System.CodeDom.Compiler; 10 | using System.Diagnostics.CodeAnalysis; 11 | using System.Runtime.CompilerServices; 12 | using System.Threading; 13 | 14 | namespace System.Reactive.Subjects 15 | { 16 | /// 17 | /// Represents an object that is both an observable sequence as well as an observer. 18 | /// Each notification is broadcasted to all subscribed observers. 19 | /// 20 | /// The type of the elements processed by the subject. 21 | [GeneratedCode("RxFree", "*")] 22 | [CompilerGenerated] 23 | [ExcludeFromCodeCoverage] 24 | partial class Subject : IObserver, IObservable, IDisposable where T : notnull 25 | { 26 | static readonly SubjectDisposable[] Terminated = new SubjectDisposable[0]; 27 | static readonly SubjectDisposable[] Disposed = new SubjectDisposable[0]; 28 | 29 | SubjectDisposable[] observers; 30 | Exception? exception; 31 | 32 | /// 33 | /// Creates a subject. 34 | /// 35 | public Subject() => observers = Array.Empty(); 36 | 37 | /// 38 | /// Indicates whether the subject has observers subscribed to it. 39 | /// 40 | public bool HasObservers => Volatile.Read(ref observers).Length != 0; 41 | 42 | /// 43 | /// Indicates whether the subject has been disposed. 44 | /// 45 | public virtual bool IsDisposed => Volatile.Read(ref this.observers) == Disposed; 46 | 47 | static void ThrowDisposed() => throw new ObjectDisposedException(string.Empty); 48 | 49 | /// 50 | /// Notifies all subscribed observers about the end of the sequence. 51 | /// 52 | public virtual void OnCompleted() 53 | { 54 | for (; ; ) 55 | { 56 | var observers = Volatile.Read(ref this.observers); 57 | if (observers == Disposed) 58 | { 59 | exception = null; 60 | ThrowDisposed(); 61 | break; 62 | } 63 | if (observers == Terminated) 64 | { 65 | break; 66 | } 67 | if (Interlocked.CompareExchange(ref this.observers, Terminated, observers) == observers) 68 | { 69 | foreach (var observer in observers) 70 | { 71 | observer.Observer?.OnCompleted(); 72 | } 73 | break; 74 | } 75 | } 76 | } 77 | 78 | /// 79 | /// Notifies all subscribed observers about the specified exception. 80 | /// 81 | /// The exception to send to all currently subscribed observers. 82 | /// is null. 83 | public virtual void OnError(Exception error) 84 | { 85 | if (error == null) 86 | { 87 | throw new ArgumentNullException(nameof(error)); 88 | } 89 | 90 | for (; ; ) 91 | { 92 | var observers = Volatile.Read(ref this.observers); 93 | if (observers == Disposed) 94 | { 95 | exception = null; 96 | ThrowDisposed(); 97 | break; 98 | } 99 | if (observers == Terminated) 100 | { 101 | break; 102 | } 103 | exception = error; 104 | if (Interlocked.CompareExchange(ref this.observers, Terminated, observers) == observers) 105 | { 106 | foreach (var observer in observers) 107 | { 108 | observer.Observer?.OnError(error); 109 | } 110 | break; 111 | } 112 | } 113 | } 114 | 115 | /// 116 | /// Notifies all subscribed observers about the arrival of the specified element in the sequence. 117 | /// 118 | /// The value to send to all currently subscribed observers. 119 | public virtual void OnNext(T value) 120 | { 121 | var observers = Volatile.Read(ref this.observers); 122 | if (observers == Disposed) 123 | { 124 | exception = null; 125 | ThrowDisposed(); 126 | return; 127 | } 128 | foreach (var observer in observers) 129 | { 130 | observer.Observer?.OnNext(value); 131 | } 132 | } 133 | 134 | /// 135 | /// Subscribes an observer to the subject. 136 | /// 137 | /// Observer to subscribe to the subject. 138 | /// Disposable object that can be used to unsubscribe the observer from the subject. 139 | /// is null. 140 | public virtual IDisposable Subscribe(IObserver observer) 141 | { 142 | if (observer == null) 143 | { 144 | throw new ArgumentNullException(nameof(observer)); 145 | } 146 | 147 | var disposable = default(SubjectDisposable); 148 | for (; ; ) 149 | { 150 | var observers = Volatile.Read(ref this.observers); 151 | if (observers == Disposed) 152 | { 153 | exception = null; 154 | ThrowDisposed(); 155 | break; 156 | } 157 | if (observers == Terminated) 158 | { 159 | var ex = exception; 160 | if (ex != null) 161 | { 162 | observer.OnError(ex); 163 | } 164 | else 165 | { 166 | observer.OnCompleted(); 167 | } 168 | break; 169 | } 170 | 171 | disposable ??= new SubjectDisposable(this, observer); 172 | 173 | var n = observers.Length; 174 | var b = new SubjectDisposable[n + 1]; 175 | 176 | Array.Copy(observers, 0, b, 0, n); 177 | 178 | b[n] = disposable; 179 | if (Interlocked.CompareExchange(ref this.observers, b, observers) == observers) 180 | { 181 | return disposable; 182 | } 183 | } 184 | 185 | return Disposable.Empty; 186 | } 187 | 188 | void Unsubscribe(SubjectDisposable observer) 189 | { 190 | for (; ; ) 191 | { 192 | var a = Volatile.Read(ref observers); 193 | var n = a.Length; 194 | if (n == 0) 195 | { 196 | break; 197 | } 198 | 199 | var j = Array.IndexOf(a, observer); 200 | 201 | if (j < 0) 202 | { 203 | break; 204 | } 205 | 206 | SubjectDisposable[] b; 207 | 208 | if (n == 1) 209 | { 210 | b = Array.Empty(); 211 | } 212 | else 213 | { 214 | b = new SubjectDisposable[n - 1]; 215 | Array.Copy(a, 0, b, 0, j); 216 | Array.Copy(a, j + 1, b, j, n - j - 1); 217 | } 218 | if (Interlocked.CompareExchange(ref observers, b, a) == a) 219 | { 220 | break; 221 | } 222 | } 223 | } 224 | 225 | class Disposable : IDisposable 226 | { 227 | public static IDisposable Empty { get; } = new Disposable(); 228 | 229 | Disposable() { } 230 | 231 | public void Dispose() { } 232 | } 233 | 234 | sealed class SubjectDisposable : IDisposable 235 | { 236 | Subject subject; 237 | volatile IObserver? observer; 238 | 239 | public SubjectDisposable(Subject subject, IObserver observer) 240 | { 241 | this.subject = subject; 242 | this.observer = observer; 243 | } 244 | 245 | public IObserver? Observer => observer; 246 | 247 | public void Dispose() 248 | { 249 | var observer = Interlocked.Exchange(ref this.observer, null); 250 | if (observer == null) 251 | { 252 | return; 253 | } 254 | 255 | subject.Unsubscribe(this); 256 | subject = null!; 257 | } 258 | } 259 | 260 | /// 261 | /// Releases all resources used by the current instance of the class and unsubscribes all observers. 262 | /// 263 | public virtual void Dispose() 264 | { 265 | Interlocked.Exchange(ref observers, Disposed); 266 | exception = null; 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/AnonymousObserver.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.ExceptionServices; 2 | 3 | namespace System.Reactive 4 | { 5 | /// 6 | /// Create an instance from delegate-based implementations 7 | /// of the On* methods. 8 | /// 9 | /// The type of the elements in the sequence. 10 | partial class AnonymousObserver : IObserver 11 | { 12 | static readonly Action rethrow = e => ExceptionDispatchInfo.Capture(e).Throw(); 13 | static readonly Action nop = () => { }; 14 | 15 | readonly Action onNext; 16 | readonly Action onError; 17 | readonly Action onCompleted; 18 | 19 | /// 20 | /// Creates the observable providing just the action. 21 | /// 22 | public AnonymousObserver(Action onNext) 23 | : this(onNext, rethrow, nop) { } 24 | 25 | /// 26 | /// Creates the observable providing both the and 27 | /// actions. 28 | /// 29 | public AnonymousObserver(Action onNext, Action onError) 30 | : this(onNext, onError, nop) { } 31 | 32 | /// 33 | /// Creates the observable providing both the and 34 | /// actions. 35 | /// 36 | public AnonymousObserver(Action onNext, Action onCompleted) 37 | : this(onNext, rethrow, onCompleted) { } 38 | 39 | /// 40 | /// Creates the observable providing all three , 41 | /// and actions. 42 | /// 43 | public AnonymousObserver(Action onNext, Action onError, Action onCompleted) 44 | { 45 | this.onNext = onNext; 46 | this.onError = onError; 47 | this.onCompleted = onCompleted; 48 | } 49 | 50 | /// 51 | /// Calls the action implementing . 52 | /// 53 | public void OnCompleted() => onCompleted(); 54 | 55 | /// 56 | /// Calls the action implementing . 57 | /// 58 | public void OnError(Exception error) => onError(error); 59 | 60 | /// 61 | /// Calls the action implementing . 62 | /// 63 | public void OnNext(T value) => onNext(value); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/CompositeDisposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.CodeDom.Compiler; 4 | using System.Collections.Concurrent; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | 9 | namespace System.Reactive.Disposables 10 | { 11 | /// 12 | /// Represents a group of disposable resources that are disposed together. 13 | /// 14 | [GeneratedCode("RxFree", "*")] 15 | [CompilerGenerated] 16 | [ExcludeFromCodeCoverage] 17 | partial class CompositeDisposable : IDisposable 18 | { 19 | bool disposed; 20 | readonly ConcurrentBag disposables; 21 | 22 | /// 23 | /// Initializes a new instance of the class from a group of disposables. 24 | /// 25 | /// Disposables that will be disposed together. 26 | /// is . 27 | public CompositeDisposable(params IDisposable[] disposables) 28 | { 29 | this.disposables = new ConcurrentBag(disposables) ?? throw new ArgumentNullException(nameof(disposables)); 30 | } 31 | 32 | /// 33 | /// Gets a value that indicates whether the object is disposed. 34 | /// 35 | public bool IsDisposed => disposed; 36 | 37 | /// 38 | /// Adds a disposable to the or disposes the disposable if the is disposed. 39 | /// 40 | /// Disposable to add. 41 | public void Add(IDisposable disposable) 42 | { 43 | if (disposed) 44 | disposable?.Dispose(); 45 | else 46 | disposables.Add(disposable); 47 | } 48 | 49 | /// 50 | /// Disposes all disposables in the group and removes them from the group. 51 | /// 52 | public void Dispose() 53 | { 54 | if (disposed) 55 | return; 56 | 57 | disposed = true; 58 | while (disposables.TryTake(out var disposable)) 59 | { 60 | disposable?.Dispose(); 61 | } 62 | } 63 | 64 | /// 65 | /// Creates a new group of disposable resources that are disposed together. 66 | /// 67 | /// Disposable resources to add to the group. 68 | /// Group of disposable resources that are disposed together. 69 | public static IDisposable Create(params IDisposable[] disposables) => new DisposableArray(disposables); 70 | 71 | [ExcludeFromCodeCoverage] 72 | sealed class DisposableArray : IDisposable 73 | { 74 | IDisposable[]? disposables; 75 | 76 | public DisposableArray(IDisposable[] disposables) 77 | { 78 | Volatile.Write(ref this.disposables, disposables ?? throw new ArgumentNullException(nameof(disposables))); 79 | } 80 | 81 | public void Dispose() 82 | { 83 | var old = Interlocked.Exchange(ref disposables, null); 84 | if (old != null) 85 | { 86 | foreach (var disposable in old) 87 | { 88 | disposable?.Dispose(); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/Disposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Threading; 5 | 6 | namespace System.Reactive.Disposables 7 | { 8 | /// 9 | /// Provides a set of static methods for creating objects. 10 | /// 11 | [ExcludeFromCodeCoverage] 12 | static partial class Disposable 13 | { 14 | /// 15 | /// Gets the disposable that does nothing when disposed. 16 | /// 17 | public static IDisposable Empty { get; } = new EmptyDisposable(); 18 | 19 | /// 20 | /// Creates a disposable object that invokes the specified action when disposed. 21 | /// 22 | /// Action to run during the first call to . The action is guaranteed to be run at most once. 23 | /// The disposable object that runs the given action upon disposal. 24 | /// is null. 25 | public static IDisposable Create(Action dispose) => 26 | new AnonymousDisposable(dispose ?? throw new ArgumentNullException(nameof(dispose))); 27 | 28 | sealed class EmptyDisposable : IDisposable 29 | { 30 | public void Dispose() { } 31 | } 32 | 33 | /// 34 | /// Represents an Action-based disposable. 35 | /// 36 | internal sealed class AnonymousDisposable : IDisposable 37 | { 38 | volatile Action? dispose; 39 | 40 | /// 41 | /// Constructs a new disposable with the given action used for disposal. 42 | /// 43 | /// Disposal action which will be run upon calling Dispose. 44 | public AnonymousDisposable(Action dispose) => 45 | this.dispose = dispose; 46 | 47 | /// 48 | /// Calls the disposal action if and only if the current instance hasn't been disposed yet. 49 | /// 50 | public void Dispose() => 51 | Interlocked.Exchange(ref dispose, null)?.Invoke(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/ObservableExtensions.OfType.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | static partial class ObservableExtensions 6 | { 7 | /// 8 | /// Filters the elements of an observable sequence based on the specified type. 9 | /// 10 | /// The type to filter the elements in the source sequence on. 11 | /// The sequence that contains the elements to be filtered. 12 | /// is null. 13 | public static IObservable OfType(this IObservable source) 14 | => new OfTypeSubject(source ?? throw new ArgumentNullException(nameof(source))); 15 | 16 | class OfTypeSubject : Subject 17 | { 18 | IDisposable? subscription; 19 | 20 | public OfTypeSubject(IObservable source) 21 | { 22 | subscription = source.Subscribe( 23 | next => 24 | { 25 | if (next is T result) 26 | OnNext(result); 27 | }, 28 | OnError, 29 | OnCompleted); 30 | } 31 | 32 | public override void Dispose() 33 | { 34 | base.Dispose(); 35 | subscription?.Dispose(); 36 | subscription = null; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/ObservableExtensions.Select.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | partial class ObservableExtensions 6 | { 7 | /// 8 | /// Projects each element of a sequence into a new form. 9 | /// 10 | /// The type of the elements in the source sequence. 11 | /// The type of the elements in the result sequence, obtained by running the selector function for each element in the source sequence. 12 | /// A sequence of elements to invoke a transform function on. 13 | /// A transform function to apply to each source element. 14 | /// An sequence whose elements are the result of invoking the transform function on each element of source. 15 | /// or is null. 16 | public static IObservable Select(this IObservable source, Func selector) 17 | => new SelectorSubject(source ?? throw new ArgumentNullException(nameof(source)), selector ?? throw new ArgumentNullException(nameof(selector))); 18 | 19 | class SelectorSubject : Subject 20 | { 21 | IDisposable? subscription; 22 | Func? selector; 23 | 24 | public SelectorSubject(IObservable source, Func selector) 25 | { 26 | this.selector = selector; 27 | subscription = source.Subscribe( 28 | next => OnNext(this.selector(next)), 29 | OnError, 30 | OnCompleted); 31 | } 32 | 33 | public override void Dispose() 34 | { 35 | base.Dispose(); 36 | subscription?.Dispose(); 37 | subscription = null; 38 | selector = null; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/ObservableExtensions.Where.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive.Subjects; 2 | 3 | namespace System.Reactive.Linq 4 | { 5 | partial class ObservableExtensions 6 | { 7 | /// 8 | /// Filters the elements of an observable sequence based on the specified type. 9 | /// 10 | /// The type to filter the elements in the source sequence on. 11 | /// The sequence that contains the elements to be filtered. 12 | /// Predicate to apply to elements in for filtering. 13 | /// is null. 14 | public static IObservable Where(this IObservable source, Func predicate) 15 | => new WhereSubject(source ?? throw new ArgumentNullException(nameof(source)), predicate ?? throw new ArgumentNullException(nameof(predicate))); 16 | 17 | class WhereSubject : Subject 18 | { 19 | IDisposable? subscription; 20 | Func? predicate; 21 | 22 | public WhereSubject(IObservable source, Func predicate) 23 | { 24 | this.predicate = predicate; 25 | subscription = source.Subscribe( 26 | next => 27 | { 28 | if (this.predicate(next)) 29 | OnNext(next); 30 | }, 31 | OnError, 32 | OnCompleted); 33 | } 34 | 35 | public override void Dispose() 36 | { 37 | base.Dispose(); 38 | subscription?.Dispose(); 39 | subscription = null; 40 | predicate = null; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/ObservableExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.CodeDom.Compiler; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Reactive; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.ExceptionServices; 8 | 9 | namespace System 10 | { 11 | /// 12 | /// Provides a set of static methods for subscribing delegates to observables. 13 | /// 14 | [GeneratedCode("RxFree", "*")] 15 | [CompilerGenerated] 16 | [ExcludeFromCodeCoverage] 17 | static partial class ObservableExtensions 18 | { 19 | static readonly Action rethrow = e => ExceptionDispatchInfo.Capture(e).Throw(); 20 | static readonly Action nop = () => { }; 21 | 22 | /// 23 | /// Subscribes to the observable providing just the delegate. 24 | /// 25 | public static IDisposable Subscribe(this IObservable source, Action onNext) 26 | => Subscribe(source, onNext, rethrow, nop); 27 | 28 | /// 29 | /// Subscribes to the observable providing both the and 30 | /// delegates. 31 | /// 32 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onError) 33 | => Subscribe(source, onNext, onError, nop); 34 | 35 | /// 36 | /// Subscribes to the observable providing both the and 37 | /// delegates. 38 | /// 39 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onCompleted) 40 | => Subscribe(source, onNext, rethrow, onCompleted); 41 | 42 | /// 43 | /// Subscribes to the observable providing all three , 44 | /// and delegates. 45 | /// 46 | public static IDisposable Subscribe(this IObservable source, Action onNext, Action onError, Action onCompleted) 47 | => source.Subscribe(new AnonymousObserver(onNext, onError, onCompleted)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/StableCompositeDisposable.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | using System.ComponentModel; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace System.Reactive.Disposables 7 | { 8 | /// 9 | /// For compatibility with Rx 10 | /// 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | [ExcludeFromCodeCoverage] 13 | partial class StableCompositeDisposable 14 | { 15 | /// 16 | /// For compatibility with Rx 17 | /// 18 | [EditorBrowsable(EditorBrowsableState.Never)] 19 | public static IDisposable Create(params IDisposable[] disposables) => CompositeDisposable.Create(disposables); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/RxFree/build/Nullable/Subject.cs: -------------------------------------------------------------------------------- 1 | // 2 | #nullable enable 3 | // Licensed to the .NET Foundation under one or more agreements. 4 | // The .NET Foundation licenses this file to you under the MIT License. 5 | 6 | // https://github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/System.Reactive/Subjects/Subject.cs 7 | // https://github.com/dotnet/reactive/blob/main/LICENSE 8 | 9 | using System.CodeDom.Compiler; 10 | using System.Diagnostics.CodeAnalysis; 11 | using System.Runtime.CompilerServices; 12 | using System.Threading; 13 | 14 | namespace System.Reactive.Subjects 15 | { 16 | /// 17 | /// Represents an object that is both an observable sequence as well as an observer. 18 | /// Each notification is broadcasted to all subscribed observers. 19 | /// 20 | /// The type of the elements processed by the subject. 21 | [GeneratedCode("RxFree", "*")] 22 | [CompilerGenerated] 23 | [ExcludeFromCodeCoverage] 24 | partial class Subject : IObserver, IObservable, IDisposable 25 | { 26 | static readonly SubjectDisposable[] Terminated = new SubjectDisposable[0]; 27 | static readonly SubjectDisposable[] Disposed = new SubjectDisposable[0]; 28 | 29 | SubjectDisposable[] observers; 30 | Exception? exception; 31 | 32 | /// 33 | /// Creates a subject. 34 | /// 35 | public Subject() => observers = Array.Empty(); 36 | 37 | /// 38 | /// Indicates whether the subject has observers subscribed to it. 39 | /// 40 | public virtual bool HasObservers => Volatile.Read(ref observers).Length != 0; 41 | 42 | /// 43 | /// Indicates whether the subject has been disposed. 44 | /// 45 | public virtual bool IsDisposed => Volatile.Read(ref this.observers) == Disposed; 46 | 47 | static void ThrowDisposed() => throw new ObjectDisposedException(string.Empty); 48 | 49 | /// 50 | /// Notifies all subscribed observers about the end of the sequence. 51 | /// 52 | public virtual void OnCompleted() 53 | { 54 | for (; ; ) 55 | { 56 | var observers = Volatile.Read(ref this.observers); 57 | if (observers == Disposed) 58 | { 59 | exception = null; 60 | ThrowDisposed(); 61 | break; 62 | } 63 | if (observers == Terminated) 64 | { 65 | break; 66 | } 67 | if (Interlocked.CompareExchange(ref this.observers, Terminated, observers) == observers) 68 | { 69 | foreach (var observer in observers) 70 | { 71 | observer.Observer?.OnCompleted(); 72 | } 73 | break; 74 | } 75 | } 76 | } 77 | 78 | /// 79 | /// Notifies all subscribed observers about the specified exception. 80 | /// 81 | /// The exception to send to all currently subscribed observers. 82 | /// is null. 83 | public virtual void OnError(Exception error) 84 | { 85 | if (error == null) 86 | { 87 | throw new ArgumentNullException(nameof(error)); 88 | } 89 | 90 | for (; ; ) 91 | { 92 | var observers = Volatile.Read(ref this.observers); 93 | if (observers == Disposed) 94 | { 95 | exception = null; 96 | ThrowDisposed(); 97 | break; 98 | } 99 | if (observers == Terminated) 100 | { 101 | break; 102 | } 103 | exception = error; 104 | if (Interlocked.CompareExchange(ref this.observers, Terminated, observers) == observers) 105 | { 106 | foreach (var observer in observers) 107 | { 108 | observer.Observer?.OnError(error); 109 | } 110 | break; 111 | } 112 | } 113 | } 114 | 115 | /// 116 | /// Notifies all subscribed observers about the arrival of the specified element in the sequence. 117 | /// 118 | /// The value to send to all currently subscribed observers. 119 | public virtual void OnNext(T value) 120 | { 121 | var observers = Volatile.Read(ref this.observers); 122 | if (observers == Disposed) 123 | { 124 | exception = null; 125 | ThrowDisposed(); 126 | return; 127 | } 128 | foreach (var observer in observers) 129 | { 130 | observer.Observer?.OnNext(value); 131 | } 132 | } 133 | 134 | /// 135 | /// Subscribes an observer to the subject. 136 | /// 137 | /// Observer to subscribe to the subject. 138 | /// Disposable object that can be used to unsubscribe the observer from the subject. 139 | /// is null. 140 | public virtual IDisposable Subscribe(IObserver observer) 141 | { 142 | if (observer == null) 143 | { 144 | throw new ArgumentNullException(nameof(observer)); 145 | } 146 | 147 | var disposable = default(SubjectDisposable); 148 | for (; ; ) 149 | { 150 | var observers = Volatile.Read(ref this.observers); 151 | if (observers == Disposed) 152 | { 153 | exception = null; 154 | ThrowDisposed(); 155 | break; 156 | } 157 | if (observers == Terminated) 158 | { 159 | var ex = exception; 160 | if (ex != null) 161 | { 162 | observer.OnError(ex); 163 | } 164 | else 165 | { 166 | observer.OnCompleted(); 167 | } 168 | break; 169 | } 170 | 171 | disposable ??= new SubjectDisposable(this, observer); 172 | 173 | var n = observers.Length; 174 | var b = new SubjectDisposable[n + 1]; 175 | 176 | Array.Copy(observers, 0, b, 0, n); 177 | 178 | b[n] = disposable; 179 | if (Interlocked.CompareExchange(ref this.observers, b, observers) == observers) 180 | { 181 | return disposable; 182 | } 183 | } 184 | 185 | return Disposable.Empty; 186 | } 187 | 188 | void Unsubscribe(SubjectDisposable observer) 189 | { 190 | for (; ; ) 191 | { 192 | var a = Volatile.Read(ref observers); 193 | var n = a.Length; 194 | if (n == 0) 195 | { 196 | break; 197 | } 198 | 199 | var j = Array.IndexOf(a, observer); 200 | 201 | if (j < 0) 202 | { 203 | break; 204 | } 205 | 206 | SubjectDisposable[] b; 207 | 208 | if (n == 1) 209 | { 210 | b = Array.Empty(); 211 | } 212 | else 213 | { 214 | b = new SubjectDisposable[n - 1]; 215 | Array.Copy(a, 0, b, 0, j); 216 | Array.Copy(a, j + 1, b, j, n - j - 1); 217 | } 218 | if (Interlocked.CompareExchange(ref observers, b, a) == a) 219 | { 220 | break; 221 | } 222 | } 223 | } 224 | 225 | class Disposable : IDisposable 226 | { 227 | public static IDisposable Empty { get; } = new Disposable(); 228 | 229 | Disposable() { } 230 | 231 | public void Dispose() { } 232 | } 233 | 234 | sealed class SubjectDisposable : IDisposable 235 | { 236 | Subject subject; 237 | volatile IObserver? observer; 238 | 239 | public SubjectDisposable(Subject subject, IObserver observer) 240 | { 241 | this.subject = subject; 242 | this.observer = observer; 243 | } 244 | 245 | public IObserver? Observer => observer; 246 | 247 | public void Dispose() 248 | { 249 | var observer = Interlocked.Exchange(ref this.observer, null); 250 | if (observer == null) 251 | { 252 | return; 253 | } 254 | 255 | subject.Unsubscribe(this); 256 | subject = null!; 257 | } 258 | } 259 | 260 | /// 261 | /// Releases all resources used by the current instance of the class and unsubscribes all observers. 262 | /// 263 | public virtual void Dispose() 264 | { 265 | Interlocked.Exchange(ref observers, Disposed); 266 | exception = null; 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/RxFree/build/RxFree.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/RxFree/build/RxFree.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | false 5 | <_RxFlavor>Legacy 6 | <_RxFlavor Condition="'$(Nullable)' == 'enable' And '$(UseNotNullRx)' == 'true'">NotNull 7 | <_RxFlavor Condition="'$(Nullable)' == 'enable' And '$(UseNotNullRx)' != 'true'">Nullable 8 | 9 | 10 | 11 | 12 | false 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/RxFree/readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlooped/RxFree/8c964e3ef83fff4458948c36d79a6e5b7f79487b/src/icon.png -------------------------------------------------------------------------------- /src/kzu.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devlooped/RxFree/8c964e3ef83fff4458948c36d79a6e5b7f79487b/src/kzu.snk -------------------------------------------------------------------------------- /src/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------