12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553
/**
 * This module provides a templated function that performs value-preserving
 * conversions between arbitrary types.  This function's behaviour can be
 * extended for user-defined types as needed.
 *
 * Copyright:   Copyright © 2007 Daniel Keep.
 * License:     BSD style: $(LICENSE)
 * Authors:     Daniel Keep
 * Credits:     Inspired in part by Andrei Alexandrescu's work on std.conv.
 */

module tango.util.Convert;

private import tango.core.Exception;
private import tango.core.Traits;
private import tango.core.Tuple : Tuple;

private import tango.math.Math;
private import tango.text.convert.Utf;
private import tango.text.convert.Float;
private import tango.text.convert.Integer;

private import Ascii = tango.text.Ascii;

version( TangoDoc )
{
    /**
     * Attempts to perform a value-preserving conversion of the given value
     * from type S to type D.  If the conversion cannot be performed in any
     * context, a compile-time error will be issued describing the types
     * involved.  If the conversion fails at run-time because the destination
     * type could not represent the value being converted, a
     * ConversionException will be thrown.
     *
     * For example, to convert the string "123" into an equivalent integer
     * value, you would use:
     *
     * -----
     * auto v = to!(int)("123");
     * -----
     *
     * You may also specify a default value which should be returned in the
     * event that the conversion cannot take place:
     *
     * -----
     * auto v = to!(int)("abc", 456);
     * -----
     *
     * The function will attempt to preserve the input value as exactly as
     * possible, given the limitations of the destination format.  For
     * instance, converting a floating-point value to an integer will cause it
     * to round the value to the nearest integer value.
     *
     * Below is a complete list of conversions between built-in types and
     * strings.  Capitalised names indicate classes of types.  Conversions
     * between types in the same class are also possible.
     *
     * -----
     * bool         <-- Integer (0/!0), Char ('t'/'f'), String ("true"/"false")
     * Integer      <-- bool, Real, Char ('0'-'9'), String
     * Real         <-- Integer, String
     * Imaginary    <-- Complex
     * Complex      <-- Integer, Real, Imaginary
     * Char         <-- bool, Integer (0-9)
     * String       <-- bool, Integer, Real, Char
     * -----
     *
     * Conversions between arrays and associative arrays are also supported,
     * and are done element-by-element.
     *
     * You can add support for value conversions to your types by defining
     * appropriate static and instance member functions.  Given a type
     * the_type, any of the following members of a type T may be used:
     *
     * -----
     * the_type to_the_type();
     * static T from_the_type(the_type);
     * -----
     *
     * You may also use "camel case" names:
     *
     * -----
     * the_type toTheType();
     * static T fromTheType(the_type);
     * -----
     *
     * Arrays and associative arrays can also be explicitly supported:
     *
     * -----
     * the_type[] to_the_type_array();
     * the_type[] toTheTypeArray();
     *
     * static T from_the_type_array(the_type[]);
     * static T fromTheTypeArray(the_type[]);
     *
     * the_type[int] to_int_to_the_type_map();
     * the_type[int] toIntToTheTypeMap();
     *
     * static T from_int_to_the_type_map(the_type[int]);
     * static T fromIntToTheTypeMap(the_type[int]);
     * -----
     *
     * If you have more complex requirements, you can also use the generic to
     * and from templated members:
     *
     * -----
     * the_type to(the_type)();
     * static T from(the_type)(the_type);
     * -----
     *
     * These templates will have the_type explicitly passed to them in the
     * template instantiation.
     *
     * Finally, strings are given special support.  The following members will
     * be checked for:
     *
     * -----
     * char[]  toString();
     * wchar[] toString16();
     * dchar[] toString32();
     * char[]  toString();
     * -----
     *
     * The "toString_" method corresponding to the destination string type will be
     * tried first.  If this method does not exist, then the function will
     * look for another "toString_" method from which it will convert the result.
     * Failing this, it will try "toString" and convert the result to the
     * appropriate encoding.
     *
     * The rules for converting to a user-defined type are much the same,
     * except it makes use of the "fromUtf8", "fromUtf16", "fromUtf32" and
     * "fromString" static methods.
     *
     * Note: This module contains imports to other Tango modules that needs
     * semantic analysis to be discovered. If your build tool doesn't do this
     * properly, causing compile or link time problems, import the relevant
     * module explicitly.
     */
    D to(D,S)(S value);
    D to(D,S)(S value, D default_); /// ditto
}
else
{
    D to(D, S)(S value)
    {
        return toImpl!(D,S)(value);
    }
    
    D to(D, S, Def)(S value, Def def=Def.init)
    {
        try
        {
            return toImpl!(D,S)(value);
        }
        catch( ConversionException e )
            {}

        return def;
    }
}

/**
 * This exception is thrown when the to template is unable to perform a
 * conversion at run-time.  This typically occurs when the source value cannot
 * be represented in the destination type.  This exception is also thrown when
 * the conversion would cause an over- or underflow.
 */
class ConversionException : Exception
{
    this( immutable(char)[] msg )
    {
        super( msg );
    }
}

private:

