#ifndef SOIL_MOISTURE_IO_H #define SOIL_MOISTURE_IO_H #include #include #include #include #include #include #include #include #include namespace trygvis { namespace soil_moisture { using namespace std; using namespace boost::asio; template using o = boost::optional; enum class sample_format_type { AUTO, CSV, JSON, SQL }; string to_string(const sample_format_type &arg); class SampleStreamParser; class SampleOutputStream; class KeyDictionary; class SampleKey; unique_ptr open_sample_input_stream( KeyDictionary &dict, shared_ptr output, sample_format_type type = sample_format_type::AUTO); unique_ptr open_sample_output_stream( KeyDictionary &dict, sample_format_type type, unique_ptr output, o> fields = o>()); class sample_exception : public runtime_error { public: sample_exception(const string &what) : runtime_error(what) { } }; struct SampleKey { // TODO: only the dictionary should be able to create keys SampleKey(string &name) : name(name) { if (name.length() == 0) { throw sample_exception("Bad sample key."); } } inline bool operator==(const SampleKey &that) const { return name == that.name; } string name; }; class KeyDictionary { public: typedef vector v; typedef v::size_type index_t; KeyDictionary() { } index_t indexOf(const SampleKey key) { index_t i = 0; for (auto ptr = keys.begin(); ptr != keys.end(); ptr++, i++) { if (*ptr == key) { return i; } } keys.push_back(key); return keys.size() - 1; } vector findIndexes(v keys); inline v::const_iterator begin() { return keys.begin(); } inline v::const_iterator end() { return keys.end(); } string nameOf(index_t index) { return keys.at(index).name; } private: v keys; }; class SampleRecord { public: typedef vector> vec; SampleRecord(KeyDictionary &dict) : dict(dict) { } SampleRecord(KeyDictionary &dict, vec values) : dict(dict), values(values) { } inline vec::const_iterator begin() { return values.begin(); } inline vec::const_iterator end() { return values.end(); } inline bool empty() { return values.empty(); } o at(size_t index) { if (index >= values.size()) { return o(); } return values.at(index); } void set(const KeyDictionary::index_t index, const std::string &value) { values.resize(max(values.size(), index + 1)); values[index] = o(value); } template const o lexical_at(KeyDictionary::index_t index) { auto value = at(index); if (!value) { return o(); } return o(boost::lexical_cast(value.get())); } string to_string() { KeyDictionary::index_t i = 0; string s; for (auto ptr = values.begin(); ptr != values.end(); ptr++, i++) { auto o = *ptr; if (!o) { continue; } auto value = o.get(); s += dict.nameOf(i) + " = " + value + ", "; } return s; } private: KeyDictionary &dict; vec values; }; class SampleOutputStream { public: virtual void write(SampleRecord sample) = 0; }; class VectorSampleOutputStream : public SampleOutputStream { public: virtual void write(SampleRecord sample) override; public: vector samples; }; class CsvSampleOutputStream : public SampleOutputStream { public: CsvSampleOutputStream(KeyDictionary &dict, unique_ptr stream); CsvSampleOutputStream(KeyDictionary &dict, unique_ptr stream, vector fields); void write(SampleRecord values); private: void writeHeader(); KeyDictionary &dict; unique_ptr stream; bool headerWritten; vector fields; }; class JsonSampleOutputStream : public SampleOutputStream { public: JsonSampleOutputStream(KeyDictionary &dict, unique_ptr stream); JsonSampleOutputStream(KeyDictionary &dict, unique_ptr stream, vector fields); void write(SampleRecord values); private: KeyDictionary &dict; unique_ptr stream; bool filterFields; vector fields; }; class SqlSampleOutputStream : public SampleOutputStream { public: SqlSampleOutputStream(KeyDictionary &dict, unique_ptr stream, string table_name); SqlSampleOutputStream(KeyDictionary &dict, unique_ptr stream, string table_name, vector fields); void write(SampleRecord values); private: KeyDictionary &dict; unique_ptr stream; bool filter_fields; vector fields; const string table_name; }; class SampleStreamParser { public: virtual void process(mutable_buffers_1 buffer) = 0; virtual sample_format_type type() { return type_; } protected: sample_format_type type_; SampleStreamParser(const sample_format_type type) : type_(type) { } }; class CsvSampleParser : public SampleStreamParser { public: CsvSampleParser(KeyDictionary &dict, shared_ptr output) : SampleStreamParser(sample_format_type::CSV), dict(dict), output(output), line(make_shared>()) { } void process(mutable_buffers_1 buffer) override; private: void process_line(shared_ptr> packet); static const uint8_t packet_delimiter = '\n'; KeyDictionary &dict; shared_ptr output; shared_ptr> line; }; class AutoSampleParser : public SampleStreamParser { public: AutoSampleParser(KeyDictionary &dict, shared_ptr output); private: unique_ptr parser; unique_ptr csvParser; public: virtual void process(mutable_buffers_1 buffer); }; } } #endif