Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
34 changes: 30 additions & 4 deletions src/IceRpc.Slice.Generator/OperationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,38 @@ internal CodeBlock BuildEncodeStreamMethod(
var items = op.ReturnType
.Select(r => (r.Name, Overview: DocCommentFormatter.FormatOverview(r.Comment, currentNamespace)))
.Where(item => item.Overview is not null)
.Select(item => $"<item><term>{item.Name}</term><description>{item.Overview}</description></item>")
.Select(item => (item.Name, item.Overview!))
.ToList();

return items.Count > 0
? $"A tuple containing:\n<list type=\"bullet\">\n{string.Join("\n", items)}\n</list>"
: null;
return items.Count > 0 ? TupleReturnsDocComment(items) : null;
}

/// <summary>Returns the <c>&lt;returns&gt;</c> doc comment for a service operation with
/// <c>cs::encodedReturn</c>: a fixed description of the encoded return payload, or for an operation with a
/// streamed return, a list with the payload and the streamed return.</summary>
internal string GetEncodedReturnsDocComment(string currentNamespace)
{
const string payloadDescription = "The encoded return value.";

if (op.StreamedReturn is Field streamReturn)
{
string streamDescription =
DocCommentFormatter.FormatOverview(streamReturn.Comment, currentNamespace) ??
"The streamed return value.";
return TupleReturnsDocComment(
[(op.EncodedReturnPayloadName, payloadDescription), (streamReturn.Name, streamDescription)]);
}
else
{
return payloadDescription;
}
}

private static string TupleReturnsDocComment(IEnumerable<(string Name, string Description)> items)
{
IEnumerable<string> listItems = items.Select(
item => $"<item><term>{item.Name}</term><description>{item.Description}</description></item>");
return $"A tuple containing:\n<list type=\"bullet\">\n{string.Join("\n", listItems)}\n</list>";
}

/// <summary>Returns the C# return type for an operation (<c>Task</c>, <c>Task&lt;T&gt;</c>, or
Expand Down
7 changes: 5 additions & 2 deletions src/IceRpc.Slice.Generator/ServiceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,11 @@ private static CodeBlock BuildServiceOperationDeclaration(Operation op, string c
{
operationBuilder.AddComment("returns", "A value task that completes when this implementation completes.");
}
else if (!op.Attributes.HasAttribute(CSAttributes.CSEncodedReturn) &&
op.GetReturnsDocComment(currentNamespace) is string returns)
else if (op.Attributes.HasAttribute(CSAttributes.CSEncodedReturn))
{
operationBuilder.AddComment("returns", op.GetEncodedReturnsDocComment(currentNamespace));
}
else if (op.GetReturnsDocComment(currentNamespace) is string returns)
{
operationBuilder.AddComment("returns", returns);
}
Expand Down
27 changes: 27 additions & 0 deletions tests/IceRpc.Slice.Generator.Tests/DocumentationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,33 @@ public void Tuple_return_gets_returns_doc_comment_with_one_item_per_field(string
Assert.That(items["LastSeen"], Is.EqualTo("The time at what the user was last seen."));
}

[TestCase("IMySessionManager", "The log entries.")]
[TestCase("IMySessionManagerService", "The encoded return value.")]
public void Encoded_return_gets_returns_doc_comment(string interfaceName, string expected)
{
// Arrange / Act
XElement member = GetMember($"{MemberPrefix}{interfaceName}.GetLogAsync(System.String,{TrailingParams}");

// Assert
Assert.That(member.Element("returns")?.Value, Is.EqualTo(expected));
}

[Test]
public void Encoded_return_with_stream_gets_returns_doc_comment_with_payload_and_stream_items()
{
// Arrange / Act
XElement member = GetMember(
$"{MemberPrefix}IMySessionManagerService.StreamLogAsync(System.String,{TrailingParams}");
var items = member.Element("returns")!.Element("list")!.Elements("item")
.Select(item => (item.Element("term")!.Value, item.Element("description")!.Value));

// Assert
Assert.That(member.Element("returns")!.Value.Trim(), Does.StartWith("A tuple containing:"));
Assert.That(
items,
Is.EqualTo(new[] { ("Payload", "The encoded return value."), ("Entries", "The log entries.") }));
}

private static XElement GetMember(string name) =>
XDocument.Load(Path.ChangeExtension(typeof(DocumentationTests).Assembly.Location, ".xml"))
.Descendants("member")
Expand Down
11 changes: 11 additions & 0 deletions tests/IceRpc.Slice.Generator.Tests/DocumentationTests.slice
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ interface MySessionManager {

/// Retrieves the {@link UserList} containing the names of the active users.
getActiveUsers() -> UserList

/// Retrieves the log of the given user.
/// @param user: The user.
/// @returns: The log entries.
[cs::encodedReturn] getLog(user: string) -> Sequence<string>

/// Streams the log of the given user.
/// @param user: The user.
/// @returns count: The number of entries.
/// @returns entries: The log entries.
[cs::encodedReturn] streamLog(user: string) -> (count: int32, entries: stream string)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added XML documentation tests for both operations in 678b20f.

}

/// This summary comment includes special XML characters like < > & ' ", which should be properly escaped by
Expand Down