/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
#include "fossil-scm/fossil.hpp" /* MUST come first b/c of config bits. */
#include <cassert>
#include <iostream>
#include <fstream>
#include <list>
#include <cstdlib> /* EXIT_SUCCESS, EXIT_FAILURE */
#define CERR std::cerr << __FILE__ << ":" << std::dec << __LINE__ << " : "
#define COUT std::cout << __FILE__ << ":" << std::dec << __LINE__ << " : "
#define RCStr(C) fsl_rc_cstr(C)
/**
A functor for use with fsl::Stmt::eachRow().
*/
struct RowDumper {
std::ostream & os;
char const * separator;
bool showHeader;
explicit RowDumper(std::ostream & os = std::cout)
: os(os),
separator("\t"),
showHeader(true)
{}
void operator()(fsl::Stmt &s) const{
int i;
int const n = s.columnCount();
if(this->showHeader && (1==s.stepCount())){
for(i = 0; i < n; ++i ){
this->os << (i ? this->separator : "") << s.columnName(i);
}
this->os << '\n';
}
for(i = 0; i < n; ++i ){
this->os << (i ? this->separator : "") << s.getText(i);
}
this->os << '\n';
}
};
static void test_db_1(){
namespace f = fsl;
char const * dbName = 0
? "/fail"
: ":memory:"
;
//using fsl::Exception;
//throw Exception(FSL_RC_NYI, "Just %s", "testing");
f::Db db;
f::Stmt st(db);
db.open(dbName, FSL_OPEN_F_RWC);
COUT << "Opened db: "<<db.filename()<<'\n';
db.begin();
st.prepare("CREATE TABLE t(a INTEGER,b TEXT)").stepExpectDone();
st.finalize().prepare("INSERT INTO t(a,b) VALUES(?1,?2)");
// Try out the StmtBinder for insertions...
f::StmtBinder b(st);
b
(1)("once").insert()
(2)("twice").insert()
(3.3)("Quite possibly thrice-point-thrice")
.insert();
int threw = 0;
try{
b(7)(8)
("must throw - too many bind() values");
}catch(f::Exception const & ex){
threw = ex.code();
}
assert(FSL_RC_RANGE==threw);
// Bind a std::list of parameters... (not terribly useful, probably)
typedef std::list<std::string> LI;
LI li;
li.push_back("4");
li.push_back("fource. Or fice.");
b.bindList(li).insert();
st.finalize()
.prepare("SELECT rowid, a, b FROM t ORDER BY a")
.eachRow( RowDumper() )
.finalize();
f::Buffer sb;
(sb << "SELECT ").appendf("%Q", "percent 'Q'");
st.prepare(sb)
.eachRow( RowDumper() );
}
static void test_stream_1(fsl::Context & cx){
namespace f = fsl;
f::ContextOStream fout(cx);
{
char const * sym = "rid:1";
fout << "UUID of ["<<sym<<"]: " << cx.symToUuid(sym)<<'\n';
}
{
fsl_uuid_cstr uuid = NULL;
fsl_id_t rid = 0;
fsl_checkout_version_info(cx, &rid, &uuid);
fout << "Checkout version: "<<rid << " ==> "<<uuid<<'\n';
}
char const * filename = 1
? __FILE__
: "/fail"
;
std::ifstream is(filename);
if(!is.good()){
throw f::Exception(FSL_RC_IO,"Cannot open input file: %s",
filename);
}
f::Buffer buf;
int rc = fsl_buffer_fill_from( buf,
f::fsl_input_f_std_istream,
&is );
if(rc){
throw f::Exception(rc,"Error (%d) %s reading from file: %s",
rc, fsl_rc_cstr(rc), filename);
}
assert(buf.used());
COUT << "Read "<< buf.used()
<<" bytes from "<<filename
<<" via std::istream proxy.\n";
buf.reset();
assert(0==buf.used());
assert(0<buf.capacity());
buf << "Hi, world! ";
buf.appendf("%Q", "sql 'escaped'") << '\n';
COUT << "Buffer contents: " << buf;
buf.reset();
assert(0==buf.used());
assert(0<buf.capacity());
f::FslOutputFStream ops(fsl_output_f_buffer, buf.handle());
ops << "Hi, world!";
assert(10 == buf.used());
buf.clear();
assert(0==buf.used());
assert(0==buf.capacity());
assert(NULL==buf.mem());
}
static void test_deck_1(fsl::Context &cx){
namespace f = fsl;
int threw = 0;
f::Deck d(cx, FSL_CATYPE_CONTROL);
f::ContextOStream fout(cx);
try {
d.assertHasRequiredCards();
}catch(f::Exception const &ex){
threw = ex.code();
fout << "Got expected exception: " << ex.what() << '\n';
}
assert(FSL_RC_CA_SYNTAX==threw);
{
f::Context::Transaction tr(cx)
/* We'll roll back everything done here... */
;
fout << "Custom "<< fsl_catype_cstr(d.type()) << " deck:\n";
d.setCardD()
.setCardU()
.addCardT(FSL_TAGTYPE_ADD, "myTag", NULL, "its value")
.addCardT(FSL_TAGTYPE_PROPAGATING, "myOtherTag")
.addCardT(FSL_TAGTYPE_CANCEL, "sumdumtag")
//.unshuffle().output(fout)
;
d.save();
fout << "RID/UUID after save (will be rolled back): "<<d.rid() << ' ' << d.uuid()<<'\n';
{
typedef f::Deck::TCardIterator TagIter;
TagIter it(d);
TagIter end;
fout << "Iterating over tags:";
for( ; it != end; ++it ){
fsl_card_T const * tag = *it;
fout << ' ' << fsl_tag_prefix_char(tag->type)
<< tag->name;
}
fout << '\n';
}
fout << "Re-read artifact via Deck::load():\n";
fout << f::Deck(cx).load( d.rid() );
f::Buffer content;
cx.getContent( d.rid(), content );
fout << "And again via Context::getContent():\n" << content;
}
if(0){
fsl_id_t rid = 1;
fout << "Loading checkin #"<<rid<<"...\n";
d.load( rid ).output( fout );
}
{ /* FCardIterator... */
f::Deck withF(cx);
withF.load("current");
f::Deck::FCardIterator fit(withF);
f::Deck::FCardIterator end;
fout << "Iterating over F-cards...\n";
assert(*fit);
assert(!*end);
assert(fit!=end);
int counter = 0;
for( ; fit != end; ++fit, ++counter ){
fsl_card_F const * fc = *fit;
assert(fc);
assert(fc->name);
assert(fit->name == fc->name);
}
fout << "Traversed "
<<counter<<" F-card(s) in deck #"
<<withF.rid()<<".\n";
}
}
int main(int argc, char const * const * argv ){
int rc = EXIT_SUCCESS;
try {
namespace f = fsl;
f::Context cx;
f::ContextOStream fout(cx);
fout << "ContextOStream...\n";
f::ContextOStreamBuf cout2(cx, std::cerr);
f::ContextOStreamBuf cerr2(cx, std::cout);
CERR << "cerr through fsl_output()\n";
COUT << "cout through fsl_output()\n";
cx.openCheckout();
test_db_1();
test_stream_1(cx);
test_deck_1(cx);
/* Manually closing the Context is not generally required,
we're just testing an assertion or three... */
assert(cx.dbRepo().isOpened());
assert(cx.dbCheckout().isOpened());
cx.closeDbs();
assert(!cx.dbRepo().isOpened());
assert(!cx.dbCheckout().isOpened());
}catch(fsl::Exception const &fex){
CERR << "EXCEPTION: " << RCStr(fex.code()) << ": " << fex.what() << '\n';
rc = EXIT_FAILURE;
}catch(std::exception const &ex){
CERR << "EXCEPTION: " << ex.what() << '\n';
rc = EXIT_FAILURE;
}
COUT << "Exiting. Result = " << (rc ? ":`(" : ":-D") << '\n';
return rc;
}