Source/Routing/MqttRouteTableFactory.cs
/// <summary>
/// Route precedence algorithm. We collect all the routes and sort them from most specific to less specific. The
/// specificity of a route is given by the specificity of its segments and the position of those segments in the route.
/// * A literal segment is more specific than a parameter segment.
/// * A parameter segment with more constraints is more specific than one with fewer constraints
/// * Segment earlier in the route are evaluated before segments later in the route. For example: /Literal is
/// more specific than /Parameter /Route/With/{parameter} is more specific than /{multiple}/With/{parameters}
/// /Product/{id:int} is more specific than /Product/{id}
///
/// Routes can be ambiguous if: They are composed of literals and those literals have the same values (case
/// insensitive) They are composed of a mix of literals and parameters, in the same relative order and the
/// literals have the same values. For example:
/// * /literal and /Literal /{parameter}/literal and /{something}/literal /{parameter:constraint}/literal and /{something:constraint}/literal
///
/// To calculate the precedence we sort the list of routes as follows:
/// * Shorter routes go first.
/// * A literal wins over a parameter in precedence.
/// * For literals with different values (case insensitive) we choose the lexical order
/// * For parameters with different numbers of constraints, the one with more wins If we get to the end of the
/// comparison routing we've detected an ambiguous pair of routes.
/// * Catch-all routes go last
/// </summary>
Route precedence algorithm. We collect all the routes and sort them from most specific to less specific. The
specificity of a route is given by the specificity of its segments and the position of those segments in the route.
A literal segment is more specific than a parameter segment.
A parameter segment with more constraints is more specific than one with fewer constraints
Segment earlier in the route are evaluated before segments later in the route.

Routes can be ambiguous if: They are composed of literals and those literals have the same values (case
insensitive) They are composed of a mix of literals and parameters, in the same relative order and the
literals have the same values.