/*
 * So, how is this module structured?
 *
 * Firstly, we need a bunch of support code.  The first block of this contains
 * some CTFE functions for string manipulation (to cut down on the number of
 * template symbols we generate.)
 *
 * The next contains a boat-load of templates.  Most of these are trait
 * templates (things like isPOD, isObject, etc.)  There are also a number of
 * mixins, and some switching templates (like toString_(n).)
 *
 * Another thing to mention is intCmp, which performs a safe comparison
 * between two integers of arbitrary size and signage.
 *
 * Following all this are the templated to* implementations.
 *
 * The actual toImpl template is the second last thing in the module, with the
 * module unit tests coming last.
 */

char ctfe_upper(char c)
{
    if( 'a' <= c && c <= 'z' )
        return cast(char)((c - 'a') + 'A');
    else
        return c;
}

char[] ctfe_camelCase(const(char[]) s)
{
    char[] result;

    bool nextIsCapital = true;

    foreach( c ; s )
    {
        if( nextIsCapital )
        {
            if( c == '_' )
                result ~= c;
            else
            {
                result ~= ctfe_upper(c);
                nextIsCapital = false;
            }
        }
        else
        {
            if( c == '_' )
                nextIsCapital = true;
            else
                result ~= c;
        }
    }

    return result;
}

bool ctfe_isSpace(T)(T c)
{
    static if (T.sizeof is 1)
        return (c <= 32 && (c is ' ' || c is '\t' || c is '\r'
                    || c is '\n' || c is '\v' || c is '\f'));
    else
        return (c <= 32 && (c is ' ' || c is '\t' || c is '\r'
                    || c is '\n' || c is '\v' || c is '\f'))
            || (c is '\u2028' || c is '\u2029');
}

T[] ctfe_triml(T)(T[] source)
{
    if( source.length == 0 )
        return null;

    foreach( i,c ; source )
        if( !ctfe_isSpace(c) )
            return source[i..$];

    return null;
}

T[] ctfe_trimr(T)(T[] source)
{
    if( source.length == 0 )
        return null;

    foreach_reverse( i,c ; source )
        if( !ctfe_isSpace(c) )
            return source[0..i+1];

    return null;
}

T[] ctfe_trim(T)(T[] source)
{
    return ctfe_trimr(ctfe_triml(source));
}

template isPOD(T)
{
    static if( is( T == struct ) || is( T == union ) )
        enum isPOD = true;
    else
        enum isPOD = false;
}

template isObject(T)
{
    static if( is( T == class ) || is( T == interface ) )
        enum isObject = true;
    else
        enum isObject = false;
}

template isUDT(T)
{
    enum isUDT = isPOD!(T) || isObject!(T);
}

template isString(T)
{
    static if( is( typeof(T[]) : const(char[]) )
            || is( typeof(T[]) : const(wchar[]) )
            || is( typeof(T[]) : const(dchar[]) ) )
        enum isString = true;
    else
        enum isString = false;
}

template isMutableString(T)
{
    static if( is( typeof(T[]) == char[] )
            || is( typeof(T[]) == wchar[] )
            || is( typeof(T[]) == dchar[] ) )
        enum isMutableString = true;
    else
        enum isMutableString = false;
}

template isImmutableString(T)
{
    static if( is( typeof(T[]) == immutable(char)[] )
            || is( typeof(T[]) == immutable(wchar)[] )
            || is( typeof(T[]) == immutable(dchar)[] ) )
        enum isImmutableString = true;
    else
        enum isImmutableString = false;
}

template isArrayType(T)
{
    enum isArrayType = isDynamicArrayType!(T) || isStaticArrayType!(T);
}

/*
 * Determines which signed integer type of T and U is larger.
 */
template sintSuperType(T,U)
{
    static if( is( T == long ) || is( U == long ) )
        alias long sintSuperType;
    else static if( is( T == int ) || is( U == int ) )
        alias int sintSuperType;
    else static if( is( T == short ) || is( U == short ) )
        alias short sintSuperType;
    else static if( is( T == byte ) || is( U == byte ) )
        alias byte sintSuperType;
}

/*
 * Determines which unsigned integer type of T and U is larger.
 */
template uintSuperType(T,U)
{
    static if( is( T == ulong ) || is( U == ulong ) )
        alias ulong uintSuperType;
    else static if( is( T == uint ) || is( U == uint ) )
        alias uint uintSuperType;
    else static if( is( T == ushort ) || is( U == ushort ) )
        alias ushort uintSuperType;
    else static if( is( T == ubyte ) || is( U == ubyte ) )
        alias ubyte uintSuperType;
}

template uintOfSize(uint bytes)
{
    static if( bytes == 1 )
        alias ubyte uintOfSize;
    else static if( bytes == 2 )
        alias ushort uintOfSize;
    else static if( bytes == 4 )
        alias uint uintOfSize;
}

/*
 * Safely performs a comparison between two integer values, taking into
 * account different sizes and signages.
 */
