Repository navigation
Expand file tree
/
Copy pathReadMeTest.cs
More file actions
248 lines (212 loc) · 8.94 KB
/
Copy pathReadMeTest.cs
File metadata and controls
248 lines (212 loc) · 8.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace nietras.SeparatedValues.Test;
[TestClass]
public class ReadMeTest
{
[TestMethod]
public void ReadMeTest_()
{
var text = """
A;B;C;D;E;F
Sep;🚀;1;1.2;0.1;0.5
CSV;✅;2;2.2;0.2;1.5
""";
using var reader = Sep.Reader().FromText(text); // Infers separator 'Sep' from header
using var writer = reader.Spec.Writer().ToText(); // Writer defined from reader 'Spec'
// Use .FromFile(...)/ToFile(...) for files
var idx = reader.Header.IndexOf("B");
var nms = new[] { "E", "F" };
foreach (var readRow in reader) // Read one row at a time
{
var a = readRow["A"].Span; // Column as ReadOnlySpan<char>
var b = readRow[idx].ToString(); // Column to string (might be pooled)
var c = readRow["C"].Parse<int>(); // Parse any T : ISpanParsable<T>
var d = readRow["D"].Parse<float>(); // Parse float/double fast via csFastFloat
var s = readRow[nms].Parse<double>(); // Parse multiple columns as Span<T>
// - Sep handles array allocation and reuse
foreach (ref var v in s) { v *= 10; }
using var writeRow = writer.NewRow(); // Start new row. Row written on Dispose.
writeRow["A"].Set(a); // Set by ReadOnlySpan<char>
writeRow["B"].Set(b); // Set by string
writeRow["C"].Set($"{c * 2}"); // Set via InterpolatedStringHandler, no allocs
writeRow["D"].Format(d / 2); // Format any T : ISpanFormattable
writeRow[nms].Format(s); // Format multiple columns directly
// Columns are added on first access as ordered, header written when first row written
}
var expected = """
A;B;C;D;E;F
Sep;🚀;2;0.6;1;5
CSV;✅;4;1.1;2;15
""";
Assert.AreEqual(expected, writer.ToString());
// Above example code is for demonstration purposes only.
// Short names and repeated constants are only for demonstration.
}
[TestMethod]
public void ReadMeTest_LocalFunction_YieldReturn()
{
var text = """
Key;Value
A;1.1
B;2.2
""";
var expected = new (string Key, double Value)[] {
("A", 1.1),
("B", 2.2),
};
using var reader = Sep.Reader().FromText(text);
var actual = Enumerate(reader).ToArray();
CollectionAssert.AreEqual(expected, actual);
static IEnumerable<(string Key, double Value)> Enumerate(SepReader reader)
{
foreach (var row in reader)
{
yield return (row["Key"].ToString(), row["Value"].Parse<double>());
}
}
}
[TestMethod]
public void ReadMeTest_Enumerate()
{
var text = """
Key;Value
A;1.1
B;2.2
""";
var expected = new (string Key, double Value)[] {
("A", 1.1),
("B", 2.2),
};
using var reader = Sep.Reader().FromText(text);
var actual = Enumerate(reader,
row => (row["Key"].ToString(), row["Value"].Parse<double>()))
.ToArray();
CollectionAssert.AreEqual(expected, actual);
static IEnumerable<T> Enumerate<T>(SepReader reader, SepReader.RowFunc<T> func)
{
foreach (var row in reader)
{
yield return func(row);
}
}
}
[TestMethod]
public void ReadMeTest_EnumerateWhere()
{
var text = """
Key;Value
A;1.1
B;2.2
""";
var expected = new (string Key, double Value)[] {
("B", 2.2),
};
using var reader = Sep.Reader().FromText(text);
var actual = Enumerate(reader,
row => (row["Key"].ToString(), row["Value"].Parse<double>()))
.Where(kv => kv.Item1.StartsWith("B", StringComparison.Ordinal))
.ToArray();
CollectionAssert.AreEqual(expected, actual);
static IEnumerable<T> Enumerate<T>(SepReader reader, SepReader.RowFunc<T> func)
{
foreach (var row in reader)
{
yield return func(row);
}
}
}
[TestMethod]
public void ReadMeTest_IteratorWhere()
{
var text = """
Key;Value
A;1.1
B;2.2
""";
var expected = new (string Key, double Value)[] {
("B", 2.2),
};
using var reader = Sep.Reader().FromText(text);
var actual = Enumerate(reader).ToArray();
CollectionAssert.AreEqual(expected, actual);
static IEnumerable<(string Key, double Value)> Enumerate(SepReader reader)
{
foreach (var row in reader)
{
var keyCol = row["Key"];
if (keyCol.Span.StartsWith("B"))
{
yield return (keyCol.ToString(), row["Value"].Parse<double>());
}
}
}
}
[TestMethod]
public void ReadMeTest_Example_Copy_Rows()
{
var text = """
A;B;C;D;E;F
Sep;🚀;1;1.2;0.1;0.5
CSV;✅;2;2.2;0.2;1.5
""";
using var reader = Sep.Reader().FromText(text);
using var writer = reader.Spec.Writer().ToText();
foreach (var readRow in reader)
{
using var writeRow = writer.NewRow(readRow);
}
Assert.AreEqual(text, writer.ToString());
}
[TestMethod]
public void ReadMeTest_UpdateExampleCodeInMarkdown()
{
var sourceFile = SourceFile();
var sourceLines = File.ReadAllLines(sourceFile);
var readmeFile = Path.GetDirectoryName(sourceFile) + @"../../../README.md";
var readmeLines = File.ReadAllLines(readmeFile);
var blocksToUpdate = new (string MethodNameWithParenthesis, string ReadmeLineBeforeCodeBlock)[]
{
(nameof(ReadMeTest_) + "()", "## Example"),
(nameof(ReadMeTest_LocalFunction_YieldReturn) + "()", "If you want to use LINQ"),
(nameof(ReadMeTest_Enumerate) + "()", "Now if instead refactoring this to something LINQ-compatible"),
(nameof(ReadMeTest_EnumerateWhere) + "()", "Which discounting the `Enumerate`"),
(nameof(ReadMeTest_IteratorWhere) + "()", "Instead, you should focus on how to express the enumeration"),
(nameof(ReadMeTest_Example_Copy_Rows) + "()", "### Example - Copy Rows"),
};
foreach (var (methodNameWithParenthesis, readmeLineBeforeCodeBlock) in blocksToUpdate)
{
var sourceExampleLines = GetSourceExampleLines(sourceLines, methodNameWithParenthesis);
var readmeLineBefore = Array.FindIndex(readmeLines,
l => l.StartsWith(readmeLineBeforeCodeBlock, StringComparison.Ordinal)) + 1;
if (readmeLineBefore == 0)
{ throw new ArgumentException($"README line '{readmeLineBeforeCodeBlock}' not found."); }
var readmeCodeStart = Array.FindIndex(readmeLines, readmeLineBefore,
l => l.StartsWith("```csharp", StringComparison.Ordinal)) + 1;
var readmeCodeEnd = Array.FindIndex(readmeLines, readmeCodeStart,
l => l.StartsWith("```", StringComparison.Ordinal));
readmeLines = readmeLines[..readmeCodeStart].AsEnumerable()
.Concat(sourceExampleLines)
.Concat(readmeLines[readmeCodeEnd..]).ToArray();
}
var newReadme = string.Join(Environment.NewLine, readmeLines) + Environment.NewLine;
File.WriteAllText(readmeFile, newReadme, Encoding.UTF8);
}
static string[] GetSourceExampleLines(string[] sourceLines, string methodNameWithParenthesis)
{
var sourceStartMethodLine = Array.FindIndex(sourceLines,
l => l.Contains(methodNameWithParenthesis, StringComparison.Ordinal));
var sourceStartLine = sourceStartMethodLine + 2;
var sourceEndLine = Array.FindIndex(sourceLines, sourceStartLine,
l => l.StartsWith(" }", StringComparison.Ordinal));
var sourceExampleLines = sourceLines[sourceStartLine..sourceEndLine]
.Select(l => l.Length > 0 ? l.Remove(0, 8) : l).ToArray();
return sourceExampleLines;
}
static string SourceFile([CallerFilePath] string sourceFilePath = "") => sourceFilePath;
}