aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/client/input.hpp
blob: 0295f3bfe5b89cca406a9beab121d2a7578a4d9d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
// This file is part of the "math-server" project.
// For details, see https://github.com/egor-tensin/math-server.
// Distributed under the MIT License.

#pragma once

#include "error.hpp"

#include <exception>
#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

namespace math::client::input {

class Error : public client::Error {
public:
    explicit Error(const std::string& what) : client::Error{"input error: " + what} {}
};

class Reader {
public:
    using InputHandler = std::function<bool(const std::string&)>;

    virtual ~Reader() = default;

    virtual bool for_each_input(const InputHandler& process) const = 0;
};

using ReaderPtr = std::unique_ptr<input::Reader>;

class FileReader : public Reader {
public:
    explicit FileReader(const std::string& path) : m_path{path} {}

    bool for_each_input(const InputHandler& process) const override { return enum_lines(process); }

private:
    bool enum_lines(const InputHandler& process) const {
        std::ifstream ifs;
        ifs.exceptions(std::ifstream::badbit);

        try {
            ifs.open(m_path);
            if (!ifs.is_open()) {
                throw Error{"couldn't open file: " + m_path};
            }

            for (std::string line; std::getline(ifs, line);) {
                if (!process(line)) {
                    return false;
                }
            }
        } catch (const std::exception& e) {
            throw Error{e.what()};
        }

        return true;
    }

    const std::string m_path;
};

class MultiFileReader : public Reader {
public:
    explicit MultiFileReader(const std::vector<std::string>& paths) : m_paths{paths} {}

    bool for_each_input(const InputHandler& process) const override {
        for (const auto& path : m_paths) {
            const FileReader reader{path};
            if (!reader.for_each_input(process)) {
                return false;
            }
        }
        return true;
    }

private:
    const std::vector<std::string> m_paths;
};

inline input::ReaderPtr make_file_reader(const std::string& path) {
    return std::make_unique<input::FileReader>(path);
}

inline input::ReaderPtr make_file_reader(const std::vector<std::string>& paths) {
    return std::make_unique<input::MultiFileReader>(paths);
}

class StringReader : public Reader {
public:
    explicit StringReader(const std::string& input) : m_input{input} {}

    bool for_each_input(const InputHandler& process) const override { return process(m_input); }

private:
    const std::string m_input;
};

inline input::ReaderPtr make_string_reader(const std::string& input) {
    return std::make_unique<input::StringReader>(input);
}

class ConsoleReader : public Reader {
public:
    ConsoleReader() = default;

    bool for_each_input(const InputHandler& process) const override {
        std::string line;
        while (read_line(line)) {
            if (!process(line)) {
                return false;
            }
        }
        return true;
    }

private:
    static bool read_line(std::string& dest) {
        return static_cast<bool>(std::getline(std::cin, dest));
    }
};

inline input::ReaderPtr make_console_reader() {
    return std::make_unique<input::ConsoleReader>();
}

} // namespace math::client::input