Last active
September 20, 2020 08:47
-
-
Save atifaziz/b166858fb3581f652846e73909e9b5ad to your computer and use it in GitHub Desktop.
LINQPad helper for WebLINQ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<NuGetReference Version="4.0.0">System.Reactive</NuGetReference> | |
<NuGetReference Version="4.5.0">System.Text.Encoding.CodePages</NuGetReference> | |
<NuGetReference Version="1.0.0-alpha-20200416" Prerelease="true">WebLinq</NuGetReference> | |
<Namespace>System.CodeDom.Compiler</Namespace> | |
<Namespace>System.Globalization</Namespace> | |
<Namespace>System.Net</Namespace> | |
<Namespace>System.Net.Http</Namespace> | |
<Namespace>System.Net.Http.Headers</Namespace> | |
<Namespace>System.Reactive</Namespace> | |
<Namespace>System.Reactive.Linq</Namespace> | |
<Namespace>System.Runtime.Versioning</Namespace> | |
<Namespace>System.Security.Cryptography</Namespace> | |
<Namespace>System.Web</Namespace> | |
<Namespace>WebLinq</Namespace> | |
<RemoveNamespace>System.Collections</RemoveNamespace> | |
<RemoveNamespace>System.Data</RemoveNamespace> | |
<RemoveNamespace>System.Linq.Expressions</RemoveNamespace> | |
<RemoveNamespace>System.Transactions</RemoveNamespace> | |
<RemoveNamespace>System.Xml</RemoveNamespace> | |
<RemoveNamespace>System.Xml.Linq</RemoveNamespace> | |
<RemoveNamespace>System.Xml.XPath</RemoveNamespace> | |
</Query> | |
DateTimeOffset QueryStartTime { get; set; } | |
[Conditional("LINQPAD")] | |
void DisableLoggingInLinqPad() => Logger.IsDisabled = true; | |
void OnInit() | |
{ | |
if (GetType().Assembly.GetCustomAttribute<TargetFrameworkAttribute>() is TargetFrameworkAttribute fna) | |
{ | |
Logger.Log("Target Framework = " + fna.FrameworkName); | |
if (Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER") == null | |
&& new FrameworkName(fna.FrameworkName).Version >= new Version(3, 0)) | |
{ | |
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false); | |
} | |
} | |
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); | |
DisableLoggingInLinqPad(); | |
} | |
void OnStart() | |
{ | |
DisableLoggingInLinqPad(); | |
var path = | |
#if LPLESS | |
Environment.GetEnvironmentVariable("LPLESS_LINQ_FILE_PATH") | |
#else | |
Util.CurrentQueryPath | |
#endif | |
; | |
var startTime = QueryStartTime = DateTimeOffset.Now; | |
Logger.Log($"{Path.GetFileNameWithoutExtension(path)} started at {startTime}."); | |
if (Assembly.GetExecutingAssembly().GetCustomAttribute<GeneratedCodeAttribute>() is GeneratedCodeAttribute gc) | |
Logger.Log($"Generator: {gc.Tool} ({gc.Version})"); | |
} | |
void OnFinish() | |
{ | |
var endTime = DateTimeOffset.Now; | |
Logger.Log($"Time taken was {endTime - QueryStartTime}."); | |
} | |
static void Print(string s) | |
{ | |
using var _ = ConsoleLock.Enter(); | |
Console.WriteLine(s); | |
} | |
[AttributeUsage(AttributeTargets.Method)] | |
sealed class QueryExpressionPrinterAttribute : Attribute {} | |
[QueryExpressionPrinter] | |
static void PrintQueryExpression<T>(IObservable<T> source) => | |
PrintQueryExpression(source.ToEnumerable()); | |
[QueryExpressionPrinter] | |
static void PrintQueryExpression<T>(IEnumerable<T> source) | |
{ | |
var type = typeof(T); | |
if (type.IsPrimitive || Type.GetTypeCode(type) != TypeCode.Object) | |
{ | |
Print(Enquote(type.Name)); | |
foreach (var item in source.Select(Enquote)) | |
Print(item); | |
} | |
else | |
{ | |
var properties = Enumerable.ToArray( | |
from p in type.GetProperties(BindingFlags.Public | BindingFlags.Instance) | |
where p.CanRead && p.GetIndexParameters().Length == 0 | |
select new | |
{ | |
Name = SnakeCaseFromPascal(p.Name), | |
GetValue = new Func<object, object>(p.GetValue), | |
}); | |
if (!properties.Any()) | |
throw new Exception(type + " objects have no properties."); | |
Print(string.Join(",", from p in properties select Enquote(p.Name))); | |
foreach (var item in source) | |
Print(string.Join(",", from p in properties select Enquote(p.GetValue(item)))); | |
} | |
static string Enquote<TValue>(TValue value) => | |
"\"" + Convert.ToString(value, CultureInfo.InvariantCulture).Replace("\"", "\"\"") + "\""; | |
static string SnakeCaseFromPascal(string s) => | |
Regex.Replace(s, @"((?<![A-Z]|^)[A-Z]|(?<=[A-Z]+)[A-Z](?=[a-z]))", m => "_" + m.Value) | |
.ToLowerInvariant(); | |
} | |
static readonly OptionalLock ConsoleLock = OptionalLock.FreeWhen(Console.IsOutputRedirected || Console.IsErrorRedirected); | |
sealed class OptionalLock | |
{ | |
readonly object _lock; | |
readonly IDisposable _disposable; | |
static readonly OptionalLock Nil = new OptionalLock(null, Disposable.Nop); | |
public static OptionalLock Create() => When(true); | |
public static OptionalLock FreeWhen(bool disable) => When(!disable); | |
public static OptionalLock When(bool enable) | |
{ | |
if (!enable) | |
return Nil; | |
var @lock = new object(); | |
return new OptionalLock(@lock, new Disposable(() => Monitor.Exit(@lock))); | |
} | |
OptionalLock(object @lock, IDisposable disposable) => | |
(_lock, _disposable) = (@lock, disposable); | |
public bool IsFree => _lock == null; | |
public IDisposable Enter() | |
{ | |
if (_lock is object @lock) | |
Monitor.Enter(@lock); | |
return _disposable; | |
} | |
sealed class Disposable : IDisposable | |
{ | |
public static readonly IDisposable Nop = new Disposable(null); | |
Action _delegatee; | |
public Disposable(Action delegatee) =>_delegatee = delegatee; | |
public void Dispose() => _delegatee?.Invoke(); | |
} | |
} | |
public static class Logger | |
{ | |
public static bool IsDisabled = false; | |
static readonly object Lock = new object(); | |
public static void Log(string line, ConsoleColor backgroundColor = ConsoleColor.DarkGray, | |
ConsoleColor foregroundColor = ConsoleColor.White) | |
{ | |
if (IsDisabled) | |
return; | |
using var _ = ConsoleLock.Enter(); | |
lock (Lock) | |
{ | |
ConsoleColor? oldBackgroundColor = default; | |
ConsoleColor? oldForegroundColor = default; | |
if (!Console.IsErrorRedirected) | |
{ | |
oldBackgroundColor = Console.BackgroundColor; | |
Console.BackgroundColor = backgroundColor; | |
oldForegroundColor = Console.ForegroundColor; | |
Console.ForegroundColor = foregroundColor; | |
} | |
Console.Error.Write(line); | |
Console.Error.Flush(); | |
if (oldBackgroundColor is ConsoleColor bc) | |
Console.BackgroundColor = bc; | |
if (oldForegroundColor is ConsoleColor fc) | |
Console.ForegroundColor = fc; | |
Console.Error.WriteLine(); | |
} | |
} | |
} | |
static readonly IHttpClient Http = | |
WebLinq.HttpClient.Default | |
#if LPLESS | |
.Wrap(async (send, req, config) => | |
{ | |
Log(">", ConsoleColor.DarkCyan, $"{req.Method} {req.RequestUri}", req.Content?.Headers); | |
var rsp = await send(req, config); | |
Log("<", (int)rsp.StatusCode switch | |
{ | |
var sc when sc >= 200 && sc < 400 => ConsoleColor.DarkGreen, | |
_ => ConsoleColor.DarkRed | |
}, | |
$"{(int) rsp.StatusCode} ({rsp.StatusCode}) {rsp.ReasonPhrase}", | |
rsp.Content.Headers); | |
return rsp; | |
static void Log(string dir, ConsoleColor color, string epilogue, HttpContentHeaders headers) | |
{ | |
var line = | |
$"{dir} HTTP {epilogue}" | |
+ (headers?.ContentType is MediaTypeHeaderValue h ? " (" + h + ")" : null) | |
+ (headers?.ContentLength is long len ? " [" + len + " bytes]" : null); | |
Logger.Log(line, color); | |
} | |
}) | |
#endif | |
; | |
void Main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<NuGetReference Version="1.3.1">Dsv</NuGetReference> | |
<NuGetReference Version="4.0.0">System.Reactive</NuGetReference> | |
<NuGetReference Version="1.0.0-alpha-20200416" Prerelease="true">WebLinq</NuGetReference> | |
<Namespace>Dsv</Namespace> | |
<Namespace>System.Reactive.Linq</Namespace> | |
<Namespace>WebLinq</Namespace> | |
<Namespace>WebLinq.Text</Namespace> | |
<RemoveNamespace>System.Collections</RemoveNamespace> | |
<RemoveNamespace>System.Data</RemoveNamespace> | |
<RemoveNamespace>System.Diagnostics</RemoveNamespace> | |
<RemoveNamespace>System.IO</RemoveNamespace> | |
<RemoveNamespace>System.Linq</RemoveNamespace> | |
<RemoveNamespace>System.Linq.Expressions</RemoveNamespace> | |
<RemoveNamespace>System.Reflection</RemoveNamespace> | |
<RemoveNamespace>System.Threading</RemoveNamespace> | |
<RemoveNamespace>System.Transactions</RemoveNamespace> | |
<RemoveNamespace>System.Xml</RemoveNamespace> | |
<RemoveNamespace>System.Xml.Linq</RemoveNamespace> | |
<RemoveNamespace>System.Xml.XPath</RemoveNamespace> | |
</Query> | |
public sealed class DsvOptions | |
{ | |
public static readonly DsvOptions CsvFormat = new DsvOptions(Format.Csv); | |
public static DsvOptions Delimiter(char ch) => | |
CsvFormat.WithFormat(new Format(ch)); | |
public Format Format { get; } | |
public Encoding Encoding { get; } | |
public Func<IEnumerable<string>, IEnumerable<string>> LinesFunction { get; } | |
public Func<string, bool> LineFilter { get; } | |
public DsvOptions(Format format) : | |
this(format, null, ls => ls, _ => false) {} | |
DsvOptions(Format format, | |
Encoding encoding, | |
Func<IEnumerable<string>, IEnumerable<string>> linesFunction, | |
Func<string, bool> lineFilter) | |
{ | |
Format = format ?? throw new ArgumentNullException(nameof(format)); | |
Encoding = encoding; | |
LinesFunction = linesFunction ?? throw new ArgumentNullException(nameof(linesFunction)); | |
LineFilter = lineFilter ?? throw new ArgumentNullException(nameof(lineFilter)); | |
} | |
public DsvOptions WithFormat(Format value) => | |
value == Format ? this : new DsvOptions(value, Encoding, LinesFunction, LineFilter); | |
public DsvOptions WithEncoding(Encoding value) => | |
value == Encoding ? this : new DsvOptions(Format, value, LinesFunction, LineFilter); | |
public DsvOptions WithLinesFunction(Func<IEnumerable<string>, IEnumerable<string>> value) => | |
value == LinesFunction ? this : new DsvOptions(Format, Encoding, value, LineFilter); | |
public DsvOptions WithLineFilter(Func<string, bool> value) => | |
value == LineFilter ? this : new DsvOptions(Format, Encoding, LinesFunction, value); | |
} | |
static partial class DsvExtensions | |
{ | |
public static IObservable<HttpFetch<IEnumerable<(T Header, TextRow Row)>>> | |
Csv<T>(this IHttpObservable http, | |
Func<TextRow, T> headSelector) => | |
http.Dsv(UserQuery.DsvOptions.CsvFormat, headSelector, ValueTuple.Create); | |
public static IObservable<HttpFetch<IEnumerable<TRow>>> | |
Csv<THead, TRow>(this IHttpObservable http, | |
Func<TextRow, THead> headSelector, | |
Func<THead, TextRow, TRow> rowSelector) => | |
http.Dsv(UserQuery.DsvOptions.CsvFormat, headSelector, rowSelector); | |
public static IObservable<HttpFetch<IEnumerable<(T Header, TextRow Row)>>> | |
Dsv<T>(this IHttpObservable http, | |
UserQuery.DsvOptions options, | |
Func<TextRow, T> headSelector) => | |
http.Dsv(options, headSelector, ValueTuple.Create); | |
public static IObservable<HttpFetch<IEnumerable<TRow>>> | |
Dsv<THead, TRow>(this IHttpObservable http, | |
UserQuery.DsvOptions options, | |
Func<TextRow, THead> headSelector, | |
Func<THead, TextRow, TRow> rowSelector) => | |
from f in options.Encoding == null ? http.Text() : http.Text(options.Encoding) | |
select f.WithContent(options.LinesFunction(Regex.Split(f.Content.Trim(), @"\r?\n")) | |
.ParseDsv(options.Format, options.LineFilter, headSelector, rowSelector)); | |
} | |
void Main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Query Kind="Program"> | |
<NuGetReference Version="12.0.3">Newtonsoft.Json</NuGetReference> | |
<NuGetReference Version="4.0.0">System.Reactive</NuGetReference> | |
<NuGetReference Version="1.0.0-alpha-20190716T1343" Prerelease="true">WebLinq</NuGetReference> | |
<Namespace>Newtonsoft.Json</Namespace> | |
<Namespace>System.Reactive</Namespace> | |
<Namespace>System.Reactive.Linq</Namespace> | |
<Namespace>WebLinq</Namespace> | |
<RemoveNamespace>System.Collections</RemoveNamespace> | |
<RemoveNamespace>System.Collections.Generic</RemoveNamespace> | |
<RemoveNamespace>System.Data</RemoveNamespace> | |
<RemoveNamespace>System.Diagnostics</RemoveNamespace> | |
<RemoveNamespace>System.IO</RemoveNamespace> | |
<RemoveNamespace>System.Linq.Expressions</RemoveNamespace> | |
<RemoveNamespace>System.Reflection</RemoveNamespace> | |
<RemoveNamespace>System.Text.RegularExpressions</RemoveNamespace> | |
<RemoveNamespace>System.Threading</RemoveNamespace> | |
<RemoveNamespace>System.Transactions</RemoveNamespace> | |
<RemoveNamespace>System.Xml</RemoveNamespace> | |
<RemoveNamespace>System.Xml.Linq</RemoveNamespace> | |
<RemoveNamespace>System.Xml.XPath</RemoveNamespace> | |
</Query> | |
static partial class Json | |
{ | |
public static string Stringify(object obj) => | |
JsonConvert.SerializeObject(obj); | |
public static T Parse<T>(string json) => | |
JsonConvert.DeserializeObject<T>(json); | |
public static T ParseAsPrototype<T>(string json, T prototype) => | |
JsonConvert.DeserializeAnonymousType(json, prototype); | |
public static T ReinterpretAsPrototype<T>(object obj, T prototype) => | |
Reinterpret<T>(obj); | |
public static T Reinterpret<T>(object obj) | |
{ | |
var serializer = new JsonSerializer(); | |
using var reader = Newtonsoft.Json.Linq.JToken.FromObject(obj).CreateReader(); | |
return (T)serializer.Deserialize(reader, typeof(T)); | |
} | |
} | |
static partial class JsonExtensions | |
{ | |
public static IObservable<HttpFetch<T>> Json<T>(this IHttpObservable http, T prototype) => | |
http.ReadContent(async f => | |
JsonConvert.DeserializeAnonymousType(await f.Content.ReadAsStringAsync(), prototype)); | |
public static IObservable<HttpFetch<T>> Json<T>(this IHttpObservable http, T prototype, Encoding encoding) => | |
http.ReadContent(async f => | |
{ | |
var byteArray = await f.Content.ReadAsByteArrayAsync(); | |
return JsonConvert.DeserializeAnonymousType(encoding.GetString(byteArray, 0, byteArray.Length), prototype); | |
}); | |
public static IObservable<HttpFetch<T>> Json<T>(this IObservable<HttpFetch<string>> http, T prototype) => | |
from fetch in http | |
select fetch.WithContent(JsonConvert.DeserializeAnonymousType(fetch.Content, prototype)); | |
public static IObservable<T> DeserializeJson<T>(this IObservable<string> source, T prototype) => | |
from json in source | |
select JsonConvert.DeserializeAnonymousType(json, prototype); | |
public static IObservable<HttpFetch<T>> Json<T>(this IHttpObservable http) => | |
http.ReadContent(async f => | |
JsonConvert.DeserializeObject<T>(await f.Content.ReadAsStringAsync())); | |
public static IObservable<HttpFetch<T>> Json<T>(this IHttpObservable http, Encoding encoding) => | |
http.ReadContent(async f => | |
{ | |
var byteArray = await f.Content.ReadAsByteArrayAsync(); | |
return JsonConvert.DeserializeObject<T>(encoding.GetString(byteArray, 0, byteArray.Length)); | |
}); | |
public static IObservable<HttpFetch<T>> Json<T>(this IObservable<HttpFetch<string>> http) => | |
from fetch in http | |
select fetch.WithContent(JsonConvert.DeserializeObject<T>(fetch.Content)); | |
public static IObservable<T> Json<T>(this IObservable<string> source, T prototype) => | |
from json in source | |
select JsonConvert.DeserializeAnonymousType(json, prototype); | |
public static IObservable<T> Json<T>(this IObservable<string> source) => | |
from json in source | |
select JsonConvert.DeserializeObject<T>(json); | |
} | |
void Main() {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment