diff --git a/pom.xml b/pom.xml
index b4c0aac..0dfd304 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,8 +9,8 @@
1.0-SNAPSHOT
- 17
- 17
+ 21
+ 21
UTF-8
@@ -20,6 +20,18 @@
jedis
5.1.5
+
+ org.projectlombok
+ lombok
+ 1.18.36
+ provided
+
+
+ org.testcontainers
+ testcontainers
+ 1.20.0
+ test
+
org.junit.jupiter
junit-jupiter
diff --git a/src/main/java/org/redis/Main.java b/src/main/java/org/redis/Main.java
index 68b3db6..6042eef 100644
--- a/src/main/java/org/redis/Main.java
+++ b/src/main/java/org/redis/Main.java
@@ -1,7 +1,8 @@
package org.redis;
+import org.redis.model.RedisMap;
+
public class Main {
public static void main(String[] args) {
- System.out.println("Hello world!");
}
}
\ No newline at end of file
diff --git a/src/main/java/org/redis/model/RedisMap.java b/src/main/java/org/redis/model/RedisMap.java
new file mode 100644
index 0000000..41233e9
--- /dev/null
+++ b/src/main/java/org/redis/model/RedisMap.java
@@ -0,0 +1,146 @@
+package org.redis.model;
+
+import com.google.gson.Gson;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import redis.clients.jedis.Jedis;
+
+public class RedisMap implements Map {
+
+ final Gson gson = new Gson();
+ final Jedis jedis;
+ final String redisKey;
+ private final Class valueClass;
+ private final Class keyClass;
+
+ public RedisMap(Jedis jedis, String redisKey, Class keyClass, Class valueClass) {
+ this.jedis = jedis;
+ this.redisKey = redisKey;
+ this.keyClass = keyClass;
+ if (!jedis.isConnected()) {
+ jedis.connect();
+ }
+ this.valueClass = valueClass;
+ }
+
+ public RedisMap(String address, int port, String password, String redisKey,
+ Class keyClass, Class valueClass) {
+ this.redisKey = redisKey;
+ this.valueClass = valueClass;
+ this.keyClass = keyClass;
+ jedis = new Jedis(address, port);
+ jedis.auth(password);
+ jedis.connect();
+ }
+
+ public RedisMap(String address, int port, String redisKey, Class keyClass,
+ Class valueClass) {
+ this.redisKey = redisKey;
+ this.valueClass = valueClass;
+ this.keyClass = keyClass;
+ jedis = new Jedis(address, port);
+ jedis.connect();
+ }
+
+ @Override
+ public int size() {
+ return Math.toIntExact(jedis.hlen(redisKey));
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return jedis.hlen(redisKey) == 0;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return jedis.hexists(redisKey, serialize(key));
+ }
+
+ private String serialize(Object obj) {
+ if (obj instanceof String) {
+ return obj.toString();
+ }
+
+ return gson.toJson(obj);
+ }
+
+ private R deserialize(String v, Class clazz) {
+ if (clazz.equals(String.class)){
+ return (R) v;
+ }
+
+ return gson.fromJson(v, clazz);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ if (value == null) {
+ return false;
+ }
+
+ return jedis.hgetAll(redisKey).containsValue(serialize(value));
+ }
+
+ @Override
+ public V get(Object key) {
+ String jsonValue = jedis.hget(redisKey, serialize(key));
+ return deserialize(jsonValue, valueClass);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ V previousVal = get(key);
+ jedis.hset(redisKey, serialize(key), serialize(value));
+ return previousVal;
+ }
+
+ @Override
+ public V remove(Object key) {
+ V value = get(key);
+ jedis.hdel(redisKey, serialize(key));
+ return value;
+ }
+
+ @Override
+ public void putAll(Map extends K, ? extends V> m) {
+ Map map = m.entrySet().stream()
+ .collect(Collectors.toMap(entry -> serialize(entry.getKey()),
+ entry -> serialize(entry.getValue())));
+ jedis.hset(redisKey, map);
+ }
+
+ @Override
+ public void clear() {
+ jedis.del(redisKey);
+ }
+
+ @Override
+ public Set keySet() {
+ return jedis.hgetAll(redisKey)
+ .keySet().stream()
+ .map(elem -> deserialize(elem, keyClass))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Collection values() {
+ return jedis.hgetAll(redisKey)
+ .values().stream()
+ .map(value -> deserialize(value, valueClass))
+ .toList();
+ }
+
+ @Override
+ public Set> entrySet() {
+ return jedis.hgetAll(redisKey)
+ .entrySet().stream()
+ .collect(
+ Collectors.toMap(
+ entry -> deserialize(entry.getKey(), keyClass),
+ entry -> deserialize(entry.getValue(), valueClass)))
+ .entrySet();
+ }
+}
diff --git a/src/test/java/org/redis/model/RedisMapTest.java b/src/test/java/org/redis/model/RedisMapTest.java
new file mode 100644
index 0000000..4edefec
--- /dev/null
+++ b/src/test/java/org/redis/model/RedisMapTest.java
@@ -0,0 +1,283 @@
+package org.redis.model;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import redis.clients.jedis.Jedis;
+
+class RedisMapTest {
+
+ private static Jedis jedis;
+ private Map map;
+ private TestUser user1 = new TestUser("Name", "LastName", 21);
+ private TestUser user2 = new TestUser("John", "Doe", 66);
+ private TestUser user3 = new TestUser("Matvey", "Testoviy", 18);
+
+ @BeforeAll
+ public static void setup() {
+ jedis = new Jedis("localhost", 6379);
+ jedis.connect();
+ }
+
+ @AfterAll
+ public static void shutDown() {
+ if (jedis != null) {
+ jedis.close();
+ }
+ }
+
+ @BeforeEach
+ void setupJedis() {
+ jedis.del("test:users");
+ map = new RedisMap<>(jedis, "test:users", String.class, TestUser.class);
+ }
+
+ @Test
+ void size_shouldReturnCorrectSize_When_AddingValues() {
+ assertEquals(0, map.size());
+
+ map.put("1", user1);
+ map.put("2", user2);
+ map.put("3", user3);
+ }
+
+ @Test
+ void size_shouldReturnCorrectSize_When_RemovingValues() {
+ map.put("1", user1);
+ map.put("2", user2);
+ map.put("3", user3);
+
+ assertEquals(3, map.size());
+
+ map.remove("3");
+
+ assertEquals(2, map.size());
+
+ map.clear();
+
+ assertEquals(0, map.size());
+ }
+
+ @Test
+ void isEmpty_ShouldReturnFalse_When_NotEmpty() {
+
+ map.put("1", user1);
+ assertFalse(map.isEmpty());
+
+ map.put("2", user2);
+ assertFalse(map.isEmpty());
+
+ map.remove("1");
+ assertFalse(map.isEmpty());
+ }
+
+ @Test
+ void isEmpty_ShouldReturnTrue_When_Empty() {
+ assertTrue(map.isEmpty());
+
+ map.put("1", user1);
+ map.put("2", user2);
+ map.remove("1");
+
+ map.clear();
+ assertTrue(map.isEmpty());
+
+ }
+
+ @Test
+ void containsKey() {
+ assertFalse(map.containsKey("1"));
+
+ map.put("1", user1);
+ assertTrue(map.containsKey("1"));
+ assertFalse(map.containsKey("2"));
+
+ map.put("2", user2);
+ map.remove("1");
+ assertFalse(map.containsKey("1"));
+ assertTrue(map.containsKey("2"));
+
+ map.clear();
+ assertFalse(map.containsKey("1"));
+ assertFalse(map.containsKey("2"));
+
+ }
+
+ @Test
+ void containsValue_ShouldReturnTrue_After_ValuesAdded() {
+
+ map.put("1", user1);
+ assertTrue(map.containsValue(user1));
+
+ map.put("2", user2);
+ assertTrue(map.containsValue(user2));
+ assertTrue(map.containsValue(user1));
+ }
+
+ @Test
+ void containsValue_ShouldReturnFalse_After_ValuesRemoved() {
+
+ map.put("1", user1);
+
+ map.put("2", user2);
+
+ map.remove("1");
+ assertFalse(map.containsValue(user1));
+ assertTrue(map.containsValue(user2));
+
+ map.clear();
+ assertFalse(map.containsValue(user1));
+ assertFalse(map.containsValue(user2));
+ }
+
+ @Test
+ void get_ShouldReturnNull_When_ValuesNotExist() {
+ assertNull(map.get("1"));
+
+ map.put("1", user1);
+ map.put("2", user2);
+
+ map.remove("1");
+ assertNull(map.get("1"));
+
+ map.clear();
+ assertNull(map.get("1"));
+ assertNull(map.get("2"));
+
+ }
+ @Test
+ void get_ShouldReturnValue_When_ValueExist() {
+
+ map.put("1", user1);
+ assertEquals(user1, map.get("1"));
+
+ map.put("2", user2);
+ assertEquals(user1, map.get("1"));
+ assertEquals(user2, map.get("2"));
+ }
+
+ @Test
+ void put_ShouldContainNewValueAndReturnNull_When_AddingNonExistentKey() {
+ assertNull(map.put("1", user1));
+
+ assertNull(map.put("2", user3));
+
+ }
+
+ @Test
+ void put_ShouldContainNewValueAndReturnOldValue_When_OverridingKey() {
+
+ map.put("1", user1);
+ assertEquals(user1, map.put("1", user2));
+
+ assertTrue(map.containsValue(user2));
+ assertFalse(map.containsValue(user1));
+ assertTrue(map.containsKey("1"));
+
+ map.put("2", user3);
+ assertEquals(user3, map.get("2"));
+ assertEquals(user3, map.put("2", user1));
+ }
+
+ @Test
+ void remove() {
+ assertNull(map.remove("1"));
+
+ assertEquals(0, map.size());
+
+ map.put("2", user2);
+ assertEquals(1, map.size());
+ assertNull(map.remove("1"));
+ assertEquals(user2, map.remove("2"));
+ assertEquals(0, map.size());
+
+ }
+
+ @Test
+ void putAll() {
+ assertTrue(map.isEmpty());
+
+ map.putAll(Map.of("1", user1, "2", user2, "3", user3));
+ assertFalse(map.isEmpty());
+ assertEquals(3, map.size());
+
+ map.putAll(Map.of("4", user1, "3", user2, "5", user3));
+ assertEquals(5, map.size());
+
+ }
+
+ @Test
+ void clear() {
+ assertTrue(map.isEmpty());
+ map.put("1", user1);
+ map.put("2", user2);
+ map.putAll(Map.of("3", user1, "4", user2, "5", user3));
+ assertFalse(map.isEmpty());
+
+ map.clear();
+ assertTrue(map.isEmpty());
+ }
+
+ @Test
+ void keySet() {
+ assertEquals(Collections.emptySet(), map.keySet());
+
+ map.putAll(Map.of("1", user1, "2", user2, "3", user3));
+ assertEquals(3, map.keySet().size());
+ assertEquals(Set.of("1", "2", "3"), map.keySet());
+
+ map.put("4", user2);
+ map.put("5", user1);
+ assertEquals(5, map.keySet().size());
+ assertEquals(Set.of("1", "2", "3", "4", "5"), map.keySet());
+
+ map.clear();
+ assertEquals(Collections.emptySet(), map.keySet());
+ }
+
+ @Test
+ void values() {
+ assertEquals(Collections.emptyList(), map.values());
+ map.putAll(Map.of("1", user1, "2", user2, "3", user3));
+
+ assertTrue( map.values().containsAll(List.of(user1, user2, user3)));
+
+ map.put("4", user2);
+ map.put("5", user1);
+ assertTrue( map.values().containsAll(List.of(user1, user2, user3, user2, user1)));
+
+ map.clear();
+ assertEquals(Collections.emptyList(), map.values());
+
+ }
+
+ @Test
+ void entrySet() {
+ assertEquals(Collections.emptySet(), map.entrySet());
+
+ Map testMap = new java.util.HashMap<>(
+ Map.of("1", user1, "2", user2, "3", user3));
+ map.putAll(testMap);
+ assertEquals(testMap.entrySet(), map.entrySet());
+
+ map.put("4", user3);
+ map.put("5", user1);
+ testMap.put("4", user3);
+ testMap.put("5", user1);
+
+ assertEquals(testMap.entrySet(), map.entrySet());
+
+ map.clear();
+ assertEquals(Collections.emptySet(), map.entrySet());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/redis/model/TestUser.java b/src/test/java/org/redis/model/TestUser.java
new file mode 100644
index 0000000..4424996
--- /dev/null
+++ b/src/test/java/org/redis/model/TestUser.java
@@ -0,0 +1,16 @@
+package org.redis.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TestUser {
+
+ private String name;
+ private String lastName;
+ private int years;
+
+}