Skip to content

Commit

Permalink
Refactor strucutre and improve coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
NeonGraal committed Feb 19, 2025
1 parent 1824b16 commit 51a1d1d
Show file tree
Hide file tree
Showing 39 changed files with 374 additions and 449 deletions.
6 changes: 4 additions & 2 deletions coverage.runsettings
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
<CodeCoverage>
<ModulePaths>
<Include>
<ModulePath>.*\\GqlPlus\.Abstractions\.dll$</ModulePath>
<ModulePath>.*\\GqlPlus\.Modeller\.dll$</ModulePath>
<ModulePath>.*\\GqlPlus\.Parser\.dll$</ModulePath>
<ModulePath>.*\\GqlPlus\.Verifier\.dll$</ModulePath>
<ModulePath>.*\\GqlPlus\.Parser\.ClassTests\.dll$</ModulePath>
</Include>
</ModulePaths>

Expand Down Expand Up @@ -48,4 +50,4 @@
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
</RunSettings>
50 changes: 0 additions & 50 deletions src/GqlPlus.Abstractions/Abstractions/Definition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,53 +77,3 @@ public interface IGqlpFields<TValue>
: IImmutableDictionary<IGqlpFieldKey, TValue>
, IEquatable<IGqlpFields<TValue>>
{ }

