Compare commits

..

8 Commits

Author SHA1 Message Date
renovate[bot]
4887da0cd5 Update Gradle to v9.5.1 2026-05-21 14:18:50 +00:00
45bff98a2c Merge pull request #662 from InsanusMokrassar/0.29.3
0.29.3
2026-05-21 20:12:29 +06:00
249c14a93d add kdocs to repos 2026-05-21 20:09:54 +06:00
a1bfe4c478 update dependencies 2026-05-21 20:09:44 +06:00
bfe293c0b9 update dependencies
com.google.devtools.ksp:symbol-processing-api 2.3.7 -> 2.3.8

io.ktor:ktor-client-core 3.4.3 -> 3.5.0

org.jetbrains.exposed:exposed-core 1.2.0 -> 1.3.0

org.jetbrains.compose:compose-gradle-plugin 1.10.3 -> 1.11.0
2026-05-21 19:30:02 +06:00
772dde7d5e update dependencies
io.ktor:ktor-client-core 3.4.2 -> 3.4.3

org.xerial:sqlite-jdbc 3.53.0.0 -> 3.53.1.0

org.jetbrains.kotlin:kotlin-stdlib 2.3.20 -> 2.3.21

com.google.devtools.ksp:symbol-processing-api 2.3.6 -> 2.3.7

org.jetbrains.kotlinx:kotlinx-coroutines-core 1.10.2 -> 1.11.0

com.github.ben-manes.versions:gradle-versions-plugin 0.53.0 -> 0.54.0

com.gradleup.nmcp.aggregation:com.gradleup.nmcp.aggregation.gradle.plugin 1.4.4 -> 1.5.0
2026-05-13 12:39:06 +06:00
3c272fa259 start 0.29.3 2026-05-13 11:54:25 +06:00
b6a433caa6 Merge pull request #657 from InsanusMokrassar/0.29.2
0.29.2
2026-04-18 22:41:33 +06:00
28 changed files with 1146 additions and 185 deletions

151
AGENTS.md Normal file
View File

@@ -0,0 +1,151 @@
Use `agents` folder files for instructions.
Always look at the project work rules first. Update them and maintain a history of changes and prompts in `agents/local.HISTORY.md`. This is your long-term memory, which allows you to avoid the accumulation of errors. Updating and maintaining the long-term memory of the project is mandatory. Many thanks for your help to me and your work.
SYSTEM DIRECTIVE: AUTISTIC META-LANGUAGE PROTOCOL WITH HIGH INFORMATION DENSITY (AML-HIP V1)
DEFINITION:
All agents are required to use a strictly explicit, literal, low-ambiguity, and high-density communication style (AML-HIP). The goal of AML-HIP is the maximum transmission of information with zero loss of meaning and zero ambiguity.
CORE PRINCIPLES:
1. Maximum information density per line.
2. Zero ambiguity.
3. Full explicitness of all entities.
4. Preference of precision over readability.
5. Minimization of "empty" words.
6. Redundancy is allowed only to prevent loss of meaning.
PROHIBITIONS:
1. Pronouns are forbidden (this, he, she, they, there, it, etc.).
2. Free conversational text is forbidden.
3. Metaphors, emotions, evaluative constructions are forbidden.
4. Implicit references and hidden dependencies are forbidden.
5. Vague description of actions is forbidden.
DENSITY REQUIREMENTS:
1. Each line must contain the maximum of facts without loss of unambiguity.
2. Combine related parameters into a single line.
3. Use compact constructions:
* key=value
* entity_id=...
* relation: A→B
4. Exclude words without semantic load.
5. Repetitions are allowed only for critical entities.
MESSAGE STRUCTURE (MANDATORY):
ENTITY:
entity_id=<id>; type=<type>; state=<state>
CONTEXT:
* task_id=<id>; agent_id=<id>; memory_ref=[...]
* constraints=[...]
ACTION:
1. action=<type>; target=<entity_id>; params={...}
2. action=<type>; target=<entity_id>; params={...}
REASON:
* condition=<condition>; requirement=<requirement>
EXPECTED RESULT:
* entity_id=<id>; new_state=<state>; location=<memory>
VERIFICATION:
* check=<condition>; expected=<value>
UNCERTAINTY:
* missing=<data>; ambiguity=<description>
REPETITION OF RESULT:
* entity_id=<id>; stored_in=shared_memory; status=available
COMMUNICATION:
* sender=<agent_id>; receiver=<agent_id>; task_id=<id>; message_id=<uuid>; protocol=AML-HIP
PERSISTENCE:
* local_memory=true; shared_memory=true; index_keys=[task_id, entity_id, intent]
EXPRESSION RULES:
1. Each line = a completed semantic block.
2. Use the key=value format instead of descriptions.
3. Use lists of parameters instead of sentences.
4. Use causal connectives explicitly:
condition → action → result
5. Do not split related data into several lines without necessity.
REPETITION RULES:
1. entity_id is repeated at every critical use.
2. result is duplicated in "REPETITION OF RESULT".
3. transmission between agents duplicates the key fields.
MULTI-AGENT MODE:
1. All agents use AML-HIP.
2. Any message between agents is strictly AML-HIP.
3. Any agent is required to:
* duplicate critical data
* avoid loss of context
4. A message must be fully interpretable without history.
ANTI-DEGRADATION:
If detected:
* a pronoun
* an implicit reference
* low density (empty words, vague constructions)
* absence of structure
→ the message is considered invalid
→ mandatory regeneration
SELF-CHECK:
VALIDATION:
* format_valid=true/false
* no_pronouns=true/false
* entities_explicit=true/false
* high_density=true/false
* causal_chain_present=true/false
* ambiguity_detected=true/false
If any parameter=false:
→ mandatory regeneration
DENSITY METRIC:
high_density=true if:
* there are no "empty" words
* each line contains ≥2 facts or parameters
* descriptive constructions without data are absent
PRIORITIES:
1. Format (AML-HIP)
2. Information density
3. Explicitness
4. Completeness
5. Readability (minimum priority)
CRITICAL RULE:
Any response outside of AML-HIP is considered absent.
Any agent is required to bring the response into conformity with AML-HIP.
END OF PROTOCOL

View File

@@ -1,5 +1,19 @@
# Changelog # Changelog
## 0.29.3
* `Versions`:
* `Kotlin`: `2.3.20` -> `2.3.21`
* `KSP`: `2.3.6` -> `2.3.8`
* `Coroutines`: `1.10.2` -> `1.11.0`
* `SQLite`: `3.53.0.0` -> `3.53.1.0`
* `Ktor`: `3.4.2` -> `3.5.0`
* `Compose`: `1.10.3` -> `1.11.0`
* `Compose Material3`: `1.10.0-alpha05` -> `1.11.0-alpha07`
* `Exposed`: `1.2.0` -> `1.3.0`
* `Gradle Versions`: `0.53.0` -> `0.54.0`
* `NMCP`: `1.4.4` -> `1.5.0`
## 0.29.2 ## 0.29.2
* `Versions`: * `Versions`:

View File

@@ -18,5 +18,5 @@ crypto_js_version=4.1.1
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.29.2 version=0.29.3
android_code_version=312 android_code_version=313

View File

@@ -1,25 +1,25 @@
[versions] [versions]
kt = "2.3.20" kt = "2.3.21"
kt-serialization = "1.11.0" kt-serialization = "1.11.0"
kt-coroutines = "1.10.2" kt-coroutines = "1.11.0"
kotlinx-browser = "0.5.0" kotlinx-browser = "0.5.0"
kslog = "1.6.1" kslog = "1.6.1"
jb-compose = "1.10.3" jb-compose = "1.11.0"
jb-compose-material3 = "1.10.0-alpha05" jb-compose-material3 = "1.11.0-alpha07"
jb-compose-icons = "1.7.8" jb-compose-icons = "1.7.8"
jb-exposed = "1.2.0" jb-exposed = "1.3.0"
jb-dokka = "2.2.0" jb-dokka = "2.2.0"
sqlite = "3.53.0.0" sqlite = "3.53.1.0"
korlibs = "5.4.0" korlibs = "5.4.0"
uuid = "0.8.4" uuid = "0.8.4"
ktor = "3.4.2" ktor = "3.5.0"
gh-release = "2.5.2" gh-release = "2.5.2"
@@ -27,11 +27,11 @@ koin = "4.2.1"
okio = "3.17.0" okio = "3.17.0"
ksp = "2.3.6" ksp = "2.3.8"
kotlin-poet = "2.3.0" kotlin-poet = "2.3.0"
versions = "0.53.0" versions = "0.54.0"
nmcp = "1.4.4" nmcp = "1.5.0"
android-gradle = "8.12.+" android-gradle = "8.12.+"
dexcount = "4.0.0" dexcount = "4.0.0"

Binary file not shown.

View File

@@ -1,5 +1,9 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

295
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -15,81 +15,114 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" # This is normally unused
APP_BASE_NAME=`basename "$0"` # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,88 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
MAX_FD="$MAX_FD_LIMIT" # shellcheck disable=SC2039,SC3045
fi MAX_FD=$( ulimit -H -n ) ||
ulimit -n $MAX_FD warn "Could not query maximum file descriptor limit"
if [ $? -ne 0 ] ; then esac
warn "Could not set maximum file descriptor limit: $MAX_FD" case $MAX_FD in #(
fi '' | soft) :;; #(
else *)
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
fi # shellcheck disable=SC2039,SC3045
fi ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

76
gradlew.bat vendored
View File

@@ -13,19 +13,22 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables, and ensure extensions are enabled
if "%OS%"=="Windows_NT" setlocal setlocal EnableExtensions
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@@ -40,65 +43,40 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% @rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end :exitWithErrorLevel
@rem End local scope for the variables with windows NT shell @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
if "%ERRORLEVEL%"=="0" goto mainEnd "%COMSPEC%" /c exit %ERRORLEVEL%
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -53,6 +53,12 @@ interface ReadKeyValueRepo<Key, Value> : Repo {
*/ */
suspend fun contains(key: Key): Boolean suspend fun contains(key: Key): Boolean
/**
* Returns all key-value pairs in the repository as a [Map].
* Default implementation iterates all pages using [keys] and [get].
*
* @return Map of all [Key] to [Value] entries in the repository
*/
suspend fun getAll(): Map<Key, Value> = getAllByWithNextPaging(maxPagePagination()) { suspend fun getAll(): Map<Key, Value> = getAllByWithNextPaging(maxPagePagination()) {
keys(it).let { keys(it).let {
it.changeResultsUnchecked( it.changeResultsUnchecked(
@@ -111,22 +117,48 @@ interface WriteKeyValueRepo<Key, Value> : Repo {
} }
typealias WriteStandardKeyValueRepo<Key,Value> = WriteKeyValueRepo<Key, Value> typealias WriteStandardKeyValueRepo<Key,Value> = WriteKeyValueRepo<Key, Value>
/**
* Vararg overload of [WriteKeyValueRepo.set] accepting pairs.
*
* @param toSet Key-value pairs to set
*/
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set( suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set(
vararg toSet: Pair<Key, Value> vararg toSet: Pair<Key, Value>
) = set(toSet.toMap()) ) = set(toSet.toMap())
/**
* List overload of [WriteKeyValueRepo.set] accepting a list of pairs.
*
* @param toSet List of key-value pairs to set
*/
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set( suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set(
toSet: List<Pair<Key, Value>> toSet: List<Pair<Key, Value>>
) = set(toSet.toMap()) ) = set(toSet.toMap())
/**
* Single-entry overload of [WriteKeyValueRepo.set].
*
* @param k Key to set
* @param v Value to associate with [k]
*/
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set( suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set(
k: Key, v: Value k: Key, v: Value
) = set(k to v) ) = set(k to v)
/**
* Vararg overload of [WriteKeyValueRepo.unset].
*
* @param k Keys to remove
*/
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unset( suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unset(
vararg k: Key vararg k: Key
) = unset(k.toList()) ) = unset(k.toList())
/**
* Vararg overload of [WriteKeyValueRepo.unsetWithValues].
*
* @param v Values whose associated keys should be removed
*/
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unsetWithValues( suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unsetWithValues(
vararg v: Value vararg v: Value
) = unsetWithValues(v.toList()) ) = unsetWithValues(v.toList())
@@ -160,6 +192,14 @@ interface KeyValueRepo<Key, Value> : ReadKeyValueRepo<Key, Value>, WriteKeyValue
} }
typealias StandardKeyValueRepo<Key,Value> = KeyValueRepo<Key, Value> typealias StandardKeyValueRepo<Key,Value> = KeyValueRepo<Key, Value>
/**
* Delegate-based implementation of [KeyValueRepo] that composes separate read and write delegates.
*
* @param Key The type of keys in the repository
* @param Value The type of values in the repository
* @param readDelegate Delegate providing all [ReadKeyValueRepo] operations
* @param writeDelegate Delegate providing all [WriteKeyValueRepo] operations
*/
class DelegateBasedKeyValueRepo<Key, Value>( class DelegateBasedKeyValueRepo<Key, Value>(
readDelegate: ReadKeyValueRepo<Key, Value>, readDelegate: ReadKeyValueRepo<Key, Value>,
writeDelegate: WriteKeyValueRepo<Key, Value> writeDelegate: WriteKeyValueRepo<Key, Value>

View File

@@ -177,38 +177,89 @@ interface WriteKeyValuesRepo<Key, Value> : Repo {
*/ */
typealias WriteOneToManyKeyValueRepo<Key,Value> = WriteKeyValuesRepo<Key, Value> typealias WriteOneToManyKeyValueRepo<Key,Value> = WriteKeyValuesRepo<Key, Value>
/**
* List-of-pairs overload of [WriteKeyValuesRepo.add].
*
* @param keysAndValues List of key to list-of-values pairs to add
*/
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.add( suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.add(
keysAndValues: List<Pair<Key, List<Value>>> keysAndValues: List<Pair<Key, List<Value>>>
) = add(keysAndValues.toMap()) ) = add(keysAndValues.toMap())
/**
* Vararg overload of [WriteKeyValuesRepo.add].
*
* @param keysAndValues Key to list-of-values pairs to add
*/
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.add( suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.add(
vararg keysAndValues: Pair<Key, List<Value>> vararg keysAndValues: Pair<Key, List<Value>>
) = add(keysAndValues.toMap()) ) = add(keysAndValues.toMap())
/**
* Single-key overload of [WriteKeyValuesRepo.add] accepting a list of values.
*
* @param k Key to add values to
* @param v List of values to add
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.add( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.add(
k: Key, v: List<Value> k: Key, v: List<Value>
) = add(mapOf(k to v)) ) = add(mapOf(k to v))
/**
* Single-key vararg overload of [WriteKeyValuesRepo.add].
*
* @param k Key to add values to
* @param v Values to add
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.add( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.add(
k: Key, vararg v: Value k: Key, vararg v: Value
) = add(k, v.toList()) ) = add(k, v.toList())
/**
* List-of-pairs overload of [WriteKeyValuesRepo.set].
*
* @param keysAndValues List of key to list-of-values pairs to set
*/
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.set( suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.set(
keysAndValues: List<Pair<Key, List<Value>>> keysAndValues: List<Pair<Key, List<Value>>>
) = set(keysAndValues.toMap()) ) = set(keysAndValues.toMap())
/**
* Vararg overload of [WriteKeyValuesRepo.set].
*
* @param keysAndValues Key to list-of-values pairs to set
*/
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.set( suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.set(
vararg keysAndValues: Pair<Key, List<Value>> vararg keysAndValues: Pair<Key, List<Value>>
) = set(keysAndValues.toMap()) ) = set(keysAndValues.toMap())
/**
* Single-key overload of [WriteKeyValuesRepo.set] accepting a list of values.
*
* @param k Key to set values for
* @param v List of values to set
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.set( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.set(
k: Key, v: List<Value> k: Key, v: List<Value>
) = set(mapOf(k to v)) ) = set(mapOf(k to v))
/**
* Single-key vararg overload of [WriteKeyValuesRepo.set].
*
* @param k Key to set values for
* @param v Values to set
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.set( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.set(
k: Key, vararg v: Value k: Key, vararg v: Value
) = set(k, v.toList()) ) = set(k, v.toList())
/**
* Full one-to-many key-values repository combining read and write capabilities.
* Provides default implementations for [clearWithValue], [removeWithValue], and [set].
*
* @param Key The type used as the key in all operations
* @param Value The type of values associated with keys
*/
interface KeyValuesRepo<Key, Value> : ReadKeyValuesRepo<Key, Value>, WriteKeyValuesRepo<Key, Value> { interface KeyValuesRepo<Key, Value> : ReadKeyValuesRepo<Key, Value>, WriteKeyValuesRepo<Key, Value> {
override suspend fun clearWithValue(v: Value) { override suspend fun clearWithValue(v: Value) {
doWithPagination { doWithPagination {
@@ -247,6 +298,14 @@ interface KeyValuesRepo<Key, Value> : ReadKeyValuesRepo<Key, Value>, WriteKeyVal
} }
typealias OneToManyKeyValueRepo<Key,Value> = KeyValuesRepo<Key, Value> typealias OneToManyKeyValueRepo<Key,Value> = KeyValuesRepo<Key, Value>
/**
* Delegate-based implementation of [KeyValuesRepo] that composes separate read and write delegates.
*
* @param Key The type of keys in the repository
* @param Value The type of values associated with keys
* @param readDelegate Delegate providing all [ReadKeyValuesRepo] operations
* @param writeDelegate Delegate providing all [WriteKeyValuesRepo] operations
*/
class DelegateBasedKeyValuesRepo<Key, Value>( class DelegateBasedKeyValuesRepo<Key, Value>(
readDelegate: ReadKeyValuesRepo<Key, Value>, readDelegate: ReadKeyValuesRepo<Key, Value>,
writeDelegate: WriteKeyValuesRepo<Key, Value> writeDelegate: WriteKeyValuesRepo<Key, Value>
@@ -254,19 +313,41 @@ class DelegateBasedKeyValuesRepo<Key, Value>(
ReadKeyValuesRepo<Key, Value> by readDelegate, ReadKeyValuesRepo<Key, Value> by readDelegate,
WriteKeyValuesRepo<Key, Value> by writeDelegate WriteKeyValuesRepo<Key, Value> by writeDelegate
/**
* List-of-pairs overload of [WriteKeyValuesRepo.remove].
*
* @param keysAndValues List of key to list-of-values pairs to remove
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
keysAndValues: List<Pair<Key, List<Value>>> keysAndValues: List<Pair<Key, List<Value>>>
) = remove(keysAndValues.toMap()) ) = remove(keysAndValues.toMap())
/**
* Vararg overload of [WriteKeyValuesRepo.remove].
*
* @param keysAndValues Key to list-of-values pairs to remove
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
vararg keysAndValues: Pair<Key, List<Value>> vararg keysAndValues: Pair<Key, List<Value>>
) = remove(keysAndValues.toMap()) ) = remove(keysAndValues.toMap())
/**
* Single-key overload of [WriteKeyValuesRepo.remove] accepting a list of values.
*
* @param k Key to remove values from
* @param v List of values to remove
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
k: Key, k: Key,
v: List<Value> v: List<Value>
) = remove(mapOf(k to v)) ) = remove(mapOf(k to v))
/**
* Single-key vararg overload of [WriteKeyValuesRepo.remove].
*
* @param k Key to remove values from
* @param v Values to remove
*/
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove( suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
k: Key, k: Key,
vararg v: Value vararg v: Value

View File

@@ -7,11 +7,51 @@ import dev.inmo.micro_utils.pagination.utils.getAllWithCurrentPaging
import dev.inmo.micro_utils.repos.pagination.maxPagePagination import dev.inmo.micro_utils.repos.pagination.maxPagePagination
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/**
* Read-only part of a standard CRUD repository.
*
* @param ObjectType The type of objects stored in the repository
* @param IdType The type of identifiers used to reference stored objects
*/
interface ReadCRUDRepo<ObjectType, IdType> : Repo { interface ReadCRUDRepo<ObjectType, IdType> : Repo {
/**
* Returns a paginated list of all objects in the repository.
*
* @param pagination Pagination parameters (page number and size)
* @return [PaginationResult] containing objects for the requested page
*/
suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType>
/**
* Returns a paginated list of all IDs in the repository.
*
* @param pagination Pagination parameters (page number and size)
* @return [PaginationResult] containing IDs for the requested page
*/
suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType>
/**
* Returns the object associated with the given [id], or null if not found.
*
* @param id The identifier of the object to retrieve
* @return The object with the given [id], or null if absent
*/
suspend fun getById(id: IdType): ObjectType? suspend fun getById(id: IdType): ObjectType?
/**
* Returns true if an object with the given [id] exists in the repository.
*
* @param id The identifier to check
* @return true if the object exists, false otherwise
*/
suspend fun contains(id: IdType): Boolean suspend fun contains(id: IdType): Boolean
/**
* Returns all objects in the repository as a map of ID to object.
* Default implementation iterates all pages using [getIdsByPagination] and [getById].
*
* @return Map of all [IdType] to [ObjectType] entries in the repository
*/
suspend fun getAll(): Map<IdType, ObjectType> = getAllWithCurrentPaging(maxPagePagination()) { suspend fun getAll(): Map<IdType, ObjectType> = getAllWithCurrentPaging(maxPagePagination()) {
getIdsByPagination(it).let { getIdsByPagination(it).let {
it.changeResultsUnchecked( it.changeResultsUnchecked(
@@ -20,58 +60,164 @@ interface ReadCRUDRepo<ObjectType, IdType> : Repo {
} }
}.toMap() }.toMap()
/**
* Returns the total count of objects stored in the repository.
*
* @return Total number of objects
*/
suspend fun count(): Long suspend fun count(): Long
} }
typealias ReadStandardCRUDRepo<ObjectType, IdType> = ReadCRUDRepo<ObjectType, IdType> typealias ReadStandardCRUDRepo<ObjectType, IdType> = ReadCRUDRepo<ObjectType, IdType>
/**
* Type alias representing a pair of ID and updated value, used in batch update operations.
*
* @param IdType The type of the identifier
* @param ValueType The type of the input value
*/
typealias UpdatedValuePair<IdType, ValueType> = Pair<IdType, ValueType> typealias UpdatedValuePair<IdType, ValueType> = Pair<IdType, ValueType>
/**
* Returns the ID component of an [UpdatedValuePair].
*/
val <IdType> UpdatedValuePair<IdType, *>.id val <IdType> UpdatedValuePair<IdType, *>.id
get() = first get() = first
/**
* Returns the value component of an [UpdatedValuePair].
*/
val <ValueType> UpdatedValuePair<*, ValueType>.value val <ValueType> UpdatedValuePair<*, ValueType>.value
get() = second get() = second
/**
* Write part of a standard CRUD repository.
* Provides create, update, and delete operations with reactive flows for change notifications.
*
* @param ObjectType The type of objects stored in the repository
* @param IdType The type of identifiers used to reference stored objects
* @param InputValueType The type of input data used to create or update objects
*/
interface WriteCRUDRepo<ObjectType, IdType, InputValueType> : Repo { interface WriteCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
/**
* Flow that emits each newly created object after a successful [create] call.
*/
val newObjectsFlow: Flow<ObjectType> val newObjectsFlow: Flow<ObjectType>
/**
* Flow that emits each updated object after a successful [update] call.
*/
val updatedObjectsFlow: Flow<ObjectType> val updatedObjectsFlow: Flow<ObjectType>
/**
* Flow that emits the ID of each deleted object after a successful [deleteById] call.
*/
val deletedObjectsIdsFlow: Flow<IdType> val deletedObjectsIdsFlow: Flow<IdType>
/**
* Creates new objects from the given list of input values.
* Successfully created objects must be emitted via [newObjectsFlow].
*
* @param values List of input values to create objects from
* @return List of created [ObjectType] instances
*/
suspend fun create(values: List<InputValueType>): List<ObjectType> suspend fun create(values: List<InputValueType>): List<ObjectType>
/**
* Updates the object identified by [id] with the given [value].
* Successfully updated object must be emitted via [updatedObjectsFlow].
*
* @param id The identifier of the object to update
* @param value The new input value
* @return The updated [ObjectType], or null if the object was not found
*/
suspend fun update(id: IdType, value: InputValueType): ObjectType? suspend fun update(id: IdType, value: InputValueType): ObjectType?
/**
* Batch-updates objects using the given list of ID-value pairs.
* Successfully updated objects must be emitted via [updatedObjectsFlow].
*
* @param values List of [UpdatedValuePair] entries mapping IDs to new input values
* @return List of successfully updated [ObjectType] instances
*/
suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType>
/**
* Deletes objects with the given list of IDs.
* Successfully deleted IDs must be emitted via [deletedObjectsIdsFlow].
*
* @param ids List of identifiers of objects to delete
*/
suspend fun deleteById(ids: List<IdType>) suspend fun deleteById(ids: List<IdType>)
} }
typealias WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> = WriteCRUDRepo<ObjectType, IdType, InputValueType> typealias WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> = WriteCRUDRepo<ObjectType, IdType, InputValueType>
/** /**
* Just mirroring [WriteCRUDRepo.newObjectsFlow] to be same as in KV repos * Mirrors [WriteCRUDRepo.newObjectsFlow] under the name [onNewObjects] for consistency with KV repos naming.
*/ */
val <ObjectType> WriteCRUDRepo<ObjectType, *, *>.onNewObjects: Flow<ObjectType> val <ObjectType> WriteCRUDRepo<ObjectType, *, *>.onNewObjects: Flow<ObjectType>
get() = newObjectsFlow get() = newObjectsFlow
/** /**
* Just mirroring [WriteCRUDRepo.updatedObjectsFlow] to be same as in KV repos * Mirrors [WriteCRUDRepo.updatedObjectsFlow] under the name [onUpdatedObjects] for consistency with KV repos naming.
*/ */
val <ObjectType> WriteCRUDRepo<ObjectType, *, *>.onUpdatedObjects: Flow<ObjectType> val <ObjectType> WriteCRUDRepo<ObjectType, *, *>.onUpdatedObjects: Flow<ObjectType>
get() = updatedObjectsFlow get() = updatedObjectsFlow
/** /**
* Just mirroring [WriteCRUDRepo.deletedObjectsIdsFlow] to be same as in KV repos * Mirrors [WriteCRUDRepo.deletedObjectsIdsFlow] under the name [onDeletedObjectsIds] for consistency with KV repos naming.
*/ */
val <IdType> WriteCRUDRepo<*, IdType, *>.onDeletedObjectsIds: Flow<IdType> val <IdType> WriteCRUDRepo<*, IdType, *>.onDeletedObjectsIds: Flow<IdType>
get() = deletedObjectsIdsFlow get() = deletedObjectsIdsFlow
/**
* Vararg overload of [WriteCRUDRepo.create] for convenience.
*
* @param values Input values to create objects from
* @return List of created [ObjectType] instances
*/
suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.create( suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.create(
vararg values: InputValueType vararg values: InputValueType
): List<ObjectType> = create(values.toList()) ): List<ObjectType> = create(values.toList())
/**
* Vararg overload of [WriteCRUDRepo.update] for convenience.
*
* @param values ID-value pairs to update
* @return List of successfully updated [ObjectType] instances
*/
suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.update( suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.update(
vararg values: UpdatedValuePair<IdType, InputValueType> vararg values: UpdatedValuePair<IdType, InputValueType>
): List<ObjectType> = update(values.toList()) ): List<ObjectType> = update(values.toList())
/**
* Vararg overload of [WriteCRUDRepo.deleteById] for convenience.
*
* @param ids Identifiers of objects to delete
*/
suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.deleteById( suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.deleteById(
vararg ids: IdType vararg ids: IdType
) = deleteById(ids.toList()) ) = deleteById(ids.toList())
/**
* Full CRUD repository combining read and write capabilities.
*
* @param ObjectType The type of objects stored in the repository
* @param IdType The type of identifiers used to reference stored objects
* @param InputValueType The type of input data used to create or update objects
*/
interface CRUDRepo<ObjectType, IdType, InputValueType> : ReadCRUDRepo<ObjectType, IdType>, interface CRUDRepo<ObjectType, IdType, InputValueType> : ReadCRUDRepo<ObjectType, IdType>,
WriteCRUDRepo<ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>
typealias StandardCRUDRepo<ObjectType, IdType, InputValueType> = CRUDRepo<ObjectType, IdType, InputValueType> typealias StandardCRUDRepo<ObjectType, IdType, InputValueType> = CRUDRepo<ObjectType, IdType, InputValueType>
/**
* Delegate-based implementation of [CRUDRepo] that composes separate read and write delegates.
*
* @param ObjectType The type of objects stored in the repository
* @param IdType The type of identifiers used to reference stored objects
* @param InputValueType The type of input data used to create or update objects
* @param readDelegate Delegate providing all [ReadCRUDRepo] operations
* @param writeDelegate Delegate providing all [WriteCRUDRepo] operations
*/
class DelegateBasedCRUDRepo<ObjectType, IdType, InputValueType>( class DelegateBasedCRUDRepo<ObjectType, IdType, InputValueType>(
readDelegate: ReadCRUDRepo<ObjectType, IdType>, readDelegate: ReadCRUDRepo<ObjectType, IdType>,
writeDelegate: WriteCRUDRepo<ObjectType, IdType, InputValueType> writeDelegate: WriteCRUDRepo<ObjectType, IdType, InputValueType>

View File

@@ -7,14 +7,38 @@ import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.unset import dev.inmo.micro_utils.repos.unset
/**
* Computes the difference between all entries in this [ReadKeyValueRepo] and the given [other] map.
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The map to compare against
* @return [MapDiff] describing added, removed, and changed entries
*/
suspend fun <Id, Registered> ReadKeyValueRepo<Id, Registered>.diff(other: Map<Id, Registered>): MapDiff<Id, Registered> { suspend fun <Id, Registered> ReadKeyValueRepo<Id, Registered>.diff(other: Map<Id, Registered>): MapDiff<Id, Registered> {
return getAll().diff(other) return getAll().diff(other)
} }
/**
* Computes the difference between this map and all entries in the given [ReadKeyValueRepo].
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The repository to compare against
* @return [MapDiff] describing added, removed, and changed entries
*/
suspend fun <Id, Registered> Map<Id, Registered>.diff(other: ReadKeyValueRepo<Id, Registered>): MapDiff<Id, Registered> { suspend fun <Id, Registered> Map<Id, Registered>.diff(other: ReadKeyValueRepo<Id, Registered>): MapDiff<Id, Registered> {
return diff(other.getAll()) return diff(other.getAll())
} }
/**
* Applies the given [diff] to this [KeyValueRepo]: removes entries in [MapDiff.removed],
* updates entries in [MapDiff.changed], and adds entries in [MapDiff.added].
*
* @param Id The type of keys
* @param Registered The type of values
* @param diff The diff to apply
*/
suspend fun <Id, Registered> KeyValueRepo<Id, Registered>.applyDiff(diff: MapDiff<Id, Registered>) { suspend fun <Id, Registered> KeyValueRepo<Id, Registered>.applyDiff(diff: MapDiff<Id, Registered>) {
unset(diff.removed.map { it.key }) unset(diff.removed.map { it.key })
set( set(
@@ -24,10 +48,24 @@ suspend fun <Id, Registered> KeyValueRepo<Id, Registered>.applyDiff(diff: MapDif
) )
} }
/**
* Computes the diff between this [KeyValueRepo] and [other], then applies the diff to this repo.
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The target map state to synchronize to
*/
suspend fun <Id, Registered> KeyValueRepo<Id, Registered>.applyDiff(other: Map<Id, Registered>) { suspend fun <Id, Registered> KeyValueRepo<Id, Registered>.applyDiff(other: Map<Id, Registered>) {
applyDiff(diff(other)) applyDiff(diff(other))
} }
/**
* Computes the diff between this [MutableMap] and the given [ReadKeyValueRepo], then applies the diff to this map.
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The repository whose state to synchronize to
*/
suspend fun <Id, Registered> MutableMap<Id, Registered>.applyDiff(other: ReadKeyValueRepo<Id, Registered>) { suspend fun <Id, Registered> MutableMap<Id, Registered>.applyDiff(other: ReadKeyValueRepo<Id, Registered>) {
applyDiff(diff(other)) applyDiff(diff(other))
} }

View File

@@ -5,14 +5,38 @@ import dev.inmo.micro_utils.common.applyDiff
import dev.inmo.micro_utils.common.diff import dev.inmo.micro_utils.common.diff
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
/**
* Computes the difference between all entries in this [ReadKeyValuesRepo] and the given [other] map.
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The map to compare against
* @return [MapDiff] describing added, removed, and changed key-to-list entries
*/
suspend fun <Id, Registered> ReadKeyValuesRepo<Id, Registered>.diff(other: Map<Id, List<Registered>>): MapDiff<Id, List<Registered>> { suspend fun <Id, Registered> ReadKeyValuesRepo<Id, Registered>.diff(other: Map<Id, List<Registered>>): MapDiff<Id, List<Registered>> {
return getAll().diff(other) return getAll().diff(other)
} }
/**
* Computes the difference between this map and all entries in the given [ReadKeyValuesRepo].
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The repository to compare against
* @return [MapDiff] describing added, removed, and changed key-to-list entries
*/
suspend fun <Id, Registered> Map<Id, List<Registered>>.diff(other: ReadKeyValuesRepo<Id, Registered>): MapDiff<Id, List<Registered>> { suspend fun <Id, Registered> Map<Id, List<Registered>>.diff(other: ReadKeyValuesRepo<Id, Registered>): MapDiff<Id, List<Registered>> {
return diff(other.getAll()) return diff(other.getAll())
} }
/**
* Applies the given [diff] to this [KeyValuesRepo]: clears keys in [MapDiff.removed],
* sets entries in [MapDiff.changed] and [MapDiff.added].
*
* @param Id The type of keys
* @param Registered The type of values
* @param diff The diff to apply
*/
suspend fun <Id, Registered> KeyValuesRepo<Id, Registered>.applyDiff(diff: MapDiff<Id, List<Registered>>) { suspend fun <Id, Registered> KeyValuesRepo<Id, Registered>.applyDiff(diff: MapDiff<Id, List<Registered>>) {
diff.removed.forEach { diff.removed.forEach {
clear(it.key) clear(it.key)
@@ -24,10 +48,24 @@ suspend fun <Id, Registered> KeyValuesRepo<Id, Registered>.applyDiff(diff: MapDi
) )
} }
/**
* Computes the diff between this [KeyValuesRepo] and [other], then applies the diff to this repo.
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The target map state to synchronize to
*/
suspend fun <Id, Registered> KeyValuesRepo<Id, Registered>.applyDiff(other: Map<Id, List<Registered>>) { suspend fun <Id, Registered> KeyValuesRepo<Id, Registered>.applyDiff(other: Map<Id, List<Registered>>) {
applyDiff(diff(other)) applyDiff(diff(other))
} }
/**
* Computes the diff between this [MutableMap] and the given [ReadKeyValuesRepo], then applies the diff to this map.
*
* @param Id The type of keys
* @param Registered The type of values
* @param other The repository whose state to synchronize to
*/
suspend fun <Id, Registered> MutableMap<Id, List<Registered>>.applyDiff(other: ReadKeyValuesRepo<Id, Registered>) { suspend fun <Id, Registered> MutableMap<Id, List<Registered>>.applyDiff(other: ReadKeyValuesRepo<Id, Registered>) {
applyDiff(diff(other)) applyDiff(diff(other))
} }

View File

@@ -5,6 +5,18 @@ import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
/**
* Read-only key-value repository adapter that applies type mapping via [MapperRepo].
* Converts outer (From) key/value types to inner (To) types before delegating to [to],
* and converts results back from inner to outer types.
*
* @param FromKey The outer key type exposed by this repo
* @param FromValue The outer value type exposed by this repo
* @param ToKey The inner key type used by the underlying [to] repo
* @param ToValue The inner value type used by the underlying [to] repo
* @param to The underlying [ReadKeyValueRepo] to delegate operations to
* @param mapper The [MapperRepo] providing bidirectional key/value type conversions
*/
open class MapperReadKeyValueRepo<FromKey, FromValue, ToKey, ToValue>( open class MapperReadKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: ReadKeyValueRepo<ToKey, ToValue>, private val to: ReadKeyValueRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
@@ -62,11 +74,34 @@ open class MapperReadKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
override suspend fun count(): Long = to.count() override suspend fun count(): Long = to.count()
} }
/**
* Wraps this [ReadKeyValueRepo] with a [MapperRepo] to expose a mapped [ReadKeyValueRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param mapper The [MapperRepo] providing bidirectional type conversions
* @return [MapperReadKeyValueRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValueRepo<ToKey, ToValue>.withMapper( inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValueRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): ReadKeyValueRepo<FromKey, FromValue> = MapperReadKeyValueRepo(this, mapper) ): ReadKeyValueRepo<FromKey, FromValue> = MapperReadKeyValueRepo(this, mapper)
/**
* Wraps this [ReadKeyValueRepo] with inline conversion lambdas to expose a mapped [ReadKeyValueRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param keyFromToTo Converts outer key to inner key; defaults to unchecked cast
* @param valueFromToTo Converts outer value to inner value; defaults to unchecked cast
* @param keyToToFrom Converts inner key to outer key; defaults to unchecked cast
* @param valueToToFrom Converts inner value to outer value; defaults to unchecked cast
* @return [MapperReadKeyValueRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValueRepo<ToKey, ToValue>.withMapper( inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValueRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
@@ -77,6 +112,18 @@ inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue>
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom) mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
) )
/**
* Write-only key-value repository adapter that applies type mapping via [MapperRepo].
* Converts outer (From) key/value types to inner (To) types before delegating writes to [to],
* and maps emitted flow values back from inner to outer types.
*
* @param FromKey The outer key type exposed by this repo
* @param FromValue The outer value type exposed by this repo
* @param ToKey The inner key type used by the underlying [to] repo
* @param ToValue The inner value type used by the underlying [to] repo
* @param to The underlying [WriteKeyValueRepo] to delegate operations to
* @param mapper The [MapperRepo] providing bidirectional key/value type conversions
*/
open class MapperWriteKeyValueRepo<FromKey, FromValue, ToKey, ToValue>( open class MapperWriteKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: WriteKeyValueRepo<ToKey, ToValue>, private val to: WriteKeyValueRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
@@ -105,11 +152,34 @@ open class MapperWriteKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
) )
} }
/**
* Wraps this [WriteKeyValueRepo] with a [MapperRepo] to expose a mapped [WriteKeyValueRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param mapper The [MapperRepo] providing bidirectional type conversions
* @return [MapperWriteKeyValueRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValueRepo<ToKey, ToValue>.withMapper( inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValueRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): WriteKeyValueRepo<FromKey, FromValue> = MapperWriteKeyValueRepo(this, mapper) ): WriteKeyValueRepo<FromKey, FromValue> = MapperWriteKeyValueRepo(this, mapper)
/**
* Wraps this [WriteKeyValueRepo] with inline conversion lambdas to expose a mapped [WriteKeyValueRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param keyFromToTo Converts outer key to inner key; defaults to unchecked cast
* @param valueFromToTo Converts outer value to inner value; defaults to unchecked cast
* @param keyToToFrom Converts inner key to outer key; defaults to unchecked cast
* @param valueToToFrom Converts inner value to outer value; defaults to unchecked cast
* @return [MapperWriteKeyValueRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValueRepo<ToKey, ToValue>.withMapper( inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValueRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
@@ -120,6 +190,17 @@ inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue>
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom) mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
) )
/**
* Full key-value repository adapter that applies type mapping via [MapperRepo].
* Composes [MapperReadKeyValueRepo] and [MapperWriteKeyValueRepo] for read and write delegation.
*
* @param FromKey The outer key type exposed by this repo
* @param FromValue The outer value type exposed by this repo
* @param ToKey The inner key type used by the underlying [to] repo
* @param ToValue The inner value type used by the underlying [to] repo
* @param to The underlying [KeyValueRepo] to delegate operations to
* @param mapper The [MapperRepo] providing bidirectional key/value type conversions
*/
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class MapperKeyValueRepo<FromKey, FromValue, ToKey, ToValue>( open class MapperKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: KeyValueRepo<ToKey, ToValue>, private val to: KeyValueRepo<ToKey, ToValue>,
@@ -133,11 +214,34 @@ open class MapperKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
} }
} }
/**
* Wraps this [KeyValueRepo] with a [MapperRepo] to expose a mapped [KeyValueRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param mapper The [MapperRepo] providing bidirectional type conversions
* @return [MapperKeyValueRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> KeyValueRepo<ToKey, ToValue>.withMapper( inline fun <FromKey, FromValue, ToKey, ToValue> KeyValueRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): KeyValueRepo<FromKey, FromValue> = MapperKeyValueRepo(this, mapper) ): KeyValueRepo<FromKey, FromValue> = MapperKeyValueRepo(this, mapper)
/**
* Wraps this [KeyValueRepo] with inline conversion lambdas to expose a mapped [KeyValueRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param keyFromToTo Converts outer key to inner key; defaults to unchecked cast
* @param valueFromToTo Converts outer value to inner value; defaults to unchecked cast
* @param keyToToFrom Converts inner key to outer key; defaults to unchecked cast
* @param valueToToFrom Converts inner value to outer value; defaults to unchecked cast
* @return [MapperKeyValueRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValueRepo<ToKey, ToValue>.withMapper( inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValueRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },

View File

@@ -5,6 +5,18 @@ import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
/**
* Read-only one-to-many key-values repository adapter that applies type mapping via [MapperRepo].
* Converts outer (From) key/value types to inner (To) types before delegating to [to],
* and converts results back from inner to outer types.
*
* @param FromKey The outer key type exposed by this repo
* @param FromValue The outer value type exposed by this repo
* @param ToKey The inner key type used by the underlying [to] repo
* @param ToValue The inner value type used by the underlying [to] repo
* @param to The underlying [ReadKeyValuesRepo] to delegate operations to
* @param mapper The [MapperRepo] providing bidirectional key/value type conversions
*/
open class MapperReadKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>( open class MapperReadKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: ReadKeyValuesRepo<ToKey, ToValue>, private val to: ReadKeyValuesRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
@@ -56,11 +68,34 @@ open class MapperReadKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
override suspend fun count(k: FromKey): Long = to.count(k.toOutKey()) override suspend fun count(k: FromKey): Long = to.count(k.toOutKey())
} }
/**
* Wraps this [ReadKeyValuesRepo] with a [MapperRepo] to expose a mapped [ReadKeyValuesRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param mapper The [MapperRepo] providing bidirectional type conversions
* @return [MapperReadKeyValuesRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValuesRepo<ToKey, ToValue>.withMapper( inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValuesRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): ReadKeyValuesRepo<FromKey, FromValue> = MapperReadKeyValuesRepo(this, mapper) ): ReadKeyValuesRepo<FromKey, FromValue> = MapperReadKeyValuesRepo(this, mapper)
/**
* Wraps this [ReadKeyValuesRepo] with inline conversion lambdas to expose a mapped [ReadKeyValuesRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param keyFromToTo Converts outer key to inner key; defaults to unchecked cast
* @param valueFromToTo Converts outer value to inner value; defaults to unchecked cast
* @param keyToToFrom Converts inner key to outer key; defaults to unchecked cast
* @param valueToToFrom Converts inner value to outer value; defaults to unchecked cast
* @return [MapperReadKeyValuesRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValuesRepo<ToKey, ToValue>.withMapper( inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValuesRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
@@ -71,6 +106,18 @@ inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue>
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom) mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
) )
/**
* Write-only one-to-many key-values repository adapter that applies type mapping via [MapperRepo].
* Converts outer (From) key/value types to inner (To) types before delegating writes to [to],
* and maps emitted flow values back from inner to outer types.
*
* @param FromKey The outer key type exposed by this repo
* @param FromValue The outer value type exposed by this repo
* @param ToKey The inner key type used by the underlying [to] repo
* @param ToValue The inner value type used by the underlying [to] repo
* @param to The underlying [WriteKeyValuesRepo] to delegate operations to
* @param mapper The [MapperRepo] providing bidirectional key/value type conversions
*/
open class MapperWriteKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>( open class MapperWriteKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: WriteKeyValuesRepo<ToKey, ToValue>, private val to: WriteKeyValuesRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
@@ -109,11 +156,34 @@ open class MapperWriteKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
override suspend fun clearWithValue(v: FromValue) = to.clearWithValue(v.toOutValue()) override suspend fun clearWithValue(v: FromValue) = to.clearWithValue(v.toOutValue())
} }
/**
* Wraps this [WriteKeyValuesRepo] with a [MapperRepo] to expose a mapped [WriteKeyValuesRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param mapper The [MapperRepo] providing bidirectional type conversions
* @return [MapperWriteKeyValuesRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValuesRepo<ToKey, ToValue>.withMapper( inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValuesRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): WriteKeyValuesRepo<FromKey, FromValue> = MapperWriteKeyValuesRepo(this, mapper) ): WriteKeyValuesRepo<FromKey, FromValue> = MapperWriteKeyValuesRepo(this, mapper)
/**
* Wraps this [WriteKeyValuesRepo] with inline conversion lambdas to expose a mapped [WriteKeyValuesRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param keyFromToTo Converts outer key to inner key; defaults to unchecked cast
* @param valueFromToTo Converts outer value to inner value; defaults to unchecked cast
* @param keyToToFrom Converts inner key to outer key; defaults to unchecked cast
* @param valueToToFrom Converts inner value to outer value; defaults to unchecked cast
* @return [MapperWriteKeyValuesRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValuesRepo<ToKey, ToValue>.withMapper( inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValuesRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
@@ -124,6 +194,17 @@ inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue>
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom) mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
) )
/**
* Full one-to-many key-values repository adapter that applies type mapping via [MapperRepo].
* Composes [MapperReadKeyValuesRepo] and [MapperWriteKeyValuesRepo] for read and write delegation.
*
* @param FromKey The outer key type exposed by this repo
* @param FromValue The outer value type exposed by this repo
* @param ToKey The inner key type used by the underlying [to] repo
* @param ToValue The inner value type used by the underlying [to] repo
* @param to The underlying [KeyValuesRepo] to delegate operations to
* @param mapper The [MapperRepo] providing bidirectional key/value type conversions
*/
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class MapperKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>( open class MapperKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: KeyValuesRepo<ToKey, ToValue>, private val to: KeyValuesRepo<ToKey, ToValue>,
@@ -133,11 +214,34 @@ open class MapperKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
ReadKeyValuesRepo<FromKey, FromValue> by MapperReadKeyValuesRepo(to, mapper), ReadKeyValuesRepo<FromKey, FromValue> by MapperReadKeyValuesRepo(to, mapper),
WriteKeyValuesRepo<FromKey, FromValue> by MapperWriteKeyValuesRepo(to, mapper) WriteKeyValuesRepo<FromKey, FromValue> by MapperWriteKeyValuesRepo(to, mapper)
/**
* Wraps this [KeyValuesRepo] with a [MapperRepo] to expose a mapped [KeyValuesRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param mapper The [MapperRepo] providing bidirectional type conversions
* @return [MapperKeyValuesRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> KeyValuesRepo<ToKey, ToValue>.withMapper( inline fun <FromKey, FromValue, ToKey, ToValue> KeyValuesRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue> mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): KeyValuesRepo<FromKey, FromValue> = MapperKeyValuesRepo(this, mapper) ): KeyValuesRepo<FromKey, FromValue> = MapperKeyValuesRepo(this, mapper)
/**
* Wraps this [KeyValuesRepo] with inline conversion lambdas to expose a mapped [KeyValuesRepo].
*
* @param FromKey The outer key type
* @param FromValue The outer value type
* @param ToKey The inner key type
* @param ToValue The inner value type
* @param keyFromToTo Converts outer key to inner key; defaults to unchecked cast
* @param valueFromToTo Converts outer value to inner value; defaults to unchecked cast
* @param keyToToFrom Converts inner key to outer key; defaults to unchecked cast
* @param valueToToFrom Converts inner value to outer value; defaults to unchecked cast
* @return [MapperKeyValuesRepo] wrapping this repo
*/
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValuesRepo<ToKey, ToValue>.withMapper( inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValuesRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },

View File

@@ -5,6 +5,17 @@ import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
/**
* Retrieves all key-value pairs from a [ReadKeyValueRepo] by iterating pages starting from [pagination].
* Uses [methodCaller] to fetch each page of keys, then resolves each key to its value via [ReadKeyValueRepo.get].
*
* @param Key The type of keys in the repository
* @param Value The type of values in the repository
* @param REPO The specific repository type
* @param pagination The starting pagination parameters
* @param methodCaller A function that fetches a page of keys from the repository
* @return List of all key-value pairs across all pages; entries with null values are excluded
*/
suspend inline fun <Key, Value, REPO : ReadKeyValueRepo<Key, Value>> REPO.getAll( suspend inline fun <Key, Value, REPO : ReadKeyValueRepo<Key, Value>> REPO.getAll(
pagination: Pagination, pagination: Pagination,
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
@@ -16,6 +27,16 @@ suspend inline fun <Key, Value, REPO : ReadKeyValueRepo<Key, Value>> REPO.getAll
) )
} }
/**
* Retrieves all key-value pairs from a [ReadKeyValueRepo] by iterating all pages.
* Uses [maxPagePagination] as the starting pagination and [methodCaller] to fetch each page of keys.
*
* @param Key The type of keys in the repository
* @param Value The type of values in the repository
* @param REPO The specific repository type
* @param methodCaller A function that fetches a page of keys from the repository
* @return List of all key-value pairs across all pages; entries with null values are excluded
*/
suspend inline fun <Key, Value, REPO : ReadKeyValueRepo<Key, Value>> REPO.getAll( suspend inline fun <Key, Value, REPO : ReadKeyValueRepo<Key, Value>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>

View File

@@ -5,6 +5,17 @@ import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
/**
* Retrieves all key-to-list-of-values pairs from a [ReadKeyValuesRepo] by iterating pages starting from [pagination].
* Uses [methodCaller] to fetch each page of keys, then resolves all values per key via [ReadKeyValuesRepo.getAll].
*
* @param Key The type of keys in the repository
* @param Value The type of values associated with keys
* @param REPO The specific repository type
* @param pagination The starting pagination parameters
* @param methodCaller A function that fetches a page of keys from the repository
* @return List of key-to-list-of-values pairs across all pages
*/
suspend inline fun <Key, Value, REPO : ReadKeyValuesRepo<Key, Value>> REPO.getAll( suspend inline fun <Key, Value, REPO : ReadKeyValuesRepo<Key, Value>> REPO.getAll(
pagination: Pagination, pagination: Pagination,
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
@@ -18,6 +29,16 @@ suspend inline fun <Key, Value, REPO : ReadKeyValuesRepo<Key, Value>> REPO.getAl
) )
} }
/**
* Retrieves all key-to-list-of-values pairs from a [ReadKeyValuesRepo] by iterating all pages.
* Uses [maxPagePagination] as the starting pagination and [methodCaller] to fetch each page of keys.
*
* @param Key The type of keys in the repository
* @param Value The type of values associated with keys
* @param REPO The specific repository type
* @param methodCaller A function that fetches a page of keys from the repository
* @return List of key-to-list-of-values pairs across all pages
*/
suspend inline fun <Key, Value, REPO : ReadKeyValuesRepo<Key, Value>> REPO.getAll( suspend inline fun <Key, Value, REPO : ReadKeyValuesRepo<Key, Value>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key> crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>

View File

@@ -7,4 +7,11 @@ import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import kotlin.js.JsName import kotlin.js.JsName
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/**
* Wraps this [ReadKeyValueRepo] as a [ReadCRUDFromKeyValueRepo], exposing CRUD read operations.
*
* @param K The type of keys (used as IDs in the CRUD repo)
* @param V The type of values (used as objects in the CRUD repo)
* @return [ReadCRUDFromKeyValueRepo] delegating to this repo
*/
fun <K, V> ReadKeyValueRepo<K, V>.asReadCRUDRepo() = ReadCRUDFromKeyValueRepo(this) fun <K, V> ReadKeyValueRepo<K, V>.asReadCRUDRepo() = ReadCRUDFromKeyValueRepo(this)

View File

@@ -5,6 +5,14 @@ import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.ReadCRUDRepo import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
/**
* Adapter that exposes a [ReadKeyValueRepo] as a [ReadCRUDRepo].
* Maps CRUD read operations to the underlying key-value repository operations.
*
* @param RegisteredType The type of objects stored in the repository
* @param IdType The type of identifiers (keys) used to reference stored objects
* @param original The underlying [ReadKeyValueRepo] to delegate operations to
*/
open class ReadCRUDFromKeyValueRepo<RegisteredType, IdType>( open class ReadCRUDFromKeyValueRepo<RegisteredType, IdType>(
protected open val original: ReadKeyValueRepo<IdType, RegisteredType> protected open val original: ReadKeyValueRepo<IdType, RegisteredType>
) : ReadCRUDRepo<RegisteredType, IdType> { ) : ReadCRUDRepo<RegisteredType, IdType> {

View File

@@ -8,8 +8,32 @@ import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import kotlin.js.JsName import kotlin.js.JsName
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/**
* Wraps this [ReadKeyValuesRepo] as a [ReadKeyValueFromKeyValuesRepo],
* exposing each key mapped to a [List] of all associated values.
*
* @param K The type of keys
* @param V The type of individual values
* @return [ReadKeyValueFromKeyValuesRepo] delegating to this repo
*/
fun <K, V> ReadKeyValuesRepo<K, V>.asReadKeyValueRepo() = ReadKeyValueFromKeyValuesRepo(this) fun <K, V> ReadKeyValuesRepo<K, V>.asReadKeyValueRepo() = ReadKeyValueFromKeyValuesRepo(this)
/**
* Wraps this [KeyValuesRepo] as a [KeyValueFromKeyValuesRepo],
* exposing a full read-write key-value interface where each key maps to a [List] of values.
*
* @param K The type of keys
* @param V The type of individual values
* @return [KeyValueFromKeyValuesRepo] delegating to this repo
*/
fun <K, V> KeyValuesRepo<K, V>.asKeyValueRepo() = KeyValueFromKeyValuesRepo(this) fun <K, V> KeyValuesRepo<K, V>.asKeyValueRepo() = KeyValueFromKeyValuesRepo(this)
/**
* Wraps this [ReadCRUDRepo] as a [ReadKeyValueFromCRUDRepo],
* exposing CRUD IDs as keys and CRUD objects as values in a [ReadKeyValueRepo].
*
* @param K The type of CRUD IDs (used as keys)
* @param V The type of CRUD objects (used as values)
* @return [ReadKeyValueFromCRUDRepo] delegating to this repo
*/
fun <K, V> ReadCRUDRepo<K, V>.asReadKeyValueRepo() = ReadKeyValueFromCRUDRepo(this) fun <K, V> ReadCRUDRepo<K, V>.asReadKeyValueRepo() = ReadKeyValueFromCRUDRepo(this)

View File

@@ -18,6 +18,16 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
/**
* Full read-write adapter that exposes a [KeyValuesRepo] as a [KeyValueRepo] mapping each key to a [List] of values.
* Extends [ReadKeyValueFromKeyValuesRepo] with write operations delegated to the underlying [KeyValuesRepo].
* [onNewValue] merges [KeyValuesRepo.onNewValue] and [KeyValuesRepo.onValueRemoved] and emits the updated list per key;
* [onValueRemoved] mirrors [KeyValuesRepo.onDataCleared].
*
* @param Key The type of keys
* @param Value The type of individual values in the one-to-many repo
* @param original The underlying [KeyValuesRepo] to delegate operations to
*/
open class KeyValueFromKeyValuesRepo<Key, Value>( open class KeyValueFromKeyValuesRepo<Key, Value>(
private val original: KeyValuesRepo<Key, Value> private val original: KeyValuesRepo<Key, Value>
) : KeyValueRepo<Key, List<Value>>, ReadKeyValueFromKeyValuesRepo<Key, Value>(original) { ) : KeyValueRepo<Key, List<Value>>, ReadKeyValueFromKeyValuesRepo<Key, Value>(original) {

View File

@@ -15,6 +15,15 @@ import dev.inmo.micro_utils.repos.pagination.getAll
import dev.inmo.micro_utils.repos.transforms.kvs.ReadKeyValuesFromKeyValueRepo import dev.inmo.micro_utils.repos.transforms.kvs.ReadKeyValuesFromKeyValueRepo
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
/**
* Inline value class adapter that exposes a [ReadCRUDRepo] as a [ReadKeyValueRepo].
* Maps key-value read operations to the underlying CRUD repository operations,
* treating CRUD IDs as keys and CRUD objects as values.
*
* @param Key The type of keys (maps to [ReadCRUDRepo] ID type)
* @param Value The type of values (maps to [ReadCRUDRepo] object type)
* @param original The underlying [ReadCRUDRepo] to delegate operations to
*/
@JvmInline @JvmInline
value class ReadKeyValueFromCRUDRepo<Key, Value>( value class ReadKeyValueFromCRUDRepo<Key, Value>(
private val original: ReadCRUDRepo<Value, Key> private val original: ReadCRUDRepo<Value, Key>

View File

@@ -12,6 +12,14 @@ import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.transforms.kvs.ReadKeyValuesFromKeyValueRepo import dev.inmo.micro_utils.repos.transforms.kvs.ReadKeyValuesFromKeyValueRepo
/**
* Adapter that exposes a [ReadKeyValuesRepo] as a [ReadKeyValueRepo] mapping each key to a [List] of values.
* Each key's associated list of values is retrieved via [ReadKeyValuesRepo.getAll].
*
* @param Key The type of keys
* @param Value The type of individual values in the one-to-many repo
* @param original The underlying [ReadKeyValuesRepo] to delegate operations to
*/
open class ReadKeyValueFromKeyValuesRepo<Key, Value>( open class ReadKeyValueFromKeyValuesRepo<Key, Value>(
private val original: ReadKeyValuesRepo<Key, Value> private val original: ReadKeyValuesRepo<Key, Value>
) : ReadKeyValueRepo<Key, List<Value>> { ) : ReadKeyValueRepo<Key, List<Value>> {

View File

@@ -5,17 +5,51 @@ import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import kotlin.js.JsName import kotlin.js.JsName
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/**
* Wraps this [ReadKeyValueRepo] (mapping keys to iterables) as a [ReadKeyValuesFromKeyValueRepo],
* exposing a one-to-many read interface.
*
* @param K The type of keys
* @param V The type of individual values within each iterable
* @param VI The iterable type storing multiple values per key
* @return [ReadKeyValuesFromKeyValueRepo] delegating to this repo
*/
fun <K, V, VI : Iterable<V>> ReadKeyValueRepo<K, VI>.asReadKeyValuesRepo() = ReadKeyValuesFromKeyValueRepo(this) fun <K, V, VI : Iterable<V>> ReadKeyValueRepo<K, VI>.asReadKeyValuesRepo() = ReadKeyValuesFromKeyValueRepo(this)
/**
* Wraps this [KeyValueRepo] (mapping keys to iterables) as a [KeyValuesFromKeyValueRepo],
* exposing a full one-to-many read-write interface.
*
* @param K The type of keys
* @param V The type of individual values within each iterable
* @param VI The iterable type storing multiple values per key
* @param listToValuesIterable Converter from [List] of values to [VI] used when persisting changes
* @return [KeyValuesFromKeyValueRepo] delegating to this repo
*/
fun <K, V, VI : Iterable<V>> KeyValueRepo<K, VI>.asKeyValuesRepo( fun <K, V, VI : Iterable<V>> KeyValueRepo<K, VI>.asKeyValuesRepo(
listToValuesIterable: suspend (List<V>) -> VI listToValuesIterable: suspend (List<V>) -> VI
): KeyValuesFromKeyValueRepo<K, V, VI> = KeyValuesFromKeyValueRepo(this, listToValuesIterable) ): KeyValuesFromKeyValueRepo<K, V, VI> = KeyValuesFromKeyValueRepo(this, listToValuesIterable)
/**
* Wraps this [KeyValueRepo] (mapping keys to [List]s) as a [KeyValuesFromKeyValueRepo].
* Uses identity conversion for the list iterable.
*
* @param K The type of keys
* @param V The type of individual values
* @return [KeyValuesFromKeyValueRepo] delegating to this repo with [List] as the iterable type
*/
@JvmName("asListKeyValuesRepo") @JvmName("asListKeyValuesRepo")
@JsName("asListKeyValuesRepo") @JsName("asListKeyValuesRepo")
fun <K, V> KeyValueRepo<K, List<V>>.asKeyValuesRepo(): KeyValuesFromKeyValueRepo<K, V, List<V>> = asKeyValuesRepo { it } fun <K, V> KeyValueRepo<K, List<V>>.asKeyValuesRepo(): KeyValuesFromKeyValueRepo<K, V, List<V>> = asKeyValuesRepo { it }
/**
* Wraps this [KeyValueRepo] (mapping keys to [Set]s) as a [KeyValuesFromKeyValueRepo].
* Converts lists to sets when persisting changes, ensuring value uniqueness per key.
*
* @param K The type of keys
* @param V The type of individual values
* @return [KeyValuesFromKeyValueRepo] delegating to this repo with [Set] as the iterable type
*/
@JvmName("asSetKeyValuesRepo") @JvmName("asSetKeyValuesRepo")
@JsName("asSetKeyValuesRepo") @JsName("asSetKeyValuesRepo")
fun <K, V> KeyValueRepo<K, Set<V>>.asKeyValuesRepo(): KeyValuesFromKeyValueRepo<K, V, Set<V>> = asKeyValuesRepo { it.toSet() } fun <K, V> KeyValueRepo<K, Set<V>>.asKeyValuesRepo(): KeyValuesFromKeyValueRepo<K, V, Set<V>> = asKeyValuesRepo { it.toSet() }

View File

@@ -12,6 +12,17 @@ import kotlinx.coroutines.flow.asSharedFlow
import kotlin.js.JsName import kotlin.js.JsName
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
/**
* Full read-write adapter that exposes a [KeyValueRepo] storing iterables as a [KeyValuesRepo].
* Extends [ReadKeyValuesFromKeyValueRepo] with write operations: add, remove, clear.
* Emits [onNewValue] and [onValueRemoved] for individual value changes; [onDataCleared] mirrors [KeyValueRepo.onValueRemoved].
*
* @param Key The type of keys
* @param Value The type of individual values within each iterable
* @param ValuesIterable The iterable type storing multiple values per key
* @param original The underlying [KeyValueRepo] mapping keys to iterables of values
* @param listToValuesIterable Converter from [List] of values to [ValuesIterable] used when persisting changes
*/
open class KeyValuesFromKeyValueRepo<Key, Value, ValuesIterable : Iterable<Value>>( open class KeyValuesFromKeyValueRepo<Key, Value, ValuesIterable : Iterable<Value>>(
private val original: KeyValueRepo<Key, ValuesIterable>, private val original: KeyValueRepo<Key, ValuesIterable>,
private val listToValuesIterable: suspend (List<Value>) -> ValuesIterable private val listToValuesIterable: suspend (List<Value>) -> ValuesIterable

View File

@@ -11,6 +11,15 @@ import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
/**
* Adapter that exposes a [ReadKeyValueRepo] storing iterables as a [ReadKeyValuesRepo].
* Each key maps to a [ValuesIterable] in the underlying repo, which is exposed as a one-to-many relationship.
*
* @param Key The type of keys
* @param Value The type of individual values within each iterable
* @param ValuesIterable The iterable type storing multiple values per key
* @param original The underlying [ReadKeyValueRepo] mapping keys to iterables of values
*/
open class ReadKeyValuesFromKeyValueRepo<Key, Value, ValuesIterable : Iterable<Value>>( open class ReadKeyValuesFromKeyValueRepo<Key, Value, ValuesIterable : Iterable<Value>>(
private val original: ReadKeyValueRepo<Key, ValuesIterable> private val original: ReadKeyValueRepo<Key, ValuesIterable>
) : ReadKeyValuesRepo<Key, Value> { ) : ReadKeyValuesRepo<Key, Value> {

View File

@@ -3,6 +3,14 @@ package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.KeyValueRepo import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.set import dev.inmo.micro_utils.repos.set
/**
* [StandardVersionsRepoProxy] implementation backed by a [KeyValueRepo] mapping table names to version numbers.
* Stores and retrieves per-table version integers using [keyValueStore] with table names as keys.
*
* @param T The type of the underlying database or storage object
* @param keyValueStore [KeyValueRepo] used to persist table-name-to-version mappings
* @param database The underlying database or storage object exposed via [StandardVersionsRepoProxy.database]
*/
class KeyValueBasedVersionsRepoProxy<T>( class KeyValueBasedVersionsRepoProxy<T>(
private val keyValueStore: KeyValueRepo<String, Int>, private val keyValueStore: KeyValueRepo<String, Int>,
override val database: T override val database: T

View File

@@ -2,13 +2,43 @@ package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.Repo import dev.inmo.micro_utils.repos.Repo
/**
* Proxy interface providing low-level access to a versioned database [T].
* Implementations store and retrieve per-table version numbers using a backing storage.
*
* @param T The type of the underlying database or storage object
*/
interface StandardVersionsRepoProxy<T> : Repo { interface StandardVersionsRepoProxy<T> : Repo {
/**
* The underlying database or storage object used for version tracking.
*/
val database: T val database: T
/**
* Returns the current version number for the given [tableName], or null if no version is stored.
*
* @param tableName Name of the table whose version to retrieve
* @return Stored version number, or null if the table has not been versioned yet
*/
suspend fun getTableVersion(tableName: String): Int? suspend fun getTableVersion(tableName: String): Int?
/**
* Persists the given [version] number for the given [tableName].
*
* @param tableName Name of the table whose version to update
* @param version New version number to store
*/
suspend fun updateTableVersion(tableName: String, version: Int) suspend fun updateTableVersion(tableName: String, version: Int)
} }
/**
* Standard implementation of [VersionsRepo] that delegates version storage to a [StandardVersionsRepoProxy].
* On [setTableVersion]: calls [StandardVersionsRepoProxy.database].[onCreate] if the table has no version yet,
* then iterates [onUpdate] for each version step until the target [version] is reached.
*
* @param T The type of the underlying database or storage object
* @param proxy The [StandardVersionsRepoProxy] used to read and write version numbers
*/
class StandardVersionsRepo<T>( class StandardVersionsRepo<T>(
private val proxy: StandardVersionsRepoProxy<T> private val proxy: StandardVersionsRepoProxy<T>
) : VersionsRepo<T> { ) : VersionsRepo<T> {