Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 packages/http-client-csharp/emitter/src/type/input-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export type InputDateTimeType = InputUtcDateTimeType | InputOffsetDateTimeType;

interface InputDateTimeTypeBase extends InputTypeBase {
name: string;
encode: DateTimeKnownEncoding;
encode: DateTimeKnownEncoding | string;
wireType: InputPrimitiveType;
crossLanguageDefinitionId: string;
baseType?: InputDateTimeType;
Expand All @@ -124,7 +124,7 @@ export interface InputOffsetDateTimeType extends InputDateTimeTypeBase {
export interface InputDurationType extends InputTypeBase {
kind: "duration";
name: string;
encode: DurationKnownEncoding;
encode: DurationKnownEncoding | string;
wireType: InputPrimitiveType;
crossLanguageDefinitionId: string;
baseType?: InputDurationType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ $repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
$failingSpecs = @(
Join-Path 'http' 'type' 'model' 'flatten'
Join-Path 'http' 'type' 'model' 'templated'
Join-Path 'http' 'type' 'file'
Join-Path 'http' 'client' 'naming' # pending until https://github.com/microsoft/typespec/issues/5653 is resolved
Join-Path 'http' 'streaming' 'jsonl'
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1957,14 +1957,15 @@ private MethodBodyStatement CreateEncodedArraySerializationStatement(
{
stringJoinExpression = StringSnippets.Join(Literal(delimiter), propertyExpression);
}
else if (elementType.IsEnum && !elementType.IsStruct && elementType.UnderlyingEnumType?.Equals(typeof(string)) == true)
else if (elementType.IsEnum && elementType.UnderlyingEnumType?.Equals(typeof(string)) == true)
{
var x = new VariableExpression(typeof(object), "x");
var x = new VariableExpression(elementType, "x");
var body = elementType.ToSerial(x);
var selectExpression = propertyExpression.Invoke(nameof(Enumerable.Select),
new FuncExpression([x.Declaration], new TernaryConditionalExpression(
x.Equal(Null),
Literal(""),
elementType.ToSerial(x))));
[new FuncExpression([x.Declaration], body)],
[],
false,
extensionType: typeof(Enumerable));
stringJoinExpression = StringSnippets.Join(Literal(delimiter), selectExpression);
}
else
Expand Down Expand Up @@ -2028,27 +2029,32 @@ private MethodBodyStatement[] CreateEncodedArrayDeserializationStatements(
createArrayStatement = variableExpression.Assign(conditionalExpression).Terminate();
}
}
else if (elementType.IsEnum && !elementType.IsStruct && elementType.UnderlyingEnumType?.Equals(typeof(string)) == true)
else if (elementType.IsEnum && elementType.UnderlyingEnumType?.Equals(typeof(string)) == true)
{
var splitExpression = new TernaryConditionalExpression(
isNullOrEmptyCheck,
New.Array(typeof(string)),
stringValueVar.As<string>().Split(delimiterChar));

var s = new VariableExpression(typeof(string), "s");
var trimmedS = s.Invoke(nameof(string.Trim));
var parseExpression = elementType.ToEnum(trimmedS);

var parseExpression = Static(elementType).Invoke("Parse", trimmedS);

var selectExpression = splitExpression.Invoke(nameof(Enumerable.Select),
new FuncExpression([s.Declaration], parseExpression));
var splitAndParse = stringValueVar.As<string>().Split(delimiterChar)
.Invoke(nameof(Enumerable.Select), new FuncExpression([s.Declaration], parseExpression));

var finalExpression = propertyType.IsArray
? selectExpression.Invoke(nameof(Enumerable.ToArray))
: propertyType.IsList
? New.Instance(typeof(List<>).MakeGenericType(elementType.FrameworkType), selectExpression)
: New.Instance(propertyType.PropertyInitializationType, selectExpression);
createArrayStatement = variableExpression.Assign(finalExpression).Terminate();
if (propertyType.IsArray)
{
var conditionalExpression = new TernaryConditionalExpression(
isNullOrEmptyCheck,
New.Array(elementType),
splitAndParse.Invoke(nameof(Enumerable.ToArray)));
createArrayStatement = variableExpression.Assign(conditionalExpression).Terminate();
}
else
{
var initType = propertyType.PropertyInitializationType;
var conditionalExpression = new TernaryConditionalExpression(
isNullOrEmptyCheck,
New.Instance(initType),
New.Instance(initType, splitAndParse.Invoke(nameof(Enumerable.ToList)).CastTo(new CSharpType(typeof(IList<>), elementType))));
createArrayStatement = variableExpression.Assign(conditionalExpression).Terminate();
}
}
else
{
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,70 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.ComponentModel;

namespace Microsoft.TypeSpec.Generator.Input
{
public enum DateTimeKnownEncoding
/// <summary>
/// Represents a DateTime encoding format.
/// </summary>
public readonly struct DateTimeKnownEncoding : IEquatable<DateTimeKnownEncoding>
{
Rfc3339, Rfc7231, UnixTimestamp
private readonly string _value;

/// <summary>
/// Initializes a new instance of <see cref="DateTimeKnownEncoding"/>.
/// </summary>
/// <param name="value">The string value of the encoding.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
public DateTimeKnownEncoding(string value)
{
_value = value ?? throw new ArgumentNullException(nameof(value));
}

/// <summary>
/// RFC 3339 date-time format (ISO 8601).
/// </summary>
public static DateTimeKnownEncoding Rfc3339 { get; } = new DateTimeKnownEncoding("Rfc3339");

/// <summary>
/// RFC 7231 HTTP date format.
/// </summary>
public static DateTimeKnownEncoding Rfc7231 { get; } = new DateTimeKnownEncoding("Rfc7231");

/// <summary>
/// Unix timestamp (seconds since epoch).
/// </summary>
public static DateTimeKnownEncoding UnixTimestamp { get; } = new DateTimeKnownEncoding("UnixTimestamp");

/// <summary>
/// Determines if two <see cref="DateTimeKnownEncoding"/> values are the same.
/// </summary>
public static bool operator ==(DateTimeKnownEncoding left, DateTimeKnownEncoding right) => left.Equals(right);

/// <summary>
/// Determines if two <see cref="DateTimeKnownEncoding"/> values are not the same.
/// </summary>
public static bool operator !=(DateTimeKnownEncoding left, DateTimeKnownEncoding right) => !left.Equals(right);

/// <summary>
/// Converts a string to a <see cref="DateTimeKnownEncoding"/>.
/// </summary>
public static implicit operator DateTimeKnownEncoding(string value) => new DateTimeKnownEncoding(value);

/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => obj is DateTimeKnownEncoding other && Equals(other);

/// <inheritdoc/>
public bool Equals(DateTimeKnownEncoding other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase);

/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0;

/// <inheritdoc/>
public override string ToString() => _value;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,75 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.ComponentModel;

namespace Microsoft.TypeSpec.Generator.Input
{
public enum DurationKnownEncoding
/// <summary>
/// Represents a Duration encoding format.
/// </summary>
public readonly struct DurationKnownEncoding : IEquatable<DurationKnownEncoding>
{
Iso8601, Seconds, Constant, Milliseconds
private readonly string _value;

/// <summary>
/// Initializes a new instance of <see cref="DurationKnownEncoding"/>.
/// </summary>
/// <param name="value">The string value of the encoding.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
public DurationKnownEncoding(string value)
{
_value = value ?? throw new ArgumentNullException(nameof(value));
}

/// <summary>
/// ISO 8601 duration format.
/// </summary>
public static DurationKnownEncoding Iso8601 { get; } = new DurationKnownEncoding("Iso8601");

/// <summary>
/// Duration as seconds.
/// </summary>
public static DurationKnownEncoding Seconds { get; } = new DurationKnownEncoding("Seconds");

/// <summary>
/// Constant duration value.
/// </summary>
public static DurationKnownEncoding Constant { get; } = new DurationKnownEncoding("Constant");

/// <summary>
/// Duration as milliseconds.
/// </summary>
public static DurationKnownEncoding Milliseconds { get; } = new DurationKnownEncoding("Milliseconds");

/// <summary>
/// Determines if two <see cref="DurationKnownEncoding"/> values are the same.
/// </summary>
public static bool operator ==(DurationKnownEncoding left, DurationKnownEncoding right) => left.Equals(right);

/// <summary>
/// Determines if two <see cref="DurationKnownEncoding"/> values are not the same.
/// </summary>
public static bool operator !=(DurationKnownEncoding left, DurationKnownEncoding right) => !left.Equals(right);

/// <summary>
/// Converts a string to a <see cref="DurationKnownEncoding"/>.
/// </summary>
public static implicit operator DurationKnownEncoding(string value) => new DurationKnownEncoding(value);

/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => obj is DurationKnownEncoding other && Equals(other);

/// <inheritdoc/>
public bool Equals(DurationKnownEncoding other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase);

/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => _value != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0;

/// <inheritdoc/>
public override string ToString() => _value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ public static InputDateTimeType CreateDateTimeType(ref Utf8JsonReader reader, st
encode = encode ?? throw new JsonException("DateTime type must have encoding");
wireType = wireType ?? throw new JsonException("DateTime type must have wireType");

var dateTimeType = Enum.TryParse<DateTimeKnownEncoding>(encode, ignoreCase: true, out var encodeKind)
? new InputDateTimeType(encodeKind, name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [], External = external }
: throw new JsonException($"Encoding of DateTime type {encode} is unknown.");
var dateTimeType = new InputDateTimeType(new DateTimeKnownEncoding(encode), name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [], External = external };

if (id != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ public static InputDurationType CreateDurationType(ref Utf8JsonReader reader, st
encode = encode ?? throw new JsonException("Duration type must have encoding");
wireType = wireType ?? throw new JsonException("Duration type must have wireType");

var dateTimeType = DurationKnownEncodingExtensions.TryParse(encode, out var encodeKind)
? new InputDurationType(encodeKind.Value, name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [], External = external }
: throw new JsonException($"Encoding of Duration type {encode} is unknown.");
var dateTimeType = new InputDurationType(new DurationKnownEncoding(encode), name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [], External = external };

if (id != null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.TypeSpec.Generator.Input.Extensions;
using NUnit.Framework;

namespace Microsoft.TypeSpec.Generator.Input.Tests
{
public class DurationKnownEncodingTests
{
[TestCase("duration-constant", DurationKnownEncoding.Constant)]
[TestCase("iso8601", DurationKnownEncoding.Iso8601)]
[TestCase("seconds", DurationKnownEncoding.Seconds)]
[TestCase("milliseconds", DurationKnownEncoding.Milliseconds)]
public void TryParseEachEncoding(string encoding, DurationKnownEncoding expectedEncoding)
[TestCase("duration-constant")]
[TestCase("Constant")]
[TestCase("iso8601")]
[TestCase("Iso8601")]
[TestCase("seconds")]
[TestCase("Seconds")]
[TestCase("milliseconds")]
[TestCase("Milliseconds")]
public void CanCreateFromString(string encoding)
{
var result = DurationKnownEncodingExtensions.TryParse(encoding, out var parsedEncoding);
Assert.IsTrue(result);
Assert.AreEqual(expectedEncoding, parsedEncoding);
var parsedEncoding = new DurationKnownEncoding(encoding);
Assert.IsNotNull(parsedEncoding);
Assert.AreEqual(encoding, parsedEncoding.ToString());
}

[Test]
public void TryParseInvalidEncoding()
public void KnownEncodingsAreEqual()
{
var result = DurationKnownEncodingExtensions.TryParse("invalid-encoding", out var parsedEncoding);
Assert.IsFalse(result);
Assert.IsNull(parsedEncoding);
Assert.AreEqual(DurationKnownEncoding.Iso8601, new DurationKnownEncoding("Iso8601"));
Assert.AreEqual(DurationKnownEncoding.Seconds, new DurationKnownEncoding("Seconds"));
Assert.AreEqual(DurationKnownEncoding.Constant, new DurationKnownEncoding("Constant"));
Assert.AreEqual(DurationKnownEncoding.Milliseconds, new DurationKnownEncoding("Milliseconds"));
}

[Test]
public void CustomEncodingWorks()
{
var customEncoding = new DurationKnownEncoding("custom-format");
Assert.AreEqual("custom-format", customEncoding.ToString());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public void LoadsInputDurationType()

var inputDuration = inputType as InputDurationType;
Assert.IsNotNull(inputDuration);
Assert.AreEqual(DurationKnownEncoding.Constant, inputDuration!.Encode);
Assert.AreEqual("duration-constant", inputDuration!.Encode.ToString());
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,29 +352,29 @@ protected internal TypeFactory()
InputNullableType nullableType => GetSerializationFormat(nullableType.Type),
InputDateTimeType dateTimeType => dateTimeType.Encode switch
{
DateTimeKnownEncoding.Rfc3339 => SerializationFormat.DateTime_RFC3339,
DateTimeKnownEncoding.Rfc7231 => SerializationFormat.DateTime_RFC7231,
DateTimeKnownEncoding.UnixTimestamp => SerializationFormat.DateTime_Unix,
_ => throw new IndexOutOfRangeException($"unknown encode {dateTimeType.Encode}"),
var e when e == DateTimeKnownEncoding.Rfc3339 => SerializationFormat.DateTime_RFC3339,
var e when e == DateTimeKnownEncoding.Rfc7231 => SerializationFormat.DateTime_RFC7231,
var e when e == DateTimeKnownEncoding.UnixTimestamp => SerializationFormat.DateTime_Unix,
_ => SerializationFormat.Default, // Custom encoding formats use default serialization
},
InputDurationType durationType => durationType.Encode switch
{
// there is no such thing as `DurationConstant`
DurationKnownEncoding.Iso8601 => SerializationFormat.Duration_ISO8601,
DurationKnownEncoding.Seconds => durationType.WireType.Kind switch
var e when e == DurationKnownEncoding.Iso8601 => SerializationFormat.Duration_ISO8601,
var e when e == DurationKnownEncoding.Seconds => durationType.WireType.Kind switch
{
InputPrimitiveTypeKind.Int32 => SerializationFormat.Duration_Seconds,
InputPrimitiveTypeKind.Float or InputPrimitiveTypeKind.Float32 => SerializationFormat.Duration_Seconds_Float,
_ => SerializationFormat.Duration_Seconds_Double
},
DurationKnownEncoding.Milliseconds => durationType.WireType.Kind switch
var e when e == DurationKnownEncoding.Milliseconds => durationType.WireType.Kind switch
{
InputPrimitiveTypeKind.Int32 => SerializationFormat.Duration_Milliseconds,
InputPrimitiveTypeKind.Float or InputPrimitiveTypeKind.Float32 => SerializationFormat.Duration_Milliseconds_Float,
_ => SerializationFormat.Duration_Milliseconds_Double
},
DurationKnownEncoding.Constant => SerializationFormat.Duration_Constant,
_ => throw new IndexOutOfRangeException($"unknown encode {durationType.Encode}")
var e when e == DurationKnownEncoding.Constant => SerializationFormat.Duration_Constant,
_ => SerializationFormat.Default // Custom encoding formats use default serialization
},
InputPrimitiveType primitiveType => primitiveType.Kind switch
{
Expand Down
Loading