diff --git a/.editorconfig b/.editorconfig index 1ea2ffe..56c91c6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -25,10 +25,10 @@ indent_size = 2 dotnet_sort_system_directives_first = true # Avoid "this." and "Me." if not necessary -dotnet_style_qualification_for_field = false:suggestion -dotnet_style_qualification_for_property = false:suggestion -dotnet_style_qualification_for_method = false:suggestion -dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = true:suggestion +dotnet_style_qualification_for_property = true:suggestion +dotnet_style_qualification_for_method = true:suggestion +dotnet_style_qualification_for_event = true:suggestion # Use language keywords instead of framework type names for type references dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion @@ -44,14 +44,14 @@ dotnet_style_explicit_tuple_names = true:suggestion # CSharp code style settings: [*.cs] # Prefer "var" where the type is apparent -csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = false:suggestion +csharp_style_var_elsewhere = true:suggestion # Prefer method-like constructs to have a block body -csharp_style_expression_bodied_methods = false:none -csharp_style_expression_bodied_constructors = false:none -csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_methods = true:none +csharp_style_expression_bodied_constructors = true:none +csharp_style_expression_bodied_operators = true:none # Prefer property-like constructs to have an expression-body csharp_style_expression_bodied_properties = true:none diff --git a/Cycle.NET.Demo/Cycle.NET.Demo.csproj b/Cycle.NET.Demo/Cycle.NET.Demo.csproj index 894227f..fd1486f 100644 --- a/Cycle.NET.Demo/Cycle.NET.Demo.csproj +++ b/Cycle.NET.Demo/Cycle.NET.Demo.csproj @@ -2,9 +2,13 @@ Exe - netcoreapp2.0 + net6.0 + + + + diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 0d7e7b1..f6d3fde 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -4,7 +4,7 @@ namespace Cycle.NET.Demo { - class Program + internal static class Program { private static IObservable LogDriver(IObservable sinks) { @@ -15,6 +15,8 @@ private static IObservable LogDriver(IObservable sinks) private static IObservable KeyInputDriver(IObservable sinks) { + _ = sinks; + return Observable.Range(20, 5).Select(i => (object)i); } @@ -27,7 +29,7 @@ private static Streams CycleMain(Streams sources) return sinks; } - static void Main(string[] args) + static void Main() { var drivers = new Drivers { diff --git a/Cycle.NET.ToggleDemo/App.xaml b/Cycle.NET.ToggleDemo/App.xaml new file mode 100644 index 0000000..f1e7bf1 --- /dev/null +++ b/Cycle.NET.ToggleDemo/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/Cycle.NET.ToggleDemo/App.xaml.cs b/Cycle.NET.ToggleDemo/App.xaml.cs new file mode 100644 index 0000000..95f90ab --- /dev/null +++ b/Cycle.NET.ToggleDemo/App.xaml.cs @@ -0,0 +1,12 @@ +namespace Cycle.NET.ToggleDemo +{ + public partial class App : Application + { + public App() + { + this.InitializeComponent(); + + this.MainPage = AppBootstrapper.CreateMainPage(); + } + } +} diff --git a/Cycle.NET.ToggleDemo/AppBootstrapper.cs b/Cycle.NET.ToggleDemo/AppBootstrapper.cs new file mode 100644 index 0000000..2053d7c --- /dev/null +++ b/Cycle.NET.ToggleDemo/AppBootstrapper.cs @@ -0,0 +1,62 @@ +using Cycle.NET.ToggleDemo.ViewModels; +using Cycle.NET.ToggleDemo.Views; +using ReactiveUI; +using Splat; + +namespace Cycle.NET.ToggleDemo +{ + /// + /// The app bootstrapper which is used to register everything with the Splat service locator. + /// + public static class AppBootstrapper + { + /// + /// Registers everything with the Splat service locator. + /// + public static MauiAppBuilder UseAppBootstrapper(this MauiAppBuilder builder) + { + var router = new RoutingState(); + var screen = new AppBootstrapScreen(router); + Locator.CurrentMutable.RegisterConstant(screen, typeof(IScreen)); + Locator.CurrentMutable.Register(() => new ToggleDemoView(), typeof(IViewFor)); + + router + .NavigateAndReset + .Execute(new ToggleDemoViewModel()) + .Subscribe(); + + return builder; + } + + /// + /// Creates the first main page used within the application. + /// + /// The page generated. + public static Page CreateMainPage() + { + // NB: This returns the opening page that the platform-specific + // boilerplate code will look for. It will know to find us because + // we've registered our AppBootstrapScreen. + return new ReactiveUI.Maui.RoutedViewHost(); + } + + /// + /// The app bootstrap screen is the central location for the RoutingState used for routing between views. + /// + private sealed class AppBootstrapScreen : ReactiveObject, IScreen + { + /// + /// Initializes a new instance of the class. + /// + public AppBootstrapScreen(RoutingState router) + { + this.Router = router; + } + + /// + /// Gets or sets the router which is used to navigate between views. + /// + public RoutingState Router { get; set; } + } + } +} diff --git a/Cycle.NET.ToggleDemo/Cycle.NET.ToggleDemo.csproj b/Cycle.NET.ToggleDemo/Cycle.NET.ToggleDemo.csproj new file mode 100644 index 0000000..d537e29 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Cycle.NET.ToggleDemo.csproj @@ -0,0 +1,60 @@ + + + + net6.0-android;net6.0-ios;net6.0-maccatalyst + $(TargetFrameworks);net6.0-windows10.0.19041.0 + + + Exe + Cycle.NET.ToggleDemo + true + true + enable + + + Cycle.NET.ToggleDemo + + + com.companyname.cycle.net.toggledemo + 890c6119-9c69-4ea5-9c90-82044e4cd5d2 + + + 1.0 + 1 + + 14.2 + 14.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cycle.NET.ToggleDemo/MauiProgram.cs b/Cycle.NET.ToggleDemo/MauiProgram.cs new file mode 100644 index 0000000..b6575bc --- /dev/null +++ b/Cycle.NET.ToggleDemo/MauiProgram.cs @@ -0,0 +1,20 @@ +namespace Cycle.NET.ToggleDemo +{ + public static class MauiProgram + { + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .UseAppBootstrapper() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); + }); + + return builder.Build(); + } + } +} diff --git a/Cycle.NET.ToggleDemo/Platforms/Android/AndroidManifest.xml b/Cycle.NET.ToggleDemo/Platforms/Android/AndroidManifest.xml new file mode 100644 index 0000000..e9937ad --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Android/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/Android/MainActivity.cs b/Cycle.NET.ToggleDemo/Platforms/Android/MainActivity.cs new file mode 100644 index 0000000..551c556 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Android/MainActivity.cs @@ -0,0 +1,11 @@ +using Android.App; +using Android.Content.PM; +using Android.OS; + +namespace Cycle.NET.ToggleDemo +{ + [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] + public class MainActivity : MauiAppCompatActivity + { + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/Android/MainApplication.cs b/Cycle.NET.ToggleDemo/Platforms/Android/MainApplication.cs new file mode 100644 index 0000000..71268ec --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Android/MainApplication.cs @@ -0,0 +1,16 @@ +using Android.App; +using Android.Runtime; + +namespace Cycle.NET.ToggleDemo +{ + [Application] + public class MainApplication : MauiApplication + { + public MainApplication(IntPtr handle, JniHandleOwnership ownership) + : base(handle, ownership) + { + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/Android/Resources/values/colors.xml b/Cycle.NET.ToggleDemo/Platforms/Android/Resources/values/colors.xml new file mode 100644 index 0000000..c04d749 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Android/Resources/values/colors.xml @@ -0,0 +1,6 @@ + + + #512BD4 + #2B0B98 + #2B0B98 + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/AppDelegate.cs b/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/AppDelegate.cs new file mode 100644 index 0000000..35ae98f --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/AppDelegate.cs @@ -0,0 +1,10 @@ +using Foundation; + +namespace Cycle.NET.ToggleDemo +{ + [Register("AppDelegate")] + public class AppDelegate : MauiUIApplicationDelegate + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Info.plist b/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Info.plist new file mode 100644 index 0000000..c96dd0a --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Info.plist @@ -0,0 +1,30 @@ + + + + + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/appicon.appiconset + + diff --git a/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Program.cs b/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Program.cs new file mode 100644 index 0000000..a0b8c62 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Program.cs @@ -0,0 +1,16 @@ +using ObjCRuntime; +using UIKit; + +namespace Cycle.NET.ToggleDemo +{ + public static class Program + { + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/Tizen/Main.cs b/Cycle.NET.ToggleDemo/Platforms/Tizen/Main.cs new file mode 100644 index 0000000..232ff56 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Tizen/Main.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.Maui; +using Microsoft.Maui.Hosting; + +namespace Cycle.NET.ToggleDemo +{ + internal class Program : MauiApplication + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + + static void Main(string[] args) + { + var app = new Program(); + app.Run(args); + } + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/Tizen/tizen-manifest.xml b/Cycle.NET.ToggleDemo/Platforms/Tizen/tizen-manifest.xml new file mode 100644 index 0000000..002f1ca --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Tizen/tizen-manifest.xml @@ -0,0 +1,15 @@ + + + + + + maui-appicon-placeholder + + + + + http://tizen.org/privilege/internet + + + + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml b/Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml new file mode 100644 index 0000000..a2375e1 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml.cs b/Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml.cs new file mode 100644 index 0000000..a2304a5 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml.cs @@ -0,0 +1,24 @@ +using Microsoft.UI.Xaml; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Cycle.NET.ToggleDemo.WinUI +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : MauiWinUIApplication + { + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/Windows/Package.appxmanifest b/Cycle.NET.ToggleDemo/Platforms/Windows/Package.appxmanifest new file mode 100644 index 0000000..d6714fd --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Windows/Package.appxmanifest @@ -0,0 +1,46 @@ + + + + + + + + + $placeholder$ + User Name + $placeholder$.png + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cycle.NET.ToggleDemo/Platforms/Windows/app.manifest b/Cycle.NET.ToggleDemo/Platforms/Windows/app.manifest new file mode 100644 index 0000000..95bbeaf --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/Windows/app.manifest @@ -0,0 +1,15 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + diff --git a/Cycle.NET.ToggleDemo/Platforms/iOS/AppDelegate.cs b/Cycle.NET.ToggleDemo/Platforms/iOS/AppDelegate.cs new file mode 100644 index 0000000..35ae98f --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/iOS/AppDelegate.cs @@ -0,0 +1,10 @@ +using Foundation; + +namespace Cycle.NET.ToggleDemo +{ + [Register("AppDelegate")] + public class AppDelegate : MauiUIApplicationDelegate + { + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Platforms/iOS/Info.plist b/Cycle.NET.ToggleDemo/Platforms/iOS/Info.plist new file mode 100644 index 0000000..0004a4f --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/iOS/Info.plist @@ -0,0 +1,32 @@ + + + + + LSRequiresIPhoneOS + + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/appicon.appiconset + + diff --git a/Cycle.NET.ToggleDemo/Platforms/iOS/Program.cs b/Cycle.NET.ToggleDemo/Platforms/iOS/Program.cs new file mode 100644 index 0000000..a0b8c62 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Platforms/iOS/Program.cs @@ -0,0 +1,16 @@ +using ObjCRuntime; +using UIKit; + +namespace Cycle.NET.ToggleDemo +{ + public static class Program + { + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Properties/launchSettings.json b/Cycle.NET.ToggleDemo/Properties/launchSettings.json new file mode 100644 index 0000000..edf8aad --- /dev/null +++ b/Cycle.NET.ToggleDemo/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Windows Machine": { + "commandName": "MsixPackage", + "nativeDebugging": false + } + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Resources/AppIcon/appicon.svg b/Cycle.NET.ToggleDemo/Resources/AppIcon/appicon.svg new file mode 100644 index 0000000..9d63b65 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Resources/AppIcon/appicon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Resources/AppIcon/appiconfg.svg b/Cycle.NET.ToggleDemo/Resources/AppIcon/appiconfg.svg new file mode 100644 index 0000000..21dfb25 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Resources/AppIcon/appiconfg.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Regular.ttf b/Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..e248a95 Binary files /dev/null and b/Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Regular.ttf differ diff --git a/Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Semibold.ttf b/Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Semibold.ttf new file mode 100644 index 0000000..dbc9572 Binary files /dev/null and b/Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Semibold.ttf differ diff --git a/Cycle.NET.ToggleDemo/Resources/Images/dotnet_bot.svg b/Cycle.NET.ToggleDemo/Resources/Images/dotnet_bot.svg new file mode 100644 index 0000000..abfaff2 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Resources/Images/dotnet_bot.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cycle.NET.ToggleDemo/Resources/Raw/AboutAssets.txt b/Cycle.NET.ToggleDemo/Resources/Raw/AboutAssets.txt new file mode 100644 index 0000000..15d6244 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Resources/Raw/AboutAssets.txt @@ -0,0 +1,15 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories). Deployment of the asset to your application +is automatically handled by the following `MauiAsset` Build Action within your `.csproj`. + + + +These files will be deployed with you package and will be accessible using Essentials: + + async Task LoadMauiAsset() + { + using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); + using var reader = new StreamReader(stream); + + var contents = reader.ReadToEnd(); + } diff --git a/Cycle.NET.ToggleDemo/Resources/Splash/splash.svg b/Cycle.NET.ToggleDemo/Resources/Splash/splash.svg new file mode 100644 index 0000000..21dfb25 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Resources/Splash/splash.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Resources/Styles/Colors.xaml b/Cycle.NET.ToggleDemo/Resources/Styles/Colors.xaml new file mode 100644 index 0000000..245758b --- /dev/null +++ b/Cycle.NET.ToggleDemo/Resources/Styles/Colors.xaml @@ -0,0 +1,44 @@ + + + + + #512BD4 + #DFD8F7 + #2B0B98 + White + Black + #E1E1E1 + #C8C8C8 + #ACACAC + #919191 + #6E6E6E + #404040 + #212121 + #141414 + + + + + + + + + + + + + + + #F7B548 + #FFD590 + #FFE5B9 + #28C2D1 + #7BDDEF + #C3F2F4 + #3E8EED + #72ACF1 + #A7CBF6 + + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Resources/Styles/Styles.xaml b/Cycle.NET.ToggleDemo/Resources/Styles/Styles.xaml new file mode 100644 index 0000000..dc4a034 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Resources/Styles/Styles.xaml @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cycle.NET.ToggleDemo/ViewModels/ToggleDemoViewModel.cs b/Cycle.NET.ToggleDemo/ViewModels/ToggleDemoViewModel.cs new file mode 100644 index 0000000..6521471 --- /dev/null +++ b/Cycle.NET.ToggleDemo/ViewModels/ToggleDemoViewModel.cs @@ -0,0 +1,62 @@ +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using ReactiveUI; + +namespace Cycle.NET.ToggleDemo.ViewModels +{ + /// + /// A view model that contains a toggled checkbox. + /// + public class ToggleDemoViewModel : ViewModelBase + { + private ObservableAsPropertyHelper _status; + private bool _toggled; + + /// + /// Initializes a new instance of the class. + /// + /// The scheduler to use for main thread operations. + /// The scheduler to use for task pool operations. + /// The screen to use for routing operations. + public ToggleDemoViewModel( + IScheduler mainThreadScheduler = null, + IScheduler taskPoolScheduler = null, + IScreen hostScreen = null) + : base("Toggle a Checkbox", mainThreadScheduler, taskPoolScheduler, hostScreen) + { + Func, IObservable> domDriver = (IObservable sinks) => + { + sinks.ToProperty(this, x => x.Status, out this._status); + + return this.WhenAnyValue(x => x.Toggled); + }; + + Runner.Run( + CycleMain, + domDriver); + } + + private static IObservable CycleMain(IObservable domSources) + { + var domSink = + domSources + .Select(toggled => toggled ? "ON" : "off"); + + return domSink; + } + + /// + /// Gets or sets a value indicaying whether the toggle is toggled. + /// + public bool Toggled + { + get { return this._toggled; } + set { this.RaiseAndSetIfChanged(ref this._toggled, value); } + } + + /// + /// Gets status text. + /// + public string Status => this._status.Value; + } +} diff --git a/Cycle.NET.ToggleDemo/ViewModels/ViewModelBase.cs b/Cycle.NET.ToggleDemo/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..520d8b7 --- /dev/null +++ b/Cycle.NET.ToggleDemo/ViewModels/ViewModelBase.cs @@ -0,0 +1,54 @@ +using System.Reactive.Concurrency; +using ReactiveUI; +using Splat; + +namespace Cycle.NET.ToggleDemo.ViewModels +{ + /// + /// A base for all the different view models used throughout the application. + /// + public abstract class ViewModelBase : ReactiveObject, IRoutableViewModel, IActivatableViewModel + { + /// + /// Initializes a new instance of the class. + /// + /// The title of the view model for routing purposes. + /// The scheduler to use to schedule operations on the main thread. + /// The scheduler to use to schedule operations on the task pool. + /// The screen used for routing purposes. + protected ViewModelBase(string title, IScheduler mainThreadScheduler = null, IScheduler taskPoolScheduler = null, IScreen hostScreen = null) + { + this.UrlPathSegment = title; + this.HostScreen = hostScreen ?? Locator.Current.GetService(); + + // Set the schedulers like this so we can inject the test scheduler later on when doing VM unit tests + this.MainThreadScheduler = mainThreadScheduler ?? RxApp.MainThreadScheduler; + this.TaskPoolScheduler = taskPoolScheduler ?? RxApp.TaskpoolScheduler; + } + + /// + /// Gets the current page path. + /// + public string UrlPathSegment { get; } + + /// + /// Gets the screen used for routing operations. + /// + public IScreen HostScreen { get; } + + /// + /// Gets the activator which contains context information for use in activation of the view model. + /// + public ViewModelActivator Activator { get; } = new ViewModelActivator(); + + /// + /// Gets the scheduler for scheduling operations on the main thread. + /// + protected IScheduler MainThreadScheduler { get; } + + /// + /// Gets the scheduler for scheduling operations on the task pool. + /// + protected IScheduler TaskPoolScheduler { get; } + } +} diff --git a/Cycle.NET.ToggleDemo/Views/ContentPageBase.cs b/Cycle.NET.ToggleDemo/Views/ContentPageBase.cs new file mode 100644 index 0000000..d57dd08 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Views/ContentPageBase.cs @@ -0,0 +1,20 @@ +using Cycle.NET.ToggleDemo.ViewModels; +using ReactiveUI.Maui; + +namespace Cycle.NET.ToggleDemo.Views +{ + /// + /// A base page used for all our content pages. It is mainly used for interaction registrations. + /// + /// The view model which the page contains. + public class ContentPageBase : ReactiveContentPage + where TViewModel : ViewModelBase + { + /// + /// Initializes a new instance of the class. + /// + public ContentPageBase() + { + } + } +} diff --git a/Cycle.NET.ToggleDemo/Views/ToggleDemoView.xaml b/Cycle.NET.ToggleDemo/Views/ToggleDemoView.xaml new file mode 100644 index 0000000..c9ffc34 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Views/ToggleDemoView.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Views/ToggleDemoView.xaml.cs b/Cycle.NET.ToggleDemo/Views/ToggleDemoView.xaml.cs new file mode 100644 index 0000000..08d57aa --- /dev/null +++ b/Cycle.NET.ToggleDemo/Views/ToggleDemoView.xaml.cs @@ -0,0 +1,34 @@ +using System.Reactive.Disposables; +using System.Reactive.Linq; +using Cycle.NET.ToggleDemo.ViewModels; +using ReactiveUI; + +namespace Cycle.NET.ToggleDemo.Views +{ + /// + /// Contains a toggled checkbox. + /// + public partial class ToggleDemoView : ContentPageBase + { + /// + /// Initializes a new instance of the class. + /// + public ToggleDemoView() + { + this.InitializeComponent(); + + this.WhenActivated(disposables => + { + Observable.FromEventPattern( + handler => this.Toggled.CheckedChanged += handler, + handler => this.Toggled.CheckedChanged -= handler) + .Select(p => p.EventArgs) + .Select(e => e.Value) + .BindTo(this, x => x.ViewModel.Toggled) + .DisposeWith(disposables); + + this.OneWayBind(this.ViewModel, vm => vm.Status, v => v.Status.Text).DisposeWith(disposables); + }); + } + } +} diff --git a/Cycle.NET.UnionDemo/Cycle.NET.UnionDemo.csproj b/Cycle.NET.UnionDemo/Cycle.NET.UnionDemo.csproj new file mode 100644 index 0000000..fd1486f --- /dev/null +++ b/Cycle.NET.UnionDemo/Cycle.NET.UnionDemo.csproj @@ -0,0 +1,16 @@ + + + + Exe + net6.0 + + + + + + + + + + + diff --git a/Cycle.NET.UnionDemo/Program.cs b/Cycle.NET.UnionDemo/Program.cs new file mode 100644 index 0000000..f1d5360 --- /dev/null +++ b/Cycle.NET.UnionDemo/Program.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; + +namespace Cycle.NET.Demo +{ + internal static class Program + { +#if ORIG_DEMO + private static IObservable LogDriver(IObservable sinks) + { + sinks.Subscribe(i => Console.WriteLine("Log " + i.ToString())); + + return Observable.Empty(); + } + + private static IObservable KeyInputDriver(IObservable sinks) + { + return Observable.Range(20, 5); + } + + private static ( + IObservable LogSinks, + IObservable KeyInputSinks) + CycleMain( + IObservable logSources, + IObservable keyInputSources) + { + _ = logSources; + var logSink = keyInputSources; + + return ( + logSink, + Observable.Empty()); + } + static void Main(string[] args) + { + Runner.Run( + ( + IObservable logSources, + IObservable keyInputSources) => CycleMain( + logSources, + keyInputSources), + LogDriver, + KeyInputDriver); + Console.ReadLine(); + } +#else + private static IObservable DomDriver(IObservable sinks) + { + sinks.Subscribe(Console.WriteLine); + + return new List + { + false, + true, + false, + true, + } + .ToObservable(); + } + + private static + IObservable + CycleMain( + IObservable domSources) + { + var domSink = + domSources + .Select(toggled => toggled ? "ON" : "off"); + + return domSink; + } + static void Main() + { + Runner.Run( + ( + IObservable domSources) => CycleMain( + domSources), + DomDriver); + Console.ReadLine(); + } +#endif + } +} diff --git a/Cycle.NET.sln b/Cycle.NET.sln index 5cc5ff2..005cafc 100644 --- a/Cycle.NET.sln +++ b/Cycle.NET.sln @@ -1,11 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26621.2 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cycle.NET", "Cycle.NET\Cycle.NET.csproj", "{6A0D9074-CE78-4217-A7D6-07554439A5BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cycle.NET", "Cycle.NET\Cycle.NET.csproj", "{6A0D9074-CE78-4217-A7D6-07554439A5BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cycle.NET.Demo", "Cycle.NET.Demo\Cycle.NET.Demo.csproj", "{E0B049E9-C963-4627-872B-56F6BCE5728C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cycle.NET.Demo", "Cycle.NET.Demo\Cycle.NET.Demo.csproj", "{E0B049E9-C963-4627-872B-56F6BCE5728C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cycle.NET.UnionDemo", "Cycle.NET.UnionDemo\Cycle.NET.UnionDemo.csproj", "{65F17018-1196-4153-A2D4-9711768A030B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cycle.NET.ToggleDemo", "Cycle.NET.ToggleDemo\Cycle.NET.ToggleDemo.csproj", "{17CB7110-4CB9-48DA-B31D-7ED8E8EF0660}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +25,16 @@ Global {E0B049E9-C963-4627-872B-56F6BCE5728C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0B049E9-C963-4627-872B-56F6BCE5728C}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0B049E9-C963-4627-872B-56F6BCE5728C}.Release|Any CPU.Build.0 = Release|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Release|Any CPU.Build.0 = Release|Any CPU + {17CB7110-4CB9-48DA-B31D-7ED8E8EF0660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17CB7110-4CB9-48DA-B31D-7ED8E8EF0660}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17CB7110-4CB9-48DA-B31D-7ED8E8EF0660}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {17CB7110-4CB9-48DA-B31D-7ED8E8EF0660}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17CB7110-4CB9-48DA-B31D-7ED8E8EF0660}.Release|Any CPU.Build.0 = Release|Any CPU + {17CB7110-4CB9-48DA-B31D-7ED8E8EF0660}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Cycle.NET/Cycle.NET.csproj b/Cycle.NET/Cycle.NET.csproj index e7aa633..2021f00 100644 --- a/Cycle.NET/Cycle.NET.csproj +++ b/Cycle.NET/Cycle.NET.csproj @@ -1,11 +1,12 @@ - netstandard2.0 + net6.0 - + + diff --git a/Cycle.NET/Extensions/ObservableUnionExtensions.cs b/Cycle.NET/Extensions/ObservableUnionExtensions.cs new file mode 100644 index 0000000..44f4c84 --- /dev/null +++ b/Cycle.NET/Extensions/ObservableUnionExtensions.cs @@ -0,0 +1,661 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using SdgApps.Common.DotnetSealedUnions; + +namespace Cycle.NET.Extensions +{ + public static class ObservableUnionExtensions + { + public static + IDictionary> + Split( + this IObservable> source, + IEnumerable keys) => + keys + .Select(k => new KeyValuePair>(k, source + .Where(p => p.Key == k) + .Select(p => p.Value))) + .ToDictionary(p => p.Key, p => p.Value); + + public static + IObservable + Split< + TFirst>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return)); + + return firsts; + } + + public static ( + IObservable Nones, + IObservable Firsts) + Split< + TFirst>( + this IObservable> source) + { + var nones = source.SelectMany(s => s.Join( + mapNone: () => Observable.Return(Unit.Default), + mapFirst: _ => Observable.Empty())); + + var firsts = source.SelectMany(s => s.Join( + mapNone: Observable.Empty, + mapFirst: Observable.Return)); + + return ( + Nones: nones, + Firsts: firsts); + } + + public static ( + IObservable Firsts, + IObservable Seconds) + Split< + TFirst, + TSecond>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds) + Split< + TFirst, + TSecond, + TThird>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths) + Split< + TFirst, + TSecond, + TThird, + TFourth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths, + IObservable Sevenths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return, + mapSeventh: _ => Observable.Empty())); + + var sevenths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths, + Sevenths: sevenths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths, + IObservable Sevenths, + IObservable Eighths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return, + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var sevenths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: Observable.Return, + mapEighth: _ => Observable.Empty())); + + var eighths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths, + Sevenths: sevenths, + Eighths: eighths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths, + IObservable Sevenths, + IObservable Eighths, + IObservable Ninths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth, + TNinth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return, + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var sevenths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: Observable.Return, + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var eighths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: Observable.Return, + mapNinth: _ => Observable.Empty())); + + var ninths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths, + Sevenths: sevenths, + Eighths: eighths, + Ninths: ninths); + } + } +} diff --git a/Cycle.NET/Extensions/RunnerExtensions.cs b/Cycle.NET/Extensions/RunnerExtensions.cs new file mode 100644 index 0000000..385aabe --- /dev/null +++ b/Cycle.NET/Extensions/RunnerExtensions.cs @@ -0,0 +1,497 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using Cycle.NET.Extensions; +using SdgApps.Common.DotnetSealedUnions; + +namespace Cycle.NET +{ + public static class RunnerExtensions + { + public static IObservable> + CallDrivers( + this IObservable> sinks, + IDictionary, IObservable>> drivers) + { + var splitSinks = sinks.Split(drivers.Keys); + + var splitSources = splitSinks + .ToDictionary(p => p.Key, p => drivers[p.Key](p.Value)); + + return ObservableUnion.Merge( + splitSources); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource>( + this IObservable> sinks, + Func, IObservable> firstDriver) + { + var firstSink = sinks.Split(); + + var firstSource = firstDriver(firstSink); + + return ObservableUnion.Merge( + firstSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource>( + this IObservable> sinks, + Func, IObservable> noneDriver, + Func, IObservable> firstDriver) + { + var ( + noneSink, + firstSink) = sinks.Split(); + + var noneSource = noneDriver(noneSink); + var firstSource = firstDriver(firstSink); + + return ObservableUnion.Merge( + noneSource, + firstSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver) + { + var ( + firstSink, + secondSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + + return ObservableUnion.Merge( + firstSource, + secondSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver) + { + var ( + firstSink, + secondSink, + thirdSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource, + TSeventhSink, + TSeventhSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink, + seventhSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + var seventhSource = seventhDriver(seventhSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource, + seventhSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource, + TSeventhSink, + TSeventhSource, + TEighthSink, + TEighthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink, + seventhSink, + eighthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + var seventhSource = seventhDriver(seventhSink); + var eighthSource = eighthDriver(eighthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource, + seventhSource, + eighthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource, + TSeventhSink, + TSeventhSource, + TEighthSink, + TEighthSource, + TNinthSink, + TNinthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver, + Func, IObservable> ninthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink, + seventhSink, + eighthSink, + ninthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + var seventhSource = seventhDriver(seventhSink); + var eighthSource = eighthDriver(eighthSink); + var ninthSource = ninthDriver(ninthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource, + seventhSource, + eighthSource, + ninthSource); + } + } +} diff --git a/Cycle.NET/Kernel.cs b/Cycle.NET/Kernel.cs new file mode 100644 index 0000000..b836d3d --- /dev/null +++ b/Cycle.NET/Kernel.cs @@ -0,0 +1,26 @@ +using System; +using System.Reactive.Linq; +using System.Reactive.Subjects; + +namespace Cycle.NET +{ + public static class Kernel + { + public static void Run( + Func, IObservable> main, + Func, IObservable> drivers) + { + // Create fake sinks to use to call the drivers to get around the interdependency between + // main and the drivers. + var fakeSinks = new ReplaySubject(); + + // Call the drivers and collate the returned sources. + var sources = drivers(fakeSinks); + + var sinks = main(sources); + + // Update the sinks returned from main with the sinks used by the drivers. + sinks.Subscribe(fakeSinks.OnNext); + } + } +} diff --git a/Cycle.NET/ObservableUnion.cs b/Cycle.NET/ObservableUnion.cs new file mode 100644 index 0000000..a74198d --- /dev/null +++ b/Cycle.NET/ObservableUnion.cs @@ -0,0 +1,390 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using SdgApps.Common.DotnetSealedUnions; +using SdgApps.Common.DotnetSealedUnions.Generic; + +namespace Cycle.NET +{ + public static class ObservableUnion + { + public static IObservable> + Merge( + IDictionary> streams) => + Observable + .Merge(streams + .Select(p => p.Value + .Select(v => new KeyValuePair(p.Key, v)))); + + public static IObservable> + Merge< + TFirst>( + IObservable firsts) + { + var fac = GenericUnions.NulletFactory< + TFirst>(); + + var unionFirsts = firsts.Select(fac.First); + + return Observable.Merge( + unionFirsts); + } + + public static IObservable> + Merge< + TFirst>( + IObservable nones, + IObservable firsts) + { + var fac = GenericUnions.SingletFactory< + TFirst>(); + + var unionNones = nones.Select(_ => fac.None()); + var unionFirsts = firsts.Select(fac.First); + + return Observable.Merge( + unionNones, + unionFirsts); + } + + public static IObservable> + Merge< + TFirst, + TSecond>( + IObservable firsts, + IObservable seconds) + { + var fac = GenericUnions.DoubletFactory< + TFirst, + TSecond>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + + return Observable.Merge( + unionFirsts, + unionSeconds); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird>( + IObservable firsts, + IObservable seconds, + IObservable thirds) + { + var fac = GenericUnions.TripletFactory< + TFirst, + TSecond, + TThird>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths) + { + var fac = GenericUnions.QuartetFactory< + TFirst, + TSecond, + TThird, + TFourth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths) + { + var fac = GenericUnions.QuintetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths) + { + var fac = GenericUnions.SextetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths, + IObservable sevenths) + { + var fac = GenericUnions.SeptetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + var unionSevenths = sevenths.Select(fac.Seventh); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths, + unionSevenths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths, + IObservable sevenths, + IObservable eighths) + { + var fac = GenericUnions.OctetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + var unionSevenths = sevenths.Select(fac.Seventh); + var unionEighths = eighths.Select(fac.Eighth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths, + unionSevenths, + unionEighths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth, + TNinth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths, + IObservable sevenths, + IObservable eighths, + IObservable ninths) + { + var fac = GenericUnions.NonetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth, + TNinth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + var unionSevenths = sevenths.Select(fac.Seventh); + var unionEighths = eighths.Select(fac.Eighth); + var unionNinths = ninths.Select(fac.Ninth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths, + unionSevenths, + unionEighths, + unionNinths); + } + } +} diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index 07b668c..83ce79d 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -4,38 +4,700 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using System.Linq; +using Cycle.NET.Extensions; +using SdgApps.Common.DotnetSealedUnions; +using System.Runtime.Serialization; namespace Cycle.NET { + [Serializable] public class Streams : Dictionary> { public Streams() : base() { } + public Streams(Dictionary> value) : base(value) { } + + protected Streams( + SerializationInfo info, + StreamingContext context) : base(info, context) { } } + [Serializable] public class Drivers : Dictionary, IObservable>> { public Drivers() : base() { } + + protected Drivers( + SerializationInfo info, + StreamingContext context) : base(info, context) { } } public static class Runner { - public static void Run(Func main, Drivers drivers) - { - // Create fake sinks to use to call the drivers to get around the interdependency between - // main and the drivers. - var fakeSinks = drivers.ToDictionary(d => d.Key, d => new ReplaySubject()); - - // Call the drivers and collate the returned sources. - var sources = drivers.ToDictionary(d => d.Key, d => d.Value(fakeSinks[d.Key])); - - Streams sinks = main(new Streams(sources)); - - // Update the sinks returned from main with the sinks used by the drivers. - foreach (var sink in sinks) - { - sink.Value.Subscribe(s => fakeSinks[sink.Key].OnNext(s)); - } - } + public static void Run(Func main, Drivers drivers) => + Kernel.Run( + sources => + { + var sourceStreams = new Streams(sources + .Split(drivers.Keys) + .ToDictionary(p => p.Key, p => p.Value)); + + var sinkStreams = main(sourceStreams); + + return ObservableUnion + .Merge(sinkStreams); + }, + (IObservable> sinks) => sinks.CallDrivers(drivers)); + + public static void Run< + TSource1, + TSink1>( + Func< + IObservable, + IObservable> main, + Func, IObservable> firstDriver) => + Kernel.Run( + sources => + { + var firstSources = sources.Split(); + + var firstSinks = main( + firstSources); + + return ObservableUnion.Merge( + firstSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver)); + + public static void Run< + TSource1, + TSink1>( + Func< + IObservable, + IObservable, + ( + IObservable NoneSinks, + IObservable FirstSinks)> main, + Func, IObservable> noneDriver, + Func, IObservable> firstDriver) => + Kernel.Run( + sources => + { + var ( + noneSources, + firstSources) = sources.Split(); + + var ( + noneSinks, + firstSinks) = main( + noneSources, + firstSources); + + return ObservableUnion.Merge( + noneSinks, + firstSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + noneDriver, + firstDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2>( + Func< + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources) = sources.Split(); + + var ( + firstSinks, + secondSinks) = main( + firstSources, + secondSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3>( + Func< + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks) = main( + firstSources, + secondSources, + thirdSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6, + TSource7, + TSink7>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks, + IObservable SeventhSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver, + seventhDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6, + TSource7, + TSink7, + TSource8, + TSink8>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks, + IObservable SeventhSinks, + IObservable EighthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver, + seventhDriver, + eighthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6, + TSource7, + TSink7, + TSource8, + TSink8, + TSource9, + TSink9>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks, + IObservable SeventhSinks, + IObservable EighthSinks, + IObservable NinthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver, + Func, IObservable> ninthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources, + ninthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks, + ninthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources, + ninthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks, + ninthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver, + seventhDriver, + eighthDriver, + ninthDriver)); } }