From 2348632ecdb681910c3234882d9f3a435fbc5804 Mon Sep 17 00:00:00 2001 From: Shravan Rajinikanth Date: Wed, 27 May 2020 15:20:24 -0700 Subject: [PATCH] Improved VObject NRT for key indexer --- Gameloop.Vdf/Linq/VObject.cs | 17 ++-- Gameloop.Vdf/Linq/VValue.cs | 5 ++ Gameloop.Vdf/Utilities/NullableAttributes.cs | 88 ++++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 Gameloop.Vdf/Utilities/NullableAttributes.cs diff --git a/Gameloop.Vdf/Linq/VObject.cs b/Gameloop.Vdf/Linq/VObject.cs index 1aaa1dd..1c24432 100644 --- a/Gameloop.Vdf/Linq/VObject.cs +++ b/Gameloop.Vdf/Linq/VObject.cs @@ -1,6 +1,7 @@ using Gameloop.Vdf.Utilities; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq; using System.Linq.Expressions; @@ -53,11 +54,11 @@ public VToken this[int index] set => _children[index] = value; } - public VToken this[string key] + public VToken? this[string key] { get { - if (!TryGetValue(key, out VToken result)) + if (!TryGetValue(key, out VToken? result)) return null; return result; @@ -67,9 +68,9 @@ public VToken this[string key] { VProperty prop = Properties().FirstOrDefault(x => x.Key == key); if (prop != null) - prop.Value = value; + prop.Value = value ?? VValue.CreateEmpty(); else - Add(key, value); + Add(key, value ?? VValue.CreateEmpty()); } } @@ -162,7 +163,7 @@ public void RemoveAt(int index) _children.RemoveAt(index); } - public bool TryGetValue(string key, out VToken value) + public bool TryGetValue(string key, [MaybeNullWhen(false)] out VToken value) { value = Properties().FirstOrDefault(x => x.Key == key)?.Value; return (value != null); @@ -186,6 +187,12 @@ public IEnumerator> GetEnumerator() yield return new KeyValuePair(property.Key, property.Value); } + VToken IDictionary.this[string key] + { + get => this[key] ?? throw new KeyNotFoundException(); + set => this[key] = value ?? throw new ArgumentNullException(nameof(value)); + } + void ICollection>.Add(KeyValuePair item) { Add(new VProperty(item.Key, item.Value)); diff --git a/Gameloop.Vdf/Linq/VValue.cs b/Gameloop.Vdf/Linq/VValue.cs index 71b068e..0258227 100644 --- a/Gameloop.Vdf/Linq/VValue.cs +++ b/Gameloop.Vdf/Linq/VValue.cs @@ -44,6 +44,11 @@ public static VValue CreateComment(string value) return new VValue(value, VTokenType.Comment); } + public static VValue CreateEmpty() + { + return new VValue(String.Empty); + } + protected override bool DeepEquals(VToken token) { if (!(token is VValue otherVal)) diff --git a/Gameloop.Vdf/Utilities/NullableAttributes.cs b/Gameloop.Vdf/Utilities/NullableAttributes.cs new file mode 100644 index 0000000..3e76eb2 --- /dev/null +++ b/Gameloop.Vdf/Utilities/NullableAttributes.cs @@ -0,0 +1,88 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that an output will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)] + internal sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute + { } + + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute + { } + + /// + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + /// + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal class DoesNotReturnIfAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => this.ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } +}