├── LPRun.md ├── Tests └── CsvLINQPadDriverTest │ ├── LPRun │ ├── Data │ │ ├── Comments.csv │ │ ├── Same.csv │ │ ├── Authors.csv │ │ ├── Authors2.csv │ │ ├── Books.csv │ │ ├── Encoding │ │ │ ├── Utf8Cp65001.csv │ │ │ └── German │ │ │ │ └── Cp1252.csv │ │ ├── SkipLeadingRows.csv │ │ └── Books2.csv │ └── Templates │ │ ├── Comments.linq │ │ ├── SkipLeadingRowsAll.linq │ │ ├── SkipLeadingRows.linq │ │ ├── StringComparisonForInterning.linq │ │ ├── RenameTable.linq │ │ ├── StringComparison.linq │ │ ├── Encoding.linq │ │ ├── Relations.linq │ │ ├── SimilarFilesRelations.linq │ │ └── Generation.linq │ ├── LPRunTests.cs │ ├── LPRunTests.FromSource.cs │ └── CsvLINQPadDriverTest.csproj ├── Update-Version.bat ├── global.json ├── Src ├── CsvLINQPadDriver │ ├── Connection.ico │ ├── Connection.png │ ├── NuGetIcon.png │ ├── CodeGen │ │ ├── CsvDataContextBase.cs │ │ ├── CsvColumnInfo.cs │ │ ├── ICsvRowBase.cs │ │ ├── CsvColumnInfoList.cs │ │ ├── LazyEnumerable.cs │ │ ├── CsvRowMappingBase.cs │ │ ├── CsvTableEnumerable.cs │ │ ├── CsvTableFactory.cs │ │ ├── CsvTableList.cs │ │ └── CsvTableBase.cs │ ├── FailedConnection.png │ ├── FileType.cs │ ├── DataModel │ │ ├── ICsvNames.cs │ │ ├── CsvColumn.cs │ │ ├── CsvDatabase.cs │ │ ├── CsvRelation.cs │ │ └── CsvTable.cs │ ├── Wpf │ │ ├── EnumObjectDataSources │ │ │ ├── FileTypeEnumObjectDataSource.cs │ │ │ ├── NoBomEncodingEnumObjectDataSource.cs │ │ │ ├── CsvModeOptionsEnumObjectDataSource.cs │ │ │ ├── FilesOrderByEnumObjectDataSource.cs │ │ │ ├── HeaderFormatEnumObjectDataSource.cs │ │ │ ├── TableNameFormatEnumObjectDataSource.cs │ │ │ ├── HeaderDetectionEnumObjectDataSource.cs │ │ │ ├── WhitespaceTrimOptionsEnumObjectDataSource.cs │ │ │ ├── StringComparisonEnumObjectDataSource.cs │ │ │ └── EnumObjectDataSource.cs │ │ ├── Converters │ │ │ ├── InvertedBooleanConverter.cs │ │ │ ├── RemoveHotKeyCharValueConverter.cs │ │ │ └── SkipLeadingRowsCountConverter.cs │ │ ├── RoutedUICommandEx.cs │ │ └── Extensions │ │ │ ├── ControlExtensions.cs │ │ │ └── DialogExtensions.cs │ ├── Directory.Build.targets │ ├── DataDisplay │ │ ├── HideFromDumpAttribute.cs │ │ └── CsvRowMemberProvider.cs │ ├── CsvModeOptions.cs │ ├── Bcl │ │ ├── Extensions │ │ │ ├── ImmutableExtensions.cs │ │ │ └── DirectoryExtensions.cs │ │ └── Microsoft │ │ │ └── System.Private.CoreLib │ │ │ └── System │ │ │ ├── Range.cs │ │ │ └── Index.cs │ ├── WhitespaceTrimOptions.cs │ ├── Config.cs │ ├── Extensions │ │ ├── LogExtensions.cs │ │ ├── TextExtensions.Regex.cs │ │ ├── ExceptionExtensions.cs │ │ ├── ShellExtensions.Regex.cs │ │ ├── EnumExtensions.cs │ │ ├── EnumerableExtensions.cs │ │ ├── TextExtensions.cs │ │ ├── CodeGenExtensions.Regex.cs │ │ ├── Exported │ │ │ ├── StringExtensions.cs │ │ │ ├── StringExtensions.Half.cs │ │ │ ├── StringExtensions.Float.cs │ │ │ ├── StringExtensions.Double.cs │ │ │ ├── StringExtensions.Decimal.cs │ │ │ ├── StringExtensions.BigInteger.cs │ │ │ ├── StringExtensions.Bool.cs │ │ │ ├── StringExtensions.Int128.cs │ │ │ ├── StringExtensions.Int.cs │ │ │ ├── StringExtensions.Long.cs │ │ │ ├── StringExtensions.Short.cs │ │ │ ├── StringExtensions.Byte.cs │ │ │ ├── StringExtensions.NInt.cs │ │ │ ├── StringExtensions.Guid.cs │ │ │ ├── StringExtensions.TimeSpan.cs │ │ │ ├── StringExtensions.DateOnly.cs │ │ │ ├── StringExtensions.TimeOnly.cs │ │ │ └── StringExtensions.Complex.cs │ │ ├── CodeGenExtensions.cs │ │ ├── ShellExtensions.cs │ │ └── FileExtensions.Regex.cs │ ├── FilesOrderBy.cs │ ├── ConnectionDialog.xaml.Regex.cs │ ├── DevDeploy.bat │ ├── HeaderDetection.cs │ ├── ConvertException.cs │ ├── CsvLINQPadDriver.LINQPad5.csproj │ ├── HeaderFormat.cs │ ├── TableNameFormat.cs │ ├── Directory.Build.props │ ├── app.manifest │ ├── CsvLINQPadDriver.csproj │ ├── CsvDataContextDriverPropertiesBase.cs │ ├── CsvDataContextDriver.cs │ ├── CsvDataContextDriverPropertiesEqualityComparer.cs │ ├── ConnectionDialog.xaml.Static.cs │ └── ICsvDataContextDriverProperties.cs └── StringEqualsBenchmark │ ├── StringEqualsBenchmark │ ├── StringEqualsBenchmark.csproj │ └── Program.cs │ └── StringEqualsBenchmark.sln ├── Deploy ├── header.xml └── buildlpx.cmd ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .editorconfig ├── LICENSE ├── CsvLINQPadDriver.LINQPad5.sln ├── CsvLINQPadDriver.sln └── Update-Version.ps1 /LPRun.md: -------------------------------------------------------------------------------- 1 | LPRun project can be found [here](https://github.com/i2van/LPRun). 2 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Comments.csv: -------------------------------------------------------------------------------- 1 | column 2 | #comment 3 | value -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Same.csv: -------------------------------------------------------------------------------- 1 | column 2 | Value 3 | vAlUe 4 | VALUE 5 | Value 6 | -------------------------------------------------------------------------------- /Update-Version.bat: -------------------------------------------------------------------------------- 1 | pwsh.exe -NoLogo -NoProfile -NonInteractive -Command "& '%~dpn0.ps1'" %* 2 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Authors.csv: -------------------------------------------------------------------------------- 1 | Id,Name 2 | 1,Author 1 3 | 2,Author 2 4 | 3,Author 3 -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Authors2.csv: -------------------------------------------------------------------------------- 1 | Id,Name 2 | 4,Author 4 3 | 5,Author 5 4 | 6,Author 6 -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "10.0.100", 4 | "rollForward": "latestFeature" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/Comments.linq: -------------------------------------------------------------------------------- 1 | Comments.Should().HaveCount(context.ExpectedCount, Reason()); 2 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Connection.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i2van/CsvLINQPadDriver/HEAD/Src/CsvLINQPadDriver/Connection.ico -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i2van/CsvLINQPadDriver/HEAD/Src/CsvLINQPadDriver/Connection.png -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/NuGetIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i2van/CsvLINQPadDriver/HEAD/Src/CsvLINQPadDriver/NuGetIcon.png -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvDataContextBase.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.CodeGen; 2 | 3 | public class CsvDataContextBase; 4 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/FailedConnection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i2van/CsvLINQPadDriver/HEAD/Src/CsvLINQPadDriver/FailedConnection.png -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Books.csv: -------------------------------------------------------------------------------- 1 | Id,Title,AuthorId 2 | 11,Author 1 Book 1,1 3 | 12,Author 1 Book 2,1 4 | 21,Author 2 Book 1,2 5 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Encoding/Utf8Cp65001.csv: -------------------------------------------------------------------------------- 1 | encoding,кодировка,الترميز,编码,コーディング 2 | encoding,кодировка,الترميز,编码,コーディング 3 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/SkipLeadingRows.csv: -------------------------------------------------------------------------------- 1 | SkippedHeader 2 | SkippedValue1 3 | SkippedValue2 4 | Header 5 | Value1 6 | Value2 7 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvColumnInfo.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.CodeGen; 2 | 3 | public sealed record CsvColumnInfo(int CsvColumnIndex, string PropertyName); 4 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Books2.csv: -------------------------------------------------------------------------------- 1 | Id,Title,AuthorId 2 | 41,Author 4 Book 1,4 3 | 51,Author 5 Book 1,5 4 | 52,Author 5 Book 2,5 5 | 61,Author 6 Book 1,6 6 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/FileType.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver; 2 | 3 | public enum FileType 4 | { 5 | CSV, 6 | TSV, 7 | Text, 8 | Log, 9 | All 10 | } 11 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Data/Encoding/German/Cp1252.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i2van/CsvLINQPadDriver/HEAD/Tests/CsvLINQPadDriverTest/LPRun/Data/Encoding/German/Cp1252.csv -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DataModel/ICsvNames.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.DataModel; 2 | 3 | internal interface ICsvNames 4 | { 5 | string? CodeName { get; set; } 6 | string? DisplayName { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/SkipLeadingRowsAll.linq: -------------------------------------------------------------------------------- 1 | this.GetType() 2 | .GetProperties() 3 | .Where(p => p.DeclaringType.ToString() == "LINQPad.User.CsvDataContext") 4 | .Should() 5 | .BeEmpty(Reason()); 6 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/FileTypeEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal sealed class FileTypeEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/NoBomEncodingEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal class NoBomEncodingEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Deploy/header.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CsvLINQPadDriver.dll 4 | https://github.com/i2van/CsvLINQPadDriver 5 | 6 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %(Filename).Replace('.Regex.', '.')) 5 | 6 | 7 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/CsvModeOptionsEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal class CsvModeOptionsEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/FilesOrderByEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal sealed class FilesOrderByEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/HeaderFormatEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal sealed class HeaderFormatEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/SkipLeadingRows.linq: -------------------------------------------------------------------------------- 1 | const string header = "Header"; 2 | 3 | SkipLeadingRows.First()[header].Should().Be("Value1", Reason()); 4 | SkipLeadingRows. Last()[header].Should().Be("Value2", Reason()); 5 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/TableNameFormatEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal class TableNameFormatEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/HeaderDetectionEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal sealed class HeaderDetectionEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DataDisplay/HideFromDumpAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CsvLINQPadDriver.DataDisplay; 4 | 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class HideFromDumpAttribute : Attribute; 7 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/WhitespaceTrimOptionsEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 2 | 3 | internal class WhitespaceTrimOptionsEnumObjectDataSource : EnumObjectDataSource; 4 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/StringComparisonEnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 4 | 5 | internal class StringComparisonEnumObjectDataSource : EnumObjectDataSource; 6 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/StringComparisonForInterning.linq: -------------------------------------------------------------------------------- 1 | new [] { Comments, Same }.SelectMany(_ => _) 2 | .Distinct() 3 | .Should() 4 | .HaveCount( 5 | context.StringComparison.ToString().EndsWith("IgnoreCase") 6 | ? 2 7 | : 5, 8 | Reason()); 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | 8 | - package-ecosystem: "nuget" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/ICsvRowBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable UnusedMember.Global 2 | 3 | namespace CsvLINQPadDriver.CodeGen; 4 | 5 | public interface ICsvRowBase 6 | { 7 | string? this[int index] { get; set; } 8 | string? this[string index] { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/RenameTable.linq: -------------------------------------------------------------------------------- 1 | GetType() 2 | .GetProperties() 3 | .Where(p => p.DeclaringType.IsAssignableFrom(typeof(CsvDataContext))) 4 | .Should() 5 | .OnlyContain(p => p.Name.StartsWith("table_"), Reason()); 6 | 7 | table_1.Should().NotBeEmpty(Reason()); 8 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DataModel/CsvColumn.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.DataModel; 2 | 3 | internal sealed record CsvColumn 4 | ( 5 | string Name, 6 | int Index 7 | ) : ICsvNames 8 | { 9 | public string? CodeName { get; set; } 10 | public string? DisplayName { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DataModel/CsvDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CsvLINQPadDriver.DataModel; 5 | 6 | internal sealed record CsvDatabase( 7 | string Name, 8 | IList Tables, 9 | IReadOnlyCollection Files, 10 | IReadOnlyCollection Exceptions 11 | ); 12 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CsvModeOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace CsvLINQPadDriver; 4 | 5 | public enum CsvModeOptions 6 | { 7 | [Description("Use RFC 4180 format")] 8 | Default, 9 | 10 | [Description("Use escapes")] 11 | Escape, 12 | 13 | [Description("Do not use quotes or escapes")] 14 | NoEscape 15 | } -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Bcl/Extensions/ImmutableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace CsvLINQPadDriver.Bcl.Extensions 5 | { 6 | internal static class ImmutableExtensions 7 | { 8 | public static List ToImmutableList(this IEnumerable source) => 9 | source.ToList(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DataModel/CsvRelation.cs: -------------------------------------------------------------------------------- 1 | namespace CsvLINQPadDriver.DataModel; 2 | 3 | internal sealed record CsvRelation( 4 | CsvTable SourceTable, 5 | CsvTable TargetTable, 6 | CsvColumn SourceColumn, 7 | CsvColumn TargetColumn 8 | ) : ICsvNames 9 | { 10 | public string? CodeName { get; set; } 11 | public string? DisplayName { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvColumnInfoList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CsvLINQPadDriver.CodeGen; 4 | 5 | public sealed class CsvColumnInfoList : List 6 | { 7 | // ReSharper disable once UnusedMember.Global 8 | public void Add(int csvColumnIndex, string name) => 9 | Add(new CsvColumnInfo(csvColumnIndex, name)); 10 | } 11 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/WhitespaceTrimOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace CsvLINQPadDriver; 4 | 5 | public enum WhitespaceTrimOptions 6 | { 7 | [Description("Around fields and inside quotes around fields")] 8 | All, 9 | 10 | [Description("Around fields")] 11 | Trim, 12 | 13 | [Description("Inside quotes around fields")] 14 | InsideQuotes 15 | } 16 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CsvLINQPadDriver; 4 | 5 | internal static class Config 6 | { 7 | internal static class Regex 8 | { 9 | #if NET7_0_OR_GREATER 10 | public 11 | #else 12 | private 13 | #endif 14 | const int TimeoutMs = 250; 15 | 16 | public static readonly TimeSpan Timeout = TimeSpan.FromMilliseconds(TimeoutMs); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Src/StringEqualsBenchmark/StringEqualsBenchmark/StringEqualsBenchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net461;net48;netcoreapp3.1;net5.0;net6.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/LogExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CsvLINQPadDriver.Extensions; 4 | 5 | internal static class LogExtensions 6 | { 7 | internal static void WriteToLog(this string additionalInfo, bool write, Exception? exception = null) 8 | { 9 | if (write) 10 | { 11 | CsvDataContextDriver.WriteToLog(additionalInfo, exception); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/StringComparison.linq: -------------------------------------------------------------------------------- 1 | var original = Books.First(); 2 | var copy = original with { Title = original.Title.ToUpperInvariant() }; 3 | 4 | var expectedEquality = original.Title.Equals(copy.Title, context.StringComparison); 5 | 6 | original.Equals(copy).Should().Be(expectedEquality, Reason()); 7 | 8 | original.GetHashCode() 9 | .Equals(copy.GetHashCode()) 10 | .Should() 11 | .Be(expectedEquality, Reason()); 12 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/Encoding.linq: -------------------------------------------------------------------------------- 1 | var data = context.SelectMany(_ => _).ToList(); 2 | 3 | var properties = data.First().GetType().GetProperties() 4 | .Where(property => property.Name != "Item") 5 | .Select(property => property.Name); 6 | 7 | data.Should().HaveCount(5, Reason()); 8 | data.Distinct().Should().HaveCount(1, Reason()); 9 | 10 | data.SelectMany(row => properties.Select(property => row[property] == property)) 11 | .Should() 12 | .OnlyContain(trueValue => trueValue, Reason()); 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | dotnet_diagnostic.CA1859.severity = none 3 | dotnet_diagnostic.CA1860.severity = none 4 | dotnet_diagnostic.CA1861.severity = none 5 | dotnet_diagnostic.IDE0079.severity = none 6 | dotnet_diagnostic.IDE0270.severity = none 7 | dotnet_diagnostic.IDE0290.severity = none 8 | dotnet_diagnostic.IDE0305.severity = none 9 | dotnet_diagnostic.S1121.severity = none 10 | dotnet_diagnostic.S2094.severity = none 11 | dotnet_diagnostic.S3358.severity = none 12 | dotnet_diagnostic.S6602.severity = none 13 | dotnet_diagnostic.S6608.severity = none 14 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/LazyEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace CsvLINQPadDriver.CodeGen; 6 | 7 | public sealed class LazyEnumerable : IEnumerable 8 | { 9 | private readonly Lazy> _data; 10 | 11 | public LazyEnumerable(Func> data) => 12 | _data = new Lazy>(data); 13 | 14 | public IEnumerator GetEnumerator() => 15 | _data.Value.GetEnumerator(); 16 | 17 | IEnumerator IEnumerable.GetEnumerator() => 18 | GetEnumerator(); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/Converters/InvertedBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace CsvLINQPadDriver.Wpf.Converters; 6 | 7 | [ValueConversion(typeof(bool), typeof(bool))] 8 | internal sealed class InvertedBooleanConverter : IValueConverter 9 | { 10 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => 11 | !(bool)value!; 12 | 13 | public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => 14 | throw new NotSupportedException(); 15 | } 16 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DataModel/CsvTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CsvLINQPadDriver.DataModel; 4 | 5 | internal sealed record CsvTable 6 | ( 7 | string FilePath, 8 | string? CsvSeparator, 9 | 10 | IList Columns, 11 | IList Relations 12 | ) : ICsvNames 13 | { 14 | public string? CodeName { get; set; } 15 | public string? DisplayName { get; set; } 16 | 17 | public string? ClassName 18 | { 19 | get => _className ?? CodeName; 20 | init => _className = value; 21 | } 22 | 23 | private readonly string? _className; 24 | } 25 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/FilesOrderBy.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace CsvLINQPadDriver; 4 | 5 | public enum FilesOrderBy 6 | { 7 | [Description("None")] 8 | None, 9 | 10 | [Description("Name (A to Z)")] 11 | NameAsc, 12 | 13 | [Description("Name (Z to A)")] 14 | NameDesc, 15 | 16 | [Description("Last write time (oldest to newest)")] 17 | LastWriteTimeAsc, 18 | 19 | [Description("Last write time (newest to oldest)")] 20 | LastWriteTimeDesc, 21 | 22 | [Description("Size (smallest to largest)")] 23 | SizeAsc, 24 | 25 | [Description("Size (largest to smallest)")] 26 | SizeDesc 27 | } 28 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/Relations.linq: -------------------------------------------------------------------------------- 1 | var actual = 2 | from book in Books 3 | join author in Authors on book.AuthorId equals author.Id 4 | select (author.Name, book.Title); 5 | 6 | var expected = new[] 7 | { 8 | ("Author 1", "Author 1 Book 1"), 9 | ("Author 1", "Author 1 Book 2"), 10 | ("Author 2", "Author 2 Book 1") 11 | }; 12 | 13 | actual.Should().BeEquivalentTo(expected, Reason()); 14 | 15 | Authors.ToList().ForEach( 16 | author => author.Books.Should().Equal(Books.Where(book => book.AuthorId == author.Id), Reason())); 17 | 18 | Books.ToList().ForEach( 19 | book => book.Authors.Should().Equal(Authors.Where(author => author.Id == book.AuthorId), Reason())); 20 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/ConnectionDialog.xaml.Regex.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | using static System.Text.RegularExpressions.RegexOptions; 4 | 5 | namespace CsvLINQPadDriver; 6 | 7 | internal partial class ConnectionDialog 8 | { 9 | private const string AppendFilesRegexPattern = @"[\r\n]$"; 10 | 11 | #if NET7_0_OR_GREATER 12 | [GeneratedRegex(AppendFilesRegexPattern, None, Config.Regex.TimeoutMs)] 13 | private static partial Regex AppendFilesRegex(); 14 | #else 15 | private static readonly Regex AppendFilesRegexVar = new (AppendFilesRegexPattern, Compiled, Config.Regex.Timeout); 16 | private static Regex AppendFilesRegex() => AppendFilesRegexVar; 17 | #endif 18 | } 19 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DevDeploy.bat: -------------------------------------------------------------------------------- 1 | rem 2 | rem You can simplify development by updating this batch file and then calling it from the 3 | rem project's post-build event. 4 | rem 5 | rem It copies the output .DLL (and .PDB) to LINQPad's drivers folder, so that LINQPad 6 | rem picks up the drivers immediately (without needing to click 'Add Driver'). 7 | rem 8 | rem The final part of the directory is the name of the assembly. 9 | 10 | set driverDir="%LOCALAPPDATA%\LINQPad\Drivers\DataContext\NetCore\CsvLINQPadDriver" 11 | 12 | mkdir %driverDir% 13 | 14 | for %%e in (CsvLINQPadDriver.dll CsvLINQPadDriver.pdb CsvLINQPadDriver.deps.json Connection.png FailedConnection.png) do xcopy /i/y %%e %driverDir% 15 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/Converters/RemoveHotKeyCharValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | using CsvLINQPadDriver.Extensions; 6 | 7 | namespace CsvLINQPadDriver.Wpf.Converters; 8 | 9 | [ValueConversion(typeof(string), typeof(string))] 10 | internal sealed class RemoveHotKeyCharValueConverter : IValueConverter 11 | { 12 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => 13 | ((string)value!).ReplaceHotKeyChar(); 14 | 15 | public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => 16 | throw new NotSupportedException(); 17 | } 18 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/SimilarFilesRelations.linq: -------------------------------------------------------------------------------- 1 | var actual = 2 | from book in new [] 3 | { 4 | Books, 5 | Books2, 6 | }.SelectMany(_ => _) 7 | join author in new [] 8 | { 9 | Authors, 10 | Authors2, 11 | }.SelectMany(_ => _) on book.AuthorId equals author.Id 12 | select (author.Name, book.Title); 13 | 14 | var expected = new[] 15 | { 16 | ("Author 1", "Author 1 Book 1"), 17 | ("Author 1", "Author 1 Book 2"), 18 | ("Author 2", "Author 2 Book 1"), 19 | ("Author 4", "Author 4 Book 1"), 20 | ("Author 5", "Author 5 Book 1"), 21 | ("Author 5", "Author 5 Book 2"), 22 | ("Author 6", "Author 6 Book 1") 23 | }; 24 | 25 | actual.Should().BeEquivalentTo(expected, Reason()); 26 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/TextExtensions.Regex.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | using static System.Text.RegularExpressions.RegexOptions; 4 | 5 | namespace CsvLINQPadDriver.Extensions; 6 | 7 | internal static partial class TextExtensions 8 | { 9 | private const string AppendDotRegexPattern = @"\p{P}\s*$"; 10 | 11 | #if NET7_0_OR_GREATER 12 | [GeneratedRegex(AppendDotRegexPattern, None, Config.Regex.TimeoutMs)] 13 | private static partial Regex AppendDotRegex(); 14 | #else 15 | private static readonly Regex AppendDotRegexVar = new(AppendDotRegexPattern, Compiled, Config.Regex.Timeout); 16 | private static Regex AppendDotRegex() => AppendDotRegexVar; 17 | #endif 18 | } 19 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/ExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Threading; 4 | 5 | namespace CsvLINQPadDriver.Extensions; 6 | 7 | internal static class ExceptionExtensions 8 | { 9 | public static bool CanBeHandled(this Exception exception) => 10 | exception is not ( 11 | AccessViolationException or 12 | ArgumentNullException or 13 | IndexOutOfRangeException or 14 | InvalidEnumArgumentException or 15 | NullReferenceException or 16 | OutOfMemoryException or 17 | StackOverflowException or 18 | ThreadAbortException 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/ShellExtensions.Regex.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | using static System.Text.RegularExpressions.RegexOptions; 4 | 5 | namespace CsvLINQPadDriver.Extensions; 6 | 7 | internal static partial class ShellExtensions 8 | { 9 | private const string SelectFileRegexPattern = @"\s+\([^)]+?\)$"; 10 | 11 | #if NET7_0_OR_GREATER 12 | [GeneratedRegex(SelectFileRegexPattern, None, Config.Regex.TimeoutMs)] 13 | private static partial Regex SelectFileRegex(); 14 | #else 15 | private static readonly Regex SelectFileRegexVar = new(SelectFileRegexPattern, Compiled, Config.Regex.Timeout); 16 | private static Regex SelectFileRegex() => SelectFileRegexVar; 17 | #endif 18 | } 19 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/HeaderDetection.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace CsvLINQPadDriver; 4 | 5 | public enum HeaderDetection 6 | { 7 | [Description("Header is missing")] 8 | NoHeader, 9 | 10 | [Description("Header is present")] 11 | HasHeader, 12 | 13 | [Description("All letters|Numbers|Punctuation")] 14 | AllLettersNumbersPunctuation, 15 | 16 | [Description("All letters|Numbers")] 17 | AllLettersNumbers, 18 | 19 | [Description("All letters only")] 20 | AllLetters, 21 | 22 | [Description("Latin letters|Numbers|Punctuation")] 23 | LatinLettersNumbersPunctuation, 24 | 25 | [Description("Latin letters|Numbers")] 26 | LatinLettersNumbers, 27 | 28 | [Description("Latin letters only")] 29 | LatinLetters 30 | } 31 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | 5 | namespace CsvLINQPadDriver.Extensions; 6 | 7 | internal static class EnumExtensions 8 | { 9 | public static Func GetFormatFunc(this T value) where T: struct, Enum 10 | { 11 | var name = 12 | #if NET5_0_OR_GREATER 13 | Enum.GetName(value) 14 | #else 15 | Enum.GetName(typeof(T), value) 16 | #endif 17 | ; 18 | 19 | if (name is null) 20 | { 21 | throw new InvalidEnumArgumentException($"Unknown {typeof(T).Name} {value}"); 22 | } 23 | 24 | var format = $"{name[..^1]}{{0}}"; 25 | var startIndex = name[^1] == '0' ? 0 : 1; 26 | 27 | return i => string.Format(CultureInfo.InvariantCulture, format, i + startIndex); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CsvLINQPadDriver.Extensions; 5 | 6 | internal static class EnumerableExtensions 7 | { 8 | public static IEnumerable SkipExceptions(this IEnumerable source, ICollection? exceptions = null) 9 | { 10 | using var enumerator = source.GetEnumerator(); 11 | 12 | while (true) 13 | { 14 | try 15 | { 16 | if (!enumerator.MoveNext()) 17 | { 18 | break; 19 | } 20 | } 21 | catch (Exception exception) when (exception.CanBeHandled()) 22 | { 23 | exceptions?.Add(exception); 24 | continue; 25 | } 26 | 27 | yield return enumerator.Current; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/RoutedUICommandEx.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows.Input; 3 | 4 | namespace CsvLINQPadDriver.Wpf; 5 | 6 | internal sealed class RoutedUICommandEx : RoutedUICommand 7 | { 8 | private readonly string? _toolTip; 9 | 10 | public string? ToolTip 11 | { 12 | get => _toolTip is null ? null : string.Format($"{_toolTip}{(_toolTip.Contains("{0}") ? string.Empty : " ({0})")}", InputGestureText); 13 | init => _toolTip = value; 14 | } 15 | 16 | public string InputGestureText { get; init; } = null!; 17 | 18 | public KeyGesture InputGestureAsKeyGesture => 19 | InputGestureAs(); 20 | 21 | public MouseGesture InputGestureAsMouseGesture => 22 | InputGestureAs(); 23 | 24 | private T InputGestureAs() where T : InputGesture => 25 | (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(null!, null!, InputGestureText)!; 26 | } 27 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/Converters/SkipLeadingRowsCountConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | using CsvLINQPadDriver.Extensions; 6 | 7 | namespace CsvLINQPadDriver.Wpf.Converters; 8 | 9 | [ValueConversion(typeof(string), typeof(int))] 10 | [ValueConversion(typeof(int), typeof(string))] 11 | internal sealed class SkipLeadingRowsCountConverter : IValueConverter 12 | { 13 | private const int DefaultCount = 0; 14 | 15 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 16 | { 17 | var intValue = (int)value!; 18 | return intValue == DefaultCount 19 | ? string.Empty 20 | : intValue.ToString(StringExtensions.DefaultFormatProvider); 21 | } 22 | 23 | public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => 24 | ((string?)value).ToIntSafe() ?? DefaultCount; 25 | } 26 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/EnumObjectDataSources/EnumObjectDataSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | 5 | namespace CsvLINQPadDriver.Wpf.EnumObjectDataSources; 6 | 7 | internal abstract class EnumObjectDataSource where T : struct, Enum 8 | { 9 | // ReSharper disable once UnusedMethodReturnValue.Global 10 | public static Tuple[] GetValues() => 11 | #if NET5_0_OR_GREATER 12 | Enum.GetValues() 13 | #else 14 | Enum.GetValues(typeof(T)).Cast() 15 | #endif 16 | .Select(value => 17 | { 18 | var valueAsString = value.ToString(); 19 | var fieldInfo = typeof(T).GetField(valueAsString); 20 | var descriptionAttributes = (DescriptionAttribute[])fieldInfo!.GetCustomAttributes(typeof(DescriptionAttribute), true); 21 | return Tuple.Create(value, descriptionAttributes.FirstOrDefault()?.Description ?? valueAsString); 22 | }).ToArray(); 23 | } 24 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/TextExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Humanizer; 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | internal static partial class TextExtensions 10 | { 11 | public static string JoinNewLine(this string? first, params string?[] other) => 12 | JoinNewLine(other.Prepend(first)); 13 | 14 | public static string JoinNewLine(this IEnumerable other) => 15 | string.Join(Environment.NewLine, other); 16 | 17 | public static string Pluralize(this IReadOnlyCollection collection, string what, string? fallback = null) => 18 | collection.Count > 1 19 | ? fallback ?? what.Pluralize() 20 | : what; 21 | 22 | public static string AppendDot(this string str) => 23 | AppendDotRegex().IsMatch(str) 24 | ? str 25 | : str + "."; 26 | 27 | public static string ReplaceHotKeyChar(this string str, string? newChar = null) => 28 | str.Replace("_", newChar); 29 | } 30 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/ConvertException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | #if !NET8_0_OR_GREATER 4 | using System.Runtime.Serialization; 5 | #endif 6 | 7 | namespace CsvLINQPadDriver; 8 | 9 | [Serializable] 10 | public class ConvertException : Exception 11 | { 12 | private ConvertException(string type, string? value, Exception? innerException) 13 | : base($@"Failed to convert ""{value}"" to {type}.", innerException) 14 | { 15 | } 16 | 17 | #if !NET8_0_OR_GREATER 18 | protected ConvertException(SerializationInfo info, StreamingContext context) 19 | : base(info, context) 20 | { 21 | } 22 | #endif 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | internal static ConvertException Create(string type, string? value, Exception? innerException) => 26 | new(type, value, innerException); 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | internal static ConvertException Create(string type, ReadOnlySpan value, Exception? innerException) => 30 | new(type, value.ToString(), innerException); 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 dobrou, 2021-2024 Ivan Ivon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /CsvLINQPadDriver.LINQPad5.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29411.108 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CsvLINQPadDriver.LINQPad5", "Src\CsvLINQPadDriver\CsvLINQPadDriver.LINQPad5.csproj", "{421D7A1C-AF93-430C-A705-B9323200EFD7}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {421D7A1C-AF93-430C-A705-B9323200EFD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {421D7A1C-AF93-430C-A705-B9323200EFD7}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {421D7A1C-AF93-430C-A705-B9323200EFD7}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {421D7A1C-AF93-430C-A705-B9323200EFD7}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {9F99CBEE-76DE-45EF-B67B-4E556971551B} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Src/StringEqualsBenchmark/StringEqualsBenchmark.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32811.315 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StringEqualsBenchmark", "StringEqualsBenchmark\StringEqualsBenchmark.csproj", "{A3CDC3DC-9BBE-4F4C-8410-195DB557AFED}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A3CDC3DC-9BBE-4F4C-8410-195DB557AFED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A3CDC3DC-9BBE-4F4C-8410-195DB557AFED}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A3CDC3DC-9BBE-4F4C-8410-195DB557AFED}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A3CDC3DC-9BBE-4F4C-8410-195DB557AFED}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {428B9679-0210-4350-B965-9BBEE7EAA447} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CsvLINQPadDriver.LINQPad5.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net481 4 | CsvLINQPadDriver 5 | CsvLINQPadDriver 6 | 7 | 8 | 9 | false 10 | true 11 | LINQPad 5 CSV Driver 12 | LINQPad 5 CSV driver 13 | $(AssemblyTitle). 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/CodeGenExtensions.Regex.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | using static System.Text.RegularExpressions.RegexOptions; 4 | 5 | namespace CsvLINQPadDriver.Extensions; 6 | 7 | internal static partial class CodeGenExtensions 8 | { 9 | private const string ReplaceRegexPattern1 = @"[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Nl}\p{Mn}\p{Mc}\p{Cf}\p{Pc}\p{Lm}]"; 10 | private const string ReplaceRegexPattern2 = $"{SafeChar}+"; 11 | private const string ReplaceRegexPattern3 = $"^{SafeChar}|{SafeChar}$"; 12 | 13 | #if NET7_0_OR_GREATER 14 | [GeneratedRegex(ReplaceRegexPattern1, None, Config.Regex.TimeoutMs)] 15 | private static partial Regex ReplaceRegex1(); 16 | 17 | [GeneratedRegex(ReplaceRegexPattern2, None, Config.Regex.TimeoutMs)] 18 | private static partial Regex ReplaceRegex2(); 19 | 20 | [GeneratedRegex(ReplaceRegexPattern3, None, Config.Regex.TimeoutMs)] 21 | private static partial Regex ReplaceRegex3(); 22 | #else 23 | private static readonly Regex ReplaceRegex1Var = new(ReplaceRegexPattern1, Compiled, Config.Regex.Timeout); 24 | private static Regex ReplaceRegex1() => ReplaceRegex1Var; 25 | 26 | private static readonly Regex ReplaceRegex2Var = new(ReplaceRegexPattern2, Compiled, Config.Regex.Timeout); 27 | private static Regex ReplaceRegex2() => ReplaceRegex2Var; 28 | 29 | private static readonly Regex ReplaceRegex3Var = new(ReplaceRegexPattern3, Compiled, Config.Regex.Timeout); 30 | private static Regex ReplaceRegex3() => ReplaceRegex3Var; 31 | #endif 32 | } 33 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Bcl/Extensions/DirectoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | using CsvLINQPadDriver.Extensions; 7 | 8 | namespace CsvLINQPadDriver.Bcl.Extensions 9 | { 10 | internal static class DirectoryExtensions 11 | { 12 | public static IEnumerable EnumerateFiles(this ICollection? exceptions, string path, string searchPattern, SearchOption searchOption) 13 | { 14 | var directories = new Queue(new[] { path }); 15 | 16 | while (directories.Any()) 17 | { 18 | var currentDirectory = directories.Dequeue(); 19 | 20 | if (searchOption == SearchOption.AllDirectories) 21 | { 22 | try 23 | { 24 | foreach (var directory in Directory.EnumerateDirectories(currentDirectory)) 25 | { 26 | directories.Enqueue(directory); 27 | } 28 | } 29 | catch (Exception e) 30 | { 31 | exceptions?.Add(e); 32 | continue; 33 | } 34 | } 35 | 36 | foreach (var file in Directory.EnumerateFiles(currentDirectory, searchPattern).SkipExceptions(exceptions)) 37 | { 38 | yield return file; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Runtime.CompilerServices; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static class Styles 13 | { 14 | public const NumberStyles Integer = NumberStyles.Integer | NumberStyles.AllowThousands; 15 | public const NumberStyles Float = NumberStyles.Float | NumberStyles.AllowThousands; 16 | public const NumberStyles Decimal = NumberStyles.Number; 17 | 18 | public const DateTimeStyles DateTime = DateTimeStyles.None; 19 | public const DateTimeStyles DateTimeOffset = DateTimeStyles.None; 20 | public const DateTimeStyles UtcDateTime = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal; 21 | public const TimeSpanStyles TimeSpan = TimeSpanStyles.None; 22 | 23 | #if NET6_0_OR_GREATER 24 | public const DateTimeStyles DateOnly = DateTimeStyles.None; 25 | public const DateTimeStyles TimeOnly = DateTimeStyles.None; 26 | #endif 27 | } 28 | 29 | public static readonly IFormatProvider DefaultFormatProvider = CultureInfo.InvariantCulture; 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | private static IFormatProvider ResolveFormatProvider(this IFormatProvider? provider) => 33 | provider ?? DefaultFormatProvider; 34 | } -------------------------------------------------------------------------------- /Deploy/buildlpx.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set version=8.15.0 4 | set fileName=CsvLINQPadDriver.%version% 5 | set ext=lpx 6 | set ext6=%ext%6 7 | set rootDir=%~dp0 8 | set rootDir=%rootDir:~0,-1% 9 | 10 | set zip="%ProgramFiles%\7-Zip\7z.exe" 11 | 12 | echo on 13 | 14 | call :pack %fileName%-net10.%ext6% net10.0-windows || goto break 15 | call :pack %fileName%-net9.%ext6% net9.0-windows || goto break 16 | call :pack %fileName%-net8.%ext6% net8.0-windows || goto break 17 | call :pack %fileName%.%ext% net481 || goto break 18 | 19 | :break 20 | 21 | @echo off 22 | 23 | @echo. 24 | 25 | if not %errorlevel%==0 echo ERROR: Packaging has failed. See log for details. 26 | 27 | exit /b %errorlevel% 28 | 29 | :pack 30 | 31 | @echo off 32 | 33 | set lpx=%rootDir%\%1 34 | set folder=%rootDir%\..\bin\Release\%2 35 | 36 | set additional=%folder%\CsvLINQPadDriver.deps.json 37 | 38 | if exist %lpx% del %lpx% 39 | if exist %folder%\Microsoft.Bcl.*.dll set additional=^ 40 | %folder%\Microsoft.Bcl.AsyncInterfaces.dll ^ 41 | %folder%\Microsoft.Bcl.HashCode.dll ^ 42 | %folder%\CsvHelper.dll ^ 43 | %folder%\Humanizer.dll ^ 44 | %folder%\Microsoft.WindowsAPICodePack.dll ^ 45 | %folder%\Microsoft.WindowsAPICodePack.Shell.dll ^ 46 | %folder%\System.Threading.Tasks.Extensions.dll ^ 47 | %folder%\UnicodeCharsetDetector.dll ^ 48 | %folder%\UtfUnknown.dll 49 | 50 | echo on 51 | 52 | %zip% a -tzip -mx=9 %lpx% ^ 53 | %rootDir%\header.xml ^ 54 | %rootDir%\..\README.md ^ 55 | %rootDir%\..\LICENSE ^ 56 | %folder%\*Connection.png ^ 57 | %folder%\CsvLINQPadDriver.dll ^ 58 | %additional% || exit /b 1 59 | 60 | @exit /b 0 61 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvRowMappingBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using static System.Linq.Expressions.Expression; 6 | 7 | namespace CsvLINQPadDriver.CodeGen; 8 | 9 | public sealed class CsvRowMappingBase 10 | where TRow : ICsvRowBase, new() 11 | { 12 | private readonly Action _propertiesInit; 13 | private readonly Action? _relationsInit; 14 | 15 | public CsvRowMappingBase(IEnumerable propertiesInfo, Action? relationsInit = null) 16 | { 17 | _relationsInit = relationsInit; 18 | 19 | var paramRow = Parameter(typeof(TRow)); 20 | var paramValues = Parameter(typeof(string[])); 21 | 22 | _propertiesInit = 23 | Lambda>( 24 | Block(propertiesInfo.Select(property => 25 | Assign( 26 | PropertyOrField(paramRow, property.PropertyName), 27 | Condition( 28 | LessThan(Constant(property.CsvColumnIndex), ArrayLength(paramValues)), 29 | ArrayIndex(paramValues, Constant(property.CsvColumnIndex)), 30 | Constant(null, typeof(string)) 31 | ) 32 | ) 33 | )), 34 | paramRow, paramValues 35 | ).Compile(); 36 | } 37 | 38 | public TRow InitRowObject(string?[] data) 39 | { 40 | var row = new TRow(); 41 | 42 | _propertiesInit(row, data); 43 | _relationsInit?.Invoke(row); 44 | 45 | return row; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/HeaderFormat.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | // ReSharper disable UnusedMember.Global 4 | 5 | namespace CsvLINQPadDriver; 6 | 7 | public enum HeaderFormat 8 | { 9 | [Description("c0|c1|c2")] 10 | c0, 11 | 12 | [Description("c1|c2|c3")] 13 | c1, 14 | 15 | [Description("c_0|c_1|c_2")] 16 | c_0, 17 | 18 | [Description("c_1|c_2|c_3")] 19 | c_1, 20 | 21 | [Description("col0|col1|col2")] 22 | col0, 23 | 24 | [Description("col1|col2|col3")] 25 | col1, 26 | 27 | [Description("col_0|col_1|col_2")] 28 | col_0, 29 | 30 | [Description("col_1|col_2|col_3")] 31 | col_1, 32 | 33 | [Description("column0|column1|column2")] 34 | column0, 35 | 36 | [Description("column1|column2|column3")] 37 | column1, 38 | 39 | [Description("column_0|column_1|column_2")] 40 | column_0, 41 | 42 | [Description("column_1|column_2|column_3")] 43 | column_1, 44 | 45 | [Description("C0|C1|C2")] 46 | C0, 47 | 48 | [Description("C1|C2|C3")] 49 | C1, 50 | 51 | [Description("C_0|C_1|C_2")] 52 | C_0, 53 | 54 | [Description("C_1|C_2|C_3")] 55 | C_1, 56 | 57 | [Description("Col0|Col1|Col2")] 58 | Col0, 59 | 60 | [Description("Col1|Col2|Col3")] 61 | Col1, 62 | 63 | [Description("Col_0|Col_1|Col_2")] 64 | Col_0, 65 | 66 | [Description("Col_1|Col_2|Col_3")] 67 | Col_1, 68 | 69 | [Description("Column0|Column1|Column2")] 70 | Column0, 71 | 72 | [Description("Column1|Column2|Column3")] 73 | Column1, 74 | 75 | [Description("Column_0|Column_1|Column_2")] 76 | Column_0, 77 | 78 | [Description("Column_1|Column_2|Column_3")] 79 | Column_1 80 | } 81 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/TableNameFormat.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | // ReSharper disable InconsistentNaming 4 | // ReSharper disable UnusedMember.Global 5 | 6 | namespace CsvLINQPadDriver; 7 | 8 | public enum TableNameFormat 9 | { 10 | [Description("t0|t1|t2")] 11 | t0, 12 | 13 | [Description("t1|t2|t3")] 14 | t1, 15 | 16 | [Description("t_0|t_1|t_2")] 17 | t_0, 18 | 19 | [Description("t_1|t_2|t_3")] 20 | t_1, 21 | 22 | [Description("tbl0|tbl1|tbl2")] 23 | tbl0, 24 | 25 | [Description("tbl1|tbl2|tbl3")] 26 | tbl1, 27 | 28 | [Description("tbl_0|tbl_1|tbl_2")] 29 | tbl_0, 30 | 31 | [Description("tbl_1|tbl_2|tbl_3")] 32 | tbl_1, 33 | 34 | [Description("table0|table1|table2")] 35 | table0, 36 | 37 | [Description("table1|table2|table3")] 38 | table1, 39 | 40 | [Description("table_0|table_1|table_2")] 41 | table_0, 42 | 43 | [Description("table_1|table_2|table_3")] 44 | table_1, 45 | 46 | [Description("T0|T1|T2")] 47 | T0, 48 | 49 | [Description("T1|T2|T3")] 50 | T1, 51 | 52 | [Description("T_0|T_1|T_2")] 53 | T_0, 54 | 55 | [Description("T_1|T_2|T_3")] 56 | T_1, 57 | 58 | [Description("Tbl0|Tbl1|Tbl2")] 59 | Tbl0, 60 | 61 | [Description("Tbl1|Tbl2|Tbl3")] 62 | Tbl1, 63 | 64 | [Description("Tbl_0|Tbl_1|Tbl_2")] 65 | Tbl_0, 66 | 67 | [Description("Tbl_1|Tbl_2|Tbl_3")] 68 | Tbl_1, 69 | 70 | [Description("Table0|Table1|Table2")] 71 | Table0, 72 | 73 | [Description("Table1|Table2|Table3")] 74 | Table1, 75 | 76 | [Description("Table_0|Table_1|Table_2")] 77 | Table_0, 78 | 79 | [Description("Table_1|Table_2|Table_3")] 80 | Table_1 81 | } 82 | -------------------------------------------------------------------------------- /CsvLINQPadDriver.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.10.34804.81 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CsvLINQPadDriverTest", "Tests\CsvLINQPadDriverTest\CsvLINQPadDriverTest.csproj", "{F9DD2249-D6AE-4320-8510-D26A2CAA2BB5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CsvLINQPadDriver", "Src\CsvLINQPadDriver\CsvLINQPadDriver.csproj", "{FA05F129-5DEB-4D69-BDC7-6FD89DFE83CC}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F9DD2249-D6AE-4320-8510-D26A2CAA2BB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F9DD2249-D6AE-4320-8510-D26A2CAA2BB5}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F9DD2249-D6AE-4320-8510-D26A2CAA2BB5}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F9DD2249-D6AE-4320-8510-D26A2CAA2BB5}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {FA05F129-5DEB-4D69-BDC7-6FD89DFE83CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {FA05F129-5DEB-4D69-BDC7-6FD89DFE83CC}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {FA05F129-5DEB-4D69-BDC7-6FD89DFE83CC}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {FA05F129-5DEB-4D69-BDC7-6FD89DFE83CC}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {9F99CBEE-76DE-45EF-B67B-4E556971551B} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/CodeGenExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | internal static partial class CodeGenExtensions 10 | { 11 | private static readonly Lazy CsCodeDomProvider = new(static () => CodeDomProvider.CreateProvider("C#")); 12 | private static readonly string[] InvalidIdentifierNames = [nameof(System), nameof(ToString), nameof(Equals), nameof(GetHashCode)]; 13 | 14 | private const string SafeChar = "_"; 15 | 16 | public static string GetSafeCodeName(this string? name) 17 | { 18 | const int maxLength = 128; 19 | 20 | var safeName = name ?? string.Empty; 21 | 22 | safeName = Replaces().Aggregate(safeName[..Math.Min(safeName.Length, maxLength)], Replace); 23 | 24 | if (string.IsNullOrWhiteSpace(safeName)) 25 | { 26 | return $"{SafeChar}empty"; 27 | } 28 | 29 | if (!char.IsLetter(safeName, 0)) 30 | { 31 | safeName = SafeChar + safeName; 32 | } 33 | 34 | return !CsCodeDomProvider.Value.IsValidIdentifier(safeName) || InvalidIdentifierNames.Contains(safeName) 35 | ? safeName + SafeChar 36 | : safeName; 37 | 38 | static string Replace(string input, (Regex Regex, string Replacement) replace) => 39 | replace.Regex.Replace(input, replace.Replacement); 40 | 41 | static IEnumerable<(Regex Regex, string Replacement)> Replaces() 42 | { 43 | yield return (ReplaceRegex1(), SafeChar); 44 | yield return (ReplaceRegex2(), SafeChar); 45 | yield return (ReplaceRegex3(), string.Empty); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Half.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER 2 | using System; 3 | using System.Globalization; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static Half? ToHalf(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 13 | { 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return Half.Parse(s, style, provider.ResolveFormatProvider()); 22 | } 23 | catch (Exception e) when (e.CanBeHandled()) 24 | { 25 | throw ConvertException.Create("Half", s, e); 26 | } 27 | } 28 | 29 | public static Half? ToHalf(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return Half.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("Half", s, e); 43 | } 44 | } 45 | 46 | public static Half? ToHalfSafe(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 47 | Half.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 48 | 49 | public static Half? ToHalfSafe(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 50 | Half.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Float.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static float? ToFloat(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return float.Parse(s, style, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("float", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static float? ToFloat(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return float.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("float", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static float? ToFloatSafe(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 48 | float.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 49 | 50 | #if NETCOREAPP 51 | public static float? ToFloatSafe(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 52 | float.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 53 | #endif 54 | } 55 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Double.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static double? ToDouble(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return double.Parse(s, style, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("double", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static double? ToDouble(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return double.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("double", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static double? ToDoubleSafe(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 48 | double.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 49 | 50 | #if NETCOREAPP 51 | public static double? ToDoubleSafe(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 52 | double.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 53 | #endif 54 | } 55 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Decimal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static decimal? ToDecimal(this string? s, NumberStyles style = Styles.Decimal, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return decimal.Parse(s, style, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("decimal", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static decimal? ToDecimal(this ReadOnlySpan s, NumberStyles style = Styles.Decimal, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return decimal.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("decimal", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static decimal? ToDecimalSafe(this string? s, NumberStyles style = Styles.Decimal, IFormatProvider? provider = null) => 48 | decimal.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 49 | 50 | #if NETCOREAPP 51 | public static decimal? ToDecimalSafe(this ReadOnlySpan s, NumberStyles style = Styles.Decimal, IFormatProvider? provider = null) => 52 | decimal.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 53 | #endif 54 | } 55 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.BigInteger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Numerics; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static BigInteger? ToBigInteger(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 13 | { 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return BigInteger.Parse(s, style, provider.ResolveFormatProvider()); 22 | } 23 | catch (Exception e) when (e.CanBeHandled()) 24 | { 25 | throw ConvertException.Create("BigInteger", s, e); 26 | } 27 | } 28 | 29 | #if NETCOREAPP 30 | public static BigInteger? ToBigInteger(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 31 | { 32 | if (s.IsEmpty) 33 | { 34 | return null; 35 | } 36 | 37 | try 38 | { 39 | return BigInteger.Parse(s, style, provider.ResolveFormatProvider()); 40 | } 41 | catch (Exception e) when (e.CanBeHandled()) 42 | { 43 | throw ConvertException.Create("BigInteger", s, e); 44 | } 45 | } 46 | #endif 47 | 48 | public static BigInteger? ToBigIntegerSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 49 | BigInteger.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 50 | 51 | #if NETCOREAPP 52 | public static BigInteger? ToBigIntegerSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 53 | BigInteger.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 54 | #endif 55 | } 56 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvTableEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace CsvLINQPadDriver.CodeGen; 6 | 7 | internal sealed class CsvTableEnumerable : CsvTableBase 8 | where TRow : ICsvRowBase, new() 9 | { 10 | public CsvTableEnumerable( 11 | bool isStringInternEnabled, 12 | StringComparer? internStringComparer, 13 | string? csvSeparator, 14 | NoBomEncoding noBomEncoding, 15 | bool allowComments, 16 | char? commentChar, 17 | char? escapeChar, 18 | char? quoteChar, 19 | bool ignoreBadData, 20 | bool autoDetectEncoding, 21 | bool ignoreBlankLines, 22 | bool doNotLockFiles, 23 | bool addHeader, 24 | HeaderDetection? headerDetection, 25 | CsvModeOptions? csvMode, 26 | WhitespaceTrimOptions? whitespaceTrimOptions, 27 | bool allowSkipLeadingRows, 28 | int skipLeadingRowsCount, 29 | string filePath, 30 | IEnumerable propertiesInfo, 31 | Action relationsInit) 32 | : base( 33 | isStringInternEnabled, 34 | internStringComparer, 35 | csvSeparator, 36 | noBomEncoding, 37 | allowComments, 38 | commentChar, 39 | escapeChar, 40 | quoteChar, 41 | ignoreBadData, 42 | autoDetectEncoding, 43 | ignoreBlankLines, 44 | doNotLockFiles, 45 | addHeader, 46 | headerDetection, 47 | csvMode, 48 | whitespaceTrimOptions, 49 | allowSkipLeadingRows, 50 | skipLeadingRowsCount, 51 | filePath, 52 | propertiesInfo, 53 | relationsInit) 54 | { 55 | } 56 | 57 | public override IEnumerator GetEnumerator() => 58 | ReadData().GetEnumerator(); 59 | 60 | public override IEnumerable WhereIndexed(Func getProperty, string propertyName, params string[] values) => 61 | this.Where(row => values.Contains(getProperty(row), StringComparer)); 62 | } 63 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/Extensions/ControlExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | using CsvLINQPadDriver.Extensions; 6 | 7 | namespace CsvLINQPadDriver.Wpf.Extensions; 8 | 9 | internal static class ControlExtensions 10 | { 11 | private static readonly char[] NewLineChars = Environment.NewLine.ToCharArray(); 12 | 13 | public static string GetLineTextAtCaretIndex(this TextBox textBox) 14 | { 15 | var caretIndex = textBox.CaretIndex; 16 | var text = textBox.Text; 17 | var textLength = text.Length; 18 | 19 | return text[ScanLeft()..ScanRight()]; 20 | 21 | int ScanLeft() 22 | { 23 | var pos = GetCurrentPos(); 24 | 25 | for (var newLineCheck = NewLineChars.Length; --newLineCheck != 0 && pos >= 0 && IsNewLine(pos); pos--) 26 | { 27 | } 28 | 29 | for (; pos >= 0; pos--) 30 | { 31 | if (IsNewLine(pos)) 32 | { 33 | return pos; 34 | } 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | int ScanRight() 41 | { 42 | var pos = GetCurrentPos(); 43 | 44 | for (; pos < textLength; pos++) 45 | { 46 | if (IsNewLine(pos)) 47 | { 48 | return pos; 49 | } 50 | } 51 | 52 | return textLength; 53 | } 54 | 55 | int GetCurrentPos() => 56 | caretIndex == 0 || caretIndex < textLength 57 | ? caretIndex 58 | : textLength - 1; 59 | 60 | bool IsNewLine(int pos) => 61 | textLength > pos && Array.IndexOf(NewLineChars, text[pos]) != -1; 62 | } 63 | 64 | public static void UpdateTargetBinding(this FrameworkElement frameworkElement, DependencyProperty dependencyProperty) => 65 | frameworkElement.GetBindingExpression(dependencyProperty)!.UpdateTarget(); 66 | 67 | public static string ReplaceHotKeyChar(this ContentControl contentControl, string? newChar = null) => 68 | ((string)contentControl.Content).ReplaceHotKeyChar(newChar); 69 | } 70 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8.15.0 4 | .NET 10 support 5 | true 6 | 7 | 8 | 9 | 1701;1702;8032;NETSDK1182;NETSDK1138 10 | 11 | 12 | 13 | true 14 | Martin Dobroucký (dobrou@gmail.com), Ivan Ivon (ivan.ivon@gmail.com) 15 | Copyright © Martin Dobroucký 2013-2014, Ivan Ivon 2021-$([System.DateTime]::Now.Year) 16 | Connection.ico 17 | app.manifest 18 | 19 | 20 | 21 | true 22 | true 23 | true 24 | latest 25 | enable 26 | ..\..\bin\$(Configuration) 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | all 41 | runtime; build; native; contentfiles; analyzers; buildtransitive 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | MSBuild:Compile 52 | 53 | 54 | -------------------------------------------------------------------------------- /Update-Version.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | 4 | Gets project version from Directory.Build.props and updates it for dependent files. 5 | 6 | .DESCRIPTION 7 | 8 | Gets project version from Directory.Build.props and updates it for dependent files. Prints version and release notes. 9 | 10 | .EXAMPLE 11 | pwsh ./Update-Version.ps1 12 | Gets project version from Directory.Build.props and updates it for dependent files. 13 | #> 14 | 15 | [CmdletBinding()] 16 | param() 17 | 18 | #requires -Version 7 19 | Set-StrictMode -Version Latest 20 | 21 | $ErrorActionPreference = 'Stop' 22 | $Verbose = $VerbosePreference -ne 'SilentlyContinue' 23 | 24 | $PSDefaultParameterValues['*:Verbose'] = $Verbose 25 | $PSDefaultParameterValues['*:ErrorAction'] = $ErrorActionPreference 26 | 27 | $ThisFolder = Split-Path (Get-Item (&{ $MyInvocation.ScriptName })) 28 | 29 | function Main 30 | { 31 | $projectFolder = Join-Path $ThisFolder Src\CsvLINQPadDriver 32 | $buildPropsFile = Join-Path $projectFolder Directory.Build.props 33 | $appManifestFile = Join-Path $projectFolder app.manifest 34 | $lpxBuildScriptFile = Join-Path $ThisFolder Deploy\buildlpx.cmd 35 | 36 | Write-Output "Reading '$buildPropsFile'..." 37 | 38 | $buildPropsXml = ([xml](Get-Content $buildPropsFile)).Project.PropertyGroup[0] 39 | $version = $buildPropsXml.Version 40 | $buildProps = [PSCustomObject]@{ 41 | Version = $version 42 | FullVersion = "$version.0" 43 | ReleaseNotes = $buildPropsXml.PackageReleaseNotes 44 | } 45 | 46 | $fieldsFormat = 47 | @{ n = 'Version'; e = { $_.Version } }, 48 | @{ n = 'Full version'; e = { $_.FullVersion } }, 49 | @{ n = 'Release notes'; e = { $_.ReleaseNotes } } 50 | 51 | Write-Output "`n$(($buildProps | Format-List $fieldsFormat | Out-String).Trim())`n" 52 | 53 | Update-Content $appManifestFile '(?<=CsvLINQPadDriver[\s\S]+\s+version=")[^"]+(?=[\s\S]+?type)',$buildProps.FullVersion 54 | Update-Content $lpxBuildScriptFile '(?<=version=)[^\r\n]+',$buildProps.Version 55 | } 56 | 57 | function Update-Content($file, $replace) 58 | { 59 | Write-Output "Updating '$file'..." 60 | 61 | (Get-Content $file -Raw) -replace $replace | Set-Content $file -NoNewline 62 | } 63 | 64 | . Main 65 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 16 | 17 | 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 | PerMonitorV2 54 | true/PM 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | permissions: 4 | contents: read 5 | pull-requests: write 6 | 7 | on: [push, pull_request, workflow_dispatch] 8 | 9 | env: 10 | NAME: CsvLINQPadDriver 11 | SLN: CsvLINQPadDriver.sln 12 | SLN5: CsvLINQPadDriver.LINQPad5.sln 13 | CONFIG: Release 14 | BIN_POSTFIX: .Bin 15 | RETENTION_DAYS: 1 16 | 17 | jobs: 18 | build: 19 | runs-on: ${{matrix.os}} 20 | 21 | strategy: 22 | matrix: 23 | os: [windows-11-arm, windows-latest] 24 | 25 | steps: 26 | 27 | # Set up 28 | 29 | - name: Setup .NET 30 | uses: actions/setup-dotnet@v5.0.1 31 | with: 32 | dotnet-version: | 33 | 8.0.x 34 | 9.0.x 35 | 10.0.x 36 | 37 | # Check out 38 | 39 | - name: Check out ${{env.SLN}} 40 | uses: actions/checkout@v6.0.1 41 | 42 | # LINQPad 43 | 44 | - name: Build ${{env.SLN}} ${{env.CONFIG}} 45 | run: dotnet build ${{env.SLN}} --configuration ${{env.CONFIG}} -p:GITHUB_ACTIONS=true 46 | 47 | - name: Test ${{env.SLN}} ${{env.CONFIG}} 48 | run: dotnet test ${{env.SLN}} --configuration ${{env.CONFIG}} --no-build --verbosity normal 49 | 50 | - name: Create ${{env.SLN}} ${{env.CONFIG}} test report 51 | uses: dorny/test-reporter@v2.2.0 52 | if: ${{success() || failure()}} 53 | with: 54 | name: tests 55 | path: '**/*.trx' 56 | reporter: dotnet-trx 57 | 58 | # LINQPad 5 59 | 60 | - name: Setup msbuild 61 | uses: microsoft/setup-msbuild@v2 62 | 63 | - name: Setup NuGet 64 | uses: NuGet/setup-nuget@v2.0.1 65 | 66 | - name: Restore ${{env.SLN5}} 67 | run: nuget restore ${{env.SLN5}} 68 | 69 | - name: Build ${{env.SLN5}} ${{env.CONFIG}} 70 | run: msbuild ${{env.SLN5}} /p:Configuration=${{env.CONFIG}} 71 | 72 | # Create LPX 73 | 74 | - name: Build LPX 75 | if: ${{matrix.os == 'windows-latest'}} 76 | run: Deploy/buildlpx.cmd 77 | 78 | # Upload artifacts 79 | 80 | - name: Publish ${{env.SLN}} ${{env.CONFIG}}, ${{env.SLN5}} ${{env.CONFIG}} 81 | uses: actions/upload-artifact@v5.0.0 82 | if: ${{matrix.os == 'windows-latest'}} 83 | with: 84 | name: ${{env.NAME}}${{env.BIN_POSTFIX}} 85 | path: | 86 | bin/${{env.CONFIG}}/${{env.NAME}}.*.*nupkg 87 | Deploy/*.lpx 88 | Deploy/*.lpx? 89 | retention-days: ${{env.RETENTION_DAYS}} 90 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Bool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static bool? ToBool(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return s.ToLong(style, provider) != 0; 21 | } 22 | catch 23 | { 24 | // ignored 25 | } 26 | 27 | try 28 | { 29 | return bool.Parse(s); 30 | } 31 | catch (Exception e) when (e.CanBeHandled()) 32 | { 33 | throw ConvertException.Create("bool", s, e); 34 | } 35 | } 36 | 37 | #if NETCOREAPP 38 | public static bool? ToBool(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 39 | { 40 | if (s.IsEmpty) 41 | { 42 | return null; 43 | } 44 | 45 | try 46 | { 47 | return s.ToLong(style, provider) != 0; 48 | } 49 | catch 50 | { 51 | // ignored 52 | } 53 | 54 | try 55 | { 56 | return bool.Parse(s); 57 | } 58 | catch (Exception e) when (e.CanBeHandled()) 59 | { 60 | throw ConvertException.Create("bool", s, e); 61 | } 62 | } 63 | #endif 64 | 65 | public static bool? ToBoolSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 66 | { 67 | var longValue = s.ToLongSafe(style, provider); 68 | 69 | return longValue.HasValue 70 | ? longValue.Value != 0 71 | : bool.TryParse(s, out var parsedValue) ? parsedValue : null; 72 | } 73 | 74 | #if NETCOREAPP 75 | public static bool? ToBoolSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 76 | { 77 | var longValue = s.ToLongSafe(style, provider); 78 | 79 | return longValue.HasValue 80 | ? longValue.Value != 0 81 | : bool.TryParse(s, out var parsedValue) ? parsedValue : null; 82 | } 83 | #endif 84 | } 85 | -------------------------------------------------------------------------------- /Src/StringEqualsBenchmark/StringEqualsBenchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using BenchmarkDotNet.Attributes; 4 | using BenchmarkDotNet.Jobs; 5 | using BenchmarkDotNet.Running; 6 | 7 | [assembly: CompilationRelaxations(CompilationRelaxations.NoStringInterning)] 8 | 9 | [SimpleJob(RuntimeMoniker.Net461)] 10 | [SimpleJob(RuntimeMoniker.Net48)] 11 | [SimpleJob(RuntimeMoniker.NetCoreApp31)] 12 | [SimpleJob(RuntimeMoniker.Net50)] 13 | [SimpleJob(RuntimeMoniker.Net60, baseline: true)] 14 | public class StringEqualsBenchmark 15 | { 16 | private const string Str = "QazxswedcvfrtGBNHYujM,kiOL>123456"; 17 | private const string StrSameLen = "QazxswedcvfrtGBNHYujM,kiOL>123455"; // Diff by last char. 18 | private const string StrDiffLen = Str + "_"; 19 | private const string StrSame = Str; 20 | 21 | [Benchmark(Description = "Equals.DiffLen")] 22 | [MethodImpl(MethodImplOptions.NoInlining)] 23 | public bool DiffLenStringEquals() => 24 | DoStringEquals(Str, StrDiffLen); 25 | 26 | [Benchmark(Description = "StringComparer.DiffLen")] 27 | [MethodImpl(MethodImplOptions.NoInlining)] 28 | public bool DiffLenStringComparerEquals() => 29 | DoStringComparerEquals(Str, StrDiffLen); 30 | 31 | [Benchmark(Description = "Equals.SameLen")] 32 | [MethodImpl(MethodImplOptions.NoInlining)] 33 | public bool SameLenStringEquals() => 34 | DoStringEquals(Str, StrSameLen); 35 | 36 | [Benchmark(Description = "StringComparer.SameLen", Baseline = true)] 37 | [MethodImpl(MethodImplOptions.NoInlining)] 38 | public bool SameLenStringComparerEquals() => 39 | DoStringComparerEquals(Str, StrSameLen); 40 | 41 | [Benchmark(Description = "Equals.Same")] 42 | [MethodImpl(MethodImplOptions.NoInlining)] 43 | public bool SameStringEquals() => 44 | DoStringEquals(Str, StrSame); 45 | 46 | [Benchmark(Description = "StringComparer.Same")] 47 | [MethodImpl(MethodImplOptions.NoInlining)] 48 | public bool SameStringComparerEquals() => 49 | DoStringComparerEquals(Str, StrSame); 50 | 51 | private static bool DoStringEquals(string s1, string s2) => 52 | string.Equals(s1, s2, StringComparison.Ordinal); 53 | 54 | private static bool DoStringComparerEquals(string s1, string s2) => 55 | StringComparer.Ordinal.Equals(s1, s2); 56 | } 57 | 58 | public class Program 59 | { 60 | public static void Main(string[] args) => 61 | BenchmarkRunner.Run(typeof(Program).Assembly); 62 | } 63 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRunTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using NUnit.Framework; 9 | 10 | using LPRun; 11 | 12 | [assembly: Parallelizable(ParallelScope.ContextMask)] 13 | 14 | namespace CsvLINQPadDriverTest; 15 | 16 | [TestFixture] 17 | public sealed partial class LPRunTests 18 | { 19 | #if GITHUB_ACTIONS 20 | private const int RetryCount = 3; 21 | 22 | private static readonly TimeSpan RetryTimeout = TimeSpan.FromSeconds(10); 23 | #endif 24 | private static readonly TimeSpan WaitTimeout = TimeSpan.FromMinutes(3); 25 | 26 | private sealed record FileEncoding(string FileName, Encoding Encoding); 27 | 28 | [OneTimeSetUp] 29 | public void Init() 30 | { 31 | const string driverFileName = "CsvLINQPadDriver"; 32 | 33 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 34 | 35 | Driver.EnsureNotInstalledViaNuGet(driverFileName); 36 | Driver.InstallWithDepsJson(driverFileName, $"{driverFileName}.dll", "Tests"); 37 | 38 | CreateFileEncodings(@"Encoding\Utf8Cp65001", Encoding.Default); 39 | CreateFileEncodings(@"Encoding\German\Cp1252", Encoding.GetEncoding("Windows-1252")); 40 | 41 | static void CreateFileEncodings(string baseFile, Encoding encoding) 42 | { 43 | var directory = Path.GetDirectoryName(baseFile); 44 | var content = File.ReadAllText(GetFilePath(baseFile), encoding); 45 | 46 | Array.ForEach(GetFileEncodings().ToArray(), WriteFiles); 47 | 48 | static IEnumerable GetFileEncodings() 49 | { 50 | yield return new("Utf16BomCp1200", Encoding.Unicode); 51 | yield return new("Utf16BomCp1201", Encoding.BigEndianUnicode); 52 | yield return new("Utf8BomCp65001", Encoding.UTF8); 53 | yield return new("Utf32Bom", Encoding.UTF32); 54 | } 55 | 56 | void WriteFiles(FileEncoding fileEncoding) => 57 | File.WriteAllText(GetFilePath(fileEncoding.FileName), content, fileEncoding.Encoding); 58 | 59 | string GetFilePath(string fileName) => 60 | Context.GetDataFullPath(Path.Combine(directory!, $"{Path.GetFileName(fileName)}.csv")); 61 | } 62 | } 63 | 64 | private static Task ExecuteAsync(string scriptFile) => 65 | Runner.ExecuteAsync(scriptFile, 66 | WaitTimeout 67 | #if GITHUB_ACTIONS 68 | , new (RetryCount, RetryTimeout) 69 | #endif 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvTableFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CsvLINQPadDriver.CodeGen; 5 | 6 | public static class CsvTableFactory 7 | { 8 | // ReSharper disable once UnusedMember.Global 9 | public static CsvTableBase CreateTable( 10 | bool isStringInternEnabled, 11 | StringComparer? internStringComparer, 12 | bool isCacheEnabled, 13 | string? csvSeparator, 14 | NoBomEncoding noBomEncoding, 15 | bool allowComments, 16 | char? commentChar, 17 | char? escapeChar, 18 | char? quoteChar, 19 | bool ignoreBadData, 20 | bool autoDetectEncoding, 21 | bool ignoreBlankLines, 22 | bool doNotLockFiles, 23 | bool addHeader, 24 | HeaderDetection? headerDetection, 25 | CsvModeOptions? csvMode, 26 | WhitespaceTrimOptions? whitespaceTrimOptions, 27 | bool allowSkipLeadingRows, 28 | int skipLeadingRowsCount, 29 | string filePath, 30 | IEnumerable propertiesInfo, 31 | Action relationsInit) 32 | where TRow : ICsvRowBase, new() => 33 | isCacheEnabled 34 | ? new CsvTableList( 35 | isStringInternEnabled, 36 | internStringComparer, 37 | csvSeparator, 38 | noBomEncoding, 39 | allowComments, 40 | commentChar, 41 | escapeChar, 42 | quoteChar, 43 | ignoreBadData, 44 | autoDetectEncoding, 45 | ignoreBlankLines, 46 | doNotLockFiles, 47 | addHeader, 48 | headerDetection, 49 | csvMode, 50 | whitespaceTrimOptions, 51 | allowSkipLeadingRows, 52 | skipLeadingRowsCount, 53 | filePath, 54 | propertiesInfo, 55 | relationsInit) 56 | : new CsvTableEnumerable( 57 | isStringInternEnabled, 58 | internStringComparer, 59 | csvSeparator, 60 | noBomEncoding, 61 | allowComments, 62 | commentChar, 63 | escapeChar, 64 | quoteChar, 65 | ignoreBadData, 66 | autoDetectEncoding, 67 | ignoreBlankLines, 68 | doNotLockFiles, 69 | addHeader, 70 | headerDetection, 71 | csvMode, 72 | whitespaceTrimOptions, 73 | allowSkipLeadingRows, 74 | skipLeadingRowsCount, 75 | filePath, 76 | propertiesInfo, 77 | relationsInit); 78 | } 79 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRunTests.FromSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | using AwesomeAssertions; 6 | 7 | using NUnit.Framework; 8 | 9 | using LPRun; 10 | 11 | namespace CsvLINQPadDriverTest; 12 | 13 | [TestFixture] 14 | public sealed partial class LPRunTests 15 | { 16 | private const int SuccessExitCode = 0; 17 | private static readonly int ErrorExitCode = new Random().Next() % 250 + 2; 18 | 19 | private static readonly string SuccessMessage = Guid.NewGuid().ToString(); 20 | private static readonly string ErrorMessage = Guid.NewGuid().ToString(); 21 | 22 | public sealed record ScriptFromSourceTestData(bool Succeeded, string Payload, bool HasErrorOutput = false, int? ExitCode = null); 23 | 24 | [Test] 25 | [TestCaseSource(nameof(ScriptFromSourceTestDataTestsData))] 26 | public async Task Execute_ScriptFromSource_Success(ScriptFromSourceTestData testData) 27 | { 28 | var scriptFile = LinqScript.FromScript(GetScript(), @""); 29 | 30 | var result = await ExecuteAsync(scriptFile); 31 | 32 | result.Success.Should().Be(testData.Succeeded); 33 | result.ExitCode.Should().Be(testData.ExitCode ?? (testData.Succeeded ? SuccessExitCode : ErrorExitCode)); 34 | 35 | if (testData.HasErrorOutput) 36 | { 37 | result.Error.Should().NotBeNullOrEmpty().And.Contain(ErrorMessage); 38 | } 39 | else 40 | { 41 | result.Error.Should().BeNullOrEmpty(); 42 | } 43 | 44 | string GetScript() 45 | { 46 | var isTaskFromResult = testData.Payload.Contains("Task.FromResult"); 47 | 48 | return 49 | $$""" 50 | using System.Threading.Tasks; 51 | 52 | {{(isTaskFromResult ? "Task" : "int")}} Main() 53 | { 54 | {{testData.Payload}}; 55 | return {{(isTaskFromResult ? "Task.FromResult(" : "(")}}{{SuccessExitCode}}); 56 | } 57 | """; 58 | } 59 | } 60 | 61 | private static IEnumerable ScriptFromSourceTestDataTestsData() 62 | { 63 | yield return new(false, $@"Console.Error.WriteLine(""{ErrorMessage}"")", true, SuccessExitCode); 64 | yield return new(true, $@"Console.WriteLine(""{SuccessMessage}"")"); 65 | yield return new(true, $"Environment.Exit({SuccessExitCode})"); 66 | yield return new(false, $"Environment.Exit({ErrorExitCode})"); 67 | yield return new(true, "//"); 68 | yield return new(false, $@"throw new Exception(""{ErrorMessage}"")", true, 1); // LPRun exit code. 69 | yield return new(false, $"return {ErrorExitCode}"); 70 | yield return new(true, $"return {SuccessExitCode}"); 71 | yield return new(false, $"return Task.FromResult({ErrorExitCode})"); 72 | yield return new(true, $"return Task.FromResult({SuccessExitCode})"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/LPRun/Templates/Generation.linq: -------------------------------------------------------------------------------- 1 | // Copy. 2 | var original = Books.First(); 3 | 4 | #if USE_RECORD_TYPE 5 | var copy = original with {}; 6 | var modifiedCopy = copy with { Title = null }; 7 | #else 8 | var copy = new RBook 9 | { 10 | Id = original.Id, 11 | Title = original.Title, 12 | AuthorId = original.AuthorId, 13 | Authors = original.Authors 14 | }; 15 | 16 | var modifiedCopy = new RBook 17 | { 18 | Id = copy.Id, 19 | Title = null, 20 | AuthorId = copy.AuthorId, 21 | Authors = copy.Authors 22 | }; 23 | #endif 24 | 25 | // Reference equality. 26 | copy.Should().NotBeSameAs(original, Reason()); 27 | copy.Should().NotBeSameAs(modifiedCopy, Reason()); 28 | 29 | // Equality. 30 | original.Should().NotBe(Authors.First(), Reason()); 31 | 32 | copy.Should().Be(original, Reason()); 33 | copy.Should().Be((object)original, Reason()); 34 | 35 | copy.Should().NotBe(modifiedCopy, Reason()); 36 | 37 | copy.Equals(null).Should().BeFalse(Reason()); 38 | 39 | copy.Equals(original).Should().BeTrue(Reason()); 40 | copy.Equals((object)original).Should().BeTrue(Reason()); 41 | 42 | (copy == original).Should().BeTrue(Reason()); 43 | (copy != original).Should().BeFalse(Reason()); 44 | 45 | (copy is null).Should().BeFalse(Reason()); 46 | (copy is not null).Should().BeTrue(Reason()); 47 | 48 | // Hash code. 49 | copy.GetHashCode().Should().Be(original.GetHashCode(), Reason()); 50 | 51 | // Stringify. 52 | copy.ToString().Should().Be(string.Format("Id : {1}{0}Title : {2}{0}AuthorId : {3}{0}", Environment.NewLine, copy.Id, copy.Title, copy.AuthorId), Reason()); 53 | 54 | // Indexers. 55 | copy[nameof(copy.Id)].Should().Be(copy.Id, Reason()); 56 | copy[nameof(copy.Title)].Should().Be(copy.Title, Reason()); 57 | copy[nameof(copy.AuthorId)].Should().Be(copy.AuthorId, Reason()); 58 | 59 | new Action(() => Console.WriteLine(copy["Title1"])) 60 | .Should() 61 | .Throw(Reason()) 62 | .WithMessage("There is no property *Title1*", Reason()); 63 | 64 | copy[0].Should().Be(copy[nameof(copy.Id)], Reason()); 65 | copy[1].Should().Be(copy[nameof(copy.Title)], Reason()); 66 | copy[2].Should().Be(copy[nameof(copy.AuthorId)], Reason()); 67 | 68 | new Action(() => Console.WriteLine(copy[3])) 69 | .Should() 70 | .Throw(Reason()) 71 | .WithMessage("There is no property *3*", Reason()); 72 | 73 | // Mutability. 74 | var oldId = copy.Id; 75 | var newId = oldId + 1; 76 | 77 | copy.Id = newId; 78 | copy.Id.Should().Be(newId, Reason()); 79 | 80 | copy[nameof(copy.Id)] = oldId; 81 | copy.Id.Should().Be(oldId, Reason()); 82 | 83 | copy[0] = newId; 84 | copy.Id.Should().Be(newId, Reason()); 85 | 86 | new Action(() => copy["Title1"] = "") 87 | .Should() 88 | .Throw(Reason()) 89 | .WithMessage("There is no property *Title1*", Reason()); 90 | 91 | new Action(() => copy[3] = "") 92 | .Should() 93 | .Throw(Reason()) 94 | .WithMessage("There is no property *3*", Reason()); 95 | 96 | // Generate single class for similar files. 97 | Authors.First().GetType() 98 | .Equals(Authors2.First().GetType()) 99 | .Should() 100 | .Be(context.UseSingleClassForSameFiles, Reason()); 101 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CsvLINQPadDriver.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows;net9.0-windows;net10.0-windows 4 | true 5 | 6 | 7 | 8 | true 9 | LINQPad CSV Driver 10 | LINQPad CSV driver 11 | $(Product) 12 | $(AssemblyTitle). 13 | 14 | 15 | 16 | true 17 | true 18 | git 19 | linqpaddriver csv tsv 20 | https://github.com/i2van/CsvLINQPadDriver 21 | MIT 22 | https://github.com/i2van/CsvLINQPadDriver 23 | NuGetIcon.png 24 | README.md 25 | 26 | 27 | 28 | true 29 | true 30 | snupkg 31 | 32 | 33 | 34 | true 35 | 36 | 37 | 38 | lib\net8.0-windows7.0;lib\net9.0-windows7.0;lib\net10.0-windows7.0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | True 54 | $(PackagesPaths) 55 | true 56 | PreserveNewest 57 | 58 | 59 | True 60 | $(PackagesPaths) 61 | true 62 | PreserveNewest 63 | 64 | 65 | True 66 | 67 | true 68 | PreserveNewest 69 | 70 | 71 | true 72 | 73 | 74 | 75 | 76 | 77 | 78 | <_Parameter1>$(AssemblyName)Test 79 | 80 | 81 | 82 | 83 | 84 | PreserveNewest 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Int128.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0_OR_GREATER 2 | using System; 3 | using System.Globalization; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static Int128? ToInt128(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 13 | { 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return Int128.Parse(s, style, provider.ResolveFormatProvider()); 22 | } 23 | catch (Exception e) when (e.CanBeHandled()) 24 | { 25 | throw ConvertException.Create("Int128", s, e); 26 | } 27 | } 28 | 29 | public static Int128? ToInt128(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return Int128.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("Int128", s, e); 43 | } 44 | } 45 | 46 | public static UInt128? ToUInt128(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 47 | { 48 | if (string.IsNullOrEmpty(s)) 49 | { 50 | return null; 51 | } 52 | 53 | try 54 | { 55 | return UInt128.Parse(s, style, provider.ResolveFormatProvider()); 56 | } 57 | catch (Exception e) when (e.CanBeHandled()) 58 | { 59 | throw ConvertException.Create("UInt128", s, e); 60 | } 61 | } 62 | 63 | public static UInt128? ToUInt128(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 64 | { 65 | if (s.IsEmpty) 66 | { 67 | return null; 68 | } 69 | 70 | try 71 | { 72 | return UInt128.Parse(s, style, provider.ResolveFormatProvider()); 73 | } 74 | catch (Exception e) when (e.CanBeHandled()) 75 | { 76 | throw ConvertException.Create("UInt128", s, e); 77 | } 78 | } 79 | 80 | public static Int128? ToInt128Safe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 81 | Int128.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 82 | 83 | public static Int128? ToInt128Safe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 84 | Int128.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 85 | 86 | public static UInt128? ToUInt128Safe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 87 | UInt128.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 88 | 89 | public static UInt128? ToUInt128Safe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 90 | UInt128.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 91 | } 92 | #endif 93 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Int.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static int? ToInt(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return int.Parse(s, style, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("int", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static int? ToInt(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return int.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("int", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static uint? ToUInt(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 48 | { 49 | if (string.IsNullOrEmpty(s)) 50 | { 51 | return null; 52 | } 53 | 54 | try 55 | { 56 | return uint.Parse(s, style, provider.ResolveFormatProvider()); 57 | } 58 | catch (Exception e) when (e.CanBeHandled()) 59 | { 60 | throw ConvertException.Create("uint", s, e); 61 | } 62 | } 63 | 64 | #if NETCOREAPP 65 | public static uint? ToUInt(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 66 | { 67 | if (s.IsEmpty) 68 | { 69 | return null; 70 | } 71 | 72 | try 73 | { 74 | return uint.Parse(s, style, provider.ResolveFormatProvider()); 75 | } 76 | catch (Exception e) when (e.CanBeHandled()) 77 | { 78 | throw ConvertException.Create("uint", s, e); 79 | } 80 | } 81 | #endif 82 | 83 | public static int? ToIntSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 84 | int.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 85 | 86 | #if NETCOREAPP 87 | public static int? ToIntSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 88 | int.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 89 | #endif 90 | 91 | public static uint? ToUIntSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 92 | uint.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 93 | 94 | #if NETCOREAPP 95 | public static uint? ToUIntSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 96 | uint.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 97 | #endif 98 | } 99 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Long.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static long? ToLong(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return long.Parse(s, style, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("long", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static long? ToLong(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return long.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("long", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static ulong? ToULong(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 48 | { 49 | if (string.IsNullOrEmpty(s)) 50 | { 51 | return null; 52 | } 53 | 54 | try 55 | { 56 | return ulong.Parse(s, style, provider.ResolveFormatProvider()); 57 | } 58 | catch (Exception e) when (e.CanBeHandled()) 59 | { 60 | throw ConvertException.Create("ulong", s, e); 61 | } 62 | } 63 | 64 | #if NETCOREAPP 65 | public static ulong? ToULong(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 66 | { 67 | if (s.IsEmpty) 68 | { 69 | return null; 70 | } 71 | 72 | try 73 | { 74 | return ulong.Parse(s, style, provider.ResolveFormatProvider()); 75 | } 76 | catch (Exception e) when (e.CanBeHandled()) 77 | { 78 | throw ConvertException.Create("ulong", s, e); 79 | } 80 | } 81 | #endif 82 | 83 | public static long? ToLongSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 84 | long.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 85 | 86 | #if NETCOREAPP 87 | public static long? ToLongSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 88 | long.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 89 | #endif 90 | 91 | public static ulong? ToULongSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 92 | ulong.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 93 | 94 | #if NETCOREAPP 95 | public static ulong? ToULongSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 96 | ulong.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 97 | #endif 98 | } 99 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Short.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static short? ToShort(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return short.Parse(s, style, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("short", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static short? ToShort(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return short.Parse(s, style, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("short", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static ushort? ToUShort(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 48 | { 49 | if (string.IsNullOrEmpty(s)) 50 | { 51 | return null; 52 | } 53 | 54 | try 55 | { 56 | return ushort.Parse(s, style, provider.ResolveFormatProvider()); 57 | } 58 | catch (Exception e) when (e.CanBeHandled()) 59 | { 60 | throw ConvertException.Create("ushort", s, e); 61 | } 62 | } 63 | 64 | #if NETCOREAPP 65 | public static ushort? ToUShort(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 66 | { 67 | if (s.IsEmpty) 68 | { 69 | return null; 70 | } 71 | 72 | try 73 | { 74 | return ushort.Parse(s, style, provider.ResolveFormatProvider()); 75 | } 76 | catch (Exception e) when (e.CanBeHandled()) 77 | { 78 | throw ConvertException.Create("ushort", s, e); 79 | } 80 | } 81 | #endif 82 | 83 | public static short? ToShortSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 84 | short.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 85 | 86 | #if NETCOREAPP 87 | public static short? ToShortSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 88 | short.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 89 | #endif 90 | 91 | public static ushort? ToUShortSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 92 | ushort.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 93 | 94 | #if NETCOREAPP 95 | public static ushort? ToUShortSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 96 | ushort.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 97 | #endif 98 | } 99 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Byte.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static sbyte? ToSByte(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return sbyte.Parse(s, style, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("sbyte", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static sbyte? ToSByte(this ReadOnlySpan s, NumberStyles style = Styles.Integer, 30 | IFormatProvider? provider = null) 31 | { 32 | if (s.IsEmpty) 33 | { 34 | return null; 35 | } 36 | 37 | try 38 | { 39 | return sbyte.Parse(s, style, provider.ResolveFormatProvider()); 40 | } 41 | catch (Exception e) when (e.CanBeHandled()) 42 | { 43 | throw ConvertException.Create("sbyte", s, e); 44 | } 45 | } 46 | #endif 47 | 48 | public static byte? ToByte(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 49 | { 50 | if (string.IsNullOrEmpty(s)) 51 | { 52 | return null; 53 | } 54 | 55 | try 56 | { 57 | return byte.Parse(s, style, provider.ResolveFormatProvider()); 58 | } 59 | catch (Exception e) when (e.CanBeHandled()) 60 | { 61 | throw ConvertException.Create("byte", s, e); 62 | } 63 | } 64 | 65 | #if NETCOREAPP 66 | public static byte? ToByte(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 67 | { 68 | if (s.IsEmpty) 69 | { 70 | return null; 71 | } 72 | 73 | try 74 | { 75 | return byte.Parse(s, style, provider.ResolveFormatProvider()); 76 | } 77 | catch (Exception e) when (e.CanBeHandled()) 78 | { 79 | throw ConvertException.Create("byte", s, e); 80 | } 81 | } 82 | #endif 83 | 84 | public static sbyte? ToSByteSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 85 | sbyte.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 86 | 87 | #if NETCOREAPP 88 | public static sbyte? ToSByteSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 89 | sbyte.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 90 | #endif 91 | 92 | public static byte? ToByteSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 93 | byte.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 94 | 95 | #if NETCOREAPP 96 | public static byte? ToByteSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 97 | byte.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 98 | #endif 99 | } 100 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/DataDisplay/CsvRowMemberProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | using CsvLINQPadDriver.CodeGen; 7 | 8 | using LINQPad; 9 | 10 | using static System.Linq.Expressions.Expression; 11 | 12 | #if NETCOREAPP 13 | using System.Collections.Immutable; 14 | #else 15 | using CsvLINQPadDriver.Bcl.Extensions; 16 | #endif 17 | 18 | namespace CsvLINQPadDriver.DataDisplay; 19 | 20 | internal sealed class CsvRowMemberProvider : ICustomMemberProvider 21 | { 22 | private static readonly Dictionary ProvidersDataCache = []; 23 | 24 | private readonly object _objectToDisplay; 25 | private readonly ProviderData _providerData; 26 | 27 | private CsvRowMemberProvider(object objectToDisplay, ProviderData providerData) 28 | { 29 | _objectToDisplay = objectToDisplay; 30 | _providerData = providerData; 31 | } 32 | 33 | public IEnumerable GetNames() => 34 | _providerData.Properties 35 | .Select(static propertyInfo => propertyInfo.Name) 36 | .Concat(_providerData.Fields.Select(static fieldInfo => fieldInfo.Name)); 37 | 38 | public IEnumerable GetTypes() => 39 | _providerData.Properties 40 | .Select(static propertyInfo => propertyInfo.PropertyType) 41 | .Concat(_providerData.Fields.Select(static fieldInfo => fieldInfo.FieldType)); 42 | 43 | public IEnumerable GetValues() => 44 | _providerData.ValuesGetter(_objectToDisplay); 45 | 46 | private sealed record ProviderData(IList Properties, IList Fields, Func ValuesGetter); 47 | 48 | private static ProviderData GetProviderData(Type objectType) 49 | { 50 | var param = Parameter(typeof(object)); 51 | var properties = objectType.GetProperties().Where(IsMemberVisible).ToImmutableList(); 52 | var fields = objectType.GetFields().Where(IsMemberVisible).ToImmutableList(); 53 | 54 | return new ProviderData( 55 | properties, 56 | fields, 57 | Lambda>( 58 | NewArrayInit(typeof(object), 59 | properties 60 | .Where(static propertyInfo => propertyInfo.GetIndexParameters().Length == 0) 61 | .Select(propertyInfo => Property(TypeAs(param, objectType), propertyInfo)) 62 | .Concat(fields.Select(fieldInfo => Field(TypeAs(param, objectType), fieldInfo)))), 63 | param) 64 | .Compile() 65 | ); 66 | 67 | static bool IsMemberVisible(MemberInfo member) => 68 | (member.MemberType & (MemberTypes.Field | MemberTypes.Property)) != 0 && 69 | !member.GetCustomAttributes(typeof(HideFromDumpAttribute), true).Any(); 70 | } 71 | 72 | public static ICustomMemberProvider? GetCsvRowMemberProvider(object objectToDisplay) 73 | { 74 | var objectType = objectToDisplay.GetType(); 75 | if (!IsSupportedType(objectType)) 76 | { 77 | return null; 78 | } 79 | 80 | if (!ProvidersDataCache.TryGetValue(objectType, out var providerData)) 81 | { 82 | providerData = GetProviderData(objectType); 83 | ProvidersDataCache.Add(objectType, providerData); 84 | } 85 | 86 | return new CsvRowMemberProvider(objectToDisplay, providerData); 87 | 88 | static bool IsSupportedType(Type objectType) => 89 | typeof(ICsvRowBase).IsAssignableFrom(objectType); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.NInt.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER 2 | using System; 3 | using System.Globalization; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static nint? ToNInt(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 13 | { 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return nint.Parse(s, style, provider.ResolveFormatProvider()); 22 | } 23 | catch (Exception e) when (e.CanBeHandled()) 24 | { 25 | throw ConvertException.Create("nint", s, e); 26 | } 27 | } 28 | 29 | #if NET6_0_OR_GREATER 30 | public static nint? ToNInt(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 31 | { 32 | if (s.IsEmpty) 33 | { 34 | return null; 35 | } 36 | 37 | try 38 | { 39 | return nint.Parse(s, style, provider.ResolveFormatProvider()); 40 | } 41 | catch (Exception e) when (e.CanBeHandled()) 42 | { 43 | throw ConvertException.Create("nint", s, e); 44 | } 45 | } 46 | #endif 47 | 48 | public static nuint? ToNUInt(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 49 | { 50 | if (string.IsNullOrEmpty(s)) 51 | { 52 | return null; 53 | } 54 | 55 | try 56 | { 57 | return nuint.Parse(s, style, provider.ResolveFormatProvider()); 58 | } 59 | catch (Exception e) when (e.CanBeHandled()) 60 | { 61 | throw ConvertException.Create("nuint", s, e); 62 | } 63 | } 64 | 65 | #if NET6_0_OR_GREATER 66 | public static nuint? ToNUInt(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) 67 | { 68 | if (s.IsEmpty) 69 | { 70 | return null; 71 | } 72 | 73 | try 74 | { 75 | return nuint.Parse(s, style, provider.ResolveFormatProvider()); 76 | } 77 | catch (Exception e) when (e.CanBeHandled()) 78 | { 79 | throw ConvertException.Create("nuint", s, e); 80 | } 81 | } 82 | #endif 83 | 84 | public static nint? ToNIntSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 85 | nint.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 86 | 87 | #if NET6_0_OR_GREATER 88 | public static nint? ToNIntSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 89 | nint.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 90 | #endif 91 | 92 | public static nuint? ToNUIntSafe(this string? s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 93 | nuint.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 94 | 95 | #if NET6_0_OR_GREATER 96 | public static nuint? ToNUIntSafe(this ReadOnlySpan s, NumberStyles style = Styles.Integer, IFormatProvider? provider = null) => 97 | nuint.TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 98 | #endif 99 | } 100 | #endif 101 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/ShellExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace CsvLINQPadDriver.Extensions; 7 | 8 | internal static partial class ShellExtensions 9 | { 10 | public sealed record ShellResult(string Message) 11 | { 12 | public ShellResult() : this(string.Empty) 13 | { 14 | } 15 | 16 | public static implicit operator string(ShellResult shellResult) => 17 | shellResult.Message; 18 | 19 | public static implicit operator bool(ShellResult shellResult) => 20 | string.IsNullOrWhiteSpace(shellResult.Message); 21 | } 22 | 23 | public static void ShellExecute(this string what) => 24 | DoShellExecute("open", what); 25 | 26 | public static ShellResult Explore(this string path, bool doSelect = true) => 27 | doSelect 28 | ? SelectFile(path) 29 | : DoShellExecute("explore", path); 30 | 31 | private static ShellResult DoShellExecute(string verb, string what) 32 | { 33 | var errorCode = ShellExecute(IntPtr.Zero, verb, what, null, null, SW_SHOW).ToInt32(); 34 | return new ShellResult(errorCode > 32 ? string.Empty : new Win32Exception(errorCode).Message); 35 | } 36 | 37 | private static ShellResult SelectFile(string filePath) 38 | { 39 | var folder = IntPtr.Zero; 40 | var file = IntPtr.Zero; 41 | 42 | var folderPath = Path.GetDirectoryName(filePath) ?? filePath; 43 | 44 | try 45 | { 46 | Marshal.ThrowExceptionForHR(SHParseDisplayName(folderPath, IntPtr.Zero, out folder, 0, out _)); 47 | Marshal.ThrowExceptionForHR(SHParseDisplayName(filePath, IntPtr.Zero, out file, 0, out _)); 48 | 49 | IntPtr[] files = [ file ]; 50 | Marshal.ThrowExceptionForHR(SHOpenFolderAndSelectItems(folder, (uint) files.Length, files, 0)); 51 | } 52 | catch (Exception exception) when (exception.CanBeHandled()) 53 | { 54 | return new ShellResult(SelectFileRegex().Replace(exception.Message, string.Empty)); 55 | } 56 | finally 57 | { 58 | Marshal.FreeCoTaskMem(file); 59 | Marshal.FreeCoTaskMem(folder); 60 | } 61 | 62 | return new ShellResult(); 63 | } 64 | 65 | // ReSharper disable IdentifierTypo 66 | // ReSharper disable InconsistentNaming 67 | private const int SW_SHOW = 5; 68 | 69 | #if NET7_0_OR_GREATER 70 | [LibraryImport("shell32.dll", EntryPoint = nameof(ShellExecute)+"W", StringMarshalling = StringMarshalling.Utf16)] private static partial 71 | #else 72 | [DllImport("shell32.dll", CharSet = CharSet.Unicode)] private static extern 73 | #endif 74 | IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string? lpParameters, string? lpDirectory, int nShowCmd); 75 | 76 | #if NET7_0_OR_GREATER 77 | [LibraryImport("shell32.dll")] private static partial 78 | #else 79 | [DllImport("shell32.dll")] private static extern 80 | #endif 81 | int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags); 82 | 83 | #if NET7_0_OR_GREATER 84 | [LibraryImport("shell32.dll", StringMarshalling = StringMarshalling.Utf16)] private static partial 85 | #else 86 | [DllImport("shell32.dll", CharSet = CharSet.Unicode)] private static extern 87 | #endif 88 | int SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, out IntPtr pidl, uint sfgaoIn, out uint psfgaoOut); 89 | // ReSharper restore InconsistentNaming 90 | // ReSharper restore IdentifierTypo 91 | } 92 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvTableList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using LINQPad; 6 | 7 | namespace CsvLINQPadDriver.CodeGen; 8 | 9 | internal sealed class CsvTableList : CsvTableBase, IList 10 | where TRow : ICsvRowBase, new() 11 | { 12 | private readonly IDictionary> _indices = new Dictionary>(); 13 | private readonly Lazy> _dataCache; 14 | 15 | public CsvTableList( 16 | bool isStringInternEnabled, 17 | StringComparer? internStringComparer, 18 | string? csvSeparator, 19 | NoBomEncoding noBomEncoding, 20 | bool allowComments, 21 | char? commentChar, 22 | char? escapeChar, 23 | char? quoteChar, 24 | bool ignoreBadData, 25 | bool autoDetectEncoding, 26 | bool ignoreBlankLines, 27 | bool doNotLockFiles, 28 | bool addHeader, 29 | HeaderDetection? headerDetection, 30 | CsvModeOptions? csvMode, 31 | WhitespaceTrimOptions? whitespaceTrimOptions, 32 | bool allowSkipLeadingRows, 33 | int skipLeadingRowsCount, 34 | string filePath, 35 | IEnumerable propertiesInfo, 36 | Action relationsInit) 37 | : base( 38 | isStringInternEnabled, 39 | internStringComparer, 40 | csvSeparator, 41 | noBomEncoding, 42 | allowComments, 43 | commentChar, 44 | escapeChar, 45 | quoteChar, 46 | ignoreBadData, 47 | autoDetectEncoding, 48 | ignoreBlankLines, 49 | doNotLockFiles, 50 | addHeader, 51 | headerDetection, 52 | csvMode, 53 | whitespaceTrimOptions, 54 | allowSkipLeadingRows, 55 | skipLeadingRowsCount, 56 | filePath, 57 | propertiesInfo, 58 | relationsInit) => 59 | _dataCache = new Lazy>(() => ReadData().Cache($"{typeof(TRow).Name}:{FilePath}")); 60 | 61 | private IList DataCache => 62 | _dataCache.Value; 63 | 64 | public override IEnumerator GetEnumerator() => 65 | DataCache.GetEnumerator(); 66 | 67 | public override IEnumerable WhereIndexed(Func getProperty, string propertyName, params string[] values) 68 | { 69 | if (!_indices.TryGetValue(propertyName, out var propertyIndex)) 70 | { 71 | propertyIndex = this.ToLookup(getProperty, StringComparer); 72 | _indices.Add(propertyName, propertyIndex); 73 | } 74 | 75 | var result = values.SelectMany(value => propertyIndex[value]); 76 | 77 | return values.Length > 1 ? result.Distinct() : result; 78 | } 79 | 80 | public void Add(TRow item) => 81 | DataCache.Add(item); 82 | 83 | public void Clear() => 84 | DataCache.Clear(); 85 | 86 | public bool Contains(TRow item) => 87 | DataCache.Contains(item); 88 | 89 | public void CopyTo(TRow[] array, int arrayIndex) => 90 | DataCache.CopyTo(array, arrayIndex); 91 | 92 | public bool Remove(TRow item) => 93 | DataCache.Remove(item); 94 | 95 | public int Count => 96 | DataCache.Count; 97 | 98 | public bool IsReadOnly => 99 | DataCache.IsReadOnly; 100 | 101 | public int IndexOf(TRow item) => 102 | DataCache.IndexOf(item); 103 | 104 | public void Insert(int index, TRow item) => 105 | DataCache.Insert(index, item); 106 | 107 | public void RemoveAt(int index) => 108 | DataCache.RemoveAt(index); 109 | 110 | public TRow this[int index] 111 | { 112 | get => DataCache[index]; 113 | set => DataCache[index] = value; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CsvDataContextDriverPropertiesBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | 5 | using CsvLINQPadDriver.Extensions; 6 | 7 | // ReSharper disable UnusedMember.Global 8 | 9 | namespace CsvLINQPadDriver; 10 | 11 | public abstract class CsvDataContextDriverPropertiesBase : ICsvDataContextDriverProperties 12 | { 13 | public abstract bool IsProduction { get; set; } 14 | public abstract bool Persist { get; set; } 15 | public abstract string Files { get; set; } 16 | public abstract FileType FileType { get; set; } 17 | public abstract FilesOrderBy FilesOrderBy { get; set; } 18 | public abstract NoBomEncoding NoBomEncoding { get; set; } 19 | public abstract bool IgnoreBadData { get; set; } 20 | public abstract bool AutoDetectEncoding { get; set; } 21 | public abstract bool AllowComments { get; set; } 22 | public abstract string CommentChars { get; set; } 23 | public abstract bool UseQuoteChar { get; set; } 24 | public abstract string QuoteChars { get; set; } 25 | public abstract bool UseEscapeChar { get; set; } 26 | public abstract string EscapeChars { get; set; } 27 | 28 | public char? CommentChar => 29 | GetFirstCharOrNull(CommentChars); 30 | 31 | public char? QuoteChar => 32 | GetFirstCharOrNull(QuoteChars); 33 | 34 | public char? EscapeChar => GetFirstCharOrNull(EscapeChars); 35 | 36 | private static char? GetFirstCharOrNull(string? s) => 37 | string.IsNullOrWhiteSpace(s) 38 | ? null 39 | : s!.TrimStart()[0]; 40 | 41 | public IEnumerable ParsedFiles => 42 | Files.GetFiles(); 43 | 44 | public abstract string CsvSeparator { get; set; } 45 | 46 | public string? SafeCsvSeparator 47 | { 48 | get 49 | { 50 | var csvSeparator = CsvSeparator; 51 | 52 | if (string.IsNullOrEmpty(csvSeparator)) 53 | { 54 | return null; 55 | } 56 | 57 | try 58 | { 59 | return Regex.Unescape(csvSeparator); 60 | } 61 | catch (Exception exception) when (exception.CanBeHandled()) 62 | { 63 | $"Falling back to CSV separator '{csvSeparator}'".WriteToLog(DebugInfo, exception); 64 | 65 | return csvSeparator; 66 | } 67 | } 68 | } 69 | 70 | public abstract bool IgnoreBlankLines { get; set; } 71 | public abstract bool AddHeader { get; set; } 72 | public abstract HeaderDetection HeaderDetection { get; set; } 73 | public abstract HeaderFormat HeaderFormat { get; set; } 74 | public abstract bool AllowSkipLeadingRows { get; set; } 75 | public abstract int SkipLeadingRowsCount { get; set; } 76 | public abstract bool AllowCsvMode { get; set; } 77 | public abstract CsvModeOptions CsvMode { get; set; } 78 | public abstract bool TrimSpaces { get; set; } 79 | public abstract WhitespaceTrimOptions WhitespaceTrimOptions { get; set; } 80 | public abstract bool UseCsvHelperSeparatorAutoDetection { get; set; } 81 | public abstract bool RenameTable { get; set; } 82 | public abstract TableNameFormat TableNameFormat { get; set; } 83 | public abstract bool UseRecordType { get; set; } 84 | public abstract bool UseSingleClassForSameFiles { get; set; } 85 | public abstract bool ShowSameFilesNonGrouped { get; set; } 86 | public abstract StringComparison StringComparison { get; set; } 87 | public abstract bool DetectRelations { get; set; } 88 | public abstract bool HideRelationsFromDump { get; set; } 89 | public abstract bool DebugInfo { get; set; } 90 | public abstract bool ValidateFilePaths { get; set; } 91 | public abstract bool IgnoreInvalidFiles { get; set; } 92 | public abstract bool DoNotLockFiles { get; set; } 93 | public abstract bool IsStringInternEnabled { get; set; } 94 | public abstract bool UseStringComparerForStringIntern { get; set; } 95 | public abstract bool IsCacheEnabled { get; set; } 96 | } 97 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CsvDataContextDriver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | using Humanizer; 6 | 7 | using LINQPad; 8 | using LINQPad.Extensibility.DataContext; 9 | 10 | using CsvLINQPadDriver.DataDisplay; 11 | using CsvLINQPadDriver.Extensions; 12 | 13 | #if NETCOREAPP 14 | using System.Collections.Immutable; 15 | #else 16 | using CsvLINQPadDriver.Bcl.Extensions; 17 | #endif 18 | 19 | namespace CsvLINQPadDriver; 20 | 21 | // ReSharper disable once ClassNeverInstantiated.Global 22 | public class CsvDataContextDriver : DynamicDataContextDriver 23 | { 24 | public override string Name => 25 | "CSV Context Driver"; 26 | 27 | public override Version Version => 28 | Assembly.GetExecutingAssembly().GetName().Version!; 29 | 30 | public override string Author => 31 | // ReSharper disable StringLiteralTypo 32 | "Martin Dobroucký (dobrou@gmail.com), Ivan Ivon (ivan.ivon@gmail.com)"; 33 | // ReSharper restore StringLiteralTypo 34 | 35 | public override bool AreRepositoriesEquivalent(IConnectionInfo c1, IConnectionInfo c2) => 36 | CsvDataContextDriverPropertiesEqualityComparer.Default.Equals( 37 | new CsvDataContextDriverProperties(c1), 38 | new CsvDataContextDriverProperties(c2)); 39 | 40 | public override string GetConnectionDescription(IConnectionInfo cxInfo) 41 | { 42 | var csvDataContextDriverProperties = new CsvDataContextDriverProperties(cxInfo); 43 | 44 | var parsedFiles = csvDataContextDriverProperties.ParsedFiles.EnumFiles().ToImmutableList(); 45 | var parsedFilesCount = parsedFiles.Count; 46 | var dateTime = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}"; 47 | var filesAndTotalSize = $"{"file".ToQuantity(parsedFilesCount)} {parsedFiles.GetHumanizedFileSize(csvDataContextDriverProperties.DebugInfo)}"; 48 | 49 | return $"{parsedFiles.GetLongestCommonPrefixPath()}{GetFilesCountString()}"; 50 | 51 | string GetFilesCountString() => 52 | parsedFilesCount switch 53 | { 54 | 0 => $"({dateTime}, no files)", 55 | _ => $" ({dateTime}, {filesAndTotalSize})" 56 | }; 57 | } 58 | 59 | public override bool ShowConnectionDialog(IConnectionInfo cxInfo, ConnectionDialogOptions dialogOptions) 60 | { 61 | var csvDataContextDriverProperties = new CsvDataContextDriverProperties(cxInfo); 62 | if (new ConnectionDialog(csvDataContextDriverProperties).ShowDialog() != true) 63 | { 64 | return false; 65 | } 66 | 67 | cxInfo.Persist = csvDataContextDriverProperties.Persist; 68 | cxInfo.IsProduction = csvDataContextDriverProperties.IsProduction; 69 | cxInfo.DisplayName = GetConnectionDescription(cxInfo); 70 | 71 | return true; 72 | } 73 | 74 | public override ICustomMemberProvider GetCustomDisplayMemberProvider(object objectToWrite) => 75 | CsvRowMemberProvider.GetCsvRowMemberProvider(objectToWrite) ?? 76 | base.GetCustomDisplayMemberProvider(objectToWrite); 77 | 78 | public override IEnumerable GetNamespacesToAdd(IConnectionInfo cxInfo) 79 | { 80 | yield return typeof(Extensions.StringExtensions).Namespace!; 81 | } 82 | 83 | public override List GetSchemaAndBuildAssembly(IConnectionInfo cxInfo, AssemblyName assemblyToBuild, ref string nameSpace, ref string typeName) => 84 | SchemaBuilder.GetSchemaAndBuildAssembly( 85 | new CsvDataContextDriverProperties(cxInfo), 86 | assemblyToBuild, 87 | ref nameSpace, 88 | ref typeName); 89 | 90 | internal static void WriteToLog(string additionalInfo, Exception? exception = null) 91 | { 92 | const string logFileName = nameof(CsvLINQPadDriver) + ".txt"; 93 | 94 | if (exception is null) 95 | { 96 | WriteToLog(additionalInfo, logFileName); 97 | } 98 | else 99 | { 100 | WriteToLog(exception, logFileName, additionalInfo); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Guid.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static Guid? ToGuid(this string? s) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return Guid.Parse(s); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("Guid", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static Guid? ToGuid(this ReadOnlySpan s) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return Guid.Parse(s); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("Guid", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static Guid? ToGuid(this string? s, string format) 48 | { 49 | if (string.IsNullOrEmpty(s)) 50 | { 51 | return null; 52 | } 53 | 54 | try 55 | { 56 | return Guid.ParseExact(s, format); 57 | } 58 | catch (Exception e) when (e.CanBeHandled()) 59 | { 60 | throw ConvertException.Create("Guid", s, e); 61 | } 62 | } 63 | 64 | #if NETCOREAPP 65 | public static Guid? ToGuid(this ReadOnlySpan s, ReadOnlySpan format) 66 | { 67 | if (s.IsEmpty) 68 | { 69 | return null; 70 | } 71 | 72 | try 73 | { 74 | return Guid.ParseExact(s, format); 75 | } 76 | catch (Exception e) when (e.CanBeHandled()) 77 | { 78 | throw ConvertException.Create("Guid", s, e); 79 | } 80 | } 81 | #endif 82 | 83 | // ReSharper disable once ParameterTypeCanBeEnumerable.Global 84 | public static Guid? ToGuid(this string? s, string[] formats) 85 | { 86 | if (string.IsNullOrEmpty(s)) 87 | { 88 | return null; 89 | } 90 | 91 | foreach (var format in formats) 92 | { 93 | if (Guid.TryParseExact(s, format, out var guid)) 94 | { 95 | return guid; 96 | } 97 | } 98 | 99 | throw ConvertException.Create("Guid", s, null); 100 | } 101 | 102 | #if NETCOREAPP 103 | public static Guid? ToGuid(this ReadOnlySpan s, string[] formats) 104 | { 105 | if (s.IsEmpty) 106 | { 107 | return null; 108 | } 109 | 110 | foreach (var format in formats) 111 | { 112 | if (Guid.TryParseExact(s, format, out var guid)) 113 | { 114 | return guid; 115 | } 116 | } 117 | 118 | throw ConvertException.Create("Guid", s, null); 119 | } 120 | #endif 121 | 122 | public static Guid? ToGuidSafe(this string? s) => 123 | Guid.TryParse(s, out var parsedValue) ? parsedValue : null; 124 | 125 | #if NETCOREAPP 126 | public static Guid? ToGuidSafe(this ReadOnlySpan s) => 127 | Guid.TryParse(s, out var parsedValue) ? parsedValue : null; 128 | #endif 129 | 130 | public static Guid? ToGuidSafe(this string? s, string format) => 131 | Guid.TryParseExact(s, format, out var parsedValue) ? parsedValue : null; 132 | 133 | #if NETCOREAPP 134 | public static Guid? ToGuidSafe(this ReadOnlySpan s, ReadOnlySpan format) => 135 | Guid.TryParseExact(s, format, out var parsedValue) ? parsedValue : null; 136 | #endif 137 | 138 | // ReSharper disable once ParameterTypeCanBeEnumerable.Global 139 | public static Guid? ToGuidSafe(this string? s, string[] formats) => 140 | formats 141 | .Select(format => Guid.TryParseExact(s, format, out var parsedValue) ? parsedValue : (Guid?)null) 142 | .FirstOrDefault(static guid => guid is not null); 143 | 144 | #if NETCOREAPP 145 | public static Guid? ToGuidSafe(this ReadOnlySpan s, string[] formats) 146 | { 147 | foreach (var format in formats) 148 | { 149 | if (Guid.TryParseExact(s, format, out var parsedValue)) 150 | { 151 | return parsedValue; 152 | } 153 | } 154 | 155 | return null; 156 | } 157 | #endif 158 | } 159 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CodeGen/CsvTableBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | using CsvLINQPadDriver.Extensions; 6 | 7 | namespace CsvLINQPadDriver.CodeGen; 8 | 9 | public class CsvTableBase 10 | { 11 | public static readonly StringComparer StringComparer = StringComparer.Ordinal; 12 | 13 | internal bool IsStringInternEnabled { get; } 14 | 15 | protected CsvTableBase(bool isStringInternEnabled) => 16 | IsStringInternEnabled = isStringInternEnabled; 17 | } 18 | 19 | public abstract class CsvTableBase : CsvTableBase, IEnumerable 20 | where TRow : ICsvRowBase, new() 21 | { 22 | private static CsvRowMappingBase? _cachedCsvRowMappingBase; 23 | 24 | private readonly string? _csvSeparator; 25 | private readonly NoBomEncoding _noBomEncoding; 26 | private readonly StringComparer? _internStringComparer; 27 | private readonly bool _allowComments; 28 | private readonly char? _commentChar; 29 | private readonly char? _escapeChar; 30 | private readonly char? _quoteChar; 31 | private readonly bool _ignoreBadData; 32 | private readonly bool _autoDetectEncoding; 33 | private readonly bool _ignoreBlankLines; 34 | private readonly bool _doNotLockFiles; 35 | private readonly bool _addHeader; 36 | private readonly HeaderDetection? _headerDetection; 37 | private readonly CsvModeOptions? _csvMode; 38 | private readonly WhitespaceTrimOptions? _whitespaceTrimOptions; 39 | private readonly bool _allowSkipLeadingRows; 40 | private readonly int _skipLeadingRowsCount; 41 | 42 | protected readonly string FilePath; 43 | 44 | protected CsvTableBase( 45 | bool isStringInternEnabled, 46 | StringComparer? internStringComparer, 47 | string? csvSeparator, 48 | NoBomEncoding noBomEncoding, 49 | bool allowComments, 50 | char? commentChar, 51 | char? escapeChar, 52 | char? quoteChar, 53 | bool ignoreBadData, 54 | bool autoDetectEncoding, 55 | bool ignoreBlankLines, 56 | bool doNotLockFiles, 57 | bool addHeader, 58 | HeaderDetection? headerDetection, 59 | CsvModeOptions? csvMode, 60 | WhitespaceTrimOptions? whitespaceTrimOptions, 61 | bool allowSkipLeadingRows, 62 | int skipLeadingRowsCount, 63 | string filePath, 64 | IEnumerable propertiesInfo, 65 | Action relationsInit) 66 | : base(isStringInternEnabled) 67 | { 68 | _internStringComparer = internStringComparer; 69 | _csvSeparator = csvSeparator; 70 | _noBomEncoding = noBomEncoding; 71 | _allowComments = allowComments; 72 | _commentChar = commentChar; 73 | _escapeChar = escapeChar; 74 | _quoteChar = quoteChar; 75 | _ignoreBadData = ignoreBadData; 76 | _autoDetectEncoding = autoDetectEncoding; 77 | _ignoreBlankLines = ignoreBlankLines; 78 | _doNotLockFiles = doNotLockFiles; 79 | _addHeader = addHeader; 80 | _headerDetection = headerDetection; 81 | _csvMode = csvMode; 82 | _whitespaceTrimOptions = whitespaceTrimOptions; 83 | _allowSkipLeadingRows = allowSkipLeadingRows; 84 | _skipLeadingRowsCount = skipLeadingRowsCount; 85 | 86 | FilePath = filePath; 87 | 88 | #pragma warning disable S3010 89 | _cachedCsvRowMappingBase ??= new CsvRowMappingBase(propertiesInfo, relationsInit); 90 | #pragma warning restore S3010 91 | } 92 | 93 | protected IEnumerable ReadData() => 94 | FilePath.CsvReadRows( 95 | _csvSeparator, 96 | IsStringInternEnabled, 97 | _internStringComparer, 98 | _noBomEncoding, 99 | _allowComments, 100 | _escapeChar, 101 | _quoteChar, 102 | _commentChar, 103 | _ignoreBadData, 104 | _autoDetectEncoding, 105 | _ignoreBlankLines, 106 | _doNotLockFiles, 107 | _addHeader, 108 | _headerDetection, 109 | _csvMode, 110 | _whitespaceTrimOptions, 111 | _allowSkipLeadingRows, 112 | _skipLeadingRowsCount, 113 | _cachedCsvRowMappingBase!); 114 | 115 | // ReSharper disable once UnusedMember.Global 116 | public abstract IEnumerable WhereIndexed(Func getProperty, string propertyName, params string[] values); 117 | 118 | public abstract IEnumerator GetEnumerator(); 119 | 120 | IEnumerator IEnumerable.GetEnumerator() => 121 | GetEnumerator(); 122 | } 123 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/CsvDataContextDriverPropertiesEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | using CsvLINQPadDriver.Extensions; 5 | 6 | namespace CsvLINQPadDriver; 7 | 8 | internal sealed class CsvDataContextDriverPropertiesEqualityComparer : IEqualityComparer 9 | { 10 | public static readonly CsvDataContextDriverPropertiesEqualityComparer Default = new(); 11 | 12 | public bool Equals(ICsvDataContextDriverProperties? x, ICsvDataContextDriverProperties? y) 13 | { 14 | if (ReferenceEquals(x, y)) return true; 15 | if (x is null || y is null) return false; 16 | 17 | return PropertiesEqual().All(Pass); 18 | 19 | IEnumerable PropertiesEqual() 20 | { 21 | yield return GetFiles(x.ParsedFiles).SequenceEqual(GetFiles(y.ParsedFiles), FileExtensions.FileNameComparer); 22 | 23 | yield return x.FilesOrderBy == y.FilesOrderBy; 24 | yield return x.NoBomEncoding == y.NoBomEncoding; 25 | yield return x.AutoDetectEncoding == y.AutoDetectEncoding; 26 | yield return x.IgnoreInvalidFiles == y.IgnoreInvalidFiles; 27 | 28 | yield return x.UseCsvHelperSeparatorAutoDetection == y.UseCsvHelperSeparatorAutoDetection; 29 | if (!x.UseCsvHelperSeparatorAutoDetection && !y.UseCsvHelperSeparatorAutoDetection) 30 | { 31 | yield return x.SafeCsvSeparator == y.SafeCsvSeparator; 32 | } 33 | 34 | yield return x.IgnoreBadData == y.IgnoreBadData; 35 | yield return x.IgnoreBlankLines == y.IgnoreBlankLines; 36 | 37 | yield return x.AllowSkipLeadingRows == y.AllowSkipLeadingRows; 38 | if (x.AllowSkipLeadingRows && y.AllowSkipLeadingRows) 39 | { 40 | yield return x.SkipLeadingRowsCount == y.SkipLeadingRowsCount; 41 | } 42 | 43 | yield return x.AllowCsvMode == y.AllowCsvMode; 44 | if (x.AllowCsvMode && y.AllowCsvMode) 45 | { 46 | yield return x.CsvMode == y.CsvMode; 47 | } 48 | 49 | yield return x.TrimSpaces == y.TrimSpaces; 50 | if (x.TrimSpaces && y.TrimSpaces) 51 | { 52 | yield return x.WhitespaceTrimOptions == y.WhitespaceTrimOptions; 53 | } 54 | 55 | yield return x.AllowComments == y.AllowComments; 56 | if (x.AllowComments && y.AllowComments) 57 | { 58 | yield return x.CommentChar == y.CommentChar; 59 | } 60 | 61 | yield return x.UseEscapeChar == y.UseEscapeChar; 62 | if (x.UseEscapeChar && y.UseEscapeChar) 63 | { 64 | yield return x.EscapeChar == y.EscapeChar; 65 | } 66 | 67 | yield return x.UseQuoteChar == y.UseQuoteChar; 68 | if (x.UseQuoteChar && y.UseQuoteChar) 69 | { 70 | yield return x.QuoteChar == y.QuoteChar; 71 | } 72 | 73 | yield return x.AddHeader == y.AddHeader; 74 | if (x.AddHeader && y.AddHeader) 75 | { 76 | yield return x.HeaderDetection == y.HeaderDetection && 77 | x.HeaderFormat == y.HeaderFormat; 78 | } 79 | 80 | yield return x.IsCacheEnabled == y.IsCacheEnabled; 81 | 82 | yield return x.IsStringInternEnabled == y.IsStringInternEnabled; 83 | if (x.IsStringInternEnabled && y.IsStringInternEnabled) 84 | { 85 | yield return x.UseStringComparerForStringIntern == y.UseStringComparerForStringIntern; 86 | } 87 | 88 | yield return x.UseRecordType == y.UseRecordType; 89 | yield return x.UseSingleClassForSameFiles == y.UseSingleClassForSameFiles; 90 | yield return x.StringComparison == y.StringComparison; 91 | 92 | yield return x.RenameTable == y.RenameTable; 93 | if (x.RenameTable && y.RenameTable) 94 | { 95 | yield return x.TableNameFormat == y.TableNameFormat; 96 | } 97 | 98 | yield return x.DetectRelations == y.DetectRelations; 99 | 100 | IEnumerable GetFiles(IEnumerable files) => 101 | x.FilesOrderBy == FilesOrderBy.None 102 | ? files 103 | : files.OrderBy(Pass, FileExtensions.FileNameComparer); 104 | } 105 | 106 | static T Pass(T t) => t; 107 | } 108 | 109 | public int GetHashCode(ICsvDataContextDriverProperties obj) => 110 | obj.GetHashCode(); 111 | } 112 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Bcl/Microsoft/System.Private.CoreLib/System/Range.cs: -------------------------------------------------------------------------------- 1 | // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Range.cs 2 | 3 | // Licensed to the .NET Foundation under one or more agreements. 4 | // The .NET Foundation licenses this file to you under the MIT license. 5 | 6 | // ReSharper disable All 7 | 8 | using System.Runtime.CompilerServices; 9 | 10 | namespace System 11 | { 12 | /// Represent a range has start and end indexes. 13 | /// 14 | /// Range is used by the C# compiler to support the range syntax. 15 | /// 16 | /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; 17 | /// int[] subArray1 = someArray[0..2]; // { 1, 2 } 18 | /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } 19 | /// 20 | /// 21 | internal readonly struct Range : IEquatable 22 | { 23 | /// Represent the inclusive start index of the Range. 24 | public Index Start { get; } 25 | 26 | /// Represent the exclusive end index of the Range. 27 | public Index End { get; } 28 | 29 | /// Construct a Range object using the start and end indexes. 30 | /// Represent the inclusive start index of the range. 31 | /// Represent the exclusive end index of the range. 32 | public Range(Index start, Index end) 33 | { 34 | Start = start; 35 | End = end; 36 | } 37 | 38 | /// Indicates whether the current Range object is equal to another object of the same type. 39 | /// An object to compare with this object 40 | public override bool Equals(object? value) => 41 | value is Range r && 42 | r.Start.Equals(Start) && 43 | r.End.Equals(End); 44 | 45 | /// Indicates whether the current Range object is equal to another Range object. 46 | /// An object to compare with this object 47 | public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); 48 | 49 | /// Returns the hash code for this instance. 50 | public override int GetHashCode() 51 | { 52 | return HashCode.Combine(Start.GetHashCode(), End.GetHashCode()); 53 | } 54 | 55 | /// Converts the value of the current Range object to its equivalent string representation. 56 | public override string ToString() 57 | { 58 | return Start.ToString() + ".." + End.ToString(); 59 | } 60 | 61 | /// Create a Range object starting from start index to the end of the collection. 62 | public static Range StartAt(Index start) => new Range(start, Index.End); 63 | 64 | /// Create a Range object starting from first element in the collection to the end Index. 65 | public static Range EndAt(Index end) => new Range(Index.Start, end); 66 | 67 | /// Create a Range object starting from first element to the end. 68 | public static Range All => new Range(Index.Start, Index.End); 69 | 70 | /// Calculate the start offset and length of range object using a collection length. 71 | /// The length of the collection that the range will be used with. length has to be a positive value. 72 | /// 73 | /// For performance reason, we don't validate the input length parameter against negative values. 74 | /// It is expected Range will be used with collections which always have non negative length/count. 75 | /// We validate the range is inside the length scope though. 76 | /// 77 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 78 | public (int Offset, int Length) GetOffsetAndLength(int length) 79 | { 80 | int start; 81 | Index startIndex = Start; 82 | if (startIndex.IsFromEnd) 83 | start = length - startIndex.Value; 84 | else 85 | start = startIndex.Value; 86 | 87 | int end; 88 | Index endIndex = End; 89 | if (endIndex.IsFromEnd) 90 | end = length - endIndex.Value; 91 | else 92 | end = endIndex.Value; 93 | 94 | if ((uint)end > (uint)length || (uint)start > (uint)end) 95 | { 96 | throw new ArgumentOutOfRangeException(nameof(length)); 97 | } 98 | 99 | return (start, end - start); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Tests/CsvLINQPadDriverTest/CsvLINQPadDriverTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows;net9.0-windows;net10.0-windows 4 | true 5 | true 6 | latest 7 | enable 8 | NETSDK1138 9 | false 10 | ..\..\bin\$(Configuration)\Tests 11 | 12 | 13 | 14 | $(DefineConstants);GITHUB_ACTIONS 15 | 16 | 17 | 18 | trx%3bLogFileName=$(MSBuildProjectName).trx 19 | $(MSBuildThisFileDirectory)/TestResults/$(TargetFramework) 20 | 21 | 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | PreserveNewest 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | PreserveNewest 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | PreserveNewest 43 | 44 | 45 | PreserveNewest 46 | 47 | 48 | PreserveNewest 49 | 50 | 51 | PreserveNewest 52 | 53 | 54 | PreserveNewest 55 | 56 | 57 | PreserveNewest 58 | 59 | 60 | PreserveNewest 61 | 62 | 63 | PreserveNewest 64 | 65 | 66 | PreserveNewest 67 | 68 | 69 | PreserveNewest 70 | 71 | 72 | PreserveNewest 73 | 74 | 75 | PreserveNewest 76 | 77 | 78 | PreserveNewest 79 | 80 | 81 | PreserveNewest 82 | 83 | 84 | PreserveNewest 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | all 96 | runtime; build; native; contentfiles; analyzers; buildtransitive 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.TimeSpan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver.Extensions; 8 | 9 | public static partial class StringExtensions 10 | { 11 | public static TimeSpan? ToTimeSpan(this string? s, IFormatProvider? provider = null) 12 | { 13 | if (string.IsNullOrEmpty(s)) 14 | { 15 | return null; 16 | } 17 | 18 | try 19 | { 20 | return TimeSpan.Parse(s, provider.ResolveFormatProvider()); 21 | } 22 | catch (Exception e) when (e.CanBeHandled()) 23 | { 24 | throw ConvertException.Create("TimeSpan", s, e); 25 | } 26 | } 27 | 28 | #if NETCOREAPP 29 | public static TimeSpan? ToTimeSpan(this ReadOnlySpan s, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return TimeSpan.Parse(s, provider.ResolveFormatProvider()); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("TimeSpan", s, e); 43 | } 44 | } 45 | #endif 46 | 47 | public static TimeSpan? ToTimeSpan(this string? s, string format, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) 48 | { 49 | if (string.IsNullOrEmpty(s)) 50 | { 51 | return null; 52 | } 53 | 54 | try 55 | { 56 | return TimeSpan.ParseExact(s, format, provider.ResolveFormatProvider(), style); 57 | } 58 | catch (Exception e) when (e.CanBeHandled()) 59 | { 60 | throw ConvertException.Create("TimeSpan", s, e); 61 | } 62 | } 63 | 64 | #if NETCOREAPP 65 | public static TimeSpan? ToTimeSpan(this ReadOnlySpan s, ReadOnlySpan format, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) 66 | { 67 | if (s.IsEmpty) 68 | { 69 | return null; 70 | } 71 | 72 | try 73 | { 74 | return TimeSpan.ParseExact(s, format, provider.ResolveFormatProvider(), style); 75 | } 76 | catch (Exception e) when (e.CanBeHandled()) 77 | { 78 | throw ConvertException.Create("TimeSpan", s, e); 79 | } 80 | } 81 | #endif 82 | 83 | public static TimeSpan? ToTimeSpan(this string? s, string[] formats, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) 84 | { 85 | if (string.IsNullOrEmpty(s)) 86 | { 87 | return null; 88 | } 89 | 90 | try 91 | { 92 | return TimeSpan.ParseExact(s, formats, provider.ResolveFormatProvider(), style); 93 | } 94 | catch (Exception e) when (e.CanBeHandled()) 95 | { 96 | throw ConvertException.Create("TimeSpan", s, e); 97 | } 98 | } 99 | 100 | #if NETCOREAPP 101 | public static TimeSpan? ToTimeSpan(this ReadOnlySpan s, string[] formats, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) 102 | { 103 | if (s.IsEmpty) 104 | { 105 | return null; 106 | } 107 | 108 | try 109 | { 110 | return TimeSpan.ParseExact(s, formats, provider.ResolveFormatProvider(), style); 111 | } 112 | catch (Exception e) when (e.CanBeHandled()) 113 | { 114 | throw ConvertException.Create("TimeSpan", s, e); 115 | } 116 | } 117 | #endif 118 | 119 | public static TimeSpan? ToTimeSpanSafe(this string? s, IFormatProvider? provider = null) => 120 | TimeSpan.TryParse(s, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 121 | 122 | #if NETCOREAPP 123 | public static TimeSpan? ToTimeSpanSafe(this ReadOnlySpan s, IFormatProvider? provider = null) => 124 | TimeSpan.TryParse(s, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 125 | #endif 126 | 127 | public static TimeSpan? ToTimeSpanSafe(this string? s, string format, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) => 128 | TimeSpan.TryParseExact(s, format, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 129 | 130 | #if NETCOREAPP 131 | public static TimeSpan? ToTimeSpanSafe(this ReadOnlySpan s, ReadOnlySpan format, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) => 132 | TimeSpan.TryParseExact(s, format, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 133 | #endif 134 | 135 | public static TimeSpan? ToTimeSpanSafe(this string? s, string[] formats, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) => 136 | TimeSpan.TryParseExact(s, formats, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 137 | 138 | #if NETCOREAPP 139 | public static TimeSpan? ToTimeSpanSafe(this ReadOnlySpan s, string[] formats, TimeSpanStyles style = Styles.TimeSpan, IFormatProvider? provider = null) => 140 | TimeSpan.TryParseExact(s, formats, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 141 | #endif 142 | } 143 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.DateOnly.cs: -------------------------------------------------------------------------------- 1 | #if NET6_0_OR_GREATER 2 | using System; 3 | using System.Globalization; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static DateOnly? ToDateOnly(this string? s, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) 13 | { 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return DateOnly.Parse(s, provider.ResolveFormatProvider(), style); 22 | } 23 | catch (Exception e) when (e.CanBeHandled()) 24 | { 25 | throw ConvertException.Create("DateOnly", s, e); 26 | } 27 | } 28 | 29 | public static DateOnly? ToDateOnly(this ReadOnlySpan s, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return DateOnly.Parse(s, provider.ResolveFormatProvider(), style); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("DateOnly", s, e); 43 | } 44 | } 45 | 46 | public static DateOnly? ToDateOnly(this string? s, string format, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) 47 | { 48 | if (string.IsNullOrEmpty(s)) 49 | { 50 | return null; 51 | } 52 | 53 | try 54 | { 55 | return DateOnly.ParseExact(s, format, provider.ResolveFormatProvider(), style); 56 | } 57 | catch (Exception e) when (e.CanBeHandled()) 58 | { 59 | throw ConvertException.Create("DateOnly", s, e); 60 | } 61 | } 62 | 63 | public static DateOnly? ToDateOnly(this ReadOnlySpan s, ReadOnlySpan format, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) 64 | { 65 | if (s.IsEmpty) 66 | { 67 | return null; 68 | } 69 | 70 | try 71 | { 72 | return DateOnly.ParseExact(s, format, provider.ResolveFormatProvider(), style); 73 | } 74 | catch (Exception e) when (e.CanBeHandled()) 75 | { 76 | throw ConvertException.Create("DateOnly", s, e); 77 | } 78 | } 79 | 80 | public static DateOnly? ToDateOnly(this string? s, string[] formats, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) 81 | { 82 | if (string.IsNullOrEmpty(s)) 83 | { 84 | return null; 85 | } 86 | 87 | try 88 | { 89 | return DateOnly.ParseExact(s, formats, provider.ResolveFormatProvider(), style); 90 | } 91 | catch (Exception e) when (e.CanBeHandled()) 92 | { 93 | throw ConvertException.Create("DateOnly", s, e); 94 | } 95 | } 96 | 97 | public static DateOnly? ToDateOnly(this ReadOnlySpan s, string[] formats, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) 98 | { 99 | if (s.IsEmpty) 100 | { 101 | return null; 102 | } 103 | 104 | try 105 | { 106 | return DateOnly.ParseExact(s, formats, provider.ResolveFormatProvider(), style); 107 | } 108 | catch (Exception e) when (e.CanBeHandled()) 109 | { 110 | throw ConvertException.Create("DateOnly", s, e); 111 | } 112 | } 113 | 114 | public static DateOnly? ToDateOnlySafe(this string? s, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) => 115 | DateOnly.TryParse(s, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 116 | 117 | public static DateOnly? ToDateOnlySafe(this ReadOnlySpan s, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) => 118 | DateOnly.TryParse(s, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 119 | 120 | public static DateOnly? ToDateOnlySafe(this string? s, string format, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) => 121 | DateOnly.TryParseExact(s, format, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 122 | 123 | public static DateOnly? ToDateOnlySafe(this ReadOnlySpan s, ReadOnlySpan format, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) => 124 | DateOnly.TryParseExact(s, format, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 125 | 126 | public static DateOnly? ToDateOnlySafe(this string? s, string[] formats, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) => 127 | DateOnly.TryParseExact(s, formats, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 128 | 129 | public static DateOnly? ToDateOnlySafe(this ReadOnlySpan s, string[] formats, DateTimeStyles style = Styles.DateOnly, IFormatProvider? provider = null) => 130 | DateOnly.TryParseExact(s, formats, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 131 | } 132 | #endif 133 | 134 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.TimeOnly.cs: -------------------------------------------------------------------------------- 1 | #if NET6_0_OR_GREATER 2 | using System; 3 | using System.Globalization; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static TimeOnly? ToTimeOnly(this string? s, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) 13 | { 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return TimeOnly.Parse(s, provider.ResolveFormatProvider(), style); 22 | } 23 | catch (Exception e) when (e.CanBeHandled()) 24 | { 25 | throw ConvertException.Create("TimeOnly", s, e); 26 | } 27 | } 28 | 29 | public static TimeOnly? ToTimeOnly(this ReadOnlySpan s, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) 30 | { 31 | if (s.IsEmpty) 32 | { 33 | return null; 34 | } 35 | 36 | try 37 | { 38 | return TimeOnly.Parse(s, provider.ResolveFormatProvider(), style); 39 | } 40 | catch (Exception e) when (e.CanBeHandled()) 41 | { 42 | throw ConvertException.Create("TimeOnly", s, e); 43 | } 44 | } 45 | 46 | public static TimeOnly? ToTimeOnly(this string? s, string format, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) 47 | { 48 | if (string.IsNullOrEmpty(s)) 49 | { 50 | return null; 51 | } 52 | 53 | try 54 | { 55 | return TimeOnly.ParseExact(s, format, provider.ResolveFormatProvider(), style); 56 | 57 | } 58 | catch (Exception e) when (e.CanBeHandled()) 59 | { 60 | throw ConvertException.Create("TimeOnly", s, e); 61 | } 62 | } 63 | 64 | public static TimeOnly? ToTimeOnly(this ReadOnlySpan s, ReadOnlySpan format, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) 65 | { 66 | if (s.IsEmpty) 67 | { 68 | return null; 69 | } 70 | 71 | try 72 | { 73 | return TimeOnly.ParseExact(s, format, provider.ResolveFormatProvider(), style); 74 | 75 | } 76 | catch (Exception e) when (e.CanBeHandled()) 77 | { 78 | throw ConvertException.Create("TimeOnly", s, e); 79 | } 80 | } 81 | 82 | public static TimeOnly? ToTimeOnly(this string? s, string[] formats, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) 83 | { 84 | if (string.IsNullOrEmpty(s)) 85 | { 86 | return null; 87 | } 88 | 89 | try 90 | { 91 | return TimeOnly.ParseExact(s, formats, provider.ResolveFormatProvider(), style); 92 | } 93 | catch (Exception e) when (e.CanBeHandled()) 94 | { 95 | throw ConvertException.Create("TimeOnly", s, e); 96 | } 97 | } 98 | 99 | public static TimeOnly? ToTimeOnly(this ReadOnlySpan s, string[] formats, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) 100 | { 101 | if (s.IsEmpty) 102 | { 103 | return null; 104 | } 105 | 106 | try 107 | { 108 | return TimeOnly.ParseExact(s, formats, provider.ResolveFormatProvider(), style); 109 | } 110 | catch (Exception e) when (e.CanBeHandled()) 111 | { 112 | throw ConvertException.Create("TimeOnly", s, e); 113 | } 114 | } 115 | 116 | public static TimeOnly? ToTimeOnlySafe(this string? s, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) => 117 | TimeOnly.TryParse(s, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 118 | 119 | public static TimeOnly? ToTimeOnlySafe(this ReadOnlySpan s, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) => 120 | TimeOnly.TryParse(s, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 121 | 122 | public static TimeOnly? ToTimeOnlySafe(this string? s, string format, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) => 123 | TimeOnly.TryParseExact(s, format, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 124 | 125 | public static TimeOnly? ToTimeOnlySafe(this ReadOnlySpan s, ReadOnlySpan format, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) => 126 | TimeOnly.TryParseExact(s, format, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 127 | 128 | public static TimeOnly? ToTimeOnlySafe(this string? s, string[] formats, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) => 129 | TimeOnly.TryParseExact(s, formats, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 130 | 131 | public static TimeOnly? ToTimeOnlySafe(this ReadOnlySpan s, string[] formats, DateTimeStyles style = Styles.TimeOnly, IFormatProvider? provider = null) => 132 | TimeOnly.TryParseExact(s, formats, provider.ResolveFormatProvider(), style, out var parsedValue) ? parsedValue : null; 133 | } 134 | #endif 135 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Wpf/Extensions/DialogExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows; 4 | using System.Windows.Interop; 5 | 6 | using Microsoft.WindowsAPICodePack.Dialogs; 7 | 8 | using CsvLINQPadDriver.Extensions; 9 | 10 | using OpenFileDialog = Microsoft.Win32.OpenFileDialog; 11 | 12 | namespace CsvLINQPadDriver.Wpf.Extensions; 13 | 14 | internal static class DialogExtensions 15 | { 16 | public static bool TryOpenFiles(this Window owner, string title, string filter, string defaultExt, out string[] files, int filterIndex = 1) 17 | { 18 | var openFileDialog = new OpenFileDialog 19 | { 20 | Title = title, 21 | Multiselect = true, 22 | ValidateNames = true, 23 | CheckPathExists = true, 24 | CheckFileExists = true, 25 | DefaultExt = string.IsNullOrEmpty(defaultExt) ? string.Empty : $".{defaultExt.TrimStart('.')}", 26 | Filter = filter, 27 | FilterIndex = filterIndex, 28 | AddExtension = true 29 | }; 30 | 31 | var result = openFileDialog.ShowDialog(owner) == true; 32 | 33 | files = result 34 | ? openFileDialog.FileNames 35 | : []; 36 | 37 | return result; 38 | } 39 | 40 | public static bool TryBrowseForFolders(this Window owner, string title, out string[] folders) 41 | { 42 | using var commonOpenFileDialog = new CommonOpenFileDialog 43 | { 44 | Title = title, 45 | IsFolderPicker = true, 46 | Multiselect = true, 47 | EnsurePathExists = true, 48 | EnsureValidNames = true 49 | }; 50 | 51 | var result = commonOpenFileDialog.ShowDialog(owner) == CommonFileDialogResult.Ok; 52 | 53 | folders = result 54 | ? commonOpenFileDialog.FileNames.ToArray() 55 | : []; 56 | 57 | return result; 58 | } 59 | 60 | public static void ShowWarning(this Window owner, string text) => 61 | MessageBox.Show(owner, text.AppendDot(), owner.Title, MessageBoxButton.OK, MessageBoxImage.Exclamation); 62 | 63 | public static bool? ShowYesNoDialog( 64 | this Window owner, 65 | string instructionText, 66 | string text, 67 | string yesInstructions, 68 | string noInstructions, 69 | bool yesIsDefault = true 70 | ) 71 | { 72 | var footerCheckBoxChecked = false; 73 | return owner.ShowYesNoDialog(instructionText, text, yesInstructions, noInstructions, null, null, null, ref footerCheckBoxChecked, yesIsDefault); 74 | } 75 | 76 | public static bool? ShowYesNoDialog( 77 | this Window owner, 78 | string instructionText, 79 | string text, 80 | string yesInstructions, 81 | string noInstructions, 82 | string? detailsLabel, 83 | string? detailsExpandedText, 84 | string? footerCheckBoxText, 85 | ref bool footerCheckBoxChecked, 86 | bool yesIsDefault = true 87 | ) 88 | { 89 | var hasFooterCheckBox = footerCheckBoxText is not null; 90 | var hasDetailsLabel = detailsLabel is not null; 91 | 92 | using var taskDialog = new TaskDialog 93 | { 94 | OwnerWindowHandle = new WindowInteropHelper(owner).Handle, 95 | StartupLocation = TaskDialogStartupLocation.CenterOwner, 96 | Cancelable = true, 97 | 98 | Caption = owner.Title, 99 | InstructionText = instructionText, 100 | Text = text.AppendDot(), 101 | Icon = TaskDialogStandardIcon.Warning, 102 | 103 | Controls = 104 | { 105 | CreateTaskDialogCommandLink("&Yes", yesInstructions.AppendDot(), TaskDialogResult.Yes, yesIsDefault), 106 | CreateTaskDialogCommandLink("&No", noInstructions.AppendDot(), TaskDialogResult.No, !yesIsDefault) 107 | } 108 | }; 109 | 110 | if (hasFooterCheckBox) 111 | { 112 | taskDialog.FooterCheckBoxText = footerCheckBoxText; 113 | taskDialog.FooterCheckBoxChecked = footerCheckBoxChecked; 114 | } 115 | 116 | if (hasDetailsLabel) 117 | { 118 | taskDialog.DetailsCollapsedLabel = $"S&how {detailsLabel}"; 119 | taskDialog.DetailsExpandedLabel = $"&Hide {detailsLabel}"; 120 | taskDialog.DetailsExpandedText = detailsExpandedText; 121 | } 122 | 123 | bool? result = taskDialog.Show() switch 124 | { 125 | TaskDialogResult.Yes => true, 126 | TaskDialogResult.No => false, 127 | _ => null 128 | }; 129 | 130 | footerCheckBoxChecked = taskDialog.FooterCheckBoxChecked == true; 131 | 132 | foreach (var taskDialogCommandLink in taskDialog.Controls.OfType()) 133 | { 134 | taskDialogCommandLink.Click -= TaskDialogCommandLinkClicked!; 135 | } 136 | 137 | return result; 138 | 139 | static TaskDialogCommandLink CreateTaskDialogCommandLink(string text, string instructions, TaskDialogResult taskDialogResult, bool isDefault = false) 140 | { 141 | var taskDialogCommandLink = new TaskDialogCommandLink(taskDialogResult.ToString(), text, instructions) 142 | { 143 | Default = isDefault 144 | }; 145 | 146 | taskDialogCommandLink.Click += TaskDialogCommandLinkClicked!; 147 | 148 | return taskDialogCommandLink; 149 | } 150 | 151 | static void TaskDialogCommandLinkClicked(object sender, EventArgs e) 152 | { 153 | var taskDialogCommandLink = (TaskDialogCommandLink)sender; 154 | ((TaskDialog)taskDialogCommandLink.HostingDialog).Close( 155 | #if NETCOREAPP 156 | Enum.Parse( 157 | #else 158 | (TaskDialogResult)Enum.Parse(typeof(TaskDialogResult), 159 | #endif 160 | taskDialogCommandLink.Name)); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/FileExtensions.Regex.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | using static System.Text.RegularExpressions.RegexOptions; 4 | 5 | namespace CsvLINQPadDriver.Extensions; 6 | 7 | internal static partial class FileExtensions 8 | { 9 | private const string GetFilesRegexPattern = @"[\r\n]+"; 10 | private const string MatchDeduceIsFileOrFolderRegexPattern = @"[\\/]$"; 11 | private const string DeduceIsFileOrFolderRegexPattern = "[?*]"; 12 | 13 | private const RegexOptions HeaderDetectionRegexOptions = ExplicitCapture | Compiled; 14 | 15 | private const string HeaderDetectionHasHeaderRegexPattern = "."; 16 | private const string HeaderDetectionAllLettersNumbersPunctuationRegexPattern = @"^(@|\p{L}\p{M}*)(\p{L}\p{M}*|[0-9_\-. ])*$"; 17 | private const string HeaderDetectionAllLettersNumbersRegexPattern = @"^\p{L}\p{M}*(\p{L}\p{M}*|[0-9])*$"; 18 | private const string HeaderDetectionAllLettersRegexPattern = @"^(\p{L}\p{M}*)+$"; 19 | private const string HeaderDetectionLatinLettersNumbersPunctuationRegexPattern = @"^@?[a-zA-Z][a-zA-Z0-9_\-. ]*$"; 20 | private const string HeaderDetectionLatinLettersNumbersRegexPattern = "^[a-zA-Z][a-zA-Z0-9]*$"; 21 | private const string HeaderDetectionLatinLettersRegexPattern = "^[a-zA-Z]+$"; 22 | 23 | #if NET7_0_OR_GREATER 24 | [GeneratedRegex(GetFilesRegexPattern, None, Config.Regex.TimeoutMs)] 25 | private static partial Regex GetFilesRegex(); 26 | 27 | [GeneratedRegex(MatchDeduceIsFileOrFolderRegexPattern, None, Config.Regex.TimeoutMs)] 28 | private static partial Regex MatchDeduceIsFileOrFolderRegex(); 29 | 30 | [GeneratedRegex(DeduceIsFileOrFolderRegexPattern, None, Config.Regex.TimeoutMs)] 31 | private static partial Regex DeduceIsFileOrFolderRegex(); 32 | 33 | [GeneratedRegex(HeaderDetectionHasHeaderRegexPattern, HeaderDetectionRegexOptions, Config.Regex.TimeoutMs)] 34 | private static partial Regex HeaderDetectionHasHeaderRegex(); 35 | 36 | [GeneratedRegex(HeaderDetectionAllLettersNumbersPunctuationRegexPattern, HeaderDetectionRegexOptions, Config.Regex.TimeoutMs)] 37 | private static partial Regex HeaderDetectionAllLettersNumbersPunctuationRegex(); 38 | 39 | [GeneratedRegex(HeaderDetectionAllLettersNumbersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.TimeoutMs)] 40 | private static partial Regex HeaderDetectionAllLettersNumbersRegex(); 41 | 42 | [GeneratedRegex(HeaderDetectionAllLettersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.TimeoutMs)] 43 | private static partial Regex HeaderDetectionAllLettersRegex(); 44 | 45 | [GeneratedRegex(HeaderDetectionLatinLettersNumbersPunctuationRegexPattern, HeaderDetectionRegexOptions, Config.Regex.TimeoutMs)] 46 | private static partial Regex HeaderDetectionLatinLettersNumbersPunctuationRegex(); 47 | 48 | [GeneratedRegex(HeaderDetectionLatinLettersNumbersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.TimeoutMs)] 49 | private static partial Regex HeaderDetectionLatinLettersNumbersRegex(); 50 | 51 | [GeneratedRegex(HeaderDetectionLatinLettersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.TimeoutMs)] 52 | private static partial Regex HeaderDetectionLatinLettersRegex(); 53 | #else 54 | private static readonly Regex GetFilesRegexVar = new(GetFilesRegexPattern, Compiled, Config.Regex.Timeout); 55 | private static Regex GetFilesRegex() => GetFilesRegexVar; 56 | 57 | private static readonly Regex MatchDeduceIsFileOrFolderRegexVar = new(MatchDeduceIsFileOrFolderRegexPattern, Compiled, Config.Regex.Timeout); 58 | private static Regex MatchDeduceIsFileOrFolderRegex() => MatchDeduceIsFileOrFolderRegexVar; 59 | 60 | private static readonly Regex DeduceIsFileOrFolderRegexVar = new(DeduceIsFileOrFolderRegexPattern, Compiled, Config.Regex.Timeout); 61 | private static Regex DeduceIsFileOrFolderRegex() => DeduceIsFileOrFolderRegexVar; 62 | 63 | private static readonly Regex HeaderDetectionHasHeaderRegexVar = new(HeaderDetectionHasHeaderRegexPattern, HeaderDetectionRegexOptions, Config.Regex.Timeout); 64 | private static Regex HeaderDetectionHasHeaderRegex() => HeaderDetectionHasHeaderRegexVar; 65 | 66 | private static readonly Regex HeaderDetectionAllLettersNumbersPunctuationRegexVar = new(HeaderDetectionAllLettersNumbersPunctuationRegexPattern, HeaderDetectionRegexOptions, Config.Regex.Timeout); 67 | private static Regex HeaderDetectionAllLettersNumbersPunctuationRegex() => HeaderDetectionAllLettersNumbersPunctuationRegexVar; 68 | 69 | private static readonly Regex HeaderDetectionAllLettersNumbersRegexVar = new(HeaderDetectionAllLettersNumbersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.Timeout); 70 | private static Regex HeaderDetectionAllLettersNumbersRegex() => HeaderDetectionAllLettersNumbersRegexVar; 71 | 72 | private static readonly Regex HeaderDetectionAllLettersRegexVar = new(HeaderDetectionAllLettersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.Timeout); 73 | private static Regex HeaderDetectionAllLettersRegex() => HeaderDetectionAllLettersRegexVar; 74 | 75 | private static readonly Regex HeaderDetectionLatinLettersNumbersPunctuationRegexVar = new(HeaderDetectionLatinLettersNumbersPunctuationRegexPattern, HeaderDetectionRegexOptions, Config.Regex.Timeout); 76 | private static Regex HeaderDetectionLatinLettersNumbersPunctuationRegex() => HeaderDetectionLatinLettersNumbersPunctuationRegexVar; 77 | 78 | private static readonly Regex HeaderDetectionLatinLettersNumbersRegexVar = new(HeaderDetectionLatinLettersNumbersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.Timeout); 79 | private static Regex HeaderDetectionLatinLettersNumbersRegex() => HeaderDetectionLatinLettersNumbersRegexVar; 80 | 81 | private static readonly Regex HeaderDetectionLatinLettersRegexVar = new(HeaderDetectionLatinLettersRegexPattern, HeaderDetectionRegexOptions, Config.Regex.Timeout); 82 | private static Regex HeaderDetectionLatinLettersRegex() => HeaderDetectionLatinLettersRegexVar; 83 | #endif 84 | } 85 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/ConnectionDialog.xaml.Static.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Input; 4 | 5 | using CsvLINQPadDriver.Wpf; 6 | 7 | namespace CsvLINQPadDriver; 8 | 9 | internal partial class ConnectionDialog 10 | { 11 | public const string ConfirmCheck = "check"; 12 | public const string ConfirmUncheck = "uncheck"; 13 | 14 | public const Visibility NetCoreOnlyVisibility = 15 | #if NETCOREAPP 16 | Visibility.Visible; 17 | #else 18 | Visibility.Collapsed; 19 | #endif 20 | 21 | // ReSharper disable StringLiteralTypo 22 | public static readonly RoutedUICommandEx AddFilesCommand = new() 23 | { 24 | Text = "Add f_iles", 25 | InputGestureText = "Ctrl+O", 26 | ToolTip = "Add files" 27 | }; 28 | 29 | public static readonly KeyGesture AddFilesCommandKeyGesture = AddFilesCommand.InputGestureAsKeyGesture; 30 | 31 | public static readonly RoutedUICommandEx AddFoldersCommand = new() 32 | { 33 | Text = "Add f_olders", 34 | InputGestureText = "Ctrl+Shift+O", 35 | ToolTip = "Add folders" 36 | }; 37 | 38 | public static readonly KeyGesture AddFoldersCommandKeyGesture = AddFoldersCommand.InputGestureAsKeyGesture; 39 | 40 | public static readonly RoutedUICommandEx AddFoldersWithSubfoldersCommand = new() 41 | { 42 | Text = "Add folders with _sub-folders", 43 | InputGestureText = "Ctrl+Shift+Alt+O", 44 | ToolTip = "Add folders with sub-folders" 45 | }; 46 | 47 | public static readonly KeyGesture AddFoldersWithSubfoldersCommandKeyGesture = AddFoldersWithSubfoldersCommand.InputGestureAsKeyGesture; 48 | 49 | public static readonly RoutedUICommandEx DeleteCommand = new() 50 | { 51 | Text = "_Delete", 52 | InputGestureText = "Del" 53 | }; 54 | 55 | public static readonly RoutedUICommandEx SelectAllCommand = new() 56 | { 57 | Text = "Select _all", 58 | InputGestureText = "Ctrl+A" 59 | }; 60 | 61 | public static readonly RoutedUICommandEx ClearCommand = new() 62 | { 63 | Text = "C_lear", 64 | InputGestureText = "Ctrl+L", 65 | ToolTip = "Clear" 66 | }; 67 | 68 | public static readonly KeyGesture ClearCommandKeyGesture = ClearCommand.InputGestureAsKeyGesture; 69 | 70 | public static readonly RoutedUICommandEx PasteFoldersWithSubfoldersCommand = new() 71 | { 72 | Text = "_Paste (append ** active file type mask to folders)", 73 | InputGestureText = "Ctrl+Alt+V" 74 | }; 75 | 76 | public static readonly KeyGesture PasteFoldersWithSubfoldersCommandKeyGesture = PasteFoldersWithSubfoldersCommand.InputGestureAsKeyGesture; 77 | 78 | public static readonly RoutedUICommandEx PasteFromClipboardFoldersAndProceedCommand = new() 79 | { 80 | Text = "Clear, paste (append * active file type mask to folders) a_nd proceed", 81 | InputGestureText = "Ctrl+Shift+V" 82 | }; 83 | 84 | public static readonly KeyGesture PasteFromClipboardFoldersAndProceedCommandKeyGesture = PasteFromClipboardFoldersAndProceedCommand.InputGestureAsKeyGesture; 85 | 86 | public static readonly RoutedUICommandEx PasteFromClipboardFoldersWithSubfoldersAndProceedCommand = new() 87 | { 88 | Text = "Clear, paste (append ** active file type mask to folders) a_nd proceed", 89 | InputGestureText = "Ctrl+Shift+Alt+V" 90 | }; 91 | 92 | public static readonly KeyGesture PasteFromClipboardFoldersWithSubfoldersAndProceedCommandKeyGesture = PasteFromClipboardFoldersWithSubfoldersAndProceedCommand.InputGestureAsKeyGesture; 93 | 94 | public static readonly RoutedUICommandEx WrapFilesTextCommand = new() 95 | { 96 | Text = "Word _wrap", 97 | InputGestureText = "Ctrl+W" 98 | }; 99 | 100 | public static readonly KeyGesture WrapFilesTextCommandKeyGesture = WrapFilesTextCommand.InputGestureAsKeyGesture; 101 | 102 | public static readonly RoutedUICommandEx CtrlLeftClickCommand = new() 103 | { 104 | InputGestureText = "Ctrl+LeftClick" 105 | }; 106 | 107 | public static readonly MouseGesture CtrlLeftClickCommandMouseGesture = CtrlLeftClickCommand.InputGestureAsMouseGesture; 108 | 109 | public static readonly RoutedUICommandEx BrowseCommand = new() 110 | { 111 | Text = "_Browse", 112 | InputGestureText = "Ctrl+F", 113 | ToolTip = $"Browse file or folder at the current line ({{0}}) or any line ({CtrlLeftClickCommand.InputGestureText})" 114 | }; 115 | 116 | public static readonly KeyGesture BrowseCommandKeyGesture = BrowseCommand.InputGestureAsKeyGesture; 117 | 118 | public static readonly RoutedUICommandEx HelpCommand = new() 119 | { 120 | InputGestureText = "F1" 121 | }; 122 | 123 | public static readonly KeyGesture HelpCommandKeyGesture = HelpCommand.InputGestureAsKeyGesture; 124 | 125 | public static readonly string ConnectionHelp = $"View context help on GitHub. {HelpCommand.InputGestureText} for driver help on GitHub"; 126 | 127 | #pragma warning disable S1075 128 | private const string BaseHelpUri = "https://github.com/i2van/CsvLINQPadDriver#"; 129 | #pragma warning restore S1075 130 | private const string HelpUri = $"{BaseHelpUri}readme"; 131 | public static readonly Uri FilesHelp = new($"{BaseHelpUri}csv-files"); 132 | public static readonly Uri FormatHelp = new($"{BaseHelpUri}format"); 133 | public static readonly Uri MemoryHelp = new($"{BaseHelpUri}memory"); 134 | public static readonly Uri GenerationHelp = new($"{BaseHelpUri}generation"); 135 | public static readonly Uri RelationsHelp = new($"{BaseHelpUri}relations"); 136 | 137 | // ReSharper restore StringLiteralTypo 138 | 139 | private static T IfWin10(T ifTrue, T ifFalse) => 140 | Environment.OSVersion.Version.Major >= 10 ? ifTrue : ifFalse; 141 | 142 | public static readonly string WrapText = IfWin10("⭹", "Wrap"); 143 | public static readonly string UnwrapText = IfWin10("⭲", "Unwrap"); 144 | 145 | private static string GetTurnWrapText(bool on) => 146 | $"Turn {(on ? "on" : "off")} word wrap ({WrapFilesTextCommand.InputGestureText})"; 147 | 148 | public static readonly string TurnOnWrapText = GetTurnWrapText(true); 149 | public static readonly string TurnOffWrapText = GetTurnWrapText(false); 150 | } 151 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Bcl/Microsoft/System.Private.CoreLib/System/Index.cs: -------------------------------------------------------------------------------- 1 | // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Index.cs 2 | 3 | // Licensed to the .NET Foundation under one or more agreements. 4 | // The .NET Foundation licenses this file to you under the MIT license. 5 | 6 | // ReSharper disable All 7 | 8 | using System.Runtime.CompilerServices; 9 | 10 | namespace System 11 | { 12 | /// Represent a type can be used to index a collection either from the start or the end. 13 | /// 14 | /// Index is used by the C# compiler to support the new index syntax 15 | /// 16 | /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; 17 | /// int lastElement = someArray[^1]; // lastElement = 5 18 | /// 19 | /// 20 | internal readonly struct Index : IEquatable 21 | { 22 | private readonly int _value; 23 | 24 | /// Construct an Index using a value and indicating if the index is from the start or from the end. 25 | /// The index value. it has to be zero or positive number. 26 | /// Indicating if the index is from the start or from the end. 27 | /// 28 | /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. 29 | /// 30 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 31 | public Index(int value, bool fromEnd = false) 32 | { 33 | if (value < 0) 34 | { 35 | throw new ArgumentOutOfRangeException(nameof(value)); 36 | } 37 | 38 | if (fromEnd) 39 | _value = ~value; 40 | else 41 | _value = value; 42 | } 43 | 44 | // The following private constructors mainly created for perf reason to avoid the checks 45 | private Index(int value) 46 | { 47 | _value = value; 48 | } 49 | 50 | /// Create an Index pointing at first element. 51 | public static Index Start => new Index(0); 52 | 53 | /// Create an Index pointing at beyond last element. 54 | public static Index End => new Index(~0); 55 | 56 | /// Create an Index from the start at the position indicated by the value. 57 | /// The index value from the start. 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static Index FromStart(int value) 60 | { 61 | if (value < 0) 62 | { 63 | throw new ArgumentOutOfRangeException(nameof(value)); 64 | } 65 | 66 | return new Index(value); 67 | } 68 | 69 | /// Create an Index from the end at the position indicated by the value. 70 | /// The index value from the end. 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public static Index FromEnd(int value) 73 | { 74 | if (value < 0) 75 | { 76 | throw new ArgumentOutOfRangeException(nameof(value)); 77 | } 78 | 79 | return new Index(~value); 80 | } 81 | 82 | /// Returns the index value. 83 | public int Value 84 | { 85 | get 86 | { 87 | if (_value < 0) 88 | return ~_value; 89 | else 90 | return _value; 91 | } 92 | } 93 | 94 | /// Indicates whether the index is from the start or the end. 95 | public bool IsFromEnd => _value < 0; 96 | 97 | /// Calculate the offset from the start using the giving collection length. 98 | /// The length of the collection that the Index will be used with. length has to be a positive value 99 | /// 100 | /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. 101 | /// we don't validate either the returned offset is greater than the input length. 102 | /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and 103 | /// then used to index a collection will get out of range exception which will be same affect as the validation. 104 | /// 105 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 106 | public int GetOffset(int length) 107 | { 108 | int offset = _value; 109 | if (IsFromEnd) 110 | { 111 | // offset = length - (~value) 112 | // offset = length + (~(~value) + 1) 113 | // offset = length + value + 1 114 | 115 | offset += length + 1; 116 | } 117 | return offset; 118 | } 119 | 120 | /// Indicates whether the current Index object is equal to another object of the same type. 121 | /// An object to compare with this object 122 | public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value; 123 | 124 | /// Indicates whether the current Index object is equal to another Index object. 125 | /// An object to compare with this object 126 | public bool Equals(Index other) => _value == other._value; 127 | 128 | /// Returns the hash code for this instance. 129 | public override int GetHashCode() => _value; 130 | 131 | /// Converts integer number to an Index. 132 | public static implicit operator Index(int value) => FromStart(value); 133 | 134 | /// Converts the value of the current Index object to its equivalent string representation. 135 | public override string ToString() 136 | { 137 | if (IsFromEnd) 138 | return ToStringFromEnd(); 139 | 140 | return ((uint)Value).ToString(); 141 | } 142 | 143 | private string ToStringFromEnd() 144 | { 145 | return '^' + Value.ToString(); 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/Extensions/Exported/StringExtensions.Complex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Numerics; 4 | 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedMember.Global 7 | 8 | namespace CsvLINQPadDriver.Extensions; 9 | 10 | public static partial class StringExtensions 11 | { 12 | public static Complex? ToComplex(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 13 | { 14 | if (string.IsNullOrEmpty(s)) 15 | { 16 | return null; 17 | } 18 | 19 | try 20 | { 21 | return Parse(s, style, provider.ResolveFormatProvider()); 22 | } 23 | catch (Exception e) when (e.CanBeHandled()) 24 | { 25 | throw ConvertException.Create("Complex", s, e); 26 | } 27 | } 28 | 29 | #if NETCOREAPP 30 | public static Complex? ToComplex(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) 31 | { 32 | if (s.IsEmpty) 33 | { 34 | return null; 35 | } 36 | 37 | try 38 | { 39 | return Parse(s, style, provider.ResolveFormatProvider()); 40 | } 41 | catch (Exception e) when (e.CanBeHandled()) 42 | { 43 | throw ConvertException.Create("Complex", s, e); 44 | } 45 | } 46 | #endif 47 | 48 | public static Complex? ToComplexSafe(this string? s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 49 | TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 50 | 51 | #if NETCOREAPP 52 | public static Complex? ToComplexSafe(this ReadOnlySpan s, NumberStyles style = Styles.Float, IFormatProvider? provider = null) => 53 | TryParse(s, style, provider.ResolveFormatProvider(), out var parsedValue) ? parsedValue : null; 54 | #endif 55 | 56 | // Fixed TryParse/Parse source: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs 57 | private const NumberStyles InvalidNumberStyles = ~( 58 | NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite 59 | | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign 60 | | NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPoint 61 | | NumberStyles.AllowThousands | NumberStyles.AllowExponent 62 | | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier); 63 | 64 | private static bool TryParse( 65 | #if NETCOREAPP 66 | ReadOnlySpan 67 | #else 68 | string? 69 | #endif 70 | s, 71 | NumberStyles style, IFormatProvider? provider, out Complex result) 72 | { 73 | #if !NETCOREAPP 74 | if (s is null) 75 | { 76 | result = default; 77 | return false; 78 | } 79 | #endif 80 | 81 | ValidateParseStyleFloatingPoint(style); 82 | 83 | var openBracket = s.IndexOf('<'); 84 | var semicolon = s.IndexOf(';'); 85 | var closeBracket = s.IndexOf('>'); 86 | 87 | if (s.Length < 5 || openBracket == -1 || semicolon == -1 || closeBracket == -1 || openBracket > semicolon || openBracket > closeBracket || semicolon > closeBracket) 88 | { 89 | // We need at least 5 characters for `<0;0>` 90 | // We also expect a to find an open bracket, a semicolon, and a closing bracket in that order 91 | 92 | result = default; 93 | return false; 94 | } 95 | 96 | if (openBracket != 0 && ((style & NumberStyles.AllowLeadingWhite) == 0 || 97 | ! 98 | #if !NETCOREAPP 99 | string.IsNullOrWhiteSpace( 100 | #endif 101 | s[..openBracket] 102 | #if NETCOREAPP 103 | .IsWhiteSpace( 104 | #endif 105 | ) 106 | )) 107 | { 108 | // The opening bracket wasn't the first and we either didn't allow leading whitespace 109 | // or one of the leading characters wasn't whitespace at all. 110 | 111 | result = default; 112 | return false; 113 | } 114 | 115 | if (!double.TryParse(s. 116 | #if NETCOREAPP 117 | Slice 118 | #else 119 | Substring 120 | #endif 121 | (openBracket + 1, semicolon - 1), 122 | style, provider, out var real)) 123 | { 124 | result = default; 125 | return false; 126 | } 127 | 128 | if (char.IsWhiteSpace(s[semicolon + 1])) 129 | { 130 | // We allow a single whitespace after the semicolon regardless of style, this is so that 131 | // the output of `ToString` can be correctly parsed by default and values will roundtrip. 132 | semicolon += 1; 133 | } 134 | 135 | if (!double.TryParse(s. 136 | #if NETCOREAPP 137 | Slice 138 | #else 139 | Substring 140 | #endif 141 | (semicolon + 1, closeBracket - semicolon - 1), 142 | style, provider, out var imaginary)) 143 | { 144 | result = default; 145 | return false; 146 | } 147 | 148 | if (closeBracket != s.Length - 1 && ((style & NumberStyles.AllowTrailingWhite) == 0 || 149 | ! 150 | #if !NETCOREAPP 151 | string.IsNullOrWhiteSpace( 152 | #endif 153 | s[closeBracket..] 154 | #if NETCOREAPP 155 | .IsWhiteSpace( 156 | #endif 157 | ) 158 | )) 159 | { 160 | // The closing bracket wasn't the last and we either didn't allow trailing whitespace 161 | // or one of the trailing characters wasn't whitespace at all. 162 | 163 | result = default; 164 | return false; 165 | } 166 | 167 | result = new Complex(real, imaginary); 168 | return true; 169 | 170 | static void ValidateParseStyleFloatingPoint(NumberStyles style) 171 | { 172 | // Check for undefined flags or hex number 173 | if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier)) != 0) 174 | { 175 | if ((style & InvalidNumberStyles) != 0) 176 | { 177 | throw new ArgumentException("An undefined NumberStyles value is being used.", nameof(style)); 178 | } 179 | 180 | throw new ArgumentException("The number style AllowHexSpecifier is not supported on floating point data types."); 181 | } 182 | } 183 | } 184 | 185 | private static Complex Parse( 186 | #if NETCOREAPP 187 | ReadOnlySpan 188 | #else 189 | string? 190 | #endif 191 | s, NumberStyles style, IFormatProvider? provider) => 192 | TryParse(s, style, provider, out var result) 193 | ? result 194 | : throw new OverflowException(); 195 | } 196 | -------------------------------------------------------------------------------- /Src/CsvLINQPadDriver/ICsvDataContextDriverProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | // ReSharper disable UnusedMemberInSuper.Global 5 | // ReSharper disable UnusedMember.Global 6 | 7 | namespace CsvLINQPadDriver; 8 | 9 | public interface ICsvDataContextDriverProperties 10 | { 11 | /// 12 | /// The data is the production one. 13 | /// 14 | bool IsProduction { get; set; } 15 | 16 | /// 17 | /// The connection is the persistent one. 18 | /// 19 | bool Persist { get; set; } 20 | 21 | /// 22 | /// The path to the directory with the CSV files or to the CSV file. 23 | /// 24 | string Files { get; set; } 25 | 26 | /// 27 | /// The default file type. 28 | /// 29 | FileType FileType { get; set; } 30 | 31 | /// 32 | /// The files order. 33 | /// 34 | FilesOrderBy FilesOrderBy { get; set; } 35 | 36 | /// 37 | /// The encoding for the files without BOM. 38 | /// 39 | NoBomEncoding NoBomEncoding { get; set; } 40 | 41 | /// 42 | /// Indicates that the malformed CSV data should be ignored. 43 | /// 44 | bool IgnoreBadData { get; set; } 45 | 46 | /// 47 | /// Indicates that the encoding should be auto-detected. 48 | /// 49 | bool AutoDetectEncoding { get; set; } 50 | 51 | /// 52 | /// Indicates that the CSV comments should be processed. 53 | /// 54 | bool AllowComments { get; set; } 55 | 56 | /// 57 | /// The single-line comment characters. 58 | /// 59 | string CommentChars { get; set; } 60 | 61 | /// 62 | /// The single-line comment character. 63 | /// 64 | char? CommentChar { get; } 65 | 66 | /// 67 | /// Indicates that the escape character should be used. 68 | /// 69 | bool UseEscapeChar { get; set; } 70 | 71 | /// 72 | /// The escape characters. 73 | /// 74 | string EscapeChars { get; set; } 75 | 76 | /// 77 | /// The escape character. 78 | /// 79 | char? EscapeChar { get; } 80 | 81 | /// 82 | /// Indicates that the quote character should be used. 83 | /// 84 | bool UseQuoteChar { get; set; } 85 | 86 | /// 87 | /// The quote characters. 88 | /// 89 | string QuoteChars { get; set; } 90 | 91 | /// 92 | /// The quote character. 93 | /// 94 | char? QuoteChar { get; } 95 | 96 | /// 97 | /// The parsed files. 98 | /// 99 | IEnumerable ParsedFiles { get; } 100 | 101 | /// 102 | /// The default CSV separator. If empty or null, the separator will be auto-detected. 103 | /// 104 | string CsvSeparator { get; set; } 105 | 106 | /// 107 | /// The safe CSV separator. 108 | /// 109 | string? SafeCsvSeparator { get; } 110 | 111 | /// 112 | /// Indicates that the blank lines should be ignored. 113 | /// 114 | bool IgnoreBlankLines { get; } 115 | 116 | /// 117 | /// Indicates that the header should be added. 118 | /// 119 | bool AddHeader { get; } 120 | 121 | /// 122 | /// The header detection method. 123 | /// 124 | HeaderDetection HeaderDetection { get; } 125 | 126 | /// 127 | /// The header format. 128 | /// 129 | HeaderFormat HeaderFormat { get; } 130 | 131 | /// 132 | /// Indicates that the leading rows should be skipped. 133 | /// 134 | bool AllowSkipLeadingRows { get; set; } 135 | 136 | /// 137 | /// The number of leading rows to skip. 138 | /// 139 | int SkipLeadingRowsCount { get; set; } 140 | 141 | /// 142 | /// Indicates that the spaces should be trimmed. 143 | /// 144 | bool TrimSpaces { get; } 145 | 146 | /// 147 | /// Indicates that the CSV parsing mode should be used. 148 | /// 149 | bool AllowCsvMode { get; set; } 150 | 151 | /// 152 | /// The CSV parsing mode. 153 | /// 154 | CsvModeOptions CsvMode { get; set; } 155 | 156 | /// 157 | /// The spaces trimming method. 158 | /// 159 | WhitespaceTrimOptions WhitespaceTrimOptions { get; } 160 | 161 | /// 162 | /// Indicates that the CsvHelper separator auto-detection should be used. 163 | /// 164 | bool UseCsvHelperSeparatorAutoDetection { get; } 165 | 166 | /// 167 | /// Indicates that the table should be renamed. 168 | /// 169 | bool RenameTable { get; set; } 170 | 171 | /// 172 | /// The table name format. 173 | /// 174 | TableNameFormat TableNameFormat { get; set; } 175 | 176 | /// 177 | /// Indicates that the records should be created instead of the classes. 178 | /// 179 | bool UseRecordType { get; set; } 180 | 181 | /// 182 | /// Indicates that the single class should be generated for the similar CSV files. 183 | /// 184 | bool UseSingleClassForSameFiles { get; set; } 185 | 186 | /// 187 | /// Indicates that the similar files should be shown non-grouped in addition to the similar files groups. 188 | /// 189 | bool ShowSameFilesNonGrouped { get; set; } 190 | 191 | /// 192 | /// The generated class string comparison. 193 | /// 194 | StringComparison StringComparison { get; set; } 195 | 196 | /// 197 | /// Indicates that relations based on the files and the column names between the CSV files should be detected and created. 198 | /// 199 | bool DetectRelations { get; set; } 200 | 201 | /// 202 | /// Indicates that the LINQPad should not show the relations content in .Dump() 203 | /// 204 | /// 205 | /// Prevents from loading too many data. 206 | /// > 207 | bool HideRelationsFromDump { get; set; } 208 | 209 | /// 210 | /// Indicates that the debug info should be generated. 211 | /// 212 | bool DebugInfo { get; set; } 213 | 214 | /// 215 | /// Indicates that the file paths should be validated. 216 | /// 217 | bool ValidateFilePaths { get; set; } 218 | 219 | /// 220 | /// Indicates that the beginning of every file should be scanned and the suspicious files with the format not similar to CSV should be ignored. 221 | /// 222 | bool IgnoreInvalidFiles { get; set; } 223 | 224 | /// 225 | /// Indicates that the other processes can modify the files being read. 226 | /// 227 | bool DoNotLockFiles { get; set; } 228 | 229 | /// 230 | /// Indicates that all the strings are interned. 231 | /// 232 | /// 233 | /// May significantly reduce memory consumption, especially when values in CSV are repeated many times; may significantly increase memory usage otherwise. 234 | /// Custom per context interning is used. 235 | /// > 236 | bool IsStringInternEnabled { get; set; } 237 | 238 | /// 239 | /// Indicates that the strings interning uses the generation string comparer. 240 | /// 241 | bool UseStringComparerForStringIntern { get; set; } 242 | 243 | /// 244 | /// Indicates that the parsed rows are cached. 245 | /// 246 | /// 247 | /// 248 | /// Cache survives multiple query runs, even when query is changed. 249 | /// Cache is cleared as soon as LINQPad clears Application Domain of query. 250 | /// May significantly increase memory usage. 251 | /// 252 | /// 253 | /// When disabled multiple enumerations of file content results in multiple reads and parsing of file. 254 | /// Can be significantly slower for complex queries. 255 | /// Significantly reduces memory usage. Useful when reading very large files. 256 | /// 257 | /// 258 | bool IsCacheEnabled { get; set; } 259 | } 260 | --------------------------------------------------------------------------------