Compare commits
5 Commits
62dc6deb79
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8bddcc1b01 | |||
| 39d1d9a2b7 | |||
| d691c1d944 | |||
| 5dbf0aacab | |||
| be761a8c46 |
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
201
.idea/workspace.xml
generated
201
.idea/workspace.xml
generated
@@ -1,9 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="AnalysisUIOptions">
|
|
||||||
<option name="ANALYZE_INJECTED_CODE" value="false" />
|
|
||||||
<option name="SCOPE_TYPE" value="3" />
|
|
||||||
</component>
|
|
||||||
<component name="AutoImportSettings">
|
<component name="AutoImportSettings">
|
||||||
<option name="autoReloadType" value="ALL" />
|
<option name="autoReloadType" value="ALL" />
|
||||||
</component>
|
</component>
|
||||||
@@ -15,42 +11,57 @@
|
|||||||
</cargoProject>
|
</cargoProject>
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="ca698286-778f-4335-97c8-da35a666c986" name="Changes" comment="init">
|
<list default="true" id="b2b598c9-ef0b-4cbc-8852-cfbc8ce3920e" name="Changes" comment="">
|
||||||
<change afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/app/conf.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources.xml" beforeDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/domain/models.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources/26059583-0fdb-4f6f-ad11-10388e9658c2.xml" beforeDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/network/http.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources/26059583-0fdb-4f6f-ad11-10388e9658c2/storage_v2/_src_/schema/main.uQUzAA.meta" beforeDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/network/http_routes/channel.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/modules.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/network/http_routes/master.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/ox_speak_server.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/ox_speak_server.iml" afterDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/network/http_routes/message.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/src/network/http_routes/mod.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/network/http_routes/user.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/network/http_routes/websocket.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/mod.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/models/channel.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/models/link_sub_server_user.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/models/message.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/models/mod.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/models/sub_server.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/models/user.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/repositories/channel_repository.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/repositories/link_sub_server_user_repository.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/repositories/message_repository.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/repositories/mod.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/repositories/sub_server_repository.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/repositories/user_repository.rs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/src/store/store_service.rs" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/app/app.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/app.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/app/app.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/app.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/app/conf.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/conf.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/app/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/mod.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/app/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/app/mod.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/domain/client.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/client.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/domain/event.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/event.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/domain/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/mod.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/domain/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/mod.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/domain/models.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/models.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/domain/user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/domain/user.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/network/http.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/http_routes/channel.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/channel.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/http_routes/master.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/master.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/http_routes/message.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/message.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/http_routes/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/mod.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/http_routes/sub_server.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/sub_server.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/http_routes/user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/user.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/http_routes/websocket.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/http_routes/websocket.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/network/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/mod.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/network/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/mod.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/protocol.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/protocol.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/udp.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/udp.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/network/udp_back.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/network/udp_back.rs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/runtime/dispatcher.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/runtime/dispatcher.rs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/runtime/dispatcher.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/runtime/dispatcher.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/migrations/001_init.sqlite.sql" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/migrations/001_init.sqlite.sql" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/mod.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/models/channel.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/channel.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/models/link_sub_server_user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/link_sub_server_user.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/models/message.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/message.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/models/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/mod.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/models/sub_server.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/sub_server.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/models/user.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/models/user.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/repositories/channel_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/channel_repository.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/repositories/link_sub_server_user_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/link_sub_server_user_repository.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/repositories/message_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/message_repository.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/repositories/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/mod.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/repositories/sub_server_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/sub_server_repository.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/repositories/user_repository.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/repositories/user_repository.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/session/client.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/session/client.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/store/store_service.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/store/store_service.rs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/utils/shared_store.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/shared_store.rs" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -58,38 +69,32 @@
|
|||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ExecutionTargetManager" SELECTED_TARGET="RsBuildProfile:dev" />
|
<component name="ExecutionTargetManager" SELECTED_TARGET="RsBuildProfile:dev" />
|
||||||
<component name="FileTemplateManagerImpl">
|
|
||||||
<option name="RECENT_TEMPLATES">
|
|
||||||
<list>
|
|
||||||
<option value="Rust File" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="MacroExpansionManager">
|
<component name="MacroExpansionManager">
|
||||||
<option name="directoryName" value="oBePpDlK" />
|
<option name="directoryName" value="j91G0RKo" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectColorInfo">{
|
<component name="ProjectColorInfo">{
|
||||||
"associatedIndex": 5
|
"associatedIndex": 7
|
||||||
}</component>
|
}</component>
|
||||||
<component name="ProjectId" id="2zaZ93S6zEp6mJe7Xq5WfvIMexv" />
|
<component name="ProjectId" id="31h5TOFgo6LtoeKpSInG1hXoNI7" />
|
||||||
<component name="ProjectViewState">
|
<component name="ProjectViewState">
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
<option name="showLibraryContents" value="true" />
|
<option name="showLibraryContents" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PropertiesComponent">{
|
<component name="PropertiesComponent">{
|
||||||
"keyToString": {
|
"keyToString": {
|
||||||
"Cargo.Run.executor": "Run",
|
"Cargo.Run ox_speak_server.executor": "Run",
|
||||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||||
"RunOnceActivity.git.unshallow": "true",
|
"RunOnceActivity.git.unshallow": "true",
|
||||||
"RunOnceActivity.rust.reset.selective.auto.import": "true",
|
"RunOnceActivity.rust.reset.selective.auto.import": "true",
|
||||||
"git-widget-placeholder": "master",
|
"git-widget-placeholder": "master",
|
||||||
"ignore.virus.scanning.warn.message": "true",
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
"junie.onboarding.icon.badge.shown": "true",
|
"junie.onboarding.icon.badge.shown": "true",
|
||||||
"last_opened_file_path": "D:/Dev/ox_speak_server/src/network",
|
"last_opened_file_path": "//wsl.localhost/Debian/home/Nell/linux_dev/ox_speak_server",
|
||||||
"node.js.detected.package.eslint": "true",
|
"node.js.detected.package.eslint": "true",
|
||||||
"node.js.detected.package.tslint": "true",
|
"node.js.detected.package.tslint": "true",
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
@@ -98,22 +103,14 @@
|
|||||||
"org.rust.cargo.project.model.PROJECT_DISCOVERY": "true",
|
"org.rust.cargo.project.model.PROJECT_DISCOVERY": "true",
|
||||||
"org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "",
|
"org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "",
|
||||||
"org.rust.first.attach.projects": "true",
|
"org.rust.first.attach.projects": "true",
|
||||||
"run.code.analysis.last.selected.profile": "pProject Default",
|
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||||
"settings.editor.selected.configurable": "terminal",
|
"to.speed.mode.migration.done": "true",
|
||||||
"vue.rearranger.settings.migration": "true"
|
"vue.rearranger.settings.migration": "true"
|
||||||
}
|
}
|
||||||
}</component>
|
}</component>
|
||||||
<component name="RecentsManager">
|
<component name="RunManager" selected="Cargo.Run ox_speak_server">
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
<configuration name="Run ox_speak_server" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
<recent name="D:\Dev\ox_speak_server\src\network" />
|
<option name="buildProfileId" value="dev" />
|
||||||
</key>
|
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
|
||||||
<recent name="D:\Dev\ox_speak_server\src\store" />
|
|
||||||
<recent name="D:\Dev\ox_speak_server\src\store\repositories" />
|
|
||||||
</key>
|
|
||||||
</component>
|
|
||||||
<component name="RunManager">
|
|
||||||
<configuration name="Run" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
|
||||||
<option name="command" value="run --package ox_speak_server --bin ox_speak_server" />
|
<option name="command" value="run --package ox_speak_server --bin ox_speak_server" />
|
||||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
<envs />
|
<envs />
|
||||||
@@ -130,84 +127,44 @@
|
|||||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
<configuration name="Test ox_speak_server" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="command" value="test --workspace" />
|
||||||
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
|
<envs />
|
||||||
|
<option name="emulateTerminal" value="true" />
|
||||||
|
<option name="channel" value="DEFAULT" />
|
||||||
|
<option name="requiredFeatures" value="true" />
|
||||||
|
<option name="allFeatures" value="false" />
|
||||||
|
<option name="withSudo" value="false" />
|
||||||
|
<option name="buildTarget" value="REMOTE" />
|
||||||
|
<option name="backtrace" value="SHORT" />
|
||||||
|
<option name="isRedirectInput" value="false" />
|
||||||
|
<option name="redirectInputPath" value="" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
<component name="RustProjectSettings">
|
<component name="RustProjectSettings">
|
||||||
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
|
<option name="toolchainHomeDirectory" value="$PROJECT_DIR$/../../.cargo/bin" />
|
||||||
</component>
|
</component>
|
||||||
<component name="TaskManager">
|
<component name="TaskManager">
|
||||||
<task active="true" id="Default" summary="Default task">
|
<task active="true" id="Default" summary="Default task">
|
||||||
<changelist id="ca698286-778f-4335-97c8-da35a666c986" name="Changes" comment="" />
|
<changelist id="b2b598c9-ef0b-4cbc-8852-cfbc8ce3920e" name="Changes" comment="" />
|
||||||
<created>1751970990022</created>
|
<created>1755963473950</created>
|
||||||
<option name="number" value="Default" />
|
<option name="number" value="Default" />
|
||||||
<option name="presentableId" value="Default" />
|
<option name="presentableId" value="Default" />
|
||||||
<updated>1751970990022</updated>
|
<updated>1755963473950</updated>
|
||||||
<workItem from="1751970991653" duration="7327000" />
|
<workItem from="1755963475439" duration="2760000" />
|
||||||
<workItem from="1752016714585" duration="1095000" />
|
<workItem from="1756139974994" duration="110000" />
|
||||||
<workItem from="1752076174877" duration="11015000" />
|
|
||||||
<workItem from="1752189013977" duration="4750000" />
|
|
||||||
<workItem from="1752224469499" duration="14591000" />
|
|
||||||
<workItem from="1752305152715" duration="19879000" />
|
|
||||||
<workItem from="1752391851097" duration="12061000" />
|
|
||||||
<workItem from="1752446764168" duration="8166000" />
|
|
||||||
<workItem from="1752484190578" duration="17709000" />
|
|
||||||
<workItem from="1752591656862" duration="429000" />
|
|
||||||
<workItem from="1752593250094" duration="11969000" />
|
|
||||||
<workItem from="1752694022400" duration="6212000" />
|
|
||||||
<workItem from="1752741840195" duration="2946000" />
|
|
||||||
<workItem from="1752833798325" duration="5366000" />
|
|
||||||
<workItem from="1752917416027" duration="1192000" />
|
|
||||||
<workItem from="1752931843330" duration="2938000" />
|
|
||||||
<workItem from="1752997629708" duration="9070000" />
|
|
||||||
<workItem from="1753107912894" duration="595000" />
|
|
||||||
<workItem from="1753225761416" duration="1356000" />
|
|
||||||
<workItem from="1753282037526" duration="5207000" />
|
|
||||||
<workItem from="1753397680991" duration="1782000" />
|
|
||||||
<workItem from="1753399490773" duration="3189000" />
|
|
||||||
<workItem from="1753436756029" duration="16895000" />
|
|
||||||
<workItem from="1753521176318" duration="17811000" />
|
|
||||||
<workItem from="1753601912332" duration="5843000" />
|
|
||||||
<workItem from="1753718175508" duration="8774000" />
|
|
||||||
<workItem from="1753800817354" duration="3570000" />
|
|
||||||
<workItem from="1753804571241" duration="59000" />
|
|
||||||
<workItem from="1753804642657" duration="236000" />
|
|
||||||
<workItem from="1753804898179" duration="625000" />
|
|
||||||
<workItem from="1753805533139" duration="2909000" />
|
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="init">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1752591904243</created>
|
|
||||||
<option name="number" value="00001" />
|
|
||||||
<option name="presentableId" value="LOCAL-00001" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1752591904243</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00002" summary="init">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1752591989113</created>
|
|
||||||
<option name="number" value="00002" />
|
|
||||||
<option name="presentableId" value="LOCAL-00002" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1752591989113</updated>
|
|
||||||
</task>
|
|
||||||
<option name="localTasksCounter" value="3" />
|
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
<option name="version" value="3" />
|
<option name="version" value="3" />
|
||||||
</component>
|
</component>
|
||||||
<component name="Vcs.Log.Tabs.Properties">
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
<option name="TAB_STATES">
|
<expand />
|
||||||
<map>
|
<select />
|
||||||
<entry key="MAIN">
|
|
||||||
<value>
|
|
||||||
<State />
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="VcsManagerConfiguration">
|
|
||||||
<MESSAGE value="init" />
|
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="init" />
|
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -1432,12 +1432,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.10"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1776,9 +1776,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.46.1"
|
version = "1.47.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
|
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1791,7 +1791,7 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1969,9 +1969,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ debug = true
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
tokio = { version = "1.46", features = ["full"] }
|
tokio = { version = "1.47", features = ["full"] }
|
||||||
strum = {version = "0.27", features = ["derive"] }
|
strum = {version = "0.27", features = ["derive"] }
|
||||||
uuid = {version = "1.17", features = ["v4", "serde"] }
|
uuid = {version = "1.18", features = ["v4", "serde"] }
|
||||||
event-listener = "5.4"
|
event-listener = "5.4"
|
||||||
dashmap = "6.1"
|
dashmap = "6.1"
|
||||||
bytes = "1.10"
|
bytes = "1.10"
|
||||||
|
|||||||
@@ -2,3 +2,5 @@ créer un fichier de migration sqlx :
|
|||||||
```shell
|
```shell
|
||||||
sqlx migrate add --source src/store/migrations migration_name
|
sqlx migrate add --source src/store/migrations migration_name
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Example
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use axum::{
|
use axum::{extract::{ws::WebSocket, ws::WebSocketUpgrade, State}, response::Response, Json, Router, routing::{get, post}, middleware};
|
||||||
extract::{ws::WebSocket, ws::WebSocketUpgrade, State},
|
use axum::body::Body;
|
||||||
response::Response,
|
use axum::http::{HeaderValue, Request, StatusCode};
|
||||||
Json, Router,
|
use axum::middleware::Next;
|
||||||
routing::{get, post}
|
|
||||||
};
|
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use crate::domain::event::EventBus;
|
use crate::domain::event::EventBus;
|
||||||
use crate::network::http_routes::master;
|
use crate::network::http_routes::master;
|
||||||
@@ -46,7 +44,8 @@ impl HttpServer {
|
|||||||
|
|
||||||
fn create_router(&self) -> Router {
|
fn create_router(&self) -> Router {
|
||||||
let api_route = Router::new()
|
let api_route = Router::new()
|
||||||
.nest("/master", master::create_router());
|
.nest("/master", master::create_router())
|
||||||
|
.layer(middleware::from_fn(app_only_middleware));
|
||||||
|
|
||||||
Router::new()
|
Router::new()
|
||||||
.nest("/api", api_route)
|
.nest("/api", api_route)
|
||||||
@@ -68,3 +67,23 @@ impl HttpServer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Middlewares
|
||||||
|
async fn app_only_middleware(request: Request<Body>, next: Next) -> Result<Response, StatusCode> {
|
||||||
|
let headers = request.headers();
|
||||||
|
let expected_client = HeaderValue::from_static("ox_speak");
|
||||||
|
let expected_version = HeaderValue::from_static("1.0");
|
||||||
|
let expected_user_agent = HeaderValue::from_static("OxSpeak/1.0");
|
||||||
|
|
||||||
|
if headers.get("X-Client-Type") != Some(&expected_client) ||
|
||||||
|
headers.get("X-Protocol-Version") != Some(&expected_version) ||
|
||||||
|
headers.get("User-Agent") != Some(&expected_user_agent) {
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.status(StatusCode::BAD_REQUEST)
|
||||||
|
.body("Invalid client".into())
|
||||||
|
.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(next.run(request).await)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use axum::Router;
|
||||||
|
use axum::routing::post;
|
||||||
|
use crate::network::http::HttpState;
|
||||||
|
|
||||||
|
pub fn create_router() -> Router<HttpState> {
|
||||||
|
Router::new()
|
||||||
|
// .route("/auth/", post(join_master_server))
|
||||||
|
}
|
||||||
@@ -10,9 +10,37 @@ use crate::network::http::HttpState;
|
|||||||
|
|
||||||
pub fn create_router() -> Router<HttpState> {
|
pub fn create_router() -> Router<HttpState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/auth/", post(join_master_server))
|
.route("/auth/", post(join))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn join_master_server(State(state): State<HttpState>) -> Json<HashMap<String, String>> {
|
pub async fn challenge(State(state): State<HttpState>) -> Json<HashMap<String, String>> {
|
||||||
todo!("join master server")
|
// permet de sécuriser les échanges entre le client et le serveur pour qu'elle soit propre à l'application
|
||||||
|
// l'idée est d'éviter que les système comme les navigateurs, curl ... puisse accéder à l'application
|
||||||
|
todo!("challenge master server")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn join(State(state): State<HttpState>) -> Json<HashMap<String, String>> {
|
||||||
|
// Cette page est systématiquement appelé à la première connexion du client
|
||||||
|
// dans le payload de la requête, on aura les info client
|
||||||
|
// public key
|
||||||
|
// dans la réponse, on aura le status
|
||||||
|
// success, error, password_needed
|
||||||
|
|
||||||
|
// match determine_server_state().await {
|
||||||
|
// ServerState::FirstSetup => {
|
||||||
|
// // Logique de création du super admin
|
||||||
|
// // Validation des champs requis (password, username)
|
||||||
|
// }
|
||||||
|
// ServerState::RequiresPassword => {
|
||||||
|
// // Logique d'authentification
|
||||||
|
// // Validation du mot de passe serveur
|
||||||
|
// }
|
||||||
|
// ServerState::Ready => {
|
||||||
|
// // Logique de connexion normale
|
||||||
|
// // Juste la public key suffit
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
todo!("join master server")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use axum::Router;
|
||||||
|
use axum::routing::post;
|
||||||
|
use crate::network::http::HttpState;
|
||||||
|
|
||||||
|
pub fn create_router() -> Router<HttpState> {
|
||||||
|
Router::new()
|
||||||
|
// .route("/auth/", post(join_master_server))
|
||||||
|
}
|
||||||
@@ -3,3 +3,4 @@ pub mod channel;
|
|||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod websocket;
|
pub mod websocket;
|
||||||
pub mod master;
|
pub mod master;
|
||||||
|
mod sub_server;
|
||||||
9
src/network/http_routes/sub_server.rs
Normal file
9
src/network/http_routes/sub_server.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use axum::Router;
|
||||||
|
use axum::routing::post;
|
||||||
|
use crate::network::http::HttpState;
|
||||||
|
|
||||||
|
pub fn create_router() -> Router<HttpState> {
|
||||||
|
Router::new()
|
||||||
|
// .route("/auth/", post(join_master_server))
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use axum::Router;
|
||||||
|
use axum::routing::post;
|
||||||
|
use crate::network::http::HttpState;
|
||||||
|
|
||||||
|
pub fn create_router() -> Router<HttpState> {
|
||||||
|
Router::new()
|
||||||
|
// .route("/auth/", post(join_master_server))
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use axum::Router;
|
||||||
|
use axum::routing::post;
|
||||||
|
use crate::network::http::HttpState;
|
||||||
|
|
||||||
|
pub fn create_router() -> Router<HttpState> {
|
||||||
|
Router::new()
|
||||||
|
// .route("/auth/", post(join_master_server))
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod repositories;
|
pub mod repositories;
|
||||||
pub mod store_service;
|
pub mod store_service;
|
||||||
|
mod session;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::hash::{Hash, Hasher};
|
||||||
// use chrono::{DateTime, Utc};
|
// use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
// use uuid::Uuid;
|
// use uuid::Uuid;
|
||||||
@@ -30,3 +31,17 @@ impl Channel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Channel {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Channel {}
|
||||||
|
|
||||||
|
impl Hash for Channel {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
// L'application peut avoir plusieurs sous servers
|
// L'application peut avoir plusieurs sous servers
|
||||||
|
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use crate::store::models::Channel;
|
||||||
|
|
||||||
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize)]
|
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize)]
|
||||||
pub struct SubServer {
|
pub struct SubServer {
|
||||||
@@ -22,3 +24,17 @@ impl SubServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for SubServer {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for SubServer {}
|
||||||
|
|
||||||
|
impl Hash for SubServer {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
|||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::store::models::channel::Channel;
|
use crate::store::models::channel::Channel;
|
||||||
|
use crate::store::models::Message;
|
||||||
use crate::store::store_service::StoreService;
|
use crate::store::store_service::StoreService;
|
||||||
use crate::utils::shared_store::SharedArcMap;
|
use crate::utils::shared_store::SharedArcMap;
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ impl ChannelRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(&self, id: Uuid) -> Result<bool, sqlx::Error> {
|
pub async fn delete(&self, id: Uuid) -> Result<bool, sqlx::Error> {
|
||||||
let rows_affected = sqlx::query("DELETE FROM sub_server WHERE id = ?")
|
let rows_affected = sqlx::query("DELETE FROM channel WHERE id = ?")
|
||||||
.bind(&id)
|
.bind(&id)
|
||||||
.execute(&self.store.db)
|
.execute(&self.store.db)
|
||||||
.await?
|
.await?
|
||||||
@@ -84,17 +85,13 @@ impl ChannelRepository {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Pour initialiser le cache depuis la DB
|
impl ChannelRepository {
|
||||||
pub async fn load_all_from_db(&self) -> Result<(), sqlx::Error> {
|
// getters (db)
|
||||||
let servers: Vec<Channel> = sqlx::query_as("SELECT * FROM sub_server")
|
pub async fn db_all(&self) -> Result<Vec<Channel>, sqlx::Error> {
|
||||||
|
sqlx::query_as("SELECT * FROM channel")
|
||||||
.fetch_all(&self.store.db)
|
.fetch_all(&self.store.db)
|
||||||
.await?;
|
.await
|
||||||
|
|
||||||
for server in servers {
|
|
||||||
self.store.channels.insert(server.id, server);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,5 +63,13 @@ impl LinkSubServerUserRepository {
|
|||||||
|
|
||||||
Ok(through)
|
Ok(through)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinkSubServerUserRepository {
|
||||||
|
// getters (db)
|
||||||
|
pub async fn db_all(&self) -> Result<Vec<LinkSubServerUser>, sqlx::Error> {
|
||||||
|
sqlx::query_as("SELECT * FROM sub_server_users")
|
||||||
|
.fetch_all(&self.store.db)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ impl MessageRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MessageRepository {
|
impl MessageRepository {
|
||||||
// getters
|
// getters (caches)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageRepository {
|
impl MessageRepository {
|
||||||
@@ -50,7 +49,6 @@ impl MessageRepository {
|
|||||||
.bind(&message.id)
|
.bind(&message.id)
|
||||||
.execute(&self.store.db)
|
.execute(&self.store.db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,3 +66,12 @@ impl MessageRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MessageRepository {
|
||||||
|
// getters (db)
|
||||||
|
pub async fn db_all(&self) -> Result<Vec<Message>, sqlx::Error> {
|
||||||
|
sqlx::query_as("SELECT * FROM message")
|
||||||
|
.fetch_all(&self.store.db)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use crate::store::models::Channel;
|
||||||
use crate::store::models::sub_server::SubServer;
|
use crate::store::models::sub_server::SubServer;
|
||||||
use crate::store::store_service::StoreService;
|
use crate::store::store_service::StoreService;
|
||||||
use crate::utils::shared_store::SharedArcMap;
|
use crate::utils::shared_store::SharedArcMap;
|
||||||
@@ -79,17 +80,13 @@ impl SubServerRepository {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Pour initialiser le cache depuis la DB
|
impl SubServerRepository {
|
||||||
pub async fn load_all_from_db(&self) -> Result<(), sqlx::Error> {
|
// getters (db)
|
||||||
let servers: Vec<SubServer> = sqlx::query_as("SELECT * FROM sub_server")
|
pub async fn db_all(&self) -> Result<Vec<SubServer>, sqlx::Error> {
|
||||||
|
sqlx::query_as("SELECT * FROM sub_server")
|
||||||
.fetch_all(&self.store.db)
|
.fetch_all(&self.store.db)
|
||||||
.await?;
|
.await
|
||||||
|
|
||||||
for server in servers {
|
|
||||||
self.store.sub_servers.insert(server.id, server);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,19 +2,18 @@ use std::sync::Arc;
|
|||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::store::models::user::User;
|
use crate::store::models::user::User;
|
||||||
|
use crate::store::store_service::StoreService;
|
||||||
use crate::utils::shared_store::SharedArcMap;
|
use crate::utils::shared_store::SharedArcMap;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UserRepository {
|
pub struct UserRepository {
|
||||||
db: SqlitePool,
|
store: StoreService,
|
||||||
cache: SharedArcMap<Uuid, User>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserRepository {
|
impl UserRepository {
|
||||||
pub fn new(db: SqlitePool, cache: SharedArcMap<Uuid, User>) -> Self {
|
pub fn new(store: StoreService) -> Self {
|
||||||
Self {
|
Self {
|
||||||
db,
|
store
|
||||||
cache
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,11 +21,11 @@ impl UserRepository {
|
|||||||
impl UserRepository {
|
impl UserRepository {
|
||||||
// getters
|
// getters
|
||||||
pub async fn all(&self) -> Vec<Arc<User>> {
|
pub async fn all(&self) -> Vec<Arc<User>> {
|
||||||
self.cache.values().collect()
|
self.store.users.values().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get(&self, id: Uuid) -> Option<Arc<User>> {
|
pub async fn get(&self, id: Uuid) -> Option<Arc<User>> {
|
||||||
self.cache.get(&id)
|
self.store.users.get(&id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,12 +39,12 @@ impl UserRepository {
|
|||||||
.bind(&user.username)
|
.bind(&user.username)
|
||||||
.bind(&user.pub_key)
|
.bind(&user.pub_key)
|
||||||
.bind(&user.created_at)
|
.bind(&user.created_at)
|
||||||
.execute(&self.db)
|
.execute(&self.store.db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// ajouter au cache
|
// ajouter au cache
|
||||||
let arc_server = Arc::new(user.clone());
|
let arc_server = Arc::new(user.clone());
|
||||||
self.cache.insert_arc(user.id, arc_server.clone());
|
self.store.users.insert_arc(user.id, arc_server.clone());
|
||||||
|
|
||||||
Ok(arc_server)
|
Ok(arc_server)
|
||||||
}
|
}
|
||||||
@@ -57,11 +56,11 @@ impl UserRepository {
|
|||||||
.bind(&user.username)
|
.bind(&user.username)
|
||||||
.bind(&user.pub_key)
|
.bind(&user.pub_key)
|
||||||
.bind(&user.id)
|
.bind(&user.id)
|
||||||
.execute(&self.db)
|
.execute(&self.store.db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Mettre à jour le cache
|
// Mettre à jour le cache
|
||||||
self.cache.insert(user.id, user.clone());
|
self.store.users.insert(user.id, user.clone());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -69,28 +68,24 @@ impl UserRepository {
|
|||||||
pub async fn delete(&self, id: Uuid) -> Result<bool, sqlx::Error> {
|
pub async fn delete(&self, id: Uuid) -> Result<bool, sqlx::Error> {
|
||||||
let rows_affected = sqlx::query("DELETE FROM user WHERE id = ?")
|
let rows_affected = sqlx::query("DELETE FROM user WHERE id = ?")
|
||||||
.bind(&id)
|
.bind(&id)
|
||||||
.execute(&self.db)
|
.execute(&self.store.db)
|
||||||
.await?
|
.await?
|
||||||
.rows_affected();
|
.rows_affected();
|
||||||
|
|
||||||
if rows_affected > 0 {
|
if rows_affected > 0 {
|
||||||
self.cache.remove(&id);
|
self.store.users.remove(&id);
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pour initialiser le cache depuis la DB
|
|
||||||
pub async fn load_all_from_db(&self) -> Result<(), sqlx::Error> {
|
|
||||||
let servers: Vec<User> = sqlx::query_as("SELECT * FROM user")
|
|
||||||
.fetch_all(&self.db)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
for server in servers {
|
|
||||||
self.cache.insert(server.id, server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
impl UserRepository {
|
||||||
|
// getters (db)
|
||||||
|
pub async fn db_all(&self) -> Result<Vec<User>, sqlx::Error> {
|
||||||
|
sqlx::query_as("SELECT * FROM user")
|
||||||
|
.fetch_all(&self.store.db)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
213
src/store/session/client.rs
Normal file
213
src/store/session/client.rs
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
use std::time::Instant;
|
||||||
|
use axum::extract::ws::WebSocket;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use crate::store::models::{Channel, SubServer};
|
||||||
|
use crate::store::models::user::User;
|
||||||
|
use crate::utils::shared_store::{SharedArcHashSet, SharedArcMap, SharedArcVec};
|
||||||
|
|
||||||
|
/// Représente un client connecté au serveur (WebSocket + optionnel UDP)
|
||||||
|
///
|
||||||
|
/// Chaque client a une connexion WebSocket obligatoire et peut avoir une connexion UDP
|
||||||
|
/// pour la voix s'il rejoint un channel vocal.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client {
|
||||||
|
// ===== CHAMPS IMMUTABLES =====
|
||||||
|
pub session_id: Uuid,
|
||||||
|
pub user: User,
|
||||||
|
pub websocket: WebSocket,
|
||||||
|
pub created_at: Instant,
|
||||||
|
|
||||||
|
// ===== CHAMPS MUTABLES =====
|
||||||
|
pub udp_socket: Option<SocketAddr>,
|
||||||
|
pub voice_channel: Option<Channel>,
|
||||||
|
|
||||||
|
// ===== RÉFÉRENCES INTERNES =====
|
||||||
|
/// Référence faible vers le manager pour l'auto-nettoyage
|
||||||
|
manager: Option<Weak<ClientManager>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
/// Crée un nouveau client
|
||||||
|
pub fn new(user: User, websocket: WebSocket) -> Self {
|
||||||
|
Self {
|
||||||
|
session_id: Uuid::new_v4(),
|
||||||
|
user,
|
||||||
|
websocket,
|
||||||
|
created_at: Instant::now(),
|
||||||
|
udp_socket: None,
|
||||||
|
voice_channel: None,
|
||||||
|
manager: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== MÉTHODES MUTABLES INTERNES =====
|
||||||
|
// Ces méthodes sont pub(crate) - elles ne doivent être appelées que via ClientManager::modify_client()
|
||||||
|
|
||||||
|
/// Configure l'adresse UDP (méthode interne)
|
||||||
|
pub(crate) fn set_udp_socket(&mut self, udp_socket: SocketAddr) {
|
||||||
|
self.udp_socket = Some(udp_socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime l'adresse UDP (méthode interne)
|
||||||
|
pub(crate) fn remove_udp_socket(&mut self) {
|
||||||
|
self.udp_socket = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure la référence vers le manager (appelé par le manager)
|
||||||
|
pub(crate) fn set_manager(&mut self, manager: Weak<ClientManager>) {
|
||||||
|
self.manager = Some(manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Client {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.session_id == other.session_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Client {}
|
||||||
|
|
||||||
|
impl Hash for Client {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.session_id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Nettoyage automatique quand le client est détruit
|
||||||
|
impl Drop for Client {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(manager) = self.manager.as_ref().and_then(|w| w.upgrade()) {
|
||||||
|
manager.cleanup_client_index(&self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// CLIENT MANAGER
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/// Gestionnaire centralisé de tous les clients connectés
|
||||||
|
///
|
||||||
|
/// Maintient les clients et leurs index pour des recherches efficaces.
|
||||||
|
/// Thread-safe via SharedArcMap.
|
||||||
|
pub struct ClientManager {
|
||||||
|
/// Map principale des clients par session_id
|
||||||
|
clients: SharedArcMap<Uuid, Client>,
|
||||||
|
|
||||||
|
// ===== INDEX POUR RECHERCHES RAPIDES =====
|
||||||
|
/// Index des clients par channel_id
|
||||||
|
voice_channel_clients: SharedArcMap<Channel, SharedArcHashSet<Client>>, // channel_id -> HashSet<session_id>
|
||||||
|
/// Index des clients par sub_server_id
|
||||||
|
sub_server_clients: SharedArcMap<SubServer, SharedArcHashSet<Client>>, // sub_server_id -> Vec<session_id>
|
||||||
|
/// Index des clients par adresse UDP
|
||||||
|
udp_clients: SharedArcMap<SocketAddr, String>, // udp_address -> session_id
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientManager {
|
||||||
|
/// Crée un nouveau gestionnaire de clients
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
clients: SharedArcMap::new(),
|
||||||
|
voice_channel_clients: SharedArcMap::new(),
|
||||||
|
sub_server_clients: SharedArcMap::new(),
|
||||||
|
udp_clients: SharedArcMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== GESTION DES CLIENTS =====
|
||||||
|
|
||||||
|
/// Ajoute un nouveau client au gestionnaire
|
||||||
|
pub fn add_client(self: &Arc<Self>, mut client: Client) -> Arc<Client> {
|
||||||
|
let session_id = client.session_id.clone();
|
||||||
|
|
||||||
|
// Lier le client au manager pour l'auto-nettoyage
|
||||||
|
client.set_manager(Arc::downgrade(self));
|
||||||
|
|
||||||
|
// Stocker et retourner
|
||||||
|
let arc_client = Arc::new(client);
|
||||||
|
self.clients.insert_arc(session_id, arc_client.clone());
|
||||||
|
|
||||||
|
arc_client
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join_sub_server(&self, client: &Arc<Client>, sub_server: &SubServer) {
|
||||||
|
let clients = self.sub_server_clients.get(sub_server).unwrap_or_else(|| {
|
||||||
|
let new_clients = Arc::new(SharedArcHashSet::new());
|
||||||
|
self.sub_server_clients.insert_arc(sub_server.clone(), new_clients.clone());
|
||||||
|
new_clients
|
||||||
|
});
|
||||||
|
|
||||||
|
clients.insert_arc(client.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtient un client par son session_id
|
||||||
|
pub fn get_client(&self, session_id: &Uuid) -> Option<Arc<Client>> {
|
||||||
|
self.clients.get(session_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtient tous les clients connectés
|
||||||
|
pub fn get_all_clients(&self) -> Vec<Arc<Client>> {
|
||||||
|
self.clients.values().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== OPÉRATIONS DE MODIFICATION =====
|
||||||
|
|
||||||
|
/// Modifie un client via une closure thread-safe
|
||||||
|
///
|
||||||
|
/// Cette méthode garantit que la modification est atomique et que
|
||||||
|
/// les index restent cohérents avec l'état du client.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `session_id` - L'ID de session du client à modifier
|
||||||
|
/// * `f` - La closure qui recevra une référence mutable vers le client
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// `true` si le client a été trouvé et modifié, `false` sinon
|
||||||
|
pub fn modify_client<F>(&self, client: &Client, f: F) -> bool
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Client),
|
||||||
|
{
|
||||||
|
// Convertir &str en String pour les clés
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientManager {
|
||||||
|
// Delete methods
|
||||||
|
/// Supprime tous les index d'un client
|
||||||
|
pub fn cleanup_client_index(&self, client: &Client) {
|
||||||
|
if let Some(channel) = client.voice_channel.as_ref() {
|
||||||
|
self.remove_client_from_voice_channel(channel, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime un client du gestionnaire
|
||||||
|
pub fn remove_client(&self, session_id: &Uuid) {
|
||||||
|
// Convertir &str en String pour les clés
|
||||||
|
if let Some(client) = self.clients.get(session_id) {
|
||||||
|
// Nettoyer tous les index
|
||||||
|
self.cleanup_client_index(&client);
|
||||||
|
|
||||||
|
// Supprimer le client de la liste principale
|
||||||
|
self.clients.remove(&session_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_client_from_voice_channel(&self, voice_channel: &Channel, client: &Client) {
|
||||||
|
if let Some(channel) = self.voice_channel_clients.get(voice_channel) {
|
||||||
|
channel.remove(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_client_from_sub_servers(&self, client: &Client) {
|
||||||
|
for (sub_server, clients) in self.sub_server_clients.iter() {
|
||||||
|
clients.remove(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/store/session/mod.rs
Normal file
1
src/store/session/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
mod client;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use sqlx::{AnyPool, SqlitePool};
|
use sqlx::{AnyPool, SqlitePool};
|
||||||
@@ -28,9 +29,9 @@ pub struct StoreService {
|
|||||||
pub sub_servers: SharedArcMap<Uuid, SubServer>,
|
pub sub_servers: SharedArcMap<Uuid, SubServer>,
|
||||||
pub channels: SharedArcMap<Uuid, Channel>,
|
pub channels: SharedArcMap<Uuid, Channel>,
|
||||||
pub sub_server_users: SharedArcVec<LinkSubServerUser>,
|
pub sub_server_users: SharedArcVec<LinkSubServerUser>,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl StoreService {
|
impl StoreService {
|
||||||
pub async fn new(database_url: &str) -> Result<Self, sqlx::Error> {
|
pub async fn new(database_url: &str) -> Result<Self, sqlx::Error> {
|
||||||
let connection_url = Self::normalize_database_url(database_url);
|
let connection_url = Self::normalize_database_url(database_url);
|
||||||
@@ -38,9 +39,9 @@ impl StoreService {
|
|||||||
|
|
||||||
let db = SqlitePool::connect(&connection_url).await?;
|
let db = SqlitePool::connect(&connection_url).await?;
|
||||||
|
|
||||||
sqlx::migrate!("./src/store/migrations").run(&db).await?;
|
// sqlx::migrate!("./src/store/migrations").run(&db).await?;
|
||||||
|
|
||||||
let service = Self {
|
let mut service = Self {
|
||||||
db,
|
db,
|
||||||
users: SharedArcMap::new(),
|
users: SharedArcMap::new(),
|
||||||
sub_servers: SharedArcMap::new(),
|
sub_servers: SharedArcMap::new(),
|
||||||
@@ -55,43 +56,56 @@ impl StoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn load_all_caches(&self) -> Result<(), sqlx::Error> {
|
async fn load_all_caches(&self) -> Result<(), sqlx::Error> {
|
||||||
// Users
|
let sub_server_rep = SubServerRepository::new(self.clone());
|
||||||
let users: Vec<User> = sqlx::query_as("SELECT * FROM user")
|
let user_rep = UserRepository::new(self.clone());
|
||||||
.fetch_all(&self.db)
|
let channel_rep = ChannelRepository::new(self.clone());
|
||||||
.await?;
|
let link_sub_server_user_rep = LinkSubServerUserRepository::new(self.clone());
|
||||||
for user in users {
|
|
||||||
self.users.insert(user.id, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubServers
|
// sub_servers
|
||||||
let sub_servers: Vec<SubServer> = sqlx::query_as("SELECT * FROM sub_server")
|
let sub_servers = sub_server_rep.db_all().await?;
|
||||||
.fetch_all(&self.db)
|
self.sub_servers.insert_batch_with_key(sub_servers, |sub_server| sub_server.id);
|
||||||
.await?;
|
|
||||||
for sub_server in sub_servers {
|
// Users
|
||||||
self.sub_servers.insert(sub_server.id, sub_server);
|
let users = user_rep.db_all().await?;
|
||||||
}
|
self.users.insert_batch_with_key(users, |user| user.id);
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
let channels: Vec<Channel> = sqlx::query_as("SELECT * FROM channel")
|
let channels = channel_rep.db_all().await?;
|
||||||
.fetch_all(&self.db)
|
self.channels.insert_batch_with_key(channels, |channel| channel.id);
|
||||||
.await?;
|
|
||||||
for channel in channels {
|
|
||||||
self.channels.insert(channel.id, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relations N-N
|
// Relations N-N
|
||||||
let relations: Vec<LinkSubServerUser> = sqlx::query_as("SELECT * FROM sub_server_user")
|
let relations: Vec<LinkSubServerUser> = link_sub_server_user_rep.db_all().await?;
|
||||||
.fetch_all(&self.db)
|
self.sub_server_users.extend(relations);
|
||||||
.await?;
|
|
||||||
for relation in relations {
|
|
||||||
self.sub_server_users.push(relation);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StoreService {
|
||||||
|
// Getters repositories
|
||||||
|
pub fn user_repository(&self) -> UserRepository {
|
||||||
|
UserRepository::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub_server_repository(&self) -> SubServerRepository {
|
||||||
|
SubServerRepository::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn channel_repository(&self) -> ChannelRepository {
|
||||||
|
ChannelRepository::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_repository(&self) -> MessageRepository {
|
||||||
|
MessageRepository::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub_server_user_repository(&self) -> LinkSubServerUserRepository {
|
||||||
|
LinkSubServerUserRepository::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
impl StoreService {
|
impl StoreService {
|
||||||
// ===== HELPERS =====
|
// ===== HELPERS =====
|
||||||
/// ✅ Normalise l'URL pour supporter différentes bases de données
|
/// ✅ Normalise l'URL pour supporter différentes bases de données
|
||||||
|
|||||||
@@ -244,6 +244,71 @@ impl<T> SharedVec<T> {
|
|||||||
self.inner.store(Arc::new(new_vec));
|
self.inner.store(Arc::new(new_vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Supprime l'élément à l'index donné et le retourne.
|
||||||
|
/// Retourne `None` si l'index est hors limites.
|
||||||
|
///
|
||||||
|
/// Exemple : `let removed = clients.delete(2); // Supprime le 3e client`
|
||||||
|
pub fn delete(&self, index: usize) -> Option<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
if index >= current.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_vec = current.as_ref().clone();
|
||||||
|
let removed_item = new_vec.remove(index);
|
||||||
|
self.inner.store(Arc::new(new_vec));
|
||||||
|
Some(removed_item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime et retourne le dernier élément du vecteur.
|
||||||
|
/// Retourne `None` si le vecteur est vide.
|
||||||
|
///
|
||||||
|
/// Exemple : `let last_client = clients.pop(); // Supprime le dernier client`
|
||||||
|
pub fn pop(&self) -> Option<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
if current.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_vec = current.as_ref().clone();
|
||||||
|
let popped_item = new_vec.pop();
|
||||||
|
self.inner.store(Arc::new(new_vec));
|
||||||
|
popped_item
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime tous les éléments qui correspondent au prédicat donné.
|
||||||
|
/// Retourne le nombre d'éléments supprimés.
|
||||||
|
///
|
||||||
|
/// Exemple : `let removed = clients.delete_matching(|c| c.is_disconnected());`
|
||||||
|
pub fn delete_matching<F>(&self, predicate: F) -> usize
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
F: Fn(&T) -> bool,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
let mut new_vec = Vec::with_capacity(current.len());
|
||||||
|
let mut removed_count = 0;
|
||||||
|
|
||||||
|
for item in current.iter() {
|
||||||
|
if predicate(item) {
|
||||||
|
removed_count += 1;
|
||||||
|
} else {
|
||||||
|
new_vec.push(item.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if removed_count > 0 {
|
||||||
|
self.inner.store(Arc::new(new_vec));
|
||||||
|
}
|
||||||
|
removed_count
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear optimisé
|
/// Clear optimisé
|
||||||
///
|
///
|
||||||
/// Exemple : `clients.clear(); // Vide la liste`
|
/// Exemple : `clients.clear(); // Vide la liste`
|
||||||
@@ -707,6 +772,38 @@ where
|
|||||||
let map_ref = self.inner.load_full();
|
let map_ref = self.inner.load_full();
|
||||||
map_ref.values().cloned().collect::<Vec<_>>().into_iter()
|
map_ref.values().cloned().collect::<Vec<_>>().into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert une collection entière de paires clé-valeur
|
||||||
|
pub fn insert_batch<I>(&self, items: I)
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (K, V)>,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
let mut new_map = current.as_ref().clone();
|
||||||
|
|
||||||
|
for (key, value) in items {
|
||||||
|
new_map.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.store(Arc::new(new_map));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert une collection de valeurs avec une fonction pour extraire la clé
|
||||||
|
pub fn insert_batch_with_key<I, F>(&self, items: I, key_fn: F)
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = V>,
|
||||||
|
F: Fn(&V) -> K,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
let mut new_map = current.as_ref().clone();
|
||||||
|
|
||||||
|
for value in items {
|
||||||
|
let key = key_fn(&value);
|
||||||
|
new_map.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.store(Arc::new(new_map));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone gratuit (juste Arc::clone)
|
// Clone gratuit (juste Arc::clone)
|
||||||
@@ -820,6 +917,85 @@ impl<T> SharedArcVec<T> {
|
|||||||
self.inner.store(Arc::new(Vec::new()));
|
self.inner.store(Arc::new(Vec::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Supprime l'élément à l'index donné et le retourne.
|
||||||
|
/// Retourne `None` si l'index est hors limites.
|
||||||
|
pub fn delete(&self, index: usize) -> Option<Arc<T>> {
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
if index >= current.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_vec = current.as_ref().clone();
|
||||||
|
let removed_item = new_vec.remove(index);
|
||||||
|
self.inner.store(Arc::new(new_vec));
|
||||||
|
Some(removed_item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime et retourne le dernier élément du vecteur.
|
||||||
|
/// Retourne `None` si le vecteur est vide.
|
||||||
|
pub fn pop(&self) -> Option<Arc<T>> {
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
if current.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_vec = current.as_ref().clone();
|
||||||
|
let popped_item = new_vec.pop();
|
||||||
|
self.inner.store(Arc::new(new_vec));
|
||||||
|
popped_item
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime tous les éléments qui correspondent au prédicat donné.
|
||||||
|
/// Retourne le nombre d'éléments supprimés.
|
||||||
|
pub fn delete_matching<F>(&self, predicate: F) -> usize
|
||||||
|
where
|
||||||
|
F: Fn(&T) -> bool,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
let mut new_vec = Vec::with_capacity(current.len());
|
||||||
|
let mut removed_count = 0;
|
||||||
|
|
||||||
|
for item in current.iter() {
|
||||||
|
if predicate(item.as_ref()) {
|
||||||
|
removed_count += 1;
|
||||||
|
} else {
|
||||||
|
new_vec.push(item.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if removed_count > 0 {
|
||||||
|
self.inner.store(Arc::new(new_vec));
|
||||||
|
}
|
||||||
|
removed_count
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime le premier élément trouvé qui correspond au prédicat.
|
||||||
|
/// Retourne `Some(Arc<T>)` si un élément a été trouvé et supprimé, `None` sinon.
|
||||||
|
pub fn delete_first_matching<F>(&self, predicate: F) -> Option<Arc<T>>
|
||||||
|
where
|
||||||
|
F: Fn(&T) -> bool,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
let mut found_index = None;
|
||||||
|
|
||||||
|
for (index, item) in current.iter().enumerate() {
|
||||||
|
if predicate(item.as_ref()) {
|
||||||
|
found_index = Some(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(index) = found_index {
|
||||||
|
let mut new_vec = current.as_ref().clone();
|
||||||
|
let removed_item = new_vec.remove(index);
|
||||||
|
self.inner.store(Arc::new(new_vec));
|
||||||
|
Some(removed_item)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Iteration retournant Arc<T>
|
/// Iteration retournant Arc<T>
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Arc<T>> + '_ {
|
pub fn iter(&self) -> impl Iterator<Item = Arc<T>> + '_ {
|
||||||
let vec_ref = self.inner.load_full();
|
let vec_ref = self.inner.load_full();
|
||||||
@@ -1059,8 +1235,41 @@ where
|
|||||||
let map_ref = self.inner.load_full();
|
let map_ref = self.inner.load_full();
|
||||||
map_ref.iter().map(|(k, v)| (k.clone(), v.clone())).collect::<Vec<_>>().into_iter()
|
map_ref.iter().map(|(k, v)| (k.clone(), v.clone())).collect::<Vec<_>>().into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert une collection entière de paires clé-valeur
|
||||||
|
pub fn insert_batch<I>(&self, items: I)
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (K, V)>,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
let mut new_map = current.as_ref().clone();
|
||||||
|
|
||||||
|
for (key, value) in items {
|
||||||
|
new_map.insert(key, Arc::new(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.inner.store(Arc::new(new_map));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert une collection de valeurs avec une fonction pour extraire la clé
|
||||||
|
pub fn insert_batch_with_key<I, F>(&self, items: I, key_fn: F)
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = V>,
|
||||||
|
F: Fn(&V) -> K,
|
||||||
|
{
|
||||||
|
let current = self.inner.load_full();
|
||||||
|
let mut new_map = current.as_ref().clone();
|
||||||
|
|
||||||
|
for value in items {
|
||||||
|
let key = key_fn(&value);
|
||||||
|
new_map.insert(key, Arc::new(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.store(Arc::new(new_map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ===== IMPLÉMENTATIONS CLONE =====
|
// ===== IMPLÉMENTATIONS CLONE =====
|
||||||
|
|
||||||
impl<T> Clone for SharedArcVec<T> {
|
impl<T> Clone for SharedArcVec<T> {
|
||||||
|
|||||||
Reference in New Issue
Block a user