This site provides a machine-readable index at /llms.txt.

Skip to main content Skip to navigation

UrlPath Pennington.Routing

A URL path value supporting composition and normalization.

Properties

Value string
Underlying path string.

Constructors

.ctor

#
public readonly record struct UrlPath(string Value)
{
    /// <summary>Implicitly converts a string to a <see cref="UrlPath"/>.</summary>
    public static implicit operator UrlPath(string value) => new(value);

    /// <summary>Joins two URL path segments, normalizing slashes.</summary>
    public static UrlPath operator /(UrlPath left, UrlPath right)
    {
        var l = left.Value.TrimEnd('/');
        var r = right.Value.TrimStart('/');
        if (string.IsNullOrEmpty(l)) return new UrlPath("/" + r);
        if (string.IsNullOrEmpty(r)) return new UrlPath(l.StartsWith('/') ? l : "/" + l);
        return new UrlPath(l + "/" + r);
    }

    /// <summary>Returns a path guaranteed to start with a slash.</summary>
    public UrlPath EnsureLeadingSlash()
        => Value.StartsWith('/') ? this : new UrlPath("/" + Value);

    /// <summary>Returns a path guaranteed to end with a slash.</summary>
    public UrlPath EnsureTrailingSlash()
        => Value.EndsWith('/') ? this : new UrlPath(Value + "/");

    /// <summary>Removes a trailing slash (except from the root path).</summary>
    public UrlPath RemoveTrailingSlash()
        => Value.Length > 1 && Value.EndsWith('/') ? new UrlPath(Value[..^1]) : this;

    /// <summary>Removes a leading slash if present.</summary>
    public UrlPath RemoveLeadingSlash()
        => Value.StartsWith('/') ? new UrlPath(Value[1..]) : this;

    /// <summary>Compares two URL paths ignoring trailing slashes, index.html suffixes, and case.</summary>
    public bool Matches(UrlPath other)
    {
        var a = Normalize(Value);
        var b = Normalize(other.Value);
        return string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
    }

    private static string Normalize(string path)
    {
        var s = path.TrimEnd('/');
        if (s.EndsWith("/index.html", StringComparison.OrdinalIgnoreCase))
            s = s[..^"/index.html".Length];
        else if (s.EndsWith("/index.htm", StringComparison.OrdinalIgnoreCase))
            s = s[..^"/index.htm".Length];
        if (string.IsNullOrEmpty(s)) s = "/";
        return s.ToLowerInvariant();
    }

    /// <summary>Returns the underlying URL string.</summary>
    public override string ToString() => Value;
}

A URL path value supporting composition and normalization.

Parameters

Value string
Underlying path string.

Methods

EnsureLeadingSlash

#
public UrlPath EnsureLeadingSlash();

Returns a path guaranteed to start with a slash.

Returns

UrlPath

EnsureTrailingSlash

#
public UrlPath EnsureTrailingSlash();

Returns a path guaranteed to end with a slash.

Returns

UrlPath

Matches

#
public bool Matches(UrlPath other);

Compares two URL paths ignoring trailing slashes, index.html suffixes, and case.

Parameters

other UrlPath

Returns

bool

op_Division

#
public static UrlPath operator /(UrlPath left, UrlPath right);

Joins two URL path segments, normalizing slashes.

Parameters

left UrlPath
right UrlPath

Returns

UrlPath

op_Implicit

#
public static implicit operator UrlPath(string value);

Implicitly converts a string to a UrlPath.

Parameters

value string

Returns

UrlPath

RemoveLeadingSlash

#
public UrlPath RemoveLeadingSlash();

Removes a leading slash if present.

Returns

UrlPath

RemoveTrailingSlash

#
public UrlPath RemoveTrailingSlash();

Removes a trailing slash (except from the root path).

Returns

UrlPath

ToString

#
public override string ToString();

Returns the underlying URL string.

Returns

string

Pennington.Routing.UrlPath

namespace Pennington.Routing;

/// A URL path value supporting composition and normalization.
public record UrlPath
{
    /// A URL path value supporting composition and normalization.
    
public readonly record struct UrlPath(string Value)
{
    /// <summary>Implicitly converts a string to a <see cref="UrlPath"/>.</summary>
    public static implicit operator UrlPath(string value) => new(value);

    /// <summary>Joins two URL path segments, normalizing slashes.</summary>
    public static UrlPath operator /(UrlPath left, UrlPath right)
    {
        var l = left.Value.TrimEnd('/');
        var r = right.Value.TrimStart('/');
        if (string.IsNullOrEmpty(l)) return new UrlPath("/" + r);
        if (string.IsNullOrEmpty(r)) return new UrlPath(l.StartsWith('/') ? l : "/" + l);
        return new UrlPath(l + "/" + r);
    }

    /// <summary>Returns a path guaranteed to start with a slash.</summary>
    public UrlPath EnsureLeadingSlash()
        => Value.StartsWith('/') ? this : new UrlPath("/" + Value);

    /// <summary>Returns a path guaranteed to end with a slash.</summary>
    public UrlPath EnsureTrailingSlash()
        => Value.EndsWith('/') ? this : new UrlPath(Value + "/");

    /// <summary>Removes a trailing slash (except from the root path).</summary>
    public UrlPath RemoveTrailingSlash()
        => Value.Length > 1 && Value.EndsWith('/') ? new UrlPath(Value[..^1]) : this;

    /// <summary>Removes a leading slash if present.</summary>
    public UrlPath RemoveLeadingSlash()
        => Value.StartsWith('/') ? new UrlPath(Value[1..]) : this;

    /// <summary>Compares two URL paths ignoring trailing slashes, index.html suffixes, and case.</summary>
    public bool Matches(UrlPath other)
    {
        var a = Normalize(Value);
        var b = Normalize(other.Value);
        return string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
    }

    private static string Normalize(string path)
    {
        var s = path.TrimEnd('/');
        if (s.EndsWith("/index.html", StringComparison.OrdinalIgnoreCase))
            s = s[..^"/index.html".Length];
        else if (s.EndsWith("/index.htm", StringComparison.OrdinalIgnoreCase))
            s = s[..^"/index.htm".Length];
        if (string.IsNullOrEmpty(s)) s = "/";
        return s.ToLowerInvariant();
    }

    /// <summary>Returns the underlying URL string.</summary>
    public override string ToString() => Value;
}
/// Returns a path guaranteed to start with a slash.
public UrlPath EnsureLeadingSlash();
/// Returns a path guaranteed to end with a slash.
public UrlPath EnsureTrailingSlash();
/// Compares two URL paths ignoring trailing slashes, index.html suffixes, and case.
public bool Matches(UrlPath other);
/// Joins two URL path segments, normalizing slashes.
public static UrlPath operator /(UrlPath left, UrlPath right);
/// Implicitly converts a string to a UrlPath.
public static implicit operator UrlPath(string value);
/// Removes a leading slash if present.
public UrlPath RemoveLeadingSlash();
/// Removes a trailing slash (except from the root path).
public UrlPath RemoveTrailingSlash();
/// Returns the underlying URL string.
public override string ToString();
/// Underlying path string.
string Value
; }