To calculate the precedence we sort the list of routes as follows:
Shorter routes go first.
A literal wins over a parameter in precedence.
For literals with different values (case insensitive) we choose the lexical order
For parameters with different numbers of constraints, the one with more wins If we get to the end of the
comparison routing we've detected an ambiguous pair of routes.
Catch-all routes go last
Route precedence algorithm . We collect all the routes and sort them from most specific to less specific . The specificity of a route is given by the specificity of its segments and the position of those segments in the route . A literal segment is more specific than a parameter segment . A parameter segment with more constraints is more specific than one with fewer constraints Segment earlier in the route are evaluated before segments later in the route . Routes can be ambiguous if : They are composed of literals and those literals have the same values ( case insensitive ) They are composed of a mix of literals and parameters in the same relative order and the literals have the same values . To calculate the precedence we sort the list of routes as follows : Shorter routes go first . A literal wins over a parameter in precedence . For literals with different values ( case insensitive ) we choose the lexical order For parameters with different numbers of constraints the one with more wins If we get to the end of the comparison routing we ' ve detected an ambiguous pair of routes . Catch - all routes go last
internal static int RouteComparison(MqttRoute x, MqttRoute y)
        {
            if (ReferenceEquals(x, y))
            {
                return 0;
            }
            var xTemplate = x.Template;
            var yTemplate = y.Template;
            if (xTemplate.Segments.Count != y.Template.Segments.Count)
            {
                if (!xTemplate.Segments[xTemplate.Segments.Count - 1].IsCatchAll && yTemplate.Segments[yTemplate.Segments.Count - 1].IsCatchAll)
                {
                    return -1;
                }
                if (xTemplate.Segments[xTemplate.Segments.Count - 1].IsCatchAll && !yTemplate.Segments[yTemplate.Segments.Count - 1].IsCatchAll)
                {
                    return 1;
                }
                return xTemplate.Segments.Count < y.Template.Segments.Count ? -1 : 1;
            }
            else
            {
                for (var i = 0; i < xTemplate.Segments.Count; i++)
                {
                    var xSegment = xTemplate.Segments[i];
                    var ySegment = yTemplate.Segments[i];
                    if (!xSegment.IsCatchAll && ySegment.IsCatchAll)
                    {
                        return -1;
                    }
                    if (xSegment.IsCatchAll && !ySegment.IsCatchAll)
                    {
                        return 1;
                    }
                    if (!xSegment.IsParameter && ySegment.IsParameter)
                    {
                        return -1;
                    }
                    if (xSegment.IsParameter && !ySegment.IsParameter)
                    {
                        return 1;
                    }
                    if (xSegment.IsParameter)
                    {
                        if (!xSegment.IsOptional && ySegment.IsOptional)
                        {
                            return -1;
                        }
                        if (xSegment.IsOptional && !ySegment.IsOptional)
                        {
                            return 1;
                        }
                        if (xSegment.Constraints.Length > ySegment.Constraints.Length)
                        {
                            return -1;
                        }
                        else if (xSegment.Constraints.Length < ySegment.Constraints.Length)
                        {
                            return 1;
                        }
                    }
                    else
                    {
                        var comparison = string.Compare(xSegment.Value, ySegment.Value, StringComparison.OrdinalIgnoreCase);
                        if (comparison != 0)
                        {
                            return comparison;
                        }
                    }
                }
                throw new InvalidOperationException($@"The following routes are ambiguous:
'{x.Template.TemplateText}' in '{x.Handler.DeclaringType.FullName}.{x.Handler.Name}'
'{y.Template.TemplateText}' in '{y.Handler.DeclaringType.FullName}.{y.Handler.Name}'
");
            }
        }
internal static int RouteComparison ( MqttRoute x , MqttRoute y ) { if ( ReferenceEquals ( x , y ) ) { return 0 ; } var xTemplate = x . Template ; var yTemplate = y . Template ; if ( xTemplate . Segments . Count != y . Template . Segments . Count ) { if ( ! xTemplate . Segments [ xTemplate . Segments . Count - 1 ] . IsCatchAll && yTemplate . Segments [ yTemplate . Segments . Count - 1 ] . IsCatchAll ) { return - 1 ; } if ( xTemplate . Segments [ xTemplate . Segments . Count - 1 ] . IsCatchAll && ! yTemplate . Segments [ yTemplate . Segments . Count - 1 ] . IsCatchAll ) { return 1 ; } return xTemplate . Segments . Count < y . Template . Segments . Count ? - 1 : 1 ; } else { for ( var i = 0 ; i < xTemplate . Segments . Count ; i ++ ) { var xSegment = xTemplate . Segments [ i ] ; var ySegment = yTemplate . Segments [ i ] ; if ( ! xSegment . IsCatchAll && ySegment . IsCatchAll ) { return - 1 ; } if ( xSegment . IsCatchAll && ! ySegment . IsCatchAll ) { return 1 ; } if ( ! xSegment . IsParameter && ySegment . IsParameter ) { return - 1 ; } if ( xSegment . IsParameter && ! ySegment . IsParameter ) { return 1 ; } if ( xSegment . IsParameter ) { if ( ! xSegment . IsOptional && ySegment . IsOptional ) { return - 1 ; } if ( xSegment . IsOptional && ! ySegment . IsOptional ) { return 1 ; } if ( xSegment . Constraints . Length > ySegment . Constraints . Length ) { return - 1 ; } else if ( xSegment . Constraints . Length < ySegment . Constraints . Length ) { return 1 ; } } else { var comparison = string . Compare ( xSegment . Value , ySegment . Value , StringComparison . OrdinalIgnoreCase ) ; if ( comparison != 0 ) { return comparison ; } } } throw new InvalidOperationException ( $@" The following routes are ambiguous:
' { x . Template . TemplateText } ' in ' { x . Handler . DeclaringType . FullName } . { x . Handler . Name } '
' { y . Template . TemplateText } ' in ' { y . Handler . DeclaringType . FullName } . { y . Handler . Name } '
 " ) ; } }
Route precedence algorithm.
Route precedence algorithm .
// Always favor non-optional parameters over optional ones
x
MqttRoute
y
MqttRoute