int intCmp(T,U)(T lhs, U rhs)
{
    static if( isSignedIntegerType!(T) && isSignedIntegerType!(U) )
    {
        alias sintSuperType!(T,U) S;
        auto l = cast(S) lhs;
        auto r = cast(S) rhs;
        if( l < r ) return -1;
        else if( l > r ) return 1;
        else return 0;
    }
    else static if( isUnsignedIntegerType!(T) && isUnsignedIntegerType!(U) )
    {
        alias uintSuperType!(T,U) S;
        auto l = cast(S) lhs;
        auto r = cast(S) rhs;
        if( l < r ) return -1;
        else if( l > r ) return 1;
        else return 0;
    }
    else
    {
        static if( isSignedIntegerType!(T) )
        {
            if( lhs < 0 )
                return -1;
            else
            {
                static if( U.sizeof >= T.sizeof )
                {
                    auto l = cast(U) lhs;
                    if( l < rhs ) return -1;
                    else if( l > rhs ) return 1;
                    else return 0;
                }
                else
                {
                    auto l = cast(ulong) lhs;
                    auto r = cast(ulong) rhs;
                    if( l < r ) return -1;
                    else if( l > r ) return 1;
                    else return 0;
                }
            }
        }
        else static if( isSignedIntegerType!(U) )
        {
            if( rhs < 0 )
                return 1;
            else
            {
                static if( T.sizeof >= U.sizeof )
                {
                    auto r = cast(T) rhs;
                    if( lhs < r ) return -1;
                    else if( lhs > r ) return 1;
                    else return 0;
                }
                else
                {
                    auto l = cast(ulong) lhs;
                    auto r = cast(ulong) rhs;
                    if( l < r ) return -1;
                    else if( l > r ) return 1;
                    else return 0;
                }
            }
        }
    }
}

template unsupported(immutable(char)[] desc="")
{
    static assert(false, "Unsupported conversion: cannot convert to "
            ~ctfe_trim(D.stringof)~" from "
            ~(desc!="" ? desc~" " : "")~ctfe_trim(S.stringof)~".");
}

template unsupported_backwards(immutable(char)[] desc="")
{
    static assert(false, "Unsupported conversion: cannot convert to "
            ~(desc!="" ? desc~" " : "")~ctfe_trim(D.stringof)
            ~" from "~ctfe_trim(S.stringof)~".");
}

// TN works out the c_case name of the given type.
template TN(T:const(T[]))
{
    static if( is( T == char ) )
        enum TN = "string";
    else static if( is( T == wchar ) )
        enum TN = "wstring";
    else static if( is( T == dchar ) )
        enum TN = "dstring";
    else
        enum TN = TN!(T)~"_array";
}

// ditto
template TN(T:T*)
{
    enum TN = TN!(T)~"_pointer";
}

// ditto
template TN(T)
{
    static if( isAssocArrayType!(T) )
        enum TN = TN!(typeof(T.keys[0]))~"_to_"
            ~TN!(typeof(T.values[0]))~"_map";
    else
        enum TN = ctfe_trim(T.stringof);
}

// Takes care of converting between mutable and immutable strings
D convertString_(D, C)(C[] ret)
{
    static if(isImmutableString!(C))
    {
        static if(isMutableString!(D))
            return cast(D) ret.dup;
        else
            return cast(D) ret;
    }
    else
    {
        static if(isImmutableString!(D))
            return cast(D) ret.idup;
        else
            return cast(D) ret;
    }
}

// Picks an appropriate toString* method from t.text.convert.Utf.
T toString_(T, C)(C[] str)
{
    static if( is( T : const(char[]) ) )
        return convertString_!(T)(tango.text.convert.Utf.toString(str));

    else static if( is( T : const(wchar[]) ) )
        return convertString_!(T)(tango.text.convert.Utf.toString16(str));

    else
        return convertString_!(T)(tango.text.convert.Utf.toString32(str));
}

template UtfNum(T)
{
    enum UtfNum = is(typeof(T[0]) : const(char)) ? "8" : (
            is(typeof(T[0]) : const(wchar)) ? "16" : "32");
}

template StringNum(T)
{
    enum StringNum = is(typeof(T[0]) : const(char)) ? "" : (
            is(typeof(T[0]) : const(wchar)) ? "16" : "32");
}

// Decodes a single dchar character from a string.  Yes, I know they're
// actually code points, but I can't be bothered to type that much.  Although
// I suppose I just typed MORE than that by writing this comment.  Meh.
dchar firstCharOf(T)(T s, out size_t used)
{
    static if( is( T : const(char[]) ) || is( T : const(wchar[]) ) )
    {
        return tango.text.convert.Utf.decode(s, used);
    }
    else
    {
        used = 1;
        return s[0];
    }
}

