﻿/**
 * Authors: k.inaba
 * License: NYSL 0.9982 http://www.kmonos.net/nysl/
 *
 * Common tricks and utilities for programming in D.
 */
module polemy.tricks;
static import std.array;
static import std.format;

/// Simple Wrapper for std.format.doFormat

string sprintf(string fmt, T...)(T params)
{
	auto writer = std.array.appender!string();
	std.format.formattedWrite(writer, fmt, params);
	return writer.data;
}

unittest
{
	assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" );
	assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" );
}

/// Mixing-in the bean constructor for a class

/*mixin*/ template SimpleConstructor()
{
	static if( is(typeof(super) == Object) || super.tupleof.length==0 )
		this( typeof(this.tupleof) params )
		{
			this.tupleof = params;
		}
	else
		// this parameter list is not always desirable but should work for many cases
		this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
		{
			super(ps);
			this.tupleof = params;
		}
}

/// Mixing-in the (MOST-DERIVED) member-wise comparator for a class

/*mixin*/ template SimpleCompare()
{
	override bool opEquals(Object rhs_) const
	{
		if( auto rhs = cast(typeof(this))rhs_ )
		{
			foreach(i,_; this.tupleof)
				if( this.tupleof[i] != rhs.tupleof[i] )
					return false;
			return true;
		}
		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
	}

	override hash_t toHash() const
	{
		hash_t h = 0;
		foreach(mem; this.tupleof)
			h += typeid(mem).getHash(&mem);
		return h;
	}

	override int opCmp(Object rhs_) const
	{
		if( auto rhs = cast(typeof(this))rhs_ )
		{
			foreach(i,_; this.tupleof)
				if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]))
					return c;
			return 0;
		}
		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
	}
}

unittest
{
	class Temp
	{
		int x;
		string y;
		mixin SimpleConstructor;
		mixin SimpleCompare;
	}
	assert( (new Temp(1,"foo")).x == 1 );
	assert( (new Temp(1,"foo")).y == "foo" );
	assert( !__traits(compiles, new Temp) );
	assert( !__traits(compiles, new Temp(1)) );
	assert( !__traits(compiles, new Temp("foo",1)) );
	assert( new Temp(1,"foo") == new Temp(1,"foo") );
	assert( (new Temp(1,"foo")).toHash == (new Temp(1,"foo")).toHash );
	assert( new Temp(1,"foo") != new Temp(2,"foo") );
	assert( new Temp(1,"foo") != new Temp(1,"bar") );
	assert( new Temp(1,"foo") > new Temp(1,"bar") );
	assert( new Temp(1,"foo") < new Temp(2,"bar") );
}
