116 lines
3.6 KiB
C++
116 lines
3.6 KiB
C++
#ifndef DIRECTORY_WALKER_H
|
|
#define DIRECTORY_WALKER_H
|
|
|
|
#include <Arduino.h>
|
|
#include <vector>
|
|
#include "DirectoryNode.h"
|
|
|
|
struct WalkerState {
|
|
const DirectoryNode* node;
|
|
uint8_t phase; // 0: Start, 1: Files, 2: Subdirs, 3: End
|
|
size_t idx; // Index for vectors
|
|
|
|
WalkerState(const DirectoryNode* n) : node(n), phase(0), idx(0) {}
|
|
};
|
|
|
|
class DirectoryWalker {
|
|
private:
|
|
std::vector<WalkerState> stack;
|
|
String pending;
|
|
size_t pendingOffset;
|
|
|
|
void generateNext() {
|
|
if (stack.empty()) return;
|
|
|
|
WalkerState& state = stack.back();
|
|
const DirectoryNode* node = state.node;
|
|
|
|
switch (state.phase) {
|
|
case 0: // Start
|
|
if (node->getName() == "/") {
|
|
pending += F("<ul>\r\n");
|
|
} else {
|
|
pending += F("<li data-id=\"");
|
|
pending += String(node->getId());
|
|
pending += F("\"><b>");
|
|
pending += node->getName();
|
|
pending += F("</b></li>\r\n");
|
|
}
|
|
state.phase = 1;
|
|
state.idx = 0;
|
|
break;
|
|
|
|
case 1: // Files
|
|
if (state.idx < node->getMP3Files().size()) {
|
|
pending += F("<li data-id=\"");
|
|
pending += String(node->getFileIdAt(state.idx));
|
|
pending += F("\">");
|
|
pending += node->getMP3Files()[state.idx];
|
|
pending += F("</li>\r\n");
|
|
state.idx++;
|
|
} else {
|
|
state.phase = 2;
|
|
state.idx = 0;
|
|
}
|
|
break;
|
|
|
|
case 2: // Subdirs
|
|
if (state.idx < node->getSubdirectories().size()) {
|
|
// Push child
|
|
const DirectoryNode* child = node->getSubdirectories()[state.idx];
|
|
state.idx++; // Advance index for when we return
|
|
stack.emplace_back(child);
|
|
// Next loop will process the child (Phase 0)
|
|
} else {
|
|
state.phase = 3;
|
|
}
|
|
break;
|
|
|
|
case 3: // End
|
|
if (node->getName() == "/") {
|
|
pending += F("</ul>\r\n");
|
|
}
|
|
stack.pop_back();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public:
|
|
DirectoryWalker(const DirectoryNode* root) : pendingOffset(0) {
|
|
if (root) {
|
|
stack.emplace_back(root);
|
|
// Reserve some space for pending string to avoid frequent reallocations
|
|
pending.reserve(256);
|
|
}
|
|
}
|
|
|
|
size_t read(uint8_t* buffer, size_t maxLen) {
|
|
size_t written = 0;
|
|
|
|
while (written < maxLen) {
|
|
// If pending buffer is empty or fully consumed, generate more
|
|
if (pending.length() == 0 || pendingOffset >= pending.length()) {
|
|
pending = ""; // Reset string content (capacity is kept)
|
|
pendingOffset = 0;
|
|
|
|
if (stack.empty()) {
|
|
break; // Done
|
|
}
|
|
generateNext();
|
|
}
|
|
|
|
// Copy from pending to output buffer
|
|
if (pending.length() > pendingOffset) {
|
|
size_t available = pending.length() - pendingOffset;
|
|
size_t toCopy = std::min(available, maxLen - written);
|
|
memcpy(buffer + written, pending.c_str() + pendingOffset, toCopy);
|
|
written += toCopy;
|
|
pendingOffset += toCopy;
|
|
}
|
|
}
|
|
return written;
|
|
}
|
|
};
|
|
|
|
#endif
|