Commit b33f7cd2 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

first cli test passing :)

parent f4ca710a
Loading
Loading
Loading
Loading
+39 −2
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <string>
#include <fnordmetric/cli/cli.h>
#include <fnordmetric/cli/flagparser.h>
#include <fnordmetric/query/queryservice.h>
#include <fnordmetric/util/inputstream.h>
#include <fnordmetric/util/outputstream.h>
#include <fnordmetric/util/runtimeexception.h>
@@ -16,21 +17,57 @@ namespace fnordmetric {
namespace cli {

void CLI::execute(const cli::FlagParser& flag_parser) {
  std::unique_ptr<util::InputStream> input;

  bool verbose = flag_parser.isSet("verbose");
  auto err = util::OutputStream::getStderr();
  const auto& args = flag_parser.getArgv();

  /* web / cgi mode */

  /* execute query */

  /* open input stream */
  std::unique_ptr<util::InputStream> input;
  switch (args.size()) {
    case 0:
      if (verbose) {
        err->printf("[INFO] input from stdin %s\n");
      }

      input = std::move(util::InputStream::getStdin());
      break;
    case 1:
      if (verbose) {
        err->printf("[INFO] input file: %s\n", args[0].c_str());
      }

      input = std::move(util::FileInputStream::openFile(args[0]));
      break;
    default:
      RAISE(UsageError);
  }

  /* open output stream */
  std::unique_ptr<util::OutputStream> output;
  if (flag_parser.isSet("output")) {
    auto output_file = flag_parser.getString("output");

    if (verbose) {
      err->printf("[INFO] output file: %s\n", output_file.c_str());
    }

    output = std::move(util::FileOutputStream::openFile(output_file));
  } else {
    if (verbose) {
      err->printf("[INFO] output to stdout\n");
    }
    output = std::move(util::OutputStream::getStdout());
  }

  query::QueryService query_service;
  query_service.executeQuery(
      input.get(),
      query::QueryService::FORMAT_SVG,
      output.get());
}

}
+12 −4
Original line number Diff line number Diff line
@@ -20,25 +20,33 @@ static fnordmetric::util::UnitTest::TestCase __test_simple_sql_to_svg_(
  std::vector<std::string> args = {
    "test/fixtures/gdp_bar_chart.sql",
    "-f", "svg",
    "-o", "build/test/tmp/CLITest_TestSimpleSQLToSVG_out.svg.html"
    "-o", "build/tests/tmp/CLITest_TestSimpleSQLToSVG_out.svg.html"
  };

  FlagParser flag_parser;

  flag_parser.defineFlag(
      "verbose",
      FlagParser::T_SWITCH,
      false,
      "v",
      NULL,
      "Be verbose");

  flag_parser.defineFlag(
      "format",
      FlagParser::T_STRING,
      false,
      "f",
      "format",
      "human",
      "The output format (svg,csv,human)",
      "<output_format>");

  flag_parser.defineFlag(
      "output",
      FlagParser::T_STRING,
      false,
      "o",
      "output",
      NULL,
      "The output format (svg,csv,human)",
      "<output_format>");
@@ -48,6 +56,6 @@ static fnordmetric::util::UnitTest::TestCase __test_simple_sql_to_svg_(

  EXPECT_FILES_EQ(
    "test/fixtures/CLITest_TestSimpleSQLToSVG_out.svg.html",
    "build/test/tmp/CLITest_TestSimpleSQLToSVG_out.svg.html");
    "build/tests/tmp/CLITest_TestSimpleSQLToSVG_out.svg.html");
});
+58 −17
Original line number Diff line number Diff line
@@ -9,10 +9,10 @@ namespace cli {
FlagParser::FlagParser() {}

void FlagParser::defineFlag(
    const char* longopt,
    kFlagType type,
    bool required,
    const char* shortopt,
    const char* longopt /* = NULL */,
    const char* shortopt /* = NULL */,
    const char* default_value /* = NULL */,
    const char* description /* = NULL */,
    const char* placeholder /* = NULL */) {
@@ -20,50 +20,73 @@ void FlagParser::defineFlag(
  FlagState flag_state;
  flag_state.type = type;
  flag_state.required = required;
  flag_state.shortopt = shortopt;
  flag_state.longopt = longopt;
  flag_state.shortopt = shortopt;
  flag_state.default_value = default_value;
  flag_state.description = description;
  flag_state.placeholder = placeholder;
  flags_.emplace_back(flag_state);
}

bool FlagParser::isSet(const char* longopt) const {
  for (auto& flag : flags_) {
    if (flag.longopt == longopt) {
      return flag.values.size() > 0;
    }
  }

  return false;
}

std::string FlagParser::getString(const char* longopt) const {
  for (auto& flag : flags_) {
    if (flag.longopt == longopt) {
      if (flag.type != T_STRING) {
        RAISE(FlagError, "flag '%s' is not a string", longopt);
      }

      return flag.values.back();
    }
  }

  RAISE(FlagError, "flag '%s' is not set", longopt);
}

// FIXPAUL optimize with hashmap?
void FlagParser::parseArgv(const std::vector<std::string>& argv) {
  for (int i = 0; i < argv.size(); i++) {
    int eq_len = -1;
    FlagState* flag_ptr = nullptr;
    auto& arg = argv[i];

    if (arg.size() == 0) {
      continue;
    }

    int eq_len = -1;
    FlagState* flag_ptr = nullptr;

    for (auto& flag : flags_) {
      auto shortopt = std::string("-") + flag.shortopt;
      auto shortopt_eq = std::string("-") + flag.shortopt + "=";
      auto longopt = std::string("--") + flag.longopt;
      auto longopt_eq = std::string("--") + flag.longopt + "=";

      if (arg.compare(0, shortopt.size(), shortopt)) {
      if (arg.compare(0, longopt.size(), longopt) == 0) {
        flag_ptr = &flag;
      }

      else if (arg.compare(0, shortopt_eq.size(), shortopt_eq)) {
      else if (arg.compare(0, longopt_eq.size(), longopt_eq) == 0) {
        flag_ptr = &flag;
        eq_len = shortopt_eq.size();
        eq_len = longopt_eq.size();
      }

      else if (flag.longopt != nullptr) {
        auto longopt = std::string("--") + flag.longopt;
        auto longopt_eq = std::string("--") + flag.longopt + "=";
      else if (flag.shortopt != nullptr) {
        auto shortopt = std::string("-") + flag.shortopt;
        auto shortopt_eq = std::string("-") + flag.shortopt + "=";

        if (arg.compare(0, longopt.size(), longopt)) {
        if (arg.compare(0, shortopt.size(), shortopt) == 0) {
          flag_ptr = &flag;
        }

        else if (arg.compare(0, longopt_eq.size(), longopt_eq)) {
        else if (arg.compare(0, shortopt_eq.size(), shortopt_eq) == 0) {
          flag_ptr = &flag;
          eq_len = longopt_eq.size();
          eq_len = shortopt_eq.size();
        }
      }

@@ -74,8 +97,26 @@ void FlagParser::parseArgv(const std::vector<std::string>& argv) {

    if (flag_ptr == nullptr) {
      argv_.push_back(arg);
    } else if (flag_ptr->type == T_SWITCH) {
      flag_ptr->values.emplace_back("true");
    } else if (eq_len > 0) {
      if (arg.size() == eq_len) {
        RAISE(FlagError, "flag --%s=... has no value", flag_ptr->longopt);
      }

      flag_ptr->values.emplace_back(arg.substr(eq_len));
    } else {
      if (i + 1 >= argv.size()) {
        RAISE(FlagError, "flag --%s has no value", flag_ptr->longopt);
      }

      flag_ptr->values.emplace_back(argv[++i]);
    }
  }

  for (const auto& flag : flags_) {
    if (flag.required == true && flag.values.size() == 0) {
      RAISE(FlagError, "flag --%s is required", flag.longopt);
    }
  }
}
+25 −3
Original line number Diff line number Diff line
@@ -15,6 +15,13 @@ namespace cli {

class FlagParser {
public:
  struct FlagError : public fnordmetric::util::RuntimeException {
    template <typename... T>
    FlagError(
        const char* message, T... args) :
        RuntimeException(message, args...) {}
  };

  enum kFlagType {
    T_SWITCH,
    T_STRING,
@@ -28,14 +35,29 @@ public:
   * Define a flag
   */
  void defineFlag(
      const char* longopt,
      kFlagType type,
      bool required,
      const char* shortopt,
      const char* longopt = NULL,
      const char* shortopt = NULL,
      const char* default_value = NULL,
      const char* description = NULL,
      const char* placeholder = NULL);

  /**
   * Returns true if the flag is set and false otherwise
   *
   * @param longopt the longopt of the flag
   */
  bool isSet(const char* longopt) const;

  /**
   * Returns the string value of the flag or throws an exception if the value
   * is invalid.
   *
   * @param longopt the longopt of the flag
   */
  std::string getString(const char* longopt) const;

  /**
   * Parse an argv array. This may throw an exception.
   */
@@ -50,8 +72,8 @@ protected:
  struct FlagState {
    kFlagType type;
    bool required;
    const char* shortopt;
    const char* longopt;
    const char* shortopt;
    const char* default_value;
    const char* description;
    const char* placeholder;
+23 −11
Original line number Diff line number Diff line
@@ -204,8 +204,10 @@ ASTNode* Parser::statement() {

  RAISE(
      ParseError,
      "unexpected token '%s', expected one of SELECT, DRAW or IMPORT",
      Token::getTypeName(cur_token_->getType()));
      "unexpected token %s%s%s, expected one of SELECT, DRAW or IMPORT",
        Token::getTypeName(cur_token_->getType()),
        cur_token_->getString().size() > 0 ? ": " : "",
        cur_token_->getString().c_str());

  return nullptr;
}
@@ -293,8 +295,10 @@ ASTNode* Parser::importStatement() {
    default:
      RAISE(
          ParseError,
          "unexpected token '%s', expected one of CSV, MYSQL",
          Token::getTypeName(cur_token_->getType()));
          "unexpected token %s%s%s, expected one of CSV, MYSQL",
          Token::getTypeName(cur_token_->getType()),
          cur_token_->getString().size() > 0 ? ": " : "",
          cur_token_->getString().c_str());
  }

  consumeIf(Token::T_SEMICOLON);
@@ -354,8 +358,10 @@ ASTNode* Parser::chartStatement() {
    default:
      RAISE(
          ParseError,
          "unexpected token '%s', expected one of BAR, LINE or AREA",
          Token::getTypeName(cur_token_->getType()));
          "unexpected token %s%s%s, expected one of BAR, LINE or AREA",
          Token::getTypeName(cur_token_->getType()),
          cur_token_->getString().size() > 0 ? ": " : "",
          cur_token_->getString().c_str());
      return nullptr;
  }

@@ -389,8 +395,10 @@ ASTNode* Parser::axisStatement() {
    default:
      RAISE(
          ParseError,
          "unexpected token '%s', expected one of TOP, RIGHT, BOTTOM, LEFT",
          Token::getTypeName(cur_token_->getType()));
          "unexpected token %s%s%s, expected one of TOP, RIGHT, BOTTOM, LEFT",
          Token::getTypeName(cur_token_->getType()),
          cur_token_->getString().size() > 0 ? ": " : "",
          cur_token_->getString().c_str());
      return nullptr;
  }

@@ -422,8 +430,10 @@ ASTNode* Parser::selectSublist() {
  if (value_expr == nullptr) {
    RAISE(
        ParseError,
        "unexpected token '%s', expected: value expression",
        Token::getTypeName(cur_token_->getType()));
        "unexpected token %s%s%s, expected: value expression",
        Token::getTypeName(cur_token_->getType()),
        cur_token_->getString().size() > 0 ? ": " : "",
        cur_token_->getString().c_str());
    delete derived;
    return nullptr;
  }
@@ -699,8 +709,10 @@ bool Parser::assertExpectation(Token::kTokenType expectation) {
  if (!(*cur_token_ == expectation)) {
    RAISE(
        ParseError,
        "unexpected token '%s', expected: '%s'",
        "unexpected token %s%s%s, expected: '%s'",
        Token::getTypeName(cur_token_->getType()),
        cur_token_->getString().size() > 0 ? ": " : "",
        cur_token_->getString().c_str(),
        Token::getTypeName(expectation));
    return false;
  }
Loading