@ -102,12 +102,30 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
else return leven_diff < N > ( std : : string_view { sa } , std : : string_view { sb } ) ;
else return leven_diff < N > ( std : : string_view { sa } , std : : string_view { sb } ) ;
}
}
class OrderedString {
public :
constexpr OrderedString ( ) noexcept = default ;
constexpr OrderedString ( OrderedString const & ) noexcept = default ;
constexpr OrderedString ( OrderedString & & ) noexcept = default ;
constexpr OrderedString & operator = ( OrderedString const & ) noexcept = default ;
constexpr OrderedString & operator = ( OrderedString & & ) noexcept = default ;
constexpr virtual ~ OrderedString ( ) = default ;
constexpr virtual std : : string_view view ( ) const noexcept = 0 ;
constexpr virtual size_t size ( ) const noexcept { return view ( ) . size ( ) ; }
constexpr operator std : : string_view ( ) const noexcept { return view ( ) ; }
} ;
/// String that is ordered by Levenshtein distance.
/// String that is ordered by Levenshtein distance.
///
///
/// Used as sorted map key for finding suggestions for possible typos. All valid strings are added as `string_ord`, and the invalid string lookup looks for its closest neighbour(s), those are presented to the user in a "did you mean ..." format with the lowest distance first and so on.
/// Used as sorted map key for finding suggestions for possible typos. All valid strings are added as `string_ord`, and the invalid string lookup looks for its closest neighbour(s), those are presented to the user in a "did you mean ..." format with the lowest distance first and so on.
template < std : : convertible_to < std : : string_view > S >
template < std : : convertible_to < std : : string_view > S >
struct string_ord {
struct string_ord {
using string_type = std : : remove_reference_t < S > ;
using string_type = std : : remove_reference_t < S > ;
constexpr static inline bool is_array = std : : is_array_v < string_type > ;
constexpr static size_t array_size ( ) noexcept requires ( is_array ) { return sizeof ( string_type ) - 1 ; }
//constexpr static inline size_t ARRAY_SIZE = array_size();
template < typename . . . Args > requires ( std : : is_constructible_v < string_type , Args . . . > )
template < typename . . . Args > requires ( std : : is_constructible_v < string_type , Args . . . > )
constexpr string_ord ( Args & & . . . args ) noexcept ( std : : is_nothrow_constructible_v < string_type , Args . . . > )
constexpr string_ord ( Args & & . . . args ) noexcept ( std : : is_nothrow_constructible_v < string_type , Args . . . > )
@ -134,13 +152,16 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
constexpr auto difference_to ( std : : convertible_to < std : : string_view > auto const & str ) const noexcept {
constexpr auto difference_to ( std : : convertible_to < std : : string_view > auto const & str ) const noexcept {
return leven_diff ( std : : forward < decltype ( str ) > ( str ) , m_string ) ;
return leven_diff ( std : : forward < decltype ( str ) > ( str ) , m_string ) ;
}
}
constexpr friend auto operator < = > ( const string_ord & a , const string_ord & b ) noexcept {
template < std : : convertible_to < std : : string_view > S2 >
constexpr friend auto operator < = > ( const string_ord & a , const string_ord < S2 > & b ) noexcept {
auto ab = leven_diff ( a . m_string , b . m_string ) ;
auto ab = leven_diff ( a . m_string , b . m_string ) ;
auto ba = leven_diff ( b . m_string , a . m_string ) ;
auto ba = leven_diff ( b . m_string , a . m_string ) ;
return ab < = > ba ;
return ab < = > ba ;
}
}
constexpr std : : string_view view ( ) const noexcept { if constexpr ( std : : is_array_v < string_type > ) return { m_string , sizeof ( string_type ) - 1 } ; else return { m_string } ; }
constexpr std : : string_view view ( ) const noexcept { if constexpr ( std : : is_array_v < string_type > ) return { m_string , sizeof ( string_type ) - 1 } ; else return { m_string } ; }
constexpr size_t size ( ) const noexcept { if constexpr ( std : : is_array_v < string_type > ) return sizeof ( string_type ) - 1 ; else return view ( ) . size ( ) ; }
constexpr operator std : : string_view ( ) const noexcept { return view ( ) ; }
constexpr operator std : : string_view ( ) const noexcept { return view ( ) ; }
template < typename U > requires ( std : : is_convertible_v < U , std : : string_view > and std : : is_convertible_v < string_type & & , U > )
template < typename U > requires ( std : : is_convertible_v < U , std : : string_view > and std : : is_convertible_v < string_type & & , U > )
@ -161,6 +182,46 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
constexpr string_type & & operator * ( ) & & noexcept { return std : : move ( m_string ) ; }
constexpr string_type & & operator * ( ) & & noexcept { return std : : move ( m_string ) ; }
constexpr string_type const & & operator * ( ) const & & noexcept { return std : : move ( m_string ) ; }
constexpr string_type const & & operator * ( ) const & & noexcept { return std : : move ( m_string ) ; }
constexpr decltype ( auto ) make_dynamic ( ) const noexcept requires ( std : : is_copy_constructible_v < string_ord < S > > ) {
return string_ord < S > { * this } . make_dynamic ( ) ;
}
constexpr auto make_dynamic ( ) & & noexcept {
if constexpr ( std : : is_array_v < string_type > ) {
struct OS : public OrderedString {
std : : array < char , sizeof ( string_type ) > str ;
constexpr virtual ~ OS ( ) = default ;
constexpr OS ( string_type const & str ) noexcept : OrderedString ( ) , str ( util : : array_literal ( std : : forward < decltype ( str ) > ( str ) ) ) { }
constexpr OS ( const OS & ) = default ;
constexpr OS ( OS & & ) = default ;
constexpr OS & operator = ( const OS & ) = default ;
constexpr OS & operator = ( OS & & ) = default ;
constexpr virtual size_t size ( ) const noexcept override { return sizeof ( string_type ) - 1 ; }
constexpr virtual std : : string_view view ( ) const noexcept override { return { str . data ( ) , sizeof ( string_type ) - 1 } ; }
} ;
return OS { std : : move ( m_string ) } ;
} else {
struct OS : public OrderedString {
string_type str ;
constexpr virtual ~ OS ( ) = default ;
constexpr OS ( string_type & & str ) noexcept : OrderedString ( ) , str ( std : : move ( str ) ) { }
constexpr OS ( const OS & ) = default ;
constexpr OS ( OS & & ) = default ;
constexpr virtual std : : string_view view ( ) const noexcept override { return { str } ; }
constexpr OS & operator = ( const OS & ) = default ;
constexpr OS & operator = ( OS & & ) = default ;
} ;
return OS { std : : move ( m_string ) } ;
}
}
private :
private :
template < size_t . . . Is > requires ( sizeof . . . ( Is ) = = sizeof ( string_type ) )
template < size_t . . . Is > requires ( sizeof . . . ( Is ) = = sizeof ( string_type ) )
constexpr string_ord ( const char ( & & l ) [ sizeof ( string_type ) ] , std : : index_sequence < Is . . . > ) noexcept requires ( std : : is_array_v < string_type > )
constexpr string_ord ( const char ( & & l ) [ sizeof ( string_type ) ] , std : : index_sequence < Is . . . > ) noexcept requires ( std : : is_array_v < string_type > )
@ -170,6 +231,8 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
} ;
} ;
template < size_t N >
template < size_t N >
string_ord ( const char ( & l ) [ N ] ) - > string_ord < decltype ( l ) > ;
string_ord ( const char ( & l ) [ N ] ) - > string_ord < decltype ( l ) > ;
template < typename T >
string_ord ( T & & ) - > string_ord < T > ;
/// Used to store all valid command names, so when an invalid one is found, the closest matching one(s) can be suggested to the user in a "did you mean ..." format with the *lowest difference* neighbour(s) to the invalid string first.
/// Used to store all valid command names, so when an invalid one is found, the closest matching one(s) can be suggested to the user in a "did you mean ..." format with the *lowest difference* neighbour(s) to the invalid string first.
template < typename T , std : : convertible_to < std : : string_view > S = std : : string_view >
template < typename T , std : : convertible_to < std : : string_view > S = std : : string_view >