view libclient-js/malikania/js_rectangle.cpp @ 43:fabbe1759cec

Misc: switch to mlk namespace, closes #589
author David Demelier <markand@malikania.fr>
date Tue, 29 Nov 2016 22:25:17 +0100
parents a47a4477f347
children 96ba0c5cf893
line wrap: on
line source

/*
 * js_rectangle.cpp -- rectangle description (JavaScript binding)
 *
 * Copyright (c) 2013-2016 Malikania Authors
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <cassert>

#include "js_rectangle.hpp"

namespace mlk {

namespace {

unsigned clamp(duk_context* ctx, int value, bool required)
{
    if (value < 0) {
        if (required)
            duk_error(ctx, DUK_ERR_RANGE_ERROR, "%d can not be negative", value);
        else
            value = 0;
    }

    return static_cast<unsigned>(value);
}

rectangle parse(duk_context* ctx, duk_idx_t index, bool required, rectangle rect = {})
{
    StackAssert sa(ctx);

    if (duk_is_object(ctx, index)) {
        auto get = [&] (auto prop) {
            if (required && !duk_has_prop_string(ctx, index, prop))
                duk_error(ctx, DUK_ERR_ERROR, "missing '%s' property", prop);

            duk_get_prop_string(ctx, index, prop);

            if (required && !duk_is_number(ctx, -1)) {
                duk_pop(ctx);
                duk_error(ctx, DUK_ERR_ERROR, "invalid '%s' property (number expected)", prop);
            }

            auto value = duk_to_int(ctx, -1);

            duk_pop(ctx);

            return value;
        };

        rect = rectangle(get("x"), get("y"),
            clamp(ctx, get("width"), required), clamp(ctx, get("height"), required));
    } else if (required)
        duk_error(ctx, DUK_ERR_TYPE_ERROR, "rectangle object expected");

    return rect;
}

duk_ret_t constructor(duk_context* ctx)
{
    rectangle rect;

    if (duk_get_top(ctx) == 4) {
        rect = rectangle(
            duk_require_int(ctx, 0),
            duk_require_int(ctx, 1),
            clamp(ctx, duk_require_int(ctx, 2), true),
            clamp(ctx, duk_require_int(ctx, 3), true)
        );
    } else if (duk_get_top(ctx) == 1)
        rect = parse(ctx, 0, true);

    duk_ret_t ret;

    // Allow both constructor and non constructor calls.
    if (duk_is_constructor_call(ctx)) {
        duk_push_this(ctx);
        dukx_put_rect(ctx, rect);
        duk_pop(ctx);
        ret = 0;
    } else {
        dukx_push_rect(ctx, rect);
        ret = 1;
    }

    return ret;
}

} // !namespace

rectangle dukx_get_rect(duk_context* ctx, duk_idx_t index)
{
    return parse(ctx, index, false);
}

rectangle dukx_require_rect(duk_context* ctx, duk_idx_t index)
{
    return parse(ctx, index, true);
}

rectangle dukx_optional_rect(duk_context* ctx, duk_idx_t index, rectangle def)
{
    return parse(ctx, index, false, std::move(def));
}

void dukx_push_rect(duk_context* ctx, const rectangle& rect)
{
    StackAssert sa(ctx, 1);

    duk_push_object(ctx);
    dukx_put_rect(ctx, rect);
}

void dukx_put_rect(duk_context* ctx, const rectangle& rect)
{
    assert(duk_is_object(ctx, -1));

    StackAssert sa(ctx);

    duk_push_int(ctx, rect.x());
    duk_put_prop_string(ctx, -2, "x");
    duk_push_int(ctx, rect.y());
    duk_put_prop_string(ctx, -2, "y");
    duk_push_uint(ctx, rect.width());
    duk_put_prop_string(ctx, -2, "width");
    duk_push_uint(ctx, rect.height());
    duk_put_prop_string(ctx, -2, "height");
}

void dukx_load_rect(duk_context* ctx)
{
    StackAssert sa(ctx, 0);

    duk_get_global_string(ctx, "Malikania");
    duk_push_c_function(ctx, constructor, DUK_VARARGS);
    duk_put_prop_string(ctx, -2, "Rectangle");
    duk_pop(ctx);
}

} // !mlk