public static class IGqlpValuesHelper
{
private sealed class GqlpFieldsWrapper<TValue>(
IImmutableDictionary<IGqlpFieldKey, TValue> fields
) : IGqlpFields<TValue>
{
TValue IReadOnlyDictionary<IGqlpFieldKey, TValue>.this[IGqlpFieldKey key] => fields[key];
IEnumerable<IGqlpFieldKey> IReadOnlyDictionary<IGqlpFieldKey, TValue>.Keys => fields.Keys;
IEnumerable<TValue> IReadOnlyDictionary<IGqlpFieldKey, TValue>.Values => fields.Values;
int IReadOnlyCollection<KeyValuePair<IGqlpFieldKey, TValue>>.Count => fields.Count;

IImmutableDictionary<IGqlpFieldKey, TValue> IImmutableDictionary<IGqlpFieldKey, TValue>.Add(IGqlpFieldKey key, TValue value)
=> fields.Add(key, value);
IImmutableDictionary<IGqlpFieldKey, TValue> IImmutableDictionary<IGqlpFieldKey, TValue>.AddRange(IEnumerable<KeyValuePair<IGqlpFieldKey, TValue>> pairs)
=> fields.AddRange(pairs);
IImmutableDictionary<IGqlpFieldKey, TValue> IImmutableDictionary<IGqlpFieldKey, TValue>.Clear()
=> fields.Clear();
bool IImmutableDictionary<IGqlpFieldKey, TValue>.Contains(KeyValuePair<IGqlpFieldKey, TValue> pair)
=> fields.Contains(pair);
bool IReadOnlyDictionary<IGqlpFieldKey, TValue>.ContainsKey(IGqlpFieldKey key)
=> fields.ContainsKey(key);
bool IEquatable<IGqlpFields<TValue>>.Equals(IGqlpFields<TValue>? other)
=> fields.Equals(other);
IEnumerator<KeyValuePair<IGqlpFieldKey, TValue>> IEnumerable<KeyValuePair<IGqlpFieldKey, TValue>>.GetEnumerator()
=> fields.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> fields.GetEnumerator();
IImmutableDictionary<IGqlpFieldKey, TValue> IImmutableDictionary<IGqlpFieldKey, TValue>.Remove(IGqlpFieldKey key)
=> fields.Remove(key);
IImmutableDictionary<IGqlpFieldKey, TValue> IImmutableDictionary<IGqlpFieldKey, TValue>.RemoveRange(IEnumerable<IGqlpFieldKey> keys)
=> fields.RemoveRange(keys);
IImmutableDictionary<IGqlpFieldKey, TValue> IImmutableDictionary<IGqlpFieldKey, TValue>.SetItem(IGqlpFieldKey key, TValue value)
=> fields.SetItem(key, value);
IImmutableDictionary<IGqlpFieldKey, TValue> IImmutableDictionary<IGqlpFieldKey, TValue>.SetItems(IEnumerable<KeyValuePair<IGqlpFieldKey, TValue>> items)
=> fields.SetItems(items);
bool IImmutableDictionary<IGqlpFieldKey, TValue>.TryGetKey(IGqlpFieldKey equalKey, out IGqlpFieldKey actualKey)
=> fields.TryGetKey(equalKey, out actualKey);
#pragma warning disable CS8601 // Possible null reference assignment.
bool IReadOnlyDictionary<IGqlpFieldKey, TValue>.TryGetValue(IGqlpFieldKey key, out TValue value)
=> fields.TryGetValue(key, out value);
#pragma warning restore CS8601
}

public static IGqlpFields<TResult> ToFields<TKey, TValue, TResult>(this IDictionary<TKey, TValue> dictionary, Func<TValue, TResult> valueSelector)
where TKey : IGqlpFieldKey
=> new GqlpFieldsWrapper<TResult>(dictionary.ToImmutableDictionary(
kv => (IGqlpFieldKey)kv.Key,
kv => valueSelector(kv.Value)));
}
1 change: 1 addition & 0 deletions src/GqlPlus.Abstractions/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
[assembly: SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix")]
[assembly: SuppressMessage("Naming", "CA1720:Identifier contains type name")]
[assembly: SuppressMessage("Usage", "CA2217:Do not mark enums with FlagsAttribute")]
[assembly: SuppressMessage("Usage", "CA2225:Operator overloads have named alternates")]
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace GqlPlus.Rendering;

public interface IRenderer<TModel>
where TModel : IModelBase
{
RenderStructure Render(TModel model);
Structured Render(TModel model);
}
41 changes: 41 additions & 0 deletions src/GqlPlus.Abstractions/Rendering/StructureHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using GqlPlus.Abstractions;

namespace GqlPlus.Rendering;

public static class StructureHelper
{
public static Structured Render(this ITokenMessages errors)
=> new(errors.Select(Render), "_Errors");

private static Structured Render(ITokenMessage error)
=> Structured.New("_Error")
.AddIf(error.Kind is TokenKind.Start or TokenKind.End,
onFalse: r => r.Add("_at", RenderAt(error)))
.AddEnum("_kind", error.Kind)
.Add("_message", error.Message);

private static Structured RenderAt(ITokenAt at)
=> Structured.New("_At", true)
.Add("_col", at.Column)
.Add("_line", at.Line);

public static string TrueFalse(this bool value)
=> value ? "true" : "false";

public static string TypeTag(this Type type)
{
string result = "_" + type.ThrowIfNull().Name.Replace("Model", "", StringComparison.InvariantCulture);

if (type.IsGenericType) {
IEnumerable<string> typeParams = type.GetGenericArguments().Select(TypeTag);

result = result.Split('`')[0] + "(" + string.Concat(typeParams) + ")";
}

return result;
}

public static Structured RenderEnum<TEnum>(this TEnum value)
where TEnum : struct
=> new(value.ToString(), value.GetType().TypeTag());
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace GqlPlus.Rendering;

#pragma warning disable CA1036 // Override methods on comparable types
public sealed record class RenderValue
: IComparable<RenderValue> //, IEquatable<RenderValue>
public sealed record class StructureValue
: IComparable<StructureValue> //, IEquatable<RenderValue>
{
public bool IsEmpty
=> string.IsNullOrEmpty(Identifier)
Expand All @@ -19,19 +19,19 @@ public bool IsEmpty

public string Tag { get; private init; } = "";

public RenderValue(bool? value, string tag = "")
public StructureValue(bool? value, string tag = "")
=> (Boolean, Tag) = (value, tag);
public RenderValue(string? value, string tag = "")
public StructureValue(string? value, string tag = "")
=> (Identifier, Tag) = (value, tag);
public RenderValue(decimal? value, string tag = "")
public StructureValue(decimal? value, string tag = "")
=> (Number, Tag) = (value, tag);

private RenderValue() { }
private StructureValue() { }

public static RenderValue Str(string? value, string tag = "")
public static StructureValue Str(string? value, string tag = "")
=> new() { Text = value, Tag = tag };

public int CompareTo(RenderValue? other)
public int CompareTo(StructureValue? other)
=> string.Equals(Tag, other?.Tag, StringComparison.Ordinal)
? BothValued(other?.Boolean, Boolean) ? Boolean.Value.CompareTo(other.Boolean)
: BothValued(other?.Number, Number) ? Number.Value.CompareTo(other.Number)
Expand Down
149 changes: 149 additions & 0 deletions src/GqlPlus.Abstractions/Rendering/Structured.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System.Reflection;

namespace GqlPlus.Rendering;

public class Structured
: Structured<StructureValue, Structured>
{
public bool IsEmpty
=> List.Count == 0
&& Map.Count == 0
&& (Value?.IsEmpty ?? true);

public bool Flow { get; }
public string Tag { get; } = string.Empty;

public static Structured New(string tag, bool flow = false)
=> new(tag, flow);
public static Structured ForAll(IEnumerable<string> values, string tag = "", bool flow = false)
=> new(values.Select(v => new Structured(v)), tag, flow);

private Structured(string tag, bool flow)
: base() => (Tag, Flow) = (tag, flow);
public Structured(bool? value, string tag = "")
: base(new StructureValue(value)) => Tag = tag;
public Structured(string? value, string tag = "")
: base(new StructureValue(value)) => Tag = tag;
public Structured(decimal? value, string tag = "")
: base(new StructureValue(value)) => Tag = tag;
public Structured(StructureValue value, string tag = "")
: base(value) => Tag = tag;
public Structured(IEnumerable<Structured> list, string tag = "", bool flow = false)
: base(list) => (Tag, Flow) = (tag, flow);
public Structured(IDictionary<StructureValue, Structured> map, string tag, bool flow = false)
: base(map) => (Tag, Flow) = (tag, flow);

public static implicit operator Structured(StructureValue value)
=> new(value);
public static implicit operator Structured(bool value)
=> new(value);
public static implicit operator Structured(string value)
=> new(value);
public static implicit operator Structured(decimal value)
=> new(value);

public Structured Add(string key, Structured? value)
{
if (value is null || value.IsEmpty) {
return this;
}

Map.Add(new(key), value);
return this;
}

public Structured AddBool(string key, bool value, bool blankIfDefault)
=> value || !blankIfDefault
? Add(key, value)
: this;

public Structured IncludeRendered<TValue>(TValue? value, IRenderer<TValue> renderer)
{
if (value is null) {
return this;
}

ArgumentNullException.ThrowIfNull(renderer);

foreach ((StructureValue key, Structured item) in renderer.Render(value).Map) {
Map.Add(key, item);
}

return this;
}

public Structured AddEnum<TValue>(string key, TValue value, string? tag = null)
where TValue : Enum
=> Add(key, new(value.ToString(), tag ?? typeof(TValue).TypeTag()));

public Structured AddRendered<TValue>(string key, TValue? value, IRenderer<TValue> renderer)
=> value is null ? this
: Add(key, renderer.ThrowIfNull().Render(value));

public Structured AddList<TValue>(string key, IEnumerable<TValue> values, IRenderer<TValue> renderer, string tag = "", bool flow = false)
=> Add(key, new(values.Select(renderer.ThrowIfNull().Render), tag, flow));

public Structured AddMap<TValue>(string key, IMap<TValue> values, IRenderer<TValue> renderer, string dictTag, bool flow = false, string keyTag = "_Identifier")
=> Add(key, new(values.ToDictionary(
p => new StructureValue(p.Key, keyTag),
p => renderer.Render(p.Value)), "_Map" + dictTag, flow));

public Structured AddIf(bool optional, Func<Structured, Structured>? onTrue = null, Func<Structured, Structured>? onFalse = null)
=> optional
? (onTrue is not null ? onTrue(this) : this)
: (onFalse is not null ? onFalse(this) : this);

public Structured AddSet<TEnum>(string key, TEnum set, string? tag = null, bool flow = true)
where TEnum : Enum
{
Type type = typeof(TEnum);

if (type.GetCustomAttributes<FlagsAttribute>().Any()) {
int flags = (int)(object)set;
IDict result = NewDict;

foreach (object? value in Enum.GetValues(type)) {
int flag = (int)value;
if (int.PopCount(flag) == 1 && (flags & flag) == flag) {
result.Add(new(Enum.GetName(type, value)), new("_"));
}
}

return Add(key, new(result, $"_Set({tag ?? type.TypeTag()})", flow: flow));
}

return this;
}
}

#pragma warning disable CA1034 // Nested types should not be visible
public class Structured<TValue, TObject>
where TValue : notnull
where TObject : Structured<TValue, TObject>
{
internal sealed class Dict
: Dictionary<TValue, TObject>, IDict
{
internal Dict() : base() { }
internal Dict(IDictionary<TValue, TObject> dictionary)
: base(dictionary) { }
}

public interface IDict
: IDictionary<TValue, TObject>
{ }

public IDict NewDict => new Dict();

public TValue? Value { get; }
public IList<TObject> List { get; } = [];
public IDict Map { get; } = new Dict();

public Structured() { }
public Structured(TValue value)
=> Value = value;
public Structured(IEnumerable<TObject> values)
=> List = [.. values];
public Structured(IDictionary<TValue, TObject> values)
=> Map = new Dict(values);
}
33 changes: 0 additions & 33 deletions src/GqlPlus.Abstractions/Structured.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/GqlPlus.Modeller/Convert/RenderJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ private static JsonSerializerOptions JsonOptions(bool indented)
WriteIndented = indented,
};

public static string ToJson(this RenderStructure model)
public static string ToJson(this Structured model)
=> JsonSerializer.Serialize(model, Options) + "\n";
}
2 changes: 1 addition & 1 deletion src/GqlPlus.Modeller/Convert/RenderJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace GqlPlus.Convert;
internal abstract class RenderJsonConverter<T>
: JsonConverter<T>
{
protected void WriteValue(Utf8JsonWriter writer, RenderValue value)
protected void WriteValue(Utf8JsonWriter writer, StructureValue value)
{
if (value is null || value.IsEmpty) {
return;
Expand Down
Loading

0 comments on commit 51a1d1d

Please sign in to comment.