parsing files

# Parsing Files and Strings #

In toml11, you can parse files, strings, and byte arrays using `toml::parse` or `toml::try_parse`.

Upon success, these functions return a `toml::value`. Although the parsed file is always a table, the return type is not `toml::table`. This is because `toml::value` contains metadata about the file, whereas `toml::table` is merely an alias for `std::unordered_map<std::string, toml::value>`. To include metadata, a `toml::value` is returned instead of a `toml::table`. The `toml::value` corresponding to the root of the file will always hold a `table_type`.

## Parsing Files #

To parse files, use either `toml::parse` or `toml::try_parse`.

### `toml::parse` #

#### Specifying the Filename with `std::string` #

`toml::parse` accepts a filename as a string, opens the file, and parses it.

The following sample code parses a file named `input.toml`, extracts the `title` variable as a string, and prints it.

    #include <toml.hpp>
    #include <iostream>

    int main()
    {
        const toml::value input = toml::parse("input.toml");
        std::cout << input.at("title").as_string() << std::endl;
        return 0;
    }

#### Specifying a File with `std::filesystem::path` #

`toml::parse` can accept a `std::filesystem::path`.

This requires C++17 or later, as it relies on the `<filesystem>` support.

#### Specifying an Input Stream with `std::istream` #

`toml::parse` can also accept an `std::istream`.

Open a stream in binary mode by passing `std::ios::binary` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.

Without the filename information, error messages will display `"unknown file"`. To avoid this, you can pass the filename as a `std::string` in the second argument when using `std::istream`.

You can use streams other than `std::ifstream`, such as `std::istringstream`. Note that the entire content is readable at the time of the call.

    #include <toml.hpp>
    #include <iostream>

    int main()
    {
        std::string filename("input.toml");
        std::ifstream ifs(filename);
        const toml::value input = toml::parse(ifs, filename);
        std::cout << input.at("title").as_string() << std::endl;
        return 0;
    }

#### Specifying a File with `FILE*` #

`toml::parse` can also accept a `FILE*`.

Open a stream in binary mode by passing `"rb"` to avoid inconsistency between the file size and the number of characters due to automatic conversion of newline characters by the standard library.

As with `std::istream`, you need to provide the filename as a string in the second argument.

When passing a `FILE*`, if the file read fails, `errno` will be reported.

#### Error Handling #

`toml::parse` throws a `toml::syntax_error` if it encounters a syntax error.

`toml::syntax_error` is derived from `toml::exception`, which in turn is derived from `std::exception`.

Therefore, you can use the `what()` member function to retrieve the error message from a `toml::syntax_error`.

Additionally, `toml::syntax_error` contains a `std::vector<toml::error_info>`, which can be accessed using the `errors()` member function.

`toml::parse` attempts to recover from minor errors and report multiple errors whenever possible. While it can often recover from simple errors like number format issues, errors within arrays or tables might not be recoverable and may lead to multiple similar error reports. If you find the error messages redundant, you can use only the `front()` of the `std::vector<toml::error_info>` to get a message about the most critical issue.

    #include <toml.hpp>

    int main()
    {
        try {
            const toml::value input = toml::parse("input.toml");
            std::cout << input.at("title").as_string() << std::endl;
        } catch(const toml::syntax_error& err) {
            // report all the errors
            std::cerr << err.what() << std::endl;

            // report the first error only
            std::cerr << err.errors().front() << std::endl;
        }
    }

### `toml::try_parse` #

While `toml::parse` throws an exception on failure, `toml::try_parse` does not throw exceptions when it fails.

Instead, its return type is `toml::result<toml::value, std::vector<toml::error_info>>`.

The `result` type holds either a success value or a failure value, similar to Rust’s `Result` or Haskell’s `Either`.

    #include <toml.hpp>

    int main()
    {
        const auto parse_result = toml::try_parse("input.toml");
        if(parse_result.is_ok())
        {
            std::cout << parse_result.unwrap().at("title").as_string() << std::endl;
        }
        else
        {
            std::cerr << parse_result.unwrap_err().at(0) << std::endl;
        }
        return 0;
    }

To check which value the `result` type holds, use the `is_ok()` and `is_err()` functions. The success or failure value can be retrieved using `unwrap()` and `unwrap_err()`, respectively. If `unwrap` fails, it throws a `bad_result_access` exception. Using the `as_ok()` and `as_err()` functions does not throw exceptions on failure, but results in undefined behavior.

> Although `try_parse` does not throw `syntax_error` or `file_io_error`, it returns the same `toml::error_info` as the failure type in the `result`. However, it is not entirely exception-free.

> If an internal standard library error occurs, such as `std::bad_alloc` when a `vector` fails to allocate memory, `toml::try_parse` does not catch this and will let it propagate. Thus, exceptions originating from the standard library may still be thrown.

## Parsing Strings #

### `toml::parse_str` #

`toml::parse_str` accepts the string to be parsed directly, instead of a filename.

For the part of the error message that corresponds to the TOML file’s name, if the `std::source_location` equivalent compiler extension is available, the name and line number of the C++ file that called `parse_str` will be used instead.

    #include <toml.hpp>
    #include <iostream>

    int main()
    {
        const toml::value input = toml::parse_str("title = \"parse_str\"");
        std::cout << input.at("title").as_string() << std::endl;
        return 0;
    }

### `toml::try_parse_str` #

`toml::try_parse_str` also takes the string to be parsed directly, similar to `parse_str`. Like `try_parse`, it uses `toml::result` to report errors.

    #include <toml.hpp>
    #include <iostream>

    int main()
    {
        const auto parse_result = toml::try_parse_str("title = \"parse_str\"");
        if(parse_result.is_ok())
        {
            std::cout << parse_result.unwrap().at("title").as_string() << std::endl;
        }
        else
        {
            std::cerr << parse_result.unwrap_err().at(0) << std::endl;
        }
        return 0;
    }

## Parsing Byte Arrays #

It is also possible to parse byte arrays instead of files.

Since the byte arrays must be encoded in UTF-8, `unsigned char` is used.

### `toml::parse(std::vector<unsigned char>)` #

The behavior is the same as `toml::parse`.

When parsing byte arrays, a `filename` is required.

    #include <toml.hpp>
    #include <iostream>

    int main()
    {
        std::vector<unsigned char> bytes{/* ... */};
        const toml::value input = toml::parse(bytes, "internal bytes");
        std::cout << input.at("title").as_string() << std::endl;
        return 0;
    }

### `toml::try_parse(std::vector<unsigned char>)` #

The behavior is the same as `toml::try_parse`.

When parsing byte arrays, a `filename` is required.

    #include <toml.hpp>
    #include <iostream>

    int main()
    {
        std::vector<unsigned char> bytes{/* ... */};
        const auto parse_result = toml::try_parse(bytes, "internal bytes");
        if(parse_result.is_ok())
        {
            std::cout << parse_result.unwrap().at("title").as_string() << std::endl;
        }
        else
        {
            std::cerr << parse_result.unwrap_err().at(0) << std::endl;
        }
        return 0;
    }