-
Notifications
You must be signed in to change notification settings - Fork 9.9k
/
FragmentString.cs
164 lines (147 loc) · 5.75 KB
/
FragmentString.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
namespace Microsoft.AspNetCore.Http;
/// <summary>
/// Provides correct handling for FragmentString value when needed to generate a URI string
/// </summary>
[DebuggerDisplay("{Value}")]
public readonly struct FragmentString : IEquatable<FragmentString>
{
/// <summary>
/// Represents the empty fragment string. This field is read-only.
/// </summary>
public static readonly FragmentString Empty = new FragmentString(string.Empty);
private readonly string _value;
/// <summary>
/// Initialize the fragment string with a given value. This value must be in escaped and delimited format with
/// a leading '#' character.
/// </summary>
/// <param name="value">The fragment string to be assigned to the Value property.</param>
public FragmentString(string value)
{
if (!string.IsNullOrEmpty(value) && value[0] != '#')
{
throw new ArgumentException("The leading '#' must be included for a non-empty fragment.", nameof(value));
}
_value = value;
}
/// <summary>
/// The escaped fragment string with the leading '#' character
/// </summary>
public string Value
{
get { return _value; }
}
/// <summary>
/// True if the fragment string is not empty
/// </summary>
public bool HasValue
{
get { return !string.IsNullOrEmpty(_value); }
}
/// <summary>
/// Provides the fragment string escaped in a way which is correct for combining into the URI representation.
/// A leading '#' character will be included unless the Value is null or empty. Characters which are potentially
/// dangerous are escaped.
/// </summary>
/// <returns>The fragment string value</returns>
public override string ToString()
{
return ToUriComponent();
}
/// <summary>
/// Provides the fragment string escaped in a way which is correct for combining into the URI representation.
/// A leading '#' character will be included unless the Value is null or empty. Characters which are potentially
/// dangerous are escaped.
/// </summary>
/// <returns>The fragment string value</returns>
public string ToUriComponent()
{
// Escape things properly so System.Uri doesn't mis-interpret the data.
return HasValue ? _value : string.Empty;
}
/// <summary>
/// Returns an FragmentString given the fragment as it is escaped in the URI format. The string MUST NOT contain any
/// value that is not a fragment.
/// </summary>
/// <param name="uriComponent">The escaped fragment as it appears in the URI format.</param>
/// <returns>The resulting FragmentString</returns>
public static FragmentString FromUriComponent(string uriComponent)
{
if (String.IsNullOrEmpty(uriComponent))
{
return Empty;
}
return new FragmentString(uriComponent);
}
/// <summary>
/// Returns an FragmentString given the fragment as from a Uri object. Relative Uri objects are not supported.
/// </summary>
/// <param name="uri">The Uri object</param>
/// <returns>The resulting FragmentString</returns>
public static FragmentString FromUriComponent(Uri uri)
{
ArgumentNullException.ThrowIfNull(uri);
string fragmentValue = uri.GetComponents(UriComponents.Fragment, UriFormat.UriEscaped);
if (!string.IsNullOrEmpty(fragmentValue))
{
fragmentValue = "#" + fragmentValue;
}
return new FragmentString(fragmentValue);
}
/// <summary>
/// Evaluates if the current fragment is equal to another fragment <paramref name="other"/>.
/// </summary>
/// <param name="other">A <see cref="FragmentString" /> to compare.</param>
/// <returns><see langword="true" /> if the fragments are equal.</returns>
public bool Equals(FragmentString other)
{
if (!HasValue && !other.HasValue)
{
return true;
}
return string.Equals(_value, other._value, StringComparison.Ordinal);
}
/// <summary>
/// Evaluates if the current fragment is equal to an object <paramref name="obj"/>.
/// </summary>
/// <param name="obj">An object to compare.</param>
/// <returns><see langword="true" /> if the fragments are equal.</returns>
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
{
return !HasValue;
}
return obj is FragmentString && Equals((FragmentString)obj);
}
/// <summary>
/// Gets a hash code for the value.
/// </summary>
/// <returns>The hash code as an <see cref="int"/>.</returns>
public override int GetHashCode()
{
return (HasValue ? _value.GetHashCode() : 0);
}
/// <summary>
/// Evaluates if one fragment is equal to another.
/// </summary>
/// <param name="left">A <see cref="FragmentString"/> instance.</param>
/// <param name="right">A <see cref="FragmentString"/> instance.</param>
/// <returns><see langword="true" /> if the fragments are equal.</returns>
public static bool operator ==(FragmentString left, FragmentString right)
{
return left.Equals(right);
}
/// <summary>
/// Evalutes if one fragment is not equal to another.
/// </summary>
/// <param name="left">A <see cref="FragmentString"/> instance.</param>
/// <param name="right">A <see cref="FragmentString"/> instance.</param>
/// <returns><see langword="true" /> if the fragments are not equal.</returns>
public static bool operator !=(FragmentString left, FragmentString right)
{
return !left.Equals(right);
}
}