Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow building workflows as typed sequences #1942

Merged
merged 5 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Bonsai.Core.Tests/ExpressionBuilderGraphTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,36 @@ public void Build_BranchPropertyMapping_AvoidMulticastExpression()
Assert.IsFalse(visitor.HasPublishBranch);
}

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void BuildObservable_InvalidWorkflowType_ThrowsArgumentException()
{
new TestWorkflow()
.AppendValue(0)
.AppendOutput()
.BuildObservable<Unit>();
}

[TestMethod]
public void BuildObservable_CovariantWorkflowType_IsCompatibleAssignment()
{
var workflow = new TestWorkflow()
.AppendValue("")
.AppendOutput()
.BuildObservable<object>();
Assert.IsNotNull(workflow);
}

[TestMethod]
public void BuildObservable_ConvertibleWorkflowType_IsCompatibleAssignment()
{
var workflow = new TestWorkflow()
.AppendValue(1)
.AppendOutput()
.BuildObservable<double>();
Assert.IsNotNull(workflow);
}

class MergeBranchVisitor : ExpressionVisitor
{
public int BranchCount { get; private set; }
Expand Down
4 changes: 1 addition & 3 deletions Bonsai.Core.Tests/TestWorkflow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,7 @@ public ExpressionBuilderGraph ToInspectableGraph()

public IObservable<T> BuildObservable<T>()
{
var expression = Workflow.Build();
var observableFactory = Expression.Lambda<Func<IObservable<T>>>(expression).Compile();
return observableFactory();
return Workflow.BuildObservable<T>();
}
}
}
3 changes: 2 additions & 1 deletion Bonsai.Core/Expressions/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,8 @@ internal static Expression CoerceMethodArgument(Type parameterType, Expression a
{
if (argument.Type.IsGenericType && parameterType.IsGenericType &&
argument.Type.GetGenericTypeDefinition() == typeof(IObservable<>) &&
parameterType.GetGenericTypeDefinition() == typeof(IObservable<>))
parameterType.GetGenericTypeDefinition() == typeof(IObservable<>) &&
!parameterType.IsAssignableFrom(argument.Type))
{
var argumentObservableType = argument.Type.GetGenericArguments()[0];
var parameterObservableType = parameterType.GetGenericArguments()[0];
Expand Down
25 changes: 25 additions & 0 deletions Bonsai.Core/Expressions/ExpressionBuilderGraphExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,31 @@ public static IObservable<Unit> BuildObservable(this ExpressionBuilderGraph sour
return observableFactory();
}

/// <summary>
/// Builds and compiles an expression builder workflow into an observable sequence
/// with the specified element type.
/// </summary>
/// <typeparam name="TResult">The type of the elements in the observable sequence.</typeparam>
/// <param name="source">The expression builder workflow to compile.</param>
/// <returns>
/// An observable sequence with the specified element type.
/// </returns>
/// <exception cref="ArgumentException">
/// The specified expression builder workflow does not compile into an observable sequence
/// with the expected element type.
/// </exception>
public static IObservable<TResult> BuildObservable<TResult>(this ExpressionBuilderGraph source)
{
var workflow = source.Build();
if (!typeof(IObservable<TResult>).IsAssignableFrom(workflow.Type))
{
workflow = ExpressionBuilder.CoerceMethodArgument(typeof(IObservable<TResult>), workflow);
}

var observableFactory = Expression.Lambda<Func<IObservable<TResult>>>(workflow).Compile();
return observableFactory();
}

static WorkflowExpressionBuilder Clone(this WorkflowExpressionBuilder builder, ExpressionBuilderGraph workflow)
{
var workflowExpression = (WorkflowExpressionBuilder)Activator.CreateInstance(builder.GetType(), workflow);
Expand Down