Add DFTE sources and tests

This commit is contained in:
2025-11-09 10:17:57 +10:00
parent 470efbf2d4
commit 5719aa6c39
27 changed files with 4993 additions and 0 deletions
@@ -0,0 +1,144 @@
#ifndef DEVICEFRAMEWORK_PLACEHOLDER_REGISTRY_H
#define DEVICEFRAMEWORK_PLACEHOLDER_REGISTRY_H
#include <Arduino.h>
#include <stdio.h>
#include "DeviceFrameworkTemplateTypes.h"
// Fallback defaults when DeviceFrameworkConfig is not available (standalone usage)
// Always use internal macro names (DFTE_*) to avoid conflicts with DeviceFrameworkConfig extern declarations
// When DeviceFrameworkConfig is included, extern variables are declared with CONFIG_* names
// We use DFTE_* internal names for compile-time constants (constexpr) and default parameter values
// NEVER define CONFIG_* macros here to avoid conflicts with extern declarations in DeviceFrameworkConfig
#ifndef DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
#define DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT 24
#endif
#ifndef DFTE_MAX_PLACEHOLDERS_DEFAULT
#define DFTE_MAX_PLACEHOLDERS_DEFAULT 16
#endif
#ifndef DFTE_PROGMEM_CHUNK_SIZE_DEFAULT
#define DFTE_PROGMEM_CHUNK_SIZE_DEFAULT 512
#endif
#ifndef DFTE_RAM_CHUNK_SIZE_DEFAULT
#define DFTE_RAM_CHUNK_SIZE_DEFAULT 128
#endif
// Use DeviceFramework config defaults at compile-time if available, otherwise use internal defaults
#ifdef DEVICEFRAMEWORK_CONFIG_H
// DeviceFramework is present - use config defaults
// DeviceFrameworkConfig.h must be included before this file to access CONFIG_*_default macros
#ifdef CONFIG_templateProgmemChunkSize_default
#define DFTE_PROGMEM_CHUNK_SIZE CONFIG_templateProgmemChunkSize_default
#else
#define DFTE_PROGMEM_CHUNK_SIZE DFTE_PROGMEM_CHUNK_SIZE_DEFAULT
#endif
#ifdef CONFIG_templateRamChunkSize_default
#define DFTE_RAM_CHUNK_SIZE CONFIG_templateRamChunkSize_default
#else
#define DFTE_RAM_CHUNK_SIZE DFTE_RAM_CHUNK_SIZE_DEFAULT
#endif
#else
// Standalone usage - use internal defaults
#define DFTE_PROGMEM_CHUNK_SIZE DFTE_PROGMEM_CHUNK_SIZE_DEFAULT
#define DFTE_RAM_CHUNK_SIZE DFTE_RAM_CHUNK_SIZE_DEFAULT
#endif
/**
* DeviceFramework Placeholder Registry
* Manages runtime registration of template placeholders
* Registry starts empty - users must register their placeholders
*/
class DeviceFrameworkPlaceholderRegistry {
public:
/**
* Constructor with optional max placeholders parameter
* @param maxPlaceholders Maximum number of placeholders (defaults to DFTE_MAX_PLACEHOLDERS_DEFAULT)
* Allows creating multiple registries with different sizes
* When using with DeviceFramework, pass getConfigMaxTemplatePlaceholders() for runtime config
*/
explicit DeviceFrameworkPlaceholderRegistry(uint16_t maxPlaceholders = DFTE_MAX_PLACEHOLDERS_DEFAULT);
~DeviceFrameworkPlaceholderRegistry();
// Prevent copying
DeviceFrameworkPlaceholderRegistry(const DeviceFrameworkPlaceholderRegistry&) = delete;
DeviceFrameworkPlaceholderRegistry& operator=(const DeviceFrameworkPlaceholderRegistry&) = delete;
/**
* Register a PROGMEM data placeholder
* @param name Placeholder name (e.g., "%CSS%")
* @param progmemData Pointer to PROGMEM data
* @return true if registered successfully, false if registry full
*/
bool registerProgmemData(const char* name, const char* progmemData);
/**
* Register a PROGMEM template placeholder (nested template)
* @param name Placeholder name (e.g., "%HEADER%")
* @param progmemTemplate Pointer to PROGMEM template
* @return true if registered successfully
*/
bool registerProgmemTemplate(const char* name, const char* progmemTemplate);
/**
* Register a RAM data placeholder with getter function
* @param name Placeholder name (e.g., "%PAGE_TITLE%")
* @param getter Function that returns current value
* @return true if registered successfully
*/
bool registerRamData(const char* name, PlaceholderDataGetter getter);
bool registerDynamicTemplate(const char* name, const DynamicTemplateDescriptor* descriptor);
bool registerConditional(const char* name, const ConditionalDescriptor* descriptor);
bool registerIterator(const char* name, const IteratorDescriptor* descriptor);
/**
* Clear all registered placeholders
*/
void clear();
/**
* Get number of registered placeholders
*/
int getCount() const { return count; }
/**
* Get maximum number of placeholders
*/
uint16_t getMaxPlaceholders() const { return maxPlaceholders; }
/**
* Find placeholder entry by name
* @return PlaceholderEntry pointer or nullptr if not found
*/
const PlaceholderEntry* getPlaceholder(const char* name) const;
/**
* Render placeholder content at given offset
*/
size_t renderPlaceholder(const PlaceholderEntry* entry, size_t offset,
uint8_t* buffer, size_t maxLen) const;
// Static helper functions for length calculation
static size_t getProgmemLength(const void* data);
static size_t getRamLength(const void* data);
static size_t getDynamicTemplateLength(const DynamicTemplateDescriptor* descriptor, const char* templateData);
private:
static constexpr uint16_t MAX_PLACEHOLDER_NAME_SIZE = DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT;
PlaceholderEntry* placeholders; // Dynamically allocated array
uint16_t maxPlaceholders; // Configurable size
int count;
bool validatePlaceholderName(const char* name) const;
static size_t copyProgmemData(const char* source, size_t offset,
uint8_t* dest, size_t maxLen);
static size_t copyRamData(PlaceholderDataGetter getter, size_t offset,
uint8_t* dest, size_t maxLen);
};
#endif // DEVICEFRAMEWORK_PLACEHOLDER_REGISTRY_H
+114
View File
@@ -0,0 +1,114 @@
#ifndef DEVICEFRAMEWORK_TEMPLATE_CONTEXT_H
#define DEVICEFRAMEWORK_TEMPLATE_CONTEXT_H
#include <Arduino.h>
#include "DeviceFrameworkTemplateTypes.h"
// Fallback defaults when DeviceFrameworkConfig is not available (standalone usage)
// Always use internal macro names (DFTE_*) to avoid conflicts with DeviceFrameworkConfig extern declarations
// When DeviceFrameworkConfig is included, extern variables are declared with CONFIG_* names
// We use DFTE_* internal names for compile-time constants (constexpr) and array sizes
#ifndef DFTE_MAX_STACK_DEPTH_DEFAULT
#define DFTE_MAX_STACK_DEPTH_DEFAULT 16
#endif
#ifndef DFTE_BUFFER_SIZE_DEFAULT
#define DFTE_BUFFER_SIZE_DEFAULT 512
#endif
#ifndef DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
#define DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT 24
#endif
// Use DeviceFramework config defaults at compile-time if available, otherwise use internal defaults
// Array sizes must be compile-time constants
#ifdef DEVICEFRAMEWORK_CONFIG_H
// DeviceFramework is present - use config defaults for array sizing
// DeviceFrameworkConfig.h must be included before this file to access CONFIG_*_default macros
#ifdef CONFIG_templateStackDepth_default
#define DFTE_MAX_STACK_DEPTH CONFIG_templateStackDepth_default
#else
#define DFTE_MAX_STACK_DEPTH DFTE_MAX_STACK_DEPTH_DEFAULT
#endif
#ifdef CONFIG_templateBufferSize_default
#define DFTE_BUFFER_SIZE CONFIG_templateBufferSize_default
#else
#define DFTE_BUFFER_SIZE DFTE_BUFFER_SIZE_DEFAULT
#endif
// DFTE_PLACEHOLDER_NAME_SIZE is already defined in DeviceFrameworkTemplateTypes.h
#else
// Standalone usage - use internal defaults
#define DFTE_MAX_STACK_DEPTH DFTE_MAX_STACK_DEPTH_DEFAULT
#define DFTE_BUFFER_SIZE DFTE_BUFFER_SIZE_DEFAULT
// DFTE_PLACEHOLDER_NAME_SIZE is already defined in DeviceFrameworkTemplateTypes.h
#endif
// Forward declaration
class DeviceFrameworkPlaceholderRegistry;
/**
* Template rendering context
* Maintains state for chunked streaming rendering with nested template support
*/
class DeviceFrameworkTemplateContext {
public:
using State = TemplateRenderState;
// Context state
State state;
// Unified rendering stack
static constexpr int MAX_RENDERING_DEPTH = DFTE_MAX_STACK_DEPTH;
RenderingContext renderingStack[MAX_RENDERING_DEPTH];
int renderingDepth;
// Current placeholder being built (only valid during BUILDING_PLACEHOLDER state)
// Allocated to configured size - matches CONFIG_templatePlaceholderNameSize when DeviceFramework is present
char placeholderName[DFTE_PLACEHOLDER_NAME_SIZE];
size_t placeholderPos;
// Centralized buffer management
// Allocated to configured size - matches CONFIG_templateBufferSize when DeviceFramework is present
static const size_t BUFFER_SIZE = DFTE_BUFFER_SIZE;
uint8_t readBuffer[BUFFER_SIZE];
size_t bufferPos;
size_t bufferLen;
size_t bufferOffset;
// Placeholder registry (injected, not owned)
DeviceFrameworkPlaceholderRegistry* registry;
// Statistics
size_t totalBytesProcessed;
unsigned long startTime;
DeviceFrameworkTemplateContext();
void reset();
// Unified stack management methods
bool pushContext(RenderingContextType type, const char* name);
void popContext();
RenderingContext* getCurrentContext();
RenderingContext* getContext(int depth);
bool isRenderingTemplate() const;
bool isRenderingPlaceholder() const;
RenderingContextType getCurrentContextType() const;
bool isComplete() const;
bool hasError() const;
String getStateString() const;
String getStackTrace() const;
// Set the registry to use for placeholder lookups
void setRegistry(DeviceFrameworkPlaceholderRegistry* reg) { registry = reg; }
// Unified buffer management
bool refillBuffer();
char getNextChar();
size_t getAvailableBytes() const;
bool hasMoreData() const;
void resetPlaceholder();
};
#endif // DEVICEFRAMEWORK_TEMPLATE_CONTEXT_H
@@ -0,0 +1,121 @@
#ifndef DEVICEFRAMEWORK_TEMPLATE_ENGINE_DEBUG_H
#define DEVICEFRAMEWORK_TEMPLATE_ENGINE_DEBUG_H
#include <Arduino.h>
// ============================================================================
// LOGGING INTERFACE - Framework Agnostic
// ============================================================================
/**
* Logging interface for the DeviceFramework Template Engine
*
* This interface allows any logging system to be plugged into the template engine.
* The template engine itself remains completely framework-agnostic.
*
* Usage:
* 1. Implement this interface with your preferred logging system
* 2. Call deviceFrameworkTemplateEngineEnableLogging() with your logger instance
* 3. All DFTE_LOG_* macros will then use your logger
*
* Example:
* class MyLogger : public DeviceFrameworkTemplateEngineLogger {
* void error(const String& msg) override { // your logging logic }
* void warn(const String& msg) override { // your logging logic }
* void info(const String& msg) override { // your logging logic }
* void debug(const String& msg) override { // your logging logic }
* };
*
* MyLogger myLogger;
* deviceFrameworkTemplateEngineEnableLogging(&myLogger);
*/
class DeviceFrameworkTemplateEngineLogger {
public:
virtual void error(const String& msg) = 0;
virtual void warn(const String& msg) = 0;
virtual void info(const String& msg) = 0;
virtual void debug(const String& msg) = 0;
virtual ~DeviceFrameworkTemplateEngineLogger() {}
};
// Global logger pointer - set by user or defaults to nullptr (disabled)
extern DeviceFrameworkTemplateEngineLogger* deviceFrameworkTemplateEngineLogger;
// ============================================================================
// LOGGING MACROS - NO-OPS BY DEFAULT
// ============================================================================
/**
* DeviceFramework Template Engine Logging System
*
* LOGGING IS DISABLED BY DEFAULT - All macros are no-ops unless explicitly enabled
*
* To enable logging:
* 1. Implement the DeviceFrameworkTemplateEngineLogger interface with your preferred logging system
* 2. Call deviceFrameworkTemplateEngineEnableLogging() with your logger instance
*
* This design ensures:
* - Zero logging overhead by default
* - Complete framework decoupling
* - Flexible logging system integration
* - No build flags or conditional compilation needed
*/
// All logging macros are no-ops by default - zero overhead unless explicitly enabled
#define DFTE_LOG_ERROR(msg) do { \
if (deviceFrameworkTemplateEngineLogger) { \
deviceFrameworkTemplateEngineLogger->error(msg); \
} \
} while(0)
#define DFTE_LOG_WARN(msg) do { \
if (deviceFrameworkTemplateEngineLogger) { \
deviceFrameworkTemplateEngineLogger->warn(msg); \
} \
} while(0)
#define DFTE_LOG_INFO(msg) do { \
if (deviceFrameworkTemplateEngineLogger) { \
deviceFrameworkTemplateEngineLogger->info(msg); \
} \
} while(0)
#define DFTE_LOG_DEBUG(msg) do { \
if (deviceFrameworkTemplateEngineLogger) { \
deviceFrameworkTemplateEngineLogger->debug(msg); \
} \
} while(0)
// ============================================================================
// LOGGING CONFIGURATION FUNCTIONS
// ============================================================================
/**
* Enable logging with a custom logger implementation
*
* @param logger Pointer to custom logger implementation
* @return true if logging was enabled, false if logger is null
*/
bool deviceFrameworkTemplateEngineEnableLogging(DeviceFrameworkTemplateEngineLogger* logger);
/**
* Disable all logging
* Sets logger to nullptr, making all DFTE_LOG_* macros no-ops
*/
void deviceFrameworkTemplateEngineDisableLogging();
/**
* Check if logging is currently enabled
*
* @return true if logging is enabled, false if disabled
*/
bool deviceFrameworkTemplateEngineIsLoggingEnabled();
/**
* Get current logger
*
* @return Pointer to current logger or nullptr if disabled
*/
DeviceFrameworkTemplateEngineLogger* deviceFrameworkTemplateEngineGetLogger();
#endif // DEVICEFRAMEWORK_TEMPLATE_ENGINE_DEBUG_H
+111
View File
@@ -0,0 +1,111 @@
#ifndef DEVICEFRAMEWORK_TEMPLATE_RENDERER_H
#define DEVICEFRAMEWORK_TEMPLATE_RENDERER_H
#include <Arduino.h>
#include "DeviceFrameworkTemplateContext.h"
#include "DeviceFrameworkPlaceholderRegistry.h"
// Fallback defaults when DeviceFrameworkConfig is not available (standalone usage)
#ifndef DFTE_MAX_ITERATIONS_DEFAULT
#define DFTE_MAX_ITERATIONS_DEFAULT 50
#endif
// Use DeviceFramework config defaults at compile-time if available, otherwise use internal defaults
#ifdef DEVICEFRAMEWORK_CONFIG_H
// DeviceFramework is present - use config defaults
// DeviceFrameworkConfig.h must be included before this file to access CONFIG_*_default macros
#ifdef CONFIG_templateMaxIterations_default
#define DFTE_MAX_ITERATIONS CONFIG_templateMaxIterations_default
#else
#define DFTE_MAX_ITERATIONS DFTE_MAX_ITERATIONS_DEFAULT
#endif
#else
// Standalone usage - use internal defaults
#define DFTE_MAX_ITERATIONS DFTE_MAX_ITERATIONS_DEFAULT
#endif
/**
* DeviceFramework Template Renderer
* Core rendering engine for streaming template output with chunked processing
*/
class DeviceFrameworkTemplateRenderer {
public:
struct RenderOutcome {
size_t bytesWritten;
TemplateRenderState nextState;
bool repeat;
bool finished;
bool errored;
uint8_t popCount;
struct {
bool active;
RenderingContextType type;
const PlaceholderEntry* entry;
} pushContext;
};
/**
* Render next chunk of template
* Call repeatedly until returns 0
*
* @param ctx Rendering context (maintains state between calls)
* @param buffer Output buffer to write to
* @param maxLen Maximum bytes to write to buffer
* @return Number of bytes written (0 = complete or error)
*/
static size_t renderNextChunk(DeviceFrameworkTemplateContext& ctx, uint8_t* buffer, size_t maxLen);
/**
* Initialize rendering context with template stored in PROGMEM
* Call once before rendering begins
*
* @param ctx Context to initialize
* @param templateData Pointer to PROGMEM template string
*/
static void initializeContext(DeviceFrameworkTemplateContext& ctx, const char* templateData);
/**
* Initialize rendering context with template stored in RAM or PROGMEM
*
* @param ctx Context to initialize
* @param templateData Pointer to template string
* @param templateInProgmem True if template is stored in PROGMEM, false if in RAM
*/
static void initializeContext(DeviceFrameworkTemplateContext& ctx, const char* templateData, bool templateInProgmem);
/**
* Check if rendering is complete
*/
static bool isComplete(const DeviceFrameworkTemplateContext& ctx);
/**
* Check if rendering encountered an error
*/
static bool hasError(const DeviceFrameworkTemplateContext& ctx);
/**
* Helper constructors for RenderOutcome
*/
static RenderOutcome makeWritten(size_t bytes, TemplateRenderState state, bool repeat = false);
static RenderOutcome makeState(TemplateRenderState nextState, bool repeat = true);
static RenderOutcome makeComplete();
static RenderOutcome makeError();
private:
static RenderOutcome renderChunk(DeviceFrameworkTemplateContext& ctx, uint8_t* buffer, size_t maxLen);
static bool applyStackCommands(DeviceFrameworkTemplateContext& ctx, const RenderOutcome& outcome);
// Core processing methods
static RenderOutcome consumeTemplateText(DeviceFrameworkTemplateContext& ctx, uint8_t* buffer, size_t maxLen);
static RenderOutcome buildPlaceholderToken(DeviceFrameworkTemplateContext& ctx);
static RenderOutcome resolvePlaceholder(DeviceFrameworkTemplateContext& ctx);
static RenderOutcome emitActiveContext(DeviceFrameworkTemplateContext& ctx, uint8_t* buffer, size_t maxLen);
static RenderOutcome streamPlaceholderData(DeviceFrameworkTemplateContext& ctx, RenderingContext* context, uint8_t* buffer, size_t maxLen);
static RenderOutcome handleTemplateCompletion(DeviceFrameworkTemplateContext& ctx);
// Constants
static constexpr size_t MAX_ITERATIONS = DFTE_MAX_ITERATIONS;
};
#endif // DEVICEFRAMEWORK_TEMPLATE_RENDERER_H
+204
View File
@@ -0,0 +1,204 @@
#ifndef DEVICEFRAMEWORK_TEMPLATE_TYPES_H
#define DEVICEFRAMEWORK_TEMPLATE_TYPES_H
#include <Arduino.h>
// Fallback defaults when DeviceFrameworkConfig is not available (standalone usage)
// Always use internal macro names (DFTE_*) to avoid conflicts with DeviceFrameworkConfig extern declarations
#ifndef DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
#define DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT 24
#endif
// Use DeviceFramework config defaults at compile-time if available, otherwise use internal defaults
// Array sizes must be compile-time constants
#ifdef DEVICEFRAMEWORK_CONFIG_H
// DeviceFramework is present - use config defaults for array sizing
// DeviceFrameworkConfig.h must be included before this file to access CONFIG_*_default macros
#ifdef CONFIG_templatePlaceholderNameSize_default
#define DFTE_PLACEHOLDER_NAME_SIZE CONFIG_templatePlaceholderNameSize_default
#else
#define DFTE_PLACEHOLDER_NAME_SIZE DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
#endif
#else
// Standalone usage - use internal defaults
#define DFTE_PLACEHOLDER_NAME_SIZE DFTE_PLACEHOLDER_NAME_SIZE_DEFAULT
#endif
/**
* Placeholder types for template substitution
*/
enum class PlaceholderType {
PROGMEM_DATA, // Large PROGMEM data (CSS, JS, base64 images)
PROGMEM_TEMPLATE, // Nested template in PROGMEM
RAM_DATA, // Dynamic RAM data via getter functions
DYNAMIC_TEMPLATE,
CONDITIONAL,
ITERATOR
};
/**
* Function pointer types for placeholder data access
*/
typedef const char* (*PlaceholderDataGetter)();
typedef size_t (*PlaceholderLengthGetter)(const void* data);
typedef const char* (*DynamicTemplateGetter)(void* userData);
typedef size_t (*DynamicTemplateLengthGetter)(const char* data, void* userData);
enum class ConditionalBranchResult {
SKIP,
TRUE_BRANCH,
FALSE_BRANCH
};
typedef ConditionalBranchResult (*ConditionalEvaluator)(void* userData);
enum class IteratorStepResult {
ITEM_READY,
COMPLETE,
ERROR
};
struct IteratorItemView;
typedef void* (*IteratorOpenHandler)(void* userData);
typedef IteratorStepResult (*IteratorNextHandler)(void* handle, struct IteratorItemView& view);
typedef void (*IteratorCloseHandler)(void* handle);
struct DynamicTemplateDescriptor {
DynamicTemplateGetter getter;
DynamicTemplateLengthGetter getLength;
void* userData;
};
struct ConditionalDescriptor {
ConditionalEvaluator evaluate;
const char* truePlaceholder;
const char* falsePlaceholder;
void* userData;
};
/**
* Placeholder definition entry
* Represents a single registered placeholder in the registry
*/
struct PlaceholderEntry {
// Allocated to configured size - matches CONFIG_templatePlaceholderNameSize when DeviceFramework is present
char name[DFTE_PLACEHOLDER_NAME_SIZE];
PlaceholderType type;
const void* data;
PlaceholderLengthGetter getLength;
PlaceholderEntry()
: type(PlaceholderType::RAM_DATA),
data(nullptr),
getLength(nullptr) {
name[0] = '\0';
}
};
struct IteratorItemView {
const char* templateData;
size_t templateLength;
bool templateIsProgmem;
const PlaceholderEntry* placeholders;
size_t placeholderCount;
};
struct IteratorDescriptor {
IteratorOpenHandler open;
IteratorNextHandler next;
IteratorCloseHandler close;
void* userData;
};
/**
* Rendering context types - what kind of thing are we currently rendering?
*/
enum class RenderingContextType {
TEMPLATE, // Rendering a template (contains placeholders)
PLACEHOLDER_DATA, // Rendering a data placeholder (PROGMEM_DATA, RAM_DATA)
PLACEHOLDER_TEMPLATE, // Rendering a template placeholder (resolved to template)
PLACEHOLDER_DYNAMIC_TEMPLATE,
PLACEHOLDER_CONDITIONAL,
PLACEHOLDER_ITERATOR
};
/**
* Unified rendering context entry
* Represents any active rendering context (template or placeholder)
* Note: Uses PlaceholderEntry which is defined above
*/
struct RenderingContext {
RenderingContextType type;
const char* name; // Placeholder name or template identifier
// Type-specific data (using union to save memory)
union {
// TEMPLATE context
struct {
const char* templateData; // Pointer to PROGMEM or RAM template data (not a copy)
size_t templateLen; // Length of template in bytes
bool isProgmem; // Storage location flag
size_t position; // Current position in template
size_t bufferPos; // Current position in buffer
size_t bufferLen; // Current buffer length
size_t bufferOffset; // Offset in template where buffer starts
const PlaceholderEntry* iteratorPlaceholders;
size_t iteratorPlaceholderCount;
} templateCtx;
// PLACEHOLDER_DATA context
struct {
const PlaceholderEntry* entry;
size_t offset; // Current offset in data
} data;
// PLACEHOLDER_TEMPLATE context
struct {
const PlaceholderEntry* entry;
// Template state is stored in nested TEMPLATE context
} templatePlaceholder;
struct {
const PlaceholderEntry* entry;
size_t offset;
const char* templateData;
size_t templateLength;
} dynamicTemplate;
struct {
const PlaceholderEntry* entry;
const ConditionalDescriptor* descriptor;
bool branchResolved;
const char* delegateName;
const PlaceholderEntry* delegateEntry;
} conditional;
struct {
const PlaceholderEntry* entry;
const IteratorDescriptor* descriptor;
void* handle;
bool initialized;
bool handleOpen;
} iterator;
} context;
RenderingContext()
: type(RenderingContextType::TEMPLATE), name(nullptr) {
memset(&context, 0, sizeof(context));
}
};
/**
* Template rendering state
*/
enum class TemplateRenderState {
TEXT, // Reading normal text in current context
BUILDING_PLACEHOLDER, // Building placeholder name between % and %
RENDERING_CONTEXT, // Rendering current context (unified for all context types)
COMPLETE, // All rendering complete
ERROR // Error state
};
#endif // DEVICEFRAMEWORK_TEMPLATE_TYPES_H
+55
View File
@@ -0,0 +1,55 @@
#ifndef TEMPLATE_ENGINE_H
#define TEMPLATE_ENGINE_H
/**
* DeviceFramework Template Engine
* Main public API header - include this in your projects
*
* Memory-efficient streaming template renderer for ESP8266/ESP32
* Supports chunked rendering with PROGMEM templates and nested template support
*
* Usage:
* 1. Create a PlaceholderRegistry and register your placeholders
* 2. Create a TemplateContext
* 3. Set the registry on the context
* 4. Initialize with your template
* 5. Call renderNextChunk() repeatedly until complete
*
* Example:
* DeviceFrameworkPlaceholderRegistry registry;
* registry.registerProgmemData("%CSS%", my_css);
*
* DeviceFrameworkTemplateContext ctx;
* ctx.setRegistry(&registry);
* DeviceFrameworkTemplateRenderer::initializeContext(ctx, my_template);
*
* uint8_t buffer[512];
* while (!DeviceFrameworkTemplateRenderer::isComplete(ctx)) {
* size_t written = DeviceFrameworkTemplateRenderer::renderNextChunk(ctx, buffer, sizeof(buffer));
* server->sendContent((const char*)buffer, written);
* }
*/
// Type definitions first
#include "DeviceFrameworkTemplateTypes.h"
// Core components
#include "DeviceFrameworkTemplateRenderer.h"
#include "DeviceFrameworkTemplateContext.h"
#include "DeviceFrameworkPlaceholderRegistry.h"
#include "DeviceFrameworkTemplateEngineDebug.h"
// Type aliases for convenience
using TemplateRenderer = DeviceFrameworkTemplateRenderer;
using TemplateContext = DeviceFrameworkTemplateContext;
using PlaceholderRegistry = DeviceFrameworkPlaceholderRegistry;
// Use new type names
using PlaceholderType = PlaceholderType;
using PlaceholderEntry = PlaceholderEntry;
using RenderingContextType = RenderingContextType;
using RenderingContext = RenderingContext;
using TemplateRenderState = TemplateRenderState;
#endif // TEMPLATE_ENGINE_H