// faults.hpp
//
// Copyright (C) MicroNeil Research Corporation 2009
// This file is part of the CodeDweller library.
// See www.codedweller.com for details.
//
// Faults and Checks are classes we can use in place of assert() to handle
// unreasonable or necessary conditions in our code. They are constructed with
// friendly descriptions (and optionally error codes) and then used just
// like assert() would be used-- except they are designed to remain in the
// production code. After all, assert() is a C (not C++) concept.
//
// A ...Check(test_expression) activates when the test_expression is not true.
// A ...Fault(text_expression) activates when the test_expression is true.
//
// An Abort...() sends it's description to cerr then aborts (no cleanup).
// An Exit...() sends it's description to cerr then exits with a result code.
// A Runtime...() throws a runtime_error (self) with it's description in what().
// A Logic...() throws a logic_error (self) with it's description in what().

#ifndef MNR_faults
#define MNR_faults

#include <stdexcept>
#include <cstdlib>
#include <iostream>
#include <string>

using namespace std;

const int DefaultExitCode = EXIT_FAILURE;                                       // Use this when no code is provided.

class AbortCheck {                                                              // If this check is false we will abort.

  private:

    const string myDescription;                                                 // This is what I have to say.

  public:

    AbortCheck(const string& Text) : myDescription(Text) {}                     // I am constructed with a description

    void operator()(bool X) const {                                             // Apply me like assert(exp)
        if(false == X) {                                                        // If the expression is false then we
            cerr << myDescription << endl;                                      // failed the check so we display our
            abort();                                                            // description and abort.
        }
    }

    const string Description() { return myDescription; }                        // You can ask for my Description.
};

class AbortFault {                                                              // If this fault occurs we will abort.

  private:

    const string myDescription;                                                 // This is what I have to say.

  public:

    AbortFault(const string& Text) :  myDescription(Text) {}                    // I am constructed with a description

    void operator()(bool X) const {                                             // Apply me like assert(! exp)
        if(true == X) {                                                         // If the expression is true then we
            cerr << myDescription << endl;                                      // have a fault so we display our fault
            abort();                                                            // description and abort.
        }
    }

    const string Description() const { return myDescription; }                  // You can ask for my Description.
};

class ExitCheck {                                                               // If this check is false we will exit.

  private:

    const string myDescription;                                                 // This is what I have to say.
    const int myExitCode;                                                       // This is what I send to exit().

  public:

    ExitCheck(const string& Text, int Code=DefaultExitCode) :                   // I am constructed with a description
      myDescription(Text), myExitCode(Code) {}                                  // and (optionlly) an exit code.

    void operator()(bool X) const {                                             // Apply me like assert(exp)
        if(false == X) {                                                        // If the expression is false then we
            cerr << myDescription << endl;                                      // failed the check so we display our
            exit(myExitCode);                                                   // description and exit with our code.
        }
    }

    const string Description() { return myDescription; }                        // You can ask for my Description.
    const int ExitCode() { return myExitCode; }                                 // You can ask for my ExitCode.
};

class ExitFault {                                                               // If this fault occurs we will exit.

  private:

    const string myDescription;                                                 // This is what I have to say.
    const int myExitCode;                                                       // This is what I send to exit().

  public:

    ExitFault(const string& Text, int Code=DefaultExitCode) :                   // I am constructed with a description
      myDescription(Text), myExitCode(Code) {}                                  // and (optionlly) an exit code.

    void operator()(bool X) const {                                             // Apply me like assert(! exp)
        if(true == X) {                                                         // If the expression is true then we
            cerr << myDescription << endl;                                      // have a fault so we display our fault
            exit(myExitCode);                                                   // description and exit with our code.
        }
    }

    const string Description() const { return myDescription; }                  // You can ask for my Description.
    const int ExitCode() const { return myExitCode; }                           // You can ask for my ExitCode.
};

class RuntimeCheck : public runtime_error {                                     // Throw if this check fails.

  public:

    RuntimeCheck(const string& Text) : runtime_error(Text) {}                   // Construct me with a description.

    void operator()(bool X) const {                                             // Apply me like assert(exp)
        if(false == X) {                                                        // If the expression is false then we
            throw *this;                                                        // failed the check so we throw.
        }
    }
};

class RuntimeFault : public runtime_error {                                     // Throw if we find this fault.

  public:

    RuntimeFault(const string& Text) : runtime_error(Text) {}                   // Construct me with a description.

    void operator()(bool X) const {                                             // Apply me like assert(exp)
        if(true == X) {                                                         // If the expression is true then we
            throw *this;                                                        // found the fault so we throw.
        }
    }
};

class LogicCheck : public logic_error {                                         // Throw if this check fails.

  public:

    LogicCheck(const string& Text) : logic_error(Text) {}                       // Construct me with a description.

    void operator()(bool X) const {                                             // Apply me like assert(exp)
        if(false == X) {                                                        // If the expression is false then we
            throw *this;                                                        // failed the check so we throw.
        }
    }
};

class LogicFault : public logic_error {                                         // Throw if we find this fault.

  public:

    LogicFault(const string& Text) : logic_error(Text) {}                       // Construct me with a description.

    void operator()(bool X) const {                                             // Apply me like assert(exp)
        if(true == X) {                                                         // If the expression is true then we
            throw *this;                                                        // found the fault so we throw.
        }
    }
};

#endif

// End Of Include MNR_faults Once Only =========================================