// This mixin defines a general function for converting to a UDT.
template toUDT()
{
    D toDfromS()
    {
        static if( isString!(S) )
        {
            static if( is( typeof(mixin("D.fromUtf"
                                ~UtfNum!(S)~"(value)")) : D ) )
                return mixin("D.fromUtf"~UtfNum!(S)~"(value)");

            else static if( is( typeof(D.fromUtf8(""c)) : D ) )
                return D.fromUtf8(toString_!(char[])(value));

            else static if( is( typeof(D.fromUtf16(""w)) : D ) )
                return D.fromUtf16(toString_!(wchar[])(value));

            else static if( is( typeof(D.fromUtf32(""d)) : D ) )
                return D.fromUtf32(toString_!(dchar[])(value));

            else static if( is( typeof(D.fromString(""c)) : D ) )
            {
                static if( is( S == char[] ) )
                    return D.fromString(value);

                else
                    return D.fromString(toString_!(char[])(value));
            }

            // Default fallbacks

            else static if( is( typeof(D.from!(S)(value)) : D ) )
                return D.from!(S)(value);

            else
                mixin unsupported!("user-defined type");
        }
        else
        {
            // TODO: Check for templates.  Dunno what to do about them.

            static if( is( typeof(mixin("D.from_"~TN!(S)~"()")) : D ) )
                return mixin("D.from_"~TN!(S)~"()");

            else static if( is( typeof(mixin("D.from"
                                ~ctfe_camelCase(TN!(S))~"()")) : D ) )
                return mixin("D.from"~ctfe_camelCase(TN!(S))~"()");

            else static if( is( typeof(D.from!(S)(value)) : D ) )
                return D.from!(S)(value);

            else
                mixin unsupported!("user-defined type");
        }
    }
}

// This mixin defines a general function for converting from a UDT.
template fromUDT(immutable(char)[] fallthrough="")
{
    D toDfromS()
    {
        static if( isString!(D) )
        {
            static if( is( typeof(convertString_!(D)(mixin("value.toString"
                                ~StringNum!(D)~"()"))) : D ) )
                return convertString_!(D)(mixin("value.toString"~StringNum!(D)~"()"));

            else static if( is( typeof(value.toString()) : const(char[]) ) )
                return toString_!(D)(value.toString());

            else static if( is( typeof(value.toString16()) : const(wchar[]) ) )
                return toString_!(D)(value.toString16);

            else static if( is( typeof(value.toString32()) : const(dchar[]) ) )
                return toString_!(D)(value.toString32);

            else static if( is( typeof(value.toString()) : const(char[]) ) )
            {
                static if( is( D : const(char[]) ) )
                    return value.toString();

                else
                {
                    return toString_!(D)(value.toString());
                }
            }

            // Default fallbacks

            else static if( is( typeof(value.to!(D)()) : D ) )
                return value.to!(D)();

            else static if( fallthrough != "" )
                mixin(fallthrough);

            else
                mixin unsupported!("user-defined type");
        }
        else
        {
            // TODO: Check for templates.  Dunno what to do about them.

            static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) )
                return mixin("value.to_"~TN!(D)~"()");

            else static if( is( typeof(mixin("value.to"
                                ~ctfe_camelCase(TN!(D))~"()")) : D ) )
                return mixin("value.to"~ctfe_camelCase(TN!(D))~"()");

            else static if( is( typeof(value.to!(D)()) : D ) )
                return value.to!(D)();

            else static if( fallthrough != "" )
                mixin(fallthrough);

            else
                mixin unsupported!("user-defined type");
        }
    }
}

template convError()
{
    void throwConvError()
    {
        // Since we're going to use to!(T) to convert the value to a string,
        // we need to make sure we don't end up in a loop...
        static if( isString!(D) || !is( typeof(to!(immutable(char)[])(value)) == immutable(char)[] ) )
        {
            throw new ConversionException("Could not convert a value of type "
                    ~S.stringof~" to type "~D.stringof~".");
        }
        else
        {
            throw new ConversionException("Could not convert `"
                    ~to!(immutable(char)[])(value)~"` of type "
                    ~S.stringof~" to type "~D.stringof~".");
        }
    }
}

D toBool(D,S)(S value)
{
    static assert(is(D==bool));

    static if( isIntegerType!(S) /+|| isRealType!(S) || isImaginaryType!(S)
                || isComplexType!(S)+/ )
        // The weird comparison is to support NaN as true
        return !(value == 0);

    else static if( isCharType!(S) )
    {
        switch( value )
        {
            case 'F': case 'f':
                return false;

            case 'T': case 't':
                return true;

            default:
                mixin convError;
                throwConvError();
                assert(0);
        }
    }

    else static if( isString!(S) )
    {
        if(value.length == 5 || value.length == 4)
        {
            char[5] buf;
            buf[0..value.length] = value[];
            switch( Ascii.toLower(buf[0..value.length]) )
            {
                case "false":
                    return false;

                case "true":
                    return true;

                default:
            }
        }
        mixin convError;
        throwConvError();
        assert(0);
    }
    /+
    else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
    {
        mixin unsupported!("array type");
    }
    else static if( isAssocArrayType!(S) )
    {
        mixin unsupported!("associative array type");
    }
    else static if( isPointerType!(S) )
    {
        mixin unsupported!("pointer type");
    }
    else static if( is( S == alias ) )
    {
        mixin unsupported!("alias'ed type");
    }
    // +/
    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin fromUDT;
        return toDfromS();
    }
    else
    {
        mixin unsupported;
    }
}

D toIntegerFromInteger(D,S)(S value)
{
    static if( (cast(ulong) D.max) < (cast(ulong) S.max)
            || (cast(long) D.min) > (cast(long) S.min) )
    {
        mixin convError; // TODO: Overflow error

        if( intCmp(value,D.min)<0 || intCmp(value,D.max)>0 )
        {
            throwConvError();
        }
    }
    return cast(D) value;
}

