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
Original file line number Diff line number Diff line change
Expand Up @@ -983,14 +983,20 @@ private MethodProvider BuildTryGetIndexMethodProvider()
},
MethodBodyStatement.EmptyLine, Declare("indexEnd", typeof(int),
ReadOnlySpanSnippets.Slice(indexable, Int(1)).Invoke("IndexOf", Literal(']').CastTo(typeof(byte))), out var indexEnd),
new IfStatement(indexEnd.LessThan(Int(0))) { Return(False) }, MethodBodyStatement.EmptyLine, Return(
Static(typeof(Utf8Parser)).Invoke(
new IfStatement(indexEnd.LessThan(Int(0))) { Return(False) }, MethodBodyStatement.EmptyLine,
new IfStatement(Not(Static(typeof(Utf8Parser)).Invoke(
"TryParse",
[
ReadOnlySpanSnippets.Slice(indexable, Int(0), indexEnd.As<int>().Add(Int(1))),
indexParameter.AsArgument(),
bytesConsumedParameter.AsArgument()
]))
])))
{
Return(False)
},
MethodBodyStatement.EmptyLine,
bytesConsumedParameter.AsVariable().AddAndAssign(Int(2)),
Return(True)
};

return new MethodProvider(signature, body, this, XmlDocProvider.Empty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,40 @@ public void JsonPatchSet_CollectionIndexIsBoundsChecked()
#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
}

[Test]
public void JsonPatch_CollectionPathPropagatesToChildPatch(
[Values("children[{0}]", "nestedChildren[{0}][{0}]")] string collectionPath,
[Values(0, 12)] int index,
[Values(false, true)] bool setThroughParent)
{
var children = Enumerable.Range(0, index + 1).Select(i => new AnotherDynamicModel($"child-{i}")).ToList();
var model = new NullableDynamicModel
{
Children = children,
NestedChildren = Enumerable.Range(0, index + 1).Select(_ => (IList<AnotherDynamicModel>)children.ToList()).ToList()
};
var child = children[index];
var path = Encoding.UTF8.GetBytes($"$.{string.Format(collectionPath, index)}.description");
string expectedValue = setThroughParent ? "updated" : "original";

#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
child.Patch.Set("$.description"u8, "original");
if (setThroughParent)
{
model.Patch.Set(path, expectedValue);
}

Assert.That(model.Patch.TryGetValue(path, out string? parentValue), Is.True);
Assert.That(parentValue, Is.EqualTo(expectedValue));
Assert.That(child.Patch.TryGetValue("$.description"u8, out string? childValue), Is.True);
Assert.That(childValue, Is.EqualTo(expectedValue));
if (index > 0)
{
Assert.That(children[0].Patch.TryGetValue("$.description"u8, out string? _), Is.False);
}
#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
}

[TestCase("modelValue", "J")]
[TestCase("modelValue", "W")]
[TestCase("children", "J")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static string GetFirstPropertyName(this global::System.ReadOnlySpan<byte>
#else
key = global::System.Text.Encoding.UTF8.GetString(local.Slice(0, bytesConsumed).ToArray());
#endif
(bytesConsumed += (jsonPath.Length - local.Length));
bytesConsumed += (jsonPath.Length - local.Length);
return key;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ public static bool TryGetIndex(this global::System.ReadOnlySpan<byte> indexSlice
return false;
}

return global::System.Buffers.Text.Utf8Parser.TryParse(indexSlice.Slice(0, (indexEnd + 1)), out index, out bytesConsumed);
if (!global::System.Buffers.Text.Utf8Parser.TryParse(indexSlice.Slice(0, (indexEnd + 1)), out index, out bytesConsumed))
{
return false;
}

bytesConsumed += 2;
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@

namespace Microsoft.TypeSpec.Generator.Expressions
{
public sealed record BinaryOperatorExpression(string Operator, ValueExpression Left, ValueExpression Right) : ValueExpression
public sealed record BinaryOperatorExpression(string Operator, ValueExpression Left, ValueExpression Right, bool AddParentheses = true) : ValueExpression
{
internal override void Write(CodeWriter writer)
{
writer.AppendRaw("(");
writer.AppendRawIf("(", AddParentheses);
Left.Write(writer);
writer.AppendRawIf(" ", !Left.IsEmptyExpression());
writer.AppendRaw(Operator);
writer.AppendRawIf(" ", !Right.IsEmptyExpression());
Right.Write(writer);
writer.AppendRaw(")");
writer.AppendRawIf(")", AddParentheses);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public InvokeMethodExpression Invoke(string methodName, IReadOnlyList<ValueExpre

public ValueExpression NullConditional() => new NullConditionalExpression(this);

public MethodBodyStatement AddAndAssign(ValueExpression value) => new BinaryOperatorExpression("+=", this, value).As<int>().Terminate();
public MethodBodyStatement AddAndAssign(ValueExpression value) => new BinaryOperatorExpression("+=", this, value, AddParentheses: false).As<int>().Terminate();

public AssignmentExpression Assign(ValueExpression value, bool nullCoalesce = false) => new AssignmentExpression(this, value, nullCoalesce);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,37 @@ namespace TestProjects.Local.Tests
{
public class ModelSerializationExtensionsTests
{
[TestCase("[0].description", 0, 3, ".description")]
[TestCase("[12].description", 12, 4, ".description")]
[TestCase("[0]", 0, 3, "")]
[TestCase("[12][3].description", 12, 4, "[3].description")]
[TestCase("[2147483647].description", int.MaxValue, 12, ".description")]
public void TryGetIndexConsumesCompleteIndexSegment(string path, int expectedIndex, int expectedBytesConsumed, string expectedRemainder)
{
ReadOnlySpan<byte> slice = Encoding.UTF8.GetBytes(path);

Assert.That(slice.TryGetIndex(out int index, out int bytesConsumed), Is.True);
Assert.That(index, Is.EqualTo(expectedIndex));
Assert.That(bytesConsumed, Is.EqualTo(expectedBytesConsumed));
Assert.That(Encoding.UTF8.GetString(slice.Slice(bytesConsumed)), Is.EqualTo(expectedRemainder));
}

[TestCase("")]
[TestCase("0]")]
[TestCase("[")]
[TestCase("[]")]
[TestCase("[-1]")]
[TestCase("[0")]
[TestCase("[abc]")]
[TestCase("[2147483648]")]
public void TryGetIndexRejectsInvalidIndex(string path)
{
ReadOnlySpan<byte> slice = Encoding.UTF8.GetBytes(path);

Assert.That(slice.TryGetIndex(out _, out int bytesConsumed), Is.False);
Assert.That(bytesConsumed, Is.Zero);
}

[TestCase(short.MinValue, "-32768")]
[TestCase(short.MaxValue, "32767")]
[TestCase(ushort.MinValue, "0")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,13 @@ public static bool TryGetIndex(this ReadOnlySpan<byte> indexSlice, out int index
return false;
}

return Utf8Parser.TryParse(indexSlice.Slice(0, indexEnd + 1), out index, out bytesConsumed);
if (!Utf8Parser.TryParse(indexSlice.Slice(0, indexEnd + 1), out index, out bytesConsumed))
{
return false;
}

bytesConsumed += 2;
Comment thread
jorgerangel-msft marked this conversation as resolved.
Resolved
return true;
}

public static ReadOnlySpan<byte> GetRemainder(this ReadOnlySpan<byte> jsonPath, int index)
Expand Down
Loading