#pragma once #include "Config.h" #include "LittleFS.h" #include #include #include namespace userdb { typedef uint16_t linenumber; enum USERATTRIBUTES { LINE_ID, USER_ID, FIRST_NAME, LAST_NAME, RFID_UID, USER_PIN, ENABLED, NONE }; struct User { // User(unsigned long new_uid): uid(new_uid){} unsigned long uid = -1; linenumber line = UINT16_MAX; mutable String first_name; mutable String last_name; mutable String rfid_uid; mutable String user_pin; mutable bool enabled = false; mutable bool match = false; bool operator<(const User &o) const { return uid < o.uid; } bool operator==(const User &o) const { return uid == o.uid; } String toString() { return "ID: " + String(uid) + " Name: " + last_name + " " + first_name + " Enabled: " + (enabled ? "Yes" : "No") + " LineNo.: " + String(line) + "[RFID/PIN]" + rfid_uid + "//" + user_pin; } String toCSVString() { return String(uid) + ";" + first_name + ";" + last_name + ";" + rfid_uid + ";" + user_pin + ";" + (enabled ? "1" : "0"); } String toJSONString() { return "{\"uid\":\"" + String(uid) + "\",\"first_name\":\"" + first_name + "\",\"last_name\":\"" + last_name + "\",\"rfid_uid\":\"" + rfid_uid + "\",\"user_pin\":\"" + user_pin + "\",\"enabled\":" + (enabled ? "true" : "false") + ",\"line\":\"" + line + "\"}"; } }; class UserDb { private: const char *filename; public: UserDb(const char *filename); void print_to_serial(); void PrintAll(); // User find_user(); User user_by_pin(String); User user_by_rfid(String); User user_by_uid(unsigned long); User user_by_uid(String cmp); User user_by_line(linenumber cmp); bool drop() { LittleFS.remove(filename); File n = LittleFS.open(filename, "a"); n.close(); return true; } static inline User read_csv_line(User &res, File &instream, linenumber &line, String *match, USERATTRIBUTES attr) { String uid = instream.readStringUntil(';'); if (attr == USER_ID && !uid.equals(*match)) return res; res.uid = uid.toInt(); res.first_name = instream.readStringUntil(';'); res.last_name = instream.readStringUntil(';'); res.rfid_uid = instream.readStringUntil(';'); if (attr == RFID_UID && !res.rfid_uid.equals(*match)) return res; res.user_pin = instream.readStringUntil(';'); if (attr == USER_PIN && !res.user_pin.equals(*match)) return res; res.enabled = instream.read() == '1' ? true : false; res.line = line; res.match = true; return res; } void inline static coursor_to_end_of_line(File &f); bool remove_user(User &user); bool update_user(User *user); static bool update_db_line(const char *file, linenumber line, User *replace = nullptr); bool add_user(User &user); struct Iterator { using iterator_category = std::input_iterator_tag; using pointer = void; using value_type = User; using reference = User; using difference_type = std::ptrdiff_t; Iterator(File data) : Iterator(data, nullptr, NONE) { } Iterator(File data, linenumber m, USERATTRIBUTES attr) : db_file(data), filter_attr(attr), matchline(m) { if (!data) { Serial.println("Failed to open userdb while creating an iterator"); } else { next(); } } Iterator(File data, String *m, USERATTRIBUTES attr) : db_file(data), filter_attr(attr), match(m) { #ifdef DEBUG if (filter_attr == NONE) Serial.println("Started user iterator with "); else Serial.println("Started filtered user iterator with " + String(filter_attr) + " query: " + *match); #endif if (!data) { Serial.println("Failed to open userdb while creating an iterator"); } else { next(); } } reference operator*() const { return current; } Iterator &operator++() { next(); return *this; } Iterator operator++(int) { next(); return *this; } ~Iterator() { close_file(); } void close_file() { db_file.close(); } bool has_next() { return available; } void next() { if (db_file.available()) { if (filter_attr == USERATTRIBUTES::LINE_ID) { if (matchline == line) { current = read_csv_line(current, db_file, line, match, filter_attr); current.match = true; } } else current = read_csv_line(current, db_file, line, match, filter_attr); // Position stream pointer to beginning of the next line while (db_file.available()) { if (db_file.read() == '\n') { ++line; break; } } } else { available = false; current = User{}; db_file.close(); } } private: File db_file; User current; USERATTRIBUTES filter_attr; String *match; bool available = true; linenumber line = 0; linenumber matchline = 0; }; Iterator begin() { return Iterator(LittleFS.open(filename, "r")); } Iterator begin_with_filter(String *match, USERATTRIBUTES attr) { return Iterator(LittleFS.open(filename, "r"), match, attr); } Iterator begin_with_filter(linenumber match, USERATTRIBUTES attr) { return Iterator(LittleFS.open(filename, "r"), match, attr); } }; }