diff --git a/features/record/pom.xml b/features/record/pom.xml
new file mode 100644
index 0000000..4374b9c
--- /dev/null
+++ b/features/record/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+ cc.carm.lib
+ configured-parent
+ 4.1.6
+ ../../pom.xml
+
+
+ configured-feature-record
+ jar
+
+
+ 16
+ 16
+ UTF-8
+
+
+
+
+
+ cc.carm.lib
+ configured-core
+ ${project.version}
+
+
+
+ cc.carm.lib
+ configured-temp
+ ${project.version}
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 16
+ 16
+ UTF-8
+ -parameters
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
diff --git a/features/record/src/main/java/cc/carm/lib/configured/adapter/record/RecordAdapter.java b/features/record/src/main/java/cc/carm/lib/configured/adapter/record/RecordAdapter.java
new file mode 100644
index 0000000..dabc43e
--- /dev/null
+++ b/features/record/src/main/java/cc/carm/lib/configured/adapter/record/RecordAdapter.java
@@ -0,0 +1,108 @@
+package cc.carm.lib.configured.adapter.record;
+
+import cc.carm.lib.configuration.adapter.ValueAdapter;
+import cc.carm.lib.configuration.adapter.ValueParser;
+import cc.carm.lib.configuration.adapter.ValueSerializer;
+import cc.carm.lib.configuration.adapter.ValueType;
+import cc.carm.lib.configuration.source.ConfigurationHolder;
+import cc.carm.lib.configuration.source.section.ConfigureSection;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.RecordComponent;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class RecordAdapter extends ValueAdapter {
+
+ public static void register(ConfigurationHolder> holder) {
+ holder.adapters().register(of(Record.class));
+ }
+
+ public static RecordAdapter of(@NotNull Class type) {
+ return of(ValueType.of(type));
+ }
+
+ public static RecordAdapter of(@NotNull ValueType type) {
+ return new RecordAdapter<>(type);
+ }
+
+ public RecordAdapter(@NotNull ValueType type) {
+ super(type, serializer(type), parser(type));
+ }
+
+ public static ValueSerializer serializer(@NotNull ValueType type) {
+ return (holder, type1, r) -> toMap(holder, r);
+ }
+
+ public static ValueParser parser(@NotNull ValueType type) {
+ return (holder, valueType, value) -> {
+ if (!(value instanceof ConfigureSection section)) return null;
+ return fromMap(holder, type.getRawType(), section.asMap());
+ };
+ }
+
+ public static Map toMap(
+ @NotNull ConfigurationHolder> holder, @NotNull R record
+ ) throws Exception {
+ Map map = new LinkedHashMap<>();
+ Class> recordClass = record.getClass();
+ if (!recordClass.isRecord()) {
+ throw new IllegalArgumentException("Object is not a record");
+ }
+ try {
+ for (RecordComponent component : recordClass.getRecordComponents()) {
+ String name = component.getName();
+ Method accessor = component.getAccessor();
+ Object value = accessor.invoke(record);
+ map.put(name, serializeValue(holder, value));
+ }
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException("Failed to convert record to map", e);
+ }
+ return map;
+ }
+
+ public static R fromMap(
+ @NotNull ConfigurationHolder> holder,
+ @NotNull Class type, @NotNull Map data
+ ) throws Exception {
+ RecordComponent[] components = type.getRecordComponents();
+ Object[] args = new Object[components.length];
+ for (int i = 0; i < components.length; i++) {
+ RecordComponent component = components[i];
+ args[i] = parseValue(holder, component.getType(), data.get(component.getName()));
+ }
+ return createInstance(type, args);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object parseValue(ConfigurationHolder> holder, Class> targetType, Object value) throws Exception {
+ if (value == null) return null;
+ if (targetType.isRecord()) {
+ return fromMap(holder, targetType.asSubclass(Record.class), (Map) value);
+ }
+ return holder.deserialize(targetType, value);
+ }
+
+ private static Object serializeValue(ConfigurationHolder> holder, Object value) throws Exception {
+ if (value == null) return null;
+ if (value.getClass().isRecord()) {
+ return toMap(holder, (Record) value);
+ }
+ return holder.serialize(value);
+ }
+
+ private static T createInstance(Class t, Object[] args) throws Exception {
+ Class>[] paramTypes = Arrays.stream(t.getRecordComponents())
+ .map(RecordComponent::getType).toArray(Class[]::new);
+ Constructor constructor = t.getDeclaredConstructor(paramTypes);
+ constructor.setAccessible(true); // Make sure the constructor is accessible
+ return constructor.newInstance(args);
+ }
+
+
+}
diff --git a/features/record/src/test/java/RecordTest.java b/features/record/src/test/java/RecordTest.java
new file mode 100644
index 0000000..421c5ef
--- /dev/null
+++ b/features/record/src/test/java/RecordTest.java
@@ -0,0 +1,76 @@
+import cc.carm.lib.configuration.Configuration;
+import cc.carm.lib.configuration.source.ConfigurationHolder;
+import cc.carm.lib.configuration.source.temp.TempConfigFactory;
+import cc.carm.lib.configuration.value.standard.ConfiguredValue;
+import cc.carm.lib.configured.adapter.record.RecordAdapter;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class RecordTest {
+
+ interface Config extends Configuration {
+
+ ConfiguredValue VAL = ConfiguredValue.of(new Device(
+ "device1",
+ "My Device",
+ UUID.fromString("123e4567-e89b-12d3-a456-426614174000"),
+ new Chip("chip1", "SN123456")
+ ));
+
+ }
+
+ @Test
+ public void test() {
+
+ ConfigurationHolder> holder = TempConfigFactory.create().build();
+ RecordAdapter.register(holder);
+ holder.initialize(Config.class);
+
+ System.out.println("Device ID: " + Config.VAL.resolve().id());
+ System.out.println("Device Name: " + Config.VAL.resolve().name());
+ System.out.println("Device Serial: " + Config.VAL.resolve().serial());
+ System.out.println("Chip ID: " + Config.VAL.resolve().chip().id());
+ System.out.println("Chip Serial: " + Config.VAL.resolve().chip().serialNumber());
+
+ try {
+ holder.save();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ printMap(holder.config().asMap(), 0);
+
+ }
+
+
+ public record Device(String id, String name, UUID serial, Chip chip) {
+ }
+
+ public record Chip(String id, String serialNumber) {
+ }
+
+ static void printMap(Map map, int indent) {
+ String indentStr = " ".repeat(indent);
+ for (Map.Entry entry : map.entrySet()) {
+ if (entry.getValue() instanceof Map, ?> subMap) {
+ System.out.println(indentStr + entry.getKey() + ":");
+ printMap((Map) subMap, indent + 2);
+ } else if (entry.getValue() instanceof List> subList) {
+ System.out.println(indentStr + entry.getKey() + ":");
+ for (Object item : subList) {
+ if (item instanceof Map, ?> itemMap) {
+ printMap((Map) itemMap, indent + 2);
+ } else {
+ System.out.println(indentStr + " - " + item);
+ }
+ }
+ } else {
+ System.out.println(indentStr + entry.getKey() + ": " + entry.getValue());
+ }
+ }
+ }
+
+}