Skip to content

Instantly share code, notes, and snippets.

@in-async
Last active February 16, 2022 12:53
Show Gist options
  • Save in-async/741d853eb149df133b96cf60518814c4 to your computer and use it in GitHub Desktop.
Save in-async/741d853eb149df133b96cf60518814c4 to your computer and use it in GitHub Desktop.
URI Template Level 1
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Commons {
public class UriTemplate1 {
public static void Expand(TextWriter writer, string template, IDictionary<string, object?> parameters) {
if (writer is null) { throw new ArgumentNullException(nameof(writer)); }
if (template is null) { throw new ArgumentNullException(nameof(template)); }
if (parameters is null) { throw new ArgumentNullException(nameof(parameters)); }
IEnumerable<Enclosing> enclosings = template.EnumerateEnclosings('{', '}');
ExpandCore(writer, template, enclosings, parameters);
}
public static string Expand(string template, IDictionary<string, object?> parameters) {
if (template is null) { throw new ArgumentNullException(nameof(template)); }
if (parameters is null) { throw new ArgumentNullException(nameof(parameters)); }
using StringWriter writer = new();
IEnumerable<Enclosing> enclosings = template.EnumerateEnclosings('{', '}');
ExpandCore(writer, template, enclosings, parameters);
return writer.ToString();
}
private readonly Enclosing[] _enclosings;
public UriTemplate1(string template) {
Template = template ?? throw new ArgumentNullException(nameof(template));
_enclosings = template.EnumerateEnclosings('{', '}').ToArray();
}
public string Template { get; }
public void Expand(TextWriter writer, IDictionary<string, object?> parameters) {
if (writer is null) { throw new ArgumentNullException(nameof(writer)); }
if (parameters is null) { throw new ArgumentNullException(nameof(parameters)); }
ExpandCore(writer, Template, _enclosings, parameters);
}
public string Expand(IDictionary<string, object?> parameters) {
if (parameters is null) { throw new ArgumentNullException(nameof(parameters)); }
using StringWriter writer = new();
ExpandCore(writer, Template, _enclosings, parameters);
return writer.ToString();
}
private static void ExpandCore(TextWriter writer, string template, IEnumerable<Enclosing> enclosings, IDictionary<string, object?> parameters) {
int currentIdx = 0;
foreach (Enclosing enclosing in enclosings) {
writer.Write(template.Substring(currentIdx, enclosing.OpenIndex - currentIdx));
currentIdx = enclosing.CloseIndex + 1;
string expr = enclosing.Inner.ToString();
if (parameters.TryGetValue(expr, out object? value)) {
if (value is { }) {
writer.Write(Uri.EscapeDataString(value.ToString()!));
}
}
}
writer.Write(template.Substring(currentIdx));
}
}
}
using System;
using System.Collections.Generic;
namespace Commons {
public static class StringExtensions {
public static IEnumerable<Enclosing> EnumerateEnclosings(this string value, char open, char close) {
if (value is null) { throw new ArgumentNullException(nameof(value)); }
for (int currentIdx = 0; currentIdx < value.Length;) {
int openIdx = value.IndexOf(open, currentIdx);
if (openIdx < 0) { break; }
int closeIdx = value.IndexOf(close, openIdx + 1);
if (closeIdx < 0) { break; }
yield return new Enclosing(value, openIdx, closeIdx);
currentIdx = closeIdx + 1;
}
}
}
public readonly struct Enclosing {
public Enclosing(string source, int openIndex, int closeIndex) {
Source = source;
OpenIndex = openIndex;
CloseIndex = closeIndex;
}
public string Source { get; }
public int OpenIndex { get; }
public int CloseIndex { get; }
public ReadOnlySpan<char> Inner => Source.AsSpan(OpenIndex + 1, CloseIndex - OpenIndex - 1);
public ReadOnlySpan<char> Outer => Source.AsSpan(OpenIndex, CloseIndex - OpenIndex + 1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment