mirror of
https://github.com/alexhopeoconnor/DFTE.git
synced 2026-10-04 04:28:10 +10:00
Add DFTE sources and tests
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(®istry);
|
||||
* 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
|
||||
|
||||
Reference in New Issue
Block a user