mirror of
https://github.com/ToruNiina/toml11.git
synced 2024-11-08 22:00:07 +00:00
feat: Add a parse(FILE *) interface
The fstream classes are notorious for their non-existent error handling. This adds a C-style fILE * IO (fopen(), etc.) alternative interface, so that if a user needs reliable error handling, they can use that, albeit more inconvenient, but more robust approach.
This commit is contained in:
parent
594accf9a7
commit
bf9c9d620d
@ -142,6 +142,75 @@ BOOST_AUTO_TEST_CASE(test_example_stream)
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_example_file_pointer)
|
||||
{
|
||||
FILE * file = fopen("toml/tests/example.toml", "rb");
|
||||
const auto data = toml::parse(file, "toml/tests/example.toml");
|
||||
fclose(file);
|
||||
|
||||
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
|
||||
const auto& owner = toml::find(data, "owner");
|
||||
{
|
||||
BOOST_TEST(toml::find<std::string>(owner, "name") == "Tom Preston-Werner");
|
||||
BOOST_TEST(toml::find<std::string>(owner, "organization") == "GitHub");
|
||||
BOOST_TEST(toml::find<std::string>(owner, "bio") ==
|
||||
"GitHub Cofounder & CEO\nLikes tater tots and beer.");
|
||||
BOOST_TEST(toml::find<toml::offset_datetime>(owner, "dob") ==
|
||||
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
|
||||
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
|
||||
}
|
||||
|
||||
const auto& database = toml::find(data, "database");
|
||||
{
|
||||
BOOST_TEST(toml::find<std::string>(database, "server") == "192.168.1.1");
|
||||
const std::vector<int> expected_ports{8001, 8001, 8002};
|
||||
BOOST_CHECK(toml::find<std::vector<int>>(database, "ports") == expected_ports);
|
||||
BOOST_TEST(toml::find<int >(database, "connection_max") == 5000);
|
||||
BOOST_TEST(toml::find<bool>(database, "enabled") == true);
|
||||
}
|
||||
|
||||
const auto& servers = toml::find(data, "servers");
|
||||
{
|
||||
toml::table alpha = toml::find<toml::table>(servers, "alpha");
|
||||
BOOST_TEST(toml::get<std::string>(alpha.at("ip")) == "10.0.0.1");
|
||||
BOOST_TEST(toml::get<std::string>(alpha.at("dc")) == "eqdc10");
|
||||
|
||||
toml::table beta = toml::find<toml::table>(servers, "beta");
|
||||
BOOST_TEST(toml::get<std::string>(beta.at("ip")) == "10.0.0.2");
|
||||
BOOST_TEST(toml::get<std::string>(beta.at("dc")) == "eqdc10");
|
||||
BOOST_TEST(toml::get<std::string>(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD");
|
||||
}
|
||||
|
||||
const auto& clients = toml::find(data, "clients");
|
||||
{
|
||||
toml::array clients_data = toml::find<toml::array>(clients, "data");
|
||||
std::vector<std::string> expected_name{"gamma", "delta"};
|
||||
BOOST_CHECK(toml::get<std::vector<std::string>>(clients_data.at(0)) == expected_name);
|
||||
|
||||
std::vector<int> expected_number{1, 2};
|
||||
BOOST_CHECK(toml::get<std::vector<int>>(clients_data.at(1)) == expected_number);
|
||||
|
||||
std::vector<std::string> expected_hosts{"alpha", "omega"};
|
||||
BOOST_CHECK(toml::find<std::vector<std::string>>(clients, "hosts") == expected_hosts);
|
||||
}
|
||||
|
||||
std::vector<toml::table> products =
|
||||
toml::find<std::vector<toml::table>>(data, "products");
|
||||
{
|
||||
BOOST_TEST(toml::get<std::string>(products.at(0).at("name")) ==
|
||||
"Hammer");
|
||||
BOOST_TEST(toml::get<std::int64_t>(products.at(0).at("sku")) ==
|
||||
738594937);
|
||||
|
||||
BOOST_TEST(toml::get<std::string>(products.at(1).at("name")) ==
|
||||
"Nail");
|
||||
BOOST_TEST(toml::get<std::int64_t>(products.at(1).at("sku")) ==
|
||||
284758393);
|
||||
BOOST_TEST(toml::get<std::string>(products.at(1).at("color")) ==
|
||||
"gray");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_fruit)
|
||||
{
|
||||
const auto data = toml::parse("toml/tests/fruit.toml");
|
||||
@ -959,3 +1028,8 @@ BOOST_AUTO_TEST_CASE(test_parse_function_compiles)
|
||||
BOOST_TEST_MESSAGE("path");
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_parse_nonexistent_file)
|
||||
{
|
||||
BOOST_CHECK_THROW(toml::parse("nonexistent.toml"), std::runtime_error);
|
||||
}
|
||||
|
@ -10,6 +10,18 @@
|
||||
namespace toml
|
||||
{
|
||||
|
||||
struct file_io_error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
file_io_error(int errnum, const std::string& msg, const std::string& fname)
|
||||
: std::runtime_error(msg + " \"" + fname + "\": " + std::strerror(errnum)),
|
||||
errno_(errnum)
|
||||
{}
|
||||
int get_errno() {return errno_;}
|
||||
private:
|
||||
int errno_;
|
||||
};
|
||||
|
||||
struct exception : public std::exception
|
||||
{
|
||||
public:
|
||||
|
@ -2377,27 +2377,14 @@ result<Value, std::string> parse_toml_file(location& loc)
|
||||
return ok(Value(std::move(data), file, comments));
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
|
||||
template<typename ...> class Table = std::unordered_map,
|
||||
template<typename ...> class Array = std::vector>
|
||||
basic_value<Comment, Table, Array>
|
||||
parse(std::istream& is, std::string fname = "unknown file")
|
||||
parse(std::vector<char>& letters, const std::string& fname)
|
||||
{
|
||||
using value_type = basic_value<Comment, Table, Array>;
|
||||
|
||||
const auto beg = is.tellg();
|
||||
is.seekg(0, std::ios::end);
|
||||
const auto end = is.tellg();
|
||||
const auto fsize = end - beg;
|
||||
is.seekg(beg);
|
||||
|
||||
// read whole file as a sequence of char
|
||||
assert(fsize >= 0);
|
||||
std::vector<char> letters(static_cast<std::size_t>(fsize));
|
||||
is.read(letters.data(), fsize);
|
||||
|
||||
// append LF.
|
||||
// Although TOML does not require LF at the EOF, to make parsing logic
|
||||
// simpler, we "normalize" the content by adding LF if it does not exist.
|
||||
@ -2435,12 +2422,70 @@ parse(std::istream& is, std::string fname = "unknown file")
|
||||
return data.unwrap();
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
|
||||
template<typename ...> class Table = std::unordered_map,
|
||||
template<typename ...> class Array = std::vector>
|
||||
basic_value<Comment, Table, Array>
|
||||
parse(FILE * file, const std::string& fname)
|
||||
{
|
||||
const long beg = std::ftell(file);
|
||||
if (beg == -1l) {
|
||||
throw file_io_error(errno, "Failed to access", fname);
|
||||
}
|
||||
|
||||
int res = std::fseek(file, 0, SEEK_END);
|
||||
if (res != 0) {
|
||||
throw file_io_error(errno, "Failed to seek", fname);
|
||||
}
|
||||
|
||||
const long end = std::ftell(file);
|
||||
if (end == -1l) {
|
||||
throw file_io_error(errno, "Failed to access", fname);
|
||||
}
|
||||
|
||||
const auto fsize = end - beg;
|
||||
|
||||
res = std::fseek(file, beg, SEEK_SET);
|
||||
if (res != 0) {
|
||||
throw file_io_error(errno, "Failed to seek", fname);
|
||||
}
|
||||
|
||||
// read whole file as a sequence of char
|
||||
assert(fsize >= 0);
|
||||
std::vector<char> letters(static_cast<std::size_t>(fsize));
|
||||
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), file);
|
||||
|
||||
return detail::parse<Comment, Table, Array>(letters, fname);
|
||||
}
|
||||
|
||||
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
|
||||
template<typename ...> class Table = std::unordered_map,
|
||||
template<typename ...> class Array = std::vector>
|
||||
basic_value<Comment, Table, Array>
|
||||
parse(std::istream& is, std::string fname = "unknown file")
|
||||
{
|
||||
const auto beg = is.tellg();
|
||||
is.seekg(0, std::ios::end);
|
||||
const auto end = is.tellg();
|
||||
const auto fsize = end - beg;
|
||||
is.seekg(beg);
|
||||
|
||||
// read whole file as a sequence of char
|
||||
assert(fsize >= 0);
|
||||
std::vector<char> letters(static_cast<std::size_t>(fsize));
|
||||
is.read(letters.data(), fsize);
|
||||
|
||||
return detail::parse<Comment, Table, Array>(letters, fname);
|
||||
}
|
||||
|
||||
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
|
||||
template<typename ...> class Table = std::unordered_map,
|
||||
template<typename ...> class Array = std::vector>
|
||||
basic_value<Comment, Table, Array> parse(std::string fname)
|
||||
{
|
||||
std::ifstream ifs(fname.c_str(), std::ios_base::binary);
|
||||
std::ifstream ifs(fname, std::ios_base::binary);
|
||||
if(!ifs.good())
|
||||
{
|
||||
throw std::runtime_error("toml::parse: file open error -> " + fname);
|
||||
|
Loading…
Reference in New Issue
Block a user