D toIntegerFromReal(D,S)(S value)
{
    auto v = tango.math.Math.round(value);
    if( (cast(real) D.min) <= v && v <= (cast(real) D.max) )
    {
        return cast(D) v;
    }
    else
    {
        mixin convError; // TODO: Overflow error
        throwConvError();
        assert(0);
    }
}

D toIntegerFromString(D,S)(S value)
{
    static if( is( S charT : charT[] ) )
    {
        mixin convError;

        static if( is( D == ulong ) )
        {
            // Check for sign
            S s = value;

            if( s.length == 0 )
                throwConvError();

            else if( s[0] == '-' )
                throwConvError();

            else if( s[0] == '+' )
                s = s[1..$];

            size_t len;
            auto result = tango.text.convert.Integer.convert(s, 10, &len);

            if( len < s.length || len == 0 )
                throwConvError();

            return result;
        }
        else
        {
            size_t len;
            auto result = tango.text.convert.Integer.parse(value, 10, &len);

            if( len < value.length || len == 0 )
                throwConvError();

            return toIntegerFromInteger!(D,long)(result);
        }
    }
}

D toInteger(D,S)(S value)
{
    static if( is( S == bool ) )
        return (value ? 1 : 0);

    else static if( isIntegerType!(S) )
    {
        return toIntegerFromInteger!(D,S)(value);
    }
    else static if( isCharType!(S) )
    {
        if( value >= '0' && value <= '9' )
        {
            return cast(D)(value - '0');
        }
        else
        {
            mixin convError;
            throwConvError();
            assert(0);
        }
    }
    else static if( isRealType!(S) )
    {
        return toIntegerFromReal!(D,S)(value);
    }
    else static if( isString!(S) )
    {
        return toIntegerFromString!(D,S)(value);
    }
    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin fromUDT;
        return toDfromS();
    }
    else
        mixin unsupported;
}

D toReal(D,S)(S value)
{
    /+static if( is( S == bool ) )
        return (value ? 1.0 : 0.0);

    else+/ static if( isIntegerType!(S) || isRealType!(S) )
        return cast(D) value;

    /+else static if( isCharType!(S) )
        return cast(D) to!(uint)(value);+/

    else static if( isString!(S) )
    {
        /+
        try
        {
            return tango.text.convert.Float.toFloat(value);
        }
        catch( IllegalArgumentException e )
        {
            mixin convError;
            throwConvError();
        }
        +/

        mixin convError;

        size_t len;
        auto r = tango.text.convert.Float.parse(value, &len);
        if( len < value.length || len == 0 )
            throwConvError();

        return r;
    }

    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin fromUDT;
        return toDfromS();
    }
    else
        mixin unsupported;
}

D toImaginary(D,S)(S value)
{
    /+static if( is( S == bool ) )
        return (value ? 1.0i : 0.0i);

    else+/ static if( isComplexType!(S) )
    {
        if( value.re == 0.0 )
            return value.im * cast(D)1.0i;

        else
        {
            mixin convError;
            throwConvError();
            assert(0);
        }
    }
    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin fromUDT;
        return toDfromS();
    }
    else
        mixin unsupported;
}

D toComplex(D,S)(S value)
{
    static if( isIntegerType!(S) || isRealType!(S) || isImaginaryType!(S)
            || isComplexType!(S) )
        return cast(D) value;

    /+else static if( isCharType!(S) )
        return cast(D) to!(uint)(value);+/

    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin fromUDT;
        return toDfromS();
    }
    else
        mixin unsupported;
}

D toChar(D,S)(S value)
{
    static if( is( S == bool ) )
        return (value ? 't' : 'f');

    else static if( isIntegerType!(S) )
    {
        if( value >= 0 && value <= 9 )
            return cast(D) (value+'0');

        else
        {
            mixin convError; // TODO: Overflow error
            throwConvError();
            assert(0);
        }
    }
    else static if( isString!(S) )
    {
        void fail()
        {
            mixin convError;
            throwConvError();
        }

        if( value.length == 0 )
        {
            fail();
            assert(0);
        }

        else
        {
            size_t used;
            dchar c = firstCharOf(value, used);

            if( used < value.length )
            {
                fail(); // TODO: Overflow error
                assert(0);
            }

            if( (cast(size_t) c) > (cast(size_t) D.max) )
            {
                fail(); // TODO: Overflow error
                assert(0);
            }

            return cast(D) c;
        }
    }
    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin fromUDT;
        return toDfromS();
    }
    else
        mixin unsupported;
}

D toStringFromString(D,S)(S value)
{
    return toString_!(D)(value);
}

enum immutable(char)[] CHARS =
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e";

D toStringFromChar(D,S)(S value)
{
    static if( is( D : S[] ) )
    {
        static if( is( S == char ) )
        {
            if( 0x20 <= value && value <= 0x7e )
            {
                return convertString_!(D)((&CHARS[value-0x20])[0..1]);
            }
        }
        auto r = new S[1];
        r[0] = value;
        return convertString_!(D)(r);
    }
    else
    {
        S[1] temp;
        temp[0] = value;
        return toStringFromString!(D,S[])(temp);
    }
}

