Skip to content

Commit ee517fc

Browse files
committed
Added Waffle, DateTimeOffset and TimeSpan generators
- Also removed redundant DateTime and DateOnly generators - Also added a shuffle generator
1 parent 9904369 commit ee517fc

17 files changed

Lines changed: 506 additions & 121 deletions

.scripts/New.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ namespace Forged.Core.Generators.{{module}};
3636
3737
/// <summary>
3838
/// </summary>
39-
/// <param name="rng">The random number generator to use</param>
40-
public sealed class {{name}}Generator<T>(System.Random rng) : Generator<T>(rng)
39+
public sealed class {{name}}Generator<T>(Forge forge) : Generator<T>(forge)
4140
{
4241
public override T Generate() => throw new NotImplementedException();
4342
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Text;
2+
3+
namespace Forged.Core.Core;
4+
5+
internal static class TemplateCompiler
6+
{
7+
public static List<Token> Compile(string template)
8+
{
9+
var tokens = new List<Token>();
10+
var span = template.AsSpan();
11+
12+
var i = 0;
13+
14+
while (i < span.Length)
15+
{
16+
var start = template.IndexOf('{', i);
17+
if (start == -1)
18+
{
19+
tokens.Add(new Literal(template[i..]));
20+
break;
21+
}
22+
23+
if (start > i)
24+
{
25+
tokens.Add(new Literal(template[i..start]));
26+
}
27+
28+
var end = template.IndexOf('}', start);
29+
var key = template[(start + 1)..end];
30+
31+
tokens.Add(new Placeholder(key));
32+
33+
i = end + 1;
34+
}
35+
36+
return tokens;
37+
}
38+
39+
public static string Render(List<Token> tokens, Dictionary<string, string> values)
40+
{
41+
var sb = new StringBuilder();
42+
43+
foreach (var token in tokens)
44+
{
45+
var part = token switch
46+
{
47+
Literal l => l.Value,
48+
Placeholder p => values[p.Key],
49+
_ => throw new InvalidOperationException("Token was somehow neither literal nor placeholder"),
50+
};
51+
sb.Append(part);
52+
}
53+
54+
return sb.ToString();
55+
}
56+
57+
public static string Render(List<Token> tokens, Dictionary<string, Func<string>> values)
58+
{
59+
var sb = new StringBuilder();
60+
61+
foreach (var token in tokens)
62+
{
63+
var part = token switch
64+
{
65+
Literal l => l.Value,
66+
Placeholder p => values[p.Key](),
67+
_ => throw new InvalidOperationException("Token was somehow neither literal nor placeholder"),
68+
};
69+
sb.Append(part);
70+
}
71+
72+
return sb.ToString();
73+
}
74+
75+
internal abstract record Token;
76+
internal sealed record Literal(string Value) : Token;
77+
internal sealed record Placeholder(string Key) : Token;
78+
}

Forged.Core/Forged.Core.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
<PackageReference Include="Atulin.SealAnalyzer" Version="1.0.0" PrivateAssets="all" />
2323
<PackageReference Include="JetBrains.Annotations" Version="2025.2.4" />
2424
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="10.0.9" />
25+
<PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta21" />
26+
<PackageReference Include="NetEscapades.EnumGenerators.Interceptors" Version="1.0.0-beta21" />
2527
</ItemGroup>
2628

2729
</Project>

Forged.Core/Generators/GeneratorExtensions.cs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Globalization;
22
using Forged.Core.Generators.Utility;
3+
using Forged.Core.Generators.Utility.Collections;
34
using Forged.Core.Generators.Utility.Temporal;
45
using Forged.Core.Generators.Utility.Text;
56

@@ -183,20 +184,31 @@ public static Generator<HashSet<T>> AsHashSet<T>(this Generator<IEnumerable<T>>
183184
public static Generator<HashSet<T>> AsHashSet<T>(this Generator<ICollection<T>> generator)
184185
=> new RefineGenerator<ICollection<T>, HashSet<T>>(generator, static c => c.ToHashSet(), generator.Forge);
185186

186-
/// <summary>
187-
/// Converts enumerables from a generator to dictionaries.
188-
/// </summary>
189-
/// <typeparam name="T">The type of items in the enumerable.</typeparam>
190-
/// <typeparam name="TKey">The type of dictionary keys.</typeparam>
191-
/// <typeparam name="TValue">The type of dictionary values.</typeparam>
192187
/// <param name="generator">The generator to convert.</param>
193-
/// <param name="keySelector">A function to extract the key from each item.</param>
194-
/// <param name="valueSelector">A function to extract the value from each item.</param>
195-
/// <returns>A generator that produces dictionaries.</returns>
196-
public static Generator<Dictionary<TKey, TValue>> AsDictionary<T, TKey, TValue>(
197-
this Generator<IEnumerable<T>> generator,
198-
Func<T, TKey> keySelector,
199-
Func<T, TValue> valueSelector
200-
) where TKey : notnull
201-
=> generator.Refine(e => e.ToDictionary(keySelector, valueSelector));
188+
/// <typeparam name="T">The type of items in the enumerable.</typeparam>
189+
extension<T>(Generator<IEnumerable<T>> generator)
190+
{
191+
/// <summary>
192+
/// Converts enumerables from a generator to dictionaries.
193+
/// </summary>
194+
/// <typeparam name="TKey">The type of dictionary keys.</typeparam>
195+
/// <typeparam name="TValue">The type of dictionary values.</typeparam>
196+
/// <param name="keySelector">A function to extract the key from each item.</param>
197+
/// <param name="valueSelector">A function to extract the value from each item.</param>
198+
/// <returns>A generator that produces dictionaries.</returns>
199+
public Generator<Dictionary<TKey, TValue>> AsDictionary<TKey, TValue>(Func<T, TKey> keySelector,
200+
Func<T, TValue> valueSelector
201+
) where TKey : notnull
202+
=> generator.Refine(e => e.ToDictionary(keySelector, valueSelector));
203+
204+
205+
/// <summary>
206+
/// Creates a generator that produces shuffled versions of the enumerable generated by the given generator.
207+
/// </summary>
208+
/// <typeparam name="T">The type of elements in the enumerable being shuffled.</typeparam>
209+
/// <returns>A generator that produces shuffled enumerables.</returns>
210+
public Generator<IEnumerable<T>> Shuffle()
211+
=> new ShuffleGenerator<T>(generator, generator.Forge);
212+
}
213+
202214
}

Forged.Core/Generators/Temporal/DateOnlyInFutureGenerator.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.

Forged.Core/Generators/Temporal/DateOnlyInPastGenerator.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.

Forged.Core/Generators/Temporal/DateTimeInFutureGenerator.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.

Forged.Core/Generators/Temporal/DateTimeInPastGenerator.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Forged.Core.Generators.Temporal;
2+
3+
/// <summary>
4+
/// Generates random <see cref="DateTimeOffset"/> values within a specified range.
5+
/// </summary>
6+
/// <param name="min">The minimum date/time offset (inclusive). If null, uses <see cref="DateTimeOffset.MinValue"/>. </param>
7+
/// <param name="max">The maximum date/time offset (inclusive). If null, uses <see cref="DateTimeOffset.MaxValue"/>. </param>
8+
/// <param name="forge">The Forge instance to use.</param>
9+
public sealed class DateTimeOffsetGenerator(DateTimeOffset? min, DateTimeOffset? max, Forge forge) : Generator<DateTimeOffset>(forge)
10+
{
11+
private readonly long _minTicks = (min ?? DateTimeOffset.MinValue).Ticks;
12+
private readonly long _maxTicks = (max ?? DateTimeOffset.MaxValue).Ticks;
13+
14+
/// <summary>
15+
/// Generates a random <see cref="DateTimeOffset"/> value.
16+
/// </summary>
17+
/// <returns>A random <see cref="DateTimeOffset"/> between the specified minimum and maximum.</returns>
18+
public override DateTimeOffset Generate()
19+
{
20+
var ticks = Rng.NextInt64(_minTicks, _maxTicks);
21+
return new DateTimeOffset(ticks, TimeSpan.Zero);
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Forged.Core.Generators.Temporal;
2+
3+
/// <summary>
4+
/// Generates random <see cref="TimeSpan"/> values within a specified range.
5+
/// </summary>
6+
/// <param name="min">The minimum time span (inclusive). If null, uses <see cref="TimeSpan.MinValue"/>. </param>
7+
/// <param name="max">The maximum time span (inclusive). If null, uses <see cref="TimeSpan.MaxValue"/>. </param>
8+
/// <param name="forge">The Forge instance to use.</param>
9+
public sealed class TimeSpanGenerator(TimeSpan? min, TimeSpan? max, Forge forge) : Generator<TimeSpan>(forge)
10+
{
11+
private readonly long _minTicks = (min ?? TimeSpan.MinValue).Ticks;
12+
private readonly long _maxTicks = (max ?? TimeSpan.MaxValue).Ticks;
13+
14+
/// <summary>
15+
/// Generates a random <see cref="TimeSpan"/> value.
16+
/// </summary>
17+
/// <returns>A random <see cref="TimeSpan"/> between the specified minimum and maximum.</returns>
18+
public override TimeSpan Generate()
19+
{
20+
var ticks = Rng.NextInt64(_minTicks, _maxTicks);
21+
return new TimeSpan(ticks);
22+
}
23+
}

0 commit comments

Comments
 (0)