├── .gitignore ├── README.md ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── ix-core │ ├── bsconfig.json │ ├── package.json │ └── src │ │ ├── IxIterable.re │ │ ├── IxIterable.rei │ │ ├── IxIterator.re │ │ └── IxIterator.rei ├── reunit │ ├── bsconfig.json │ ├── package.json │ └── src │ │ ├── ReUnit.re │ │ ├── ReUnit.rei │ │ └── platform │ │ ├── js │ │ └── ExnHelpers.re │ │ └── ocaml │ │ └── ExnHelpers.re ├── rx-core │ ├── README.md │ ├── bsconfig.json │ ├── package.json │ ├── src │ │ ├── RxNotification.re │ │ ├── RxNotification.rei │ │ ├── RxObservable.re │ │ ├── RxObservable.rei │ │ ├── RxObserverLike.re │ │ ├── RxOperator.re │ │ ├── RxSubscriber.re │ │ └── RxSubscriber.rei │ └── test │ │ ├── RxObservableTest.re │ │ ├── RxSubscriberTest.re │ │ └── TestRunner.re ├── rx-disposables │ ├── README.md │ ├── bsconfig.json │ ├── package.json │ ├── src │ │ ├── RxCompositeDisposable.re │ │ ├── RxCompositeDisposable.rei │ │ ├── RxCompositeDisposableLike.re │ │ ├── RxConnectableLike.re │ │ ├── RxDisposable.re │ │ ├── RxDisposable.rei │ │ ├── RxDisposableLike.re │ │ ├── RxSerialDisposable.re │ │ ├── RxSerialDisposable.rei │ │ └── RxSerialDisposableLike.re │ └── test │ │ ├── RxCompositeDisposableTest.re │ │ ├── RxDisposableTest.re │ │ ├── RxSerialDisposableTest.re │ │ └── TestRunner.re ├── rx-imperative │ ├── bsconfig.json │ ├── package.json │ ├── src │ │ ├── RxEvent.re │ │ ├── RxEvent.rei │ │ ├── RxEventLike.re │ │ ├── RxSubject.re │ │ ├── RxSubject.rei │ │ ├── RxSubjectLike.re │ │ ├── RxValue.re │ │ ├── RxValue.rei │ │ └── RxValueLike.re │ └── test │ │ ├── RxEventTest.re │ │ ├── RxSubjectTest.re │ │ ├── RxValueTest.re │ │ └── TestRunner.re ├── rx-observables │ ├── bsconfig.json │ ├── package.json │ ├── src │ │ ├── RxEmptyException.re │ │ ├── RxObservables.re │ │ ├── RxObservables.rei │ │ ├── RxTimeoutException.re │ │ └── internal │ │ │ ├── observables │ │ │ ├── CombineLatest2Observable.re │ │ │ ├── ConcatListObservable.re │ │ │ ├── ConcatMapObservable.re │ │ │ ├── ConcatObservable.re │ │ │ ├── DebounceTimeObservable.re │ │ │ ├── DefaultIfEmptyObservable.re │ │ │ ├── DeferObservable.re │ │ │ ├── DelayObservable.re │ │ │ ├── DematerializeObservable.re │ │ │ ├── DistinctUntilChangedObservable.re │ │ │ ├── EmptyObservable.re │ │ │ ├── EveryObservable.re │ │ │ ├── ExhaustMapObservable.re │ │ │ ├── ExhaustObservable.re │ │ │ ├── FindObservable.re │ │ │ ├── FirstObservable.re │ │ │ ├── FirstOrNoneObservable.re │ │ │ ├── IgnoreElementsObservable.re │ │ │ ├── IsEmptyObservable.re │ │ │ ├── KeepObservable.re │ │ │ ├── LastObservable.re │ │ │ ├── LastOrNoneObservable.re │ │ │ ├── MapObservable.re │ │ │ ├── MapToObservable.re │ │ │ ├── MaterializeObservable.re │ │ │ ├── MaybeFirstObservable.re │ │ │ ├── MaybeLastObservable.re │ │ │ ├── MergeListObservable.re │ │ │ ├── MergeMapObservable.re │ │ │ ├── MergeObservable.re │ │ │ ├── NoneObservable.re │ │ │ ├── ObserveObservable.re │ │ │ ├── ObserveOnObservable.re │ │ │ ├── OfListObservable.re │ │ │ ├── OfNotificationsObservable.re │ │ │ ├── OfValueObservable.re │ │ │ ├── OnCompleteObservable.re │ │ │ ├── OnConnectObservable.re │ │ │ ├── OnExnObservable.re │ │ │ ├── OnNextObservable.re │ │ │ ├── PublishToSubjectObservable.re │ │ │ ├── PublishToSubscriberObservable.re │ │ │ ├── RaiseObservable.re │ │ │ ├── RepeatObservable.re │ │ │ ├── RetryObservable.re │ │ │ ├── ScanObservable.re │ │ │ ├── ShareObservable.re │ │ │ ├── ShareReplayBufferObservable.re │ │ │ ├── ShareReplayLast.re │ │ │ ├── ShareWithSubjectFactoryObservable.re │ │ │ ├── SkipObservable.re │ │ │ ├── SomeObservable.re │ │ │ ├── StartWithListObservable.re │ │ │ ├── StartWithValueObservable.re │ │ │ ├── SubscribeOnObservable.re │ │ │ ├── SwitchMapObservable.re │ │ │ ├── SwitchObservable.re │ │ │ ├── TakeObservable.re │ │ │ ├── TakeUntilObservable.re │ │ │ ├── TimeoutObservable.re │ │ │ ├── UsingObservable.re │ │ │ └── WithLatestFromObservable.re │ │ │ └── operators │ │ │ ├── ConcatMapOperator.re │ │ │ ├── ConcatOperator.re │ │ │ ├── DebounceTimeOperator.re │ │ │ ├── DefaultIfEmptyOperator.re │ │ │ ├── DelayOperator.re │ │ │ ├── DematerializeOperator.re │ │ │ ├── DistinctUntilChangedOperator.re │ │ │ ├── EveryOperator.re │ │ │ ├── ExhaustMapOperator.re │ │ │ ├── ExhaustOperator.re │ │ │ ├── FindOperator.re │ │ │ ├── FirstOperator.re │ │ │ ├── FirstOrNoneOperator.re │ │ │ ├── IgnoreElementsOperator.re │ │ │ ├── IsEmptyOperator.re │ │ │ ├── KeepOperator.re │ │ │ ├── LastOperator.re │ │ │ ├── LastOrNoneOperator.re │ │ │ ├── MapOperator.re │ │ │ ├── MapToOperator.re │ │ │ ├── MaterializeOperator.re │ │ │ ├── MaybeFirstOperator.re │ │ │ ├── MaybeLastOperator.re │ │ │ ├── MaybeOperator.re │ │ │ ├── MergeMapOperator.re │ │ │ ├── MergeOperator.re │ │ │ ├── NoneOperator.re │ │ │ ├── ObserveOnOperator.re │ │ │ ├── ObserveOperator.re │ │ │ ├── OnCompleteOperator.re │ │ │ ├── OnConnectOperator.re │ │ │ ├── OnExnOperator.re │ │ │ ├── PublishToSubjectOperator.re │ │ │ ├── PublishToSubscriberOperator.re │ │ │ ├── RepeatOperator.re │ │ │ ├── ScanOperator.re │ │ │ ├── SkipOperator.re │ │ │ ├── SomeOperator.re │ │ │ ├── SwitchMapOperator.re │ │ │ ├── SwitchOperator.re │ │ │ ├── TakeOperator.re │ │ │ ├── TakeUntilOperator.re │ │ │ ├── TimeoutOperator.re │ │ │ └── WithLatestFromOperator.re │ └── test │ │ ├── Option.re │ │ ├── RxObservablesTest.re │ │ ├── RxReUnit.re │ │ ├── TestRunner.re │ │ └── internal │ │ └── observables │ │ ├── CombineLatest2Test.re │ │ ├── ConcatListTest.re │ │ ├── DebounceTimeTest.re │ │ ├── DefaultIfEmptyTest.re │ │ ├── DeferTest.re │ │ ├── DistinctUntilChangedTest.re │ │ ├── EveryTest.re │ │ ├── ExhaustTest.re │ │ ├── FindTest.re │ │ ├── FirstOrNoneTest.re │ │ ├── FirstTest.re │ │ ├── IgnoreElementsTest.re │ │ ├── IsEmptyTest.re │ │ ├── KeepTest.re │ │ ├── LastOrNoneTest.re │ │ ├── LastTest.re │ │ ├── MapTest.re │ │ ├── MapToTest.re │ │ ├── MaybeFirstTest.re │ │ ├── MaybeLastTest.re │ │ ├── MergeListTest.re │ │ ├── OnCompleteTest.re │ │ ├── OnNextTest.re │ │ ├── RetryTest.re │ │ ├── ScanTest.re │ │ ├── ShareReplayBufferTest.re │ │ ├── SomeTest.re │ │ ├── StartWithListTest.re │ │ ├── StartWithValueTest.re │ │ ├── SwitchTest.re │ │ ├── TakeTest.re │ │ ├── TakeUntilTest.re │ │ ├── TimeoutTest.re │ │ └── WithLatestFromTest.re ├── rx-scheduler │ ├── bsconfig.json │ ├── package.json │ ├── src │ │ ├── RxScheduler.re │ │ └── RxScheduler.rei │ └── test │ │ ├── RxSchedulerResultTest.re │ │ └── TestRunner.re ├── rx-utils-do-not-depend-on │ ├── bsconfig.json │ ├── package.json │ └── src │ │ ├── RxFunctions.re │ │ ├── RxMutableOption.re │ │ ├── RxPreconditions.re │ │ └── bs │ │ ├── RxAtomic.re │ │ ├── RxAtomic.rei │ │ ├── RxCopyOnWriteArray.re │ │ ├── RxLock.re │ │ ├── RxMutableList.re │ │ └── RxMutableQueue.re └── rx-virtual-time-scheduler │ ├── bsconfig.json │ ├── package.json │ └── src │ ├── RxVirtualTimeScheduler.re │ └── RxVirtualTimeScheduler.rei └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | .DS_Store 11 | .nyc_output 12 | .merlin 13 | .bs.js 14 | 15 | # ocamlbuild working directory 16 | _build/ 17 | 18 | # ocamlbuild targets 19 | *.byte 20 | *.native 21 | 22 | # bsb build directory 23 | lib/ 24 | build/ 25 | 26 | node_modules/ 27 | coverage/ 28 | npm-debug.log 29 | lerna-debug.log 30 | yarn-error.log 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo is effectively an abandoned experiment. After doing some significant research into performance on JS, I'm no longer in favor of a shared/xplat implementation for RX written in OCaml. If targeting bucklescript, you should instead write bindings to RxJS or MostJS and use those. 2 | 3 | This repo could serve as the basis for a quality Native OCaml implementation, though in doing so I would re-evaluate some of the assumptions in the initial design. 4 | 5 | 6 | # Reactive Extensions for Reason/OCaml 7 | 8 | An implementation of Rx for Reason/OCaml that is designed with the goal of providing a minimum base line feature set to support asynchronous dataflow patterns. Currently development is focused on bucklescript, but support for native ocaml is intended and desirable. 9 | 10 | # Basic Concepts 11 | * Observables, Singles and Maybes 12 | * Disposables 13 | * Subscribers and Operators 14 | * Subjects 15 | * Schedulers 16 | 17 | # Build 18 | ``` 19 | cd ./Reactive.re 20 | npm run build 21 | ``` 22 | 23 | # Testing 24 | 25 | First build reunit. You only should have to do this once. 26 | ``` 27 | cd ./reUnit 28 | npm run build 29 | ``` 30 | 31 | Then run the tests: 32 | ``` 33 | cd ./Reactive.re 34 | npm run test 35 | ``` 36 | 37 | You can also test and generate code coverage reports: 38 | ``` 39 | cd ./Reactive.re 40 | npm run test-coverage 41 | ``` 42 | 43 | Finally there is support for interactive debugging: 44 | ``` 45 | cd ./Reactive.re 46 | npm run test-debug 47 | ``` 48 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.11.0", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "npmClient": "yarn", 7 | "useWorkspaces": true, 8 | "version": "0.0.0" 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "lerna run build all", 5 | "clean": "lerna run clean", 6 | "bootstrap": "lerna bootstrap", 7 | "test": "lerna run test", 8 | "test-coverage": "lerna run test-coverage", 9 | "prepare": "lerna run prepare", 10 | "cleanall": "rimraf packages/**/lib" 11 | }, 12 | "devDependencies": { 13 | "bs-platform": "^4.0.7", 14 | "lerna": "^3.4.3", 15 | "rimraf": "^2.6.2" 16 | }, 17 | "workspaces": [ 18 | "packages/*" 19 | ], 20 | "dependencies": {} 21 | } 22 | -------------------------------------------------------------------------------- /packages/ix-core/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/ix-core", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": [ 13 | "@rx-reason/rx-core", 14 | "@rx-reason/rx-disposables", 15 | "@rx-reason/rx-observables", 16 | "@rx-reason/rx-imperative" 17 | ], 18 | "bs-dev-dependencies": [ 19 | "@rx-reason/reunit" 20 | ], 21 | "warnings": { 22 | "error": "+101" 23 | }, 24 | "refmt": 3, 25 | "sources": [ 26 | { 27 | "dir": "src" 28 | }, 29 | ] 30 | } -------------------------------------------------------------------------------- /packages/ix-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/ix-core", 3 | "version": "0.0.0", 4 | "description": "Asynchoronous iterators for reason/ocaml", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "start": "bsb -make-world -w", 8 | "clean": "bsb -clean-world" 9 | }, 10 | "keywords": [ 11 | "reason", 12 | "reasonml", 13 | "ocaml", 14 | "reactive", 15 | "rx", 16 | "BuckleScript" 17 | ], 18 | "author": { 19 | "name": "David Bordoley", 20 | "email": "bordoley@gmail.com" 21 | }, 22 | "contributors": [ 23 | { 24 | "name": "David Bordoley", 25 | "email": "bordoley@gmail.com" 26 | } 27 | ], 28 | "license": "MIT", 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/bordoley/rx-reason.git" 32 | }, 33 | "dependencies": { 34 | "@rx-reason/rx-core": "^0.0.0", 35 | "@rx-reason/rx-disposables": "^0.0.0", 36 | "@rx-reason/rx-observables": "^0.0.0", 37 | "@rx-reason/rx-imperative": "^0.0.0" 38 | }, 39 | "devDependencies": { 40 | "bs-platform": "^4.0.7", 41 | "nyc": "^13.1.0", 42 | "@rx-reason/reunit": "^0.0.0" 43 | } 44 | } -------------------------------------------------------------------------------- /packages/ix-core/src/IxIterable.re: -------------------------------------------------------------------------------- 1 | let create = (~createResource, ~disposeResource, ~request) => { 2 | let createIterator = IxIterator.create(~request, ~disposeResource); 3 | 4 | () => { 5 | let resource = createResource(); 6 | createIterator(resource); 7 | }; 8 | }; 9 | 10 | let create1 = (~createResource, ~disposeResource, ~request) => { 11 | let createIterator = IxIterator.create(~request, ~disposeResource); 12 | 13 | ctx0 => { 14 | let resource = createResource(ctx0); 15 | createIterator(resource); 16 | }; 17 | }; 18 | 19 | let create2 = (~createResource, ~disposeResource, ~request) => { 20 | let createIterator = IxIterator.create(~request, ~disposeResource); 21 | 22 | (ctx0, ctx1) => { 23 | let resource = createResource(ctx0, ctx1); 24 | createIterator(resource); 25 | }; 26 | }; 27 | 28 | let create3 = (~createResource, ~disposeResource, ~request) => { 29 | let createIterator = IxIterator.create(~request, ~disposeResource); 30 | 31 | (ctx0, ctx1, ctx2) => { 32 | let resource = createResource(ctx0, ctx1, ctx2); 33 | createIterator(resource); 34 | }; 35 | }; -------------------------------------------------------------------------------- /packages/ix-core/src/IxIterable.rei: -------------------------------------------------------------------------------- 1 | let create: ( 2 | ~createResource: unit => 'resource, 3 | ~disposeResource: 'resource => unit, 4 | ~request: ('request, 'resource) => RxObservable.t('a), 5 | unit, 6 | ) => IxIterator.t('a, 'request); 7 | 8 | let create1: ( 9 | ~createResource: 'ctx0 => 'resource, 10 | ~disposeResource: 'resource => unit, 11 | ~request: ('request, 'resource) => RxObservable.t('a), 12 | 'ctx0, 13 | ) => IxIterator.t('a, 'request); 14 | 15 | let create2: ( 16 | ~createResource: ('ctx0, 'ctx1) => 'resource, 17 | ~disposeResource: 'resource => unit, 18 | ~request: ('request, 'resource) => RxObservable.t('a), 19 | 'ctx0, 20 | 'ctx1, 21 | ) => IxIterator.t('a, 'request); 22 | 23 | let create3: ( 24 | ~createResource: ('ctx0, 'ctx1, 'ctx2) => 'resource, 25 | ~disposeResource: 'resource => unit, 26 | ~request: ('request, 'resource) => RxObservable.t('a), 27 | 'ctx0, 28 | 'ctx1, 29 | 'ctx2, 30 | ) => IxIterator.t('a, 'request); -------------------------------------------------------------------------------- /packages/ix-core/src/IxIterator.re: -------------------------------------------------------------------------------- 1 | type t('a, 'request) = 2 | | Disposed 3 | | Instance(RxEvent.t('request), RxObservable.t('a), RxDisposable.t); 4 | 5 | let asDisposable = 6 | fun 7 | | Disposed => RxDisposable.disposed 8 | | Instance(_, _, disposable) => disposable; 9 | 10 | let asObservable = 11 | fun 12 | | Disposed => RxObservable.never 13 | | Instance(_, observable, _) => observable; 14 | 15 | let dispose = self => self |> asDisposable |> RxDisposable.dispose; 16 | 17 | let isDisposed = self => self |> asDisposable |> RxDisposable.isDisposed; 18 | 19 | let request = action => 20 | fun 21 | | Disposed => () 22 | | Instance(event, _, _) => event |> RxEvent.dispatch(action); 23 | 24 | let onRequestSubscriptionComplete = (dispose, disposable, _) => 25 | dispose(disposable); 26 | 27 | let create = (~request, ~disposeResource) => { 28 | let mapper = (resource, next) => resource |> request(next); 29 | 30 | resource => { 31 | let event = RxEvent.create(); 32 | let requestStream = event |> RxEvent.asObservable; 33 | 34 | let subject = RxSubject.createMulticast(); 35 | let observable = subject |> RxSubject.asObservable; 36 | 37 | let compositeDisposable = 38 | RxCompositeDisposable.create() 39 | |> RxCompositeDisposable.addDisposable( 40 | RxDisposable.create1(disposeResource, resource), 41 | ) 42 | |> RxCompositeDisposable.addDisposable(RxEvent.asDisposable(event)) 43 | |> RxCompositeDisposable.addDisposable(RxSubject.asDisposable(subject)); 44 | 45 | let requestSubscription = 46 | requestStream 47 | |> RxObservables.switchMap1(mapper, resource) 48 | |> RxObservables.publishToSubject(subject) 49 | |> RxObservables.onComplete2( 50 | onRequestSubscriptionComplete, 51 | RxCompositeDisposable.dispose, 52 | compositeDisposable, 53 | ) 54 | |> RxObservable.connect; 55 | 56 | let disposable = 57 | compositeDisposable 58 | |> RxCompositeDisposable.addDisposable(requestSubscription) 59 | |> RxCompositeDisposable.asDisposable; 60 | 61 | Instance(event, observable, disposable); 62 | }; 63 | }; 64 | 65 | let disposed = Disposed; -------------------------------------------------------------------------------- /packages/ix-core/src/IxIterator.rei: -------------------------------------------------------------------------------- 1 | type t('a, 'request); 2 | 3 | include RxDisposable.S2 with type t('a, 'request) := t('a, 'request); 4 | 5 | let asObservable: t('a, 'request) => RxObservable.t('a); 6 | 7 | let create: ( 8 | ~request: ('request, 'resource) => RxObservable.t('a), 9 | ~disposeResource: 'resource => unit, 10 | 'resource, 11 | ) => t('a, 'request); 12 | 13 | let disposed: t('a, 'request); 14 | 15 | let request: ('request, t('a, 'request)) => unit; -------------------------------------------------------------------------------- /packages/reunit/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/reunit", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": [], 13 | "bs-dev-dependencies": [], 14 | "warnings": { 15 | "error": "+101" 16 | }, 17 | "refmt": 3, 18 | "sources": [ 19 | { 20 | "dir": "src", 21 | "subdirs": [ 22 | { 23 | "dir": "platform", 24 | "public": "none", 25 | "subdirs": [ 26 | { 27 | "dir": "js", 28 | "public": "none" 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /packages/reunit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/reunit", 3 | "version": "0.0.0", 4 | "description": "Basic unit testing framework for reason", 5 | "author": "facebook", 6 | "license": "BSD-3-Clause", 7 | "devDependencies": { 8 | "bs-platform": "^4.0.7" 9 | }, 10 | "scripts": { 11 | "build": "bsb -make-world", 12 | "clean": "bsb -clean-world" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/reunit/src/ReUnit.rei: -------------------------------------------------------------------------------- 1 | /*** 2 | * Copyright (c) 2017 - present Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | module Test: { 10 | type t; 11 | let describe: (string, list(t)) => t; 12 | let it: (string, unit => unit) => t; 13 | let toList: t => list((string, unit => unit)); 14 | }; 15 | 16 | module Expect: { 17 | let toBeEqualToFalse: bool => unit; 18 | let toBeEqualToInt: (int, int) => unit; 19 | let toBeEqualToListWith: 20 | (~equals: ('a, 'a) => bool, ~toString: 'a => string, list('a), list('a)) => unit; 21 | let toBeEqualToListOfInt: (list(int), list(int)) => unit; 22 | let toBeEqualToListOfString: (list(string), list(string)) => unit; 23 | let toBeEqualToNoneWith: (~toString: 'a => string, option('a)) => unit; 24 | let toBeEqualToNoneOfInt: option(int) => unit; 25 | let toBeEqualToNoneOfString: option(string) => unit; 26 | let toBeReferenceEqualTo: ('a, 'a) => unit; 27 | let toBeEqualToSomeWith: 28 | (~equals: ('a, 'a) => bool, ~toString: 'a => string, 'a, option('a)) => unit; 29 | let toBeEqualToSomeReference: ('a, option('a)) => unit; 30 | let toBeEqualToSomeOfInt: (int, option(int)) => unit; 31 | let toBeEqualToSomeOfString: (string, option(string)) => unit; 32 | let toBeEqualToString: (string, string) => unit; 33 | let toBeEqualToTrue: bool => unit; 34 | let toBeEqualToWith: (~equals: ('a, 'a) => bool, ~toString: 'a => string, 'a, 'a) => unit; 35 | let shouldRaise: (unit => 'a) => unit; 36 | }; 37 | 38 | let run: Test.t => unit; 39 | -------------------------------------------------------------------------------- /packages/reunit/src/platform/js/ExnHelpers.re: -------------------------------------------------------------------------------- 1 | let print = exn => Js.log(exn); -------------------------------------------------------------------------------- /packages/reunit/src/platform/ocaml/ExnHelpers.re: -------------------------------------------------------------------------------- 1 | let print = exn => { 2 | Printexc.to_string(exn) |> print_string; 3 | print_newline(); 4 | Printexc.print_backtrace(stdout); 5 | }; -------------------------------------------------------------------------------- /packages/rx-core/README.md: -------------------------------------------------------------------------------- 1 | # rx-core package 2 | -------------------------------------------------------------------------------- /packages/rx-core/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-core", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": [ 13 | "@rx-reason/rx-disposables", 14 | "@rx-reason/rx-utils-do-not-depend-on" 15 | ], 16 | "bs-dev-dependencies": [ 17 | "@rx-reason/reunit" 18 | ], 19 | "warnings": { 20 | "number": "-44", 21 | "error": "+101" 22 | }, 23 | "refmt": 3, 24 | "sources": [ 25 | { 26 | "dir": "src" 27 | }, 28 | { 29 | "dir": "test", 30 | "subdirs": true, 31 | "type": "dev" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /packages/rx-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-core", 3 | "version": "0.0.0", 4 | "description": "Minimal observable/subscriber implementation for reason/ocaml ", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "start": "bsb -make-world -w", 8 | "clean": "bsb -clean-world", 9 | "test": "npm run build && node lib/js/test/TestRunner.bs.js", 10 | "test-debug": "npm run build && node --inspect-brk lib/js/test/TestRunner.bs.js", 11 | "test-coverage": "npm run build && nyc node lib/js/test/TestRunner.bs.js && nyc report --reporter=html && open coverage/index.html" 12 | }, 13 | "keywords": [ 14 | "reason", 15 | "reasonml", 16 | "ocaml", 17 | "reactive", 18 | "rx", 19 | "BuckleScript" 20 | ], 21 | "author": { 22 | "name": "David Bordoley", 23 | "email": "bordoley@gmail.com" 24 | }, 25 | "contributors": [ 26 | { 27 | "name": "David Bordoley", 28 | "email": "bordoley@gmail.com" 29 | } 30 | ], 31 | "license": "MIT", 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/bordoley/rx-reason.git" 35 | }, 36 | "dependencies": { 37 | "@rx-reason/rx-disposables": "^0.0.0", 38 | "@rx-reason/rx-utils-do-not-depend-on": "0.0.0" 39 | }, 40 | "devDependencies": { 41 | "bs-platform": "^4.0.7", 42 | "nyc": "^13.1.0", 43 | "@rx-reason/reunit": "^0.0.0" 44 | } 45 | } -------------------------------------------------------------------------------- /packages/rx-core/src/RxNotification.re: -------------------------------------------------------------------------------- 1 | type t('a) = 2 | | Next('a) 3 | | Complete(option(exn)); 4 | 5 | let completeWithoutExn = Complete(None); 6 | let complete = exn => 7 | switch (exn) { 8 | | Some(_) => Complete(exn) 9 | | _ => completeWithoutExn 10 | }; 11 | 12 | let next = next => Next(next); 13 | 14 | let map = (~onNext, ~onComplete) => 15 | fun 16 | | Next(next) => onNext(next) 17 | | Complete(exn) => onComplete(exn); 18 | 19 | let map1 = (~onNext, ~onComplete, ctx0) => 20 | fun 21 | | Next(next) => onNext(ctx0, next) 22 | | Complete(exn) => onComplete(ctx0, exn); 23 | 24 | let map2 = (~onNext, ~onComplete, ctx0, ctx1) => 25 | fun 26 | | Next(next) => onNext(ctx0, ctx1, next) 27 | | Complete(exn) => onComplete(ctx0, ctx1, exn); -------------------------------------------------------------------------------- /packages/rx-core/src/RxNotification.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * Materialized representation of reactive events. 3 | * 4 | * For performance reasons reactive events are not materialized by default, but they can 5 | * be useful for implementation of certain operators. 6 | */; 7 | 8 | /** The RxNotification type. */ 9 | type t('a); 10 | 11 | /** Returns a completed notification with the provided optional exception. */ 12 | let complete: option(exn) => t('a); 13 | 14 | /** Returns a next notification with the provided value. */ 15 | let next: 'a => t('a); 16 | 17 | /** 18 | * Applies the provided mapping functions to the underlying 19 | * notification returning the resulting value. 20 | */ 21 | let map: (~onNext: 'a => 'b, ~onComplete: option(exn) => 'b, t('a)) => 'b; 22 | 23 | /** 24 | * Applies the mapping functions with the provided context variable 25 | * to the underlying notification returning the resulting value. 26 | */ 27 | let map1: 28 | ( 29 | ~onNext: ('ctx0, 'a) => 'b, 30 | ~onComplete: ('ctx0, option(exn)) => 'b, 31 | 'ctx0, 32 | t('a) 33 | ) => 34 | 'b; 35 | 36 | /** 37 | * Applies the mapping functions with the provided context variables 38 | * to the underlying notification returning the resulting value. 39 | */ 40 | let map2: 41 | ( 42 | ~onNext: ('ctx0, 'ctx1, 'a) => 'b, 43 | ~onComplete: ('ctx0, 'ctx1, option(exn)) => 'b, 44 | 'ctx0, 45 | 'ctx1, 46 | t('a) 47 | ) => 48 | 'b; -------------------------------------------------------------------------------- /packages/rx-core/src/RxObservable.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * A provider of push-based notifications. 3 | */; 4 | 5 | /** The RxObservable type. */ 6 | type t('a); 7 | 8 | type observable('a) = t('a); 9 | 10 | /** RxObservable module type signature for types with a parametric type arity of 0. */ 11 | module type S = { 12 | type a; 13 | type t; 14 | 15 | /** Cast to RxObservable.t. */ 16 | let asObservable: t => observable(a); 17 | }; 18 | 19 | /** RxObservable module type signature for types with a parametric type arity of 0. */ 20 | module type S1 = { 21 | type t('a); 22 | 23 | /** Cast to Observable.t. */ 24 | let asObservable: t('a) => observable('a); 25 | }; 26 | 27 | include RxConnectableLike.S1 with type t('a) := t('a); 28 | 29 | /** 30 | * Returns an RxObservable using the provided subscribe function. 31 | */ 32 | let create: (RxSubscriber.t('a) => unit) => t('a); 33 | 34 | /** 35 | * Returns an RxObservable using the provide subscribe function and 36 | * context variable. 37 | */ 38 | let create1: (('ctx0, RxSubscriber.t('a)) => unit, 'ctx0) => t('a); 39 | 40 | /** 41 | * Returns an RxObservable using the provide subscribe function and 42 | * context variables. 43 | */ 44 | let create2: 45 | (('ctx0, 'ctx1, RxSubscriber.t('a)) => unit, 'ctx0, 'ctx1) => t('a); 46 | 47 | /** 48 | * Returns an RxObservable using the provide subscribe function and 49 | * context variables. 50 | */ 51 | let create3: 52 | (('ctx0, 'ctx1, 'ctx2, RxSubscriber.t('a)) => unit, 'ctx0, 'ctx1, 'ctx2) => 53 | t('a); 54 | 55 | /** 56 | * Returns an RxObservable using the provide subscribe function and 57 | * context variables. 58 | */ 59 | let create4: 60 | ( 61 | ('ctx0, 'ctx1, 'ctx2, 'ctx3, RxSubscriber.t('a)) => unit, 62 | 'ctx0, 63 | 'ctx1, 64 | 'ctx2, 65 | 'ctx3 66 | ) => 67 | t('a); 68 | 69 | /** 70 | * Returns an RxObservable using the provide subscribe function and 71 | * context variables. 72 | */ 73 | let create5: 74 | ( 75 | ('ctx0, 'ctx1, 'ctx2, 'ctx3, 'ctx4, RxSubscriber.t('a)) => unit, 76 | 'ctx0, 77 | 'ctx1, 78 | 'ctx2, 79 | 'ctx3, 80 | 'ctx4 81 | ) => 82 | t('a); 83 | 84 | /** 85 | * Returns an RxObservable that applies the RxOperator function to the 86 | * source RxObservable's notifications. 87 | */ 88 | let lift: (RxOperator.t('a, 'b), t('a)) => t('b); 89 | 90 | /** 91 | * Returns an RxObservable that emits no values and never completes. 92 | */ 93 | let never: t('a); -------------------------------------------------------------------------------- /packages/rx-core/src/RxObserverLike.re: -------------------------------------------------------------------------------- 1 | /** 2 | * Module type signatures for types that support observing push based notifications. 3 | */; 4 | 5 | /** RxObserverLike module type signature for types with a parametric type arity of 0. */ 6 | module type S = { 7 | type a; 8 | type t; 9 | 10 | /** Notify the Observer that no more notifications will be sent, optionally with an exception. */ 11 | let complete: (~exn: exn=?, t) => unit; 12 | 13 | /** 14 | * Notify the Observer that no more notifications will be sent, optionally with an exception. 15 | * Returns true if the Observer has not previously been completed, otherwise false. 16 | */ 17 | let completeWithResult: (~exn: exn=?, t) => bool; 18 | 19 | /** Notify the Observer of the next element to observe. */ 20 | let next: (a, t) => unit; 21 | 22 | /** Notify the Observer of the next notification to observe. */ 23 | let notify: (RxNotification.t(a), t) => unit; 24 | }; 25 | 26 | /** RxObserverLike module type signature for types with a parametric type arity of 1. */ 27 | module type S1 = { 28 | type t('a); 29 | 30 | /** Notify the Observer that no more notifications will be sent, optionally with an exception. */ 31 | let complete: (~exn: exn=?, t('a)) => unit; 32 | 33 | /** 34 | * Notify the Observer that no more notifications will be sent, optionally with an exception. 35 | * Returns true if the Observer has not previously been completed, otherwise false. 36 | */ 37 | let completeWithResult: (~exn: exn=?, t('a)) => bool; 38 | 39 | /** Notify the Observer of the next element to observe. */ 40 | let next: ('a, t('a)) => unit; 41 | 42 | /** Notify the Observer of the next notification to observe. */ 43 | let notify: (RxNotification.t('a), t('a)) => unit; 44 | }; -------------------------------------------------------------------------------- /packages/rx-core/src/RxOperator.re: -------------------------------------------------------------------------------- 1 | /** 2 | * RxOperator function signature. 3 | * 4 | * Operator functions are chained together via the RxObservable.lift function, 5 | * decorating the RxObservable's subscriber. RxOperators are used to tranform 6 | * the observed notifications. 7 | */; 8 | 9 | /** The RxOperator type */ 10 | type t('a, 'b) = RxSubscriber.t('b) => RxSubscriber.t('a); -------------------------------------------------------------------------------- /packages/rx-core/test/TestRunner.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | 3 | ReUnit.run( 4 | describe( 5 | "rx-core", 6 | [ 7 | RxObservableTest.test, 8 | RxSubscriberTest.test, 9 | ], 10 | ), 11 | ); -------------------------------------------------------------------------------- /packages/rx-disposables/README.md: -------------------------------------------------------------------------------- 1 | # rx-disposables package 2 | -------------------------------------------------------------------------------- /packages/rx-disposables/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-disposables", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": [ 13 | "@rx-reason/rx-utils-do-not-depend-on" 14 | ], 15 | "bs-dev-dependencies": [ 16 | "@rx-reason/reunit" 17 | ], 18 | "warnings": { 19 | "error": "+101" 20 | }, 21 | "refmt": 3, 22 | "sources": [ 23 | { 24 | "dir": "src" 25 | }, 26 | { 27 | "dir": "test", 28 | "subdirs": true, 29 | "type": "dev" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /packages/rx-disposables/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-disposables", 3 | "version": "0.0.0", 4 | "description": "disposables for reason/ocaml", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "start": "bsb -make-world -w", 8 | "clean": "bsb -clean-world", 9 | "test": "npm run build && node lib/js/test/TestRunner.bs.js", 10 | "test-debug": "npm run build && node --inspect-brk lib/js/test/TestRunner.bs.js", 11 | "test-coverage": "npm run build && nyc node lib/js/test/TestRunner.bs.js && nyc report --reporter=html && open coverage/index.html" 12 | }, 13 | "keywords": [ 14 | "ocaml", 15 | "reason", 16 | "reasonml", 17 | "BuckleScript" 18 | ], 19 | "author": { 20 | "name": "David Bordoley", 21 | "email": "bordoley@gmail.com" 22 | }, 23 | "contributors": [ 24 | { 25 | "name": "David Bordoley", 26 | "email": "bordoley@gmail.com" 27 | } 28 | ], 29 | "license": "MIT", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/bordoley/rx-reason.git" 33 | }, 34 | "dependencies": { 35 | "@rx-reason/rx-utils-do-not-depend-on": "0.0.0" 36 | }, 37 | "devDependencies": { 38 | "bs-platform": "^4.0.7", 39 | "nyc": "^13.1.0", 40 | "@rx-reason/reunit": "^0.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxCompositeDisposable.re: -------------------------------------------------------------------------------- 1 | type t = 2 | | Disposed 3 | | CompositeDisposable( 4 | RxLock.t, 5 | RxMutableList.t(RxDisposable.t), 6 | RxDisposable.t, 7 | ); 8 | 9 | type compositeDisposable = t; 10 | 11 | module type S = { 12 | type t; 13 | 14 | include RxCompositeDisposableLike.S with type t := t; 15 | include RxDisposable.S with type t := t; 16 | 17 | let asCompositeDisposable: t => compositeDisposable; 18 | }; 19 | 20 | module type S1 = { 21 | type t('a); 22 | 23 | include RxCompositeDisposableLike.S1 with type t('a) := t('a); 24 | include RxDisposable.S1 with type t('a) := t('a); 25 | 26 | let asCompositeDisposable: t('a) => compositeDisposable; 27 | }; 28 | 29 | module type S2 = { 30 | type t('a, 'b); 31 | 32 | include RxCompositeDisposableLike.S2 with type t('a, 'b) := t('a, 'b); 33 | include RxDisposable.S2 with type t('a, 'b) := t('a, 'b); 34 | 35 | /** Cast to CompositeDisposable.t. */ 36 | let asCompositeDisposable: t('a, 'b) => compositeDisposable; 37 | }; 38 | 39 | let asDisposable = 40 | fun 41 | | Disposed => RxDisposable.disposed 42 | | CompositeDisposable(_, _, disposable) => disposable; 43 | 44 | let dispose = disposable => disposable |> asDisposable |> RxDisposable.dispose; 45 | 46 | let isDisposed = disposable => 47 | disposable |> asDisposable |> RxDisposable.isDisposed; 48 | 49 | let disposed = Disposed; 50 | 51 | let create = { 52 | let teardown = (lock, children) => { 53 | lock |> RxLock.acquire; 54 | children |> RxMutableList.forEachReversed(RxDisposable.dispose); 55 | children |> RxMutableList.clear; 56 | lock |> RxLock.release; 57 | }; 58 | 59 | () => { 60 | let children = RxMutableList.create(); 61 | let lock = RxLock.create(); 62 | let disposable = RxDisposable.create2(teardown, lock, children); 63 | CompositeDisposable(lock, children, disposable); 64 | }; 65 | }; 66 | 67 | let addDisposable = (disposable, self) => { 68 | switch (self) { 69 | | Disposed => disposable |> RxDisposable.dispose 70 | | CompositeDisposable(lock, children, _) => 71 | lock |> RxLock.acquire; 72 | 73 | let isDisposed = isDisposed(self); 74 | if (! isDisposed) { 75 | children |> RxMutableList.add(disposable); 76 | lock |> RxLock.release; 77 | } else { 78 | lock |> RxLock.release; 79 | disposable |> RxDisposable.dispose; 80 | }; 81 | }; 82 | self; 83 | }; 84 | 85 | let removeDisposable = (disposable, self) => { 86 | switch (self) { 87 | | Disposed => () 88 | | CompositeDisposable(lock, children, _) => 89 | lock |> RxLock.acquire; 90 | 91 | let isDisposed = isDisposed(self); 92 | if (! isDisposed) { 93 | children |> RxMutableList.remove(disposable); 94 | }; 95 | 96 | lock |> RxLock.release; 97 | }; 98 | self; 99 | }; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxCompositeDisposable.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * A disposable container that references other disposables and teardown logic. 3 | */; 4 | type t; 5 | 6 | type compositeDisposable = t; 7 | 8 | module type S = { 9 | type t; 10 | 11 | include RxCompositeDisposableLike.S with type t := t; 12 | include RxDisposable.S with type t := t; 13 | 14 | /** Cast to CompositeDisposable.t. */ 15 | let asCompositeDisposable: t => compositeDisposable; 16 | }; 17 | 18 | module type S1 = { 19 | type t('a); 20 | 21 | include RxCompositeDisposableLike.S1 with type t('a) := t('a); 22 | include RxDisposable.S1 with type t('a) := t('a); 23 | 24 | /** Cast to CompositeDisposable.t. */ 25 | let asCompositeDisposable: t('a) => compositeDisposable; 26 | }; 27 | 28 | module type S2 = { 29 | type t('a, 'b); 30 | 31 | include RxCompositeDisposableLike.S2 with type t('a, 'b) := t('a, 'b); 32 | include RxDisposable.S2 with type t('a, 'b) := t('a, 'b); 33 | 34 | /** Cast to CompositeDisposable.t. */ 35 | let asCompositeDisposable: t('a, 'b) => compositeDisposable; 36 | }; 37 | 38 | include RxDisposable.S with type t := t; 39 | include RxCompositeDisposableLike.S with type t := t; 40 | 41 | /** Constructs a new CompositeDisposable instance. */ 42 | let create: unit => t; 43 | 44 | /** 45 | * A disposed CompositeDisposable instance. 46 | */ 47 | let disposed: t; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxCompositeDisposableLike.re: -------------------------------------------------------------------------------- 1 | module type S = { 2 | type t; 3 | 4 | include RxDisposableLike.S with type t := t; 5 | 6 | let addDisposable: (RxDisposable.t, t) => t; 7 | let removeDisposable: (RxDisposable.t, t) => t; 8 | }; 9 | 10 | module type S1 = { 11 | type t('a); 12 | 13 | include RxDisposableLike.S1 with type t('a) := t('a); 14 | 15 | let addDisposable: (RxDisposable.t, t('a)) => t('a); 16 | let removeDisposable: (RxDisposable.t, t('a)) => t('a); 17 | }; 18 | 19 | module type S2 = { 20 | type t('a, 'b); 21 | 22 | include RxDisposableLike.S2 with type t('a, 'b) := t('a, 'b); 23 | 24 | let addDisposable: (RxDisposable.t, t('a, 'b)) => t('a, 'b); 25 | let removeDisposable: (RxDisposable.t, t('a, 'b)) => t('a, 'b); 26 | }; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxConnectableLike.re: -------------------------------------------------------------------------------- 1 | module type S = { 2 | type t; 3 | 4 | let connect: t => RxDisposable.t; 5 | }; 6 | 7 | module type S1 = { 8 | type t('a); 9 | 10 | let connect: t('a) => RxDisposable.t; 11 | }; 12 | 13 | module type S2 = { 14 | type t('a, 'b); 15 | 16 | let connect: t('a, 'b) => RxDisposable.t; 17 | }; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxDisposable.re: -------------------------------------------------------------------------------- 1 | type t = 2 | | Disposable(RxAtomic.t(bool), unit => unit) 3 | | Disposable1(RxAtomic.t(bool), 'ctx0 => unit, 'ctx0): t 4 | | Disposable2(RxAtomic.t(bool), ('ctx0, 'ctx1) => unit, 'ctx0, 'ctx1): t 5 | | Disposable3( 6 | RxAtomic.t(bool), 7 | ('ctx0, 'ctx1, 'ctx2) => unit, 8 | 'ctx0, 9 | 'ctx1, 10 | 'ctx2, 11 | ): t 12 | | Disposable4( 13 | RxAtomic.t(bool), 14 | ('ctx0, 'ctx1, 'ctx2, 'ctx3) => unit, 15 | 'ctx0, 16 | 'ctx1, 17 | 'ctx2, 18 | 'ctx3, 19 | ): t 20 | | Disposable5( 21 | RxAtomic.t(bool), 22 | ('ctx0, 'ctx1, 'ctx2, 'ctx3, 'ctx4) => unit, 23 | 'ctx0, 24 | 'ctx1, 25 | 'ctx2, 26 | 'ctx3, 27 | 'ctx4, 28 | ): t 29 | | Disposed 30 | | Empty(RxAtomic.t(bool)); 31 | 32 | type disposable = t; 33 | 34 | module type S = { 35 | include RxDisposableLike.S; 36 | 37 | let asDisposable: t => disposable; 38 | }; 39 | 40 | module type S1 = { 41 | include RxDisposableLike.S1; 42 | 43 | let asDisposable: t('a) => disposable; 44 | }; 45 | 46 | module type S2 = { 47 | include RxDisposableLike.S2; 48 | 49 | /** Cast to Disposable.t. */ 50 | let asDisposable: t('a, 'b) => disposable; 51 | }; 52 | 53 | let create = teardown : t => Disposable(RxAtomic.make(false), teardown); 54 | 55 | let create1 = (teardown, d0) : t => 56 | Disposable1(RxAtomic.make(false), teardown, d0); 57 | 58 | let create2 = (teardown, d0, d1) : t => 59 | Disposable2(RxAtomic.make(false), teardown, d0, d1); 60 | 61 | let create3 = (teardown, d0, d1, d2) : t => 62 | Disposable3(RxAtomic.make(false), teardown, d0, d1, d2); 63 | 64 | let create4 = (teardown, d0, d1, d2, d3) : t => 65 | Disposable4(RxAtomic.make(false), teardown, d0, d1, d2, d3); 66 | 67 | let create5 = (teardown, d0, d1, d2, d3, d4) : t => 68 | Disposable5(RxAtomic.make(false), teardown, d0, d1, d2, d3, d4); 69 | 70 | let empty = () => Empty(RxAtomic.make(false)); 71 | 72 | let disposed: t = Disposed; 73 | 74 | let dispose = { 75 | let shouldDispose = 76 | fun 77 | | Disposable(isDisposed, _) 78 | | Disposable1(isDisposed, _, _) 79 | | Disposable2(isDisposed, _, _, _) 80 | | Disposable3(isDisposed, _, _, _, _) 81 | | Disposable4(isDisposed, _, _, _, _, _) 82 | | Disposable5(isDisposed, _, _, _, _, _, _) 83 | | Empty(isDisposed) => ! RxAtomic.exchange(isDisposed, true) 84 | | Disposed => false; 85 | 86 | let doTeardown = 87 | fun 88 | | Disposable(_, teardown) => teardown() 89 | | Disposable1(_, teardown, a0) => teardown(a0) 90 | | Disposable2(_, teardown, a0, a1) => teardown(a0, a1) 91 | | Disposable3(_, teardown, a0, a1, a2) => teardown(a0, a1, a2) 92 | | Disposable4(_, teardown, a0, a1, a2, a3) => teardown(a0, a1, a2, a3) 93 | | Disposable5(_, teardown, a0, a1, a2, a3, a4) => 94 | teardown(a0, a1, a2, a3, a4) 95 | | Empty(_) 96 | | Disposed => (); 97 | 98 | disposable => { 99 | let shouldDispose = shouldDispose(disposable); 100 | if (shouldDispose) { 101 | try( doTeardown(disposable)) { 102 | /* Proactively catch exceptions thrown in teardown logic. Teardown functions 103 | * shouldn't throw, so this is to proactively prevent unexpected exceptions. 104 | */ 105 | | _ => () 106 | } 107 | }; 108 | }; 109 | }; 110 | 111 | let compose = { 112 | let rec disposeAll = list => 113 | switch (list) { 114 | | [] => () 115 | | [hd, ...tail] => 116 | dispose(hd); 117 | disposeAll(tail); 118 | }; 119 | 120 | disposables => create1(disposeAll, disposables); 121 | }; 122 | 123 | let isDisposed = 124 | fun 125 | | Disposable(isDisposed, _) 126 | | Disposable1(isDisposed, _, _) 127 | | Disposable2(isDisposed, _, _, _) 128 | | Disposable3(isDisposed, _, _, _, _) 129 | | Disposable4(isDisposed, _, _, _, _, _) 130 | | Disposable5(isDisposed, _, _, _, _, _, _) 131 | | Empty(isDisposed) => RxAtomic.get(isDisposed) 132 | | Disposed => true; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxDisposable.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * A disposable resource providing a mechanism for releasing unmanaged resources. 3 | */; 4 | 5 | /** The Disposable type */ 6 | type t; 7 | type disposable = t; 8 | 9 | /** 10 | * Module type signature for a Disposable type with parametric type arity of 0. 11 | */ 12 | module type S = { 13 | include RxDisposableLike.S; 14 | 15 | /** Cast to Disposable.t. */ 16 | let asDisposable: t => disposable; 17 | }; 18 | 19 | /** 20 | * Module type signature for a Disposable type with parametric type arity of 1. 21 | */ 22 | module type S1 = { 23 | include RxDisposableLike.S1; 24 | 25 | /** Cast to Disposable.t. */ 26 | let asDisposable: t('a) => disposable; 27 | }; 28 | 29 | /** 30 | * Module type signature for a Disposable type with parametric type arity of 2. 31 | */ 32 | module type S2 = { 33 | include RxDisposableLike.S2; 34 | 35 | /** Cast to Disposable.t. */ 36 | let asDisposable: t('a, 'b) => disposable; 37 | }; 38 | 39 | include RxDisposableLike.S with type t := t; 40 | 41 | /** 42 | * Constructs a Disposable instance which disposes the 43 | * supplied disposables when disposed. 44 | */ 45 | let compose: list(t) => t; 46 | 47 | /** 48 | * Constructs a Disposable instance which executes 49 | * the provided function exactly once when disposed. 50 | */ 51 | let create: (unit => unit) => t; 52 | 53 | /** 54 | * Constructs a Disposable instance which executes 55 | * the provided teardown function exactly once when disposed. 56 | */ 57 | let create1: ('a => unit, 'a) => t; 58 | 59 | /** 60 | * Constructs a Disposable instance which executes 61 | * the provided teardown function exactly once when disposed. 62 | */ 63 | let create2: (('a, 'b) => unit, 'a, 'b) => t; 64 | 65 | /** 66 | * Constructs a Disposable instance which executes 67 | * the provided teardown function exactly once when disposed. 68 | */ 69 | let create3: (('a, 'b, 'c) => unit, 'a, 'b, 'c) => t; 70 | 71 | /** 72 | * Constructs a Disposable instance which executes 73 | * the provided teardown function exactly once when disposed. 74 | */ 75 | let create4: (('a, 'b, 'c, 'd) => unit, 'a, 'b, 'c, 'd) => t; 76 | 77 | /** 78 | * Constructs a Disposable instance which executes 79 | * the provided teardown function exactly once when disposed. 80 | */ 81 | let create5: (('a, 'b, 'c, 'd, 'e) => unit, 'a, 'b, 'c, 'd, 'e) => t; 82 | 83 | /** 84 | * A disposed Disposable instance. 85 | */ 86 | let disposed: t; 87 | 88 | /** 89 | * Contructs a new, non-disposed Disposable instance. 90 | */ 91 | let empty: unit => t; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxDisposableLike.re: -------------------------------------------------------------------------------- 1 | /** 2 | * Module type signature for a DisposableLike type with parametric type arity of 0. 3 | */ 4 | module type S = { 5 | type t; 6 | 7 | /** Dispose the resource. The operation must be idempotent. */ 8 | let dispose: t => unit; 9 | 10 | /** Returns true if this resource has been disposed. */ 11 | let isDisposed: t => bool; 12 | }; 13 | 14 | /** 15 | * Module type signature for a DisposableLike type with parametric type arity of 1. 16 | */ 17 | module type S1 = { 18 | type t('a); 19 | 20 | /** Dispose the resource. The operation must be idempotent. */ 21 | let dispose: t('a) => unit; 22 | 23 | /** Returns true if this resource has been disposed. */ 24 | let isDisposed: t('a) => bool; 25 | }; 26 | 27 | /** 28 | * Module type signature for a DisposableLike type with parametric type arity of 2. 29 | */ 30 | module type S2 = { 31 | type t('a, 'b); 32 | 33 | /** Dispose the resource. The operation must be idempotent. */ 34 | let dispose: t('a, 'b) => unit; 35 | 36 | /** Returns true if this resource has been disposed. */ 37 | let isDisposed: t('a, 'b) => bool; 38 | }; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxSerialDisposable.re: -------------------------------------------------------------------------------- 1 | type t = 2 | | Disposed 3 | | SerialDisposable(RxAtomic.t(RxDisposable.t), RxDisposable.t); 4 | 5 | type serialDisposable = t; 6 | 7 | module type S = { 8 | type t; 9 | 10 | include RxSerialDisposableLike.S with type t := t; 11 | include RxDisposable.S with type t := t; 12 | 13 | let asSerialDisposable: t => serialDisposable; 14 | }; 15 | 16 | module type S1 = { 17 | type t('a); 18 | 19 | include RxSerialDisposableLike.S1 with type t('a) := t('a); 20 | include RxDisposable.S1 with type t('a) := t('a); 21 | 22 | let asSerialDisposable: t('a) => serialDisposable; 23 | }; 24 | 25 | module type S2 = { 26 | type t('a, 'b); 27 | 28 | include RxSerialDisposableLike.S2 with type t('a, 'b) := t('a, 'b); 29 | include RxDisposable.S2 with type t('a, 'b) := t('a, 'b); 30 | 31 | /** Cast to SerialDisposable.t. */ 32 | let asSerialDisposable: t('a, 'b) => serialDisposable; 33 | }; 34 | 35 | let asDisposable = 36 | fun 37 | | Disposed => RxDisposable.disposed 38 | | SerialDisposable(_, disposable) => disposable; 39 | 40 | let dispose = disposable => disposable |> asDisposable |> RxDisposable.dispose; 41 | 42 | let isDisposed = disposable => 43 | disposable |> asDisposable |> RxDisposable.isDisposed; 44 | 45 | let create = { 46 | let teardown = innerDisposable => 47 | RxAtomic.exchange(innerDisposable, RxDisposable.disposed) 48 | |> RxDisposable.dispose; 49 | 50 | () => { 51 | let innerDisposable = RxAtomic.make(RxDisposable.disposed); 52 | let disposable = RxDisposable.create1(teardown, innerDisposable); 53 | SerialDisposable(innerDisposable, disposable); 54 | }; 55 | }; 56 | 57 | let disposed = Disposed; 58 | 59 | let getInnerDisposable = 60 | fun 61 | | Disposed => RxDisposable.disposed 62 | | SerialDisposable(innerDisposable, _) => RxAtomic.get(innerDisposable); 63 | 64 | let setInnerDisposable = (newDisposable, self) => { 65 | let shouldDispose = 66 | switch (self) { 67 | | Disposed => true 68 | | SerialDisposable(innerDisposable, _) => 69 | let oldDisposable = RxAtomic.exchange(innerDisposable, newDisposable); 70 | if (oldDisposable !== newDisposable) { 71 | oldDisposable |> RxDisposable.dispose; 72 | }; 73 | isDisposed(self); 74 | }; 75 | 76 | if (shouldDispose) { 77 | newDisposable |> RxDisposable.dispose; 78 | }; 79 | }; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxSerialDisposable.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * A Disposable container that allows atomically updating/replacing the contained 3 | * Disposable with another Disposable, disposing the old one when updating plus 4 | * handling the disposition when the container itself is disposed. 5 | */; 6 | 7 | type t; 8 | 9 | type serialDisposable = t; 10 | 11 | module type S = { 12 | type t; 13 | 14 | include RxSerialDisposableLike.S with type t := t; 15 | include RxDisposable.S with type t := t; 16 | 17 | /** Cast to SerialDisposable.t. */ 18 | let asSerialDisposable: t => serialDisposable; 19 | }; 20 | 21 | module type S1 = { 22 | type t('a); 23 | 24 | include RxSerialDisposableLike.S1 with type t('a) := t('a); 25 | include RxDisposable.S1 with type t('a) := t('a); 26 | 27 | /** Cast to SerialDisposable.t. */ 28 | let asSerialDisposable: t('a) => serialDisposable; 29 | }; 30 | 31 | module type S2 = { 32 | type t('a, 'b); 33 | 34 | include RxSerialDisposableLike.S2 with type t('a, 'b) := t('a, 'b); 35 | include RxDisposable.S2 with type t('a, 'b) := t('a, 'b); 36 | 37 | /** Cast to SerialDisposable.t. */ 38 | let asSerialDisposable: t('a, 'b) => serialDisposable; 39 | }; 40 | 41 | include RxDisposable.S with type t := t; 42 | include RxSerialDisposableLike.S with type t := t; 43 | 44 | /** Constructs a new SerialDisposable instance containing a disposed disposable. */ 45 | let create: unit => t; 46 | 47 | /** 48 | * A disposed SerialDisposable instance. 49 | */ 50 | let disposed: t; -------------------------------------------------------------------------------- /packages/rx-disposables/src/RxSerialDisposableLike.re: -------------------------------------------------------------------------------- 1 | module type S = { 2 | type t; 3 | 4 | include RxDisposableLike.S with type t := t; 5 | 6 | /** Returns the currently contained Disposable */ 7 | let getInnerDisposable: t => RxDisposable.t; 8 | 9 | /** Atomically set the next disposable on this container and dispose the previous 10 | * one (if any) or dispose next if the container has been disposed 11 | */ 12 | let setInnerDisposable: (RxDisposable.t, t) => unit; 13 | }; 14 | 15 | module type S1 = { 16 | type t('a); 17 | 18 | include RxDisposableLike.S1 with type t('a) := t('a); 19 | 20 | /** Returns the currently contained Disposable */ 21 | let getInnerDisposable: t('a) => RxDisposable.t; 22 | 23 | /** Atomically set the next disposable on this container and dispose the previous 24 | * one (if any) or dispose next if the container has been disposed 25 | */ 26 | let setInnerDisposable: (RxDisposable.t, t('a)) => unit; 27 | }; 28 | 29 | module type S2 = { 30 | include RxDisposableLike.S2; 31 | 32 | /** Returns the currently contained Disposable */ 33 | let getInnerDisposable: t('a, 'b) => RxDisposable.t; 34 | 35 | /** Atomically set the next disposable on this container and dispose the previous 36 | * one (if any) or dispose next if the container has been disposed 37 | */ 38 | let setInnerDisposable: (RxDisposable.t, t('a, 'b)) => unit; 39 | }; -------------------------------------------------------------------------------- /packages/rx-disposables/test/RxCompositeDisposableTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | 4 | let test = 5 | describe( 6 | "RxCompositeDisposable", 7 | [ 8 | describe( 9 | "create", 10 | [ 11 | describe( 12 | "addDisposable", 13 | [ 14 | it("disposes the added RxDisposable if disposed", () => { 15 | let disposable = RxDisposable.empty(); 16 | 17 | let compositeDisposable = RxCompositeDisposable.create(); 18 | compositeDisposable |> RxCompositeDisposable.dispose; 19 | 20 | compositeDisposable 21 | |> RxCompositeDisposable.addDisposable(disposable) 22 | |> ignore; 23 | 24 | disposable |> RxDisposable.isDisposed |> Expect.toBeEqualToTrue; 25 | }), 26 | ], 27 | ), 28 | describe( 29 | "removeDisposable", 30 | [ 31 | it( 32 | "removes the child Disposable, and does not dispose it when disposed", 33 | () => { 34 | let disposable0 = RxDisposable.empty(); 35 | let disposable1 = RxDisposable.empty(); 36 | let disposable2 = RxDisposable.empty(); 37 | 38 | let compositeDisposable = 39 | RxCompositeDisposable.create() 40 | |> RxCompositeDisposable.addDisposable(disposable0) 41 | |> RxCompositeDisposable.addDisposable(disposable1) 42 | |> RxCompositeDisposable.addDisposable(disposable2); 43 | 44 | compositeDisposable 45 | |> RxCompositeDisposable.removeDisposable(disposable1) 46 | |> ignore; 47 | 48 | compositeDisposable |> RxCompositeDisposable.dispose; 49 | disposable0 50 | |> RxDisposable.isDisposed 51 | |> Expect.toBeEqualToTrue; 52 | disposable1 53 | |> RxDisposable.isDisposed 54 | |> Expect.toBeEqualToFalse; 55 | disposable2 56 | |> RxDisposable.isDisposed 57 | |> Expect.toBeEqualToTrue; 58 | }), 59 | ], 60 | ), 61 | ], 62 | ), 63 | describe( 64 | "disposed", 65 | [ 66 | describe( 67 | "addDisposable", 68 | [ 69 | it("disposes the added RxDisposable", () => { 70 | let disposable = RxDisposable.empty(); 71 | 72 | RxCompositeDisposable.disposed 73 | |> RxCompositeDisposable.addDisposable(disposable) 74 | |> ignore; 75 | 76 | disposable |> RxDisposable.isDisposed |> Expect.toBeEqualToTrue; 77 | }), 78 | ], 79 | ), 80 | describe( 81 | "asDisposable", 82 | [ 83 | it("is equal to RxDisposable.disposed", () => 84 | RxCompositeDisposable.disposed 85 | |> RxCompositeDisposable.asDisposable 86 | |> Expect.toBeReferenceEqualTo(RxDisposable.disposed) 87 | ), 88 | ], 89 | ), 90 | describe( 91 | "isDisposed", 92 | [ 93 | it("is true", () => 94 | RxCompositeDisposable.disposed 95 | |> RxCompositeDisposable.isDisposed 96 | |> Expect.toBeEqualToTrue 97 | ), 98 | ], 99 | ), 100 | describe( 101 | "removeDisposable", 102 | [ 103 | it("does nothing", () => { 104 | let disposable = RxDisposable.empty(); 105 | RxCompositeDisposable.disposed 106 | |> RxCompositeDisposable.removeDisposable(disposable) 107 | |> ignore; 108 | }), 109 | ], 110 | ), 111 | ], 112 | ), 113 | ], 114 | ); -------------------------------------------------------------------------------- /packages/rx-disposables/test/RxSerialDisposableTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | 4 | let test = 5 | describe( 6 | "RxSerialDisposable", 7 | [ 8 | describe( 9 | "create", 10 | [ 11 | describe( 12 | "getInnerDisposable", 13 | [ 14 | it("returns the current inner Disposable", () => { 15 | let disposable = RxDisposable.empty(); 16 | let serialDisposable = RxSerialDisposable.create(); 17 | 18 | serialDisposable 19 | |> RxSerialDisposable.getInnerDisposable 20 | |> Expect.toBeReferenceEqualTo(RxDisposable.disposed); 21 | 22 | serialDisposable 23 | |> RxSerialDisposable.setInnerDisposable(disposable); 24 | 25 | serialDisposable 26 | |> RxSerialDisposable.getInnerDisposable 27 | |> Expect.toBeReferenceEqualTo(disposable); 28 | }), 29 | ], 30 | ), 31 | describe( 32 | "dispose", 33 | [ 34 | it("disposes the inner Disposable", () => { 35 | let disposable = RxDisposable.empty(); 36 | let serialDisposable = RxSerialDisposable.create(); 37 | serialDisposable 38 | |> RxSerialDisposable.setInnerDisposable(disposable); 39 | serialDisposable |> RxSerialDisposable.dispose; 40 | disposable |> RxDisposable.isDisposed |> Expect.toBeEqualToTrue; 41 | }), 42 | ], 43 | ), 44 | ], 45 | ), 46 | describe( 47 | "disposed", 48 | [ 49 | describe( 50 | "getInnerDisposable", 51 | [ 52 | it("returns RxDisposable.disposed", () => 53 | RxSerialDisposable.disposed 54 | |> RxSerialDisposable.getInnerDisposable 55 | |> Expect.toBeReferenceEqualTo(RxDisposable.disposed) 56 | ), 57 | ], 58 | ), 59 | describe( 60 | "isDisposed", 61 | [ 62 | it("is true", () => 63 | RxSerialDisposable.disposed 64 | |> RxSerialDisposable.isDisposed 65 | |> Expect.toBeEqualToTrue 66 | ), 67 | ], 68 | ), 69 | describe( 70 | "setInnerDisposable", 71 | [ 72 | it("disposes the provided disposable", () => { 73 | let disposable = RxDisposable.empty(); 74 | 75 | RxSerialDisposable.disposed 76 | |> RxSerialDisposable.setInnerDisposable(disposable); 77 | 78 | disposable |> RxDisposable.isDisposed |> Expect.toBeEqualToTrue; 79 | }), 80 | ], 81 | ), 82 | ], 83 | ), 84 | ], 85 | ); -------------------------------------------------------------------------------- /packages/rx-disposables/test/TestRunner.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | 3 | ReUnit.run( 4 | describe( 5 | "rx-disposables", 6 | [ 7 | RxCompositeDisposableTest.test, 8 | RxDisposableTest.test, 9 | RxSerialDisposableTest.test, 10 | ], 11 | ), 12 | ); -------------------------------------------------------------------------------- /packages/rx-imperative/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-imperative", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": [ 13 | "@rx-reason/rx-core", 14 | "@rx-reason/rx-disposables", 15 | "@rx-reason/rx-utils-do-not-depend-on" 16 | ], 17 | "bs-dev-dependencies": [ 18 | "@rx-reason/reunit" 19 | ], 20 | "warnings": { 21 | "error": "+101" 22 | }, 23 | "refmt": 3, 24 | "sources": [ 25 | { 26 | "dir": "src" 27 | }, 28 | { 29 | "dir": "test", 30 | "subdirs": true, 31 | "type": "dev" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /packages/rx-imperative/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-imperative", 3 | "version": "0.0.0", 4 | "description": "Rx constructs for stateful programming", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "start": "bsb -make-world -w", 8 | "clean": "bsb -clean-world", 9 | "test": "npm run build && node lib/js/test/TestRunner.bs.js", 10 | "test-debug": "npm run build && node --inspect-brk lib/js/test/TestRunner.bs.js", 11 | "test-coverage": "npm run build && nyc node lib/js/test/TestRunner.bs.js && nyc report --reporter=html && open coverage/index.html" 12 | }, 13 | "keywords": [ 14 | "ocaml", 15 | "reason", 16 | "reasonml", 17 | "rx", 18 | "BuckleScript" 19 | ], 20 | "author": { 21 | "name": "David Bordoley", 22 | "email": "bordoley@gmail.com" 23 | }, 24 | "contributors": [ 25 | { 26 | "name": "David Bordoley", 27 | "email": "bordoley@gmail.com" 28 | } 29 | ], 30 | "license": "MIT", 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/bordoley/rx-reason.git" 34 | }, 35 | "dependencies": { 36 | "@rx-reason/rx-core": "^0.0.0", 37 | "@rx-reason/rx-disposables": "^0.0.0", 38 | "@rx-reason/rx-utils-do-not-depend-on": "0.0.0" 39 | }, 40 | "devDependencies": { 41 | "bs-platform": "^4.0.7", 42 | "nyc": "^13.1.0", 43 | "@rx-reason/reunit": "^0.0.0" 44 | } 45 | } -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxEvent.re: -------------------------------------------------------------------------------- 1 | type t('a) = RxSubject.t('a); 2 | 3 | let asDisposable = RxSubject.asDisposable; 4 | let asObservable = RxSubject.asObservable; 5 | let create = RxSubject.createMulticast; 6 | let dispose = RxSubject.dispose; 7 | let disposed = RxSubject.disposed; 8 | let isDisposed = RxSubject.isDisposed; 9 | let dispatch = RxSubject.next; -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxEvent.rei: -------------------------------------------------------------------------------- 1 | type t('a); 2 | 3 | include RxDisposable.S1 with type t('a) := t('a); 4 | include RxEventLike.S1 with type t('a) := t('a); 5 | 6 | let create: unit => t('a); 7 | let disposed: t('a); -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxEventLike.re: -------------------------------------------------------------------------------- 1 | module type S = { 2 | type a; 3 | type t; 4 | 5 | include RxObservable.S with type a := a and type t := t; 6 | 7 | let dispatch: ('a, t) => unit; 8 | }; 9 | 10 | module type S1 = { 11 | type t('a); 12 | 13 | include RxObservable.S1 with type t('a) := t('a); 14 | 15 | let dispatch: ('a, t('a)) => unit; 16 | }; -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxSubject.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * A type that both an Observable sequence as well as an Subscriber. 3 | */; 4 | type t('a); 5 | 6 | include RxDisposable.S1 with type t('a) := t('a); 7 | include RxSubjectLike.S1 with type t('a) := t('a); 8 | 9 | let createMulticast: unit => t('a); 10 | let createReplayBuffer: int => t('a); 11 | let createReplayLast: unit => t('a); 12 | 13 | /** 14 | * A disposed Subject instance. 15 | */ 16 | let disposed: t('a); -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxSubjectLike.re: -------------------------------------------------------------------------------- 1 | module type S = { 2 | type a; 3 | type t; 4 | 5 | include RxObservable.S with type a := a and type t := t; 6 | include RxObserverLike.S with type a := a and type t := t; 7 | }; 8 | 9 | module type S1 = { 10 | type t('a); 11 | 12 | include RxObservable.S1 with type t('a) := t('a); 13 | include RxObserverLike.S1 with type t('a) := t('a); 14 | }; -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxValue.re: -------------------------------------------------------------------------------- 1 | type subscribers('a) = ref(RxCopyOnWriteArray.t(RxSubscriber.t('a))); 2 | 3 | type t('a) = 4 | | Disposed 5 | | Value(ref('a), subscribers('a), RxDisposable.t, RxObservable.t('a)); 6 | 7 | let asObservable = 8 | fun 9 | | Disposed => RxObservable.never 10 | | Value(_, _, _, observable) => observable; 11 | 12 | let asDisposable = 13 | fun 14 | | Disposed => RxDisposable.disposed 15 | | Value(_, _, disposable, _) => disposable; 16 | 17 | let dispose = rxValue => rxValue |> asDisposable |> RxDisposable.dispose; 18 | 19 | let isDisposed = rxValue => rxValue |> asDisposable |> RxDisposable.isDisposed; 20 | 21 | let disposed = Disposed; 22 | 23 | let subscriberTeardown = (subscribers, subscriber) => { 24 | let currentSubscribers = subscribers^; 25 | subscribers := 26 | currentSubscribers 27 | |> RxCopyOnWriteArray.findAndRemoveReference(subscriber); 28 | }; 29 | 30 | let observableSource = (value, subscribers, disposable, subscriber) => 31 | if (RxDisposable.isDisposed(disposable)) { 32 | subscriber |> RxSubscriber.dispose; 33 | } else { 34 | subscribers := subscribers^ |> RxCopyOnWriteArray.addLast(subscriber); 35 | let disposable = 36 | RxDisposable.create2(subscriberTeardown, subscribers, subscriber); 37 | 38 | subscriber |> RxSubscriber.addDisposable(disposable) |> ignore; 39 | subscriber |> RxSubscriber.next(value^); 40 | }; 41 | 42 | let onDispose = subscribers => { 43 | let currentSubscribers = subscribers^; 44 | currentSubscribers |> RxCopyOnWriteArray.forEach(RxSubscriber.dispose); 45 | subscribers := RxCopyOnWriteArray.empty(); 46 | }; 47 | 48 | let create = initialValue => { 49 | let value = ref(initialValue); 50 | let subscribers = ref(RxCopyOnWriteArray.empty()); 51 | let disposable = RxDisposable.create1(onDispose, subscribers); 52 | let observable = 53 | RxObservable.create3(observableSource, value, subscribers, disposable); 54 | 55 | Value(value, subscribers, disposable, observable); 56 | }; 57 | 58 | let notify = nextValue => 59 | fun 60 | | Value(value, subscribers, _, _) when value^ !== nextValue => { 61 | value := nextValue; 62 | subscribers^ 63 | |> RxCopyOnWriteArray.forEach(RxSubscriber.next(nextValue)); 64 | } 65 | | _ => (); 66 | 67 | let update = (f, self) => 68 | switch (self) { 69 | | Disposed => () 70 | | Value(value, _, _, _) => 71 | let nextValue = f(value^); 72 | notify(nextValue, self); 73 | }; 74 | 75 | let update1 = (f, ctx0, self) => 76 | switch (self) { 77 | | Disposed => () 78 | | Value(value, _, _, _) => 79 | let nextValue = f(ctx0, value^); 80 | notify(nextValue, self); 81 | }; 82 | 83 | let update2 = (f, ctx0, ctx1, self) => 84 | switch (self) { 85 | | Disposed => () 86 | | Value(value, _, _, _) => 87 | let nextValue = f(ctx0, ctx1, value^); 88 | notify(nextValue, self); 89 | }; 90 | 91 | let update3 = (f, ctx0, ctx1, ctx2, self) => 92 | switch (self) { 93 | | Disposed => () 94 | | Value(value, _, _, _) => 95 | let nextValue = f(ctx0, ctx1, ctx2, value^); 96 | notify(nextValue, self); 97 | }; 98 | 99 | let update4 = (f, ctx0, ctx1, ctx2, ctx3, self) => 100 | switch (self) { 101 | | Disposed => () 102 | | Value(value, _, _, _) => 103 | let nextValue = f(ctx0, ctx1, ctx2, ctx3, value^); 104 | notify(nextValue, self); 105 | }; 106 | 107 | let update5 = (f, ctx0, ctx1, ctx2, ctx3, ctx4, self) => 108 | switch (self) { 109 | | Disposed => () 110 | | Value(value, _, _, _) => 111 | let nextValue = f(ctx0, ctx1, ctx2, ctx3, ctx4, value^); 112 | notify(nextValue, self); 113 | }; -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxValue.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * Stateful values whose changes can be observed. 3 | */; 4 | type t('a); 5 | 6 | include RxDisposable.S1 with type t('a) := t('a); 7 | include RxValueLike.S1 with type t('a) := t('a); 8 | 9 | let create: 'a => t('a); 10 | 11 | let disposed: t('a); -------------------------------------------------------------------------------- /packages/rx-imperative/src/RxValueLike.re: -------------------------------------------------------------------------------- 1 | module type S = { 2 | type a; 3 | type t; 4 | 5 | include RxObservable.S with type a := a and type t := t; 6 | 7 | let update: (a => a, t) => unit; 8 | 9 | let update1: (('ctx0, a) => a, 'ctx0, t) => unit; 10 | 11 | let update2: (('ctx0, 'ctx1, a) => a, 'ctx0, 'ctx1, t) => unit; 12 | 13 | let update3: (('ctx0, 'ctx1, 'ctx2, a) => a, 'ctx0, 'ctx1, 'ctx2, t) => unit; 14 | 15 | let update4: 16 | (('ctx0, 'ctx1, 'ctx2, 'ctx3, a) => a, 'ctx0, 'ctx1, 'ctx2, 'ctx3, t) => 17 | unit; 18 | 19 | let update5: 20 | ( 21 | ('ctx0, 'ctx1, 'ctx2, 'ctx3, 'ctx4, a) => a, 22 | 'ctx0, 23 | 'ctx1, 24 | 'ctx2, 25 | 'ctx3, 26 | 'ctx4, 27 | t 28 | ) => 29 | unit; 30 | }; 31 | 32 | module type S1 = { 33 | type t('a); 34 | 35 | include RxObservable.S1 with type t('a) := t('a); 36 | 37 | let update: ('a => 'a, t('a)) => unit; 38 | 39 | let update1: (('ctx0, 'a) => 'a, 'ctx0, t('a)) => unit; 40 | 41 | let update2: (('ctx0, 'ctx1, 'a) => 'a, 'ctx0, 'ctx1, t('a)) => unit; 42 | 43 | let update3: 44 | (('ctx0, 'ctx1, 'ctx2, 'a) => 'a, 'ctx0, 'ctx1, 'ctx2, t('a)) => unit; 45 | 46 | let update4: 47 | ( 48 | ('ctx0, 'ctx1, 'ctx2, 'ctx3, 'a) => 'a, 49 | 'ctx0, 50 | 'ctx1, 51 | 'ctx2, 52 | 'ctx3, 53 | t('a) 54 | ) => 55 | unit; 56 | 57 | let update5: 58 | ( 59 | ('ctx0, 'ctx1, 'ctx2, 'ctx3, 'ctx4, 'a) => 'a, 60 | 'ctx0, 61 | 'ctx1, 62 | 'ctx2, 63 | 'ctx3, 64 | 'ctx4, 65 | t('a) 66 | ) => 67 | unit; 68 | }; -------------------------------------------------------------------------------- /packages/rx-imperative/test/RxEventTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | 4 | let test = 5 | describe( 6 | "RxEvent", 7 | [ 8 | describe( 9 | "asObservable", 10 | [ 11 | it("disposed instance emits no values", () => { 12 | let event = RxEvent.disposed; 13 | let observedValue = ref(0); 14 | 15 | event 16 | |> RxEvent.asObservable 17 | |> RxObservable.lift( 18 | RxSubscriber.decorate( 19 | ~onNext=(_, next) => observedValue := next, 20 | ~onComplete=(_, _) => (), 21 | ), 22 | ) 23 | |> RxObservable.connect 24 | |> RxDisposable.isDisposed 25 | |> Expect.toBeEqualToTrue; 26 | 27 | event |> RxEvent.dispatch(1); 28 | observedValue^ |> Expect.toBeEqualToInt(0); 29 | }), 30 | it("subscribe after dispose returns a disposed Disposable", () => { 31 | let event = RxEvent.create(); 32 | event |> RxEvent.dispose; 33 | 34 | event 35 | |> RxEvent.asObservable 36 | |> RxObservable.connect 37 | |> RxDisposable.isDisposed 38 | |> Expect.toBeEqualToTrue; 39 | }), 40 | it("emits no values after being disposed", () => { 41 | let event = RxEvent.create(); 42 | let observedValue = ref(0); 43 | 44 | event |> RxEvent.dispose; 45 | 46 | event 47 | |> RxEvent.asObservable 48 | |> RxObservable.lift( 49 | RxSubscriber.decorate( 50 | ~onNext=(_, next) => observedValue := next, 51 | ~onComplete=(_, _) => (), 52 | ), 53 | ) 54 | |> RxObservable.connect 55 | |> ignore; 56 | 57 | event |> RxEvent.dispatch(1); 58 | observedValue^ |> Expect.toBeEqualToInt(0); 59 | }), 60 | it( 61 | "no longer emits notifications when the subscription is disposed", 62 | () => { 63 | let event = RxEvent.create(); 64 | 65 | let observedValue1 = ref(0); 66 | let subscription1 = 67 | event 68 | |> RxEvent.asObservable 69 | |> RxObservable.lift( 70 | RxSubscriber.decorate( 71 | ~onNext=(_, next) => observedValue1 := next, 72 | ~onComplete=(_, _) => (), 73 | ), 74 | ) 75 | |> RxObservable.connect; 76 | 77 | let observedValue2 = ref(0); 78 | let subscription2 = 79 | event 80 | |> RxEvent.asObservable 81 | |> RxObservable.lift( 82 | RxSubscriber.decorate( 83 | ~onNext=(_, next) => observedValue2 := next, 84 | ~onComplete=(_, _) => (), 85 | ), 86 | ) 87 | |> RxObservable.connect; 88 | 89 | observedValue1^ |> Expect.toBeEqualToInt(0); 90 | observedValue2^ |> Expect.toBeEqualToInt(0); 91 | 92 | event |> RxEvent.dispatch(1); 93 | observedValue1^ |> Expect.toBeEqualToInt(1); 94 | observedValue2^ |> Expect.toBeEqualToInt(1); 95 | 96 | subscription1 |> RxDisposable.dispose; 97 | event |> RxEvent.dispatch(2); 98 | observedValue1^ |> Expect.toBeEqualToInt(1); 99 | observedValue2^ |> Expect.toBeEqualToInt(2); 100 | 101 | subscription2 |> RxDisposable.dispose; 102 | event |> RxEvent.dispatch(3); 103 | observedValue1^ |> Expect.toBeEqualToInt(1); 104 | observedValue2^ |> Expect.toBeEqualToInt(2); 105 | }), 106 | ], 107 | ), 108 | ], 109 | ); -------------------------------------------------------------------------------- /packages/rx-imperative/test/RxSubjectTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | 4 | let test = 5 | describe( 6 | "RxSubject", 7 | [ 8 | ], 9 | ); -------------------------------------------------------------------------------- /packages/rx-imperative/test/TestRunner.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | 3 | ReUnit.run( 4 | describe( 5 | "rx-imperative", 6 | [ 7 | RxEventTest.test, 8 | RxSubjectTest.test, 9 | RxValueTest.test, 10 | ], 11 | ), 12 | ); -------------------------------------------------------------------------------- /packages/rx-observables/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-observables", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": [ 13 | "@rx-reason/rx-core", 14 | "@rx-reason/rx-disposables", 15 | "@rx-reason/rx-scheduler", 16 | "@rx-reason/rx-imperative", 17 | "@rx-reason/rx-utils-do-not-depend-on" 18 | ], 19 | "bs-dev-dependencies": [ 20 | "@rx-reason/reunit", 21 | "@rx-reason/rx-virtual-time-scheduler" 22 | ], 23 | "warnings": { 24 | "error": "+101" 25 | }, 26 | "refmt": 3, 27 | "sources": [ 28 | { 29 | "dir": "src", 30 | "subdirs": [ 31 | { 32 | "dir": "internal", 33 | "public": "none", 34 | "subdirs": [ 35 | { 36 | "dir": "observables", 37 | "public": "none" 38 | }, 39 | { 40 | "dir": "operators", 41 | "public": "none" 42 | } 43 | ] 44 | } 45 | ] 46 | }, 47 | { 48 | "dir": "test", 49 | "subdirs": true, 50 | "type": "dev" 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /packages/rx-observables/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-observables", 3 | "version": "0.0.0", 4 | "description": "Functions for creating observables", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "start": "bsb -make-world -w", 8 | "clean": "bsb -clean-world", 9 | "test": "npm run build && node lib/js/test/TestRunner.bs.js", 10 | "test-debug": "npm run build && node --inspect-brk lib/js/test/TestRunner.bs.js", 11 | "test-coverage": "npm run build && nyc node lib/js/test/TestRunner.bs.js && nyc report --reporter=html && open coverage/index.html" 12 | }, 13 | "keywords": [ 14 | "ocaml", 15 | "reason", 16 | "reasonml", 17 | "rx", 18 | "BuckleScript" 19 | ], 20 | "author": { 21 | "name": "David Bordoley", 22 | "email": "bordoley@gmail.com" 23 | }, 24 | "contributors": [ 25 | { 26 | "name": "David Bordoley", 27 | "email": "bordoley@gmail.com" 28 | } 29 | ], 30 | "license": "MIT", 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/bordoley/rx-reason.git" 34 | }, 35 | "dependencies": { 36 | "@rx-reason/rx-core": "^0.0.0", 37 | "@rx-reason/rx-disposables": "^0.0.0", 38 | "@rx-reason/rx-scheduler": "^0.0.0", 39 | "@rx-reason/rx-imperative": "^0.0.0", 40 | "@rx-reason/rx-utils-do-not-depend-on": "0.0.0" 41 | }, 42 | "devDependencies": { 43 | "bs-platform": "^4.0.7", 44 | "nyc": "^13.1.0", 45 | "@rx-reason/reunit": "^0.0.0", 46 | "@rx-reason/rx-virtual-time-scheduler": "^0.0.0" 47 | } 48 | } -------------------------------------------------------------------------------- /packages/rx-observables/src/RxEmptyException.re: -------------------------------------------------------------------------------- 1 | exception Exn; 2 | 3 | let raise = () => raise(Exn); -------------------------------------------------------------------------------- /packages/rx-observables/src/RxTimeoutException.re: -------------------------------------------------------------------------------- 1 | exception Exn; 2 | 3 | let raise = () => raise(Exn); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/CombineLatest2Observable.re: -------------------------------------------------------------------------------- 1 | type context('a, 'b, 'c) = { 2 | value0: RxMutableOption.t('a), 3 | value1: RxMutableOption.t('b), 4 | selector: ('a, 'b) => 'c, 5 | subscription0: ref(RxDisposable.t), 6 | subscription1: ref(RxDisposable.t), 7 | subscriber: RxSubscriber.t('c), 8 | }; 9 | 10 | let onNext = (ctx, value, _, next) => { 11 | value |> RxMutableOption.set(next); 12 | 13 | let haveValues = 14 | RxMutableOption.isNotEmpty(ctx.value0) 15 | && RxMutableOption.isNotEmpty(ctx.value1); 16 | if (haveValues) { 17 | let next = 18 | ctx.selector( 19 | RxMutableOption.get(ctx.value0), 20 | RxMutableOption.get(ctx.value1), 21 | ); 22 | ctx.subscriber |> RxSubscriber.next(next); 23 | }; 24 | }; 25 | 26 | let onComplete = (ctx, _, other, exn) => 27 | switch (exn) { 28 | | Some(_) => ctx.subscriber |> RxSubscriber.complete(~exn?) 29 | | None => 30 | let shouldComplete = RxDisposable.isDisposed(other^); 31 | if (shouldComplete) { 32 | ctx.subscriber |> RxSubscriber.complete(~exn?); 33 | }; 34 | }; 35 | 36 | let observableSource = (selector, observable0, observable1, subscriber) => { 37 | let ctx = { 38 | value0: RxMutableOption.create(), 39 | value1: RxMutableOption.create(), 40 | selector, 41 | subscription0: ref(RxDisposable.disposed), 42 | subscription1: ref(RxDisposable.disposed), 43 | subscriber, 44 | }; 45 | 46 | ctx.subscription0 := 47 | observable0 48 | |> ObserveObservable.create3( 49 | ~onNext, 50 | ~onComplete, 51 | ctx, 52 | ctx.value0, 53 | ctx.subscription1, 54 | ) 55 | |> RxObservable.connect; 56 | 57 | ctx.subscription1 := 58 | observable1 59 | |> ObserveObservable.create3( 60 | ~onNext, 61 | ~onComplete, 62 | ctx, 63 | ctx.value1, 64 | ctx.subscription0, 65 | ) 66 | |> RxObservable.connect; 67 | 68 | subscriber 69 | |> RxSubscriber.addDisposable(ctx.subscription0^) 70 | |> RxSubscriber.addDisposable(ctx.subscription1^) 71 | |> RxSubscriber.addDisposable( 72 | RxDisposable.create1(RxMutableOption.unset, ctx.value0), 73 | ) 74 | |> RxSubscriber.addDisposable( 75 | RxDisposable.create1(RxMutableOption.unset, ctx.value1), 76 | ) 77 | |> ignore; 78 | }; 79 | 80 | let create = (~selector, observable0, observable1) => 81 | RxObservable.create3(observableSource, selector, observable0, observable1); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ConcatListObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler=?, observables) => 2 | observables |> MergeListObservable.create(~scheduler?, ~maxConcurrency=1); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ConcatMapObservable.re: -------------------------------------------------------------------------------- 1 | let create = (f, observable) => 2 | observable |> RxObservable.lift(ConcatMapOperator.create(f)); 3 | 4 | let create1 = (f, ctx0, observable) => 5 | observable |> RxObservable.lift(ConcatMapOperator.create1(f, ctx0)); 6 | 7 | let create2 = (f, ctx0, ctx1, observable) => 8 | observable |> RxObservable.lift(ConcatMapOperator.create2(f, ctx0, ctx1)); 9 | 10 | let create3 = (f, ctx0, ctx1, ctx2, observable) => 11 | observable 12 | |> RxObservable.lift(ConcatMapOperator.create3(f, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ConcatObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(ConcatOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/DebounceTimeObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler, dueTime, observable) => 2 | observable 3 | |> RxObservable.lift(DebounceTimeOperator.create(~scheduler, dueTime)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/DefaultIfEmptyObservable.re: -------------------------------------------------------------------------------- 1 | let create = (default, observable) => 2 | observable |> RxObservable.lift(DefaultIfEmptyOperator.create(default)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/DeferObservable.re: -------------------------------------------------------------------------------- 1 | let create = { 2 | let source = (f, subscriber) => { 3 | let innerSubscription = 4 | f() 5 | |> PublishToSubscriberObservable.create(subscriber) 6 | |> RxObservable.connect; 7 | 8 | subscriber |> RxSubscriber.addDisposable(innerSubscription) |> ignore; 9 | }; 10 | 11 | f => RxObservable.create1(source, f); 12 | }; 13 | 14 | let create1 = { 15 | let source = (f, ctx0, subscriber) => { 16 | let innerSubscription = 17 | f(ctx0) 18 | |> PublishToSubscriberObservable.create(subscriber) 19 | |> RxObservable.connect; 20 | 21 | subscriber |> RxSubscriber.addDisposable(innerSubscription) |> ignore; 22 | }; 23 | 24 | (f, ctx0) => RxObservable.create2(source, f, ctx0); 25 | }; 26 | 27 | let create2 = { 28 | let source = (f, ctx0, ctx1, subscriber) => { 29 | let innerSubscription = 30 | f(ctx0, ctx1) 31 | |> PublishToSubscriberObservable.create(subscriber) 32 | |> RxObservable.connect; 33 | 34 | subscriber |> RxSubscriber.addDisposable(innerSubscription) |> ignore; 35 | }; 36 | 37 | (f, ctx0, ctx1) => RxObservable.create3(source, f, ctx0, ctx1); 38 | }; 39 | 40 | let create3 = { 41 | let source = (f, ctx0, ctx1, ctx2, subscriber) => { 42 | let innerSubscription = 43 | f(ctx0, ctx1, ctx2) 44 | |> PublishToSubscriberObservable.create(subscriber) 45 | |> RxObservable.connect; 46 | 47 | subscriber |> RxSubscriber.addDisposable(innerSubscription) |> ignore; 48 | }; 49 | 50 | (f, ctx0, ctx1, ctx2) => RxObservable.create4(source, f, ctx0, ctx1, ctx2); 51 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/DelayObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler, delay, observable) => 2 | observable |> RxObservable.lift(DelayOperator.create(~scheduler, delay)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/DematerializeObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(DematerializeOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/DistinctUntilChangedObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~equals=?, observable) => 2 | observable 3 | |> RxObservable.lift(DistinctUntilChangedOperator.create(~equals?)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/EmptyObservable.re: -------------------------------------------------------------------------------- 1 | let emptySynchronousSource = subscriber => subscriber |> RxSubscriber.complete; 2 | 3 | let create = (~scheduler=?, ()) => { 4 | let source = RxObservable.create(emptySynchronousSource); 5 | 6 | switch (scheduler) { 7 | | Some(scheduler) => source |> SubscribeOnObservable.create(scheduler) 8 | | None => source 9 | }; 10 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/EveryObservable.re: -------------------------------------------------------------------------------- 1 | let create = (predicate, observable) => 2 | observable |> RxObservable.lift(EveryOperator.create(predicate)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ExhaustMapObservable.re: -------------------------------------------------------------------------------- 1 | let create = (f, observable) => 2 | observable |> RxObservable.lift(ExhaustMapOperator.create(f)); 3 | 4 | let create1 = (f, ctx0, observable) => 5 | observable |> RxObservable.lift(ExhaustMapOperator.create1(f, ctx0)); 6 | 7 | let create2 = (f, ctx0, ctx1, observable) => 8 | observable |> RxObservable.lift(ExhaustMapOperator.create2(f, ctx0, ctx1)); 9 | 10 | let create3 = (f, ctx0, ctx1, ctx2, observable) => 11 | observable |> RxObservable.lift(ExhaustMapOperator.create3(f, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ExhaustObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(ExhaustOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/FindObservable.re: -------------------------------------------------------------------------------- 1 | let create = (predicate, observable) => 2 | observable |> RxObservable.lift(FindOperator.create(predicate)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/FirstObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(FirstOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/FirstOrNoneObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(FirstOrNoneOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/IgnoreElementsObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(IgnoreElementsOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/IsEmptyObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(IsEmptyOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/KeepObservable.re: -------------------------------------------------------------------------------- 1 | let create = (predicate, observable) => 2 | observable |> RxObservable.lift(KeepOperator.create(predicate)); 3 | 4 | let create1 = (predicate, ctx0, observable) => 5 | observable |> RxObservable.lift(KeepOperator.create1(predicate, ctx0)); 6 | 7 | let create2 = (predicate, ctx0, ctx1, observable) => 8 | observable 9 | |> RxObservable.lift(KeepOperator.create2(predicate, ctx0, ctx1)); 10 | 11 | let create3 = (predicate, ctx0, ctx1, ctx2, observable) => 12 | observable 13 | |> RxObservable.lift(KeepOperator.create3(predicate, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/LastObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(LastOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/LastOrNoneObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(LastOrNoneOperator.create); 3 | -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MapObservable.re: -------------------------------------------------------------------------------- 1 | let create = (f, observable) => 2 | observable |> RxObservable.lift(MapOperator.create(f)); 3 | 4 | let create1 = (f, ctx0, observable) => 5 | observable |> RxObservable.lift(MapOperator.create1(f, ctx0)); 6 | 7 | let create2 = (f, ctx0, ctx1, observable) => 8 | observable |> RxObservable.lift(MapOperator.create2(f, ctx0, ctx1)); 9 | 10 | let create3 = (f, ctx0, ctx1, ctx2, observable) => 11 | observable |> RxObservable.lift(MapOperator.create3(f, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MapToObservable.re: -------------------------------------------------------------------------------- 1 | let create = (value, observable) => 2 | observable |> RxObservable.lift(MapToOperator.create(value)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MaterializeObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(MaterializeOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MaybeFirstObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(MaybeFirstOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MaybeLastObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(MaybeLastOperator.create) -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MergeListObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler=?, ~maxConcurrency=?, observables) => 2 | observables 3 | |> OfListObservable.create(~scheduler?) 4 | |> RxObservable.lift(MergeOperator.create(~maxConcurrency?)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MergeMapObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~maxBufferSize=?, ~maxConcurrency=?, f, observable) => 2 | observable 3 | |> RxObservable.lift( 4 | MergeMapOperator.create(~maxBufferSize?, ~maxConcurrency?, f), 5 | ); 6 | 7 | let create1 = (~maxBufferSize=?, ~maxConcurrency=?, f, ctx0, observable) => 8 | observable 9 | |> RxObservable.lift( 10 | MergeMapOperator.create1(~maxBufferSize?, ~maxConcurrency?, f, ctx0), 11 | ); 12 | 13 | let create2 = (~maxBufferSize=?, ~maxConcurrency=?, f, ctx0, ctx1, observable) => 14 | observable 15 | |> RxObservable.lift( 16 | MergeMapOperator.create2( 17 | ~maxBufferSize?, 18 | ~maxConcurrency?, 19 | f, 20 | ctx0, 21 | ctx1, 22 | ), 23 | ); 24 | 25 | let create3 = 26 | (~maxBufferSize=?, ~maxConcurrency=?, f, ctx0, ctx1, ctx2, observable) => 27 | observable 28 | |> RxObservable.lift( 29 | MergeMapOperator.create3( 30 | ~maxBufferSize?, 31 | ~maxConcurrency?, 32 | f, 33 | ctx0, 34 | ctx1, 35 | ctx2, 36 | ), 37 | ); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/MergeObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~maxBufferSize=?, ~maxConcurrency=?, observable) => 2 | observable 3 | |> RxObservable.lift( 4 | MergeOperator.create(~maxBufferSize?, ~maxConcurrency?), 5 | ); 6 | -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/NoneObservable.re: -------------------------------------------------------------------------------- 1 | let create = (predicate, observable) => 2 | observable |> RxObservable.lift(NoneOperator.create(predicate)); 3 | -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ObserveObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~onNext, ~onComplete, observable) => 2 | observable 3 | |> RxObservable.lift(ObserveOperator.create(~onNext, ~onComplete)); 4 | 5 | let create1 = (~onNext, ~onComplete, ctx0, observable) => 6 | observable 7 | |> RxObservable.lift(ObserveOperator.create1(~onNext, ~onComplete, ctx0)); 8 | 9 | let create2 = (~onNext, ~onComplete, ctx0, ctx1, observable) => 10 | observable 11 | |> RxObservable.lift( 12 | ObserveOperator.create2(~onNext, ~onComplete, ctx0, ctx1), 13 | ); 14 | 15 | let create3 = (~onNext, ~onComplete, ctx0, ctx1, ctx2, observable) => 16 | observable 17 | |> RxObservable.lift( 18 | ObserveOperator.create3(~onNext, ~onComplete, ctx0, ctx1, ctx2), 19 | ); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ObserveOnObservable.re: -------------------------------------------------------------------------------- 1 | let create = (scheduler, observable) => 2 | observable |> RxObservable.lift(ObserveOnOperator.create(scheduler)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/OfListObservable.re: -------------------------------------------------------------------------------- 1 | let ofListSynchronousSource = (list, subscriber) => { 2 | let rec loop = list => 3 | switch (list) { 4 | | [hd, ...tail] => 5 | subscriber |> RxSubscriber.next(hd); 6 | loop(tail); 7 | | [] => subscriber |> RxSubscriber.complete 8 | }; 9 | loop(list); 10 | }; 11 | 12 | let ofListScheduledSource = (scheduler, list, subscriber) => { 13 | let rec loop = (list, ~now, ~shouldYield) => 14 | if (RxSubscriber.isDisposed(subscriber)) { 15 | RxScheduler.Result.complete; 16 | } else { 17 | switch (list) { 18 | | [hd, ...tail] => 19 | subscriber |> RxSubscriber.next(hd); 20 | 21 | /* Keep pushing values until told to yield */ 22 | if (shouldYield()) { 23 | RxScheduler.Result.yield(loop(tail)); 24 | } else { 25 | loop(tail, ~now, ~shouldYield); 26 | }; 27 | | [] => 28 | subscriber |> RxSubscriber.complete; 29 | RxScheduler.Result.complete; 30 | }; 31 | }; 32 | 33 | let schedulerSubscription = scheduler |> RxScheduler.schedule(loop(list)); 34 | subscriber |> RxSubscriber.addDisposable(schedulerSubscription) |> ignore; 35 | }; 36 | 37 | let create = (~scheduler=?, list) => 38 | switch (scheduler) { 39 | | Some(scheduler) => 40 | RxObservable.create2(ofListScheduledSource, scheduler, list) 41 | | None => RxObservable.create1(ofListSynchronousSource, list) 42 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/OfNotificationsObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler=?, notifications) => 2 | OfListObservable.create(~scheduler?, notifications) 3 | |> RxObservable.lift(DematerializeOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/OfValueObservable.re: -------------------------------------------------------------------------------- 1 | let ofValueSynchronousSource = (value, subscriber) => { 2 | subscriber |> RxSubscriber.next(value); 3 | subscriber |> RxSubscriber.complete; 4 | }; 5 | 6 | let create = (~scheduler=?, value) => { 7 | let source = RxObservable.create1(ofValueSynchronousSource, value); 8 | 9 | switch (scheduler) { 10 | | Some(scheduler) => source |> SubscribeOnObservable.create(scheduler) 11 | | None => source 12 | }; 13 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/OnCompleteObservable.re: -------------------------------------------------------------------------------- 1 | let create = (onComplete, observable) => 2 | observable |> RxObservable.lift(OnCompleteOperator.create(onComplete)); 3 | 4 | let create1 = (onComplete, ctx0, observable) => 5 | observable 6 | |> RxObservable.lift(OnCompleteOperator.create1(onComplete, ctx0)); 7 | 8 | let create2 = (onComplete, ctx0, ctx1, observable) => 9 | observable 10 | |> RxObservable.lift(OnCompleteOperator.create2(onComplete, ctx0, ctx1)); 11 | 12 | let create3 = (onComplete, ctx0, ctx1, ctx2, observable) => 13 | observable 14 | |> RxObservable.lift(OnCompleteOperator.create3(onComplete, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/OnConnectObservable.re: -------------------------------------------------------------------------------- 1 | let create = (f, observable) => 2 | observable |> RxObservable.lift(OnConnectOperator.create(f)); 3 | 4 | let create1 = (f, ctx0, observable) => 5 | observable |> RxObservable.lift(OnConnectOperator.create1(f, ctx0)); 6 | 7 | let create2 = (f, ctx0, ctx1, observable) => 8 | observable |> RxObservable.lift(OnConnectOperator.create2(f, ctx0, ctx1)); 9 | 10 | let create3 = (f, ctx0, ctx1, ctx2, observable) => 11 | observable |> RxObservable.lift(OnConnectOperator.create3(f, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/OnExnObservable.re: -------------------------------------------------------------------------------- 1 | let create = (onExn, observable) => 2 | observable |> RxObservable.lift(OnExnOperator.create(onExn)); 3 | 4 | let create1 = (onExn, ctx0, observable) => 5 | observable |> RxObservable.lift(OnExnOperator.create1(onExn, ctx0)); 6 | 7 | let create2 = (onExn, ctx0, ctx1, observable) => 8 | observable |> RxObservable.lift(OnExnOperator.create2(onExn, ctx0, ctx1)); 9 | 10 | let create3 = (onExn, ctx0, ctx1, ctx2, observable) => 11 | observable |> RxObservable.lift(OnExnOperator.create3(onExn, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/OnNextObservable.re: -------------------------------------------------------------------------------- 1 | let create = (onNext, observable) => 2 | observable 3 | |> ObserveObservable.create(~onNext, ~onComplete=RxFunctions.alwaysUnit1); 4 | 5 | let create1 = (onNext, ctx0, observable) => 6 | observable 7 | |> ObserveObservable.create1( 8 | ~onNext, 9 | ~onComplete=RxFunctions.alwaysUnit2, 10 | ctx0, 11 | ); 12 | 13 | let create2 = (onNext, ctx0, ctx1, observable) => 14 | observable 15 | |> ObserveObservable.create2( 16 | ~onNext, 17 | ~onComplete=RxFunctions.alwaysUnit3, 18 | ctx0, 19 | ctx1, 20 | ); 21 | 22 | let create3 = (onNext, ctx0, ctx1, ctx2, observable) => 23 | observable 24 | |> ObserveObservable.create3( 25 | ~onNext, 26 | ~onComplete=RxFunctions.alwaysUnit4, 27 | ctx0, 28 | ctx1, 29 | ctx2, 30 | ); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/PublishToSubjectObservable.re: -------------------------------------------------------------------------------- 1 | let create = (subject, observable) => observable |> RxObservable.lift( 2 | PublishToSubjectOperator.create(subject) 3 | ); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/PublishToSubscriberObservable.re: -------------------------------------------------------------------------------- 1 | let create = (subscriber, observable) => observable |> RxObservable.lift( 2 | PublishToSubscriberOperator.create(subscriber) 3 | ); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/RaiseObservable.re: -------------------------------------------------------------------------------- 1 | let raiseSynchronousSource = (exn, subscriber) => 2 | subscriber |> RxSubscriber.complete(~exn?); 3 | 4 | let create = (~scheduler=?, exn) => { 5 | let exn = Some(exn); 6 | let source = RxObservable.create1(raiseSynchronousSource, exn); 7 | 8 | switch (scheduler) { 9 | | Some(scheduler) => source |> SubscribeOnObservable.create(scheduler) 10 | | None => source 11 | }; 12 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/RepeatObservable.re: -------------------------------------------------------------------------------- 1 | let defaultPredicate = 2 | fun 3 | | None => true 4 | | Some(_) => false; 5 | 6 | let create = (~predicate=RxFunctions.alwaysTrue1) => { 7 | let predicate = 8 | predicate === RxFunctions.alwaysTrue1 ? 9 | defaultPredicate : 10 | ( 11 | fun 12 | | None => predicate() 13 | | Some(_) => false 14 | ); 15 | 16 | observable => 17 | observable 18 | |> RxObservable.lift(RepeatOperator.create(predicate, observable)); 19 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/RetryObservable.re: -------------------------------------------------------------------------------- 1 | let defaultPredicate = 2 | fun 3 | | None => false 4 | | Some(_) => true; 5 | 6 | let create = (~predicate=RxFunctions.alwaysTrue1) => { 7 | let predicate = 8 | predicate === RxFunctions.alwaysTrue1 ? 9 | defaultPredicate : 10 | ( 11 | fun 12 | | None => false 13 | | Some(exn) => predicate(exn) 14 | ); 15 | 16 | observable => 17 | observable 18 | |> RxObservable.lift(RepeatOperator.create(predicate, observable)); 19 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ScanObservable.re: -------------------------------------------------------------------------------- 1 | let create = (reducer, initialValue, observable) => 2 | observable |> RxObservable.lift(ScanOperator.create(reducer, initialValue)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ShareObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | ShareWithSubjectFactoryObservable.create( 3 | RxSubject.createMulticast, 4 | observable, 5 | ); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ShareReplayBufferObservable.re: -------------------------------------------------------------------------------- 1 | let create = count => { 2 | RxPreconditions.checkArgument( 3 | count > 0, 4 | "ShareWithReplayBufferObservable: count must be greater than 0.", 5 | ); 6 | 7 | let createSubject = () => RxSubject.createReplayBuffer(count); 8 | obs => ShareWithSubjectFactoryObservable.create(createSubject, obs); 9 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ShareReplayLast.re: -------------------------------------------------------------------------------- 1 | let create = obs => 2 | ShareWithSubjectFactoryObservable.create(RxSubject.createReplayLast, obs); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/ShareWithSubjectFactoryObservable.re: -------------------------------------------------------------------------------- 1 | type multicastState('a) = { 2 | createSubject: unit => RxSubject.t('a), 3 | mutable refCount: int, 4 | source: RxObservable.t('a), 5 | mutable subject: RxSubject.t('a), 6 | subscription: RxSerialDisposable.t, 7 | }; 8 | 9 | let teardown = state => { 10 | state.refCount = state.refCount - 1; 11 | 12 | if (state.refCount === 0) { 13 | state.subscription 14 | |> RxSerialDisposable.setInnerDisposable(RxDisposable.disposed); 15 | state.subject |> RxSubject.dispose; 16 | state.subject = RxSubject.disposed; 17 | }; 18 | }; 19 | 20 | let source = (state, subscriber) => { 21 | if (state.refCount === 0) { 22 | state.subject = state.createSubject(); 23 | }; 24 | 25 | state.refCount = state.refCount + 1; 26 | 27 | let innerSubscription = 28 | state.subject 29 | |> RxSubject.asObservable 30 | |> PublishToSubscriberObservable.create(subscriber) 31 | |> RxObservable.connect; 32 | 33 | subscriber 34 | |> RxSubscriber.addDisposable(RxDisposable.create1(teardown, state)) 35 | |> RxSubscriber.addDisposable(innerSubscription) 36 | |> ignore; 37 | 38 | if (state.refCount === 1) { 39 | let subscriber = 40 | state.source 41 | |> PublishToSubjectObservable.create(state.subject) 42 | |> RxObservable.connect; 43 | 44 | state.subscription |> RxSerialDisposable.setInnerDisposable(subscriber); 45 | }; 46 | }; 47 | 48 | let create = (createSubject, observable) => { 49 | let state = { 50 | createSubject, 51 | refCount: 0, 52 | source: observable, 53 | subscription: RxSerialDisposable.create(), 54 | subject: RxSubject.disposed, 55 | }; 56 | 57 | RxObservable.create1(source, state); 58 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/SkipObservable.re: -------------------------------------------------------------------------------- 1 | let create = (skipCount, observable) => 2 | observable |> RxObservable.lift(SkipOperator.create(skipCount)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/SomeObservable.re: -------------------------------------------------------------------------------- 1 | let create = (predicate, observable) => 2 | observable |> RxObservable.lift(SomeOperator.create(predicate)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/StartWithListObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler=?, values, observable) => 2 | ConcatListObservable.create([ 3 | OfListObservable.create(~scheduler?, values), 4 | observable, 5 | ]); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/StartWithValueObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler=?, value, observable) => 2 | ConcatListObservable.create([ 3 | OfValueObservable.create(~scheduler?, value), 4 | observable, 5 | ]); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/SubscribeOnObservable.re: -------------------------------------------------------------------------------- 1 | let doSubscribe = (observable, subscriber, ~now as _, ~shouldYield as _) => { 2 | let innerSubscription = 3 | observable 4 | |> PublishToSubscriberObservable.create(subscriber) 5 | |> RxObservable.connect; 6 | 7 | subscriber |> RxSubscriber.addDisposable(innerSubscription) |> ignore; 8 | 9 | RxScheduler.Result.complete; 10 | }; 11 | 12 | let subscribeOnSource = (delay, scheduler, observable, subscriber) => { 13 | let schedulerSubscription = 14 | scheduler 15 | |> RxScheduler.schedule(~delay?, doSubscribe(observable, subscriber)); 16 | subscriber |> RxSubscriber.addDisposable(schedulerSubscription) |> ignore; 17 | }; 18 | 19 | let create = (~delay=?, scheduler) => { 20 | switch (delay) { 21 | | Some(delayInMs) => 22 | RxPreconditions.checkArgument( 23 | delayInMs > 0.0, 24 | "SubscribeOnObservable: If specified, delay must be greater than 0.0 milliseconds", 25 | ) 26 | | _ => () 27 | }; 28 | 29 | observable => 30 | RxObservable.create3(subscribeOnSource, delay, scheduler, observable); 31 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/SwitchMapObservable.re: -------------------------------------------------------------------------------- 1 | let create = (f, observable) => 2 | observable |> RxObservable.lift(SwitchMapOperator.create(f)); 3 | 4 | let create1 = (f, ctx0, observable) => 5 | observable |> RxObservable.lift(SwitchMapOperator.create1(f, ctx0)); 6 | 7 | let create2 = (f, ctx0, ctx1, observable) => 8 | observable |> RxObservable.lift(SwitchMapOperator.create2(f, ctx0, ctx1)); 9 | 10 | let create3 = (f, ctx0, ctx1, ctx2, observable) => 11 | observable 12 | |> RxObservable.lift(SwitchMapOperator.create3(f, ctx0, ctx1, ctx2)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/SwitchObservable.re: -------------------------------------------------------------------------------- 1 | let create = observable => 2 | observable |> RxObservable.lift(SwitchOperator.create); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/TakeObservable.re: -------------------------------------------------------------------------------- 1 | let create = (takeCount, observable) => 2 | observable |> RxObservable.lift(TakeOperator.create(takeCount)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/TakeUntilObservable.re: -------------------------------------------------------------------------------- 1 | let create = (notifier, observable) => 2 | observable |> RxObservable.lift(TakeUntilOperator.create(notifier)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/TimeoutObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~scheduler, due, observable) => 2 | observable |> RxObservable.lift(TimeoutOperator.create(~scheduler, due)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/UsingObservable.re: -------------------------------------------------------------------------------- 1 | let create = { 2 | let source = (createResource, disposeResource, f, subscriber) => { 3 | let disposable = createResource(); 4 | let innerSubscription = 5 | f(disposable) 6 | |> PublishToSubscriberObservable.create(subscriber) 7 | |> RxObservable.connect; 8 | 9 | subscriber 10 | |> RxSubscriber.addDisposable(RxDisposable.create1(disposeResource, disposable)) 11 | |> RxSubscriber.addDisposable(innerSubscription) 12 | |> ignore; 13 | }; 14 | 15 | (~createResource, ~disposeResource, f) => 16 | RxObservable.create3(source, createResource, disposeResource, f); 17 | }; 18 | 19 | let create1 = { 20 | let source = (createResource, disposeResource, ctx0, f, subscriber) => { 21 | let disposable = createResource(ctx0); 22 | 23 | let innerSubscription = 24 | f(disposable) 25 | |> PublishToSubscriberObservable.create(subscriber) 26 | |> RxObservable.connect; 27 | 28 | subscriber 29 | |> RxSubscriber.addDisposable(RxDisposable.create1(disposeResource, disposable)) 30 | |> RxSubscriber.addDisposable(innerSubscription) 31 | |> ignore; 32 | }; 33 | 34 | (~createResource, ~disposeResource, ctx0, f) => 35 | RxObservable.create4(source, createResource, disposeResource, ctx0, f); 36 | }; 37 | 38 | let create2 = { 39 | let source = (createResource, disposeResource, ctx0, ctx1, f, subscriber) => { 40 | let disposable = createResource(ctx0, ctx1); 41 | let innerSubscription = 42 | f(disposable) 43 | |> PublishToSubscriberObservable.create(subscriber) 44 | |> RxObservable.connect; 45 | 46 | subscriber 47 | |> RxSubscriber.addDisposable(RxDisposable.create1(disposeResource, disposable)) 48 | |> RxSubscriber.addDisposable(innerSubscription) 49 | |> ignore; 50 | }; 51 | 52 | (~createResource, ~disposeResource, ctx0, ctx1, f) => 53 | RxObservable.create5(source, createResource, disposeResource, ctx0, ctx1, f); 54 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/observables/WithLatestFromObservable.re: -------------------------------------------------------------------------------- 1 | let create = (~selector, other, source) => 2 | source |> RxObservable.lift(WithLatestFromOperator.create(~selector, other)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/ConcatMapOperator.re: -------------------------------------------------------------------------------- 1 | let create = f => { 2 | let mapOperator = MapOperator.create(f); 3 | s => mapOperator @@ ConcatOperator.create @@ s; 4 | }; 5 | 6 | let create1 = (f, ctx0) => { 7 | let mapOperator = MapOperator.create1(f, ctx0); 8 | s => mapOperator @@ ConcatOperator.create @@ s; 9 | }; 10 | 11 | let create2 = (f, ctx0, ctx1) => { 12 | let mapOperator = MapOperator.create2(f, ctx0, ctx1); 13 | s => mapOperator @@ ConcatOperator.create @@ s; 14 | }; 15 | 16 | let create3 = (f, ctx0, ctx1, ctx2) => { 17 | let mapOperator = MapOperator.create3(f, ctx0, ctx1, ctx2); 18 | s => mapOperator @@ ConcatOperator.create @@ s; 19 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/ConcatOperator.re: -------------------------------------------------------------------------------- 1 | let create = subscriber => MergeOperator.create(~maxConcurrency=1, subscriber); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/DebounceTimeOperator.re: -------------------------------------------------------------------------------- 1 | let clearDebounce = debounceSubscription => 2 | debounceSubscription 3 | |> RxSerialDisposable.setInnerDisposable(RxDisposable.disposed); 4 | 5 | let debounceNext = (debounceSubscription, lastValue, delegate) => { 6 | clearDebounce(debounceSubscription); 7 | 8 | if (RxMutableOption.isNotEmpty(lastValue)) { 9 | let next = RxMutableOption.get(lastValue); 10 | RxMutableOption.unset(lastValue); 11 | delegate |> RxSubscriber.next(next); 12 | }; 13 | }; 14 | 15 | let onDebounceScheduled = 16 | (debounceSubscription, lastValue, delegate, ~now as _, ~shouldYield as _) => { 17 | debounceNext(debounceSubscription, lastValue, delegate); 18 | RxScheduler.Result.complete; 19 | }; 20 | 21 | let onNext = 22 | (debounceSubscription, lastValue, scheduler, dueTime, delegate, next) => { 23 | clearDebounce(debounceSubscription); 24 | RxMutableOption.set(next, lastValue); 25 | 26 | let schedulerDisposable = 27 | scheduler 28 | |> RxScheduler.schedule( 29 | ~delay=dueTime, 30 | onDebounceScheduled(debounceSubscription, lastValue, delegate), 31 | ); 32 | 33 | debounceSubscription 34 | |> RxSerialDisposable.setInnerDisposable(schedulerDisposable); 35 | }; 36 | 37 | let onComplete = (debounceSubscription, lastValue, _, _, delegate, exn) => { 38 | switch (exn) { 39 | | Some(_) => clearDebounce(debounceSubscription) 40 | | None => debounceNext(debounceSubscription, lastValue, delegate) 41 | }; 42 | delegate |> RxSubscriber.complete(~exn?); 43 | }; 44 | 45 | let create = (~scheduler, dueTime) => { 46 | RxPreconditions.checkArgument( 47 | dueTime > 0.0, 48 | "DebounceOperator: dueTime must be greater than 0.0 milliseconds", 49 | ); 50 | 51 | subscriber => { 52 | let lastValue = RxMutableOption.create(); 53 | let debounceSubscription = RxSerialDisposable.create(); 54 | 55 | subscriber 56 | |> RxSubscriber.decorate4( 57 | ~onNext, 58 | ~onComplete, 59 | debounceSubscription, 60 | lastValue, 61 | scheduler, 62 | dueTime, 63 | ) 64 | |> RxSubscriber.addDisposable( 65 | RxDisposable.create1(RxMutableOption.unset, lastValue), 66 | ) 67 | |> RxSubscriber.addDisposable( 68 | RxSerialDisposable.asDisposable(debounceSubscription), 69 | ); 70 | }; 71 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/DefaultIfEmptyOperator.re: -------------------------------------------------------------------------------- 1 | type context('a) = { 2 | default: 'a, 3 | mutable isEmpty: bool, 4 | }; 5 | 6 | let onNext = (ctx, delegate, next) => { 7 | delegate |> RxSubscriber.next(next); 8 | ctx.isEmpty = false; 9 | }; 10 | 11 | let onComplete = ({default, isEmpty}, delegate, exn) => { 12 | let exn = 13 | switch (exn) { 14 | | Some(RxEmptyException.Exn) 15 | | None => 16 | if (isEmpty) { 17 | delegate |> RxSubscriber.next(default); 18 | }; 19 | None; 20 | | Some(_) => exn 21 | }; 22 | delegate |> RxSubscriber.complete(~exn?); 23 | }; 24 | 25 | let create = (default, subscriber) => { 26 | let ctx = {default, isEmpty: true}; 27 | subscriber |> RxSubscriber.decorate1(~onNext, ~onComplete, ctx); 28 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/DelayOperator.re: -------------------------------------------------------------------------------- 1 | let schedule = (wip, doWork, queue, delay, notification, scheduler) => { 2 | let now = scheduler |> RxScheduler.now; 3 | let dueTime = now +. delay; 4 | queue |> RxMutableQueue.enqueue((dueTime, notification)); 5 | if (RxAtomic.incr(wip) === 1) { 6 | scheduler |> RxScheduler.schedule(~delay, doWork) |> ignore; 7 | }; 8 | }; 9 | 10 | let onNext = (scheduler, wip, doWork, queue, delay, _, next) => 11 | scheduler |> schedule(wip, doWork, queue, delay, RxNotification.next(next)); 12 | 13 | let onComplete = (scheduler, wip, doWork, queue, delay, _, exn) => 14 | scheduler 15 | |> schedule(wip, doWork, queue, delay, RxNotification.complete(exn)); 16 | 17 | let createImpl = (~scheduler, delay, subscriber) => { 18 | let queue = RxMutableQueue.create(); 19 | let wip = RxAtomic.make(0); 20 | 21 | let rec doWork = (~now, ~shouldYield) => { 22 | let currentTime = now(); 23 | let subscriberIsDisposed = subscriber |> RxSubscriber.isDisposed; 24 | let nextDelay = 25 | switch (RxMutableQueue.peek(queue)) { 26 | | Some((dueTime, notification)) 27 | when currentTime >= dueTime && !subscriberIsDisposed => 28 | RxMutableQueue.dequeue(queue) |> ignore; 29 | subscriber |> RxSubscriber.notify(notification); 30 | 0.0; 31 | | Some((dueTime, _)) when !subscriberIsDisposed => 32 | dueTime -. currentTime 33 | | _ => (-1.0) 34 | }; 35 | 36 | let yieldRequested = shouldYield(); 37 | if (nextDelay > 0.0) { 38 | RxScheduler.Result.continueAfter(~delay=nextDelay, doWork); 39 | } else if (nextDelay < 0.0) { 40 | if (RxAtomic.decr(wip) !== 0) { 41 | RxScheduler.Result.yield(doWork); 42 | } else { 43 | RxScheduler.Result.complete; 44 | }; 45 | } else if (yieldRequested) { 46 | RxScheduler.Result.yield(doWork); 47 | } else { 48 | doWork(~now, ~shouldYield); 49 | }; 50 | }; 51 | 52 | subscriber 53 | |> RxSubscriber.decorate5( 54 | ~onNext, 55 | ~onComplete, 56 | scheduler, 57 | wip, 58 | doWork, 59 | queue, 60 | delay, 61 | ) 62 | |> RxSubscriber.addDisposable( 63 | RxDisposable.create1(RxMutableQueue.clear, queue), 64 | ); 65 | }; 66 | 67 | let create = (~scheduler, delay) => { 68 | RxPreconditions.checkArgument( 69 | delay > 0.0, 70 | "DelayOperator: delay must be greater than 0.0 milliseconds", 71 | ); 72 | 73 | createImpl(~scheduler, delay); 74 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/DematerializeOperator.re: -------------------------------------------------------------------------------- 1 | let onNext = { 2 | let onNext = (_, subscriber, v) => subscriber |> RxSubscriber.next(v); 3 | let onComplete = (self, _, exn) => self^ |> RxSubscriber.complete(~exn?); 4 | 5 | (self, delegate, notif) => 6 | notif |> RxNotification.map2(~onNext, ~onComplete, self, delegate); 7 | }; 8 | 9 | let create = subscriber => { 10 | let self = ref(RxSubscriber.disposed); 11 | self := subscriber |> RxSubscriber.decorateOnNext1(onNext, self); 12 | self^; 13 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/DistinctUntilChangedOperator.re: -------------------------------------------------------------------------------- 1 | let defaultShouldUpdate = (a, b) => a !== b; 2 | 3 | let predicate = (shouldUpdate, state, next) => 4 | RxMutableOption.setIf(shouldUpdate, next, state); 5 | 6 | let create = (~equals=RxFunctions.referenceEquality) => { 7 | let shouldUpdate = 8 | equals === RxFunctions.referenceEquality ? 9 | defaultShouldUpdate : ((a, b) => !equals(a, b)); 10 | 11 | subscriber => { 12 | let state = RxMutableOption.create(); 13 | KeepOperator.create2(predicate, shouldUpdate, state, subscriber); 14 | }; 15 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/EveryOperator.re: -------------------------------------------------------------------------------- 1 | exception CompleteWithoutErrorException; 2 | 3 | let completeWithoutErrorExn = Some(CompleteWithoutErrorException); 4 | 5 | let onNext = (self, _, next) => 6 | if (! next) { 7 | self^ |> RxSubscriber.complete(~exn=?completeWithoutErrorExn); 8 | }; 9 | 10 | let onComplete = (_, delegate, exn) => { 11 | let exn = 12 | switch (exn) { 13 | | Some(CompleteWithoutErrorException) => 14 | delegate |> RxSubscriber.next(false); 15 | None; 16 | | None => 17 | delegate |> RxSubscriber.next(true); 18 | None; 19 | | _ => exn 20 | }; 21 | delegate |> RxSubscriber.complete(~exn?); 22 | }; 23 | 24 | let create = (predicate, subscriber) => { 25 | let self = ref(RxSubscriber.disposed); 26 | self := subscriber |> RxSubscriber.decorate1(~onNext, ~onComplete, self); 27 | self^ |> MapOperator.create(predicate); 28 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/ExhaustMapOperator.re: -------------------------------------------------------------------------------- 1 | let create = f => { 2 | let mapOperator = MapOperator.create(f); 3 | s => mapOperator @@ ExhaustOperator.create @@ s; 4 | }; 5 | 6 | let create1 = (f, ctx0) => { 7 | let mapOperator = MapOperator.create1(f, ctx0); 8 | s => mapOperator @@ ExhaustOperator.create @@ s; 9 | }; 10 | 11 | let create2 = (f, ctx0, ctx1) => { 12 | let mapOperator = MapOperator.create2(f, ctx0, ctx1); 13 | s => mapOperator @@ ExhaustOperator.create @@ s; 14 | }; 15 | 16 | let create3 = (f, ctx0, ctx1, ctx2) => { 17 | let mapOperator = MapOperator.create3(f, ctx0, ctx1, ctx2); 18 | s => mapOperator @@ ExhaustOperator.create @@ s; 19 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/ExhaustOperator.re: -------------------------------------------------------------------------------- 1 | let create = s => MergeOperator.create(~maxBufferSize=0, ~maxConcurrency=1, s); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/FindOperator.re: -------------------------------------------------------------------------------- 1 | let create = predicate => { 2 | let keepOperator = KeepOperator.create(predicate); 3 | subscriber => keepOperator @@ MaybeFirstOperator.create @@ subscriber; 4 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/FirstOperator.re: -------------------------------------------------------------------------------- 1 | exception CompleteWithoutErrorException; 2 | 3 | let completeWithoutErrorExn = Some(CompleteWithoutErrorException); 4 | 5 | let onNext = (self, subscriber, next) => { 6 | subscriber |> RxSubscriber.next(next); 7 | self^ |> RxSubscriber.complete(~exn=?completeWithoutErrorExn); 8 | }; 9 | 10 | let onComplete = (_, subscriber, exn) => { 11 | let exn = 12 | switch (exn) { 13 | | Some(CompleteWithoutErrorException) => None 14 | | Some(_) => exn 15 | | _ => Some(RxEmptyException.Exn) 16 | }; 17 | subscriber |> RxSubscriber.complete(~exn?); 18 | }; 19 | 20 | let create = subscriber => { 21 | let self = ref(RxSubscriber.disposed); 22 | self := subscriber |> RxSubscriber.decorate1(~onNext, ~onComplete, self); 23 | self^; 24 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/FirstOrNoneOperator.re: -------------------------------------------------------------------------------- 1 | let create = subscriber => 2 | FirstOperator.create @@ 3 | MapOperator.create(RxFunctions.some) @@ 4 | DefaultIfEmptyOperator.create(None) @@ 5 | subscriber; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/IgnoreElementsOperator.re: -------------------------------------------------------------------------------- 1 | let create = subscriber => 2 | RxSubscriber.decorateOnNext(RxFunctions.alwaysUnit2, subscriber); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/IsEmptyOperator.re: -------------------------------------------------------------------------------- 1 | exception CompleteWithoutErrorException; 2 | 3 | let completeWithoutErrorExn = Some(CompleteWithoutErrorException); 4 | 5 | let onNext = (self, delegate, _) => { 6 | delegate |> RxSubscriber.next(false); 7 | self^ |> RxSubscriber.complete(~exn=?completeWithoutErrorExn); 8 | }; 9 | 10 | let onComplete = (_, delegate, exn) => { 11 | let exn = 12 | switch (exn) { 13 | | Some(CompleteWithoutErrorException) => None 14 | | Some(_) => exn 15 | | None => 16 | delegate |> RxSubscriber.next(true); 17 | exn; 18 | }; 19 | delegate |> RxSubscriber.complete(~exn?); 20 | }; 21 | 22 | let create = subscriber => { 23 | let self = ref(RxSubscriber.disposed); 24 | self := subscriber |> RxSubscriber.decorate1(~onNext, ~onComplete, self); 25 | self^; 26 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/KeepOperator.re: -------------------------------------------------------------------------------- 1 | let create = { 2 | let onNext = (predicate, subscriber, next) => { 3 | let shouldKeep = predicate(next); 4 | if (shouldKeep) { 5 | subscriber |> RxSubscriber.next(next); 6 | }; 7 | }; 8 | predicate => RxSubscriber.decorateOnNext1(onNext, predicate); 9 | }; 10 | 11 | let create1 = { 12 | let onNext = (predicate, ctx0, subscriber, next) => { 13 | let shouldKeep = predicate(ctx0, next); 14 | if (shouldKeep) { 15 | subscriber |> RxSubscriber.next(next); 16 | }; 17 | }; 18 | (predicate, ctx0) => 19 | RxSubscriber.decorateOnNext2(onNext, predicate, ctx0); 20 | }; 21 | 22 | let create2 = { 23 | let onNext = (predicate, ctx0, ctx1, subscriber, next) => { 24 | let shouldKeep = predicate(ctx0, ctx1, next); 25 | if (shouldKeep) { 26 | subscriber |> RxSubscriber.next(next); 27 | }; 28 | }; 29 | (predicate, ctx0, ctx1) => 30 | RxSubscriber.decorateOnNext3(onNext, predicate, ctx0, ctx1); 31 | }; 32 | 33 | let create3 = { 34 | let onNext = (predicate, ctx0, ctx1, ctx2, subscriber, next) => { 35 | let shouldKeep = predicate(ctx0, ctx1, ctx2, next); 36 | if (shouldKeep) { 37 | subscriber |> RxSubscriber.next(next); 38 | }; 39 | }; 40 | (predicate, ctx0, ctx1, ctx2) => 41 | RxSubscriber.decorateOnNext4(onNext, predicate, ctx0, ctx1, ctx2); 42 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/LastOperator.re: -------------------------------------------------------------------------------- 1 | let onNext = (last, _, next) => RxMutableOption.set(next, last); 2 | 3 | let onComplete = (last, delegate, exn) => { 4 | let exn = 5 | switch (exn) { 6 | | Some(_) => exn 7 | | None => 8 | if (RxMutableOption.isEmpty(last)) { 9 | Some(RxEmptyException.Exn); 10 | } else { 11 | let lastValue = RxMutableOption.get(last); 12 | delegate |> RxSubscriber.next(lastValue); 13 | None; 14 | } 15 | }; 16 | delegate |> RxSubscriber.complete(~exn?); 17 | }; 18 | 19 | let create = subscriber => { 20 | let last = RxMutableOption.create(); 21 | let disposable = RxDisposable.create1(RxMutableOption.unset, last); 22 | 23 | subscriber 24 | |> RxSubscriber.decorate1(~onNext, ~onComplete, last) 25 | |> RxSubscriber.addDisposable(disposable); 26 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/LastOrNoneOperator.re: -------------------------------------------------------------------------------- 1 | let create = subscriber => 2 | LastOperator.create @@ 3 | MapOperator.create(RxFunctions.some) @@ 4 | DefaultIfEmptyOperator.create(None) @@ 5 | subscriber; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MapOperator.re: -------------------------------------------------------------------------------- 1 | let create = { 2 | let onNext = (mapper, delegate, next) => { 3 | let mapped = mapper(next); 4 | delegate |> RxSubscriber.next(mapped); 5 | }; 6 | 7 | mapper => RxSubscriber.decorateOnNext1(onNext, mapper); 8 | }; 9 | 10 | let create1 = { 11 | let onNext = (ctx0, mapper, delegate, next) => { 12 | let mapped = mapper(ctx0, next); 13 | delegate |> RxSubscriber.next(mapped); 14 | }; 15 | 16 | (mapper, ctx0) => RxSubscriber.decorateOnNext2(onNext, ctx0, mapper); 17 | }; 18 | 19 | let create2 = { 20 | let onNext = (ctx0, ctx1, mapper, delegate, next) => { 21 | let mapped = mapper(ctx0, ctx1, next); 22 | delegate |> RxSubscriber.next(mapped); 23 | }; 24 | 25 | (mapper, ctx0, ctx1) => 26 | RxSubscriber.decorateOnNext3(onNext, ctx0, ctx1, mapper); 27 | }; 28 | 29 | let create3 = { 30 | let onNext = (ctx0, ctx1, ctx2, mapper, delegate, next) => { 31 | let mapped = mapper(ctx0, ctx1, ctx2, next); 32 | delegate |> RxSubscriber.next(mapped); 33 | }; 34 | 35 | (mapper, ctx0, ctx1, ctx2) => 36 | RxSubscriber.decorateOnNext4(onNext, ctx0, ctx1, ctx2, mapper); 37 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MapToOperator.re: -------------------------------------------------------------------------------- 1 | let onNext = (value, delegate, _) => delegate |> RxSubscriber.next(value); 2 | 3 | let create = (value, subscriber) => 4 | RxSubscriber.decorateOnNext1(onNext, value, subscriber); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MaterializeOperator.re: -------------------------------------------------------------------------------- 1 | let onNext = (delegate, next) => 2 | delegate |> RxSubscriber.next(RxNotification.next(next)); 3 | 4 | let onComplete = (delegate, exn) => { 5 | let next = RxNotification.complete(exn); 6 | delegate |> RxSubscriber.next(next); 7 | delegate |> RxSubscriber.complete; 8 | }; 9 | 10 | let create = subscriber => 11 | subscriber |> RxSubscriber.decorate(~onNext, ~onComplete); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MaybeFirstOperator.re: -------------------------------------------------------------------------------- 1 | let create = subscriber => 2 | FirstOperator.create @@ MaybeOperator.create @@ subscriber; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MaybeLastOperator.re: -------------------------------------------------------------------------------- 1 | let create = subscriber => 2 | LastOperator.create @@ MaybeOperator.create @@ subscriber; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MaybeOperator.re: -------------------------------------------------------------------------------- 1 | let onComplete = (delegate, exn) => { 2 | let exn = 3 | switch (exn) { 4 | | Some(RxEmptyException.Exn) => None 5 | | _ => exn 6 | }; 7 | delegate |> RxSubscriber.complete(~exn?); 8 | }; 9 | 10 | let create = subscriber => 11 | subscriber |> RxSubscriber.decorateOnComplete(onComplete); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MergeMapOperator.re: -------------------------------------------------------------------------------- 1 | let create = (~maxBufferSize=?, ~maxConcurrency=?, f) => { 2 | let mapOperator = MapOperator.create(f); 3 | let mergeOperator = MergeOperator.create(~maxBufferSize?, ~maxConcurrency?); 4 | 5 | s => mapOperator @@ mergeOperator @@ s; 6 | }; 7 | 8 | let create1 = (~maxBufferSize=?, ~maxConcurrency=?, f, ctx0) => { 9 | let mapOperator = MapOperator.create1(f, ctx0); 10 | let mergeOperator = MergeOperator.create(~maxBufferSize?, ~maxConcurrency?); 11 | 12 | s => mapOperator @@ mergeOperator @@ s; 13 | }; 14 | 15 | let create2 = (~maxBufferSize=?, ~maxConcurrency=?, f, ctx0, ctx1) => { 16 | let mapOperator = MapOperator.create2(f, ctx0, ctx1); 17 | let mergeOperator = MergeOperator.create(~maxBufferSize?, ~maxConcurrency?); 18 | 19 | s => mapOperator @@ mergeOperator @@ s; 20 | }; 21 | 22 | let create3 = (~maxBufferSize=?, ~maxConcurrency=?, f, ctx0, ctx1, ctx2) => { 23 | let mapOperator = MapOperator.create3(f, ctx0, ctx1, ctx2); 24 | let mergeOperator = MergeOperator.create(~maxBufferSize?, ~maxConcurrency?); 25 | 26 | s => mapOperator @@ mergeOperator @@ s; 27 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/MergeOperator.re: -------------------------------------------------------------------------------- 1 | type context('a) = { 2 | activeCount: RxAtomic.t(int), 3 | maxBufferSize: int, 4 | maxConcurrency: int, 5 | queue: RxMutableQueue.t(RxObservable.t('a)), 6 | mutable subscriber: RxSubscriber.t(RxObservable.t('a)), 7 | }; 8 | 9 | module InnerSubscriber = { 10 | type innerContext('a) = { 11 | parent: context('a), 12 | delegate: RxSubscriber.t('a), 13 | mutable disposable: RxDisposable.t, 14 | }; 15 | 16 | let onNext = (self, _, next) => self.delegate |> RxSubscriber.next(next); 17 | 18 | let rec onComplete = (self, _, exn) => { 19 | RxAtomic.decr(self.parent.activeCount) |> ignore; 20 | self.parent.subscriber 21 | |> RxSubscriber.removeDisposable(self.disposable) 22 | |> ignore; 23 | 24 | let next = self.parent.queue |> RxMutableQueue.dequeue; 25 | switch (exn, next) { 26 | | (Some(_), _) => self.parent.subscriber |> RxSubscriber.complete(~exn?) 27 | | (_, Some(next)) => doSubscribeInternal(self, next) 28 | | _ => () 29 | }; 30 | } 31 | and innerSubscriberOperator = self => 32 | RxSubscriber.decorate1(~onNext, ~onComplete, self) 33 | and doSubscribeInternal = (self, next) => { 34 | RxAtomic.incr(self.parent.activeCount) |> ignore; 35 | self.disposable = 36 | next 37 | |> RxObservable.lift(innerSubscriberOperator(self)) 38 | |> RxObservable.connect; 39 | self.parent.subscriber 40 | |> RxSubscriber.addDisposable(self.disposable) 41 | |> ignore; 42 | } 43 | and doSubscribe = (parent, delegate, next) => { 44 | let self = {parent, delegate, disposable: RxDisposable.disposed}; 45 | doSubscribeInternal(self, next); 46 | }; 47 | }; 48 | 49 | let onNext = (self, delegate, next) => 50 | if (self.activeCount |> RxAtomic.get < self.maxConcurrency) { 51 | InnerSubscriber.doSubscribe(self, delegate, next); 52 | } else if (RxMutableQueue.length(self.queue) < self.maxBufferSize) { 53 | self.queue |> RxMutableQueue.enqueue(next); 54 | }; 55 | 56 | let onComplete = (self, delegate, exn) => 57 | switch (exn) { 58 | | Some(_) 59 | | _ when self.activeCount |> RxAtomic.get === 0 => 60 | delegate |> RxSubscriber.complete(~exn?) 61 | | _ => () 62 | }; 63 | 64 | let createImpl = (~maxBufferSize, ~maxConcurrency, subscriber) => { 65 | let self = { 66 | activeCount: RxAtomic.make(0), 67 | maxBufferSize, 68 | maxConcurrency, 69 | queue: RxMutableQueue.create(), 70 | subscriber: RxSubscriber.disposed, 71 | }; 72 | 73 | self.subscriber = 74 | subscriber 75 | |> RxSubscriber.decorate1(~onNext, ~onComplete, self) 76 | |> RxSubscriber.addDisposable( 77 | RxDisposable.create1(RxMutableQueue.clear, self.queue), 78 | ); 79 | self.subscriber; 80 | }; 81 | 82 | let create = (~maxBufferSize=max_int, ~maxConcurrency=max_int) => { 83 | RxPreconditions.checkArgument( 84 | maxBufferSize >= 0, 85 | "MergeOperator: maxBufferSize must be greater than or equal to 0", 86 | ); 87 | 88 | RxPreconditions.checkArgument( 89 | maxConcurrency > 0, 90 | "MergeOperator: maxConcurrency must be greater than 0", 91 | ); 92 | 93 | createImpl(~maxBufferSize, ~maxConcurrency); 94 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/NoneOperator.re: -------------------------------------------------------------------------------- 1 | let keepIfTrueOperator = KeepOperator.create(RxFunctions.identity); 2 | 3 | let create = predicate => { 4 | let mapOperator = MapOperator.create(predicate); 5 | 6 | subscriber => 7 | mapOperator @@ keepIfTrueOperator @@ IsEmptyOperator.create @@ subscriber; 8 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/ObserveOnOperator.re: -------------------------------------------------------------------------------- 1 | let schedule = (wip, doWork, queue, notification, scheduler) => { 2 | queue |> RxMutableQueue.enqueue(notification); 3 | if (RxAtomic.incr(wip) === 1) { 4 | scheduler |> RxScheduler.schedule(doWork) |> ignore; 5 | }; 6 | }; 7 | 8 | let onNext = (scheduler, wip, doWork, queue, _, next) => 9 | scheduler |> schedule(wip, doWork, queue, RxNotification.next(next)); 10 | 11 | let onComplete = (scheduler, wip, doWork, queue, _, exn) => 12 | scheduler |> schedule(wip, doWork, queue, RxNotification.complete(exn)); 13 | 14 | let create = (scheduler, subscriber) => { 15 | let queue = RxMutableQueue.create(); 16 | let wip = RxAtomic.make(0); 17 | 18 | let rec doWork = (~now, ~shouldYield) => { 19 | let loopAgain = 20 | switch (RxMutableQueue.dequeue(queue)) { 21 | | Some(notif) when !RxSubscriber.isDisposed(subscriber) => 22 | subscriber |> RxSubscriber.notify(notif); 23 | true; 24 | | _ => false 25 | }; 26 | 27 | let yieldRequested = shouldYield(); 28 | if (loopAgain && !yieldRequested) { 29 | doWork(~now, ~shouldYield); 30 | } else if (loopAgain) { 31 | RxScheduler.Result.yield(doWork); 32 | } else if (RxAtomic.decr(wip) !== 0) { 33 | RxScheduler.Result.yield(doWork); 34 | } else { 35 | RxScheduler.Result.complete; 36 | }; 37 | }; 38 | 39 | subscriber 40 | |> RxSubscriber.decorate4( 41 | ~onNext, 42 | ~onComplete, 43 | scheduler, 44 | wip, 45 | doWork, 46 | queue, 47 | ) 48 | |> RxSubscriber.addDisposable( 49 | RxDisposable.create1(RxMutableQueue.clear, queue), 50 | ); 51 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/ObserveOperator.re: -------------------------------------------------------------------------------- 1 | let create = { 2 | let onNext_ = (onNext, _, subscriber, next) => { 3 | onNext(next); 4 | subscriber |> RxSubscriber.next(next); 5 | }; 6 | let onComplete_ = (_, onComplete, subscriber, exn) => { 7 | onComplete(exn); 8 | subscriber |> RxSubscriber.complete(~exn?); 9 | }; 10 | 11 | (~onNext, ~onComplete, subscriber) => 12 | subscriber 13 | |> RxSubscriber.decorate2( 14 | ~onNext=onNext_, 15 | ~onComplete=onComplete_, 16 | onNext, 17 | onComplete, 18 | ); 19 | }; 20 | 21 | let create1 = { 22 | let onNext_ = (onNext, _, ctx0, subscriber, next) => { 23 | onNext(ctx0, next); 24 | subscriber |> RxSubscriber.next(next); 25 | }; 26 | let onComplete_ = (_, onComplete, ctx0, subscriber, exn) => { 27 | onComplete(ctx0, exn); 28 | subscriber |> RxSubscriber.complete(~exn?); 29 | }; 30 | 31 | (~onNext, ~onComplete, ctx0, subscriber) => 32 | subscriber 33 | |> RxSubscriber.decorate3( 34 | ~onNext=onNext_, 35 | ~onComplete=onComplete_, 36 | onNext, 37 | onComplete, 38 | ctx0, 39 | ); 40 | }; 41 | 42 | let create2 = { 43 | let onNext_ = (onNext, _, ctx0, ctx1, subscriber, next) => { 44 | onNext(ctx0, ctx1, next); 45 | subscriber |> RxSubscriber.next(next); 46 | }; 47 | let onComplete_ = (_, onComplete, ctx0, ctx1, subscriber, exn) => { 48 | onComplete(ctx0, ctx1, exn); 49 | subscriber |> RxSubscriber.complete(~exn?); 50 | }; 51 | 52 | (~onNext, ~onComplete, ctx0, ctx1, subscriber) => 53 | subscriber 54 | |> RxSubscriber.decorate4( 55 | ~onNext=onNext_, 56 | ~onComplete=onComplete_, 57 | onNext, 58 | onComplete, 59 | ctx0, 60 | ctx1, 61 | ); 62 | }; 63 | 64 | let create3 = { 65 | let onNext_ = (onNext, _, ctx0, ctx1, ctx2, subscriber, next) => { 66 | onNext(ctx0, ctx1, ctx2, next); 67 | subscriber |> RxSubscriber.next(next); 68 | }; 69 | let onComplete_ = (_, onComplete, ctx0, ctx1, ctx2, subscriber, exn) => { 70 | onComplete(ctx0, ctx1, ctx2, exn); 71 | subscriber |> RxSubscriber.complete(~exn?); 72 | }; 73 | 74 | (~onNext, ~onComplete, ctx0, ctx1, ctx2, subscriber) => 75 | subscriber 76 | |> RxSubscriber.decorate5( 77 | ~onNext=onNext_, 78 | ~onComplete=onComplete_, 79 | onNext, 80 | onComplete, 81 | ctx0, 82 | ctx1, 83 | ctx2, 84 | ); 85 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/OnCompleteOperator.re: -------------------------------------------------------------------------------- 1 | let create = (onComplete, subscriber) => 2 | ObserveOperator.create( 3 | ~onNext=RxFunctions.alwaysUnit1, 4 | ~onComplete, 5 | subscriber, 6 | ); 7 | 8 | let create1 = (onComplete, ctx0, subscriber) => 9 | ObserveOperator.create1( 10 | ~onNext=RxFunctions.alwaysUnit2, 11 | ~onComplete, 12 | ctx0, 13 | subscriber, 14 | ); 15 | 16 | let create2 = (onComplete, ctx0, ctx1, subscriber) => 17 | ObserveOperator.create2( 18 | ~onNext=RxFunctions.alwaysUnit3, 19 | ~onComplete, 20 | ctx0, 21 | ctx1, 22 | subscriber, 23 | ); 24 | 25 | let create3 = (onComplete, ctx0, ctx1, ctx2, subscriber) => 26 | ObserveOperator.create3( 27 | ~onNext=RxFunctions.alwaysUnit4, 28 | ~onComplete, 29 | ctx0, 30 | ctx1, 31 | ctx2, 32 | subscriber, 33 | ); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/OnConnectOperator.re: -------------------------------------------------------------------------------- 1 | let create = (f, subscriber) => 2 | subscriber |> RxSubscriber.addDisposable(f()); 3 | 4 | let create1 = (f, ctx0, subscriber) => 5 | subscriber |> RxSubscriber.addDisposable(f(ctx0)); 6 | 7 | let create2 = (f, ctx0, ctx1, subscriber) => 8 | subscriber |> RxSubscriber.addDisposable(f(ctx0, ctx1)); 9 | 10 | let create3 = (f, ctx0, ctx1, ctx2, subscriber) => 11 | subscriber |> RxSubscriber.addDisposable(f(ctx0, ctx1, ctx2,)); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/OnExnOperator.re: -------------------------------------------------------------------------------- 1 | let create = onExn => { 2 | let onComplete = 3 | fun 4 | | None => () 5 | | Some(exn) => onExn(exn); 6 | 7 | subscriber => 8 | ObserveOperator.create( 9 | ~onNext=RxFunctions.alwaysUnit1, 10 | ~onComplete, 11 | subscriber, 12 | ); 13 | }; 14 | 15 | let create1 = onExn => { 16 | let onComplete = ctx0 => 17 | fun 18 | | None => () 19 | | Some(exn) => onExn(ctx0, exn); 20 | 21 | (ctx0, subscriber) => 22 | ObserveOperator.create1( 23 | ~onNext=RxFunctions.alwaysUnit2, 24 | ~onComplete, 25 | ctx0, 26 | subscriber, 27 | ); 28 | }; 29 | 30 | let create2 = onExn => { 31 | let onComplete = (ctx0, ctx1) => 32 | fun 33 | | None => () 34 | | Some(exn) => onExn(ctx0, ctx1, exn); 35 | 36 | (ctx0, ctx1, subscriber) => 37 | ObserveOperator.create2( 38 | ~onNext=RxFunctions.alwaysUnit3, 39 | ~onComplete, 40 | ctx0, 41 | ctx1, 42 | subscriber, 43 | ); 44 | }; 45 | 46 | let create3 = onExn => { 47 | let onComplete = (ctx0, ctx1, ctx2) => 48 | fun 49 | | None => () 50 | | Some(exn) => onExn(ctx0, ctx1, ctx2, exn); 51 | 52 | (ctx0, ctx1, ctx2, subscriber) => 53 | ObserveOperator.create3( 54 | ~onNext=RxFunctions.alwaysUnit4, 55 | ~onComplete, 56 | ctx0, 57 | ctx1, 58 | ctx2, 59 | subscriber, 60 | ); 61 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/PublishToSubjectOperator.re: -------------------------------------------------------------------------------- 1 | let onComplete = (subject, exn) => subject |> RxSubject.complete(~exn?); 2 | 3 | let onNext = (subject, v) => subject |> RxSubject.next(v); 4 | 5 | let create = subject => 6 | ObserveOperator.create1(~onNext, ~onComplete, subject); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/PublishToSubscriberOperator.re: -------------------------------------------------------------------------------- 1 | let onNext = (subscriber, v) => subscriber |> RxSubscriber.next(v); 2 | 3 | let onComplete = (subscriber, exn) => 4 | subscriber |> RxSubscriber.complete(~exn?); 5 | 6 | let create = subscriber => 7 | ObserveOperator.create1(~onNext, ~onComplete, subscriber); -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/RepeatOperator.re: -------------------------------------------------------------------------------- 1 | let onNext = (_, delegate, v) => delegate |> RxSubscriber.next(v); 2 | 3 | let rec onComplete = (ctx, delegate, exn) => { 4 | let (shouldRepeat, _, _) = ctx; 5 | let shouldComplete = !shouldRepeat(exn); 6 | 7 | shouldComplete ? 8 | delegate |> RxSubscriber.complete(~exn?) : 9 | setupSubscription(ctx, delegate); 10 | } 11 | and setupSubscription = (ctx, delegate) => { 12 | let (_, observable, subscription) = ctx; 13 | let alreadyDisposed = subscription |> RxSerialDisposable.isDisposed; 14 | 15 | if (!alreadyDisposed) { 16 | subscription 17 | |> RxSerialDisposable.getInnerDisposable 18 | |> RxDisposable.dispose; 19 | 20 | let newInnerSubscription = 21 | observable 22 | |> ObserveObservable.create2(~onNext, ~onComplete, ctx, delegate) 23 | |> RxObservable.connect; 24 | 25 | subscription 26 | |> RxSerialDisposable.setInnerDisposable(newInnerSubscription); 27 | }; 28 | }; 29 | 30 | let create = (shouldRepeat, observable, subscriber) => { 31 | let subscription = RxSerialDisposable.create(); 32 | let disposable = subscription |> RxSerialDisposable.asDisposable; 33 | let ctx = (shouldRepeat, observable, subscription); 34 | 35 | subscriber 36 | |> RxSubscriber.decorateOnComplete1(onComplete, ctx) 37 | |> RxSubscriber.addDisposable(disposable); 38 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/ScanOperator.re: -------------------------------------------------------------------------------- 1 | let mapper = (scanner, acc, next) => { 2 | let prevAcc = acc^; 3 | let nextAcc = scanner(prevAcc, next); 4 | acc := nextAcc; 5 | nextAcc; 6 | }; 7 | 8 | let create = (scanner, initialValue, subscriber) => { 9 | let acc = ref(initialValue); 10 | 11 | subscriber |> RxSubscriber.next(initialValue); 12 | MapOperator.create2(mapper, scanner, acc, subscriber); 13 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/SkipOperator.re: -------------------------------------------------------------------------------- 1 | let predicate = (skipCount, count, _) => { 2 | let count = RxAtomic.incr(count); 3 | if (count <= skipCount) { 4 | false; 5 | } else { 6 | true; 7 | }; 8 | }; 9 | 10 | let create = skipCount => { 11 | RxPreconditions.checkArgument( 12 | skipCount > 0, 13 | "SkipOperator: skipCount must be greater than 0", 14 | ); 15 | 16 | subscriber => { 17 | let count = RxAtomic.make(0); 18 | KeepOperator.create2(predicate, skipCount, count, subscriber); 19 | }; 20 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/SomeOperator.re: -------------------------------------------------------------------------------- 1 | exception CompleteWithoutErrorException; 2 | 3 | let completeWithoutErrorExn = Some(CompleteWithoutErrorException); 4 | 5 | let onNext = (self, _, next) => 6 | if (next) { 7 | self^ |> RxSubscriber.complete(~exn=?completeWithoutErrorExn); 8 | }; 9 | 10 | let onComplete = (_, delegate, exn) => { 11 | let exn = 12 | switch (exn) { 13 | | Some(CompleteWithoutErrorException) => 14 | delegate |> RxSubscriber.next(true); 15 | None; 16 | | None => 17 | delegate |> RxSubscriber.next(false); 18 | None; 19 | | _ => exn 20 | }; 21 | delegate |> RxSubscriber.complete(~exn?); 22 | }; 23 | 24 | let create = (predicate, subscriber) => { 25 | let self = ref(RxSubscriber.disposed); 26 | self := subscriber |> RxSubscriber.decorate1(~onNext, ~onComplete, self); 27 | self^ |> MapOperator.create(predicate); 28 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/SwitchMapOperator.re: -------------------------------------------------------------------------------- 1 | let create = f => { 2 | let mapOperator = MapOperator.create(f); 3 | s => mapOperator @@ SwitchOperator.create @@ s; 4 | }; 5 | 6 | let create1 = (f, ctx0) => { 7 | let mapOperator = MapOperator.create1(f, ctx0); 8 | s => mapOperator @@ SwitchOperator.create @@ s; 9 | }; 10 | 11 | let create2 = (f, ctx0, ctx1) => { 12 | let mapOperator = MapOperator.create2(f, ctx0, ctx1); 13 | s => mapOperator @@ SwitchOperator.create @@ s; 14 | }; 15 | 16 | let create3 = (f, ctx0, ctx1, ctx2) => { 17 | let mapOperator = MapOperator.create3(f, ctx0, ctx1, ctx2); 18 | s => mapOperator @@ SwitchOperator.create @@ s; 19 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/SwitchOperator.re: -------------------------------------------------------------------------------- 1 | type context('a) = { 2 | innerSubscription: RxSerialDisposable.t, 3 | mutable latest: int, 4 | lock: RxLock.t, 5 | mutable self: RxSubscriber.t('a), 6 | }; 7 | 8 | module InnerOperator = { 9 | let onNext = (id, ctx, delegate, _, next) => { 10 | ctx.lock |> RxLock.acquire; 11 | if (ctx.latest === id) { 12 | delegate |> RxSubscriber.next(next); 13 | }; 14 | ctx.lock |> RxLock.release; 15 | }; 16 | 17 | let onComplete = (id, ctx, _, _, exn) => 18 | switch (exn) { 19 | | Some(_) => 20 | if (ctx.latest === id) { 21 | ctx.self |> RxSubscriber.complete(~exn?); 22 | } 23 | | None => () 24 | }; 25 | 26 | let create = (id, ctx, delegate) => 27 | RxSubscriber.decorate3(~onNext, ~onComplete, id, ctx, delegate); 28 | }; 29 | 30 | let onNext = (ctx, delegate, next) => { 31 | ctx.lock |> RxLock.acquire; 32 | let id = ctx.latest + 1; 33 | ctx.latest = id; 34 | ctx.lock |> RxLock.release; 35 | 36 | ctx.innerSubscription 37 | |> RxSerialDisposable.setInnerDisposable(RxDisposable.disposed); 38 | 39 | let newInnerSubscription = 40 | next 41 | |> RxObservable.lift(InnerOperator.create(id, ctx, delegate)) 42 | |> RxObservable.connect; 43 | 44 | ctx.innerSubscription 45 | |> RxSerialDisposable.setInnerDisposable(newInnerSubscription); 46 | }; 47 | 48 | let onComplete = (ctx, delegate, exn) => { 49 | ctx.lock |> RxLock.acquire; 50 | ctx.innerSubscription |> RxSerialDisposable.dispose; 51 | delegate |> RxSubscriber.complete(~exn?); 52 | ctx.lock |> RxLock.release; 53 | }; 54 | 55 | let create = subscriber => { 56 | let context = { 57 | innerSubscription: RxSerialDisposable.create(), 58 | latest: 0, 59 | lock: RxLock.create(), 60 | self: RxSubscriber.disposed, 61 | }; 62 | 63 | let innerDisposable = 64 | context.innerSubscription |> RxSerialDisposable.asDisposable; 65 | 66 | context.self = 67 | subscriber 68 | |> RxSubscriber.decorate1(~onNext, ~onComplete, context) 69 | |> RxSubscriber.addDisposable(innerDisposable); 70 | context.self; 71 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/TakeOperator.re: -------------------------------------------------------------------------------- 1 | let onNext = (remainder, subscriber, next) => { 2 | decr(remainder); 3 | subscriber |> RxSubscriber.next(next); 4 | 5 | if (remainder^ === 0) { 6 | subscriber |> RxSubscriber.complete; 7 | } 8 | }; 9 | 10 | let create = takeCount => { 11 | RxPreconditions.checkArgument( 12 | takeCount > 0, 13 | "TakeOperator: takeCount must be greater than 0", 14 | ); 15 | 16 | subscriber => { 17 | let remainder = ref(takeCount); 18 | subscriber |> RxSubscriber.decorateOnNext1(onNext, remainder); 19 | }; 20 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/TakeUntilOperator.re: -------------------------------------------------------------------------------- 1 | module NotifierObservable = { 2 | let onNext = (takeUntilSubscriber, _) => 3 | takeUntilSubscriber |> RxSubscriber.complete; 4 | 5 | let onComplete = (takeUntilSubscriber, exn) => 6 | switch (exn) { 7 | | Some(_) => takeUntilSubscriber |> RxSubscriber.complete(~exn?) 8 | | None => () 9 | }; 10 | 11 | let create = takeUntilSubscriber => 12 | ObserveObservable.create1(~onNext, ~onComplete, takeUntilSubscriber); 13 | }; 14 | 15 | let create = (notifier, subscriber) => { 16 | let notifierSubscription = 17 | notifier 18 | |> NotifierObservable.create(subscriber) 19 | |> RxObservable.connect; 20 | 21 | subscriber |> RxSubscriber.addDisposable(notifierSubscription); 22 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/TimeoutOperator.re: -------------------------------------------------------------------------------- 1 | let subscribeToTimeout = (timeoutObservable, timeoutSubscription) => { 2 | timeoutSubscription 3 | |> RxSerialDisposable.getInnerDisposable 4 | |> RxDisposable.dispose; 5 | timeoutSubscription 6 | |> RxSerialDisposable.setInnerDisposable( 7 | timeoutObservable |> RxObservable.connect, 8 | ); 9 | }; 10 | 11 | let onNext = (timeoutObservable, timeoutSubscription, delegate, next) => { 12 | delegate |> RxSubscriber.next(next); 13 | subscribeToTimeout(timeoutObservable, timeoutSubscription); 14 | }; 15 | 16 | let onComplete = (_, timeoutSubscription, delegate, exn) => { 17 | timeoutSubscription |> RxSerialDisposable.dispose; 18 | delegate |> RxSubscriber.complete(~exn?); 19 | }; 20 | 21 | let create = (~scheduler, due) => { 22 | RxPreconditions.checkArgument( 23 | due > 0.0, 24 | "TimeoutOperator: due time must be greater than 0.0 milliseconds", 25 | ); 26 | 27 | subscriber => { 28 | let timeoutObservable = 29 | RaiseObservable.create(RxTimeoutException.Exn) 30 | |> DelayObservable.create(~scheduler, due) 31 | |> IgnoreElementsObservable.create 32 | |> PublishToSubscriberObservable.create(subscriber); 33 | 34 | let timeoutSubscription = RxSerialDisposable.create(); 35 | let timeoutDisposable = timeoutSubscription |> RxSerialDisposable.asDisposable; 36 | 37 | let self = 38 | subscriber 39 | |> RxSubscriber.decorate2(~onNext, ~onComplete, timeoutObservable, timeoutSubscription) 40 | |> RxSubscriber.addDisposable(timeoutDisposable); 41 | 42 | subscribeToTimeout(timeoutObservable, timeoutSubscription); 43 | self; 44 | }; 45 | }; -------------------------------------------------------------------------------- /packages/rx-observables/src/internal/operators/WithLatestFromOperator.re: -------------------------------------------------------------------------------- 1 | type context('a, 'b, 'c) = { 2 | otherLatest: RxMutableOption.t('b), 3 | mutable otherSubscription: RxDisposable.t, 4 | selector: ('a, 'b) => 'c, 5 | }; 6 | 7 | let onNext = ({otherLatest, selector}, delegate, next) => 8 | if (RxMutableOption.isNotEmpty(otherLatest)) { 9 | let latest = otherLatest |> RxMutableOption.get; 10 | let nextWithLatest = selector(next, latest); 11 | delegate |> RxSubscriber.next(nextWithLatest); 12 | }; 13 | 14 | let onComplete = ({otherSubscription}, delegate, exn) => { 15 | delegate |> RxSubscriber.complete(~exn?); 16 | otherSubscription |> RxDisposable.dispose; 17 | }; 18 | 19 | let otherOnNext = (_, otherLatest, next) => 20 | otherLatest |> RxMutableOption.set(next); 21 | 22 | let otherOnComplete = (self, _, exn) => 23 | switch (exn) { 24 | | Some(_) as exn => self |> RxSubscriber.complete(~exn?) 25 | | _ => () 26 | }; 27 | 28 | let create = (~selector, other, subscriber) => { 29 | let otherLatest = RxMutableOption.create(); 30 | 31 | let context = { 32 | otherLatest, 33 | otherSubscription: RxDisposable.disposed, 34 | selector, 35 | }; 36 | 37 | let delegateSubscriber = 38 | subscriber |> RxSubscriber.decorate1(~onNext, ~onComplete, context); 39 | 40 | context.otherSubscription = 41 | other 42 | |> ObserveObservable.create2( 43 | ~onNext=otherOnNext, 44 | ~onComplete=otherOnComplete, 45 | delegateSubscriber, 46 | otherLatest, 47 | ) 48 | |> RxObservable.connect; 49 | 50 | delegateSubscriber |> RxSubscriber.addDisposable(context.otherSubscription); 51 | }; -------------------------------------------------------------------------------- /packages/rx-observables/test/Option.re: -------------------------------------------------------------------------------- 1 | let equals = (~equals=(===), a, b) => 2 | switch (a, b) { 3 | | (Some(a), Some(b)) => equals(a, b) 4 | | (None, None) => true 5 | | _ => false 6 | }; 7 | 8 | let toString = (~toString, x) => 9 | switch (x) { 10 | | Some(v) => "Some(" ++ toString(v) ++ ")" 11 | | _ => "None" 12 | }; -------------------------------------------------------------------------------- /packages/rx-observables/test/RxObservablesTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | 3 | let test = 4 | describe( 5 | "RxObservables", 6 | [ 7 | CombineLatest2Test.test, 8 | ConcatListTest.test, 9 | DebounceTimeTest.test, 10 | DefaultIfEmptyTest.test, 11 | DeferTest.test, 12 | DistinctUntilChangedTest.test, 13 | EveryTest.test, 14 | ExhaustTest.test, 15 | FindTest.test, 16 | FirstTest.test, 17 | FirstOrNoneTest.test, 18 | IgnoreElementsTest.test, 19 | IsEmptyTest.test, 20 | KeepTest.test, 21 | LastTest.test, 22 | LastOrNoneTest.test, 23 | MapTest.test, 24 | MapToTest.test, 25 | MaybeFirstTest.test, 26 | MaybeLastTest.test, 27 | MergeListTest.test, 28 | OnCompleteTest.test, 29 | OnNextTest.test, 30 | RetryTest.test, 31 | ScanTest.test, 32 | ShareReplayBufferTest.test, 33 | SomeTest.test, 34 | StartWithListTest.test, 35 | StartWithValueTest.test, 36 | SwitchTest.test, 37 | TakeTest.test, 38 | TakeUntilTest.test, 39 | TimeoutTest.test, 40 | WithLatestFromTest.test, 41 | ], 42 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/TestRunner.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | 3 | ReUnit.run(describe("rx-observables", [RxObservablesTest.test])); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/CombineLatest2Test.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "combineLatest2", 7 | [ 8 | observableIt( 9 | "combines latest values from each observable", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => 13 | RxObservables.combineLatest2( 14 | ~selector=(a, b) => a + b, 15 | RxReUnit.ofAbsoluteTimeNotifications( 16 | ~scheduler, 17 | [ 18 | (1.0, RxNotification.next(0)), 19 | (3.0, RxNotification.next(2)), 20 | (5.0, RxNotification.next(4)), 21 | (6.0, RxNotification.complete(None)), 22 | ], 23 | ), 24 | RxReUnit.ofAbsoluteTimeNotifications( 25 | ~scheduler, 26 | [ 27 | (0.0, RxNotification.next(0)), 28 | (2.0, RxNotification.next(2)), 29 | (4.0, RxNotification.next(4)), 30 | (5.0, RxNotification.complete(None)), 31 | ], 32 | ), 33 | ), 34 | ~expected=[ 35 | RxNotification.next(0), 36 | RxNotification.next(2), 37 | RxNotification.next(4), 38 | RxNotification.next(6), 39 | RxNotification.next(8), 40 | RxNotification.complete(None), 41 | ], 42 | (), 43 | ), 44 | observableIt( 45 | "completes when either observable completes with an exception", 46 | ~nextToString=string_of_int, 47 | ~source= 48 | scheduler => 49 | RxObservables.combineLatest2( 50 | ~selector=(a, b) => a + b, 51 | RxObservables.raise(Division_by_zero) 52 | |> RxObservables.delay(~scheduler, 2.0), 53 | RxReUnit.ofAbsoluteTimeNotifications( 54 | ~scheduler, 55 | [ 56 | (1.0, RxNotification.next(0)), 57 | (3.0, RxNotification.next(2)), 58 | (5.0, RxNotification.next(4)), 59 | (6.0, RxNotification.complete(None)), 60 | ], 61 | ), 62 | ), 63 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 64 | (), 65 | ), 66 | ], 67 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/ConcatListTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "concatList", 7 | [ 8 | observableIt( 9 | "synchronous and asynchronous observables", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => 13 | RxObservables.concatList([ 14 | ofRelativeTimeNotifications( 15 | ~scheduler, 16 | [ 17 | (1.0, RxNotification.next(7)), 18 | (3.0, RxNotification.next(9)), 19 | (4.0, RxNotification.complete(None)), 20 | ], 21 | ), 22 | RxObservables.ofList([1, 2, 3]), 23 | ofRelativeTimeNotifications( 24 | ~scheduler, 25 | [ 26 | (2.0, RxNotification.next(8)), 27 | (4.0, RxNotification.next(10)), 28 | (5.0, RxNotification.complete(None)), 29 | ], 30 | ), 31 | RxObservables.ofList([4, 5, 6]), 32 | ]), 33 | ~expected=[ 34 | RxNotification.next(7), 35 | RxNotification.next(9), 36 | RxNotification.next(1), 37 | RxNotification.next(2), 38 | RxNotification.next(3), 39 | RxNotification.next(8), 40 | RxNotification.next(10), 41 | RxNotification.next(4), 42 | RxNotification.next(5), 43 | RxNotification.next(6), 44 | RxNotification.complete(None), 45 | ], 46 | (), 47 | ), 48 | ], 49 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/DebounceTimeTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "debounceTime", 7 | [ 8 | observableIt( 9 | "debounces", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => 13 | ofRelativeTimeNotifications( 14 | ~scheduler, 15 | [ 16 | (0.0, RxNotification.next(1)), 17 | (4.0, RxNotification.next(2)), 18 | (6.0, RxNotification.next(3)), 19 | (15.0, RxNotification.next(4)), 20 | (16.0, RxNotification.next(5)), 21 | (17.0, RxNotification.next(6)), 22 | (18.0, RxNotification.complete(None)), 23 | ], 24 | ) 25 | |> RxObservables.debounceTime(~scheduler, 5.0), 26 | ~expected=[ 27 | RxNotification.next(3), 28 | RxNotification.next(6), 29 | RxNotification.complete(None), 30 | ], 31 | (), 32 | ), 33 | ], 34 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/DefaultIfEmptyTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "defaultIfEmpty", 7 | [ 8 | observableIt( 9 | "returns the default if empty", 10 | ~nextToString=string_of_int, 11 | ~source=_ => RxObservables.empty() |> RxObservables.defaultIfEmpty(1), 12 | ~expected=[RxNotification.next(1), RxNotification.complete(None)], 13 | (), 14 | ), 15 | observableIt( 16 | "passes through if not empty", 17 | ~nextToString=string_of_int, 18 | ~source= 19 | _ => 20 | RxObservables.ofList([1, 2, 3]) 21 | |> RxObservables.defaultIfEmpty(1), 22 | ~expected=[ 23 | RxNotification.next(1), 24 | RxNotification.next(2), 25 | RxNotification.next(3), 26 | RxNotification.complete(None), 27 | ], 28 | (), 29 | ), 30 | ], 31 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/DeferTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | 4 | let test = 5 | describe( 6 | "defer", 7 | [ 8 | it("calls the observable factory on subscribe", () => { 9 | let count = ref(0); 10 | 11 | let observable = 12 | RxObservables.defer(() => { 13 | incr(count); 14 | RxObservables.empty(); 15 | }); 16 | 17 | observable |> RxObservable.connect |> ignore; 18 | observable |> RxObservable.connect |> ignore; 19 | observable |> RxObservable.connect |> ignore; 20 | count^ |> Expect.toBeEqualToInt(3); 21 | }), 22 | ], 23 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/DistinctUntilChangedTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "distinctUntilChanged", 7 | [ 8 | observableIt( 9 | "removes duplicates", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => 13 | RxObservables.ofList([1, 1, 1, 3, 5, 3, 3, 1]) 14 | |> RxObservables.distinctUntilChanged(~equals=(===)), 15 | ~expected=[ 16 | RxNotification.next(1), 17 | RxNotification.next(3), 18 | RxNotification.next(5), 19 | RxNotification.next(3), 20 | RxNotification.next(1), 21 | RxNotification.complete(None), 22 | ], 23 | (), 24 | ), 25 | ], 26 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/EveryTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = describe( 5 | "every", 6 | [ 7 | observableIt( 8 | "returns true for an subscriber that completes without producing values", 9 | ~nextToString=string_of_bool, 10 | ~source= 11 | _ => RxObservables.empty() |> RxObservables.every(i => i > 10), 12 | ~expected=[ 13 | RxNotification.next(true), 14 | RxNotification.complete(None), 15 | ], 16 | (), 17 | ), 18 | observableIt( 19 | "completes with false on the first observed value that fails the predicate", 20 | ~nextToString=string_of_int, 21 | ~source= 22 | scheduler => 23 | ofAbsoluteTimeNotifications( 24 | ~scheduler, 25 | [ 26 | (1.0, RxNotification.next(12)), 27 | (2.0, RxNotification.next(8)), 28 | (3.0, RxNotification.next(14)), 29 | (5.0, RxNotification.next(6)), 30 | (6.0, RxNotification.complete(None)), 31 | ], 32 | ) 33 | |> RxObservables.every(i => i > 10) 34 | |> RxObservables.map(_ => 35 | scheduler |> RxScheduler.now |> int_of_float 36 | ), 37 | ~expected=[ 38 | RxNotification.next(2), 39 | RxNotification.complete(None), 40 | ], 41 | (), 42 | ), 43 | observableIt( 44 | "completes with true if all values pass the predicate", 45 | ~nextToString=string_of_bool, 46 | ~source= 47 | _ => 48 | RxObservables.ofList([12, 13]) 49 | |> RxObservables.every(i => i > 10), 50 | ~expected=[ 51 | RxNotification.next(true), 52 | RxNotification.complete(None), 53 | ], 54 | (), 55 | ), 56 | ], 57 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/ExhaustTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "exhaust", 7 | [ 8 | observableIt( 9 | "exhausts", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => { 13 | let childObservableA = 14 | ofRelativeTimeNotifications( 15 | ~scheduler, 16 | [ 17 | (0.0, RxNotification.next(1)), 18 | (10.0, RxNotification.next(2)), 19 | (20.0, RxNotification.next(3)), 20 | (30.0, RxNotification.next(4)), 21 | (40.0, RxNotification.complete(None)), 22 | ], 23 | ); 24 | 25 | let childObservableB = 26 | ofRelativeTimeNotifications( 27 | ~scheduler, 28 | [ 29 | (0.0, RxNotification.next(5)), 30 | (10.0, RxNotification.next(6)), 31 | (19.0, RxNotification.next(7)), 32 | (30.0, RxNotification.next(8)), 33 | (40.0, RxNotification.complete(None)), 34 | ], 35 | ); 36 | 37 | ofAbsoluteTimeNotifications( 38 | ~scheduler, 39 | [ 40 | (0.0, RxNotification.next(childObservableA)), 41 | (15.0, RxNotification.next(childObservableB)), 42 | (35.0, RxNotification.next(childObservableA)), 43 | (60.0, RxNotification.next(childObservableB)), 44 | (75.0, RxNotification.complete(None)), 45 | ], 46 | ) 47 | |> RxObservables.exhaust; 48 | }, 49 | ~expected=[ 50 | RxNotification.next(1), 51 | RxNotification.next(2), 52 | RxNotification.next(3), 53 | RxNotification.next(4), 54 | RxNotification.next(5), 55 | RxNotification.next(6), 56 | RxNotification.next(7), 57 | RxNotification.next(8), 58 | RxNotification.complete(None), 59 | ], 60 | (), 61 | ), 62 | ], 63 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/FindTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = describe( 5 | "find", 6 | [ 7 | observableIt( 8 | "finds the first matching element and completes", 9 | ~nextToString=string_of_int, 10 | ~source= 11 | _ => 12 | RxObservables.ofList([1, 3, 10, 6, 8]) 13 | |> RxObservables.find(x => x mod 2 === 0), 14 | ~expected=[ 15 | RxNotification.next(10), 16 | RxNotification.complete(None), 17 | ], 18 | (), 19 | ), 20 | ], 21 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/FirstOrNoneTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "firstOrNone", 7 | [ 8 | observableIt( 9 | "publishes Some of the first observed value", 10 | ~nextEquals=Option.equals, 11 | ~nextToString=Option.toString(~toString=string_of_int), 12 | ~source= 13 | _ => RxObservables.ofList([2, 3]) |> RxObservables.firstOrNone, 14 | ~expected=[ 15 | RxNotification.next(Some(2)), 16 | RxNotification.complete(None), 17 | ], 18 | (), 19 | ), 20 | observableIt( 21 | "publishes Some of the first observed value", 22 | ~nextEquals=Option.equals, 23 | ~nextToString=Option.toString(~toString=string_of_int), 24 | ~source= 25 | _ => 26 | RxObservables.raise(Division_by_zero) |> RxObservables.firstOrNone, 27 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 28 | (), 29 | ), 30 | observableIt( 31 | "ignores EmptyException, publishes None, and completes normally", 32 | ~nextEquals=Option.equals, 33 | ~nextToString=Option.toString(~toString=string_of_int), 34 | ~source= 35 | _ => 36 | RxObservables.empty() 37 | |> RxObservables.first 38 | |> RxObservables.firstOrNone, 39 | ~expected=[ 40 | RxNotification.next(None), 41 | RxNotification.complete(None), 42 | ], 43 | (), 44 | ), 45 | ], 46 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/FirstTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = describe( 5 | "first", 6 | [ 7 | observableIt( 8 | "publishes the first observed value", 9 | ~nextToString=string_of_int, 10 | ~source=_ => RxObservables.ofList([2, 3]) |> RxObservables.first, 11 | ~expected=[ 12 | RxNotification.next(2), 13 | RxNotification.complete(None), 14 | ], 15 | (), 16 | ), 17 | observableIt( 18 | "passes through completed exceptions", 19 | ~nextToString=string_of_int, 20 | ~source= 21 | _ => 22 | RxObservables.raise(Division_by_zero) |> RxObservables.first, 23 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 24 | (), 25 | ), 26 | observableIt( 27 | "completes with exception if no values are produced", 28 | ~nextToString=string_of_int, 29 | ~source=_ => RxObservables.empty() |> RxObservables.first, 30 | ~expected=[RxNotification.complete(Some(RxEmptyException.Exn))], 31 | (), 32 | ), 33 | ], 34 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/IgnoreElementsTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "ignoreElements", 7 | [ 8 | observableIt( 9 | "ignores all elements and publishes an exception", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => 13 | RxObservables.ofNotifications([ 14 | RxNotification.next(1), 15 | RxNotification.next(2), 16 | RxNotification.next(3), 17 | RxNotification.complete(Some(Division_by_zero)), 18 | ]) 19 | |> RxObservables.ignoreElements, 20 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 21 | (), 22 | ), 23 | ], 24 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/IsEmptyTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "isEmpty", 7 | [ 8 | observableIt( 9 | "return false if not empty", 10 | ~nextToString=string_of_bool, 11 | ~source=_ => RxObservables.ofValue(1) |> RxObservables.isEmpty, 12 | ~expected=[ 13 | RxNotification.next(false), 14 | RxNotification.complete(None), 15 | ], 16 | (), 17 | ), 18 | observableIt( 19 | "return true if empty", 20 | ~nextToString=string_of_bool, 21 | ~source=_ => RxObservables.empty() |> RxObservables.isEmpty, 22 | ~expected=[ 23 | RxNotification.next(true), 24 | RxNotification.complete(None), 25 | ], 26 | (), 27 | ), 28 | ], 29 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/KeepTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "keep", 7 | [ 8 | observableIt( 9 | "completes the subscriber when the predicate throws an exception", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => 13 | RxObservables.ofValue(1) 14 | |> RxObservables.keep(_ => raise(Division_by_zero)), 15 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 16 | (), 17 | ), 18 | observableIt( 19 | "completes the subscriber when the keep subscriber is completed", 20 | ~nextToString=string_of_int, 21 | ~source= 22 | _ => RxObservables.ofValue(1) |> RxObservables.keep(_ => true), 23 | ~expected=[RxNotification.next(1), RxNotification.complete(None)], 24 | (), 25 | ), 26 | ], 27 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/LastOrNoneTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "lastOrNone", 7 | [ 8 | observableIt( 9 | "publishes the Some of the last observed value and completes", 10 | ~nextEquals=Option.equals, 11 | ~nextToString=Option.toString(~toString=string_of_int), 12 | ~source=_ => RxObservables.ofList([2, 3]) |> RxObservables.lastOrNone, 13 | ~expected=[ 14 | RxNotification.next(Some(3)), 15 | RxNotification.complete(None), 16 | ], 17 | (), 18 | ), 19 | observableIt( 20 | "passes through completed exceptions", 21 | ~nextEquals=Option.equals, 22 | ~nextToString=Option.toString(~toString=string_of_int), 23 | ~source= 24 | _ => 25 | RxObservables.ofNotifications([ 26 | RxNotification.next(1), 27 | RxNotification.next(2), 28 | RxNotification.complete(Some(Division_by_zero)), 29 | ]) 30 | |> RxObservables.lastOrNone, 31 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 32 | (), 33 | ), 34 | observableIt( 35 | "ignores EmptyException, publishes None, and completes normally", 36 | ~nextEquals=Option.equals, 37 | ~nextToString=Option.toString(~toString=string_of_int), 38 | ~source= 39 | _ => 40 | RxObservables.empty() 41 | |> RxObservables.first 42 | |> RxObservables.lastOrNone, 43 | ~expected=[ 44 | RxNotification.next(None), 45 | RxNotification.complete(None), 46 | ], 47 | (), 48 | ), 49 | ], 50 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/LastTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "last", 7 | [ 8 | observableIt( 9 | "publishes the last observed value and disposes", 10 | ~nextToString=string_of_int, 11 | ~source=_ => RxObservables.ofList([1, 2, 3]) |> RxObservables.last, 12 | ~expected=[RxNotification.next(3), RxNotification.complete(None)], 13 | (), 14 | ), 15 | observableIt( 16 | "passes through completed exceptions", 17 | ~nextToString=string_of_int, 18 | ~source= 19 | _ => 20 | RxObservables.ofNotifications([ 21 | RxNotification.next(1), 22 | RxNotification.next(2), 23 | RxNotification.next(3), 24 | RxNotification.complete(Some(Division_by_zero)), 25 | ]) 26 | |> RxObservables.last, 27 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 28 | (), 29 | ), 30 | observableIt( 31 | "completes with exception if no values are produced", 32 | ~nextToString=string_of_int, 33 | ~source=_ => RxObservables.empty() |> RxObservables.last, 34 | ~expected=[RxNotification.complete(Some(RxEmptyException.Exn))], 35 | (), 36 | ), 37 | ], 38 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/MapTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "map", 7 | [ 8 | observableIt( 9 | "completes the subscriber when the mapper throws an exception", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => 13 | RxObservables.ofList([1, 2, 3]) 14 | |> RxObservables.map(_ => raise(Division_by_zero)), 15 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 16 | (), 17 | ), 18 | observableIt( 19 | "completes the subscriber when the mapping subscriber is completed", 20 | ~nextToString=string_of_int, 21 | ~source= 22 | _ => 23 | RxObservables.ofList([1, 2, 3]) |> RxObservables.map(i => i + 1), 24 | ~expected=[ 25 | RxNotification.next(2), 26 | RxNotification.next(3), 27 | RxNotification.next(4), 28 | RxNotification.complete(None), 29 | ], 30 | (), 31 | ), 32 | ], 33 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/MapToTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "mapTo", 7 | [ 8 | observableIt( 9 | "maps any input to value", 10 | ~nextToString=RxFunctions.identity, 11 | ~source= 12 | _ => RxObservables.ofList([1, 2, 3]) |> RxObservables.mapTo("a"), 13 | ~expected=[ 14 | RxNotification.next("a"), 15 | RxNotification.next("a"), 16 | RxNotification.next("a"), 17 | RxNotification.complete(None), 18 | ], 19 | (), 20 | ), 21 | ], 22 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/MaybeFirstTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "maybeFirst", 7 | [ 8 | observableIt( 9 | "publishes the first observed value", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => RxObservables.ofList([1, 2, 3]) |> RxObservables.maybeFirst, 13 | ~expected=[RxNotification.next(1), RxNotification.complete(None)], 14 | (), 15 | ), 16 | observableIt( 17 | "passes through completed exceptions", 18 | ~nextToString=string_of_int, 19 | ~source= 20 | _ => 21 | RxObservables.raise(Division_by_zero) |> RxObservables.maybeFirst, 22 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 23 | (), 24 | ), 25 | observableIt( 26 | "ignores EmptyException and completes normally", 27 | ~nextToString=string_of_int, 28 | ~source= 29 | _ => 30 | RxObservables.empty() 31 | |> RxObservables.first 32 | |> RxObservables.maybeFirst, 33 | ~expected=[RxNotification.complete(None)], 34 | (), 35 | ), 36 | ], 37 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/MaybeLastTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "maybeLast", 7 | [ 8 | observableIt( 9 | "publishes the last observed value", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => RxObservables.ofList([1, 2, 3]) |> RxObservables.maybeLast, 13 | ~expected=[RxNotification.next(3), RxNotification.complete(None)], 14 | (), 15 | ), 16 | observableIt( 17 | "passes through completed exceptions", 18 | ~nextToString=string_of_int, 19 | ~source= 20 | _ => 21 | RxObservables.ofNotifications([ 22 | RxNotification.next(1), 23 | RxNotification.next(2), 24 | RxNotification.complete(Some(Division_by_zero)), 25 | ]) 26 | |> RxObservables.maybeLast, 27 | ~expected=[RxNotification.complete(Some(Division_by_zero))], 28 | (), 29 | ), 30 | observableIt( 31 | "ignores EmptyException and completes normally", 32 | ~nextToString=string_of_int, 33 | ~source= 34 | _ => 35 | RxObservables.empty() 36 | |> RxObservables.first 37 | |> RxObservables.maybeLast, 38 | ~expected=[RxNotification.complete(None)], 39 | (), 40 | ), 41 | ], 42 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/MergeListTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "mergeList", 7 | [ 8 | observableIt( 9 | "synchronous and asynchronous observables", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => 13 | RxObservables.mergeList([ 14 | ofAbsoluteTimeNotifications( 15 | ~scheduler, 16 | [ 17 | (1.0, RxNotification.next(7)), 18 | (3.0, RxNotification.next(9)), 19 | (4.0, RxNotification.complete(None)), 20 | ], 21 | ), 22 | RxObservables.ofList([1, 2, 3]), 23 | ofAbsoluteTimeNotifications( 24 | ~scheduler, 25 | [ 26 | (2.0, RxNotification.next(8)), 27 | (4.0, RxNotification.next(10)), 28 | (5.0, RxNotification.complete(None)), 29 | ], 30 | ), 31 | RxObservables.ofList([4, 5, 6]), 32 | ]), 33 | ~expected=[ 34 | RxNotification.next(1), 35 | RxNotification.next(2), 36 | RxNotification.next(3), 37 | RxNotification.next(4), 38 | RxNotification.next(5), 39 | RxNotification.next(6), 40 | RxNotification.next(7), 41 | RxNotification.next(8), 42 | RxNotification.next(9), 43 | RxNotification.next(10), 44 | RxNotification.complete(None), 45 | ], 46 | (), 47 | ), 48 | ], 49 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/OnCompleteTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | open RxReUnit; 4 | 5 | let test = 6 | describe( 7 | "onComplete", 8 | [ 9 | observableIt( 10 | "passes through notifications", 11 | ~nextToString=string_of_int, 12 | ~source= 13 | _ => 14 | RxObservables.ofList([1, 2]) 15 | |> RxObservables.onComplete(RxFunctions.alwaysUnit1), 16 | ~expected=[ 17 | RxNotification.next(1), 18 | RxNotification.next(2), 19 | RxNotification.complete(None), 20 | ], 21 | (), 22 | ), 23 | it("calls the side effect function", () => { 24 | let sideEffectCount = ref(0); 25 | 26 | RxObservables.ofList([1]) 27 | |> RxObservables.onComplete(_ => incr(sideEffectCount)) 28 | |> RxObservable.connect 29 | |> ignore; 30 | 31 | sideEffectCount^ |> Expect.toBeEqualToInt(1); 32 | }), 33 | ], 34 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/OnNextTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | open RxReUnit; 4 | 5 | let test = 6 | describe( 7 | "onNext", 8 | [ 9 | observableIt( 10 | "passes through notifications", 11 | ~nextToString=string_of_int, 12 | ~source= 13 | _ => 14 | RxObservables.ofList([1, 2]) 15 | |> RxObservables.onNext(RxFunctions.alwaysUnit1), 16 | ~expected=[ 17 | RxNotification.next(1), 18 | RxNotification.next(2), 19 | RxNotification.complete(None), 20 | ], 21 | (), 22 | ), 23 | it("calls the side effect function", () => { 24 | let sideEffectCount = ref(0); 25 | 26 | RxObservables.ofList([1]) 27 | |> RxObservables.onNext(_ => incr(sideEffectCount)) 28 | |> RxObservable.connect 29 | |> ignore; 30 | 31 | sideEffectCount^ |> Expect.toBeEqualToInt(1); 32 | }), 33 | ], 34 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/RetryTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | open RxReUnit; 4 | 5 | let test = 6 | describe( 7 | "retry", 8 | [ 9 | observableIt( 10 | "with cold observable", 11 | ~nextToString=string_of_int, 12 | ~source= 13 | _ => { 14 | let retryCount = ref(0); 15 | let predicate = _ => { 16 | let retry = retryCount^ < 2; 17 | if (retry) { 18 | incr(retryCount); 19 | }; 20 | retry; 21 | }; 22 | 23 | RxObservable.create(subscriber => { 24 | subscriber |> RxSubscriber.next(1); 25 | subscriber |> RxSubscriber.next(2); 26 | subscriber |> RxSubscriber.complete(~exn=Division_by_zero); 27 | }) 28 | |> RxObservables.retry(~predicate); 29 | }, 30 | ~expected=[ 31 | RxNotification.next(1), 32 | RxNotification.next(2), 33 | RxNotification.next(1), 34 | RxNotification.next(2), 35 | RxNotification.next(1), 36 | RxNotification.next(2), 37 | RxNotification.complete(Some(Division_by_zero)), 38 | ], 39 | (), 40 | ), 41 | it("with hot observable", () => { 42 | let result = ref([]); 43 | let subject = ref(RxSubject.disposed); 44 | 45 | let subscription = 46 | RxObservable.create(subscriber => { 47 | subject := RxSubject.createMulticast(); 48 | let observable = subject^ |> RxSubject.asObservable; 49 | let subscription = 50 | observable 51 | |> RxObservables.publishToSubscriber(subscriber) 52 | |> RxObservable.connect; 53 | 54 | subscriber |> RxSubscriber.addDisposable(subscription) |> ignore; 55 | }) 56 | |> RxObservables.retry 57 | |> RxObservables.onNext(x => result := [x, ...result^]) 58 | |> RxObservable.connect; 59 | 60 | let subscriber = subject^; 61 | subscriber |> RxSubject.next(1); 62 | subscriber |> RxSubject.next(2); 63 | subscriber |> RxSubject.complete(~exn=Division_by_zero); 64 | subscription |> RxDisposable.isDisposed |> Expect.toBeEqualToFalse; 65 | 66 | let subscriber = subject^; 67 | subscriber |> RxSubject.next(3); 68 | subscriber |> RxSubject.next(4); 69 | subscriber |> RxSubject.complete; 70 | subscription |> RxDisposable.isDisposed |> Expect.toBeEqualToTrue; 71 | 72 | result^ |> Expect.toBeEqualToListOfInt([4, 3, 2, 1]); 73 | }), 74 | it("doesn't retry if unsubscribed in shouldRetry callback", () => { 75 | let vts = RxVirtualTimeScheduler.create(); 76 | let scheduler = vts |> RxVirtualTimeScheduler.asScheduler; 77 | 78 | let subscription = ref(RxDisposable.disposed); 79 | let thrownException = ref(None); 80 | subscription := 81 | ofAbsoluteTimeNotifications( 82 | ~scheduler, 83 | [ 84 | (1.0, RxNotification.next(5)), 85 | (2.0, RxNotification.complete(Some(Division_by_zero))), 86 | ], 87 | ) 88 | |> RxObservables.retry(~predicate=_ => { 89 | subscription^ |> RxDisposable.dispose; 90 | true; 91 | }) 92 | |> expectObservableToProduce( 93 | ~nextToString=string_of_int, 94 | [RxNotification.next(5), RxNotification.complete(None)], 95 | ) 96 | |> RxObservables.onComplete(exn => thrownException := exn) 97 | |> RxObservable.connect; 98 | 99 | vts |> RxVirtualTimeScheduler.run; 100 | subscription^ |> RxDisposable.dispose; 101 | 102 | switch (thrownException^) { 103 | | Some(exn) => raise(exn) 104 | | _ => () 105 | }; 106 | }), 107 | ], 108 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/ScanTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = describe( 5 | "scan", 6 | [ 7 | observableIt( 8 | "publishes all intermediate values, including the initial accumulator value", 9 | ~nextToString=string_of_int, 10 | ~source= 11 | _ => 12 | RxObservables.ofList([2, 3, 4]) 13 | |> RxObservables.scan((acc, next) => acc + next, 0), 14 | ~expected=[ 15 | RxNotification.next(0), 16 | RxNotification.next(2), 17 | RxNotification.next(5), 18 | RxNotification.next(9), 19 | RxNotification.complete(None), 20 | ], 21 | (), 22 | ), 23 | ], 24 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/ShareReplayBufferTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "shareReplayBuffer", 7 | [ 8 | observableIt( 9 | "replays event on subscribe", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => { 13 | let source = 14 | ofAbsoluteTimeNotifications( 15 | ~scheduler, 16 | [ 17 | (0.0, RxNotification.next(1)), 18 | (2.0, RxNotification.next(2)), 19 | (4.0, RxNotification.next(3)), 20 | (6.0, RxNotification.next(4)), 21 | (9.0, RxNotification.complete(None)), 22 | ], 23 | ) 24 | |> RxObservables.shareReplayBuffer(2); 25 | 26 | RxObservables.mergeList([ 27 | source, 28 | source |> RxObservables.subscribeOn(~delay=5.0, scheduler), 29 | ]); 30 | }, 31 | ~expected=[ 32 | RxNotification.next(1), 33 | RxNotification.next(2), 34 | RxNotification.next(3), 35 | RxNotification.next(2), 36 | RxNotification.next(3), 37 | RxNotification.next(4), 38 | RxNotification.next(4), 39 | RxNotification.complete(None), 40 | ], 41 | (), 42 | ), 43 | ], 44 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/SomeTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "some", 7 | [ 8 | observableIt( 9 | "returns false for an subscriber that completes without producing values", 10 | ~nextToString=string_of_bool, 11 | ~source=_ => RxObservables.empty() |> RxObservables.some(i => i > 10), 12 | ~expected=[ 13 | RxNotification.next(false), 14 | RxNotification.complete(None), 15 | ], 16 | (), 17 | ), 18 | observableIt( 19 | "returns false for an subscriber for which no value passes the predicate", 20 | ~nextToString=string_of_bool, 21 | ~source= 22 | _ => 23 | RxObservables.ofList([5, 6, 7]) |> RxObservables.some(i => i > 10), 24 | ~expected=[ 25 | RxNotification.next(false), 26 | RxNotification.complete(None), 27 | ], 28 | (), 29 | ), 30 | observableIt( 31 | "completes with true on the first observed value that passed the predicate", 32 | ~nextToString=string_of_int, 33 | ~source= 34 | ({now} as scheduler) => 35 | ofAbsoluteTimeNotifications( 36 | ~scheduler, 37 | [ 38 | (1.0, RxNotification.next(8)), 39 | (2.0, RxNotification.next(11)), 40 | (3.0, RxNotification.next(14)), 41 | (4.0, RxNotification.next(6)), 42 | (5.0, RxNotification.next(5)), 43 | (6.0, RxNotification.complete(None)), 44 | ], 45 | ) 46 | |> RxObservables.some(i => i > 10) 47 | |> RxObservables.map(_ => now() |> int_of_float), 48 | ~expected=[RxNotification.next(2), RxNotification.complete(None)], 49 | (), 50 | ), 51 | ], 52 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/StartWithListTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "startWithlist", 7 | [ 8 | observableIt( 9 | "prepends the values", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => 13 | RxObservables.startWithList( 14 | [1, 2, 3], 15 | ofRelativeTimeNotifications( 16 | ~scheduler, 17 | [ 18 | (1.0, RxNotification.next(4)), 19 | (2.0, RxNotification.next(5)), 20 | (3.0, RxNotification.complete(None)), 21 | ], 22 | ), 23 | ), 24 | ~expected=[ 25 | RxNotification.next(1), 26 | RxNotification.next(2), 27 | RxNotification.next(3), 28 | RxNotification.next(4), 29 | RxNotification.next(5), 30 | RxNotification.complete(None), 31 | ], 32 | (), 33 | ), 34 | ], 35 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/StartWithValueTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "startWithValue", 7 | [ 8 | observableIt( 9 | "prepends the values", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => RxObservables.startWithValue(1, RxObservables.ofList([2, 3])), 13 | ~expected=[ 14 | RxNotification.next(1), 15 | RxNotification.next(2), 16 | RxNotification.next(3), 17 | RxNotification.complete(None), 18 | ], 19 | (), 20 | ), 21 | ], 22 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/SwitchTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "switch_", 7 | [ 8 | observableIt( 9 | "switches", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => { 13 | let childObservableA = 14 | ofRelativeTimeNotifications( 15 | ~scheduler, 16 | [ 17 | (0.0, RxNotification.next(1)), 18 | (10.0, RxNotification.next(2)), 19 | (20.0, RxNotification.next(3)), 20 | (30.0, RxNotification.next(4)), 21 | (40.0, RxNotification.complete(None)), 22 | ], 23 | ); 24 | 25 | let childObservableB = 26 | ofRelativeTimeNotifications( 27 | ~scheduler, 28 | [ 29 | (0.0, RxNotification.next(5)), 30 | (10.0, RxNotification.next(6)), 31 | (19.0, RxNotification.next(7)), 32 | (30.0, RxNotification.next(8)), 33 | (40.0, RxNotification.complete(None)), 34 | ], 35 | ); 36 | 37 | ofAbsoluteTimeNotifications( 38 | ~scheduler, 39 | [ 40 | (0.0, RxNotification.next(childObservableA)), 41 | (15.0, RxNotification.next(childObservableB)), 42 | (35.0, RxNotification.next(childObservableA)), 43 | (60.0, RxNotification.next(childObservableB)), 44 | (75.0, RxNotification.complete(None)), 45 | ], 46 | ) 47 | |> RxObservables.switch_; 48 | }, 49 | ~expected=[ 50 | RxNotification.next(1), 51 | RxNotification.next(2), 52 | RxNotification.next(5), 53 | RxNotification.next(6), 54 | RxNotification.next(7), 55 | RxNotification.next(1), 56 | RxNotification.next(2), 57 | RxNotification.next(3), 58 | RxNotification.next(5), 59 | RxNotification.next(6), 60 | RxNotification.complete(None), 61 | ], 62 | (), 63 | ), 64 | ], 65 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/TakeTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "take", 7 | [ 8 | observableIt( 9 | "emits the number of requested values ", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | _ => { 13 | let source = 14 | RxObservables.ofNotifications([ 15 | RxNotification.next(1), 16 | RxNotification.next(2), 17 | RxNotification.next(3), 18 | RxNotification.next(4), 19 | RxNotification.complete(None), 20 | ]); 21 | 22 | source |> RxObservables.take(2); 23 | }, 24 | ~expected=[ 25 | RxNotification.next(1), 26 | RxNotification.next(2), 27 | RxNotification.complete(None), 28 | ], 29 | (), 30 | ), 31 | ], 32 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/TakeUntilTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = 5 | describe( 6 | "takeUntil", 7 | [ 8 | observableIt( 9 | "emits values until notifier produces a value", 10 | ~nextToString=string_of_int, 11 | ~source= 12 | scheduler => { 13 | let source = 14 | ofRelativeTimeNotifications( 15 | ~scheduler, 16 | [ 17 | (0.0, RxNotification.next(1)), 18 | (4.0, RxNotification.next(2)), 19 | (6.0, RxNotification.next(3)), 20 | (10.0, RxNotification.next(4)), 21 | (14.0, RxNotification.complete(None)), 22 | ], 23 | ); 24 | 25 | let notifier = 26 | ofRelativeTimeNotifications( 27 | ~scheduler, 28 | [(5.0, RxNotification.next())], 29 | ); 30 | source |> RxObservables.takeUntil(notifier); 31 | }, 32 | ~expected=[ 33 | RxNotification.next(1), 34 | RxNotification.next(2), 35 | RxNotification.complete(None), 36 | ], 37 | (), 38 | ), 39 | observableIt( 40 | "notifier emitting an exception completes the observable", 41 | ~nextToString=string_of_int, 42 | ~source= 43 | scheduler => { 44 | let source = 45 | ofRelativeTimeNotifications( 46 | ~scheduler, 47 | [ 48 | (0.0, RxNotification.next(1)), 49 | (4.0, RxNotification.next(2)), 50 | (6.0, RxNotification.next(3)), 51 | (10.0, RxNotification.next(4)), 52 | (14.0, RxNotification.complete(None)), 53 | ], 54 | ); 55 | 56 | let notifier = 57 | ofRelativeTimeNotifications( 58 | ~scheduler, 59 | [(5.0, RxNotification.complete(Some(Division_by_zero)))], 60 | ); 61 | source |> RxObservables.takeUntil(notifier); 62 | }, 63 | ~expected=[ 64 | RxNotification.next(1), 65 | RxNotification.next(2), 66 | RxNotification.complete(Some(Division_by_zero)), 67 | ], 68 | (), 69 | ), 70 | ], 71 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/TimeoutTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = describe( 5 | "timeout", 6 | [ 7 | observableIt( 8 | "when timeout does not expire", 9 | ~nextToString=string_of_int, 10 | ~source= 11 | scheduler => 12 | ofRelativeTimeNotifications( 13 | ~scheduler, 14 | [ 15 | (0.0, RxNotification.next(1)), 16 | (4.0, RxNotification.next(2)), 17 | (6.0, RxNotification.next(3)), 18 | (10.0, RxNotification.next(4)), 19 | (14.0, RxNotification.complete(None)), 20 | ], 21 | ) 22 | |> RxObservables.timeout(~scheduler, 5.0), 23 | ~expected=[ 24 | RxNotification.next(1), 25 | RxNotification.next(2), 26 | RxNotification.next(3), 27 | RxNotification.next(4), 28 | RxNotification.complete(None), 29 | ], 30 | (), 31 | ), 32 | observableIt( 33 | "when timeout expires", 34 | ~nextToString=string_of_int, 35 | ~source= 36 | scheduler => 37 | ofRelativeTimeNotifications( 38 | ~scheduler, 39 | [ 40 | (0.0, RxNotification.next(1)), 41 | (4.0, RxNotification.next(2)), 42 | (6.0, RxNotification.next(3)), 43 | (15.0, RxNotification.next(4)), 44 | (20.0, RxNotification.complete(None)), 45 | ], 46 | ) 47 | |> RxObservables.timeout(~scheduler, 5.0), 48 | ~expected=[ 49 | RxNotification.next(1), 50 | RxNotification.next(2), 51 | RxNotification.next(3), 52 | RxNotification.complete(Some(RxTimeoutException.Exn)), 53 | ], 54 | (), 55 | ), 56 | ], 57 | ); -------------------------------------------------------------------------------- /packages/rx-observables/test/internal/observables/WithLatestFromTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | open RxReUnit; 3 | 4 | let test = describe( 5 | "withLatestFrom", 6 | [ 7 | observableIt( 8 | "drops values from the source, if there is no latest value", 9 | ~nextToString=string_of_int, 10 | ~source= 11 | scheduler => 12 | ofAbsoluteTimeNotifications( 13 | ~scheduler, 14 | [ 15 | (0.0, RxNotification.next(1)), 16 | (200.0, RxNotification.next(2)), 17 | (400.0, RxNotification.next(3)), 18 | (600.0, RxNotification.next(4)), 19 | (700.0, RxNotification.complete(None)), 20 | ], 21 | ) 22 | |> RxObservables.withLatestFrom( 23 | ~selector=(a, b) => a + b, 24 | ofAbsoluteTimeNotifications( 25 | ~scheduler, 26 | [ 27 | (100.0, RxNotification.next(1)), 28 | (250.0, RxNotification.next(2)), 29 | (300.0, RxNotification.next(3)), 30 | (450.0, RxNotification.next(4)), 31 | (500.0, RxNotification.complete(None)), 32 | ], 33 | ), 34 | ), 35 | ~expected=[ 36 | RxNotification.next(3), 37 | RxNotification.next(6), 38 | RxNotification.next(8), 39 | RxNotification.complete(None), 40 | ], 41 | (), 42 | ), 43 | ], 44 | ); -------------------------------------------------------------------------------- /packages/rx-scheduler/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-scheduler", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": ["@rx-reason/rx-disposables", "@rx-reason/rx-utils-do-not-depend-on"], 13 | "bs-dev-dependencies": [ 14 | "@rx-reason/reunit" 15 | ], 16 | "warnings": { 17 | "error": "+101" 18 | }, 19 | "refmt": 3, 20 | "sources": [ 21 | { 22 | "dir": "src" 23 | }, 24 | { 25 | "dir": "test", 26 | "subdirs": true, 27 | "type": "dev" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /packages/rx-scheduler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-scheduler", 3 | "version": "0.0.0", 4 | "description": "cooperative scheduler interface for reason", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "clean": "bsb -clean-world", 8 | "test": "npm run build && node lib/js/test/TestRunner.bs.js", 9 | "test-debug": "npm run build && node --inspect-brk lib/js/test/TestRunner.bs.js", 10 | "test-coverage": "npm run build && nyc node lib/js/test/TestRunner.bs.js && nyc report --reporter=html && open coverage/index.html" 11 | }, 12 | "keywords": [ 13 | "ocaml", 14 | "react", 15 | "reason", 16 | "reasonml", 17 | "BuckleScript" 18 | ], 19 | "author": { 20 | "name": "David Bordoley", 21 | "email": "bordoley@gmail.com" 22 | }, 23 | "contributors": [ 24 | { 25 | "name": "David Bordoley", 26 | "email": "bordoley@gmail.com" 27 | } 28 | ], 29 | "license": "MIT", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/bordoley/rx-reason.git" 33 | }, 34 | "dependencies": { 35 | "@rx-reason/rx-disposables": "^0.0.0", 36 | "@rx-reason/rx-utils-do-not-depend-on": "0.0.0" 37 | }, 38 | "devDependencies": { 39 | "bs-platform": "^4.0.7", 40 | "nyc": "^13.1.0", 41 | "@rx-reason/reunit": "^0.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/rx-scheduler/src/RxScheduler.re: -------------------------------------------------------------------------------- 1 | type continuation = (~now: unit => float, ~shouldYield: unit => bool) => result 2 | and result = 3 | | Yield(continuation) 4 | | ContinueAfter(float, continuation) 5 | | Complete; 6 | 7 | module Continuation = { 8 | type t = continuation; 9 | }; 10 | 11 | module Result = { 12 | type t = 13 | result = 14 | | Yield(continuation) | ContinueAfter(float, continuation) | Complete; 15 | 16 | let yield = continuation => Yield(continuation); 17 | 18 | let continueAfter = (~delay, continuation) => { 19 | RxPreconditions.checkArgument( 20 | delay > 0.0, 21 | "RxScheduler: delay must be greater than 0.0", 22 | ); 23 | ContinueAfter(delay, continuation); 24 | }; 25 | 26 | let complete = Complete; 27 | 28 | let map = (~onYield, ~onContinueAfter, ~onComplete, result) => 29 | switch (result) { 30 | | Yield(continuation) => onYield(continuation) 31 | | ContinueAfter(delay, continuation) => 32 | onContinueAfter(~delay, continuation) 33 | | Complete => onComplete() 34 | }; 35 | 36 | let map1 = (~onYield, ~onContinueAfter, ~onComplete, ctx0, result) => 37 | switch (result) { 38 | | Yield(continuation) => onYield(ctx0, continuation) 39 | | ContinueAfter(delay, continuation) => 40 | onContinueAfter(ctx0, ~delay, continuation) 41 | | Complete => onComplete(ctx0) 42 | }; 43 | 44 | let map2 = (~onYield, ~onContinueAfter, ~onComplete, ctx0, ctx1, result) => 45 | switch (result) { 46 | | Yield(continuation) => onYield(ctx0, ctx1, continuation) 47 | | ContinueAfter(delay, continuation) => 48 | onContinueAfter(ctx0, ctx1, ~delay, continuation) 49 | | Complete => onComplete(ctx0, ctx1) 50 | }; 51 | 52 | let map3 = 53 | (~onYield, ~onContinueAfter, ~onComplete, ctx0, ctx1, ctx2, result) => 54 | switch (result) { 55 | | Yield(continuation) => onYield(ctx0, ctx1, ctx2, continuation) 56 | | ContinueAfter(delay, continuation) => 57 | onContinueAfter(ctx0, ctx1, ctx2, ~delay, continuation) 58 | | Complete => onComplete(ctx0, ctx1, ctx2) 59 | }; 60 | 61 | let map4 = 62 | ( 63 | ~onYield, 64 | ~onContinueAfter, 65 | ~onComplete, 66 | ctx0, 67 | ctx1, 68 | ctx2, 69 | ctx3, 70 | result, 71 | ) => 72 | switch (result) { 73 | | Yield(continuation) => onYield(ctx0, ctx1, ctx2, ctx3, continuation) 74 | | ContinueAfter(delay, continuation) => 75 | onContinueAfter(ctx0, ctx1, ctx2, ctx3, ~delay, continuation) 76 | | Complete => onComplete(ctx0, ctx1, ctx2, ctx3) 77 | }; 78 | }; 79 | 80 | type t = { 81 | now: unit => float, 82 | schedule: (~delay: float=?, Continuation.t) => RxDisposable.t, 83 | }; 84 | 85 | let now = scheduler => scheduler.now(); 86 | 87 | let schedule = (~delay=?, continuation, scheduler) => 88 | scheduler.schedule(~delay?, continuation); -------------------------------------------------------------------------------- /packages/rx-scheduler/src/RxScheduler.rei: -------------------------------------------------------------------------------- 1 | module rec Continuation: { 2 | type t = (~now: unit => float, ~shouldYield: unit => bool) => Result.t; 3 | } 4 | and Result: { 5 | type t; 6 | 7 | let yield: Continuation.t => t; 8 | let continueAfter: (~delay: float, Continuation.t) => t; 9 | let complete: t; 10 | 11 | let map: 12 | ( 13 | ~onYield: Continuation.t => 'a, 14 | ~onContinueAfter: (~delay: float, Continuation.t) => 'a, 15 | ~onComplete: unit => 'a, 16 | t 17 | ) => 18 | 'a; 19 | 20 | let map1: 21 | ( 22 | ~onYield: ('ctx0, Continuation.t) => 'a, 23 | ~onContinueAfter: ('ctx0, ~delay: float, Continuation.t) => 'a, 24 | ~onComplete: 'ctx0 => 'a, 25 | 'ctx0, 26 | t 27 | ) => 28 | 'a; 29 | 30 | let map2: 31 | ( 32 | ~onYield: ('ctx0, 'ctx1, Continuation.t) => 'a, 33 | ~onContinueAfter: ('ctx0, 'ctx1, ~delay: float, Continuation.t) => 'a, 34 | ~onComplete: ('ctx0, 'ctx1) => 'a, 35 | 'ctx0, 36 | 'ctx1, 37 | t 38 | ) => 39 | 'a; 40 | 41 | let map3: 42 | ( 43 | ~onYield: ('ctx0, 'ctx1, 'ctx2, Continuation.t) => 'a, 44 | ~onContinueAfter: ('ctx0, 'ctx1, 'ctx2, ~delay: float, Continuation.t) => 45 | 'a, 46 | ~onComplete: ('ctx0, 'ctx1, 'ctx2) => 'a, 47 | 'ctx0, 48 | 'ctx1, 49 | 'ctx2, 50 | t 51 | ) => 52 | 'a; 53 | 54 | let map4: 55 | ( 56 | ~onYield: ('ctx0, 'ctx1, 'ctx2, 'ctx3, Continuation.t) => 'a, 57 | ~onContinueAfter: ('ctx0, 'ctx1, 'ctx2, 'ctx3, ~delay: float, Continuation.t) => 58 | 'a, 59 | ~onComplete: ('ctx0, 'ctx1, 'ctx2, 'ctx3) => 'a, 60 | 'ctx0, 61 | 'ctx1, 62 | 'ctx2, 63 | 'ctx3, 64 | t 65 | ) => 66 | 'a; 67 | }; 68 | 69 | type t = { 70 | now: unit => float, 71 | schedule: (~delay: float=?, Continuation.t) => RxDisposable.t, 72 | }; 73 | 74 | let now: t => float; 75 | 76 | let schedule: (~delay: float=?, Continuation.t, t) => RxDisposable.t; -------------------------------------------------------------------------------- /packages/rx-scheduler/test/RxSchedulerResultTest.re: -------------------------------------------------------------------------------- 1 | open ReUnit; 2 | open ReUnit.Test; 3 | 4 | let test = 5 | describe( 6 | "RxScheduler", 7 | [ 8 | describe( 9 | "Result", 10 | [ 11 | describe( 12 | "continueAfter", 13 | [ 14 | it("raises with delay less than 0.0", () => 15 | Expect.shouldRaise(() => 16 | RxScheduler.Result.continueAfter( 17 | ~delay=-1.0, (~now as _, ~shouldYield as _) => 18 | RxScheduler.Result.complete 19 | ) 20 | |> ignore 21 | ) 22 | ), 23 | ], 24 | ), 25 | ], 26 | ), 27 | ], 28 | ); -------------------------------------------------------------------------------- /packages/rx-scheduler/test/TestRunner.re: -------------------------------------------------------------------------------- 1 | open ReUnit.Test; 2 | 3 | ReUnit.run(describe("rx-scheduler", [RxSchedulerResultTest.test])); -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-utils-do-not-depend-on", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": [], 13 | "bs-dev-dependencies": [], 14 | "warnings": { 15 | "error": "+101" 16 | }, 17 | "refmt": 3, 18 | "sources": [ 19 | { 20 | "dir": "src", 21 | "subdirs": [ 22 | { 23 | "dir": "bs" 24 | } 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-utils-do-not-depend-on", 3 | "version": "0.0.0", 4 | "description": "internal library for supporting both native and js environments", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "start": "bsb -make-world -w", 8 | "clean": "bsb -clean-world" 9 | }, 10 | "keywords": [ 11 | "ocaml", 12 | "reason", 13 | "reasonml", 14 | "BuckleScript" 15 | ], 16 | "author": { 17 | "name": "David Bordoley", 18 | "email": "bordoley@gmail.com" 19 | }, 20 | "contributors": [ 21 | { 22 | "name": "David Bordoley", 23 | "email": "bordoley@gmail.com" 24 | } 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/bordoley/rx-reason.git" 30 | }, 31 | "dependencies": { 32 | }, 33 | "devDependencies": { 34 | "bs-platform": "^4.0.7" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/RxFunctions.re: -------------------------------------------------------------------------------- 1 | let alwaysFalse1 = _ => false; 2 | 3 | let alwaysTrue1 = _ => true; 4 | 5 | let alwaysUnit1 = _ => (); 6 | 7 | let alwaysUnit2 = (_, _) => (); 8 | 9 | let alwaysUnit3 = (_, _, _) => (); 10 | 11 | let alwaysUnit4 = (_, _, _, _) => (); 12 | 13 | let identity = a => a; 14 | 15 | let referenceEquality = (===); 16 | 17 | let some = a => Some(a); -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/RxMutableOption.re: -------------------------------------------------------------------------------- 1 | /*** 2 | * Copyright (c) 2017 - present Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | type t('a) = ref(array('a)); 10 | 11 | let create = () : t('a) => ref([||]); 12 | 13 | let get = (opt: t('a)) : 'a => opt^[0]; 14 | 15 | let isEmpty = (opt: t('a)) : bool => Array.length(opt^) === 0; 16 | 17 | let isNotEmpty = (opt: t('a)) : bool => Array.length(opt^) !== 0; 18 | 19 | let set = (value: 'a, opt: t('a)) => 20 | if (isEmpty(opt)) { 21 | opt := [|value|] 22 | } else { 23 | opt^[0] = value 24 | }; 25 | 26 | let setIf = ( 27 | test: 'a => 'a => bool, 28 | value: 'a, 29 | opt: t('a), 30 | ): bool => { 31 | if (opt |> isEmpty) { 32 | opt := [|value|]; 33 | true; 34 | } else { 35 | let old = opt |> get; 36 | let shouldUpdate = test(old, value); 37 | if(shouldUpdate) { 38 | opt^[0] = value; 39 | }; 40 | shouldUpdate; 41 | } 42 | }; 43 | 44 | let unset = (opt: t('a)) => opt := [||]; -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/RxPreconditions.re: -------------------------------------------------------------------------------- 1 | let checkArgument = (state, msg) => 2 | if (! state) { 3 | failwith(msg); 4 | }; -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/bs/RxAtomic.re: -------------------------------------------------------------------------------- 1 | type t('a) = ref('a); 2 | 3 | let make = v => ref(v); 4 | 5 | let get = atomic => atomic^; 6 | 7 | let set = (atomic, v) => atomic := v; 8 | 9 | let setTrue = atomic => { 10 | atomic := true; 11 | }; 12 | 13 | let compare_and_set = (atomic, vold, vnew) => 14 | if (atomic^ === vold) { 15 | atomic := vnew; 16 | true; 17 | } else { 18 | false; 19 | }; 20 | 21 | let exchange = (atomic, v) => { 22 | let old = atomic^; 23 | atomic := v; 24 | old; 25 | }; 26 | 27 | let incr = atomic => { 28 | incr(atomic); 29 | atomic^; 30 | }; 31 | 32 | let decr = atomic => { 33 | decr(atomic); 34 | atomic^; 35 | }; -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/bs/RxAtomic.rei: -------------------------------------------------------------------------------- 1 | type t('a); 2 | 3 | let make: 'a => t('a); 4 | let get: t('a) => 'a; 5 | let set: (t('a), 'a) => unit; 6 | let setTrue: t(bool) => unit; 7 | let compare_and_set: (t('a), 'a, 'a) => bool; 8 | let exchange: (t('a), 'a) => 'a; 9 | let incr: t(int) => int; 10 | let decr: t(int) => int; -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/bs/RxCopyOnWriteArray.re: -------------------------------------------------------------------------------- 1 | type t('a) = array('a); 2 | 3 | [@bs.new] external newArrayUnsafe : int => array('a) = "Array"; 4 | 5 | [@bs.val] external getUnsafe : (int, array('a)) => 'a = ""; 6 | %bs.raw 7 | {| 8 | function getUnsafe(index, array) { 9 | return array[index]; 10 | }|}; 11 | 12 | [@bs.val] external setUnsafe : (int, 'a, array('a)) => unit = ""; 13 | %bs.raw 14 | {| 15 | function setUnsafe(index, value, array) { 16 | array[index] = value; 17 | }|}; 18 | 19 | let count = Js.Array.length; 20 | 21 | let empty = () => [||]; 22 | 23 | let forEach = Js.Array.forEach; 24 | 25 | let addLast = (value, arr) => { 26 | let count = count(arr); 27 | let newLength = count + 1; 28 | let retval = newArrayUnsafe(newLength); 29 | 30 | for (index in 0 to count - 1) { 31 | let v = getUnsafe(index, arr); 32 | setUnsafe(index, v, retval); 33 | }; 34 | 35 | setUnsafe(count, value, retval); 36 | retval; 37 | }; 38 | 39 | let addLastWithMaxCount = (maxCount, value, arr) => { 40 | RxPreconditions.checkArgument( 41 | maxCount >= 0, 42 | "max count must be greater than or equal to 0", 43 | ); 44 | let count = count(arr); 45 | RxPreconditions.checkArgument( 46 | maxCount >= count, 47 | "max count must be less than or equal to 0", 48 | ); 49 | 50 | if (count < maxCount) { 51 | addLast(value, arr); 52 | } else { 53 | let retval = newArrayUnsafe(maxCount); 54 | 55 | for (index in 1 to maxCount - 1) { 56 | let v = getUnsafe(index, arr); 57 | setUnsafe(index - 1, v, retval); 58 | }; 59 | 60 | setUnsafe(maxCount - 1, value, retval); 61 | retval; 62 | }; 63 | }; 64 | 65 | let findAndRemoveReference = (value, arr) => { 66 | let index = Js.Array.indexOf(value, arr); 67 | if (index < 0) { 68 | arr; 69 | } else { 70 | let count = count(arr); 71 | let newLength = count - 1; 72 | let retval = newArrayUnsafe(newLength); 73 | 74 | for (index in 0 to index - 1) { 75 | let v = getUnsafe(index, arr); 76 | setUnsafe(index, v, retval); 77 | }; 78 | 79 | for (index in index + 1 to count - 1) { 80 | let v = getUnsafe(index, arr); 81 | setUnsafe(index - 1, v, retval); 82 | }; 83 | retval; 84 | }; 85 | }; -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/bs/RxLock.re: -------------------------------------------------------------------------------- 1 | type t = unit; 2 | let create: unit => t = () => (); 3 | let acquire = (_: t) => (); 4 | let release = (_: t) => (); -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/bs/RxMutableList.re: -------------------------------------------------------------------------------- 1 | type t('a) = array('a); 2 | 3 | let add = (value, list) => { 4 | list |> Js.Array.push(value) |> ignore; 5 | } 6 | 7 | let clear = queue => 8 | queue 9 | |> Js.Array.removeCountInPlace(~pos=0, ~count=Js.Array.length(queue)) 10 | |> ignore; 11 | 12 | let create = () : t('a) => [||]; 13 | 14 | let forEachReversed = (f, list) => { 15 | let length = Js.Array.length(list); 16 | for (i in 0 to length - 1) { 17 | let idx = length - 1 - i; 18 | f(list[idx]); 19 | }; 20 | }; 21 | 22 | let remove = (value, list) => { 23 | let pos = list |> Js.Array.indexOf(value); 24 | if (pos >= 0) { 25 | list |> Js.Array.removeCountInPlace(~pos, ~count=1) |> ignore; 26 | }; 27 | }; -------------------------------------------------------------------------------- /packages/rx-utils-do-not-depend-on/src/bs/RxMutableQueue.re: -------------------------------------------------------------------------------- 1 | type t('a) = array('a); 2 | 3 | let clear = queue => 4 | queue 5 | |> Js.Array.removeCountInPlace(~pos=0, ~count=Js.Array.length(queue)) 6 | |> ignore; 7 | 8 | let create = () : t('a) => [||]; 9 | 10 | let dequeue = queue => Js.Array.shift(queue); 11 | 12 | let enqueue = (scheduledItem, queue) => 13 | queue |> Js.Array.push(scheduledItem) |> ignore; 14 | 15 | let forEach = (f, queue) => queue |> Js.Array.forEach(f); 16 | 17 | let peek = queue => 18 | if (Js.Array.length(queue) > 0) { 19 | Some(queue[0]); 20 | } else { 21 | None; 22 | }; 23 | 24 | let length = Js.Array.length; -------------------------------------------------------------------------------- /packages/rx-virtual-time-scheduler/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-virtual-time-scheduler", 3 | "package-specs": [ 4 | { 5 | "module": "commonjs" 6 | }, 7 | { 8 | "module": "es6" 9 | } 10 | ], 11 | "suffix": ".bs.js", 12 | "bs-dependencies": ["@rx-reason/rx-disposables", "@rx-reason/rx-scheduler", "@rx-reason/rx-utils-do-not-depend-on"], 13 | "bs-dev-dependencies": [], 14 | "warnings": { 15 | "error": "+101" 16 | }, 17 | "refmt": 3, 18 | "sources": [ 19 | { 20 | "dir": "src" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /packages/rx-virtual-time-scheduler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rx-reason/rx-virtual-time-scheduler", 3 | "version": "0.0.0", 4 | "description": "virtual time scheduler for testing", 5 | "scripts": { 6 | "build": "bsb -make-world", 7 | "start": "bsb -make-world -w", 8 | "clean": "bsb -clean-world" 9 | }, 10 | "keywords": [ 11 | "ocaml", 12 | "reason", 13 | "reasonml", 14 | "BuckleScript" 15 | ], 16 | "author": { 17 | "name": "David Bordoley", 18 | "email": "bordoley@gmail.com" 19 | }, 20 | "contributors": [ 21 | { 22 | "name": "David Bordoley", 23 | "email": "bordoley@gmail.com" 24 | } 25 | ], 26 | "license": "MIT", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/bordoley/rx-reason.git" 30 | }, 31 | "dependencies": { 32 | "@rx-reason/rx-disposables": "^0.0.0", 33 | "@rx-reason/rx-scheduler": "^0.0.0", 34 | "@rx-reason/rx-utils-do-not-depend-on": "^0.0.0" 35 | }, 36 | "devDependencies": { 37 | "bs-platform": "^4.0.7" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/rx-virtual-time-scheduler/src/RxVirtualTimeScheduler.re: -------------------------------------------------------------------------------- 1 | type t = { 2 | currentTime: ref(int), 3 | disposable: RxDisposable.t, 4 | /* FIXME: Don't use Belt directly. Instead add a Multimap to platform */ 5 | timeQueue: Belt.MutableMap.Int.t(RxMutableQueue.t(unit => unit)), 6 | scheduler: RxScheduler.t, 7 | }; 8 | 9 | let asScheduler = ({scheduler}) => scheduler; 10 | 11 | let advance = ({disposable, timeQueue} as vts: t) => { 12 | let currentTime = vts.currentTime^; 13 | 14 | switch (Belt.MutableMap.Int.get(timeQueue, currentTime)) { 15 | | None => () 16 | | Some(queue) => 17 | while ({ 18 | let isDisposed = RxDisposable.isDisposed(disposable); 19 | let head = RxMutableQueue.peek(queue); 20 | head !== None && ! isDisposed; 21 | }) { 22 | switch (RxMutableQueue.dequeue(queue)) { 23 | | Some(work) => work() 24 | | None => () 25 | }; 26 | } 27 | }; 28 | 29 | Belt.MutableMap.Int.remove(timeQueue, currentTime); 30 | incr(vts.currentTime); 31 | }; 32 | 33 | let create = () => { 34 | let currentTime = ref(0); 35 | let timeQueue = Belt.MutableMap.Int.make(); 36 | let disposable = RxDisposable.create1(Belt.MutableMap.Int.clear, timeQueue); 37 | 38 | let schedule = (delay, work) => { 39 | let currentTime = currentTime^; 40 | let scheduleTime = currentTime + int_of_float(delay); 41 | 42 | if (scheduleTime >= currentTime) { 43 | switch (Belt.MutableMap.Int.get(timeQueue, scheduleTime)) { 44 | | Some(queue) => queue |> RxMutableQueue.enqueue(work) 45 | | None => 46 | let queue = RxMutableQueue.create(); 47 | queue |> RxMutableQueue.enqueue(work); 48 | Belt.MutableMap.Int.set(timeQueue, scheduleTime, queue); 49 | }; 50 | }; 51 | }; 52 | 53 | let now = () => currentTime^ |> float_of_int; 54 | 55 | let scheduler: RxScheduler.t = { 56 | now, 57 | schedule: (~delay=?, continuation) => { 58 | let disposable = RxDisposable.empty(); 59 | 60 | let rec work = (continuation, ()) => 61 | if (! RxDisposable.isDisposed(disposable)) { 62 | continuation(~now, ~shouldYield=RxFunctions.alwaysFalse1) 63 | |> RxScheduler.Result.map( 64 | ~onYield=continuation => scheduleWork(continuation), 65 | ~onContinueAfter= 66 | (~delay, continuation) => 67 | scheduleWork(~delay, continuation), 68 | ~onComplete=() => disposable |> RxDisposable.dispose, 69 | ); 70 | } 71 | and scheduleWork = (~delay=0.0, continuation) => 72 | if (! RxDisposable.isDisposed(disposable)) { 73 | schedule(delay, work(continuation)); 74 | }; 75 | 76 | scheduleWork(~delay?, continuation); 77 | disposable; 78 | }, 79 | }; 80 | 81 | {currentTime, disposable, timeQueue, scheduler}; 82 | }; 83 | 84 | let run = ({disposable, timeQueue} as vts: t) => { 85 | let break = ref(RxDisposable.isDisposed(disposable)); 86 | 87 | while (! break^) { 88 | advance(vts); 89 | break := 90 | Belt.MutableMap.Int.isEmpty(timeQueue) 91 | || RxDisposable.isDisposed(disposable); 92 | }; 93 | disposable |> RxDisposable.dispose; 94 | }; -------------------------------------------------------------------------------- /packages/rx-virtual-time-scheduler/src/RxVirtualTimeScheduler.rei: -------------------------------------------------------------------------------- 1 | /** 2 | * A Scheduler that schedules work items using virtual time in ms. 3 | */; 4 | 5 | type t; 6 | 7 | /** Returns a new Scheduler instance that schedules work on the specified VirtualTimeScheduler. */ 8 | let asScheduler: t => RxScheduler.t; 9 | 10 | /** Advances the scheduler by 1 virtual ms. */ 11 | let advance: t => unit; 12 | 13 | /** Constructs a new VirtualTimeScheduler. */ 14 | let create: unit => t; 15 | 16 | /** Runs the VirtualTimeScheduler until it has no more work to execute. */ 17 | let run: t => unit; --------------------------------------------------------------------------------