D toString(D,S)(S value)
{
    static if( is( S == bool ) )
    {
        return convertString_!(D)(value ? "true" : "false");
    }
    else static if( isCharType!(S) )
        return toStringFromChar!(D,S)(value);

    else static if( isIntegerType!(S) )
    {
        // TODO: Make sure this works with ulongs.
        return convertString_!(D)(mixin("tango.text.convert.Integer.toString"~StringNum!(D)~"(value)"));
    }

    else static if( isRealType!(S) )
        return convertString_!(D)(mixin("tango.text.convert.Float.toString"~StringNum!(D)~"(value)"));

    else static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
        mixin unsupported!("array type");

    else static if( isAssocArrayType!(S) )
        mixin unsupported!("associative array type");

    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin fromUDT;
        return toDfromS();
    }
    else
        mixin unsupported;
}

D fromString(D,S)(D value)
{
    static if( isDynamicArrayType!(S) || isStaticArrayType!(S) )
        mixin unsupported_backwards!("array type");

    else static if( isAssocArrayType!(S) )
        mixin unsupported_backwards!("associative array type");

    else static if( isPOD!(S) || isObject!(S) )
    {
        mixin toUDT;
        return toDfromS();
    }
    else
        mixin unsupported_backwards;
}

D toArrayFromArray(D,S)(S value)
{
    alias BaseTypeOf!(typeof(D[0])) De;

    De[] result; result.length = value.length;
    scope(failure) delete result;

    foreach( i,e ; value )
        result[i] = to!(De)(e);

    /* Safe because it is newly allocated */
    return cast(D)result;
}

D toMapFromMap(D,S)(S value)
{
    alias typeof(D.keys[0])   Dk;
    alias typeof(D.values[0]) Dv;

    D result;

    foreach( k,v ; value )
        result[ to!(Dk)(k) ] = to!(Dv)(v);

    return result;
}

D toFromUDT(D,S)(S value)
{
    // Try value.to* first
    static if( is( typeof(mixin("value.to_"~TN!(D)~"()")) : D ) )
        return mixin("value.to_"~TN!(D)~"()");

    else static if( is( typeof(mixin("value.to"
                        ~ctfe_camelCase(TN!(D))~"()")) : D ) )
        return mixin("value.to"~ctfe_camelCase(TN!(D))~"()");

    else static if( is( typeof(value.to!(D)()) : D ) )
        return value.to!(D)();

    // Ok, try D.from* now
    else static if( is( typeof(mixin("D.from_"~TN!(S)~"(value)")) : D ) )
        return mixin("D.from_"~TN!(S)~"(value)");

    else static if( is( typeof(mixin("D.from"
                        ~ctfe_camelCase(TN!(S))~"(value)")) : D ) )
        return mixin("D.from"~ctfe_camelCase(TN!(S))~"(value)");

    else static if( is( typeof(D.from!(S)(value)) : D ) )
        return D.from!(S)(value);

    // Give up
    else
        mixin unsupported;
}

D toImpl(D,S)(S value)
{
    static if( is( D == S ) )
        return value;

    else static if( is( S BaseType == enum ) )
        return toImpl!(D,BaseType)(value);

    else static if( isArrayType!(D) && isArrayType!(S)
            && is( typeof(D[0]) == typeof(S[0]) ) )
        // Special-case which catches to!(T[])!(T[n]).
        return value;

    else static if( is( D == bool ) )
        return toBool!(D,S)(value);

    else static if( isIntegerType!(D) )
        return toInteger!(D,S)(value);

    else static if( isRealType!(D) )
        return toReal!(D,S)(value);

    else static if( isImaginaryType!(D) )
        return toImaginary!(D,S)(value);

    else static if( isComplexType!(D) )
        return toComplex!(D,S)(value);

    else static if( isCharType!(D) )
        return toChar!(D,S)(value);

    else static if( isString!(D) && isString!(S) )
        return toStringFromString!(D,S)(value);

    else static if( isString!(D) )
        return toString!(D,S)(value);

    else static if( isString!(S) )
        return fromString!(D,S)(value);

    else static if( isArrayType!(D) && isArrayType!(S) )
        return toArrayFromArray!(D,S)(value);

    else static if( isAssocArrayType!(D) && isAssocArrayType!(S) )
        return toMapFromMap!(D,S)(value);

    else static if( isUDT!(D) || isUDT!(S) )
        return toFromUDT!(D,S)(value);

    else
        mixin unsupported;
}

debug ( ConvertTest )
{
    void main() {}
}

debug( UnitTest ):


bool ex(T)(lazy T v)
{
    bool result = false;
    try
    {
        v();
    }
    catch( ConversionException _ )
    {
        result = true;
    }
    return result;
}

bool nx(T)(lazy T v)
{
    bool result = true;
    try
    {
        v();
    }
    catch( ConversionException _ )
    {
        result = false;
    }
    return result;
}

struct Foo
{
    int toInt() { return 42; }

    immutable(char)[] toString() { return "string foo"; }

    int[] toIntArray() { return [1,2,3]; }

    Bar toBar()
    {
        Bar result; return result;
    }

