Skip to content

Commit

Permalink
Throw exception if a file cannot be opened, fix travis-ci builds (#117)
Browse files Browse the repository at this point in the history
* throw exception if a file cannot be opened

* Add a new function in utils.hpp: open_file_or_throw: This function
  returns an opened std::ifstream or throws by calling `inja_throw`.
* Use this function in Parser::load_file which previously returned an
  empty string if the file couldn't be opened.
* Use this function in Environment::load_json which previously threw
  a `nlohmann::detail::parse_error` if the file couldn't be opened.
* In Parser::parse_statement: When including files through `include`,
  do not attempt to (re-)parse templates from files that were already
  included. Additionally, this prevents inja from attempting to load
  in-memory templates by their name from disk.
* Add tests that check if an exception is thrown when attempting to
  open files that do not exist.

* cmake: enable C++11

* cmake: require C++11 when depending on single_inja

* code style
  • Loading branch information
thomastrapp authored and pantor committed Sep 8, 2019
1 parent 57ac9b9 commit 419c93c
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 9 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ target_include_directories(inja INTERFACE
$<INSTALL_INTERFACE:${INJA_INSTALL_INCLUDE_DIR}>
)

# target_compile_features(inja INTERFACE cxx_std_11)
target_compile_features(inja INTERFACE cxx_std_11)

set(INJA_PACKAGE_USE_EMBEDDED_JSON OFF)

Expand Down Expand Up @@ -89,6 +89,7 @@ if(BUILD_TESTING)


add_library(single_inja INTERFACE)
target_compile_features(single_inja INTERFACE cxx_std_11)
target_include_directories(single_inja INTERFACE single_include include third_party/include)

add_executable(single_inja_test
Expand Down
3 changes: 2 additions & 1 deletion include/inja/environment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "renderer.hpp"
#include "string_view.hpp"
#include "template.hpp"
#include "utils.hpp"


namespace inja {
Expand Down Expand Up @@ -144,7 +145,7 @@ class Environment {
}

json load_json(const std::string& filename) {
std::ifstream file(m_impl->input_path + filename);
std::ifstream file = open_file_or_throw(m_impl->input_path + filename);
json j;
file >> j;
return j;
Expand Down
8 changes: 5 additions & 3 deletions include/inja/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,10 @@ class Parser {
}
// sys::path::remove_dots(pathname, true, sys::path::Style::posix);

Template include_template = parse_template(pathname);
m_included_templates.emplace(pathname, include_template);
if (m_included_templates.find(pathname) == m_included_templates.end()) {
Template include_template = parse_template(pathname);
m_included_templates.emplace(pathname, include_template);
}

// generate a reference bytecode
tmpl.bytecodes.emplace_back(Bytecode::Op::Include, json(pathname), Bytecode::Flag::ValueImmediate);
Expand Down Expand Up @@ -505,7 +507,7 @@ class Parser {
}

std::string load_file(nonstd::string_view filename) {
std::ifstream file(static_cast<std::string>(filename));
std::ifstream file = open_file_or_throw(static_cast<std::string>(filename));
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return text;
}
Expand Down
12 changes: 12 additions & 0 deletions include/inja/utils.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef PANTOR_INJA_UTILS_HPP
#define PANTOR_INJA_UTILS_HPP

#include <fstream>
#include <stdexcept>

#include "string_view.hpp"
Expand All @@ -12,6 +13,17 @@ inline void inja_throw(const std::string& type, const std::string& message) {
throw std::runtime_error("[inja.exception." + type + "] " + message);
}

inline std::ifstream open_file_or_throw(const std::string& path) {
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
file.open(path);
} catch(const std::ios_base::failure& e) {
inja_throw("file_error", "failed accessing file at '" + path + "'");
}
return file;
}

namespace string_view {
inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
start = std::min(start, view.size());
Expand Down
24 changes: 20 additions & 4 deletions single_include/inja/inja.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,7 @@ struct Token {
#ifndef PANTOR_INJA_UTILS_HPP
#define PANTOR_INJA_UTILS_HPP

#include <fstream>
#include <stdexcept>

// #include "string_view.hpp"
Expand All @@ -1705,6 +1706,17 @@ inline void inja_throw(const std::string& type, const std::string& message) {
throw std::runtime_error("[inja.exception." + type + "] " + message);
}

inline std::ifstream open_file_or_throw(const std::string& path) {
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
file.open(path);
} catch(const std::ios_base::failure& e) {
inja_throw("file_error", "failed accessing file at '" + path + "'");
}
return file;
}

namespace string_view {
inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
start = std::min(start, view.size());
Expand Down Expand Up @@ -2384,8 +2396,10 @@ class Parser {
}
// sys::path::remove_dots(pathname, true, sys::path::Style::posix);

Template include_template = parse_template(pathname);
m_included_templates.emplace(pathname, include_template);
if (m_included_templates.find(pathname) == m_included_templates.end()) {
Template include_template = parse_template(pathname);
m_included_templates.emplace(pathname, include_template);
}

// generate a reference bytecode
tmpl.bytecodes.emplace_back(Bytecode::Op::Include, json(pathname), Bytecode::Flag::ValueImmediate);
Expand Down Expand Up @@ -2505,7 +2519,7 @@ class Parser {
}

std::string load_file(nonstd::string_view filename) {
std::ifstream file(static_cast<std::string>(filename));
std::ifstream file = open_file_or_throw(static_cast<std::string>(filename));
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return text;
}
Expand Down Expand Up @@ -3184,6 +3198,8 @@ class Renderer {

// #include "template.hpp"

// #include "utils.hpp"



namespace inja {
Expand Down Expand Up @@ -3313,7 +3329,7 @@ class Environment {
}

json load_json(const std::string& filename) {
std::ifstream file(m_impl->input_path + filename);
std::ifstream file = open_file_or_throw(m_impl->input_path + filename);
json j;
file >> j;
return j;
Expand Down
6 changes: 6 additions & 0 deletions test/unit-files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ TEST_CASE("loading") {
SECTION("File includes should be rendered") {
CHECK( env.render_file(test_file_directory + "include.txt", data) == "Answer: Hello Jeff." );
}

SECTION("File error should throw") {
std::string path(test_file_directory + "does-not-exist");
CHECK_THROWS_WITH( env.load_file(path), "[inja.exception.file_error] failed accessing file at '" + path + "'" );
CHECK_THROWS_WITH( env.load_json(path), "[inja.exception.file_error] failed accessing file at '" + path + "'" );
}
}

TEST_CASE("complete-files") {
Expand Down
1 change: 1 addition & 0 deletions test/unit-renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ TEST_CASE("templates") {

inja::Template t2 = env.parse("{% include \"greeting\" %}!");
CHECK( env.render(t2, data) == "Hello Peter!" );
CHECK_THROWS_WITH( env.parse("{% include \"does-not-exist\" %}!"), "[inja.exception.file_error] failed accessing file at 'does-not-exist'" );
}
}

Expand Down

0 comments on commit 419c93c

Please sign in to comment.