Newer
Older
ddhx / src / converter.d
/// Type conversion routines.
/// Copyright: dd86k <dd@dax.moe>
/// License: MIT
/// Authors: $(LINK2 https://github.com/dd86k, dd86k)
module converter;

import std.format : FormatSpec, singleSpec, unformatValue;
import std.encoding : transcode;
import error;

// NOTE: template to(T) can turn string values into anything.

//TODO: More types
//      - FILETIME
//      - GUID (little-endian)/UUID (big-endian)

/// Convert data to a raw pointer dynamically.
/// Params:
///     data = Data pointer receiver.
///     len = Data length receiver.
///     val = Value to parse.
///     type = Name of expected type.
/// Returns: Error code.
int convertToRaw(ref void *data, ref size_t len, string val, string type)
{
    union TypeData {
        ulong  u64;
        long   i64;
        uint   u32;
        int    i32;
        ushort u16;
        short  i16;
        ubyte  u8;
        byte   i8;
        dstring s32;
        wstring s16;
        string  s8;
    }
    __gshared TypeData types;
    
    version (Trace) trace("data=%s val=%s type=%s", data, val, type);
    
    //TODO: utf16le and all.
    //      bswap all wchars?
    
    int e = void;
    with (types) switch (type) {
    case "s32", "dstring":
        e = convertToVal(s32, val);
        if (e) return e;
        data = cast(void*)s32.ptr;
        len  = s32.length * dchar.sizeof;
        break;
    case "s16", "wstring":
        e = convertToVal(s16, val);
        if (e) return e;
        data = cast(void*)s16.ptr;
        len  = s16.length * wchar.sizeof;
        break;
    case "s8", "string":
        data = cast(void*)val.ptr;
        len  = val.length;
        break;
    case "u64", "ulong":
        e = convertToVal(u64, val);
        if (e) return e;
        data = &u64;
        len  = u64.sizeof;
        break;
    case "i64", "long":
        e = convertToVal(i64, val);
        if (e) return e;
        data = &i64;
        len  = i64.sizeof;
        break;
    case "u32", "uint":
        e = convertToVal(u32, val);
        if (e) return e;
        data = &u32;
        len  = u32.sizeof;
        break;
    case "i32", "int":
        e = convertToVal(i32, val);
        if (e) return e;
        data = &i32;
        len  = i32.sizeof;
        break;
    case "u16", "ushort":
        e = convertToVal(u16, val);
        if (e) return e;
        data = &u16;
        len  = u16.sizeof;
        break;
    case "i16", "short":
        e = convertToVal(i16, val);
        if (e) return e;
        data = &i16;
        len  = i16.sizeof;
        break;
    case "u8", "ubyte":
        e = convertToVal(u8, val);
        if (e) return e;
        data = &u8;
        len  = u8.sizeof;
        break;
    case "i8", "byte":
        e = convertToVal(i8, val);
        if (e) return e;
        data = &i8;
        len  = i8.sizeof;
        break;
    default:
        return errorSet(ErrorCode.invalidType);
    }
    
    return ErrorCode.success;
}

/// Convert data depending on the type supplied at compile-time.
/// Params:
///     v = Data reference.
///     val = String value.
/// Returns: Error code.
int convertToVal(T)(ref T v, string val)
{
    import std.conv : ConvException;
    try {
        //TODO: ubyte[] input
        static if (is(T == wstring) || is(T == dstring))
        {
            transcode(val, v);
        } else { // Scalar
            const size_t len = val.length;
            FormatSpec!char fmt = void;
            if (len >= 3 && val[0..2] == "0x")
            {
                fmt = singleSpec("%x");
                val = val[2..$];
            }
            else if (len >= 2 && val[0] == '0')
            {
                fmt = singleSpec("%o");
                val = val[1..$];
            } else {
                fmt = singleSpec("%d");
            }
            v = unformatValue!T(val, fmt);
        }
    }
    catch (Exception ex)
    {
        return errorSet(ex);
    }
    
    return 0;
}

/// 
@system unittest {
    int i;
    assert(convertToVal(i, "256") == ErrorCode.success);
    assert(i == 256);
    assert(convertToVal(i, "0100") == ErrorCode.success);
    assert(i == 64);
    assert(convertToVal(i, "0x100") == ErrorCode.success);
    assert(i == 0x100);
    ulong l;
    assert(convertToVal(l, "1000000000000000") == ErrorCode.success);
    assert(l == 1000000000000000);
    assert(convertToVal(l, "01000000000000000") == ErrorCode.success);
    assert(l == 35184372088832);
    assert(convertToVal(l, "0x1000000000000000") == ErrorCode.success);
    assert(l == 0x1000000000000000);
    wstring w;
    assert(convertToVal(w, "hello") == ErrorCode.success);
    assert(w == "hello"w);
}