Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 2 additions & 2 deletions crates/bindings-csharp/Codegen.Tests/fixtures/diag/Lib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -569,14 +569,14 @@ public static Player ViewDefWrongReturn(ViewContext ctx)
return new Player { Identity = new() };
}

// Invalid: IEnumerable<T> return type (from Iter()) is not List<T> or T?
// Valid: IEnumerable<T> return type (from Iter()) is supported
[SpacetimeDB.View(Accessor = "view_def_ienumerable_return_from_iter", Public = true)]
public static IEnumerable<Player> ViewDefIEnumerableReturnFromIter(ViewContext ctx)
{
return ctx.Db.Player.Iter();
}

// Invalid: IEnumerable<T> return type (from Filter()) is not List<T> or T?
// Valid: IEnumerable<T> return type (from Filter()) is supported
[SpacetimeDB.View(Accessor = "view_def_ienumerable_return_from_filter", Public = true)]
public static IEnumerable<TestScheduleIssues> ViewDefIEnumerableReturnFromFilter(
ViewContext ctx
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -335,12 +335,12 @@ public partial struct TestScheduleIssues
^^^^^^
{
*/
Message: View 'ViewDefWrongReturn' must return T?, List<T>, or IQuery<T>,
Message: View 'ViewDefWrongReturn' must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
Severity: Error,
Descriptor: {
Id: STDB0024,
Title: Views must return T?, List<T>, or IQuery<T>,
MessageFormat: View '{0}' must return T?, List<T>, or IQuery<T>,
Title: Views must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
MessageFormat: View '{0}' must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
Category: SpacetimeDB,
DefaultSeverity: Error,
IsEnabledByDefault: true
Expand Down Expand Up @@ -369,12 +369,12 @@ public partial struct TestScheduleIssues
^^^^^^^^^^^^^^^^^^^
{
*/
Message: View 'ViewDefIEnumerableReturnFromIter' must return T?, List<T>, or IQuery<T>,
Message: View 'ViewDefIEnumerableReturnFromIter' must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
Severity: Error,
Descriptor: {
Id: STDB0024,
Title: Views must return T?, List<T>, or IQuery<T>,
MessageFormat: View '{0}' must return T?, List<T>, or IQuery<T>,
Title: Views must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
MessageFormat: View '{0}' must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
Category: SpacetimeDB,
DefaultSeverity: Error,
IsEnabledByDefault: true
Expand Down Expand Up @@ -403,12 +403,12 @@ public partial struct TestScheduleIssues
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ViewContext ctx
*/
Message: View 'ViewDefIEnumerableReturnFromFilter' must return T?, List<T>, or IQuery<T>,
Message: View 'ViewDefIEnumerableReturnFromFilter' must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
Severity: Error,
Descriptor: {
Id: STDB0024,
Title: Views must return T?, List<T>, or IQuery<T>,
MessageFormat: View '{0}' must return T?, List<T>, or IQuery<T>,
Title: Views must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
MessageFormat: View '{0}' must return T?, List<T>, IQuery<T>, or IEnumerable<T>,
Category: SpacetimeDB,
DefaultSeverity: Error,
IsEnabledByDefault: true
Expand Down
5 changes: 3 additions & 2 deletions crates/bindings-csharp/Codegen/Diag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,9 @@ string typeName
public static readonly ErrorDescriptor<MethodDeclarationSyntax> ViewInvalidReturn =
new(
group,
"Views must return T?, List<T>, or IQuery<T>",
method => $"View '{method.Identifier}' must return T?, List<T>, or IQuery<T>",
"Views must return T?, List<T>, IQuery<T>, or IEnumerable<T>",
method =>
$"View '{method.Identifier}' must return T?, List<T>, IQuery<T>, or IEnumerable<T>.",
method => method.ReturnType
);

Expand Down
35 changes: 34 additions & 1 deletion crates/bindings-csharp/Codegen/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,7 @@ record ViewDeclaration
public readonly bool IsAnonymous;
public readonly bool IsPublic;
public readonly bool ReturnsQuery;
public readonly bool ReturnsEnumerable;
public readonly TypeUse ReturnType;
public readonly EquatableArray<MemberDeclaration> Parameters;
public readonly Scope Scope;
Expand Down Expand Up @@ -1155,6 +1156,7 @@ public ViewDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter dia
IsAnonymous = isAnonymousContext;

ReturnsQuery = false;
ReturnsEnumerable = false;
INamedTypeSymbol? iquery = null;
if (
method.ReturnType is INamedTypeSymbol
Expand Down Expand Up @@ -1192,6 +1194,24 @@ method.ReturnType is INamedTypeSymbol
// Match Rust semantics: Query<T> is described as a nullable row (T?).
ReturnType = new ReferenceUse(opt, opt);
}
else if (
method.ReturnType
is INamedTypeSymbol
{
OriginalDefinition: var originalDefinition,
TypeArguments: [var enumerableElementType]
}
&& originalDefinition.ToString() == "System.Collections.Generic.IEnumerable<T>"
)
{
ReturnsEnumerable = true;
var elementType = TypeUse.Parse(method, enumerableElementType, diag);
var elementTypeName = SymbolToName(enumerableElementType);
var listTypeName = $"System.Collections.Generic.List<{elementTypeName}>";
var listTypeInfo =
$"SpacetimeDB.BSATN.List<{elementTypeName}, {elementType.BSATNName}>";
ReturnType = new ListUse(listTypeName, listTypeInfo, elementType);
}
else
{
ReturnType = TypeUse.Parse(method, method.ReturnType, diag);
Expand All @@ -1210,11 +1230,12 @@ method.ReturnType is INamedTypeSymbol
diag.Report(ErrorDescriptor.ViewContextParam, methodSyntax);
}

// Validate return type: must be List<T> or T?
// Validate return type: must be List<T>, T?, or IEnumerable<T>
if (
!ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.ValueOption")
&& !ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.RefOption")
&& !ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.List")
Comment thread
joshua-spacetime marked this conversation as resolved.
Outdated
&& !ReturnsEnumerable
)
{
diag.Report(ErrorDescriptor.ViewInvalidReturn, methodSyntax);
Expand Down Expand Up @@ -1292,6 +1313,18 @@ public string GenerateDispatcherClass(uint index)
listSerializer.Write(writer, listValue);
return output.ToArray();
"""
: ReturnsEnumerable
? $$$"""
var listSerializer = new {{{ReturnType.BSATNName}}}();
var listValue = global::System.Linq.Enumerable.ToList(returnValue);
var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);
var headerRW = new global::SpacetimeDB.Internal.ViewResultHeader.BSATN();
using var output = new System.IO.MemoryStream();
using var writer = new System.IO.BinaryWriter(output);
headerRW.Write(writer, header);
listSerializer.Write(writer, listValue);
return output.ToArray();
"""
: $$$"""
{{{ReturnType.BSATNName}}} returnRW = new();
var header = new global::SpacetimeDB.Internal.ViewResultHeader.RowData(default);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,9 +631,9 @@ public static Player? MyPlayer(ViewContext ctx)

// Return potentially multiple rows
[SpacetimeDB.View(Accessor = "TopPlayers", Public = true)]
public static List<Player> TopPlayers(ViewContext ctx)
public static IEnumerable<Player> TopPlayers(ViewContext ctx)
{
return ctx.Db.Player.Score.Filter(1000).ToList();
return ctx.Db.Player.Score.Filter(1000);
}

// Perform a generic filter using the query builder.
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/00200-core-concepts/00200-functions/00500-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ public static partial class Module
return ctx.Db.Player.Identity.Find(ctx.Sender) as Player?;
}

// Multiple rows: return a list
// Multiple rows: return a list or enumerable
[SpacetimeDB.View(Accessor = "PlayersForLevel", Public = true)]
public static List<PlayerAndLevel> PlayersForLevel(AnonymousViewContext ctx)
public static IEnumerable<PlayerAndLevel> PlayersForLevel(AnonymousViewContext ctx)
{
var rows = new List<PlayerAndLevel>();
foreach (var player in ctx.Db.PlayerLevel.Level.Filter(1))
Expand All @@ -159,7 +159,7 @@ public static partial class Module
}
```

Views must be static methods and can return either a single row (`T?`) or a list of rows (`List<T>` or `T[]`) where `T` can be a table type or any product type.
Views must be static methods and can return either a single row (`T?`) or multiple rows (`IEnumerable<T>`, `List<T>`, or `T[]`) where `T` can be a table type or any product type.

</TabItem>
<TabItem value="rust" label="Rust">
Expand Down
Loading