    T to(T)()
    {
        static if( is( T == bool ) )
            return true;
        else
            static assert( false );
    }
}

struct Bar
{
    real toReal()
    {
        return 3.14159;
    }

    ireal toIreal()
    {
        return 42.0i;
    }
}

struct Baz
{
    static Baz fromFoo(Foo foo)
    {
        Baz result; return result;
    }

    Bar toBar()
    {
        Bar result; return result;
    }
}

unittest
{
    /*
     * bool
     */
    static assert( !is( typeof(to!(bool)(1.0)) ) );
    static assert( !is( typeof(to!(bool)(1.0i)) ) );
    static assert( !is( typeof(to!(bool)(1.0+1.0i)) ) );

    assert( to!(bool)(0) == false );
    assert( to!(bool)(1) == true );
    assert( to!(bool)(-1) == true );

    assert( to!(bool)('t') == true );
    assert( to!(bool)('T') == true );
    assert( to!(bool)('f') == false );
    assert( to!(bool)('F') == false );
    assert(ex( to!(bool)('x') ));

    assert( to!(bool)("true") == true );
    assert( to!(bool)("false") == false );
    assert( to!(bool)("TrUe") == true );
    assert( to!(bool)("fAlSe") == false );

    /*
     * Integer
     */
    assert( to!(int)(42L) == 42 );
    assert( to!(byte)(42) == cast(byte)42 );
    assert( to!(short)(-1701) == cast(short)-1701 );
    assert( to!(long)(cast(ubyte)72) == 72L );

    assert(nx( to!(byte)(127) ));
    assert(ex( to!(byte)(128) ));
    assert(nx( to!(byte)(-128) ));
    assert(ex( to!(byte)(-129) ));

    assert(nx( to!(ubyte)(255) ));
    assert(ex( to!(ubyte)(256) ));
    assert(nx( to!(ubyte)(0) ));
    assert(ex( to!(ubyte)(-1) ));

    assert(nx( to!(long)(9_223_372_036_854_775_807UL) ));
    assert(ex( to!(long)(9_223_372_036_854_775_808UL) ));
    assert(nx( to!(ulong)(0L) ));
    assert(ex( to!(ulong)(-1L) ));

    assert( to!(int)(3.14159) == 3 );
    assert( to!(int)(2.71828) == 3 );

    assert( to!(int)("1234") == 1234 );

    assert( to!(int)(true) == 1 );
    assert( to!(int)(false) == 0 );

    assert( to!(int)('0') == 0 );
    assert( to!(int)('9') == 9 );

    /*
     * Real
     */
    assert( to!(real)(3) == 3.0 );
    assert( to!(real)("1.125") == 1.125 );

    /*
     * Imaginary
     */
    static assert( !is( typeof(to!(ireal)(3.0)) ) );

    assert( to!(ireal)(0.0+1.0i) == 1.0i );
    assert(nx( to!(ireal)(0.0+1.0i) ));
    assert(ex( to!(ireal)(1.0+0.0i) ));

    /*
     * Complex
     */
    assert( to!(creal)(1) == (1.0+0.0i) );
    assert( to!(creal)(2.0) == (2.0+0.0i) );
    assert( to!(creal)(3.0i) == (0.0+3.0i) );

    /*
     * Char
     */
    assert( to!(char)(true) == 't' );
    assert( to!(char)(false) == 'f' );

    assert( to!(char)(0) == '0' );
    assert( to!(char)(9) == '9' );

    assert(ex( to!(char)(-1) ));
    assert(ex( to!(char)(10) ));

    assert( to!(char)("a"d) == 'a' );
    assert( to!(dchar)("ε"c) == 'ε' );

    assert(ex( to!(char)("ε"d) ));

    /*
     * String-string
     */
    assert( to!(char[])("Í love to æt "w) == "Í love to æt "c );
    assert( to!(char[])("them smûrƒies™,"d) == "them smûrƒies™,"c );
    assert( to!(wchar[])("Smûrfies™ I love"c) == "Smûrfies™ I love"w );
    assert( to!(wchar[])("2 食い散らす"d) == "2 食い散らす"w );
    assert( to!(dchar[])("bite đey µgly"c) == "bite đey µgly"d );
    assert( to!(dchar[])("headž ㍳ff"w) == "headž ㍳ff"d );
    // ... nibble on they bluish feet.

    /*
     * String
     */
    assert( to!(char[])(true) == "true" );
    assert( to!(char[])(false) == "false" );

    assert( to!(char[])(12345678) == "12345678" );
    assert( to!(char[])(1234.567800) == "1234.57");

    assert( to!( char[])(cast(char) 'a') == "a"c );
    assert( to!(wchar[])(cast(char) 'b') == "b"w );
    assert( to!(dchar[])(cast(char) 'c') == "c"d );
    assert( to!( char[])(cast(wchar)'d') == "d"c );
    assert( to!(wchar[])(cast(wchar)'e') == "e"w );
    assert( to!(dchar[])(cast(wchar)'f') == "f"d );
    assert( to!( char[])(cast(dchar)'g') == "g"c );
    assert( to!(wchar[])(cast(dchar)'h') == "h"w );
    assert( to!(dchar[])(cast(dchar)'i') == "i"d );

    /*
     * Array-array
     */
    assert( to!(ubyte[])([1,2,3]) == [cast(ubyte)1, 2, 3] );
    assert( to!(bool[])(["true"[], "false"]) == [true, false] );

    /*
     * Map-map
     */
    {
        immutable(char)[][int] src = [1:"true"[], 2:"false"];
        bool[ubyte] dst = to!(bool[ubyte])(src);
        assert( dst.keys.length == 2 );
        assert( dst[1] == true );
        assert( dst[2] == false );
    }

    /*
     * UDT
     */
    {
        Foo foo;

        assert( to!(bool)(foo) == true );
        assert( to!(int)(foo) == 42 );
        assert( to!(char[])(foo) == "string foo" );
        assert( to!(wchar[])(foo) == "string foo"w );
        assert( to!(dchar[])(foo) == "string foo"d );
        assert( to!(int[])(foo) == [1,2,3] );
        assert( to!(ireal)(to!(Bar)(foo)) == 42.0i );
        assert( to!(real)(to!(Bar)(to!(Baz)(foo))) == 3.14159 );
    }

    /*
     * Default values
     */
    {
        assert( to!(int)("123", 456) == 123,
                `to!(int)("123", 456) == "` ~ to!(char[])(
                    to!(int)("123", 456)) ~ `"` );
        assert( to!(int)("abc", 456) == 456,
                `to!(int)("abc", 456) == "` ~ to!(char[])(
                    to!(int)("abc", 456)) ~ `"` );
    }

    /*
     * Ticket #1486
     */
    {
        assert(ex( to!(int)("") ));

        assert(ex( to!(real)("Foo") ));
        assert(ex( to!(real)("") ));
        assert(ex( to!(real)("0x1.2cp+9") ));

        // From d0c's patch
        assert(ex( to!(int)("0x20") ));
        assert(ex( to!(int)("0x") ));
        assert(ex( to!(int)("-") ));
        assert(ex( to!(int)("-0x") ));

        assert( to!(real)("0x20") == cast(real) 0x20 );
        assert(ex( to!(real)("0x") ));
        assert(ex( to!(real)("-") ));
    }

    /*
     * Const and immutable
     */
    {
            assert( to!(immutable(char)[])("Í love to æt "w.dup) == "Í love to æt "c );
            assert( to!(immutable(char)[])("them smûrƒies™,"d.dup) == "them smûrƒies™,"c );
            assert( to!(immutable(wchar)[])("Smûrfies™ I love"c.dup) == "Smûrfies™ I love"w );
            assert( to!(immutable(wchar)[])("2 食い散らす"d.dup) == "2 食い散らす"w );
            assert( to!(immutable(dchar)[])("bite đey µgly"c.dup) == "bite đey µgly"d );
            assert( to!(immutable(dchar)[])("headž ㍳ff"w.dup) == "headž ㍳ff"d );
            // ... nibble on they bluish feet.

            assert( to!(immutable(char)[])("食い散らす"c.dup) == "食い散らす"c );
            assert( to!(immutable(wchar)[])("食い散らす"w.dup) == "食い散らす"w );
            assert( to!(immutable(dchar)[])("食い散らす"d.dup) == "食い散らす"d );
            
            assert( to!(immutable(char)[])(true) == "true" );
            assert( to!(immutable(char)[])(false) == "false" );

            assert( to!(immutable(char)[])(12345678) == "12345678" );
            assert( to!(immutable(char)[])(1234.567800) == "1234.57");

            assert( to!(immutable( char)[])(cast(char) 'a') == "a"c );
            assert( to!(immutable(wchar)[])(cast(char) 'b') == "b"w );
            assert( to!(immutable(dchar)[])(cast(char) 'c') == "c"d );
            assert( to!(immutable( char)[])(cast(wchar)'d') == "d"c );
            assert( to!(immutable(wchar)[])(cast(wchar)'e') == "e"w );
            assert( to!(immutable(dchar)[])(cast(wchar)'f') == "f"d );
            assert( to!(immutable( char)[])(cast(dchar)'g') == "g"c );
            assert( to!(immutable(wchar)[])(cast(dchar)'h') == "h"w );
            assert( to!(immutable(dchar)[])(cast(dchar)'i') == "i"d );

            assert( to!(immutable(ubyte)[])([1,2,3]) == [cast(ubyte)1, 2, 3] );
            
            assert( to!(const(char)[])("Í love to æt "w) == "Í love to æt "c );

            Foo foo;

            assert( to!(immutable(char)[])(foo) == "string foo" );
            assert( to!(immutable(wchar)[])(foo) == "string foo"w );
            assert( to!(immutable(dchar)[])(foo) == "string foo"d );
            /* assert( to!(immutable(int)[])(foo) == [1,2,3] ); */
    }
    
    /*
     * Pass through
     */
    {
        assert( to!(int)(cast(int)1) == 1 );
        assert( to!(char[])("abc".dup) == "abc" );
        assert( to!(immutable(char)[])("abc") == "abc" );
        assert( to!(immutable(dchar)[])("abc"d) == "abc"d );
    }
}