Skip to content

Instantly share code, notes, and snippets.

@MichalBrylka
Last active November 17, 2022 23:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MichalBrylka/faf99aed6a2c307a5cb9f763bed5e241 to your computer and use it in GitHub Desktop.
Save MichalBrylka/faf99aed6a2c307a5cb9f763bed5e241 to your computer and use it in GitHub Desktop.
Static Interface Members
var parsedLines = Lines<CommaSeparatedWords>.Parse("""
Ala,has,a,cat
Cat,has,Ala
""");
var parsedNumbers = CsvFile<int>.Parse("""
11
22
33
""");
var parsedStructs = CsvFile<CsvLine<int, float, char>>.Parse("""
11,1.1,A
22,2.2,B
33,3.3,C
""");
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>11.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<Using Include="System.Diagnostics" />
<Using Include="System.Diagnostics.CodeAnalysis" />
<Using Include="System.Globalization" />
<Using Include="System.Numerics" />
<Using Include="System.Runtime.CompilerServices" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Nemesis.TextParsers" />
</ItemGroup>
</Project>
using System.Collections;
using Nemesis.TextParsers;
namespace Demo;
public interface IMyParsable<TSelf>
where TSelf : IMyParsable<TSelf>?
{
static abstract TSelf Parse(ReadOnlySpan<char> s, IFormatProvider? provider);
static TSelf InvariantParse(ReadOnlySpan<char> s) =>
TSelf.Parse(s, CultureInfo.InvariantCulture);
static virtual TSelf Parse(string s, IFormatProvider? provider) =>
TSelf.Parse(s.AsSpan(), provider);
static virtual bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result)
{
result = default;
if (s == null) return false;
try
{
result = TSelf.Parse(s.AsSpan(), provider);
return true;
}
catch (Exception)
{
return false;
}
}
static virtual bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result)
{
try
{
result = TSelf.Parse(s, provider);
return true;
}
catch (Exception)
{
result = default;
return false;
}
}
}
record Lines<T>(IReadOnlyList<T> Values) : IEnumerable<T>, IMyParsable<Lines<T>>
where T : IMyParsable<T>
{
public static Lines<T> Parse(ReadOnlySpan<char> s, IFormatProvider? provider = null)
{
var splitText = s.EnumerateLines();
var lines = new List<T>();
foreach (var line in splitText)
lines.Add(T.Parse(line, provider));
//this also works in this context
//var canParse = T.TryParse("", provider, out var result);
return new(lines);
}
public override string ToString() => string.Join(Environment.NewLine, Values);
public IEnumerator<T> GetEnumerator() => Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Values.GetEnumerator();
}
record CommaSeparatedWords(IReadOnlyList<string> Words) : IMyParsable<CommaSeparatedWords>, IEnumerable<string>
{
public static CommaSeparatedWords Parse(ReadOnlySpan<char> s, IFormatProvider? provider)
{
var words = new List<string>();
foreach (var word in s.Split(','))
words.Add(word.ToString());
return new(words);
}
public override string ToString() => string.Join(",", Words);
public IEnumerator<string> GetEnumerator() => Words.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Words.GetEnumerator();
}
record CsvFile<T>(IReadOnlyList<T> Lines) : ISpanParsable<CsvFile<T>>
where T : ISpanParsable<T>
{
public static CsvFile<T> Parse(ReadOnlySpan<char> s, IFormatProvider? provider = null)
{
var splitText = s.EnumerateLines();
var lines = new List<T>();
foreach (var line in splitText)
lines.Add(T.Parse(line, provider));
return new(lines);
}
public static CsvFile<T> Parse(string s, IFormatProvider? provider = null) =>
Parse(s.AsSpan(), provider);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out CsvFile<T> result)
{
try
{
result = Parse(s, provider);
return true;
}
catch (Exception)
{
result = default;
return false;
}
}
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out CsvFile<T> result)
{
result = default;
if (s == null) return false;
try
{
result = Parse(s.AsSpan(), provider);
return true;
}
catch (Exception)
{
return false;
}
}
}
readonly record struct CsvLine<T1, T2, T3>(T1 Item1, T2 Item2, T3 Item3) : ISpanParsable<CsvLine<T1, T2, T3>>
where T1 : ISpanParsable<T1>
where T2 : ISpanParsable<T2>
where T3 : ISpanParsable<T3>
{
public static CsvLine<T1, T2, T3> Parse(ReadOnlySpan<char> s, IFormatProvider? provider = null)
{
var enumerator = s.Split(',').GetEnumerator();
if (!enumerator.MoveNext()) throw new("No element at 1st position");
var t1 = T1.Parse(enumerator.Current, CultureInfo.InvariantCulture);
if (!enumerator.MoveNext()) throw new("No element at 2nd position");
var t2 = T2.Parse(enumerator.Current, CultureInfo.InvariantCulture);
if (!enumerator.MoveNext()) throw new("No element at 3rd position");
var t3 = T3.Parse(enumerator.Current, CultureInfo.InvariantCulture);
return new(t1, t2, t3);
}
public static CsvLine<T1, T2, T3> Parse(string s, IFormatProvider? provider) =>
Parse(s.AsSpan(), provider);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out CsvLine<T1, T2, T3> result)
{
try
{
result = Parse(s, provider);
return true;
}
catch (Exception)
{
result = default;
return false;
}
}
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out CsvLine<T1, T2, T3> result)
{
result = default;
if (s == null) return false;
try
{
result = Parse(s.AsSpan(), provider);
return true;
}
catch (Exception)
{
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment